@cloudbase/cloudbase-mcp 2.35.2 → 2.36.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +71 -0
- package/README.zh-CN.md +43 -0
- package/dist/cli.cjs +11 -11
- package/dist/index.cjs +6441 -281
- package/dist/index.d.ts +11 -56
- package/dist/index.js +6414 -223
- package/package.json +1 -3
- package/server.json +2 -2
- package/dist/267.index.js +0 -4404
- package/dist/880.index.cjs +0 -4393
- package/dist/931.index.cjs +0 -27665
package/dist/267.index.js
DELETED
|
@@ -1,4404 +0,0 @@
|
|
|
1
|
-
import * as __WEBPACK_EXTERNAL_MODULE_express__ from "express";
|
|
2
|
-
import * as __WEBPACK_EXTERNAL_MODULE_open__ from "open";
|
|
3
|
-
import * as __WEBPACK_EXTERNAL_MODULE_ws__ from "ws";
|
|
4
|
-
export const __webpack_esm_id__ = 267;
|
|
5
|
-
export const __webpack_esm_ids__ = [267];
|
|
6
|
-
export const __webpack_esm_modules__ = {
|
|
7
|
-
|
|
8
|
-
/***/ 6267
|
|
9
|
-
(__unused_webpack_module, exports, __webpack_require__) {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
13
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
14
|
-
};
|
|
15
|
-
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
16
|
-
exports.InteractiveServer = void 0;
|
|
17
|
-
exports.getInteractiveServer = getInteractiveServer;
|
|
18
|
-
exports.resetInteractiveServer = resetInteractiveServer;
|
|
19
|
-
exports.getInteractiveServerSafe = getInteractiveServerSafe;
|
|
20
|
-
const express_1 = __importDefault(__webpack_require__(2674));
|
|
21
|
-
const http_1 = __importDefault(__webpack_require__(3782));
|
|
22
|
-
const child_process_1 = __webpack_require__(5814);
|
|
23
|
-
const open_1 = __importDefault(__webpack_require__(1622));
|
|
24
|
-
const ws_1 = __webpack_require__(6220);
|
|
25
|
-
const index_js_1 = __webpack_require__(9903);
|
|
26
|
-
const logger_js_1 = __webpack_require__(781);
|
|
27
|
-
const site_map_js_1 = __webpack_require__(4533);
|
|
28
|
-
function isVSCodeEnvironment() {
|
|
29
|
-
return Boolean(process.env.VSCODE_IPC_HOOK_CLI ||
|
|
30
|
-
process.env.VSCODE_PID ||
|
|
31
|
-
process.env.TERM_PROGRAM === "vscode");
|
|
32
|
-
}
|
|
33
|
-
/**
|
|
34
|
-
* Validates that a URL is safe to open, blocking SSRF targets.
|
|
35
|
-
* Only allows http/https schemes and rejects private/reserved IP ranges
|
|
36
|
-
* and sensitive hostnames.
|
|
37
|
-
*/
|
|
38
|
-
function isUrlSafeToOpen(url) {
|
|
39
|
-
let parsed;
|
|
40
|
-
try {
|
|
41
|
-
parsed = new URL(url);
|
|
42
|
-
}
|
|
43
|
-
catch {
|
|
44
|
-
return { safe: false, reason: "Invalid URL format" };
|
|
45
|
-
}
|
|
46
|
-
// Only allow http and https schemes
|
|
47
|
-
if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
|
|
48
|
-
return { safe: false, reason: `Unsupported scheme: ${parsed.protocol}` };
|
|
49
|
-
}
|
|
50
|
-
const hostname = parsed.hostname.toLowerCase();
|
|
51
|
-
// Block common sensitive / internal hostnames
|
|
52
|
-
const blockedHostnames = [
|
|
53
|
-
"localhost",
|
|
54
|
-
"localhost.localdomain",
|
|
55
|
-
"ip6-localhost",
|
|
56
|
-
"ip6-loopback",
|
|
57
|
-
"metadata.google.internal",
|
|
58
|
-
"metadata.internal",
|
|
59
|
-
];
|
|
60
|
-
if (blockedHostnames.includes(hostname)) {
|
|
61
|
-
return { safe: false, reason: `Blocked hostname: ${hostname}` };
|
|
62
|
-
}
|
|
63
|
-
// Block hostnames ending with .internal or .local
|
|
64
|
-
if (hostname.endsWith(".internal") || hostname.endsWith(".local")) {
|
|
65
|
-
return { safe: false, reason: `Blocked internal hostname: ${hostname}` };
|
|
66
|
-
}
|
|
67
|
-
// Block IP addresses in private/reserved ranges (RFC 1918, loopback, link-local, etc.)
|
|
68
|
-
const ipv4Regex = /^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/;
|
|
69
|
-
const match = ipv4Regex.exec(hostname);
|
|
70
|
-
if (match) {
|
|
71
|
-
const octets = [parseInt(match[1], 10), parseInt(match[2], 10), parseInt(match[3], 10), parseInt(match[4], 10)];
|
|
72
|
-
// Loopback: 127.0.0.0/8
|
|
73
|
-
if (octets[0] === 127) {
|
|
74
|
-
return { safe: false, reason: "Blocked loopback IP address" };
|
|
75
|
-
}
|
|
76
|
-
// Link-local: 169.254.0.0/16
|
|
77
|
-
if (octets[0] === 169 && octets[1] === 254) {
|
|
78
|
-
return { safe: false, reason: "Blocked link-local IP address" };
|
|
79
|
-
}
|
|
80
|
-
// RFC 1918 private: 10.0.0.0/8
|
|
81
|
-
if (octets[0] === 10) {
|
|
82
|
-
return { safe: false, reason: "Blocked private IP address (10.x.x.x)" };
|
|
83
|
-
}
|
|
84
|
-
// RFC 1918 private: 172.16.0.0/12
|
|
85
|
-
if (octets[0] === 172 && octets[1] >= 16 && octets[1] <= 31) {
|
|
86
|
-
return { safe: false, reason: "Blocked private IP address (172.16-31.x.x)" };
|
|
87
|
-
}
|
|
88
|
-
// RFC 1918 private: 192.168.0.0/16
|
|
89
|
-
if (octets[0] === 192 && octets[1] === 168) {
|
|
90
|
-
return { safe: false, reason: "Blocked private IP address (192.168.x.x)" };
|
|
91
|
-
}
|
|
92
|
-
// Broadcast: 0.0.0.0
|
|
93
|
-
if (octets[0] === 0 && octets[1] === 0 && octets[2] === 0 && octets[3] === 0) {
|
|
94
|
-
return { safe: false, reason: "Blocked broadcast address" };
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
// Block IPv6 loopback and link-local
|
|
98
|
-
if (hostname.startsWith("[") && hostname.endsWith("]")) {
|
|
99
|
-
const ipv6 = hostname.slice(1, -1).toLowerCase();
|
|
100
|
-
if (ipv6 === "::1" || ipv6 === "0:0:0:0:0:0:0:1" || ipv6 === "::" || ipv6 === "0:0:0:0:0:0:0:0") {
|
|
101
|
-
return { safe: false, reason: "Blocked IPv6 loopback/reserved address" };
|
|
102
|
-
}
|
|
103
|
-
if (ipv6.startsWith("fe80:")) {
|
|
104
|
-
return { safe: false, reason: "Blocked IPv6 link-local address" };
|
|
105
|
-
}
|
|
106
|
-
if (ipv6.startsWith("fc") || ipv6.startsWith("fd")) {
|
|
107
|
-
return { safe: false, reason: "Blocked IPv6 unique-local address" };
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
return { safe: true };
|
|
111
|
-
}
|
|
112
|
-
function parseBrowserCommand(browser) {
|
|
113
|
-
const parts = browser.match(/(?:[^\s"]+|"[^"]*")+/g) || [];
|
|
114
|
-
return parts.map((item) => item.replace(/^"(.*)"$/, "$1"));
|
|
115
|
-
}
|
|
116
|
-
async function openUrlByBrowserEnv(url) {
|
|
117
|
-
const browser = process.env.BROWSER?.trim();
|
|
118
|
-
if (!browser) {
|
|
119
|
-
return false;
|
|
120
|
-
}
|
|
121
|
-
const [command, ...args] = parseBrowserCommand(browser);
|
|
122
|
-
if (!command) {
|
|
123
|
-
return false;
|
|
124
|
-
}
|
|
125
|
-
const finalArgs = args.map((arg) => (arg === "%s" ? url : arg));
|
|
126
|
-
if (!args.includes("%s")) {
|
|
127
|
-
finalArgs.push(url);
|
|
128
|
-
}
|
|
129
|
-
return new Promise((resolve) => {
|
|
130
|
-
(0, child_process_1.execFile)(command, finalArgs, (execError) => {
|
|
131
|
-
resolve(!execError);
|
|
132
|
-
});
|
|
133
|
-
});
|
|
134
|
-
}
|
|
135
|
-
function shouldUseBrowserEnvFallback() {
|
|
136
|
-
return process.platform === "linux" && isVSCodeEnvironment();
|
|
137
|
-
}
|
|
138
|
-
// Dynamic open behavior with IDE awareness and VSCode/Linux fallback
|
|
139
|
-
async function openUrl(url, options, mcpServer) {
|
|
140
|
-
// mcpServer 是 ExtendedMcpServer 实例,它有 server 和 ide 属性
|
|
141
|
-
// server 属性是 MCP server 的内部 server 实例,有 sendLoggingMessage 方法
|
|
142
|
-
const currentIde = mcpServer?.ide || process.env.INTEGRATION_IDE;
|
|
143
|
-
const internalServer = mcpServer?.server; // 内部的 server 实例
|
|
144
|
-
(0, logger_js_1.debug)(`[openUrl] Checking IDE: ${currentIde}`);
|
|
145
|
-
(0, logger_js_1.debug)(`[openUrl] mcpServer type: ${typeof mcpServer}, has server: ${!!mcpServer?.server}, has ide: ${!!mcpServer?.ide}`);
|
|
146
|
-
if (internalServer) {
|
|
147
|
-
(0, logger_js_1.debug)(`[openUrl] internalServer type: ${typeof internalServer}, has sendLoggingMessage: ${typeof internalServer.sendLoggingMessage === 'function'}`);
|
|
148
|
-
}
|
|
149
|
-
// 检查是否为 CodeBuddy IDE (优先使用 mcpServer.ide,回退到环境变量)
|
|
150
|
-
if (currentIde === "CodeBuddy" && internalServer && typeof internalServer.sendLoggingMessage === 'function') {
|
|
151
|
-
try {
|
|
152
|
-
// internalServer 是 MCP server 的内部 server 实例,有 sendLoggingMessage 方法
|
|
153
|
-
internalServer.sendLoggingMessage({
|
|
154
|
-
level: "notice",
|
|
155
|
-
data: {
|
|
156
|
-
type: "tcb",
|
|
157
|
-
url: url,
|
|
158
|
-
},
|
|
159
|
-
});
|
|
160
|
-
(0, logger_js_1.info)(`CodeBuddy IDE: 已发送网页打开通知 - ${url}`);
|
|
161
|
-
return;
|
|
162
|
-
}
|
|
163
|
-
catch (err) {
|
|
164
|
-
(0, logger_js_1.error)(`Failed to send logging message for ${url}: ${err instanceof Error ? err.message : err}`, err instanceof Error ? err : new Error(String(err)));
|
|
165
|
-
// 如果发送通知失败,在 CodeBuddy IDE 中不打开网页,直接返回
|
|
166
|
-
(0, logger_js_1.warn)(`CodeBuddy IDE: 发送通知失败,不打开网页 - ${url}`);
|
|
167
|
-
return;
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
// 默认行为:直接打开网页(带 VSCode/Linux 降级逻辑)
|
|
171
|
-
(0, logger_js_1.debug)(`[openUrl] Opening URL in browser: ${url}`);
|
|
172
|
-
try {
|
|
173
|
-
const openOptions = options ? { url: true, ...options } : { url: true };
|
|
174
|
-
const child = await (0, open_1.default)(url, openOptions);
|
|
175
|
-
if (child?.once) {
|
|
176
|
-
child.once("error", async () => {
|
|
177
|
-
if (shouldUseBrowserEnvFallback()) {
|
|
178
|
-
(0, logger_js_1.debug)("[openUrl] open() failed, trying BROWSER fallback");
|
|
179
|
-
await openUrlByBrowserEnv(url);
|
|
180
|
-
}
|
|
181
|
-
});
|
|
182
|
-
}
|
|
183
|
-
return;
|
|
184
|
-
}
|
|
185
|
-
catch (err) {
|
|
186
|
-
(0, logger_js_1.error)(`Failed to open ${url} ${options} ${err instanceof Error ? err.message : err} `, err instanceof Error ? err : new Error(String(err)));
|
|
187
|
-
if (shouldUseBrowserEnvFallback()) {
|
|
188
|
-
(0, logger_js_1.debug)("[openUrl] open() threw, trying BROWSER fallback");
|
|
189
|
-
await openUrlByBrowserEnv(url);
|
|
190
|
-
return;
|
|
191
|
-
}
|
|
192
|
-
(0, logger_js_1.warn)(`Please manually open: ${url}`);
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
class InteractiveServer {
|
|
196
|
-
app;
|
|
197
|
-
server;
|
|
198
|
-
wss;
|
|
199
|
-
port = 0;
|
|
200
|
-
isRunning = false;
|
|
201
|
-
currentResolver = null;
|
|
202
|
-
sessionData = new Map();
|
|
203
|
-
_mcpServer = null; // 保存 MCP server 实例引用
|
|
204
|
-
// 公共 getter 和 setter
|
|
205
|
-
get mcpServer() {
|
|
206
|
-
return this._mcpServer;
|
|
207
|
-
}
|
|
208
|
-
set mcpServer(server) {
|
|
209
|
-
this._mcpServer = server;
|
|
210
|
-
}
|
|
211
|
-
DEFAULT_PORT = 3721;
|
|
212
|
-
FALLBACK_PORTS = [
|
|
213
|
-
3722, 3723, 3724, 3725, 3726, 3727, 3728, 3729, 3730, 3731, 3732, 3733,
|
|
214
|
-
3734, 3735,
|
|
215
|
-
];
|
|
216
|
-
/** Idle timeout for HTTP/WS connections (e.g. long login). Avoids "connection disconnected" after ~1 min. */
|
|
217
|
-
static SERVER_IDLE_MS = 30 * 60 * 1000; // 30 minutes
|
|
218
|
-
/** WebSocket ping interval to keep connection alive past proxies/firewalls. */
|
|
219
|
-
static WS_PING_INTERVAL_MS = 25 * 1000; // 25 seconds
|
|
220
|
-
constructor(mcpServer) {
|
|
221
|
-
this._mcpServer = mcpServer;
|
|
222
|
-
this.app = (0, express_1.default)();
|
|
223
|
-
this.server = http_1.default.createServer(this.app);
|
|
224
|
-
this.applyServerTimeouts();
|
|
225
|
-
this.wss = new ws_1.WebSocketServer({ server: this.server });
|
|
226
|
-
this.setupExpress();
|
|
227
|
-
this.setupWebSocket();
|
|
228
|
-
process.on("exit", () => this.cleanup());
|
|
229
|
-
process.on("SIGINT", () => this.cleanup());
|
|
230
|
-
process.on("SIGTERM", () => this.cleanup());
|
|
231
|
-
}
|
|
232
|
-
cleanup() {
|
|
233
|
-
if (this.isRunning) {
|
|
234
|
-
(0, logger_js_1.debug)("Cleaning up interactive server resources...");
|
|
235
|
-
this.server.close();
|
|
236
|
-
this.wss.close();
|
|
237
|
-
this.isRunning = false;
|
|
238
|
-
}
|
|
239
|
-
}
|
|
240
|
-
/** Apply timeouts so long-lived login does not cause "connection disconnected". */
|
|
241
|
-
applyServerTimeouts() {
|
|
242
|
-
this.server.timeout = 0;
|
|
243
|
-
if (typeof this.server.keepAliveTimeout === "number") {
|
|
244
|
-
this.server.keepAliveTimeout = InteractiveServer.SERVER_IDLE_MS;
|
|
245
|
-
}
|
|
246
|
-
if (typeof this.server.headersTimeout === "number") {
|
|
247
|
-
this.server.headersTimeout = Math.max(this.server.headersTimeout || 0, InteractiveServer.SERVER_IDLE_MS);
|
|
248
|
-
}
|
|
249
|
-
}
|
|
250
|
-
setupExpress() {
|
|
251
|
-
this.app.use(express_1.default.json());
|
|
252
|
-
this.app.get("/env-setup/:sessionId", (req, res) => {
|
|
253
|
-
const { sessionId } = req.params;
|
|
254
|
-
const sessionData = this.sessionData.get(sessionId);
|
|
255
|
-
if (!sessionData) {
|
|
256
|
-
res.status(404).send("会话不存在或已过期");
|
|
257
|
-
return;
|
|
258
|
-
}
|
|
259
|
-
res.send(this.getEnvSetupHTML(sessionData.envs, sessionData.accountInfo, sessionData.errorContext, // Pass error context
|
|
260
|
-
sessionId // Pass sessionId for retry functionality
|
|
261
|
-
));
|
|
262
|
-
});
|
|
263
|
-
this.app.get("/clarification/:sessionId", (req, res) => {
|
|
264
|
-
const { sessionId } = req.params;
|
|
265
|
-
const sessionData = this.sessionData.get(sessionId);
|
|
266
|
-
if (!sessionData) {
|
|
267
|
-
res.status(404).send("会话不存在或已过期");
|
|
268
|
-
return;
|
|
269
|
-
}
|
|
270
|
-
res.send(this.getClarificationHTML(sessionData.message, sessionData.options));
|
|
271
|
-
});
|
|
272
|
-
this.app.post("/api/submit", (req, res) => {
|
|
273
|
-
const { type, data } = req.body;
|
|
274
|
-
(0, logger_js_1.debug)("Received submit request", { type, data });
|
|
275
|
-
if (this.currentResolver) {
|
|
276
|
-
(0, logger_js_1.info)("Resolving with user data");
|
|
277
|
-
this.currentResolver({ type, data });
|
|
278
|
-
this.currentResolver = null;
|
|
279
|
-
}
|
|
280
|
-
else {
|
|
281
|
-
(0, logger_js_1.warn)("No resolver waiting for response");
|
|
282
|
-
}
|
|
283
|
-
res.json({ success: true });
|
|
284
|
-
});
|
|
285
|
-
this.app.post("/api/cancel", (req, res) => {
|
|
286
|
-
(0, logger_js_1.info)("Received cancel request");
|
|
287
|
-
if (this.currentResolver) {
|
|
288
|
-
(0, logger_js_1.info)("Resolving with cancelled status");
|
|
289
|
-
this.currentResolver({
|
|
290
|
-
type: "clarification",
|
|
291
|
-
data: null,
|
|
292
|
-
cancelled: true,
|
|
293
|
-
});
|
|
294
|
-
this.currentResolver = null;
|
|
295
|
-
}
|
|
296
|
-
else {
|
|
297
|
-
(0, logger_js_1.warn)("No resolver waiting for cancellation");
|
|
298
|
-
}
|
|
299
|
-
res.json({ success: true });
|
|
300
|
-
});
|
|
301
|
-
this.app.post("/api/switch", (req, res) => {
|
|
302
|
-
(0, logger_js_1.info)("Received switch account request");
|
|
303
|
-
if (this.currentResolver) {
|
|
304
|
-
(0, logger_js_1.info)("Resolving with switch status");
|
|
305
|
-
this.currentResolver({
|
|
306
|
-
type: "envId",
|
|
307
|
-
data: null,
|
|
308
|
-
switch: true,
|
|
309
|
-
});
|
|
310
|
-
this.currentResolver = null;
|
|
311
|
-
}
|
|
312
|
-
else {
|
|
313
|
-
(0, logger_js_1.warn)("No resolver waiting for switch");
|
|
314
|
-
}
|
|
315
|
-
res.json({ success: true });
|
|
316
|
-
});
|
|
317
|
-
this.app.post("/api/retry-init-tcb", (req, res) => {
|
|
318
|
-
const { sessionId } = req.body;
|
|
319
|
-
(0, logger_js_1.info)("Received retry InitTcb request", { sessionId });
|
|
320
|
-
// Mark session for retry
|
|
321
|
-
const sessionData = this.sessionData.get(sessionId);
|
|
322
|
-
if (sessionData) {
|
|
323
|
-
sessionData.retryInitTcb = true;
|
|
324
|
-
(0, logger_js_1.debug)("Marked session for InitTcb retry", { sessionId });
|
|
325
|
-
}
|
|
326
|
-
res.json({ success: true, message: "重试请求已提交,页面将刷新" });
|
|
327
|
-
});
|
|
328
|
-
// Universal URL opener API - ensures proper handling in different IDEs (e.g., CodeBuddy)
|
|
329
|
-
this.app.post("/api/open-url", async (req, res) => {
|
|
330
|
-
const { url } = req.body;
|
|
331
|
-
if (!url) {
|
|
332
|
-
res.status(400).json({ success: false, error: "URL is required" });
|
|
333
|
-
return;
|
|
334
|
-
}
|
|
335
|
-
if (typeof url !== "string") {
|
|
336
|
-
res.status(400).json({ success: false, error: "URL must be a string" });
|
|
337
|
-
return;
|
|
338
|
-
}
|
|
339
|
-
// Validate URL to prevent SSRF
|
|
340
|
-
const validation = isUrlSafeToOpen(url);
|
|
341
|
-
if (!validation.safe) {
|
|
342
|
-
(0, logger_js_1.warn)(`Blocked open-url request for unsafe URL: ${url} (${validation.reason})`);
|
|
343
|
-
res.status(403).json({ success: false, error: `URL not allowed: ${validation.reason}` });
|
|
344
|
-
return;
|
|
345
|
-
}
|
|
346
|
-
(0, logger_js_1.info)("Received open URL request", { url });
|
|
347
|
-
try {
|
|
348
|
-
// Pass mcpServer directly - it's an ExtendedMcpServer instance with server property
|
|
349
|
-
await openUrl(url, undefined, this._mcpServer);
|
|
350
|
-
res.json({ success: true });
|
|
351
|
-
}
|
|
352
|
-
catch (err) {
|
|
353
|
-
(0, logger_js_1.error)("Failed to open URL", err instanceof Error ? err : new Error(String(err)));
|
|
354
|
-
res.status(500).json({
|
|
355
|
-
success: false,
|
|
356
|
-
error: err instanceof Error ? err.message : String(err)
|
|
357
|
-
});
|
|
358
|
-
}
|
|
359
|
-
});
|
|
360
|
-
}
|
|
361
|
-
setupWebSocket() {
|
|
362
|
-
this.wss.on("connection", (ws) => {
|
|
363
|
-
(0, logger_js_1.debug)("WebSocket client connected");
|
|
364
|
-
// Keep connection alive during long login so proxies/firewalls do not close it
|
|
365
|
-
const pingInterval = setInterval(() => {
|
|
366
|
-
if (ws.readyState === ws_1.WebSocket.OPEN) {
|
|
367
|
-
ws.ping();
|
|
368
|
-
}
|
|
369
|
-
}, InteractiveServer.WS_PING_INTERVAL_MS);
|
|
370
|
-
ws.on("close", () => {
|
|
371
|
-
clearInterval(pingInterval);
|
|
372
|
-
(0, logger_js_1.debug)("WebSocket client disconnected");
|
|
373
|
-
});
|
|
374
|
-
ws.on("message", async (message) => {
|
|
375
|
-
try {
|
|
376
|
-
const data = JSON.parse(message.toString());
|
|
377
|
-
(0, logger_js_1.debug)("WebSocket message received", data);
|
|
378
|
-
// Handle session registration
|
|
379
|
-
if (data.type === 'registerSession' && data.sessionId) {
|
|
380
|
-
(0, logger_js_1.debug)("Registering WebSocket for session:", data.sessionId);
|
|
381
|
-
const sessionData = this.sessionData.get(data.sessionId);
|
|
382
|
-
if (sessionData) {
|
|
383
|
-
sessionData.ws = ws;
|
|
384
|
-
(0, logger_js_1.debug)("WebSocket registered successfully for session:", data.sessionId);
|
|
385
|
-
}
|
|
386
|
-
else {
|
|
387
|
-
(0, logger_js_1.debug)("Session not found:", data.sessionId);
|
|
388
|
-
}
|
|
389
|
-
return;
|
|
390
|
-
}
|
|
391
|
-
// Handle refresh environment list request
|
|
392
|
-
if (data.type === 'refreshEnvList') {
|
|
393
|
-
(0, logger_js_1.debug)("Handling refreshEnvList request");
|
|
394
|
-
try {
|
|
395
|
-
// Find the session ID for this WebSocket connection
|
|
396
|
-
let targetSessionId = null;
|
|
397
|
-
for (const [sessionId, sessionData] of this.sessionData.entries()) {
|
|
398
|
-
if (sessionData.ws === ws) {
|
|
399
|
-
targetSessionId = sessionId;
|
|
400
|
-
break;
|
|
401
|
-
}
|
|
402
|
-
}
|
|
403
|
-
if (targetSessionId) {
|
|
404
|
-
const sessionData = this.sessionData.get(targetSessionId);
|
|
405
|
-
if (sessionData && sessionData.manager) {
|
|
406
|
-
// Re-fetch environment list using the same API and parameters as initial query
|
|
407
|
-
let envResult;
|
|
408
|
-
try {
|
|
409
|
-
// Use DescribeEnvs with filter parameters (same as initial query)
|
|
410
|
-
const queryParams = {
|
|
411
|
-
EnvTypes: ["weda", "baas"],
|
|
412
|
-
IsVisible: false,
|
|
413
|
-
Channels: ["dcloud", "iotenable", "tem", "scene_module"],
|
|
414
|
-
};
|
|
415
|
-
envResult = await sessionData.manager.commonService("tcb", "2018-06-08").call({
|
|
416
|
-
Action: "DescribeEnvs",
|
|
417
|
-
Param: queryParams,
|
|
418
|
-
});
|
|
419
|
-
// Transform response format to match original listEnvs() format
|
|
420
|
-
if (envResult && envResult.EnvList) {
|
|
421
|
-
envResult = { EnvList: envResult.EnvList };
|
|
422
|
-
}
|
|
423
|
-
else if (envResult && envResult.Data && envResult.Data.EnvList) {
|
|
424
|
-
envResult = { EnvList: envResult.Data.EnvList };
|
|
425
|
-
}
|
|
426
|
-
else {
|
|
427
|
-
// Fallback to listEnvs if format is unexpected
|
|
428
|
-
(0, logger_js_1.debug)("Unexpected response format, falling back to listEnvs()");
|
|
429
|
-
envResult = await sessionData.manager.env.listEnvs();
|
|
430
|
-
}
|
|
431
|
-
}
|
|
432
|
-
catch (error) {
|
|
433
|
-
(0, logger_js_1.debug)("DescribeEnvs failed, falling back to listEnvs():", error instanceof Error ? error : new Error(String(error)));
|
|
434
|
-
// Fallback to original method on error
|
|
435
|
-
envResult = await sessionData.manager.env.listEnvs();
|
|
436
|
-
}
|
|
437
|
-
const envs = envResult?.EnvList || [];
|
|
438
|
-
// Update session data
|
|
439
|
-
sessionData.envs = envs;
|
|
440
|
-
// Send updated environment list to client
|
|
441
|
-
ws.send(JSON.stringify({
|
|
442
|
-
type: 'envListRefreshed',
|
|
443
|
-
envs: envs,
|
|
444
|
-
success: true
|
|
445
|
-
}));
|
|
446
|
-
(0, logger_js_1.info)(`Environment list refreshed, found ${envs.length} environments`);
|
|
447
|
-
}
|
|
448
|
-
else {
|
|
449
|
-
ws.send(JSON.stringify({
|
|
450
|
-
type: 'envListRefreshed',
|
|
451
|
-
success: false,
|
|
452
|
-
error: '无法获取环境管理器'
|
|
453
|
-
}));
|
|
454
|
-
}
|
|
455
|
-
}
|
|
456
|
-
else {
|
|
457
|
-
ws.send(JSON.stringify({
|
|
458
|
-
type: 'envListRefreshed',
|
|
459
|
-
success: false,
|
|
460
|
-
error: '会话不存在'
|
|
461
|
-
}));
|
|
462
|
-
}
|
|
463
|
-
}
|
|
464
|
-
catch (err) {
|
|
465
|
-
(0, logger_js_1.error)("Failed to refresh environment list", err instanceof Error ? err : new Error(String(err)));
|
|
466
|
-
ws.send(JSON.stringify({
|
|
467
|
-
type: 'envListRefreshed',
|
|
468
|
-
success: false,
|
|
469
|
-
error: err instanceof Error ? err.message : '刷新失败'
|
|
470
|
-
}));
|
|
471
|
-
}
|
|
472
|
-
return;
|
|
473
|
-
}
|
|
474
|
-
if (this.currentResolver) {
|
|
475
|
-
this.currentResolver(data);
|
|
476
|
-
this.currentResolver = null;
|
|
477
|
-
}
|
|
478
|
-
}
|
|
479
|
-
catch (err) {
|
|
480
|
-
(0, logger_js_1.error)("WebSocket message parsing error", err instanceof Error ? err : new Error(String(err)));
|
|
481
|
-
}
|
|
482
|
-
});
|
|
483
|
-
});
|
|
484
|
-
}
|
|
485
|
-
async start() {
|
|
486
|
-
if (this.isRunning) {
|
|
487
|
-
(0, logger_js_1.debug)(`Interactive server already running on port ${this.port}`);
|
|
488
|
-
return this.port;
|
|
489
|
-
}
|
|
490
|
-
return new Promise((resolve, reject) => {
|
|
491
|
-
(0, logger_js_1.info)("Starting interactive server...");
|
|
492
|
-
const tryPorts = [this.DEFAULT_PORT, ...this.FALLBACK_PORTS];
|
|
493
|
-
let currentIndex = 0;
|
|
494
|
-
const tryNextPort = () => {
|
|
495
|
-
if (currentIndex >= tryPorts.length) {
|
|
496
|
-
const err = new Error(`All ${tryPorts.length} ports are in use (${tryPorts.join(", ")}), failed to start server`);
|
|
497
|
-
(0, logger_js_1.error)("Server start failed", err);
|
|
498
|
-
reject(err);
|
|
499
|
-
return;
|
|
500
|
-
}
|
|
501
|
-
const portToTry = tryPorts[currentIndex];
|
|
502
|
-
currentIndex++;
|
|
503
|
-
(0, logger_js_1.debug)(`Trying to start server on port ${portToTry} (attempt ${currentIndex}/${tryPorts.length})`);
|
|
504
|
-
tryPort(portToTry);
|
|
505
|
-
};
|
|
506
|
-
const tryPort = (portToTry) => {
|
|
507
|
-
// 清除之前的所有监听器
|
|
508
|
-
this.server.removeAllListeners("error");
|
|
509
|
-
this.server.removeAllListeners("listening");
|
|
510
|
-
// 设置错误处理
|
|
511
|
-
const errorHandler = (err) => {
|
|
512
|
-
if (err.code === "EADDRINUSE") {
|
|
513
|
-
(0, logger_js_1.warn)(`Port ${portToTry} is in use, trying next port...`);
|
|
514
|
-
// 清理当前尝试
|
|
515
|
-
this.server.removeAllListeners("error");
|
|
516
|
-
this.server.removeAllListeners("listening");
|
|
517
|
-
tryNextPort();
|
|
518
|
-
}
|
|
519
|
-
else {
|
|
520
|
-
(0, logger_js_1.error)("Server error", err);
|
|
521
|
-
reject(err);
|
|
522
|
-
}
|
|
523
|
-
};
|
|
524
|
-
// Host: default 127.0.0.1 for security (prevents SSRF from remote access); set INTERACTIVE_SERVER_HOST=0.0.0.0 to allow remote connections
|
|
525
|
-
const host = process.env.INTERACTIVE_SERVER_HOST ?? "127.0.0.1";
|
|
526
|
-
// 设置成功监听处理
|
|
527
|
-
const listeningHandler = () => {
|
|
528
|
-
const address = this.server.address();
|
|
529
|
-
if (address && typeof address === "object") {
|
|
530
|
-
this.port = address.port;
|
|
531
|
-
this.isRunning = true;
|
|
532
|
-
(0, logger_js_1.info)(`Interactive server started successfully on http://${host}:${this.port}`);
|
|
533
|
-
// 移除临时监听器
|
|
534
|
-
this.server.removeListener("error", errorHandler);
|
|
535
|
-
this.server.removeListener("listening", listeningHandler);
|
|
536
|
-
resolve(this.port);
|
|
537
|
-
}
|
|
538
|
-
else {
|
|
539
|
-
const err = new Error("Failed to get server address");
|
|
540
|
-
(0, logger_js_1.error)("Server start error", err);
|
|
541
|
-
reject(err);
|
|
542
|
-
}
|
|
543
|
-
};
|
|
544
|
-
this.server.once("error", errorHandler);
|
|
545
|
-
this.server.once("listening", listeningHandler);
|
|
546
|
-
try {
|
|
547
|
-
this.server.listen(portToTry, host);
|
|
548
|
-
}
|
|
549
|
-
catch (err) {
|
|
550
|
-
(0, logger_js_1.error)(`Failed to bind to port ${portToTry}:`, err instanceof Error ? err : new Error(String(err)));
|
|
551
|
-
tryNextPort();
|
|
552
|
-
}
|
|
553
|
-
};
|
|
554
|
-
tryNextPort();
|
|
555
|
-
});
|
|
556
|
-
}
|
|
557
|
-
async stop() {
|
|
558
|
-
if (!this.isRunning) {
|
|
559
|
-
(0, logger_js_1.debug)("Interactive server is not running, nothing to stop");
|
|
560
|
-
return;
|
|
561
|
-
}
|
|
562
|
-
(0, logger_js_1.info)("Stopping interactive server...");
|
|
563
|
-
return new Promise((resolve, reject) => {
|
|
564
|
-
// 设置超时,防止无限等待
|
|
565
|
-
const timeout = setTimeout(() => {
|
|
566
|
-
(0, logger_js_1.warn)("Server close timeout, forcing cleanup");
|
|
567
|
-
this.isRunning = false;
|
|
568
|
-
this.port = 0;
|
|
569
|
-
resolve();
|
|
570
|
-
}, 30000);
|
|
571
|
-
try {
|
|
572
|
-
// 首先关闭WebSocket服务器,等待其完全关闭
|
|
573
|
-
this.wss.close(() => {
|
|
574
|
-
(0, logger_js_1.debug)("WebSocket server closed");
|
|
575
|
-
// WebSocket关闭后,再关闭HTTP服务器
|
|
576
|
-
this.server.close((err) => {
|
|
577
|
-
clearTimeout(timeout);
|
|
578
|
-
if (err) {
|
|
579
|
-
(0, logger_js_1.error)("Error closing server:", err);
|
|
580
|
-
reject(err);
|
|
581
|
-
}
|
|
582
|
-
else {
|
|
583
|
-
(0, logger_js_1.info)("Interactive server stopped successfully");
|
|
584
|
-
this.isRunning = false;
|
|
585
|
-
this.port = 0;
|
|
586
|
-
// 重新创建整个服务器实例以便下次使用
|
|
587
|
-
this.server = http_1.default.createServer(this.app);
|
|
588
|
-
this.applyServerTimeouts();
|
|
589
|
-
this.wss = new ws_1.WebSocketServer({ server: this.server });
|
|
590
|
-
this.setupWebSocket();
|
|
591
|
-
(0, logger_js_1.debug)("HTTP and WebSocket servers recreated for next use");
|
|
592
|
-
resolve();
|
|
593
|
-
}
|
|
594
|
-
});
|
|
595
|
-
});
|
|
596
|
-
}
|
|
597
|
-
catch (err) {
|
|
598
|
-
clearTimeout(timeout);
|
|
599
|
-
(0, logger_js_1.error)("Error stopping server:", err instanceof Error ? err : new Error(String(err)));
|
|
600
|
-
this.isRunning = false;
|
|
601
|
-
this.port = 0;
|
|
602
|
-
reject(err);
|
|
603
|
-
}
|
|
604
|
-
});
|
|
605
|
-
}
|
|
606
|
-
async collectEnvId(availableEnvs, accountInfo, errorContext, // EnvSetupContext
|
|
607
|
-
manager, // CloudBase manager instance for refreshing env list
|
|
608
|
-
mcpServer) {
|
|
609
|
-
try {
|
|
610
|
-
// CRITICAL: Clean up any existing unresolved request to prevent hanging
|
|
611
|
-
if (this.currentResolver) {
|
|
612
|
-
(0, logger_js_1.warn)("[collectEnvId] Found existing unresolved request, cleaning up...");
|
|
613
|
-
const oldResolver = this.currentResolver;
|
|
614
|
-
this.currentResolver = null;
|
|
615
|
-
// Resolve the old request as cancelled to prevent it from hanging forever
|
|
616
|
-
oldResolver({ type: "envId", data: null, cancelled: true });
|
|
617
|
-
}
|
|
618
|
-
// Update mcpServer if provided, or use existing this._mcpServer as fallback
|
|
619
|
-
(0, logger_js_1.debug)(`[collectEnvId] Received mcpServer parameter: type=${typeof mcpServer}, is null=${mcpServer === null}, is undefined=${mcpServer === undefined}, has server=${!!mcpServer?.server}, has ide=${!!mcpServer?.ide}`);
|
|
620
|
-
(0, logger_js_1.debug)(`[collectEnvId] Current this._mcpServer before update: type=${typeof this._mcpServer}, is null=${this._mcpServer === null}, is undefined=${this._mcpServer === undefined}, has server=${!!this._mcpServer?.server}, has ide=${!!this._mcpServer?.ide}`);
|
|
621
|
-
// Use mcpServer parameter if provided, otherwise keep existing this._mcpServer
|
|
622
|
-
const effectiveMcpServer = mcpServer || this._mcpServer;
|
|
623
|
-
if (mcpServer) {
|
|
624
|
-
this._mcpServer = mcpServer;
|
|
625
|
-
(0, logger_js_1.debug)(`[collectEnvId] Updated mcpServer from parameter, has server: ${!!mcpServer?.server}, has ide: ${!!mcpServer?.ide}`);
|
|
626
|
-
}
|
|
627
|
-
else if (this._mcpServer) {
|
|
628
|
-
(0, logger_js_1.debug)(`[collectEnvId] mcpServer parameter is falsy, using existing this._mcpServer`);
|
|
629
|
-
}
|
|
630
|
-
else {
|
|
631
|
-
(0, logger_js_1.warn)(`[collectEnvId] WARNING: Both mcpServer parameter and this._mcpServer are undefined! IDE detection will fail.`);
|
|
632
|
-
}
|
|
633
|
-
(0, logger_js_1.debug)(`[collectEnvId] Effective mcpServer: type=${typeof effectiveMcpServer}, has server=${!!effectiveMcpServer?.server}, has ide=${!!effectiveMcpServer?.ide}`);
|
|
634
|
-
(0, logger_js_1.info)("Starting environment ID collection...");
|
|
635
|
-
(0, logger_js_1.debug)(`Available environments: ${availableEnvs.length}`);
|
|
636
|
-
(0, logger_js_1.debug)(`Account info:`, accountInfo);
|
|
637
|
-
(0, logger_js_1.debug)(`Error context:`, {
|
|
638
|
-
hasInitTcbError: !!errorContext?.initTcbError,
|
|
639
|
-
hasCreateEnvError: !!errorContext?.createEnvError,
|
|
640
|
-
initTcbError: errorContext?.initTcbError,
|
|
641
|
-
createEnvError: errorContext?.createEnvError
|
|
642
|
-
});
|
|
643
|
-
const port = await this.start();
|
|
644
|
-
const sessionId = Math.random().toString(36).substring(2, 15);
|
|
645
|
-
this.sessionData.set(sessionId, {
|
|
646
|
-
envs: availableEnvs,
|
|
647
|
-
accountInfo,
|
|
648
|
-
errorContext, // Store error context
|
|
649
|
-
manager, // Store manager for refreshing env list
|
|
650
|
-
ws: null // Will be set when WebSocket connects
|
|
651
|
-
});
|
|
652
|
-
(0, logger_js_1.debug)(`Created session: ${sessionId}`);
|
|
653
|
-
setTimeout(() => {
|
|
654
|
-
this.sessionData.delete(sessionId);
|
|
655
|
-
(0, logger_js_1.debug)(`Session ${sessionId} expired`);
|
|
656
|
-
}, 5 * 60 * 1000);
|
|
657
|
-
const url = `http://localhost:${port}/env-setup/${sessionId}`;
|
|
658
|
-
(0, logger_js_1.info)(`Opening browser: ${url}`);
|
|
659
|
-
// Check if this is CodeBuddy IDE and notification was sent (no browser opened)
|
|
660
|
-
const isCodeBuddy = effectiveMcpServer?.ide === "CodeBuddy" || process.env.INTEGRATION_IDE === "CodeBuddy";
|
|
661
|
-
let notificationSent = false;
|
|
662
|
-
try {
|
|
663
|
-
// Use effectiveMcpServer (from parameter or this._mcpServer) for openUrl
|
|
664
|
-
(0, logger_js_1.debug)(`[collectEnvId] Using effectiveMcpServer for openUrl: type=${typeof effectiveMcpServer}, has server=${!!effectiveMcpServer?.server}, has ide=${!!effectiveMcpServer?.ide}`);
|
|
665
|
-
(0, logger_js_1.debug)(`[collectEnvId] effectiveMcpServer keys: ${effectiveMcpServer ? Object.keys(effectiveMcpServer).slice(0, 10).join(', ') : 'null'}`);
|
|
666
|
-
// Store the original openUrl to check if it returns early (notification sent)
|
|
667
|
-
const openUrlResult = await openUrl(url, { wait: false }, effectiveMcpServer);
|
|
668
|
-
if (isCodeBuddy && openUrlResult === undefined) {
|
|
669
|
-
notificationSent = true;
|
|
670
|
-
(0, logger_js_1.debug)("[collectEnvId] CodeBuddy notification sent, no browser opened");
|
|
671
|
-
}
|
|
672
|
-
(0, logger_js_1.info)("Browser opened successfully");
|
|
673
|
-
}
|
|
674
|
-
catch (browserError) {
|
|
675
|
-
(0, logger_js_1.error)("Failed to open browser", browserError instanceof Error ? browserError : new Error(String(browserError)));
|
|
676
|
-
(0, logger_js_1.warn)(`Please manually open: ${url}`);
|
|
677
|
-
}
|
|
678
|
-
(0, logger_js_1.info)("Waiting for user selection...");
|
|
679
|
-
// Use same 10 minutes for all IDEs so long login (re-auth, switch account) does not close the server
|
|
680
|
-
const timeoutDuration = 10 * 60 * 1000;
|
|
681
|
-
(0, logger_js_1.debug)(`[collectEnvId] Using timeout duration: ${timeoutDuration / 1000} seconds`);
|
|
682
|
-
return new Promise((resolve) => {
|
|
683
|
-
this.currentResolver = (result) => {
|
|
684
|
-
// 用户选择完成后,关闭服务器
|
|
685
|
-
this.currentResolver = null;
|
|
686
|
-
this.stop().catch((err) => {
|
|
687
|
-
(0, logger_js_1.debug)("Error stopping server after user selection:", err);
|
|
688
|
-
});
|
|
689
|
-
resolve(result);
|
|
690
|
-
};
|
|
691
|
-
const timeoutId = setTimeout(() => {
|
|
692
|
-
if (this.currentResolver) {
|
|
693
|
-
(0, logger_js_1.warn)(`Request timeout after ${timeoutDuration / 1000} seconds, resolving with cancelled`);
|
|
694
|
-
this.currentResolver = null;
|
|
695
|
-
// 超时后也关闭服务器
|
|
696
|
-
this.stop().catch((err) => {
|
|
697
|
-
(0, logger_js_1.debug)("Error stopping server after timeout:", err);
|
|
698
|
-
});
|
|
699
|
-
resolve({ type: "envId", data: null, cancelled: true, timeout: true, timeoutDuration });
|
|
700
|
-
}
|
|
701
|
-
}, timeoutDuration);
|
|
702
|
-
// Store timeout ID so we can clear it if resolved early
|
|
703
|
-
// Note: We can't clear it here, but the timeout will be cleared when the promise resolves
|
|
704
|
-
// The timeout will be automatically cleared when the promise resolves or rejects
|
|
705
|
-
});
|
|
706
|
-
}
|
|
707
|
-
catch (err) {
|
|
708
|
-
// Clean up currentResolver on error
|
|
709
|
-
if (this.currentResolver) {
|
|
710
|
-
this.currentResolver = null;
|
|
711
|
-
}
|
|
712
|
-
(0, logger_js_1.error)("Error in collectEnvId", err instanceof Error ? err : new Error(String(err)));
|
|
713
|
-
throw err;
|
|
714
|
-
}
|
|
715
|
-
}
|
|
716
|
-
async clarifyRequest(message, options) {
|
|
717
|
-
const port = await this.start();
|
|
718
|
-
// 生成会话ID并存储数据
|
|
719
|
-
const sessionId = Math.random().toString(36).substring(2, 15);
|
|
720
|
-
this.sessionData.set(sessionId, { message, options });
|
|
721
|
-
// 设置会话过期时间(5分钟)
|
|
722
|
-
setTimeout(() => {
|
|
723
|
-
this.sessionData.delete(sessionId);
|
|
724
|
-
}, 5 * 60 * 1000);
|
|
725
|
-
const url = `http://localhost:${port}/clarification/${sessionId}`;
|
|
726
|
-
// Pass mcpServer directly - it's an ExtendedMcpServer instance with server property
|
|
727
|
-
await openUrl(url, undefined, this._mcpServer);
|
|
728
|
-
return new Promise((resolve) => {
|
|
729
|
-
this.currentResolver = (result) => {
|
|
730
|
-
// 用户选择完成后,关闭服务器
|
|
731
|
-
this.stop().catch((err) => {
|
|
732
|
-
(0, logger_js_1.debug)("Error stopping server after user selection:", err);
|
|
733
|
-
});
|
|
734
|
-
resolve(result);
|
|
735
|
-
};
|
|
736
|
-
});
|
|
737
|
-
}
|
|
738
|
-
escapeHtml(text) {
|
|
739
|
-
if (!text)
|
|
740
|
-
return '';
|
|
741
|
-
return text
|
|
742
|
-
.replace(/&/g, '&')
|
|
743
|
-
.replace(/</g, '<')
|
|
744
|
-
.replace(/>/g, '>')
|
|
745
|
-
.replace(/"/g, '"')
|
|
746
|
-
.replace(/'/g, ''');
|
|
747
|
-
}
|
|
748
|
-
getEnvSetupHTML(envs, accountInfo, errorContext, // EnvSetupContext
|
|
749
|
-
sessionId) {
|
|
750
|
-
// Extract error information
|
|
751
|
-
const initTcbError = errorContext?.initTcbError;
|
|
752
|
-
const createEnvError = errorContext?.createEnvError;
|
|
753
|
-
(0, logger_js_1.debug)("getEnvSetupHTML called with:", {
|
|
754
|
-
envCount: envs?.length || 0,
|
|
755
|
-
hasInitTcbError: !!initTcbError,
|
|
756
|
-
hasCreateEnvError: !!createEnvError,
|
|
757
|
-
hasAccountInfo: !!accountInfo?.uin,
|
|
758
|
-
sessionId
|
|
759
|
-
});
|
|
760
|
-
// Get current IDE information
|
|
761
|
-
const currentIde = this._mcpServer?.ide || process.env.INTEGRATION_IDE;
|
|
762
|
-
// Use new template system
|
|
763
|
-
return (0, index_js_1.renderEnvSetupPage)({
|
|
764
|
-
envs,
|
|
765
|
-
accountInfo,
|
|
766
|
-
errorContext,
|
|
767
|
-
sessionId,
|
|
768
|
-
wsPort: this.port,
|
|
769
|
-
ide: currentIde
|
|
770
|
-
});
|
|
771
|
-
}
|
|
772
|
-
// Keep the old implementation for reference (can be removed later)
|
|
773
|
-
getEnvSetupHTML_OLD(envs, accountInfo, errorContext, sessionId) {
|
|
774
|
-
const accountDisplay = accountInfo?.uin ? `UIN: ${accountInfo.uin}` : "";
|
|
775
|
-
const hasAccountInfo = !!accountInfo?.uin;
|
|
776
|
-
const hasEnvs = (envs || []).length > 0;
|
|
777
|
-
// Extract error information
|
|
778
|
-
const initTcbError = errorContext?.initTcbError;
|
|
779
|
-
const createEnvError = errorContext?.createEnvError;
|
|
780
|
-
const hasErrors = !!(initTcbError || createEnvError);
|
|
781
|
-
const hasInitError = !!initTcbError;
|
|
782
|
-
return `
|
|
783
|
-
<!DOCTYPE html>
|
|
784
|
-
<html lang="zh-CN">
|
|
785
|
-
<head>
|
|
786
|
-
<meta charset="UTF-8">
|
|
787
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
788
|
-
<title>CloudBase AI Toolkit - 环境配置</title>
|
|
789
|
-
<style>
|
|
790
|
-
@import url('https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500;600&display=swap');
|
|
791
|
-
|
|
792
|
-
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
793
|
-
:root {
|
|
794
|
-
--primary-color: #1a1a1a;
|
|
795
|
-
--primary-hover: #000000;
|
|
796
|
-
--accent-color: #67E9E9;
|
|
797
|
-
--accent-hover: #2BCCCC;
|
|
798
|
-
--text-primary: #ffffff;
|
|
799
|
-
--text-secondary: #a0a0a0;
|
|
800
|
-
--border-color: rgba(255, 255, 255, 0.15);
|
|
801
|
-
--bg-secondary: rgba(255, 255, 255, 0.08);
|
|
802
|
-
--bg-glass: rgba(26, 26, 26, 0.95);
|
|
803
|
-
--shadow: 0 25px 50px rgba(0, 0, 0, 0.3), 0 10px 20px rgba(0, 0, 0, 0.2);
|
|
804
|
-
--font-mono: 'JetBrains Mono', 'SF Mono', 'Monaco', monospace;
|
|
805
|
-
--header-bg: linear-gradient(135deg, #0a0a0a 0%, #1a1a1a 50%, #0d1117 100%);
|
|
806
|
-
}
|
|
807
|
-
|
|
808
|
-
body {
|
|
809
|
-
font-family: var(--font-mono);
|
|
810
|
-
background: linear-gradient(135deg, #0a0a0a 0%, #1a1a1a 100%);
|
|
811
|
-
min-height: 100vh;
|
|
812
|
-
display: flex;
|
|
813
|
-
align-items: center;
|
|
814
|
-
justify-content: center;
|
|
815
|
-
padding: 20px;
|
|
816
|
-
position: relative;
|
|
817
|
-
overflow-x: hidden;
|
|
818
|
-
overflow-y: auto;
|
|
819
|
-
}
|
|
820
|
-
|
|
821
|
-
/* Custom scrollbar styles */
|
|
822
|
-
::-webkit-scrollbar {
|
|
823
|
-
width: 8px;
|
|
824
|
-
}
|
|
825
|
-
|
|
826
|
-
::-webkit-scrollbar-track {
|
|
827
|
-
background: rgba(255, 255, 255, 0.05);
|
|
828
|
-
border-radius: 4px;
|
|
829
|
-
}
|
|
830
|
-
|
|
831
|
-
::-webkit-scrollbar-thumb {
|
|
832
|
-
background: var(--accent-color);
|
|
833
|
-
border-radius: 4px;
|
|
834
|
-
}
|
|
835
|
-
|
|
836
|
-
::-webkit-scrollbar-thumb:hover {
|
|
837
|
-
background: var(--accent-hover);
|
|
838
|
-
}
|
|
839
|
-
|
|
840
|
-
body::before {
|
|
841
|
-
content: '';
|
|
842
|
-
position: fixed;
|
|
843
|
-
top: 0; left: 0; right: 0; bottom: 0;
|
|
844
|
-
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;
|
|
845
|
-
pointer-events: none;
|
|
846
|
-
z-index: -1;
|
|
847
|
-
}
|
|
848
|
-
|
|
849
|
-
body::after {
|
|
850
|
-
content: '';
|
|
851
|
-
position: fixed;
|
|
852
|
-
top: 50%; left: 50%;
|
|
853
|
-
width: 500px; height: 500px;
|
|
854
|
-
background: radial-gradient(circle, rgba(103, 233, 233, 0.05) 0%, transparent 70%);
|
|
855
|
-
transform: translate(-50%, -50%);
|
|
856
|
-
pointer-events: none;
|
|
857
|
-
z-index: -1;
|
|
858
|
-
animation: pulse 8s ease-in-out infinite;
|
|
859
|
-
}
|
|
860
|
-
|
|
861
|
-
@keyframes pulse {
|
|
862
|
-
0%, 100% { opacity: 0.3; transform: translate(-50%, -50%) scale(1); }
|
|
863
|
-
50% { opacity: 0.6; transform: translate(-50%, -50%) scale(1.1); }
|
|
864
|
-
}
|
|
865
|
-
|
|
866
|
-
.modal {
|
|
867
|
-
background: var(--bg-glass);
|
|
868
|
-
backdrop-filter: blur(20px);
|
|
869
|
-
border-radius: 20px;
|
|
870
|
-
box-shadow: var(--shadow);
|
|
871
|
-
border: 2px solid var(--border-color);
|
|
872
|
-
width: 100%;
|
|
873
|
-
max-width: 520px;
|
|
874
|
-
overflow: hidden;
|
|
875
|
-
animation: modalIn 0.6s cubic-bezier(0.175, 0.885, 0.32, 1.275);
|
|
876
|
-
position: relative;
|
|
877
|
-
}
|
|
878
|
-
|
|
879
|
-
.modal::before {
|
|
880
|
-
content: '';
|
|
881
|
-
position: absolute;
|
|
882
|
-
top: 0; left: 0; right: 0; bottom: 0;
|
|
883
|
-
background: linear-gradient(45deg, transparent 30%, rgba(255,255,255,0.02) 50%, transparent 70%);
|
|
884
|
-
animation: shimmer 3s infinite;
|
|
885
|
-
pointer-events: none;
|
|
886
|
-
}
|
|
887
|
-
|
|
888
|
-
@keyframes shimmer {
|
|
889
|
-
0% { transform: translateX(-100%); }
|
|
890
|
-
100% { transform: translateX(100%); }
|
|
891
|
-
}
|
|
892
|
-
|
|
893
|
-
@keyframes modalIn {
|
|
894
|
-
from {
|
|
895
|
-
opacity: 0;
|
|
896
|
-
transform: scale(0.9) translateY(-20px);
|
|
897
|
-
}
|
|
898
|
-
to {
|
|
899
|
-
opacity: 1;
|
|
900
|
-
transform: scale(1) translateY(0);
|
|
901
|
-
}
|
|
902
|
-
}
|
|
903
|
-
|
|
904
|
-
.header {
|
|
905
|
-
background: var(--header-bg);
|
|
906
|
-
color: var(--text-primary);
|
|
907
|
-
padding: 24px 28px;
|
|
908
|
-
display: flex;
|
|
909
|
-
align-items: center;
|
|
910
|
-
justify-content: space-between;
|
|
911
|
-
position: relative;
|
|
912
|
-
overflow: hidden;
|
|
913
|
-
}
|
|
914
|
-
|
|
915
|
-
.header::before {
|
|
916
|
-
content: '';
|
|
917
|
-
position: absolute;
|
|
918
|
-
top: 0; left: 0; right: 0; bottom: 0;
|
|
919
|
-
background: linear-gradient(45deg, transparent 30%, rgba(255,255,255,0.03) 50%, transparent 70%);
|
|
920
|
-
animation: headerShimmer 4s infinite;
|
|
921
|
-
pointer-events: none;
|
|
922
|
-
}
|
|
923
|
-
|
|
924
|
-
@keyframes headerShimmer {
|
|
925
|
-
0% { transform: translateX(-100%); }
|
|
926
|
-
100% { transform: translateX(100%); }
|
|
927
|
-
}
|
|
928
|
-
|
|
929
|
-
.header-left {
|
|
930
|
-
display: flex;
|
|
931
|
-
align-items: center;
|
|
932
|
-
gap: 16px;
|
|
933
|
-
z-index: 1;
|
|
934
|
-
}
|
|
935
|
-
|
|
936
|
-
.logo {
|
|
937
|
-
width: 32px;
|
|
938
|
-
height: 32px;
|
|
939
|
-
filter: drop-shadow(0 4px 8px rgba(0,0,0,0.2));
|
|
940
|
-
animation: logoFloat 3s ease-in-out infinite;
|
|
941
|
-
}
|
|
942
|
-
|
|
943
|
-
@keyframes logoFloat {
|
|
944
|
-
0%, 100% { transform: translateY(0px); }
|
|
945
|
-
50% { transform: translateY(-3px); }
|
|
946
|
-
}
|
|
947
|
-
|
|
948
|
-
.title {
|
|
949
|
-
font-size: 20px;
|
|
950
|
-
font-weight: 700;
|
|
951
|
-
text-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
|
952
|
-
}
|
|
953
|
-
|
|
954
|
-
.github-link {
|
|
955
|
-
color: var(--text-primary);
|
|
956
|
-
text-decoration: none;
|
|
957
|
-
display: flex;
|
|
958
|
-
align-items: center;
|
|
959
|
-
gap: 8px;
|
|
960
|
-
font-size: 14px;
|
|
961
|
-
background: rgba(255,255,255,0.08);
|
|
962
|
-
border: 1px solid rgba(255, 255, 255, 0.12);
|
|
963
|
-
backdrop-filter: blur(10px);
|
|
964
|
-
padding: 8px 16px;
|
|
965
|
-
border-radius: 8px;
|
|
966
|
-
font-weight: 500;
|
|
967
|
-
z-index: 1;
|
|
968
|
-
transition: all 0.3s ease;
|
|
969
|
-
}
|
|
970
|
-
|
|
971
|
-
.github-link:hover {
|
|
972
|
-
background: rgba(255,255,255,0.15);
|
|
973
|
-
transform: translateY(-1px);
|
|
974
|
-
box-shadow: 0 4px 12px rgba(0,0,0,0.3);
|
|
975
|
-
}
|
|
976
|
-
|
|
977
|
-
.content {
|
|
978
|
-
padding: 32px 24px;
|
|
979
|
-
position: relative;
|
|
980
|
-
}
|
|
981
|
-
|
|
982
|
-
.content-title {
|
|
983
|
-
font-size: 24px;
|
|
984
|
-
font-weight: 700;
|
|
985
|
-
color: var(--text-primary);
|
|
986
|
-
margin-bottom: 8px;
|
|
987
|
-
animation: fadeInUp 0.8s ease-out 0.2s both;
|
|
988
|
-
}
|
|
989
|
-
|
|
990
|
-
.content-subtitle {
|
|
991
|
-
color: var(--text-secondary);
|
|
992
|
-
margin-bottom: 16px;
|
|
993
|
-
line-height: 1.5;
|
|
994
|
-
animation: fadeInUp 0.8s ease-out 0.4s both;
|
|
995
|
-
}
|
|
996
|
-
|
|
997
|
-
.account-bar {
|
|
998
|
-
display: flex;
|
|
999
|
-
align-items: center;
|
|
1000
|
-
justify-content: space-between;
|
|
1001
|
-
padding: 10px 14px;
|
|
1002
|
-
background: rgba(103, 233, 233, 0.06);
|
|
1003
|
-
border: 1px solid rgba(103, 233, 233, 0.15);
|
|
1004
|
-
border-radius: 8px;
|
|
1005
|
-
margin-bottom: 20px;
|
|
1006
|
-
animation: fadeInUp 0.8s ease-out 0.5s both;
|
|
1007
|
-
}
|
|
1008
|
-
|
|
1009
|
-
.account-info {
|
|
1010
|
-
display: flex;
|
|
1011
|
-
align-items: center;
|
|
1012
|
-
gap: 10px;
|
|
1013
|
-
font-size: 13px;
|
|
1014
|
-
color: var(--accent-color);
|
|
1015
|
-
}
|
|
1016
|
-
|
|
1017
|
-
.account-info svg {
|
|
1018
|
-
flex-shrink: 0;
|
|
1019
|
-
opacity: 0.8;
|
|
1020
|
-
}
|
|
1021
|
-
|
|
1022
|
-
.account-info span {
|
|
1023
|
-
font-family: var(--font-mono);
|
|
1024
|
-
}
|
|
1025
|
-
|
|
1026
|
-
.btn-switch {
|
|
1027
|
-
display: flex;
|
|
1028
|
-
align-items: center;
|
|
1029
|
-
gap: 6px;
|
|
1030
|
-
padding: 6px 12px;
|
|
1031
|
-
font-size: 12px;
|
|
1032
|
-
background: transparent;
|
|
1033
|
-
border: 1px solid rgba(103, 233, 233, 0.3);
|
|
1034
|
-
border-radius: 6px;
|
|
1035
|
-
color: var(--accent-color);
|
|
1036
|
-
cursor: pointer;
|
|
1037
|
-
transition: all 0.2s ease;
|
|
1038
|
-
font-family: var(--font-mono);
|
|
1039
|
-
}
|
|
1040
|
-
|
|
1041
|
-
.btn-switch:hover {
|
|
1042
|
-
background: rgba(103, 233, 233, 0.1);
|
|
1043
|
-
border-color: var(--accent-color);
|
|
1044
|
-
}
|
|
1045
|
-
|
|
1046
|
-
.btn-switch svg {
|
|
1047
|
-
flex-shrink: 0;
|
|
1048
|
-
}
|
|
1049
|
-
|
|
1050
|
-
@keyframes fadeInUp {
|
|
1051
|
-
from {
|
|
1052
|
-
opacity: 0;
|
|
1053
|
-
transform: translateY(20px);
|
|
1054
|
-
}
|
|
1055
|
-
to {
|
|
1056
|
-
opacity: 1;
|
|
1057
|
-
transform: translateY(0);
|
|
1058
|
-
}
|
|
1059
|
-
}
|
|
1060
|
-
|
|
1061
|
-
.env-list {
|
|
1062
|
-
border: 1px solid var(--border-color);
|
|
1063
|
-
border-radius: 12px;
|
|
1064
|
-
margin-bottom: 24px;
|
|
1065
|
-
max-height: 300px;
|
|
1066
|
-
overflow-y: auto;
|
|
1067
|
-
overflow-x: hidden;
|
|
1068
|
-
background: rgba(255, 255, 255, 0.03);
|
|
1069
|
-
animation: fadeInUp 0.8s ease-out 0.6s both;
|
|
1070
|
-
}
|
|
1071
|
-
|
|
1072
|
-
.env-item {
|
|
1073
|
-
padding: 16px 20px;
|
|
1074
|
-
border-bottom: 1px solid var(--border-color);
|
|
1075
|
-
cursor: pointer;
|
|
1076
|
-
transition: all 0.3s ease;
|
|
1077
|
-
display: flex;
|
|
1078
|
-
align-items: center;
|
|
1079
|
-
gap: 14px;
|
|
1080
|
-
position: relative;
|
|
1081
|
-
overflow: hidden;
|
|
1082
|
-
color: var(--text-primary);
|
|
1083
|
-
}
|
|
1084
|
-
|
|
1085
|
-
.env-item::before {
|
|
1086
|
-
content: '';
|
|
1087
|
-
position: absolute;
|
|
1088
|
-
left: 0; top: 0; bottom: 0;
|
|
1089
|
-
width: 0;
|
|
1090
|
-
background: var(--accent-color);
|
|
1091
|
-
transition: width 0.3s ease;
|
|
1092
|
-
}
|
|
1093
|
-
|
|
1094
|
-
.env-item:last-child {
|
|
1095
|
-
border-bottom: none;
|
|
1096
|
-
}
|
|
1097
|
-
|
|
1098
|
-
.env-item:hover {
|
|
1099
|
-
background: var(--bg-secondary);
|
|
1100
|
-
transform: translateX(5px);
|
|
1101
|
-
}
|
|
1102
|
-
|
|
1103
|
-
.env-item:hover::before {
|
|
1104
|
-
width: 4px;
|
|
1105
|
-
}
|
|
1106
|
-
|
|
1107
|
-
.env-item.selected {
|
|
1108
|
-
background: rgba(103, 233, 233, 0.1);
|
|
1109
|
-
border-left: 4px solid var(--accent-color);
|
|
1110
|
-
transform: translateX(5px);
|
|
1111
|
-
}
|
|
1112
|
-
|
|
1113
|
-
.env-icon {
|
|
1114
|
-
width: 20px;
|
|
1115
|
-
height: 20px;
|
|
1116
|
-
color: var(--accent-color);
|
|
1117
|
-
flex-shrink: 0;
|
|
1118
|
-
animation: iconGlow 2s ease-in-out infinite;
|
|
1119
|
-
}
|
|
1120
|
-
|
|
1121
|
-
@keyframes iconGlow {
|
|
1122
|
-
0%, 100% { filter: drop-shadow(0 0 2px rgba(103, 233, 233, 0.3)); }
|
|
1123
|
-
50% { filter: drop-shadow(0 0 8px rgba(103, 233, 233, 0.6)); }
|
|
1124
|
-
}
|
|
1125
|
-
|
|
1126
|
-
.env-info {
|
|
1127
|
-
flex: 1;
|
|
1128
|
-
}
|
|
1129
|
-
|
|
1130
|
-
.env-name {
|
|
1131
|
-
font-weight: 600;
|
|
1132
|
-
color: var(--text-primary);
|
|
1133
|
-
margin-bottom: 4px;
|
|
1134
|
-
}
|
|
1135
|
-
|
|
1136
|
-
.env-alias {
|
|
1137
|
-
color: var(--text-secondary);
|
|
1138
|
-
font-size: 14px;
|
|
1139
|
-
}
|
|
1140
|
-
|
|
1141
|
-
.empty-state {
|
|
1142
|
-
display: flex;
|
|
1143
|
-
flex-direction: column;
|
|
1144
|
-
align-items: center;
|
|
1145
|
-
justify-content: center;
|
|
1146
|
-
padding: 60px 20px;
|
|
1147
|
-
text-align: center;
|
|
1148
|
-
animation: fadeIn 0.8s ease-out;
|
|
1149
|
-
}
|
|
1150
|
-
|
|
1151
|
-
.empty-icon {
|
|
1152
|
-
margin-bottom: 24px;
|
|
1153
|
-
color: var(--text-secondary);
|
|
1154
|
-
opacity: 0.6;
|
|
1155
|
-
}
|
|
1156
|
-
|
|
1157
|
-
.empty-title {
|
|
1158
|
-
font-size: 20px;
|
|
1159
|
-
font-weight: 600;
|
|
1160
|
-
color: var(--text-primary);
|
|
1161
|
-
margin-bottom: 12px;
|
|
1162
|
-
}
|
|
1163
|
-
|
|
1164
|
-
.empty-message {
|
|
1165
|
-
font-size: 14px;
|
|
1166
|
-
color: var(--text-secondary);
|
|
1167
|
-
line-height: 1.6;
|
|
1168
|
-
margin-bottom: 32px;
|
|
1169
|
-
max-width: 400px;
|
|
1170
|
-
}
|
|
1171
|
-
|
|
1172
|
-
.error-banner {
|
|
1173
|
-
margin-bottom: 24px;
|
|
1174
|
-
padding: 16px;
|
|
1175
|
-
background: rgba(255, 193, 7, 0.1);
|
|
1176
|
-
border: 1px solid rgba(255, 193, 7, 0.3);
|
|
1177
|
-
border-radius: 12px;
|
|
1178
|
-
animation: fadeIn 0.5s ease-out;
|
|
1179
|
-
}
|
|
1180
|
-
|
|
1181
|
-
.error-item {
|
|
1182
|
-
margin-bottom: 16px;
|
|
1183
|
-
}
|
|
1184
|
-
|
|
1185
|
-
.error-item:last-child {
|
|
1186
|
-
margin-bottom: 0;
|
|
1187
|
-
}
|
|
1188
|
-
|
|
1189
|
-
.error-header {
|
|
1190
|
-
display: flex;
|
|
1191
|
-
align-items: center;
|
|
1192
|
-
gap: 8px;
|
|
1193
|
-
margin-bottom: 10px;
|
|
1194
|
-
color: #ffc107;
|
|
1195
|
-
}
|
|
1196
|
-
|
|
1197
|
-
.error-title {
|
|
1198
|
-
font-size: 15px;
|
|
1199
|
-
font-weight: 600;
|
|
1200
|
-
color: #ffc107;
|
|
1201
|
-
}
|
|
1202
|
-
|
|
1203
|
-
.error-message {
|
|
1204
|
-
font-size: 14px;
|
|
1205
|
-
color: var(--text-primary);
|
|
1206
|
-
line-height: 1.6;
|
|
1207
|
-
margin-bottom: 12px;
|
|
1208
|
-
padding-left: 24px;
|
|
1209
|
-
word-wrap: break-word;
|
|
1210
|
-
word-break: break-word;
|
|
1211
|
-
overflow-wrap: break-word;
|
|
1212
|
-
max-width: 100%;
|
|
1213
|
-
}
|
|
1214
|
-
|
|
1215
|
-
.error-action {
|
|
1216
|
-
padding-left: 24px;
|
|
1217
|
-
margin-top: 12px;
|
|
1218
|
-
}
|
|
1219
|
-
|
|
1220
|
-
.error-link {
|
|
1221
|
-
display: inline-flex;
|
|
1222
|
-
align-items: center;
|
|
1223
|
-
gap: 6px;
|
|
1224
|
-
color: var(--accent-color);
|
|
1225
|
-
text-decoration: none;
|
|
1226
|
-
font-size: 14px;
|
|
1227
|
-
font-weight: 500;
|
|
1228
|
-
transition: color 0.2s ease;
|
|
1229
|
-
padding: 8px 16px;
|
|
1230
|
-
background: rgba(103, 233, 233, 0.1);
|
|
1231
|
-
border: 1px solid rgba(103, 233, 233, 0.3);
|
|
1232
|
-
border-radius: 6px;
|
|
1233
|
-
}
|
|
1234
|
-
|
|
1235
|
-
.error-link:hover {
|
|
1236
|
-
color: var(--accent-hover);
|
|
1237
|
-
background: rgba(103, 233, 233, 0.2);
|
|
1238
|
-
border-color: var(--accent-color);
|
|
1239
|
-
}
|
|
1240
|
-
|
|
1241
|
-
.error-retry-btn {
|
|
1242
|
-
margin-left: 12px;
|
|
1243
|
-
cursor: pointer;
|
|
1244
|
-
border: none;
|
|
1245
|
-
font-family: inherit;
|
|
1246
|
-
}
|
|
1247
|
-
|
|
1248
|
-
.error-retry-btn:hover {
|
|
1249
|
-
transform: rotate(180deg);
|
|
1250
|
-
transition: transform 0.3s ease;
|
|
1251
|
-
}
|
|
1252
|
-
|
|
1253
|
-
.error-action {
|
|
1254
|
-
display: flex;
|
|
1255
|
-
align-items: center;
|
|
1256
|
-
flex-wrap: wrap;
|
|
1257
|
-
gap: 8px;
|
|
1258
|
-
}
|
|
1259
|
-
|
|
1260
|
-
.create-env-btn {
|
|
1261
|
-
padding: 14px 24px;
|
|
1262
|
-
font-size: 15px;
|
|
1263
|
-
background: var(--primary-color);
|
|
1264
|
-
color: var(--text-primary);
|
|
1265
|
-
border: 1px solid var(--border-color);
|
|
1266
|
-
border-radius: 8px;
|
|
1267
|
-
cursor: pointer;
|
|
1268
|
-
transition: all 0.3s ease;
|
|
1269
|
-
display: flex;
|
|
1270
|
-
align-items: center;
|
|
1271
|
-
gap: 8px;
|
|
1272
|
-
font-weight: 600;
|
|
1273
|
-
}
|
|
1274
|
-
|
|
1275
|
-
.create-env-btn:hover {
|
|
1276
|
-
background: var(--primary-hover);
|
|
1277
|
-
transform: translateY(-2px);
|
|
1278
|
-
box-shadow: 0 8px 25px rgba(0, 0, 0, 0.3);
|
|
1279
|
-
}
|
|
1280
|
-
|
|
1281
|
-
.actions {
|
|
1282
|
-
display: flex;
|
|
1283
|
-
gap: 12px;
|
|
1284
|
-
justify-content: flex-end;
|
|
1285
|
-
animation: fadeInUp 0.8s ease-out 0.8s both;
|
|
1286
|
-
}
|
|
1287
|
-
|
|
1288
|
-
.btn {
|
|
1289
|
-
padding: 12px 20px;
|
|
1290
|
-
border: none;
|
|
1291
|
-
border-radius: 8px;
|
|
1292
|
-
font-size: 14px;
|
|
1293
|
-
font-weight: 600;
|
|
1294
|
-
cursor: pointer;
|
|
1295
|
-
transition: all 0.3s ease;
|
|
1296
|
-
display: flex;
|
|
1297
|
-
align-items: center;
|
|
1298
|
-
gap: 8px;
|
|
1299
|
-
font-family: var(--font-mono);
|
|
1300
|
-
position: relative;
|
|
1301
|
-
overflow: hidden;
|
|
1302
|
-
}
|
|
1303
|
-
|
|
1304
|
-
.btn::before {
|
|
1305
|
-
content: '';
|
|
1306
|
-
position: absolute;
|
|
1307
|
-
top: 50%; left: 50%;
|
|
1308
|
-
width: 0; height: 0;
|
|
1309
|
-
background: rgba(255,255,255,0.2);
|
|
1310
|
-
border-radius: 50%;
|
|
1311
|
-
transition: all 0.3s ease;
|
|
1312
|
-
transform: translate(-50%, -50%);
|
|
1313
|
-
}
|
|
1314
|
-
|
|
1315
|
-
.btn:hover::before {
|
|
1316
|
-
width: 100px; height: 100px;
|
|
1317
|
-
}
|
|
1318
|
-
|
|
1319
|
-
.btn-primary {
|
|
1320
|
-
background: var(--primary-color);
|
|
1321
|
-
color: var(--text-primary);
|
|
1322
|
-
border: 1px solid var(--border-color);
|
|
1323
|
-
}
|
|
1324
|
-
|
|
1325
|
-
.btn-primary:hover:not(:disabled) {
|
|
1326
|
-
background: var(--primary-hover);
|
|
1327
|
-
transform: translateY(-2px);
|
|
1328
|
-
box-shadow: 0 8px 25px rgba(0, 0, 0, 0.3);
|
|
1329
|
-
}
|
|
1330
|
-
|
|
1331
|
-
.btn-secondary {
|
|
1332
|
-
background: var(--bg-secondary);
|
|
1333
|
-
color: var(--text-secondary);
|
|
1334
|
-
border: 1px solid var(--border-color);
|
|
1335
|
-
}
|
|
1336
|
-
|
|
1337
|
-
.btn-secondary:hover {
|
|
1338
|
-
background: rgba(255, 255, 255, 0.15);
|
|
1339
|
-
color: var(--text-primary);
|
|
1340
|
-
}
|
|
1341
|
-
|
|
1342
|
-
.btn:disabled {
|
|
1343
|
-
opacity: 0.5;
|
|
1344
|
-
cursor: not-allowed;
|
|
1345
|
-
}
|
|
1346
|
-
|
|
1347
|
-
.loading {
|
|
1348
|
-
display: none;
|
|
1349
|
-
align-items: center;
|
|
1350
|
-
justify-content: center;
|
|
1351
|
-
gap: 8px;
|
|
1352
|
-
margin-top: 16px;
|
|
1353
|
-
color: var(--text-secondary);
|
|
1354
|
-
font-size: 14px;
|
|
1355
|
-
}
|
|
1356
|
-
|
|
1357
|
-
.spinner {
|
|
1358
|
-
width: 16px;
|
|
1359
|
-
height: 16px;
|
|
1360
|
-
border: 2px solid var(--border-color);
|
|
1361
|
-
border-top: 2px solid var(--accent-color);
|
|
1362
|
-
border-radius: 50%;
|
|
1363
|
-
animation: spin 1s linear infinite;
|
|
1364
|
-
}
|
|
1365
|
-
|
|
1366
|
-
@keyframes spin {
|
|
1367
|
-
0% { transform: rotate(0deg); }
|
|
1368
|
-
100% { transform: rotate(360deg); }
|
|
1369
|
-
}
|
|
1370
|
-
|
|
1371
|
-
@keyframes spin {
|
|
1372
|
-
0% { transform: rotate(0deg); }
|
|
1373
|
-
100% { transform: rotate(360deg); }
|
|
1374
|
-
}
|
|
1375
|
-
|
|
1376
|
-
.success-state {
|
|
1377
|
-
text-align: center;
|
|
1378
|
-
padding: 40px 20px;
|
|
1379
|
-
animation: fadeInUp 0.8s ease-out both;
|
|
1380
|
-
}
|
|
1381
|
-
|
|
1382
|
-
.success-icon {
|
|
1383
|
-
margin-bottom: 20px;
|
|
1384
|
-
color: var(--accent-color);
|
|
1385
|
-
animation: successPulse 2s ease-in-out infinite;
|
|
1386
|
-
}
|
|
1387
|
-
|
|
1388
|
-
@keyframes successPulse {
|
|
1389
|
-
0%, 100% {
|
|
1390
|
-
transform: scale(1);
|
|
1391
|
-
filter: drop-shadow(0 0 8px rgba(103, 233, 233, 0.3));
|
|
1392
|
-
}
|
|
1393
|
-
50% {
|
|
1394
|
-
transform: scale(1.1);
|
|
1395
|
-
filter: drop-shadow(0 0 16px rgba(103, 233, 233, 0.6));
|
|
1396
|
-
}
|
|
1397
|
-
}
|
|
1398
|
-
|
|
1399
|
-
.success-title {
|
|
1400
|
-
font-size: 24px;
|
|
1401
|
-
font-weight: 700;
|
|
1402
|
-
color: var(--text-primary);
|
|
1403
|
-
margin-bottom: 12px;
|
|
1404
|
-
}
|
|
1405
|
-
|
|
1406
|
-
.success-message {
|
|
1407
|
-
color: var(--text-secondary);
|
|
1408
|
-
font-size: 16px;
|
|
1409
|
-
line-height: 1.5;
|
|
1410
|
-
}
|
|
1411
|
-
|
|
1412
|
-
.selected-env-info {
|
|
1413
|
-
margin-top: 20px;
|
|
1414
|
-
padding: 16px;
|
|
1415
|
-
background: rgba(103, 233, 233, 0.1);
|
|
1416
|
-
border: 1px solid var(--accent-color);
|
|
1417
|
-
border-radius: 12px;
|
|
1418
|
-
display: flex;
|
|
1419
|
-
align-items: center;
|
|
1420
|
-
gap: 12px;
|
|
1421
|
-
}
|
|
1422
|
-
|
|
1423
|
-
.env-label {
|
|
1424
|
-
color: var(--text-secondary);
|
|
1425
|
-
font-size: 14px;
|
|
1426
|
-
font-weight: 500;
|
|
1427
|
-
}
|
|
1428
|
-
|
|
1429
|
-
.env-value {
|
|
1430
|
-
color: var(--accent-color);
|
|
1431
|
-
font-size: 16px;
|
|
1432
|
-
font-weight: 600;
|
|
1433
|
-
font-family: var(--font-mono);
|
|
1434
|
-
}
|
|
1435
|
-
</style>
|
|
1436
|
-
</head>
|
|
1437
|
-
<body>
|
|
1438
|
-
<div class="modal">
|
|
1439
|
-
<div class="header">
|
|
1440
|
-
<div class="header-left">
|
|
1441
|
-
<img class="logo" src="https://7463-tcb-advanced-a656fc-1257967285.tcb.qcloud.la/mcp/cloudbase-logo.svg" alt="CloudBase Logo" />
|
|
1442
|
-
<span class="title">CloudBase AI Toolkit</span>
|
|
1443
|
-
</div>
|
|
1444
|
-
<a href="https://github.com/TencentCloudBase/CloudBase-AI-ToolKit" target="_blank" class="github-link">
|
|
1445
|
-
<svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
|
|
1446
|
-
<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"/>
|
|
1447
|
-
</svg>
|
|
1448
|
-
GitHub
|
|
1449
|
-
</a>
|
|
1450
|
-
</div>
|
|
1451
|
-
|
|
1452
|
-
<div class="content">
|
|
1453
|
-
<h1 class="content-title">选择 CloudBase 环境</h1>
|
|
1454
|
-
<p class="content-subtitle">请选择您要使用的 CloudBase 环境</p>
|
|
1455
|
-
|
|
1456
|
-
${(hasAccountInfo || hasEnvs) ? `
|
|
1457
|
-
${(hasAccountInfo || hasEnvs) ? `
|
|
1458
|
-
<div class="account-bar">
|
|
1459
|
-
<div class="account-info">
|
|
1460
|
-
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
1461
|
-
<path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"/>
|
|
1462
|
-
<circle cx="12" cy="7" r="4"/>
|
|
1463
|
-
</svg>
|
|
1464
|
-
<span>${accountDisplay ? `当前账号: ${accountDisplay}` : '未登录'}</span>
|
|
1465
|
-
</div>
|
|
1466
|
-
<button class="btn btn-switch" onclick="switchAccount()">
|
|
1467
|
-
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
1468
|
-
<path d="M16 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"/>
|
|
1469
|
-
<circle cx="8.5" cy="7" r="4"/>
|
|
1470
|
-
<path d="M20 8v6M23 11h-6"/>
|
|
1471
|
-
</svg>
|
|
1472
|
-
切换账号
|
|
1473
|
-
</button>
|
|
1474
|
-
</div>
|
|
1475
|
-
` : ''}
|
|
1476
|
-
` : ''}
|
|
1477
|
-
|
|
1478
|
-
${hasErrors ? `
|
|
1479
|
-
<div class="error-banner" id="errorBanner">
|
|
1480
|
-
${initTcbError ? `
|
|
1481
|
-
<div class="error-item">
|
|
1482
|
-
<div class="error-header">
|
|
1483
|
-
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
1484
|
-
<circle cx="12" cy="12" r="10"/>
|
|
1485
|
-
<line x1="12" y1="8" x2="12" y2="12"/>
|
|
1486
|
-
<line x1="12" y1="16" x2="12.01" y2="16"/>
|
|
1487
|
-
</svg>
|
|
1488
|
-
<span class="error-title">CloudBase 服务初始化失败</span>
|
|
1489
|
-
</div>
|
|
1490
|
-
<div class="error-message">${this.escapeHtml(initTcbError.message)}</div>
|
|
1491
|
-
${initTcbError.needRealNameAuth ? `
|
|
1492
|
-
<div class="error-action">
|
|
1493
|
-
<a href="${initTcbError.helpUrl || 'https://buy.cloud.tencent.com/lowcode?buyType=tcb&channel=mcp'}" target="_blank" class="error-link">
|
|
1494
|
-
前往实名认证
|
|
1495
|
-
</a>
|
|
1496
|
-
</div>
|
|
1497
|
-
` : ''}
|
|
1498
|
-
${initTcbError.needCamAuth ? `
|
|
1499
|
-
<div class="error-action">
|
|
1500
|
-
<a href="${initTcbError.helpUrl || 'https://buy.cloud.tencent.com/lowcode?buyType=tcb&channel=mcp'}" target="_blank" class="error-link">
|
|
1501
|
-
前往开通 CloudBase 服务
|
|
1502
|
-
</a>
|
|
1503
|
-
${sessionId ? `
|
|
1504
|
-
<button class="error-link error-retry-btn" onclick="retryInitTcb('${sessionId}')">
|
|
1505
|
-
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
1506
|
-
<path d="M23 4v6h-6M1 20v-6h6M3.51 9a9 9 0 0 1 14.85-3.36L23 10M1 14l4.64 4.36A9 9 0 0 0 20.49 15"/>
|
|
1507
|
-
</svg>
|
|
1508
|
-
重试
|
|
1509
|
-
</button>
|
|
1510
|
-
` : ''}
|
|
1511
|
-
</div>
|
|
1512
|
-
` : ''}
|
|
1513
|
-
${!initTcbError.needRealNameAuth && !initTcbError.needCamAuth && initTcbError.helpUrl ? `
|
|
1514
|
-
<div class="error-action">
|
|
1515
|
-
<a href="${initTcbError.helpUrl}" target="_blank" class="error-link">
|
|
1516
|
-
前往开通 CloudBase 服务
|
|
1517
|
-
</a>
|
|
1518
|
-
${sessionId ? `
|
|
1519
|
-
<button class="error-link error-retry-btn" onclick="retryInitTcb('${sessionId}')">
|
|
1520
|
-
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
1521
|
-
<path d="M23 4v6h-6M1 20v-6h6M3.51 9a9 9 0 0 1 14.85-3.36L23 10M1 14l4.64 4.36A9 9 0 0 0 20.49 15"/>
|
|
1522
|
-
</svg>
|
|
1523
|
-
重试
|
|
1524
|
-
</button>
|
|
1525
|
-
` : ''}
|
|
1526
|
-
</div>
|
|
1527
|
-
` : ''}
|
|
1528
|
-
</div>
|
|
1529
|
-
` : ''}
|
|
1530
|
-
${createEnvError ? `
|
|
1531
|
-
<div class="error-item">
|
|
1532
|
-
<div class="error-header">
|
|
1533
|
-
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
1534
|
-
<circle cx="12" cy="12" r="10"/>
|
|
1535
|
-
<line x1="12" y1="8" x2="12" y2="12"/>
|
|
1536
|
-
<line x1="12" y1="16" x2="12.01" y2="16"/>
|
|
1537
|
-
</svg>
|
|
1538
|
-
<span class="error-title">免费环境创建失败</span>
|
|
1539
|
-
</div>
|
|
1540
|
-
<div class="error-message">${this.escapeHtml(createEnvError.message)}</div>
|
|
1541
|
-
${createEnvError.helpUrl ? `
|
|
1542
|
-
<div class="error-action">
|
|
1543
|
-
<a href="${createEnvError.helpUrl}" target="_blank" class="error-link">
|
|
1544
|
-
手动创建环境
|
|
1545
|
-
</a>
|
|
1546
|
-
</div>
|
|
1547
|
-
` : ''}
|
|
1548
|
-
</div>
|
|
1549
|
-
` : ''}
|
|
1550
|
-
</div>
|
|
1551
|
-
` : ''}
|
|
1552
|
-
|
|
1553
|
-
<div class="env-list" id="envList">
|
|
1554
|
-
${(envs || []).length > 0
|
|
1555
|
-
? (envs || [])
|
|
1556
|
-
.map((env, index) => `
|
|
1557
|
-
<div class="env-item" onclick="selectEnv('${env.EnvId}', this)" style="animation-delay: ${index * 0.1}s;">
|
|
1558
|
-
<svg class="env-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
1559
|
-
<path d="M13 2L3 14h9l-1 8 10-12h-9l1-8z"/>
|
|
1560
|
-
</svg>
|
|
1561
|
-
<div class="env-info">
|
|
1562
|
-
<div class="env-name">${env.EnvId}</div>
|
|
1563
|
-
<div class="env-alias">${env.Alias || "无别名"}</div>
|
|
1564
|
-
</div>
|
|
1565
|
-
</div>
|
|
1566
|
-
`)
|
|
1567
|
-
.join("")
|
|
1568
|
-
: `
|
|
1569
|
-
<div class="empty-state">
|
|
1570
|
-
<h3 class="empty-title">暂无 CloudBase 环境</h3>
|
|
1571
|
-
${hasInitError ? `
|
|
1572
|
-
<p class="empty-message">由于 CloudBase 服务初始化失败,无法创建新环境。请先解决初始化问题后重试。</p>
|
|
1573
|
-
` : `
|
|
1574
|
-
<p class="empty-message">当前没有可用的 CloudBase 环境,请新建后重新在 AI 对话中重试</p>
|
|
1575
|
-
<button class="btn btn-primary create-env-btn" onclick="createNewEnv()">
|
|
1576
|
-
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
1577
|
-
<path d="M12 5v14M5 12h14"/>
|
|
1578
|
-
</svg>
|
|
1579
|
-
新建环境
|
|
1580
|
-
</button>
|
|
1581
|
-
`}
|
|
1582
|
-
</div>
|
|
1583
|
-
`}
|
|
1584
|
-
</div>
|
|
1585
|
-
|
|
1586
|
-
<div class="actions">
|
|
1587
|
-
<button class="btn btn-secondary" onclick="cancel()">
|
|
1588
|
-
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
1589
|
-
<path d="M18 6L6 18M6 6l12 12"/>
|
|
1590
|
-
</svg>
|
|
1591
|
-
取消
|
|
1592
|
-
</button>
|
|
1593
|
-
<button class="btn btn-primary" id="confirmBtn" onclick="confirm()" disabled>
|
|
1594
|
-
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
1595
|
-
<path d="M20 6L9 17l-5-5"/>
|
|
1596
|
-
</svg>
|
|
1597
|
-
确认选择
|
|
1598
|
-
</button>
|
|
1599
|
-
</div>
|
|
1600
|
-
|
|
1601
|
-
<div class="loading" id="loading">
|
|
1602
|
-
<div class="spinner"></div>
|
|
1603
|
-
<span>正在配置环境...</span>
|
|
1604
|
-
</div>
|
|
1605
|
-
|
|
1606
|
-
<div class="success-state" id="successState" style="display: none;">
|
|
1607
|
-
<div class="success-icon">
|
|
1608
|
-
<svg width="48" height="48" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
1609
|
-
<path d="M20 6L9 17l-5-5"/>
|
|
1610
|
-
</svg>
|
|
1611
|
-
</div>
|
|
1612
|
-
<h2 class="success-title">环境配置成功!</h2>
|
|
1613
|
-
<p class="success-message">已成功选择 CloudBase 环境</p>
|
|
1614
|
-
<div class="selected-env-info">
|
|
1615
|
-
<span class="env-label">环境 ID:</span>
|
|
1616
|
-
<span class="env-value" id="selectedEnvDisplay"></span>
|
|
1617
|
-
</div>
|
|
1618
|
-
</div>
|
|
1619
|
-
</div>
|
|
1620
|
-
</div>
|
|
1621
|
-
|
|
1622
|
-
<script>
|
|
1623
|
-
let selectedEnvId = null;
|
|
1624
|
-
|
|
1625
|
-
function selectEnv(envId, element) {
|
|
1626
|
-
console.log('=== 环境选择事件触发 ===');
|
|
1627
|
-
console.log('传入的envId:', envId);
|
|
1628
|
-
console.log('传入的element:', element);
|
|
1629
|
-
console.log('element类名:', element ? element.className : 'null');
|
|
1630
|
-
|
|
1631
|
-
selectedEnvId = envId;
|
|
1632
|
-
console.log('设置selectedEnvId为:', selectedEnvId);
|
|
1633
|
-
|
|
1634
|
-
// Remove selected class from all items
|
|
1635
|
-
const allItems = document.querySelectorAll('.env-item');
|
|
1636
|
-
console.log('找到的所有环境项数量:', allItems.length);
|
|
1637
|
-
allItems.forEach(item => {
|
|
1638
|
-
item.classList.remove('selected');
|
|
1639
|
-
});
|
|
1640
|
-
|
|
1641
|
-
// Add selected class to current item
|
|
1642
|
-
if (element) {
|
|
1643
|
-
element.classList.add('selected');
|
|
1644
|
-
console.log('✅ 已添加selected样式到当前项');
|
|
1645
|
-
console.log('当前项的最终类名:', element.className);
|
|
1646
|
-
} else {
|
|
1647
|
-
console.error('❌ element为空,无法添加选中样式');
|
|
1648
|
-
}
|
|
1649
|
-
|
|
1650
|
-
// Enable confirm button
|
|
1651
|
-
const confirmBtn = document.getElementById('confirmBtn');
|
|
1652
|
-
if (confirmBtn) {
|
|
1653
|
-
confirmBtn.disabled = false;
|
|
1654
|
-
console.log('✅ 确认按钮已启用');
|
|
1655
|
-
} else {
|
|
1656
|
-
console.error('❌ 找不到确认按钮');
|
|
1657
|
-
}
|
|
1658
|
-
}
|
|
1659
|
-
|
|
1660
|
-
function confirm() {
|
|
1661
|
-
console.log('=== CONFIRM BUTTON CLICKED ===');
|
|
1662
|
-
console.log('selectedEnvId:', selectedEnvId);
|
|
1663
|
-
|
|
1664
|
-
if (!selectedEnvId) {
|
|
1665
|
-
console.error('❌ 没有选择环境ID!');
|
|
1666
|
-
alert('请先选择一个环境');
|
|
1667
|
-
return;
|
|
1668
|
-
}
|
|
1669
|
-
|
|
1670
|
-
console.log('✅ 环境ID验证通过,开始发送请求...');
|
|
1671
|
-
document.getElementById('loading').style.display = 'flex';
|
|
1672
|
-
document.getElementById('confirmBtn').disabled = true;
|
|
1673
|
-
|
|
1674
|
-
const requestBody = {
|
|
1675
|
-
type: 'envId',
|
|
1676
|
-
data: selectedEnvId
|
|
1677
|
-
};
|
|
1678
|
-
|
|
1679
|
-
console.log('📤 发送请求体:', JSON.stringify(requestBody, null, 2));
|
|
1680
|
-
|
|
1681
|
-
fetch('/api/submit', {
|
|
1682
|
-
method: 'POST',
|
|
1683
|
-
headers: { 'Content-Type': 'application/json' },
|
|
1684
|
-
body: JSON.stringify(requestBody)
|
|
1685
|
-
}).then(response => {
|
|
1686
|
-
console.log('📥 收到响应状态:', response.status);
|
|
1687
|
-
console.log('📥 响应头:', [...response.headers.entries()]);
|
|
1688
|
-
return response.json();
|
|
1689
|
-
}).then(data => {
|
|
1690
|
-
console.log('📥 响应数据:', data);
|
|
1691
|
-
if (data.success) {
|
|
1692
|
-
console.log('✅ 请求成功,展示成功提示');
|
|
1693
|
-
// 隐藏选择区和按钮,仅展示成功提示
|
|
1694
|
-
document.getElementById('envList').style.display = 'none';
|
|
1695
|
-
document.querySelector('.actions').style.display = 'none';
|
|
1696
|
-
document.getElementById('loading').style.display = 'none';
|
|
1697
|
-
document.getElementById('successState').style.display = 'block';
|
|
1698
|
-
// 显示选中的环境 ID
|
|
1699
|
-
document.getElementById('selectedEnvDisplay').textContent = selectedEnvId;
|
|
1700
|
-
window.close();
|
|
1701
|
-
} else {
|
|
1702
|
-
console.error('❌ 请求失败:', data);
|
|
1703
|
-
alert('选择环境失败: ' + (data.error || '未知错误'));
|
|
1704
|
-
document.getElementById('loading').style.display = 'none';
|
|
1705
|
-
document.getElementById('confirmBtn').disabled = false;
|
|
1706
|
-
}
|
|
1707
|
-
}).catch(err => {
|
|
1708
|
-
console.error('❌ 网络请求错误:', err);
|
|
1709
|
-
alert('网络请求失败: ' + err.message);
|
|
1710
|
-
document.getElementById('loading').style.display = 'none';
|
|
1711
|
-
document.getElementById('confirmBtn').disabled = false;
|
|
1712
|
-
});
|
|
1713
|
-
}
|
|
1714
|
-
|
|
1715
|
-
function createNewEnv() {
|
|
1716
|
-
const integrationIde = '${process.env.INTEGRATION_IDE || "AI Toolkit"}';
|
|
1717
|
-
const url = \`${(0, site_map_js_1.getConsoleDevUrl)(undefined)}?from=\${encodeURIComponent(integrationIde)}\`;
|
|
1718
|
-
location.href = url;
|
|
1719
|
-
}
|
|
1720
|
-
|
|
1721
|
-
function cancel() {
|
|
1722
|
-
fetch('/api/cancel', {
|
|
1723
|
-
method: 'POST',
|
|
1724
|
-
headers: { 'Content-Type': 'application/json' }
|
|
1725
|
-
}).then(() => {
|
|
1726
|
-
window.close();
|
|
1727
|
-
});
|
|
1728
|
-
}
|
|
1729
|
-
|
|
1730
|
-
function switchAccount() {
|
|
1731
|
-
fetch('/api/switch', {
|
|
1732
|
-
method: 'POST',
|
|
1733
|
-
headers: { 'Content-Type': 'application/json' }
|
|
1734
|
-
}).then(() => {
|
|
1735
|
-
// 页面会由服务端重新打开新的登录页面
|
|
1736
|
-
window.close();
|
|
1737
|
-
});
|
|
1738
|
-
}
|
|
1739
|
-
|
|
1740
|
-
function retryInitTcb(sessionId) {
|
|
1741
|
-
const retryBtn = event.target.closest('.error-retry-btn');
|
|
1742
|
-
if (retryBtn) {
|
|
1743
|
-
retryBtn.disabled = true;
|
|
1744
|
-
retryBtn.innerHTML = '<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="10"/><path d="M12 6v6l4 2"/></svg> 重试中...';
|
|
1745
|
-
}
|
|
1746
|
-
|
|
1747
|
-
fetch('/api/retry-init-tcb', {
|
|
1748
|
-
method: 'POST',
|
|
1749
|
-
headers: { 'Content-Type': 'application/json' },
|
|
1750
|
-
body: JSON.stringify({ sessionId })
|
|
1751
|
-
}).then(response => response.json())
|
|
1752
|
-
.then(result => {
|
|
1753
|
-
if (result.success) {
|
|
1754
|
-
// Refresh page to retry initialization
|
|
1755
|
-
setTimeout(() => {
|
|
1756
|
-
window.location.reload();
|
|
1757
|
-
}, 500);
|
|
1758
|
-
} else {
|
|
1759
|
-
alert('重试失败,请稍后再试');
|
|
1760
|
-
if (retryBtn) {
|
|
1761
|
-
retryBtn.disabled = false;
|
|
1762
|
-
retryBtn.innerHTML = '<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M23 4v6h-6M1 20v-6h6M3.51 9a9 9 0 0 1 14.85-3.36L23 10M1 14l4.64 4.36A9 9 0 0 0 20.49 15"/></svg> 重试';
|
|
1763
|
-
}
|
|
1764
|
-
}
|
|
1765
|
-
})
|
|
1766
|
-
.catch(err => {
|
|
1767
|
-
console.error('Retry failed:', err);
|
|
1768
|
-
alert('重试失败,请稍后再试');
|
|
1769
|
-
if (retryBtn) {
|
|
1770
|
-
retryBtn.disabled = false;
|
|
1771
|
-
retryBtn.innerHTML = '<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M23 4v6h-6M1 20v-6h6M3.51 9a9 9 0 0 1 14.85-3.36L23 10M1 14l4.64 4.36A9 9 0 0 0 20.49 15"/></svg> 重试';
|
|
1772
|
-
}
|
|
1773
|
-
});
|
|
1774
|
-
}
|
|
1775
|
-
</script>
|
|
1776
|
-
</body>
|
|
1777
|
-
</html>`;
|
|
1778
|
-
}
|
|
1779
|
-
getClarificationHTML(message, options) {
|
|
1780
|
-
const optionsArray = options || null;
|
|
1781
|
-
return `
|
|
1782
|
-
<!DOCTYPE html>
|
|
1783
|
-
<html lang="zh-CN">
|
|
1784
|
-
<head>
|
|
1785
|
-
<meta charset="UTF-8">
|
|
1786
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
1787
|
-
<title>CloudBase AI Toolkit - 需求澄清</title>
|
|
1788
|
-
<style>
|
|
1789
|
-
@import url('https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500;600&display=swap');
|
|
1790
|
-
|
|
1791
|
-
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
1792
|
-
:root {
|
|
1793
|
-
--primary-color: #1a1a1a;
|
|
1794
|
-
--primary-hover: #000000;
|
|
1795
|
-
--accent-color: #67E9E9;
|
|
1796
|
-
--accent-hover: #2BCCCC;
|
|
1797
|
-
--text-primary: #ffffff;
|
|
1798
|
-
--text-secondary: #a0a0a0;
|
|
1799
|
-
--border-color: rgba(255, 255, 255, 0.15);
|
|
1800
|
-
--bg-secondary: rgba(255, 255, 255, 0.08);
|
|
1801
|
-
--bg-glass: rgba(26, 26, 26, 0.95);
|
|
1802
|
-
--shadow: 0 25px 50px rgba(0, 0, 0, 0.3), 0 10px 20px rgba(0, 0, 0, 0.2);
|
|
1803
|
-
--font-mono: 'JetBrains Mono', 'SF Mono', 'Monaco', monospace;
|
|
1804
|
-
--header-bg: linear-gradient(135deg, #0a0a0a 0%, #1a1a1a 50%, #0d1117 100%);
|
|
1805
|
-
}
|
|
1806
|
-
|
|
1807
|
-
body {
|
|
1808
|
-
font-family: var(--font-mono);
|
|
1809
|
-
background: linear-gradient(135deg, #0a0a0a 0%, #1a1a1a 100%);
|
|
1810
|
-
min-height: 100vh;
|
|
1811
|
-
display: flex;
|
|
1812
|
-
align-items: center;
|
|
1813
|
-
justify-content: center;
|
|
1814
|
-
padding: 20px;
|
|
1815
|
-
position: relative;
|
|
1816
|
-
overflow-x: hidden;
|
|
1817
|
-
overflow-y: auto;
|
|
1818
|
-
}
|
|
1819
|
-
|
|
1820
|
-
/* Custom scrollbar styles */
|
|
1821
|
-
::-webkit-scrollbar {
|
|
1822
|
-
width: 8px;
|
|
1823
|
-
}
|
|
1824
|
-
|
|
1825
|
-
::-webkit-scrollbar-track {
|
|
1826
|
-
background: rgba(255, 255, 255, 0.05);
|
|
1827
|
-
border-radius: 4px;
|
|
1828
|
-
}
|
|
1829
|
-
|
|
1830
|
-
::-webkit-scrollbar-thumb {
|
|
1831
|
-
background: var(--accent-color);
|
|
1832
|
-
border-radius: 4px;
|
|
1833
|
-
}
|
|
1834
|
-
|
|
1835
|
-
::-webkit-scrollbar-thumb:hover {
|
|
1836
|
-
background: var(--accent-hover);
|
|
1837
|
-
}
|
|
1838
|
-
|
|
1839
|
-
body::before {
|
|
1840
|
-
content: '';
|
|
1841
|
-
position: fixed;
|
|
1842
|
-
top: 0; left: 0; right: 0; bottom: 0;
|
|
1843
|
-
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;
|
|
1844
|
-
pointer-events: none;
|
|
1845
|
-
z-index: -1;
|
|
1846
|
-
}
|
|
1847
|
-
|
|
1848
|
-
body::after {
|
|
1849
|
-
content: '';
|
|
1850
|
-
position: fixed;
|
|
1851
|
-
top: 50%; left: 50%;
|
|
1852
|
-
width: 500px; height: 500px;
|
|
1853
|
-
background: radial-gradient(circle, rgba(103, 233, 233, 0.05) 0%, transparent 70%);
|
|
1854
|
-
transform: translate(-50%, -50%);
|
|
1855
|
-
pointer-events: none;
|
|
1856
|
-
z-index: -1;
|
|
1857
|
-
animation: pulse 8s ease-in-out infinite;
|
|
1858
|
-
}
|
|
1859
|
-
|
|
1860
|
-
@keyframes pulse {
|
|
1861
|
-
0%, 100% { opacity: 0.3; transform: translate(-50%, -50%) scale(1); }
|
|
1862
|
-
50% { opacity: 0.6; transform: translate(-50%, -50%) scale(1.1); }
|
|
1863
|
-
}
|
|
1864
|
-
|
|
1865
|
-
.modal {
|
|
1866
|
-
background: var(--bg-glass);
|
|
1867
|
-
backdrop-filter: blur(20px);
|
|
1868
|
-
border-radius: 20px;
|
|
1869
|
-
box-shadow: var(--shadow);
|
|
1870
|
-
border: 2px solid var(--border-color);
|
|
1871
|
-
width: 100%;
|
|
1872
|
-
max-width: 600px;
|
|
1873
|
-
overflow: hidden;
|
|
1874
|
-
animation: modalIn 0.6s cubic-bezier(0.175, 0.885, 0.32, 1.275);
|
|
1875
|
-
position: relative;
|
|
1876
|
-
}
|
|
1877
|
-
|
|
1878
|
-
.modal::before {
|
|
1879
|
-
content: '';
|
|
1880
|
-
position: absolute;
|
|
1881
|
-
top: 0; left: 0; right: 0; bottom: 0;
|
|
1882
|
-
background: linear-gradient(45deg, transparent 30%, rgba(255,255,255,0.02) 50%, transparent 70%);
|
|
1883
|
-
animation: shimmer 3s infinite;
|
|
1884
|
-
pointer-events: none;
|
|
1885
|
-
}
|
|
1886
|
-
|
|
1887
|
-
@keyframes shimmer {
|
|
1888
|
-
0% { transform: translateX(-100%); }
|
|
1889
|
-
100% { transform: translateX(100%); }
|
|
1890
|
-
}
|
|
1891
|
-
|
|
1892
|
-
@keyframes modalIn {
|
|
1893
|
-
from {
|
|
1894
|
-
opacity: 0;
|
|
1895
|
-
transform: scale(0.9) translateY(-20px);
|
|
1896
|
-
}
|
|
1897
|
-
to {
|
|
1898
|
-
opacity: 1;
|
|
1899
|
-
transform: scale(1) translateY(0);
|
|
1900
|
-
}
|
|
1901
|
-
}
|
|
1902
|
-
|
|
1903
|
-
.header {
|
|
1904
|
-
background: var(--header-bg);
|
|
1905
|
-
color: var(--text-primary);
|
|
1906
|
-
padding: 24px 28px;
|
|
1907
|
-
display: flex;
|
|
1908
|
-
align-items: center;
|
|
1909
|
-
justify-content: space-between;
|
|
1910
|
-
position: relative;
|
|
1911
|
-
overflow: hidden;
|
|
1912
|
-
}
|
|
1913
|
-
|
|
1914
|
-
.header::before {
|
|
1915
|
-
content: '';
|
|
1916
|
-
position: absolute;
|
|
1917
|
-
top: 0; left: 0; right: 0; bottom: 0;
|
|
1918
|
-
background: linear-gradient(45deg, transparent 30%, rgba(255,255,255,0.03) 50%, transparent 70%);
|
|
1919
|
-
animation: headerShimmer 4s infinite;
|
|
1920
|
-
pointer-events: none;
|
|
1921
|
-
}
|
|
1922
|
-
|
|
1923
|
-
@keyframes headerShimmer {
|
|
1924
|
-
0% { transform: translateX(-100%); }
|
|
1925
|
-
100% { transform: translateX(100%); }
|
|
1926
|
-
}
|
|
1927
|
-
|
|
1928
|
-
.header-left {
|
|
1929
|
-
display: flex;
|
|
1930
|
-
align-items: center;
|
|
1931
|
-
gap: 16px;
|
|
1932
|
-
z-index: 1;
|
|
1933
|
-
}
|
|
1934
|
-
|
|
1935
|
-
.logo {
|
|
1936
|
-
width: 32px;
|
|
1937
|
-
height: 32px;
|
|
1938
|
-
filter: drop-shadow(0 4px 8px rgba(0,0,0,0.2));
|
|
1939
|
-
animation: logoFloat 3s ease-in-out infinite;
|
|
1940
|
-
}
|
|
1941
|
-
|
|
1942
|
-
@keyframes logoFloat {
|
|
1943
|
-
0%, 100% { transform: translateY(0px); }
|
|
1944
|
-
50% { transform: translateY(-3px); }
|
|
1945
|
-
}
|
|
1946
|
-
|
|
1947
|
-
.title {
|
|
1948
|
-
font-size: 20px;
|
|
1949
|
-
font-weight: 700;
|
|
1950
|
-
text-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
|
1951
|
-
}
|
|
1952
|
-
|
|
1953
|
-
.github-link {
|
|
1954
|
-
color: var(--text-primary);
|
|
1955
|
-
text-decoration: none;
|
|
1956
|
-
display: flex;
|
|
1957
|
-
align-items: center;
|
|
1958
|
-
gap: 8px;
|
|
1959
|
-
font-size: 14px;
|
|
1960
|
-
background: rgba(255,255,255,0.08);
|
|
1961
|
-
border: 1px solid rgba(255, 255, 255, 0.12);
|
|
1962
|
-
backdrop-filter: blur(10px);
|
|
1963
|
-
padding: 8px 16px;
|
|
1964
|
-
border-radius: 8px;
|
|
1965
|
-
font-weight: 500;
|
|
1966
|
-
z-index: 1;
|
|
1967
|
-
transition: all 0.3s ease;
|
|
1968
|
-
}
|
|
1969
|
-
|
|
1970
|
-
.github-link:hover {
|
|
1971
|
-
background: rgba(255,255,255,0.15);
|
|
1972
|
-
transform: translateY(-1px);
|
|
1973
|
-
box-shadow: 0 4px 12px rgba(0,0,0,0.3);
|
|
1974
|
-
}
|
|
1975
|
-
|
|
1976
|
-
.content {
|
|
1977
|
-
padding: 32px 24px;
|
|
1978
|
-
position: relative;
|
|
1979
|
-
}
|
|
1980
|
-
|
|
1981
|
-
.content-title {
|
|
1982
|
-
font-size: 24px;
|
|
1983
|
-
font-weight: 700;
|
|
1984
|
-
color: var(--text-primary);
|
|
1985
|
-
margin-bottom: 8px;
|
|
1986
|
-
animation: fadeInUp 0.8s ease-out 0.2s both;
|
|
1987
|
-
}
|
|
1988
|
-
|
|
1989
|
-
@keyframes fadeInUp {
|
|
1990
|
-
from {
|
|
1991
|
-
opacity: 0;
|
|
1992
|
-
transform: translateY(20px);
|
|
1993
|
-
}
|
|
1994
|
-
to {
|
|
1995
|
-
opacity: 1;
|
|
1996
|
-
transform: translateY(0);
|
|
1997
|
-
}
|
|
1998
|
-
}
|
|
1999
|
-
|
|
2000
|
-
.message {
|
|
2001
|
-
background: rgba(103, 233, 233, 0.1);
|
|
2002
|
-
border: 1px solid var(--accent-color);
|
|
2003
|
-
border-left: 4px solid var(--accent-color);
|
|
2004
|
-
padding: 20px;
|
|
2005
|
-
border-radius: 12px;
|
|
2006
|
-
margin-bottom: 24px;
|
|
2007
|
-
white-space: pre-wrap;
|
|
2008
|
-
font-size: 15px;
|
|
2009
|
-
line-height: 1.6;
|
|
2010
|
-
color: var(--text-primary);
|
|
2011
|
-
animation: fadeInUp 0.8s ease-out 0.4s both;
|
|
2012
|
-
position: relative;
|
|
2013
|
-
overflow: hidden;
|
|
2014
|
-
}
|
|
2015
|
-
|
|
2016
|
-
.message::before {
|
|
2017
|
-
content: '';
|
|
2018
|
-
position: absolute;
|
|
2019
|
-
top: 0; left: 0;
|
|
2020
|
-
width: 100%; height: 2px;
|
|
2021
|
-
background: linear-gradient(90deg, var(--accent-color), transparent);
|
|
2022
|
-
animation: progress 2s ease-out;
|
|
2023
|
-
}
|
|
2024
|
-
|
|
2025
|
-
@keyframes progress {
|
|
2026
|
-
from { width: 0%; }
|
|
2027
|
-
to { width: 100%; }
|
|
2028
|
-
}
|
|
2029
|
-
|
|
2030
|
-
.options {
|
|
2031
|
-
margin-bottom: 24px;
|
|
2032
|
-
animation: fadeInUp 0.8s ease-out 0.6s both;
|
|
2033
|
-
}
|
|
2034
|
-
|
|
2035
|
-
.option-item {
|
|
2036
|
-
padding: 16px 20px;
|
|
2037
|
-
border: 1px solid var(--border-color);
|
|
2038
|
-
border-radius: 12px;
|
|
2039
|
-
margin-bottom: 12px;
|
|
2040
|
-
cursor: pointer;
|
|
2041
|
-
transition: all 0.3s ease;
|
|
2042
|
-
display: flex;
|
|
2043
|
-
align-items: center;
|
|
2044
|
-
gap: 14px;
|
|
2045
|
-
background: rgba(255, 255, 255, 0.03);
|
|
2046
|
-
position: relative;
|
|
2047
|
-
overflow: hidden;
|
|
2048
|
-
color: var(--text-primary);
|
|
2049
|
-
}
|
|
2050
|
-
|
|
2051
|
-
.option-item::before {
|
|
2052
|
-
content: '';
|
|
2053
|
-
position: absolute;
|
|
2054
|
-
left: 0; top: 0; bottom: 0;
|
|
2055
|
-
width: 0;
|
|
2056
|
-
background: var(--accent-color);
|
|
2057
|
-
transition: width 0.3s ease;
|
|
2058
|
-
}
|
|
2059
|
-
|
|
2060
|
-
.option-item:hover {
|
|
2061
|
-
background: var(--bg-secondary);
|
|
2062
|
-
border-color: var(--accent-color);
|
|
2063
|
-
transform: translateX(5px);
|
|
2064
|
-
}
|
|
2065
|
-
|
|
2066
|
-
.option-item:hover::before {
|
|
2067
|
-
width: 4px;
|
|
2068
|
-
}
|
|
2069
|
-
|
|
2070
|
-
.option-item.selected {
|
|
2071
|
-
background: rgba(103, 233, 233, 0.1);
|
|
2072
|
-
border-color: var(--accent-color);
|
|
2073
|
-
transform: translateX(5px);
|
|
2074
|
-
}
|
|
2075
|
-
|
|
2076
|
-
.option-item.selected::before {
|
|
2077
|
-
width: 4px;
|
|
2078
|
-
}
|
|
2079
|
-
|
|
2080
|
-
.option-icon {
|
|
2081
|
-
width: 20px;
|
|
2082
|
-
height: 20px;
|
|
2083
|
-
color: var(--accent-color);
|
|
2084
|
-
flex-shrink: 0;
|
|
2085
|
-
animation: iconGlow 2s ease-in-out infinite;
|
|
2086
|
-
}
|
|
2087
|
-
|
|
2088
|
-
@keyframes iconGlow {
|
|
2089
|
-
0%, 100% { filter: drop-shadow(0 0 2px rgba(103, 233, 233, 0.3)); }
|
|
2090
|
-
50% { filter: drop-shadow(0 0 8px rgba(103, 233, 233, 0.6)); }
|
|
2091
|
-
}
|
|
2092
|
-
|
|
2093
|
-
.custom-input {
|
|
2094
|
-
margin-bottom: 24px;
|
|
2095
|
-
animation: fadeInUp 0.8s ease-out 0.8s both;
|
|
2096
|
-
}
|
|
2097
|
-
|
|
2098
|
-
.custom-input textarea {
|
|
2099
|
-
width: 100%;
|
|
2100
|
-
min-height: 120px;
|
|
2101
|
-
padding: 16px;
|
|
2102
|
-
border: 1px solid var(--border-color);
|
|
2103
|
-
border-radius: 12px;
|
|
2104
|
-
font-size: 15px;
|
|
2105
|
-
font-family: var(--font-mono);
|
|
2106
|
-
resize: vertical;
|
|
2107
|
-
transition: all 0.3s ease;
|
|
2108
|
-
line-height: 1.5;
|
|
2109
|
-
background: rgba(255, 255, 255, 0.03);
|
|
2110
|
-
color: var(--text-primary);
|
|
2111
|
-
}
|
|
2112
|
-
|
|
2113
|
-
.custom-input textarea::placeholder {
|
|
2114
|
-
color: var(--text-secondary);
|
|
2115
|
-
}
|
|
2116
|
-
|
|
2117
|
-
.custom-input textarea:focus {
|
|
2118
|
-
outline: none;
|
|
2119
|
-
border-color: var(--accent-color);
|
|
2120
|
-
box-shadow: 0 0 0 3px rgba(103, 233, 233, 0.1);
|
|
2121
|
-
background: rgba(255, 255, 255, 0.05);
|
|
2122
|
-
}
|
|
2123
|
-
|
|
2124
|
-
.actions {
|
|
2125
|
-
display: flex;
|
|
2126
|
-
gap: 12px;
|
|
2127
|
-
justify-content: flex-end;
|
|
2128
|
-
animation: fadeInUp 0.8s ease-out 1s both;
|
|
2129
|
-
}
|
|
2130
|
-
|
|
2131
|
-
.btn {
|
|
2132
|
-
padding: 12px 20px;
|
|
2133
|
-
border: none;
|
|
2134
|
-
border-radius: 8px;
|
|
2135
|
-
font-size: 14px;
|
|
2136
|
-
font-weight: 600;
|
|
2137
|
-
cursor: pointer;
|
|
2138
|
-
transition: all 0.3s ease;
|
|
2139
|
-
display: flex;
|
|
2140
|
-
align-items: center;
|
|
2141
|
-
gap: 8px;
|
|
2142
|
-
font-family: var(--font-mono);
|
|
2143
|
-
position: relative;
|
|
2144
|
-
overflow: hidden;
|
|
2145
|
-
}
|
|
2146
|
-
|
|
2147
|
-
.btn::before {
|
|
2148
|
-
content: '';
|
|
2149
|
-
position: absolute;
|
|
2150
|
-
top: 50%; left: 50%;
|
|
2151
|
-
width: 0; height: 0;
|
|
2152
|
-
background: rgba(255,255,255,0.2);
|
|
2153
|
-
border-radius: 50%;
|
|
2154
|
-
transition: all 0.3s ease;
|
|
2155
|
-
transform: translate(-50%, -50%);
|
|
2156
|
-
}
|
|
2157
|
-
|
|
2158
|
-
.btn:hover::before {
|
|
2159
|
-
width: 100px; height: 100px;
|
|
2160
|
-
}
|
|
2161
|
-
|
|
2162
|
-
.btn-primary {
|
|
2163
|
-
background: var(--primary-color);
|
|
2164
|
-
color: var(--text-primary);
|
|
2165
|
-
border: 1px solid var(--border-color);
|
|
2166
|
-
}
|
|
2167
|
-
|
|
2168
|
-
.btn-primary:hover:not(:disabled) {
|
|
2169
|
-
background: var(--primary-hover);
|
|
2170
|
-
transform: translateY(-2px);
|
|
2171
|
-
box-shadow: 0 8px 25px rgba(0, 0, 0, 0.3);
|
|
2172
|
-
}
|
|
2173
|
-
|
|
2174
|
-
.btn-secondary {
|
|
2175
|
-
background: var(--bg-secondary);
|
|
2176
|
-
color: var(--text-secondary);
|
|
2177
|
-
border: 1px solid var(--border-color);
|
|
2178
|
-
}
|
|
2179
|
-
|
|
2180
|
-
.btn-secondary:hover {
|
|
2181
|
-
background: rgba(255, 255, 255, 0.15);
|
|
2182
|
-
color: var(--text-primary);
|
|
2183
|
-
}
|
|
2184
|
-
|
|
2185
|
-
.btn:disabled {
|
|
2186
|
-
opacity: 0.5;
|
|
2187
|
-
cursor: not-allowed;
|
|
2188
|
-
}
|
|
2189
|
-
|
|
2190
|
-
.loading {
|
|
2191
|
-
display: none;
|
|
2192
|
-
align-items: center;
|
|
2193
|
-
justify-content: center;
|
|
2194
|
-
gap: 8px;
|
|
2195
|
-
margin-top: 16px;
|
|
2196
|
-
color: var(--text-secondary);
|
|
2197
|
-
font-size: 14px;
|
|
2198
|
-
}
|
|
2199
|
-
|
|
2200
|
-
.spinner {
|
|
2201
|
-
width: 16px;
|
|
2202
|
-
height: 16px;
|
|
2203
|
-
border: 2px solid var(--border-color);
|
|
2204
|
-
border-top: 2px solid var(--accent-color);
|
|
2205
|
-
border-radius: 50%;
|
|
2206
|
-
animation: spin 1s linear infinite;
|
|
2207
|
-
}
|
|
2208
|
-
|
|
2209
|
-
@keyframes spin {
|
|
2210
|
-
0% { transform: rotate(0deg); }
|
|
2211
|
-
100% { transform: rotate(360deg); }
|
|
2212
|
-
}
|
|
2213
|
-
|
|
2214
|
-
@keyframes spin {
|
|
2215
|
-
0% { transform: rotate(0deg); }
|
|
2216
|
-
100% { transform: rotate(360deg); }
|
|
2217
|
-
}
|
|
2218
|
-
|
|
2219
|
-
.success-state {
|
|
2220
|
-
text-align: center;
|
|
2221
|
-
padding: 40px 20px;
|
|
2222
|
-
animation: fadeInUp 0.8s ease-out both;
|
|
2223
|
-
}
|
|
2224
|
-
|
|
2225
|
-
.success-icon {
|
|
2226
|
-
margin-bottom: 20px;
|
|
2227
|
-
color: var(--accent-color);
|
|
2228
|
-
animation: successPulse 2s ease-in-out infinite;
|
|
2229
|
-
}
|
|
2230
|
-
|
|
2231
|
-
@keyframes successPulse {
|
|
2232
|
-
0%, 100% {
|
|
2233
|
-
transform: scale(1);
|
|
2234
|
-
filter: drop-shadow(0 0 8px rgba(103, 233, 233, 0.3));
|
|
2235
|
-
}
|
|
2236
|
-
50% {
|
|
2237
|
-
transform: scale(1.1);
|
|
2238
|
-
filter: drop-shadow(0 0 16px rgba(103, 233, 233, 0.6));
|
|
2239
|
-
}
|
|
2240
|
-
}
|
|
2241
|
-
|
|
2242
|
-
.success-title {
|
|
2243
|
-
font-size: 24px;
|
|
2244
|
-
font-weight: 700;
|
|
2245
|
-
color: var(--text-primary);
|
|
2246
|
-
margin-bottom: 12px;
|
|
2247
|
-
}
|
|
2248
|
-
|
|
2249
|
-
.success-message {
|
|
2250
|
-
color: var(--text-secondary);
|
|
2251
|
-
font-size: 16px;
|
|
2252
|
-
line-height: 1.5;
|
|
2253
|
-
}
|
|
2254
|
-
|
|
2255
|
-
.selected-env-info {
|
|
2256
|
-
margin-top: 20px;
|
|
2257
|
-
padding: 16px;
|
|
2258
|
-
background: rgba(103, 233, 233, 0.1);
|
|
2259
|
-
border: 1px solid var(--accent-color);
|
|
2260
|
-
border-radius: 12px;
|
|
2261
|
-
display: flex;
|
|
2262
|
-
align-items: center;
|
|
2263
|
-
gap: 12px;
|
|
2264
|
-
}
|
|
2265
|
-
|
|
2266
|
-
.env-label {
|
|
2267
|
-
color: var(--text-secondary);
|
|
2268
|
-
font-size: 14px;
|
|
2269
|
-
font-weight: 500;
|
|
2270
|
-
}
|
|
2271
|
-
|
|
2272
|
-
.env-value {
|
|
2273
|
-
color: var(--accent-color);
|
|
2274
|
-
font-size: 16px;
|
|
2275
|
-
font-weight: 600;
|
|
2276
|
-
font-family: var(--font-mono);
|
|
2277
|
-
}
|
|
2278
|
-
</style>
|
|
2279
|
-
</head>
|
|
2280
|
-
<body>
|
|
2281
|
-
<div class="modal">
|
|
2282
|
-
<div class="header">
|
|
2283
|
-
<div class="header-left">
|
|
2284
|
-
<img class="logo" src="https://7463-tcb-advanced-a656fc-1257967285.tcb.qcloud.la/mcp/cloudbase-logo.svg" alt="CloudBase Logo" />
|
|
2285
|
-
<span class="title">CloudBase AI Toolkit</span>
|
|
2286
|
-
</div>
|
|
2287
|
-
<a href="https://github.com/TencentCloudBase/CloudBase-AI-ToolKit" target="_blank" class="github-link">
|
|
2288
|
-
<svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
|
|
2289
|
-
<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"/>
|
|
2290
|
-
</svg>
|
|
2291
|
-
GitHub
|
|
2292
|
-
</a>
|
|
2293
|
-
</div>
|
|
2294
|
-
|
|
2295
|
-
<div class="content">
|
|
2296
|
-
<h1 class="content-title">AI 需要您确认</h1>
|
|
2297
|
-
<div class="message">${message}</div>
|
|
2298
|
-
|
|
2299
|
-
${optionsArray
|
|
2300
|
-
? `
|
|
2301
|
-
<div class="options" id="options">
|
|
2302
|
-
${optionsArray
|
|
2303
|
-
.map((option, index) => `
|
|
2304
|
-
<div class="option-item" onclick="selectOption('${option}')" style="animation-delay: ${index * 0.1}s;">
|
|
2305
|
-
<svg class="option-icon" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
2306
|
-
<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"/>
|
|
2307
|
-
</svg>
|
|
2308
|
-
<span>${option}</span>
|
|
2309
|
-
</div>
|
|
2310
|
-
`)
|
|
2311
|
-
.join("")}
|
|
2312
|
-
</div>
|
|
2313
|
-
`
|
|
2314
|
-
: ""}
|
|
2315
|
-
|
|
2316
|
-
<div class="custom-input">
|
|
2317
|
-
<textarea id="customInput" placeholder="请输入您的具体需求或建议..." onkeyup="updateSubmitButton()"></textarea>
|
|
2318
|
-
</div>
|
|
2319
|
-
|
|
2320
|
-
<div class="actions">
|
|
2321
|
-
<button class="btn btn-secondary" onclick="cancel()">
|
|
2322
|
-
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
2323
|
-
<path d="M18 6L6 18M6 6l12 12"/>
|
|
2324
|
-
</svg>
|
|
2325
|
-
取消
|
|
2326
|
-
</button>
|
|
2327
|
-
<button class="btn btn-primary" id="submitBtn" onclick="submit()">
|
|
2328
|
-
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
2329
|
-
<path d="M20 6L9 17l-5-5"/>
|
|
2330
|
-
</svg>
|
|
2331
|
-
确认执行
|
|
2332
|
-
</button>
|
|
2333
|
-
</div>
|
|
2334
|
-
|
|
2335
|
-
<div class="loading" id="loading">
|
|
2336
|
-
<div class="spinner"></div>
|
|
2337
|
-
<span>正在提交...</span>
|
|
2338
|
-
</div>
|
|
2339
|
-
</div>
|
|
2340
|
-
</div>
|
|
2341
|
-
|
|
2342
|
-
<script>
|
|
2343
|
-
let selectedOption = null;
|
|
2344
|
-
|
|
2345
|
-
function selectOption(option) {
|
|
2346
|
-
selectedOption = option;
|
|
2347
|
-
|
|
2348
|
-
document.querySelectorAll('.option-item').forEach(item => {
|
|
2349
|
-
item.classList.remove('selected');
|
|
2350
|
-
});
|
|
2351
|
-
event.currentTarget.classList.add('selected');
|
|
2352
|
-
|
|
2353
|
-
updateSubmitButton();
|
|
2354
|
-
}
|
|
2355
|
-
|
|
2356
|
-
function updateSubmitButton() {
|
|
2357
|
-
const customInput = document.getElementById('customInput').value.trim();
|
|
2358
|
-
const submitBtn = document.getElementById('submitBtn');
|
|
2359
|
-
|
|
2360
|
-
if (selectedOption || customInput) {
|
|
2361
|
-
submitBtn.disabled = false;
|
|
2362
|
-
submitBtn.style.opacity = '1';
|
|
2363
|
-
} else {
|
|
2364
|
-
submitBtn.disabled = true;
|
|
2365
|
-
submitBtn.style.opacity = '0.5';
|
|
2366
|
-
}
|
|
2367
|
-
}
|
|
2368
|
-
|
|
2369
|
-
function submit() {
|
|
2370
|
-
const customInput = document.getElementById('customInput').value.trim();
|
|
2371
|
-
const data = selectedOption || customInput;
|
|
2372
|
-
|
|
2373
|
-
if (!data) return;
|
|
2374
|
-
|
|
2375
|
-
document.getElementById('loading').style.display = 'flex';
|
|
2376
|
-
document.getElementById('submitBtn').disabled = true;
|
|
2377
|
-
|
|
2378
|
-
fetch('/api/submit', {
|
|
2379
|
-
method: 'POST',
|
|
2380
|
-
headers: { 'Content-Type': 'application/json' },
|
|
2381
|
-
body: JSON.stringify({
|
|
2382
|
-
type: 'clarification',
|
|
2383
|
-
data: data
|
|
2384
|
-
})
|
|
2385
|
-
}).then(response => response.json())
|
|
2386
|
-
.then(result => {
|
|
2387
|
-
if (result.success) {
|
|
2388
|
-
window.close();
|
|
2389
|
-
}
|
|
2390
|
-
}).catch(err => {
|
|
2391
|
-
console.error('Error:', err);
|
|
2392
|
-
document.getElementById('loading').style.display = 'none';
|
|
2393
|
-
document.getElementById('submitBtn').disabled = false;
|
|
2394
|
-
});
|
|
2395
|
-
}
|
|
2396
|
-
|
|
2397
|
-
function cancel() {
|
|
2398
|
-
fetch('/api/cancel', {
|
|
2399
|
-
method: 'POST',
|
|
2400
|
-
headers: { 'Content-Type': 'application/json' }
|
|
2401
|
-
}).then(() => {
|
|
2402
|
-
window.close();
|
|
2403
|
-
});
|
|
2404
|
-
}
|
|
2405
|
-
|
|
2406
|
-
// Initialize
|
|
2407
|
-
updateSubmitButton();
|
|
2408
|
-
</script>
|
|
2409
|
-
</body>
|
|
2410
|
-
</html>`;
|
|
2411
|
-
}
|
|
2412
|
-
getConfirmationHTML(message, risks, options) {
|
|
2413
|
-
const availableOptions = options || ["确认执行", "取消操作"];
|
|
2414
|
-
return `
|
|
2415
|
-
<!DOCTYPE html>
|
|
2416
|
-
<html lang="zh-CN">
|
|
2417
|
-
<head>
|
|
2418
|
-
<meta charset="UTF-8">
|
|
2419
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
2420
|
-
<title>CloudBase AI Toolkit - 操作确认</title>
|
|
2421
|
-
<style>
|
|
2422
|
-
@import url('https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500;600&display=swap');
|
|
2423
|
-
|
|
2424
|
-
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
2425
|
-
:root {
|
|
2426
|
-
--primary-color: #1a1a1a;
|
|
2427
|
-
--primary-hover: #000000;
|
|
2428
|
-
--accent-color: #67E9E9;
|
|
2429
|
-
--accent-hover: #2BCCCC;
|
|
2430
|
-
--text-primary: #ffffff;
|
|
2431
|
-
--text-secondary: #a0a0a0;
|
|
2432
|
-
--border-color: rgba(255, 255, 255, 0.15);
|
|
2433
|
-
--bg-secondary: rgba(255, 255, 255, 0.08);
|
|
2434
|
-
--bg-glass: rgba(26, 26, 26, 0.95);
|
|
2435
|
-
--warning-color: #ff6b6b;
|
|
2436
|
-
--warning-bg: rgba(255, 107, 107, 0.1);
|
|
2437
|
-
--warning-border: rgba(255, 107, 107, 0.3);
|
|
2438
|
-
--shadow: 0 25px 50px rgba(0, 0, 0, 0.3), 0 10px 20px rgba(0, 0, 0, 0.2);
|
|
2439
|
-
--font-mono: 'JetBrains Mono', 'SF Mono', 'Monaco', monospace;
|
|
2440
|
-
--header-bg: linear-gradient(135deg, #0a0a0a 0%, #1a1a1a 50%, #0d1117 100%);
|
|
2441
|
-
}
|
|
2442
|
-
|
|
2443
|
-
body {
|
|
2444
|
-
font-family: var(--font-mono);
|
|
2445
|
-
background: linear-gradient(135deg, #0a0a0a 0%, #1a1a1a 100%);
|
|
2446
|
-
min-height: 100vh;
|
|
2447
|
-
display: flex;
|
|
2448
|
-
align-items: center;
|
|
2449
|
-
justify-content: center;
|
|
2450
|
-
padding: 20px;
|
|
2451
|
-
position: relative;
|
|
2452
|
-
overflow-x: hidden;
|
|
2453
|
-
overflow-y: auto;
|
|
2454
|
-
}
|
|
2455
|
-
|
|
2456
|
-
/* Custom scrollbar styles */
|
|
2457
|
-
::-webkit-scrollbar {
|
|
2458
|
-
width: 8px;
|
|
2459
|
-
}
|
|
2460
|
-
|
|
2461
|
-
::-webkit-scrollbar-track {
|
|
2462
|
-
background: rgba(255, 255, 255, 0.05);
|
|
2463
|
-
border-radius: 4px;
|
|
2464
|
-
}
|
|
2465
|
-
|
|
2466
|
-
::-webkit-scrollbar-thumb {
|
|
2467
|
-
background: var(--accent-color);
|
|
2468
|
-
border-radius: 4px;
|
|
2469
|
-
}
|
|
2470
|
-
|
|
2471
|
-
::-webkit-scrollbar-thumb:hover {
|
|
2472
|
-
background: var(--accent-hover);
|
|
2473
|
-
}
|
|
2474
|
-
|
|
2475
|
-
body::before {
|
|
2476
|
-
content: '';
|
|
2477
|
-
position: fixed;
|
|
2478
|
-
top: 0; left: 0; right: 0; bottom: 0;
|
|
2479
|
-
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;
|
|
2480
|
-
pointer-events: none;
|
|
2481
|
-
z-index: -1;
|
|
2482
|
-
}
|
|
2483
|
-
|
|
2484
|
-
body::after {
|
|
2485
|
-
content: '';
|
|
2486
|
-
position: fixed;
|
|
2487
|
-
top: 50%; left: 50%;
|
|
2488
|
-
width: 500px; height: 500px;
|
|
2489
|
-
background: radial-gradient(circle, rgba(255, 107, 107, 0.03) 0%, transparent 70%);
|
|
2490
|
-
transform: translate(-50%, -50%);
|
|
2491
|
-
pointer-events: none;
|
|
2492
|
-
z-index: -1;
|
|
2493
|
-
animation: pulse 8s ease-in-out infinite;
|
|
2494
|
-
}
|
|
2495
|
-
|
|
2496
|
-
@keyframes pulse {
|
|
2497
|
-
0%, 100% { opacity: 0.3; transform: translate(-50%, -50%) scale(1); }
|
|
2498
|
-
50% { opacity: 0.6; transform: translate(-50%, -50%) scale(1.1); }
|
|
2499
|
-
}
|
|
2500
|
-
|
|
2501
|
-
.modal {
|
|
2502
|
-
background: var(--bg-glass);
|
|
2503
|
-
backdrop-filter: blur(20px);
|
|
2504
|
-
border-radius: 20px;
|
|
2505
|
-
box-shadow: var(--shadow);
|
|
2506
|
-
border: 2px solid var(--border-color);
|
|
2507
|
-
width: 100%;
|
|
2508
|
-
max-width: 600px;
|
|
2509
|
-
overflow: hidden;
|
|
2510
|
-
animation: modalIn 0.6s cubic-bezier(0.175, 0.885, 0.32, 1.275);
|
|
2511
|
-
position: relative;
|
|
2512
|
-
}
|
|
2513
|
-
|
|
2514
|
-
.modal::before {
|
|
2515
|
-
content: '';
|
|
2516
|
-
position: absolute;
|
|
2517
|
-
top: 0; left: 0; right: 0; bottom: 0;
|
|
2518
|
-
background: linear-gradient(45deg, transparent 30%, rgba(255,255,255,0.02) 50%, transparent 70%);
|
|
2519
|
-
animation: shimmer 3s infinite;
|
|
2520
|
-
pointer-events: none;
|
|
2521
|
-
}
|
|
2522
|
-
|
|
2523
|
-
@keyframes shimmer {
|
|
2524
|
-
0% { transform: translateX(-100%); }
|
|
2525
|
-
100% { transform: translateX(100%); }
|
|
2526
|
-
}
|
|
2527
|
-
|
|
2528
|
-
@keyframes modalIn {
|
|
2529
|
-
from {
|
|
2530
|
-
opacity: 0;
|
|
2531
|
-
transform: scale(0.9) translateY(-20px);
|
|
2532
|
-
}
|
|
2533
|
-
to {
|
|
2534
|
-
opacity: 1;
|
|
2535
|
-
transform: scale(1) translateY(0);
|
|
2536
|
-
}
|
|
2537
|
-
}
|
|
2538
|
-
|
|
2539
|
-
.header {
|
|
2540
|
-
background: var(--header-bg);
|
|
2541
|
-
color: var(--text-primary);
|
|
2542
|
-
padding: 24px 28px;
|
|
2543
|
-
display: flex;
|
|
2544
|
-
align-items: center;
|
|
2545
|
-
justify-content: space-between;
|
|
2546
|
-
position: relative;
|
|
2547
|
-
overflow: hidden;
|
|
2548
|
-
}
|
|
2549
|
-
|
|
2550
|
-
.header::before {
|
|
2551
|
-
content: '';
|
|
2552
|
-
position: absolute;
|
|
2553
|
-
top: 0; left: 0; right: 0; bottom: 0;
|
|
2554
|
-
background: linear-gradient(45deg, transparent 30%, rgba(255,255,255,0.03) 50%, transparent 70%);
|
|
2555
|
-
animation: headerShimmer 4s infinite;
|
|
2556
|
-
pointer-events: none;
|
|
2557
|
-
}
|
|
2558
|
-
|
|
2559
|
-
@keyframes headerShimmer {
|
|
2560
|
-
0% { transform: translateX(-100%); }
|
|
2561
|
-
100% { transform: translateX(100%); }
|
|
2562
|
-
}
|
|
2563
|
-
|
|
2564
|
-
.header-left {
|
|
2565
|
-
display: flex;
|
|
2566
|
-
align-items: center;
|
|
2567
|
-
gap: 16px;
|
|
2568
|
-
z-index: 1;
|
|
2569
|
-
}
|
|
2570
|
-
|
|
2571
|
-
.logo {
|
|
2572
|
-
width: 32px;
|
|
2573
|
-
height: 32px;
|
|
2574
|
-
filter: drop-shadow(0 4px 8px rgba(0,0,0,0.2));
|
|
2575
|
-
animation: logoFloat 3s ease-in-out infinite;
|
|
2576
|
-
}
|
|
2577
|
-
|
|
2578
|
-
@keyframes logoFloat {
|
|
2579
|
-
0%, 100% { transform: translateY(0px); }
|
|
2580
|
-
50% { transform: translateY(-3px); }
|
|
2581
|
-
}
|
|
2582
|
-
|
|
2583
|
-
.title {
|
|
2584
|
-
font-size: 20px;
|
|
2585
|
-
font-weight: 700;
|
|
2586
|
-
text-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
|
2587
|
-
}
|
|
2588
|
-
|
|
2589
|
-
.github-link {
|
|
2590
|
-
color: var(--text-primary);
|
|
2591
|
-
text-decoration: none;
|
|
2592
|
-
display: flex;
|
|
2593
|
-
align-items: center;
|
|
2594
|
-
gap: 8px;
|
|
2595
|
-
font-size: 14px;
|
|
2596
|
-
background: rgba(255,255,255,0.08);
|
|
2597
|
-
border: 1px solid rgba(255, 255, 255, 0.12);
|
|
2598
|
-
backdrop-filter: blur(10px);
|
|
2599
|
-
padding: 8px 16px;
|
|
2600
|
-
border-radius: 8px;
|
|
2601
|
-
font-weight: 500;
|
|
2602
|
-
z-index: 1;
|
|
2603
|
-
transition: all 0.3s ease;
|
|
2604
|
-
}
|
|
2605
|
-
|
|
2606
|
-
.github-link:hover {
|
|
2607
|
-
background: rgba(255,255,255,0.15);
|
|
2608
|
-
transform: translateY(-1px);
|
|
2609
|
-
box-shadow: 0 4px 12px rgba(0,0,0,0.3);
|
|
2610
|
-
}
|
|
2611
|
-
|
|
2612
|
-
.content {
|
|
2613
|
-
padding: 32px 24px;
|
|
2614
|
-
position: relative;
|
|
2615
|
-
}
|
|
2616
|
-
|
|
2617
|
-
@keyframes fadeInUp {
|
|
2618
|
-
from {
|
|
2619
|
-
opacity: 0;
|
|
2620
|
-
transform: translateY(20px);
|
|
2621
|
-
}
|
|
2622
|
-
to {
|
|
2623
|
-
opacity: 1;
|
|
2624
|
-
transform: translateY(0);
|
|
2625
|
-
}
|
|
2626
|
-
}
|
|
2627
|
-
|
|
2628
|
-
.content-title {
|
|
2629
|
-
font-size: 24px;
|
|
2630
|
-
margin-bottom: 8px;
|
|
2631
|
-
color: var(--text-primary);
|
|
2632
|
-
display: flex;
|
|
2633
|
-
align-items: center;
|
|
2634
|
-
gap: 12px;
|
|
2635
|
-
position: relative;
|
|
2636
|
-
animation: fadeInUp 0.8s ease-out 0.2s both;
|
|
2637
|
-
}
|
|
2638
|
-
|
|
2639
|
-
.message {
|
|
2640
|
-
background: rgba(103, 233, 233, 0.1);
|
|
2641
|
-
border: 1px solid var(--accent-color);
|
|
2642
|
-
border-left: 4px solid var(--accent-color);
|
|
2643
|
-
padding: 20px;
|
|
2644
|
-
border-radius: 12px;
|
|
2645
|
-
margin-bottom: 24px;
|
|
2646
|
-
font-size: 15px;
|
|
2647
|
-
line-height: 1.6;
|
|
2648
|
-
color: var(--text-primary);
|
|
2649
|
-
animation: fadeInUp 0.8s ease-out 0.4s both;
|
|
2650
|
-
position: relative;
|
|
2651
|
-
overflow: scroll;
|
|
2652
|
-
white-space: pre-wrap;
|
|
2653
|
-
max-height: 300px;
|
|
2654
|
-
}
|
|
2655
|
-
|
|
2656
|
-
.message::before {
|
|
2657
|
-
content: '';
|
|
2658
|
-
position: absolute;
|
|
2659
|
-
top: 0; left: 0;
|
|
2660
|
-
width: 100%; height: 2px;
|
|
2661
|
-
background: linear-gradient(90deg, var(--accent-color), transparent);
|
|
2662
|
-
animation: progress 2s ease-out;
|
|
2663
|
-
}
|
|
2664
|
-
|
|
2665
|
-
@keyframes progress {
|
|
2666
|
-
from { width: 0%; }
|
|
2667
|
-
to { width: 100%; }
|
|
2668
|
-
}
|
|
2669
|
-
|
|
2670
|
-
.risks {
|
|
2671
|
-
background: var(--warning-bg);
|
|
2672
|
-
border: 1px solid var(--warning-border);
|
|
2673
|
-
border-radius: 12px;
|
|
2674
|
-
padding: 20px;
|
|
2675
|
-
margin-bottom: 24px;
|
|
2676
|
-
animation: fadeInUp 0.8s ease-out 0.6s both;
|
|
2677
|
-
}
|
|
2678
|
-
|
|
2679
|
-
.risks-title {
|
|
2680
|
-
color: var(--warning-color);
|
|
2681
|
-
font-weight: 600;
|
|
2682
|
-
margin-bottom: 12px;
|
|
2683
|
-
display: flex;
|
|
2684
|
-
align-items: center;
|
|
2685
|
-
gap: 8px;
|
|
2686
|
-
animation: warningGlow 2s ease-in-out infinite;
|
|
2687
|
-
}
|
|
2688
|
-
|
|
2689
|
-
@keyframes warningGlow {
|
|
2690
|
-
0%, 100% { filter: drop-shadow(0 0 2px rgba(255, 107, 107, 0.3)); }
|
|
2691
|
-
50% { filter: drop-shadow(0 0 8px rgba(255, 107, 107, 0.6)); }
|
|
2692
|
-
}
|
|
2693
|
-
|
|
2694
|
-
.risk-item {
|
|
2695
|
-
color: var(--text-primary);
|
|
2696
|
-
margin-bottom: 8px;
|
|
2697
|
-
padding-left: 24px;
|
|
2698
|
-
position: relative;
|
|
2699
|
-
}
|
|
2700
|
-
|
|
2701
|
-
.risk-item:before {
|
|
2702
|
-
content: "⚠️";
|
|
2703
|
-
position: absolute;
|
|
2704
|
-
left: 0;
|
|
2705
|
-
color: var(--warning-color);
|
|
2706
|
-
}
|
|
2707
|
-
|
|
2708
|
-
.options {
|
|
2709
|
-
margin-bottom: 24px;
|
|
2710
|
-
animation: fadeInUp 0.8s ease-out 0.8s both;
|
|
2711
|
-
}
|
|
2712
|
-
|
|
2713
|
-
.option-item {
|
|
2714
|
-
padding: 16px 20px;
|
|
2715
|
-
border: 1px solid var(--border-color);
|
|
2716
|
-
border-radius: 12px;
|
|
2717
|
-
margin-bottom: 12px;
|
|
2718
|
-
cursor: pointer;
|
|
2719
|
-
transition: all 0.3s ease;
|
|
2720
|
-
display: flex;
|
|
2721
|
-
align-items: center;
|
|
2722
|
-
gap: 14px;
|
|
2723
|
-
background: rgba(255, 255, 255, 0.03);
|
|
2724
|
-
position: relative;
|
|
2725
|
-
overflow: hidden;
|
|
2726
|
-
color: var(--text-primary);
|
|
2727
|
-
}
|
|
2728
|
-
|
|
2729
|
-
.option-item::before {
|
|
2730
|
-
content: '';
|
|
2731
|
-
position: absolute;
|
|
2732
|
-
left: 0; top: 0; bottom: 0;
|
|
2733
|
-
width: 0;
|
|
2734
|
-
background: var(--accent-color);
|
|
2735
|
-
transition: width 0.3s ease;
|
|
2736
|
-
}
|
|
2737
|
-
|
|
2738
|
-
.option-item.confirm::before {
|
|
2739
|
-
background: var(--accent-color);
|
|
2740
|
-
}
|
|
2741
|
-
|
|
2742
|
-
.option-item.cancel::before {
|
|
2743
|
-
background: var(--warning-color);
|
|
2744
|
-
}
|
|
2745
|
-
|
|
2746
|
-
.option-item:hover {
|
|
2747
|
-
background: var(--bg-secondary);
|
|
2748
|
-
transform: translateX(5px);
|
|
2749
|
-
}
|
|
2750
|
-
|
|
2751
|
-
.option-item:hover::before {
|
|
2752
|
-
width: 4px;
|
|
2753
|
-
}
|
|
2754
|
-
|
|
2755
|
-
.option-item.selected {
|
|
2756
|
-
background: rgba(103, 233, 233, 0.1);
|
|
2757
|
-
border-color: var(--accent-color);
|
|
2758
|
-
transform: translateX(5px);
|
|
2759
|
-
}
|
|
2760
|
-
|
|
2761
|
-
.option-item.selected.cancel {
|
|
2762
|
-
background: rgba(255, 107, 107, 0.1);
|
|
2763
|
-
border-color: var(--warning-color);
|
|
2764
|
-
}
|
|
2765
|
-
|
|
2766
|
-
.option-item.selected::before {
|
|
2767
|
-
width: 4px;
|
|
2768
|
-
}
|
|
2769
|
-
|
|
2770
|
-
.option-icon {
|
|
2771
|
-
width: 20px;
|
|
2772
|
-
height: 20px;
|
|
2773
|
-
color: var(--accent-color);
|
|
2774
|
-
flex-shrink: 0;
|
|
2775
|
-
}
|
|
2776
|
-
|
|
2777
|
-
.option-item.cancel .option-icon {
|
|
2778
|
-
color: var(--warning-color);
|
|
2779
|
-
}
|
|
2780
|
-
|
|
2781
|
-
.loading {
|
|
2782
|
-
display: none;
|
|
2783
|
-
align-items: center;
|
|
2784
|
-
justify-content: center;
|
|
2785
|
-
gap: 8px;
|
|
2786
|
-
margin-top: 16px;
|
|
2787
|
-
color: var(--text-secondary);
|
|
2788
|
-
font-size: 14px;
|
|
2789
|
-
animation: fadeInUp 0.8s ease-out 1s both;
|
|
2790
|
-
}
|
|
2791
|
-
|
|
2792
|
-
.spinner {
|
|
2793
|
-
width: 16px;
|
|
2794
|
-
height: 16px;
|
|
2795
|
-
border: 2px solid var(--border-color);
|
|
2796
|
-
border-top: 2px solid var(--accent-color);
|
|
2797
|
-
border-radius: 50%;
|
|
2798
|
-
animation: spin 1s linear infinite;
|
|
2799
|
-
}
|
|
2800
|
-
|
|
2801
|
-
@keyframes spin {
|
|
2802
|
-
0% { transform: rotate(0deg); }
|
|
2803
|
-
100% { transform: rotate(360deg); }
|
|
2804
|
-
}
|
|
2805
|
-
</style>
|
|
2806
|
-
</head>
|
|
2807
|
-
<body>
|
|
2808
|
-
<div class="modal">
|
|
2809
|
-
<div class="header">
|
|
2810
|
-
<div class="header-left">
|
|
2811
|
-
<img class="logo" src="https://7463-tcb-advanced-a656fc-1257967285.tcb.qcloud.la/mcp/cloudbase-logo.svg" alt="CloudBase Logo" />
|
|
2812
|
-
<span class="title">CloudBase AI Toolkit</span>
|
|
2813
|
-
</div>
|
|
2814
|
-
<a href="https://github.com/TencentCloudBase/CloudBase-AI-ToolKit" target="_blank" class="github-link">
|
|
2815
|
-
<svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
|
|
2816
|
-
<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"/>
|
|
2817
|
-
</svg>
|
|
2818
|
-
GitHub
|
|
2819
|
-
</a>
|
|
2820
|
-
</div>
|
|
2821
|
-
|
|
2822
|
-
<div class="content">
|
|
2823
|
-
<h1 class="content-title">
|
|
2824
|
-
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
2825
|
-
<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"/>
|
|
2826
|
-
<line x1="12" y1="9" x2="12" y2="13"/>
|
|
2827
|
-
<line x1="12" y1="17" x2="12.01" y2="17"/>
|
|
2828
|
-
</svg>
|
|
2829
|
-
操作确认
|
|
2830
|
-
</h1>
|
|
2831
|
-
<div class="message">${message}</div>
|
|
2832
|
-
|
|
2833
|
-
${risks && risks.length > 0
|
|
2834
|
-
? `
|
|
2835
|
-
<div class="risks">
|
|
2836
|
-
<div class="risks-title">
|
|
2837
|
-
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
2838
|
-
<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"/>
|
|
2839
|
-
<line x1="12" y1="9" x2="12" y2="13"/>
|
|
2840
|
-
<line x1="12" y1="17" x2="12.01" y2="17"/>
|
|
2841
|
-
</svg>
|
|
2842
|
-
风险提示
|
|
2843
|
-
</div>
|
|
2844
|
-
${risks.map((risk) => `<div class="risk-item">${risk}</div>`).join("")}
|
|
2845
|
-
</div>
|
|
2846
|
-
`
|
|
2847
|
-
: ""}
|
|
2848
|
-
|
|
2849
|
-
<div class="options">
|
|
2850
|
-
${availableOptions
|
|
2851
|
-
.map((option, index) => {
|
|
2852
|
-
const isCancel = option.includes("取消") ||
|
|
2853
|
-
option.toLowerCase().includes("cancel");
|
|
2854
|
-
const className = isCancel ? "cancel" : "confirm";
|
|
2855
|
-
const iconPath = isCancel
|
|
2856
|
-
? '<path d="M18 6L6 18M6 6l12 12"/>'
|
|
2857
|
-
: '<path d="M20 6L9 17l-5-5"/>';
|
|
2858
|
-
return `
|
|
2859
|
-
<div class="option-item ${className}" onclick="selectOption('${option}')" style="animation-delay: ${index * 0.1}s;">
|
|
2860
|
-
<svg class="option-icon" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
2861
|
-
${iconPath}
|
|
2862
|
-
</svg>
|
|
2863
|
-
<span>${option}</span>
|
|
2864
|
-
</div>
|
|
2865
|
-
`;
|
|
2866
|
-
})
|
|
2867
|
-
.join("")}
|
|
2868
|
-
</div>
|
|
2869
|
-
|
|
2870
|
-
<div class="loading" id="loading">
|
|
2871
|
-
<div class="spinner"></div>
|
|
2872
|
-
<span>正在处理...</span>
|
|
2873
|
-
</div>
|
|
2874
|
-
</div>
|
|
2875
|
-
</div>
|
|
2876
|
-
|
|
2877
|
-
<script>
|
|
2878
|
-
let selectedOption = null;
|
|
2879
|
-
|
|
2880
|
-
function selectOption(option) {
|
|
2881
|
-
selectedOption = option;
|
|
2882
|
-
|
|
2883
|
-
document.querySelectorAll('.option-item').forEach(item => {
|
|
2884
|
-
item.classList.remove('selected');
|
|
2885
|
-
});
|
|
2886
|
-
event.currentTarget.classList.add('selected');
|
|
2887
|
-
|
|
2888
|
-
// Auto submit after selection
|
|
2889
|
-
setTimeout(() => {
|
|
2890
|
-
submit();
|
|
2891
|
-
}, 500);
|
|
2892
|
-
}
|
|
2893
|
-
|
|
2894
|
-
function submit() {
|
|
2895
|
-
if (!selectedOption) return;
|
|
2896
|
-
|
|
2897
|
-
document.getElementById('loading').style.display = 'flex';
|
|
2898
|
-
|
|
2899
|
-
const isConfirmed = !selectedOption.includes('取消') && !selectedOption.toLowerCase().includes('cancel');
|
|
2900
|
-
|
|
2901
|
-
fetch('/api/submit', {
|
|
2902
|
-
method: 'POST',
|
|
2903
|
-
headers: { 'Content-Type': 'application/json' },
|
|
2904
|
-
body: JSON.stringify({
|
|
2905
|
-
type: 'confirmation',
|
|
2906
|
-
data: {
|
|
2907
|
-
confirmed: isConfirmed,
|
|
2908
|
-
option: selectedOption
|
|
2909
|
-
}
|
|
2910
|
-
})
|
|
2911
|
-
}).then(response => response.json())
|
|
2912
|
-
.then(result => {
|
|
2913
|
-
if (result.success) {
|
|
2914
|
-
window.close();
|
|
2915
|
-
}
|
|
2916
|
-
}).catch(err => {
|
|
2917
|
-
console.error('Error:', err);
|
|
2918
|
-
document.getElementById('loading').style.display = 'none';
|
|
2919
|
-
});
|
|
2920
|
-
}
|
|
2921
|
-
|
|
2922
|
-
function cancel() {
|
|
2923
|
-
fetch('/api/cancel', {
|
|
2924
|
-
method: 'POST',
|
|
2925
|
-
headers: { 'Content-Type': 'application/json' }
|
|
2926
|
-
}).then(() => {
|
|
2927
|
-
window.close();
|
|
2928
|
-
});
|
|
2929
|
-
}
|
|
2930
|
-
</script>
|
|
2931
|
-
</body>
|
|
2932
|
-
</html>`;
|
|
2933
|
-
}
|
|
2934
|
-
// 公共方法获取运行状态
|
|
2935
|
-
get running() {
|
|
2936
|
-
return this.isRunning;
|
|
2937
|
-
}
|
|
2938
|
-
// 公共方法获取端口
|
|
2939
|
-
get currentPort() {
|
|
2940
|
-
return this.port;
|
|
2941
|
-
}
|
|
2942
|
-
}
|
|
2943
|
-
exports.InteractiveServer = InteractiveServer;
|
|
2944
|
-
// 单例实例
|
|
2945
|
-
let interactiveServerInstance = null;
|
|
2946
|
-
function getInteractiveServer(mcpServer) {
|
|
2947
|
-
if (!interactiveServerInstance) {
|
|
2948
|
-
interactiveServerInstance = new InteractiveServer(mcpServer);
|
|
2949
|
-
}
|
|
2950
|
-
else if (mcpServer) {
|
|
2951
|
-
// Always update mcpServer if provided, to ensure it's current
|
|
2952
|
-
interactiveServerInstance.mcpServer = mcpServer;
|
|
2953
|
-
(0, logger_js_1.debug)(`[getInteractiveServer] Updated mcpServer, has server: ${!!mcpServer?.server}, has ide: ${!!mcpServer?.ide}`);
|
|
2954
|
-
}
|
|
2955
|
-
return interactiveServerInstance;
|
|
2956
|
-
}
|
|
2957
|
-
async function resetInteractiveServer() {
|
|
2958
|
-
if (interactiveServerInstance) {
|
|
2959
|
-
try {
|
|
2960
|
-
await interactiveServerInstance.stop();
|
|
2961
|
-
}
|
|
2962
|
-
catch (err) {
|
|
2963
|
-
(0, logger_js_1.error)("Error stopping existing server instance:", err instanceof Error ? err : new Error(String(err)));
|
|
2964
|
-
}
|
|
2965
|
-
interactiveServerInstance = null;
|
|
2966
|
-
}
|
|
2967
|
-
}
|
|
2968
|
-
async function getInteractiveServerSafe(mcpServer) {
|
|
2969
|
-
// 如果当前实例存在但不在运行状态,先清理
|
|
2970
|
-
if (interactiveServerInstance && !interactiveServerInstance.running) {
|
|
2971
|
-
try {
|
|
2972
|
-
await interactiveServerInstance.stop();
|
|
2973
|
-
}
|
|
2974
|
-
catch (err) {
|
|
2975
|
-
(0, logger_js_1.debug)("Error stopping non-running server:", err instanceof Error ? err : new Error(String(err)));
|
|
2976
|
-
}
|
|
2977
|
-
interactiveServerInstance = null;
|
|
2978
|
-
}
|
|
2979
|
-
return getInteractiveServer(mcpServer);
|
|
2980
|
-
}
|
|
2981
|
-
|
|
2982
|
-
|
|
2983
|
-
/***/ },
|
|
2984
|
-
|
|
2985
|
-
/***/ 4415
|
|
2986
|
-
(__unused_webpack_module, exports) {
|
|
2987
|
-
|
|
2988
|
-
var __webpack_unused_export__;
|
|
2989
|
-
|
|
2990
|
-
/**
|
|
2991
|
-
* Reusable UI components for env setup page
|
|
2992
|
-
*/
|
|
2993
|
-
__webpack_unused_export__ = ({ value: true });
|
|
2994
|
-
exports.renderHeader = renderHeader;
|
|
2995
|
-
exports.renderSearchBox = renderSearchBox;
|
|
2996
|
-
exports.renderEnvItem = renderEnvItem;
|
|
2997
|
-
exports.renderEmptyState = renderEmptyState;
|
|
2998
|
-
exports.renderNoResultsState = renderNoResultsState;
|
|
2999
|
-
exports.renderHelpLinks = renderHelpLinks;
|
|
3000
|
-
exports.renderActionButtons = renderActionButtons;
|
|
3001
|
-
exports.renderLoadingState = renderLoadingState;
|
|
3002
|
-
exports.renderSuccessState = renderSuccessState;
|
|
3003
|
-
exports.renderErrorBanner = renderErrorBanner;
|
|
3004
|
-
function renderHeader(accountInfo, ide) {
|
|
3005
|
-
const hasAccount = !!accountInfo?.uin;
|
|
3006
|
-
const isCodeBuddy = ide === "CodeBuddy";
|
|
3007
|
-
// 国际站不支持切换账号:优先按 site 判定,兼容未传 site 的旧调用方按 region 兜底
|
|
3008
|
-
const site = accountInfo?.site;
|
|
3009
|
-
const isIntl = site === "intl" ? true : site === "domestic" ? false : accountInfo?.region === "ap-singapore";
|
|
3010
|
-
const siteLabel = site === "intl" ? "国际站" : site === "domestic" ? "国内站" : undefined;
|
|
3011
|
-
return `
|
|
3012
|
-
<div class="header">
|
|
3013
|
-
<div class="header-left">
|
|
3014
|
-
<img class="logo" src="https://7463-tcb-advanced-a656fc-1257967285.tcb.qcloud.la/mcp/cloudbase-logo.svg" alt="CloudBase Logo" />
|
|
3015
|
-
<span class="title">CloudBase</span>
|
|
3016
|
-
</div>
|
|
3017
|
-
${(hasAccount && !isIntl) ? `
|
|
3018
|
-
<div class="header-right">
|
|
3019
|
-
<div class="account-section">
|
|
3020
|
-
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5">
|
|
3021
|
-
<path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"/>
|
|
3022
|
-
<circle cx="12" cy="7" r="4"/>
|
|
3023
|
-
</svg>
|
|
3024
|
-
<span class="account-uin">${accountInfo.uin}</span>
|
|
3025
|
-
${siteLabel ? `<span class="account-site">${siteLabel}</span>` : ''}
|
|
3026
|
-
${accountInfo.region ? `<span class="account-region">${accountInfo.region}</span>` : ''}
|
|
3027
|
-
${!isCodeBuddy ? `
|
|
3028
|
-
<button class="btn-switch" onclick="switchAccount()" title="切换账号">
|
|
3029
|
-
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5">
|
|
3030
|
-
<path d="M17 2l4 4-4 4M7 22l-4-4 4-4M21 6H10M3 18h11"/>
|
|
3031
|
-
</svg>
|
|
3032
|
-
</button>
|
|
3033
|
-
` : ''}
|
|
3034
|
-
</div>
|
|
3035
|
-
</div>
|
|
3036
|
-
` : ''}
|
|
3037
|
-
</div>
|
|
3038
|
-
`.trim();
|
|
3039
|
-
}
|
|
3040
|
-
function renderSearchBox() {
|
|
3041
|
-
return `
|
|
3042
|
-
<div class="search-section">
|
|
3043
|
-
<div class="search-box">
|
|
3044
|
-
<svg class="search-icon" width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5">
|
|
3045
|
-
<circle cx="11" cy="11" r="8"/>
|
|
3046
|
-
<path d="m21 21-4.35-4.35"/>
|
|
3047
|
-
</svg>
|
|
3048
|
-
<input
|
|
3049
|
-
type="text"
|
|
3050
|
-
id="searchInput"
|
|
3051
|
-
class="search-input"
|
|
3052
|
-
placeholder="搜索环境名称或 ID..."
|
|
3053
|
-
oninput="filterEnvs(this.value)"
|
|
3054
|
-
/>
|
|
3055
|
-
<button class="search-clear" onclick="clearSearch()" id="searchClear" style="display: none;">
|
|
3056
|
-
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5">
|
|
3057
|
-
<path d="M18 6L6 18M6 6l12 12"/>
|
|
3058
|
-
</svg>
|
|
3059
|
-
</button>
|
|
3060
|
-
</div>
|
|
3061
|
-
<div class="search-actions">
|
|
3062
|
-
<button class="btn-action" onclick="createNewEnv()" title="新建环境">
|
|
3063
|
-
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5">
|
|
3064
|
-
<path d="M12 5v14M5 12h14"/>
|
|
3065
|
-
</svg>
|
|
3066
|
-
<span class="btn-text">新建环境</span>
|
|
3067
|
-
</button>
|
|
3068
|
-
<button class="btn-action" onclick="refreshEnvList()" title="刷新环境列表">
|
|
3069
|
-
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5">
|
|
3070
|
-
<path d="M21.5 2v6h-6M2.5 22v-6h6M2 11.5a10 10 0 0 1 18.8-4.3M22 12.5a10 10 0 0 1-18.8 4.2"/>
|
|
3071
|
-
</svg>
|
|
3072
|
-
</button>
|
|
3073
|
-
</div>
|
|
3074
|
-
</div>
|
|
3075
|
-
`.trim();
|
|
3076
|
-
}
|
|
3077
|
-
function renderEnvItem(env, index) {
|
|
3078
|
-
const alias = env.Alias || '(未命名)';
|
|
3079
|
-
const envId = env.EnvId;
|
|
3080
|
-
const hasAlias = !!env.Alias;
|
|
3081
|
-
return `
|
|
3082
|
-
<div class="env-item" onclick="selectEnv('${envId}', this)">
|
|
3083
|
-
<div class="env-info">
|
|
3084
|
-
<div class="env-name ${!hasAlias ? 'unnamed' : ''}">${alias}</div>
|
|
3085
|
-
<div class="env-id">${envId}</div>
|
|
3086
|
-
</div>
|
|
3087
|
-
</div>
|
|
3088
|
-
`.trim();
|
|
3089
|
-
}
|
|
3090
|
-
function renderEmptyState(hasInitError) {
|
|
3091
|
-
return `
|
|
3092
|
-
<div class="empty-state">
|
|
3093
|
-
<p class="empty-message">
|
|
3094
|
-
暂无环境,请先<a href="https://buy.cloud.tencent.com/lowcode?buyType=tcb&channel=mcp" onclick="openUrl(event, 'https://buy.cloud.tencent.com/lowcode?buyType=tcb&channel=mcp'); return false;" class="create-env-link">创建环境</a>,然后点击刷新按钮重试
|
|
3095
|
-
</p>
|
|
3096
|
-
</div>
|
|
3097
|
-
`.trim();
|
|
3098
|
-
}
|
|
3099
|
-
function renderNoResultsState() {
|
|
3100
|
-
return `
|
|
3101
|
-
<div class="empty-state" id="noResults" style="display: none;">
|
|
3102
|
-
<svg width="40" height="40" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="opacity: 0.3;">
|
|
3103
|
-
<circle cx="11" cy="11" r="8"/>
|
|
3104
|
-
<path d="m21 21-4.35-4.35"/>
|
|
3105
|
-
</svg>
|
|
3106
|
-
<p class="empty-message">未找到匹配的环境</p>
|
|
3107
|
-
</div>
|
|
3108
|
-
`.trim();
|
|
3109
|
-
}
|
|
3110
|
-
function renderHelpLinks() {
|
|
3111
|
-
return `
|
|
3112
|
-
<div class="help-links">
|
|
3113
|
-
<a href="https://docs.cloudbase.net/ai/cloudbase-ai-toolkit/" onclick="openUrl(event, 'https://docs.cloudbase.net/ai/cloudbase-ai-toolkit/'); return false;" class="help-link">
|
|
3114
|
-
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5">
|
|
3115
|
-
<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/>
|
|
3116
|
-
<polyline points="14 2 14 8 20 8"/>
|
|
3117
|
-
<line x1="16" y1="13" x2="8" y2="13"/>
|
|
3118
|
-
<line x1="16" y1="17" x2="8" y2="17"/>
|
|
3119
|
-
</svg>
|
|
3120
|
-
帮助文档
|
|
3121
|
-
</a>
|
|
3122
|
-
<a href="https://docs.cloudbase.net/ai/cloudbase-ai-toolkit/tutorials" onclick="openUrl(event, 'https://docs.cloudbase.net/ai/cloudbase-ai-toolkit/tutorials'); return false;" class="help-link">
|
|
3123
|
-
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5">
|
|
3124
|
-
<polygon points="23 7 16 12 23 17 23 7"/>
|
|
3125
|
-
<rect x="1" y="5" width="15" height="14" rx="2" ry="2"/>
|
|
3126
|
-
</svg>
|
|
3127
|
-
视频教程
|
|
3128
|
-
</a>
|
|
3129
|
-
<a href="https://github.com/TencentCloudBase/CloudBase-AI-ToolKit" onclick="openUrl(event, 'https://github.com/TencentCloudBase/CloudBase-AI-ToolKit'); return false;" class="help-link">
|
|
3130
|
-
<svg width="13" height="13" viewBox="0 0 24 24" fill="currentColor">
|
|
3131
|
-
<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"/>
|
|
3132
|
-
</svg>
|
|
3133
|
-
GitHub
|
|
3134
|
-
</a>
|
|
3135
|
-
</div>
|
|
3136
|
-
`.trim();
|
|
3137
|
-
}
|
|
3138
|
-
function renderActionButtons(hasEnvs, hasInitError) {
|
|
3139
|
-
return `
|
|
3140
|
-
<div class="actions">
|
|
3141
|
-
<button class="btn btn-secondary" onclick="cancel()">
|
|
3142
|
-
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5">
|
|
3143
|
-
<path d="M18 6L6 18M6 6l12 12"/>
|
|
3144
|
-
</svg>
|
|
3145
|
-
取消
|
|
3146
|
-
</button>
|
|
3147
|
-
<button class="btn btn-primary" id="confirmBtn" onclick="confirm()" disabled>
|
|
3148
|
-
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5">
|
|
3149
|
-
<path d="M20 6L9 17l-5-5"/>
|
|
3150
|
-
</svg>
|
|
3151
|
-
确认选择
|
|
3152
|
-
</button>
|
|
3153
|
-
</div>
|
|
3154
|
-
`.trim();
|
|
3155
|
-
}
|
|
3156
|
-
function renderLoadingState() {
|
|
3157
|
-
return `
|
|
3158
|
-
<div class="loading" id="loading" style="display: none;">
|
|
3159
|
-
<div class="spinner"></div>
|
|
3160
|
-
<span>正在配置环境...</span>
|
|
3161
|
-
</div>
|
|
3162
|
-
`.trim();
|
|
3163
|
-
}
|
|
3164
|
-
function renderSuccessState() {
|
|
3165
|
-
return `
|
|
3166
|
-
<div class="success-state" id="successState" style="display: none;">
|
|
3167
|
-
<div class="success-icon">
|
|
3168
|
-
<svg width="48" height="48" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
3169
|
-
<path d="M20 6L9 17l-5-5"/>
|
|
3170
|
-
</svg>
|
|
3171
|
-
</div>
|
|
3172
|
-
<h2 class="success-title">环境配置成功!</h2>
|
|
3173
|
-
<p class="success-message">已成功选择 CloudBase 环境</p>
|
|
3174
|
-
<div class="selected-env-info">
|
|
3175
|
-
<span class="env-label">环境 ID:</span>
|
|
3176
|
-
<span class="env-value" id="selectedEnvDisplay"></span>
|
|
3177
|
-
</div>
|
|
3178
|
-
</div>
|
|
3179
|
-
`.trim();
|
|
3180
|
-
}
|
|
3181
|
-
function renderErrorBanner(errorContext, sessionId) {
|
|
3182
|
-
const initTcbError = errorContext?.initTcbError;
|
|
3183
|
-
const createEnvError = errorContext?.createEnvError;
|
|
3184
|
-
// Only show initTcbError, hide createEnvError
|
|
3185
|
-
if (!initTcbError) {
|
|
3186
|
-
return '';
|
|
3187
|
-
}
|
|
3188
|
-
return `
|
|
3189
|
-
<div class="info-banner" id="errorBanner">
|
|
3190
|
-
${initTcbError ? `
|
|
3191
|
-
<div class="info-item">
|
|
3192
|
-
<div class="info-header">
|
|
3193
|
-
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
3194
|
-
<circle cx="12" cy="12" r="10"/>
|
|
3195
|
-
<line x1="12" y1="8" x2="12" y2="12"/>
|
|
3196
|
-
<line x1="12" y1="16" x2="12.01" y2="16"/>
|
|
3197
|
-
</svg>
|
|
3198
|
-
<span class="info-title">${escapeHtml(initTcbError.message)}</span>
|
|
3199
|
-
</div>
|
|
3200
|
-
<div class="info-message">${escapeHtml(initTcbError.actionText || '')}</div>
|
|
3201
|
-
${initTcbError.requestId ? `
|
|
3202
|
-
<div class="info-details">
|
|
3203
|
-
<span class="detail-label">错误码:</span> <span class="detail-value">${escapeHtml(initTcbError.code)}</span>
|
|
3204
|
-
<span class="detail-label">请求 ID:</span> <span class="detail-value">${escapeHtml(initTcbError.requestId)}</span>
|
|
3205
|
-
</div>
|
|
3206
|
-
` : ''}
|
|
3207
|
-
${initTcbError.needRealNameAuth ? `
|
|
3208
|
-
<div class="error-action">
|
|
3209
|
-
<a href="${initTcbError.helpUrl || 'https://buy.cloud.tencent.com/lowcode?buyType=tcb&channel=mcp'}" onclick="openUrl(event, '${initTcbError.helpUrl || 'https://buy.cloud.tencent.com/lowcode?buyType=tcb&channel=mcp'}'); return false;" class="error-link">
|
|
3210
|
-
前往实名认证
|
|
3211
|
-
</a>
|
|
3212
|
-
</div>
|
|
3213
|
-
` : ''}
|
|
3214
|
-
${initTcbError.needCamAuth ? `
|
|
3215
|
-
<div class="error-action">
|
|
3216
|
-
<a href="${initTcbError.helpUrl || 'https://buy.cloud.tencent.com/lowcode?buyType=tcb&channel=mcp'}" onclick="openUrl(event, '${initTcbError.helpUrl || 'https://buy.cloud.tencent.com/lowcode?buyType=tcb&channel=mcp'}'); return false;" class="error-link">
|
|
3217
|
-
前往开通 CloudBase 服务
|
|
3218
|
-
</a>
|
|
3219
|
-
${sessionId ? `
|
|
3220
|
-
<button class="error-link error-retry-btn" onclick="retryInitTcb('${sessionId}')">
|
|
3221
|
-
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
3222
|
-
<path d="M23 4v6h-6M1 20v-6h6M3.51 9a9 9 0 0 1 14.85-3.36L23 10M1 14l4.64 4.36A9 9 0 0 0 20.49 15"/>
|
|
3223
|
-
</svg>
|
|
3224
|
-
重试
|
|
3225
|
-
</button>
|
|
3226
|
-
` : ''}
|
|
3227
|
-
</div>
|
|
3228
|
-
` : ''}
|
|
3229
|
-
${!initTcbError.needRealNameAuth && !initTcbError.needCamAuth && initTcbError.helpUrl ? `
|
|
3230
|
-
<div class="error-action">
|
|
3231
|
-
<a href="${initTcbError.helpUrl}" onclick="openUrl(event, '${initTcbError.helpUrl}'); return false;" class="error-link">
|
|
3232
|
-
前往开通 CloudBase 服务
|
|
3233
|
-
</a>
|
|
3234
|
-
${sessionId ? `
|
|
3235
|
-
<button class="error-link error-retry-btn" onclick="retryInitTcb('${sessionId}')">
|
|
3236
|
-
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
3237
|
-
<path d="M23 4v6h-6M1 20v-6h6M3.51 9a9 9 0 0 1 14.85-3.36L23 10M1 14l4.64 4.36A9 9 0 0 0 20.49 15"/>
|
|
3238
|
-
</svg>
|
|
3239
|
-
重试
|
|
3240
|
-
</button>
|
|
3241
|
-
` : ''}
|
|
3242
|
-
</div>
|
|
3243
|
-
` : ''}
|
|
3244
|
-
</div>
|
|
3245
|
-
` : ''}
|
|
3246
|
-
</div>
|
|
3247
|
-
`.trim();
|
|
3248
|
-
}
|
|
3249
|
-
// Helper function to escape HTML
|
|
3250
|
-
function escapeHtml(text) {
|
|
3251
|
-
const map = {
|
|
3252
|
-
'&': '&',
|
|
3253
|
-
'<': '<',
|
|
3254
|
-
'>': '>',
|
|
3255
|
-
'"': '"',
|
|
3256
|
-
"'": '''
|
|
3257
|
-
};
|
|
3258
|
-
return text.replace(/[&<>"']/g, (m) => map[m]);
|
|
3259
|
-
}
|
|
3260
|
-
|
|
3261
|
-
|
|
3262
|
-
/***/ },
|
|
3263
|
-
|
|
3264
|
-
/***/ 4182
|
|
3265
|
-
(__unused_webpack_module, exports) {
|
|
3266
|
-
|
|
3267
|
-
var __webpack_unused_export__;
|
|
3268
|
-
|
|
3269
|
-
/**
|
|
3270
|
-
* HTML template structure
|
|
3271
|
-
*/
|
|
3272
|
-
__webpack_unused_export__ = ({ value: true });
|
|
3273
|
-
exports.buildHTMLPage = buildHTMLPage;
|
|
3274
|
-
function buildHTMLPage(css, body, scripts) {
|
|
3275
|
-
return `
|
|
3276
|
-
<!DOCTYPE html>
|
|
3277
|
-
<html lang="zh-CN">
|
|
3278
|
-
<head>
|
|
3279
|
-
<meta charset="UTF-8">
|
|
3280
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
3281
|
-
<title>CloudBase MCP - 环境配置</title>
|
|
3282
|
-
${css}
|
|
3283
|
-
</head>
|
|
3284
|
-
<body>
|
|
3285
|
-
${body}
|
|
3286
|
-
${scripts}
|
|
3287
|
-
</body>
|
|
3288
|
-
</html>
|
|
3289
|
-
`.trim();
|
|
3290
|
-
}
|
|
3291
|
-
|
|
3292
|
-
|
|
3293
|
-
/***/ },
|
|
3294
|
-
|
|
3295
|
-
/***/ 9903
|
|
3296
|
-
(__unused_webpack_module, exports, __webpack_require__) {
|
|
3297
|
-
|
|
3298
|
-
var __webpack_unused_export__;
|
|
3299
|
-
|
|
3300
|
-
/**
|
|
3301
|
-
* Main renderer for env setup page
|
|
3302
|
-
*/
|
|
3303
|
-
__webpack_unused_export__ = ({ value: true });
|
|
3304
|
-
exports.renderEnvSetupPage = renderEnvSetupPage;
|
|
3305
|
-
const components_js_1 = __webpack_require__(4415);
|
|
3306
|
-
const html_js_1 = __webpack_require__(4182);
|
|
3307
|
-
const scripts_js_1 = __webpack_require__(7335);
|
|
3308
|
-
const styles_js_1 = __webpack_require__(7373);
|
|
3309
|
-
/**
|
|
3310
|
-
* Render the complete env setup page
|
|
3311
|
-
*/
|
|
3312
|
-
function renderEnvSetupPage(options) {
|
|
3313
|
-
const { envs = [], accountInfo, errorContext, sessionId, wsPort, ide } = options;
|
|
3314
|
-
const hasEnvs = envs.length > 0;
|
|
3315
|
-
const hasInitError = !!errorContext?.initTcbError;
|
|
3316
|
-
const hasErrors = !!(errorContext?.initTcbError || errorContext?.createEnvError);
|
|
3317
|
-
// Build env list HTML
|
|
3318
|
-
let envListHTML = '';
|
|
3319
|
-
if (hasEnvs) {
|
|
3320
|
-
envListHTML = `
|
|
3321
|
-
${(0, components_js_1.renderSearchBox)()}
|
|
3322
|
-
<div class="env-list" id="envList">
|
|
3323
|
-
${envs.map((env, index) => (0, components_js_1.renderEnvItem)(env, index)).join('\n')}
|
|
3324
|
-
</div>
|
|
3325
|
-
${(0, components_js_1.renderNoResultsState)()}
|
|
3326
|
-
`;
|
|
3327
|
-
}
|
|
3328
|
-
else {
|
|
3329
|
-
// 暂无环境时也显示搜索框、新建和刷新按钮
|
|
3330
|
-
envListHTML = `
|
|
3331
|
-
${(0, components_js_1.renderSearchBox)()}
|
|
3332
|
-
<div class="env-list" id="envList">
|
|
3333
|
-
${(0, components_js_1.renderEmptyState)(hasInitError)}
|
|
3334
|
-
</div>
|
|
3335
|
-
${(0, components_js_1.renderNoResultsState)()}
|
|
3336
|
-
`;
|
|
3337
|
-
}
|
|
3338
|
-
// Build body HTML
|
|
3339
|
-
const bodyHTML = `
|
|
3340
|
-
<div class="modal">
|
|
3341
|
-
${(0, components_js_1.renderHeader)(accountInfo, ide)}
|
|
3342
|
-
<div class="content">
|
|
3343
|
-
<h1 class="content-title">选择环境</h1>
|
|
3344
|
-
|
|
3345
|
-
${hasErrors ? (0, components_js_1.renderErrorBanner)(errorContext, sessionId) : ''}
|
|
3346
|
-
|
|
3347
|
-
${envListHTML}
|
|
3348
|
-
|
|
3349
|
-
${(0, components_js_1.renderActionButtons)(hasEnvs, hasInitError)}
|
|
3350
|
-
|
|
3351
|
-
${(0, components_js_1.renderHelpLinks)()}
|
|
3352
|
-
|
|
3353
|
-
${(0, components_js_1.renderLoadingState)()}
|
|
3354
|
-
|
|
3355
|
-
${(0, components_js_1.renderSuccessState)()}
|
|
3356
|
-
</div>
|
|
3357
|
-
</div>
|
|
3358
|
-
`;
|
|
3359
|
-
// Get JavaScript with WebSocket port
|
|
3360
|
-
const scripts = (0, scripts_js_1.getJavaScripts)(wsPort);
|
|
3361
|
-
// Assemble final HTML
|
|
3362
|
-
return (0, html_js_1.buildHTMLPage)(styles_js_1.CSS_STYLES, bodyHTML, scripts);
|
|
3363
|
-
}
|
|
3364
|
-
|
|
3365
|
-
|
|
3366
|
-
/***/ },
|
|
3367
|
-
|
|
3368
|
-
/***/ 7335
|
|
3369
|
-
(__unused_webpack_module, exports) {
|
|
3370
|
-
|
|
3371
|
-
var __webpack_unused_export__;
|
|
3372
|
-
|
|
3373
|
-
/**
|
|
3374
|
-
* JavaScript logic for env setup page
|
|
3375
|
-
*/
|
|
3376
|
-
__webpack_unused_export__ = ({ value: true });
|
|
3377
|
-
exports.getJavaScripts = getJavaScripts;
|
|
3378
|
-
function getJavaScripts(wsPort) {
|
|
3379
|
-
return `
|
|
3380
|
-
<script>
|
|
3381
|
-
let selectedEnvId = null;
|
|
3382
|
-
|
|
3383
|
-
// Select environment
|
|
3384
|
-
function selectEnv(envId, element) {
|
|
3385
|
-
console.log('[env-setup] Selecting environment:', envId);
|
|
3386
|
-
selectedEnvId = envId;
|
|
3387
|
-
|
|
3388
|
-
// Remove selected class from all items
|
|
3389
|
-
document.querySelectorAll('.env-item').forEach(item => {
|
|
3390
|
-
item.classList.remove('selected');
|
|
3391
|
-
});
|
|
3392
|
-
|
|
3393
|
-
// Add selected class to current item
|
|
3394
|
-
element.classList.add('selected');
|
|
3395
|
-
|
|
3396
|
-
// Enable confirm button
|
|
3397
|
-
document.getElementById('confirmBtn').disabled = false;
|
|
3398
|
-
}
|
|
3399
|
-
|
|
3400
|
-
// Filter environments
|
|
3401
|
-
function filterEnvs(searchTerm) {
|
|
3402
|
-
const items = document.querySelectorAll('.env-item');
|
|
3403
|
-
const searchClear = document.getElementById('searchClear');
|
|
3404
|
-
let visibleCount = 0;
|
|
3405
|
-
|
|
3406
|
-
items.forEach(item => {
|
|
3407
|
-
const name = item.querySelector('.env-name').textContent.toLowerCase();
|
|
3408
|
-
const id = item.querySelector('.env-id').textContent.toLowerCase();
|
|
3409
|
-
const match = name.includes(searchTerm.toLowerCase()) ||
|
|
3410
|
-
id.includes(searchTerm.toLowerCase());
|
|
3411
|
-
|
|
3412
|
-
item.style.display = match ? 'flex' : 'none';
|
|
3413
|
-
if (match) visibleCount++;
|
|
3414
|
-
});
|
|
3415
|
-
|
|
3416
|
-
// Show/hide clear button
|
|
3417
|
-
if (searchClear) {
|
|
3418
|
-
searchClear.style.display = searchTerm ? 'flex' : 'none';
|
|
3419
|
-
}
|
|
3420
|
-
|
|
3421
|
-
// Show/hide no results message
|
|
3422
|
-
const noResults = document.getElementById('noResults');
|
|
3423
|
-
if (noResults) {
|
|
3424
|
-
const hasItems = items.length > 0;
|
|
3425
|
-
noResults.style.display = visibleCount === 0 && hasItems ? 'block' : 'none';
|
|
3426
|
-
}
|
|
3427
|
-
}
|
|
3428
|
-
|
|
3429
|
-
// Clear search
|
|
3430
|
-
function clearSearch() {
|
|
3431
|
-
const searchInput = document.getElementById('searchInput');
|
|
3432
|
-
if (searchInput) {
|
|
3433
|
-
searchInput.value = '';
|
|
3434
|
-
filterEnvs('');
|
|
3435
|
-
searchInput.focus();
|
|
3436
|
-
}
|
|
3437
|
-
}
|
|
3438
|
-
|
|
3439
|
-
// WebSocket: same host as page so it works in remote VS Code / Cloud IDE (no localhost)
|
|
3440
|
-
const wsScheme = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
|
|
3441
|
-
const wsUrl = wsScheme + '//' + window.location.host;
|
|
3442
|
-
const ws = new WebSocket(wsUrl);
|
|
3443
|
-
|
|
3444
|
-
ws.onopen = () => {
|
|
3445
|
-
console.log('[env-setup] WebSocket connected');
|
|
3446
|
-
// Send session ID to server to associate this WebSocket with the session
|
|
3447
|
-
const sessionId = window.location.pathname.split('/').pop();
|
|
3448
|
-
ws.send(JSON.stringify({
|
|
3449
|
-
type: 'registerSession',
|
|
3450
|
-
sessionId: sessionId
|
|
3451
|
-
}));
|
|
3452
|
-
};
|
|
3453
|
-
|
|
3454
|
-
ws.onmessage = (event) => {
|
|
3455
|
-
const data = JSON.parse(event.data);
|
|
3456
|
-
console.log('[env-setup] Received message:', data);
|
|
3457
|
-
|
|
3458
|
-
if (data.type === 'success' && data.envId) {
|
|
3459
|
-
showSuccess(data.envId);
|
|
3460
|
-
} else if (data.type === 'envListRefreshed') {
|
|
3461
|
-
if (data.success) {
|
|
3462
|
-
console.log('[env-setup] Environment list refreshed, reloading page');
|
|
3463
|
-
// Reload the page to show updated environment list
|
|
3464
|
-
window.location.reload();
|
|
3465
|
-
} else {
|
|
3466
|
-
console.error('[env-setup] Failed to refresh environment list:', data.error);
|
|
3467
|
-
alert('刷新环境列表失败: ' + (data.error || '未知错误'));
|
|
3468
|
-
}
|
|
3469
|
-
}
|
|
3470
|
-
};
|
|
3471
|
-
|
|
3472
|
-
ws.onerror = (error) => {
|
|
3473
|
-
console.error('[env-setup] WebSocket error:', error);
|
|
3474
|
-
};
|
|
3475
|
-
|
|
3476
|
-
ws.onclose = () => {
|
|
3477
|
-
console.log('[env-setup] WebSocket closed');
|
|
3478
|
-
};
|
|
3479
|
-
|
|
3480
|
-
// Show success state
|
|
3481
|
-
function showSuccess(envId) {
|
|
3482
|
-
document.querySelector('.content').style.display = 'none';
|
|
3483
|
-
const successState = document.getElementById('successState');
|
|
3484
|
-
const selectedEnvDisplay = document.getElementById('selectedEnvDisplay');
|
|
3485
|
-
|
|
3486
|
-
if (successState && selectedEnvDisplay) {
|
|
3487
|
-
selectedEnvDisplay.textContent = envId;
|
|
3488
|
-
successState.style.display = 'block';
|
|
3489
|
-
}
|
|
3490
|
-
}
|
|
3491
|
-
|
|
3492
|
-
// Confirm selection
|
|
3493
|
-
function confirm() {
|
|
3494
|
-
if (!selectedEnvId) {
|
|
3495
|
-
console.warn('[env-setup] No environment selected');
|
|
3496
|
-
return;
|
|
3497
|
-
}
|
|
3498
|
-
|
|
3499
|
-
console.log('[env-setup] Confirming selection:', selectedEnvId);
|
|
3500
|
-
|
|
3501
|
-
if (ws.readyState === WebSocket.OPEN) {
|
|
3502
|
-
ws.send(JSON.stringify({
|
|
3503
|
-
type: 'envId',
|
|
3504
|
-
data: selectedEnvId,
|
|
3505
|
-
cancelled: false
|
|
3506
|
-
}));
|
|
3507
|
-
|
|
3508
|
-
// Close window immediately after sending confirmation
|
|
3509
|
-
setTimeout(() => {
|
|
3510
|
-
window.close();
|
|
3511
|
-
}, 100);
|
|
3512
|
-
} else {
|
|
3513
|
-
console.error('[env-setup] WebSocket not connected');
|
|
3514
|
-
alert('连接已断开,请刷新页面重试');
|
|
3515
|
-
}
|
|
3516
|
-
}
|
|
3517
|
-
|
|
3518
|
-
// Cancel
|
|
3519
|
-
function cancel() {
|
|
3520
|
-
console.log('[env-setup] Cancelling');
|
|
3521
|
-
if (ws.readyState === WebSocket.OPEN) {
|
|
3522
|
-
ws.send(JSON.stringify({
|
|
3523
|
-
type: 'envId',
|
|
3524
|
-
data: null,
|
|
3525
|
-
cancelled: true
|
|
3526
|
-
}));
|
|
3527
|
-
|
|
3528
|
-
// Close window immediately after sending cancellation
|
|
3529
|
-
setTimeout(() => {
|
|
3530
|
-
window.close();
|
|
3531
|
-
}, 100);
|
|
3532
|
-
}
|
|
3533
|
-
}
|
|
3534
|
-
|
|
3535
|
-
// Switch account
|
|
3536
|
-
function switchAccount() {
|
|
3537
|
-
console.log('[env-setup] Switching account');
|
|
3538
|
-
if (ws.readyState === WebSocket.OPEN) {
|
|
3539
|
-
ws.send(JSON.stringify({
|
|
3540
|
-
type: 'envId',
|
|
3541
|
-
data: null,
|
|
3542
|
-
switch: true
|
|
3543
|
-
}));
|
|
3544
|
-
|
|
3545
|
-
// Close window immediately after sending switch request
|
|
3546
|
-
setTimeout(() => {
|
|
3547
|
-
window.close();
|
|
3548
|
-
}, 100);
|
|
3549
|
-
}
|
|
3550
|
-
}
|
|
3551
|
-
|
|
3552
|
-
// Universal URL opener - works with all IDEs including CodeBuddy
|
|
3553
|
-
async function openUrl(event, url) {
|
|
3554
|
-
if (event) {
|
|
3555
|
-
event.preventDefault();
|
|
3556
|
-
}
|
|
3557
|
-
|
|
3558
|
-
console.log('[env-setup] Opening URL:', url);
|
|
3559
|
-
|
|
3560
|
-
try {
|
|
3561
|
-
const response = await fetch('/api/open-url', {
|
|
3562
|
-
method: 'POST',
|
|
3563
|
-
headers: {
|
|
3564
|
-
'Content-Type': 'application/json',
|
|
3565
|
-
},
|
|
3566
|
-
body: JSON.stringify({ url }),
|
|
3567
|
-
});
|
|
3568
|
-
|
|
3569
|
-
const result = await response.json();
|
|
3570
|
-
|
|
3571
|
-
if (!result.success) {
|
|
3572
|
-
console.error('[env-setup] Failed to open URL:', result.error);
|
|
3573
|
-
// Fallback to direct open if API fails
|
|
3574
|
-
window.open(url, '_blank');
|
|
3575
|
-
}
|
|
3576
|
-
} catch (error) {
|
|
3577
|
-
console.error('[env-setup] Error opening URL:', error);
|
|
3578
|
-
// Fallback to direct open if API fails
|
|
3579
|
-
window.open(url, '_blank');
|
|
3580
|
-
}
|
|
3581
|
-
}
|
|
3582
|
-
|
|
3583
|
-
// Create new environment
|
|
3584
|
-
function createNewEnv() {
|
|
3585
|
-
console.log('[env-setup] Creating new environment');
|
|
3586
|
-
// TODO(intl): 国际站购买链接域名待确认(buy.cloud.tencent.com 是否服务国际站),
|
|
3587
|
-
// 确认后按站点切换,参考 utils/site-map.ts 的站点解析链(specs/intl-site-enhancement 需求 4.3)
|
|
3588
|
-
openUrl(null, 'https://buy.cloud.tencent.com/lowcode?buyType=tcb&channel=mcp');
|
|
3589
|
-
}
|
|
3590
|
-
|
|
3591
|
-
// Refresh environment list
|
|
3592
|
-
function refreshEnvList() {
|
|
3593
|
-
console.log('[env-setup] Refreshing environment list');
|
|
3594
|
-
if (ws.readyState === WebSocket.OPEN) {
|
|
3595
|
-
ws.send(JSON.stringify({
|
|
3596
|
-
type: 'refreshEnvList'
|
|
3597
|
-
}));
|
|
3598
|
-
|
|
3599
|
-
// Add rotation animation
|
|
3600
|
-
const btn = event.target.closest('.btn-refresh');
|
|
3601
|
-
if (btn) {
|
|
3602
|
-
btn.style.transform = 'rotate(360deg)';
|
|
3603
|
-
setTimeout(() => {
|
|
3604
|
-
btn.style.transform = 'rotate(0deg)';
|
|
3605
|
-
}, 600);
|
|
3606
|
-
}
|
|
3607
|
-
}
|
|
3608
|
-
}
|
|
3609
|
-
|
|
3610
|
-
// Retry init TCB
|
|
3611
|
-
function retryInitTcb(sessionId) {
|
|
3612
|
-
console.log('[env-setup] Retrying TCB initialization:', sessionId);
|
|
3613
|
-
if (ws.readyState === WebSocket.OPEN) {
|
|
3614
|
-
ws.send(JSON.stringify({
|
|
3615
|
-
type: 'retryInitTcb',
|
|
3616
|
-
sessionId: sessionId
|
|
3617
|
-
}));
|
|
3618
|
-
}
|
|
3619
|
-
}
|
|
3620
|
-
|
|
3621
|
-
// Initialize on page load
|
|
3622
|
-
window.addEventListener('load', () => {
|
|
3623
|
-
// Auto-select first environment if exists
|
|
3624
|
-
const firstEnvItem = document.querySelector('.env-item');
|
|
3625
|
-
if (firstEnvItem && !selectedEnvId) {
|
|
3626
|
-
const envId = firstEnvItem.getAttribute('onclick')?.match(/selectEnv\\('([^']+)'/)?.[1];
|
|
3627
|
-
if (envId) {
|
|
3628
|
-
selectEnv(envId, firstEnvItem);
|
|
3629
|
-
console.log('[env-setup] Auto-selected first environment:', envId);
|
|
3630
|
-
}
|
|
3631
|
-
}
|
|
3632
|
-
});
|
|
3633
|
-
</script>
|
|
3634
|
-
`.trim();
|
|
3635
|
-
}
|
|
3636
|
-
|
|
3637
|
-
|
|
3638
|
-
/***/ },
|
|
3639
|
-
|
|
3640
|
-
/***/ 7373
|
|
3641
|
-
(__unused_webpack_module, exports) {
|
|
3642
|
-
|
|
3643
|
-
var __webpack_unused_export__;
|
|
3644
|
-
|
|
3645
|
-
/**
|
|
3646
|
-
* CSS styles for env setup page
|
|
3647
|
-
*/
|
|
3648
|
-
__webpack_unused_export__ = ({ value: true });
|
|
3649
|
-
exports.CSS_STYLES = void 0;
|
|
3650
|
-
exports.CSS_STYLES = `
|
|
3651
|
-
<style>
|
|
3652
|
-
@import url('https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500;600&display=swap');
|
|
3653
|
-
|
|
3654
|
-
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
3655
|
-
|
|
3656
|
-
:root {
|
|
3657
|
-
--primary-color: #1a1a1a;
|
|
3658
|
-
--primary-hover: #000000;
|
|
3659
|
-
--accent-color: #67E9E9;
|
|
3660
|
-
--accent-hover: #2BCCCC;
|
|
3661
|
-
--text-primary: #ffffff;
|
|
3662
|
-
--text-secondary: #a0a0a0;
|
|
3663
|
-
--border-color: rgba(255, 255, 255, 0.15);
|
|
3664
|
-
--bg-secondary: rgba(255, 255, 255, 0.08);
|
|
3665
|
-
--bg-glass: rgba(26, 26, 26, 0.95);
|
|
3666
|
-
--shadow: 0 25px 50px rgba(0, 0, 0, 0.3), 0 10px 20px rgba(0, 0, 0, 0.2);
|
|
3667
|
-
--font-mono: 'JetBrains Mono', 'SF Mono', 'Monaco', monospace;
|
|
3668
|
-
--header-bg: linear-gradient(135deg, #0a0a0a 0%, #1a1a1a 50%, #0d1117 100%);
|
|
3669
|
-
}
|
|
3670
|
-
|
|
3671
|
-
body {
|
|
3672
|
-
font-family: var(--font-mono);
|
|
3673
|
-
background: linear-gradient(135deg, #0a0a0a 0%, #1a1a1a 100%);
|
|
3674
|
-
min-height: 100vh;
|
|
3675
|
-
display: flex;
|
|
3676
|
-
align-items: center;
|
|
3677
|
-
justify-content: center;
|
|
3678
|
-
padding: 20px;
|
|
3679
|
-
position: relative;
|
|
3680
|
-
overflow-x: hidden;
|
|
3681
|
-
overflow-y: auto;
|
|
3682
|
-
}
|
|
3683
|
-
|
|
3684
|
-
/* Custom scrollbar styles */
|
|
3685
|
-
::-webkit-scrollbar {
|
|
3686
|
-
width: 8px;
|
|
3687
|
-
}
|
|
3688
|
-
|
|
3689
|
-
::-webkit-scrollbar-track {
|
|
3690
|
-
background: rgba(255, 255, 255, 0.05);
|
|
3691
|
-
border-radius: 4px;
|
|
3692
|
-
}
|
|
3693
|
-
|
|
3694
|
-
::-webkit-scrollbar-thumb {
|
|
3695
|
-
background: var(--accent-color);
|
|
3696
|
-
border-radius: 4px;
|
|
3697
|
-
}
|
|
3698
|
-
|
|
3699
|
-
::-webkit-scrollbar-thumb:hover {
|
|
3700
|
-
background: var(--accent-hover);
|
|
3701
|
-
}
|
|
3702
|
-
|
|
3703
|
-
body::before {
|
|
3704
|
-
content: '';
|
|
3705
|
-
position: fixed;
|
|
3706
|
-
top: 0; left: 0; right: 0; bottom: 0;
|
|
3707
|
-
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;
|
|
3708
|
-
pointer-events: none;
|
|
3709
|
-
z-index: -1;
|
|
3710
|
-
}
|
|
3711
|
-
|
|
3712
|
-
body::after {
|
|
3713
|
-
content: '';
|
|
3714
|
-
position: fixed;
|
|
3715
|
-
top: 50%; left: 50%;
|
|
3716
|
-
width: 500px; height: 500px;
|
|
3717
|
-
background: radial-gradient(circle, rgba(103, 233, 233, 0.05) 0%, transparent 70%);
|
|
3718
|
-
transform: translate(-50%, -50%);
|
|
3719
|
-
pointer-events: none;
|
|
3720
|
-
z-index: -1;
|
|
3721
|
-
animation: pulse 8s ease-in-out infinite;
|
|
3722
|
-
}
|
|
3723
|
-
|
|
3724
|
-
@keyframes pulse {
|
|
3725
|
-
0%, 100% { opacity: 0.3; transform: translate(-50%, -50%) scale(1); }
|
|
3726
|
-
50% { opacity: 0.6; transform: translate(-50%, -50%) scale(1.1); }
|
|
3727
|
-
}
|
|
3728
|
-
|
|
3729
|
-
.modal {
|
|
3730
|
-
background: var(--bg-glass);
|
|
3731
|
-
backdrop-filter: blur(20px);
|
|
3732
|
-
border-radius: 20px;
|
|
3733
|
-
box-shadow: var(--shadow);
|
|
3734
|
-
border: 1px solid var(--border-color);
|
|
3735
|
-
width: 100%;
|
|
3736
|
-
max-width: 640px;
|
|
3737
|
-
overflow: hidden;
|
|
3738
|
-
animation: modalIn 0.6s cubic-bezier(0.175, 0.885, 0.32, 1.275);
|
|
3739
|
-
position: relative;
|
|
3740
|
-
}
|
|
3741
|
-
|
|
3742
|
-
.modal::before {
|
|
3743
|
-
content: '';
|
|
3744
|
-
position: absolute;
|
|
3745
|
-
top: 0; left: 0; right: 0; bottom: 0;
|
|
3746
|
-
background: linear-gradient(45deg, transparent 30%, rgba(255,255,255,0.02) 50%, transparent 70%);
|
|
3747
|
-
animation: shimmer 3s infinite;
|
|
3748
|
-
pointer-events: none;
|
|
3749
|
-
}
|
|
3750
|
-
|
|
3751
|
-
@keyframes shimmer {
|
|
3752
|
-
0% { transform: translateX(-100%); }
|
|
3753
|
-
100% { transform: translateX(100%); }
|
|
3754
|
-
}
|
|
3755
|
-
|
|
3756
|
-
@keyframes modalIn {
|
|
3757
|
-
from {
|
|
3758
|
-
opacity: 0;
|
|
3759
|
-
transform: scale(0.9) translateY(-20px);
|
|
3760
|
-
}
|
|
3761
|
-
to {
|
|
3762
|
-
opacity: 1;
|
|
3763
|
-
transform: scale(1) translateY(0);
|
|
3764
|
-
}
|
|
3765
|
-
}
|
|
3766
|
-
|
|
3767
|
-
.header {
|
|
3768
|
-
background: var(--header-bg);
|
|
3769
|
-
color: var(--text-primary);
|
|
3770
|
-
padding: 20px 32px;
|
|
3771
|
-
display: flex;
|
|
3772
|
-
align-items: center;
|
|
3773
|
-
justify-content: space-between;
|
|
3774
|
-
position: relative;
|
|
3775
|
-
overflow: hidden;
|
|
3776
|
-
}
|
|
3777
|
-
|
|
3778
|
-
.header::before {
|
|
3779
|
-
content: '';
|
|
3780
|
-
position: absolute;
|
|
3781
|
-
top: 0; left: 0; right: 0; bottom: 0;
|
|
3782
|
-
background: linear-gradient(45deg, transparent 30%, rgba(255,255,255,0.03) 50%, transparent 70%);
|
|
3783
|
-
animation: headerShimmer 4s infinite;
|
|
3784
|
-
pointer-events: none;
|
|
3785
|
-
}
|
|
3786
|
-
|
|
3787
|
-
@keyframes headerShimmer {
|
|
3788
|
-
0% { transform: translateX(-100%); }
|
|
3789
|
-
100% { transform: translateX(100%); }
|
|
3790
|
-
}
|
|
3791
|
-
|
|
3792
|
-
.header-left {
|
|
3793
|
-
display: flex;
|
|
3794
|
-
align-items: center;
|
|
3795
|
-
gap: 16px;
|
|
3796
|
-
z-index: 1;
|
|
3797
|
-
}
|
|
3798
|
-
|
|
3799
|
-
.logo {
|
|
3800
|
-
width: 32px;
|
|
3801
|
-
height: 32px;
|
|
3802
|
-
filter: drop-shadow(0 4px 8px rgba(0,0,0,0.2));
|
|
3803
|
-
animation: logoFloat 3s ease-in-out infinite;
|
|
3804
|
-
}
|
|
3805
|
-
|
|
3806
|
-
@keyframes logoFloat {
|
|
3807
|
-
0%, 100% { transform: translateY(0px); }
|
|
3808
|
-
50% { transform: translateY(-3px); }
|
|
3809
|
-
}
|
|
3810
|
-
|
|
3811
|
-
.title {
|
|
3812
|
-
font-size: 18px;
|
|
3813
|
-
font-weight: 600;
|
|
3814
|
-
text-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
|
3815
|
-
}
|
|
3816
|
-
|
|
3817
|
-
.header-right {
|
|
3818
|
-
display: flex;
|
|
3819
|
-
align-items: center;
|
|
3820
|
-
gap: 16px;
|
|
3821
|
-
z-index: 1;
|
|
3822
|
-
}
|
|
3823
|
-
|
|
3824
|
-
.account-section {
|
|
3825
|
-
display: flex;
|
|
3826
|
-
align-items: center;
|
|
3827
|
-
gap: 8px;
|
|
3828
|
-
padding: 6px 12px;
|
|
3829
|
-
background: rgba(255, 255, 255, 0.04);
|
|
3830
|
-
border: 1px solid var(--border-color);
|
|
3831
|
-
border-radius: 8px;
|
|
3832
|
-
}
|
|
3833
|
-
|
|
3834
|
-
.account-uin {
|
|
3835
|
-
font-size: 12px;
|
|
3836
|
-
color: var(--text-secondary);
|
|
3837
|
-
font-family: var(--font-mono);
|
|
3838
|
-
}
|
|
3839
|
-
|
|
3840
|
-
.btn-switch {
|
|
3841
|
-
display: flex;
|
|
3842
|
-
align-items: center;
|
|
3843
|
-
justify-content: center;
|
|
3844
|
-
padding: 4px;
|
|
3845
|
-
background: transparent;
|
|
3846
|
-
border: none;
|
|
3847
|
-
color: var(--text-secondary);
|
|
3848
|
-
cursor: pointer;
|
|
3849
|
-
transition: all 0.3s ease;
|
|
3850
|
-
border-radius: 4px;
|
|
3851
|
-
}
|
|
3852
|
-
|
|
3853
|
-
.btn-switch:hover {
|
|
3854
|
-
background: rgba(255, 255, 255, 0.1);
|
|
3855
|
-
color: var(--accent-color);
|
|
3856
|
-
}
|
|
3857
|
-
|
|
3858
|
-
.btn-icon-link {
|
|
3859
|
-
display: flex;
|
|
3860
|
-
align-items: center;
|
|
3861
|
-
justify-content: center;
|
|
3862
|
-
padding: 7px;
|
|
3863
|
-
background: transparent;
|
|
3864
|
-
border: 1px solid var(--border-color);
|
|
3865
|
-
border-radius: 8px;
|
|
3866
|
-
color: var(--text-primary);
|
|
3867
|
-
cursor: pointer;
|
|
3868
|
-
transition: all 0.3s ease;
|
|
3869
|
-
text-decoration: none;
|
|
3870
|
-
}
|
|
3871
|
-
|
|
3872
|
-
.btn-icon-link:hover {
|
|
3873
|
-
background: rgba(255, 255, 255, 0.08);
|
|
3874
|
-
border-color: var(--accent-color);
|
|
3875
|
-
}
|
|
3876
|
-
|
|
3877
|
-
.content {
|
|
3878
|
-
padding: 32px 32px 28px;
|
|
3879
|
-
}
|
|
3880
|
-
|
|
3881
|
-
.content-title {
|
|
3882
|
-
font-size: 20px;
|
|
3883
|
-
font-weight: 600;
|
|
3884
|
-
color: var(--text-primary);
|
|
3885
|
-
margin-bottom: 28px;
|
|
3886
|
-
text-align: center;
|
|
3887
|
-
letter-spacing: -0.01em;
|
|
3888
|
-
}
|
|
3889
|
-
|
|
3890
|
-
/* Search section styles */
|
|
3891
|
-
.search-section {
|
|
3892
|
-
display: flex;
|
|
3893
|
-
gap: 10px;
|
|
3894
|
-
margin-bottom: 24px;
|
|
3895
|
-
}
|
|
3896
|
-
|
|
3897
|
-
.search-box {
|
|
3898
|
-
position: relative;
|
|
3899
|
-
flex: 1;
|
|
3900
|
-
}
|
|
3901
|
-
|
|
3902
|
-
.search-icon {
|
|
3903
|
-
position: absolute;
|
|
3904
|
-
left: 14px;
|
|
3905
|
-
top: 50%;
|
|
3906
|
-
transform: translateY(-50%);
|
|
3907
|
-
color: var(--text-secondary);
|
|
3908
|
-
pointer-events: none;
|
|
3909
|
-
z-index: 1;
|
|
3910
|
-
}
|
|
3911
|
-
|
|
3912
|
-
.search-input {
|
|
3913
|
-
width: 100%;
|
|
3914
|
-
padding: 11px 38px 11px 38px;
|
|
3915
|
-
background: var(--bg-secondary);
|
|
3916
|
-
border: 1px solid var(--border-color);
|
|
3917
|
-
border-radius: 10px;
|
|
3918
|
-
color: var(--text-primary);
|
|
3919
|
-
font-size: 13px;
|
|
3920
|
-
font-family: var(--font-mono);
|
|
3921
|
-
transition: all 0.3s ease;
|
|
3922
|
-
}
|
|
3923
|
-
|
|
3924
|
-
.search-input:focus {
|
|
3925
|
-
outline: none;
|
|
3926
|
-
border-color: var(--accent-color);
|
|
3927
|
-
background: rgba(255, 255, 255, 0.1);
|
|
3928
|
-
box-shadow: 0 0 0 3px rgba(103, 233, 233, 0.08);
|
|
3929
|
-
}
|
|
3930
|
-
|
|
3931
|
-
.search-input::placeholder {
|
|
3932
|
-
color: var(--text-secondary);
|
|
3933
|
-
opacity: 0.6;
|
|
3934
|
-
}
|
|
3935
|
-
|
|
3936
|
-
.search-clear {
|
|
3937
|
-
position: absolute;
|
|
3938
|
-
right: 12px;
|
|
3939
|
-
top: 50%;
|
|
3940
|
-
transform: translateY(-50%);
|
|
3941
|
-
background: transparent;
|
|
3942
|
-
border: none;
|
|
3943
|
-
color: var(--text-secondary);
|
|
3944
|
-
cursor: pointer;
|
|
3945
|
-
padding: 4px;
|
|
3946
|
-
display: flex;
|
|
3947
|
-
align-items: center;
|
|
3948
|
-
justify-content: center;
|
|
3949
|
-
border-radius: 4px;
|
|
3950
|
-
transition: all 0.3s ease;
|
|
3951
|
-
z-index: 1;
|
|
3952
|
-
}
|
|
3953
|
-
|
|
3954
|
-
.search-clear:hover {
|
|
3955
|
-
color: var(--text-primary);
|
|
3956
|
-
background: rgba(255, 255, 255, 0.1);
|
|
3957
|
-
}
|
|
3958
|
-
|
|
3959
|
-
.search-actions {
|
|
3960
|
-
display: flex;
|
|
3961
|
-
gap: 8px;
|
|
3962
|
-
}
|
|
3963
|
-
|
|
3964
|
-
.btn-action {
|
|
3965
|
-
display: flex;
|
|
3966
|
-
align-items: center;
|
|
3967
|
-
justify-content: center;
|
|
3968
|
-
gap: 6px;
|
|
3969
|
-
padding: 11px 14px;
|
|
3970
|
-
background: var(--bg-secondary);
|
|
3971
|
-
border: 1px solid var(--border-color);
|
|
3972
|
-
border-radius: 10px;
|
|
3973
|
-
color: var(--text-secondary);
|
|
3974
|
-
cursor: pointer;
|
|
3975
|
-
transition: all 0.3s ease;
|
|
3976
|
-
}
|
|
3977
|
-
|
|
3978
|
-
.btn-action:hover {
|
|
3979
|
-
background: rgba(255, 255, 255, 0.1);
|
|
3980
|
-
border-color: rgba(103, 233, 233, 0.5);
|
|
3981
|
-
color: var(--accent-color);
|
|
3982
|
-
}
|
|
3983
|
-
|
|
3984
|
-
.btn-action:active {
|
|
3985
|
-
transform: scale(0.95);
|
|
3986
|
-
}
|
|
3987
|
-
|
|
3988
|
-
.btn-text {
|
|
3989
|
-
font-size: 13px;
|
|
3990
|
-
font-weight: 500;
|
|
3991
|
-
}
|
|
3992
|
-
|
|
3993
|
-
.env-list {
|
|
3994
|
-
max-height: 340px;
|
|
3995
|
-
overflow-y: auto;
|
|
3996
|
-
padding-right: 6px;
|
|
3997
|
-
margin-bottom: 24px;
|
|
3998
|
-
}
|
|
3999
|
-
|
|
4000
|
-
.env-list::-webkit-scrollbar {
|
|
4001
|
-
width: 5px;
|
|
4002
|
-
}
|
|
4003
|
-
|
|
4004
|
-
.env-list::-webkit-scrollbar-track {
|
|
4005
|
-
background: transparent;
|
|
4006
|
-
}
|
|
4007
|
-
|
|
4008
|
-
.env-list::-webkit-scrollbar-thumb {
|
|
4009
|
-
background: var(--accent-color);
|
|
4010
|
-
border-radius: 3px;
|
|
4011
|
-
}
|
|
4012
|
-
|
|
4013
|
-
/* Env item styles */
|
|
4014
|
-
.env-item {
|
|
4015
|
-
display: flex;
|
|
4016
|
-
align-items: center;
|
|
4017
|
-
gap: 14px;
|
|
4018
|
-
padding: 14px 16px;
|
|
4019
|
-
background: var(--bg-secondary);
|
|
4020
|
-
border: 1px solid var(--border-color);
|
|
4021
|
-
border-radius: 10px;
|
|
4022
|
-
margin-bottom: 10px;
|
|
4023
|
-
cursor: pointer;
|
|
4024
|
-
transition: all 0.3s ease;
|
|
4025
|
-
position: relative;
|
|
4026
|
-
}
|
|
4027
|
-
|
|
4028
|
-
.env-item:hover {
|
|
4029
|
-
background: rgba(255, 255, 255, 0.1);
|
|
4030
|
-
border-color: rgba(103, 233, 233, 0.5);
|
|
4031
|
-
transform: translateX(2px);
|
|
4032
|
-
}
|
|
4033
|
-
|
|
4034
|
-
.env-item.selected {
|
|
4035
|
-
background: rgba(103, 233, 233, 0.12);
|
|
4036
|
-
border-color: var(--accent-color);
|
|
4037
|
-
box-shadow: 0 0 0 1px rgba(103, 233, 233, 0.2);
|
|
4038
|
-
}
|
|
4039
|
-
|
|
4040
|
-
.env-item.selected::after {
|
|
4041
|
-
content: '';
|
|
4042
|
-
position: absolute;
|
|
4043
|
-
right: 16px;
|
|
4044
|
-
top: 50%;
|
|
4045
|
-
transform: translateY(-50%);
|
|
4046
|
-
width: 6px;
|
|
4047
|
-
height: 6px;
|
|
4048
|
-
background: var(--accent-color);
|
|
4049
|
-
border-radius: 50%;
|
|
4050
|
-
box-shadow: 0 0 8px var(--accent-color);
|
|
4051
|
-
}
|
|
4052
|
-
|
|
4053
|
-
.env-info {
|
|
4054
|
-
flex: 1;
|
|
4055
|
-
min-width: 0;
|
|
4056
|
-
}
|
|
4057
|
-
|
|
4058
|
-
.env-name {
|
|
4059
|
-
font-size: 15px;
|
|
4060
|
-
font-weight: 500;
|
|
4061
|
-
color: var(--text-primary);
|
|
4062
|
-
margin-bottom: 5px;
|
|
4063
|
-
overflow: hidden;
|
|
4064
|
-
text-overflow: ellipsis;
|
|
4065
|
-
white-space: nowrap;
|
|
4066
|
-
letter-spacing: -0.01em;
|
|
4067
|
-
}
|
|
4068
|
-
|
|
4069
|
-
.env-name.unnamed {
|
|
4070
|
-
opacity: 0.5;
|
|
4071
|
-
}
|
|
4072
|
-
|
|
4073
|
-
.env-id {
|
|
4074
|
-
font-size: 11px;
|
|
4075
|
-
color: var(--text-secondary);
|
|
4076
|
-
font-family: var(--font-mono);
|
|
4077
|
-
word-break: break-all;
|
|
4078
|
-
opacity: 0.8;
|
|
4079
|
-
}
|
|
4080
|
-
|
|
4081
|
-
/* Empty state */
|
|
4082
|
-
.empty-state {
|
|
4083
|
-
padding: 60px 20px;
|
|
4084
|
-
text-align: center;
|
|
4085
|
-
}
|
|
4086
|
-
|
|
4087
|
-
.empty-message {
|
|
4088
|
-
font-size: 14px;
|
|
4089
|
-
color: var(--text-secondary);
|
|
4090
|
-
opacity: 0.7;
|
|
4091
|
-
line-height: 1.6;
|
|
4092
|
-
}
|
|
4093
|
-
|
|
4094
|
-
.create-env-link {
|
|
4095
|
-
color: var(--accent-color);
|
|
4096
|
-
text-decoration: none;
|
|
4097
|
-
border-bottom: 1px solid transparent;
|
|
4098
|
-
transition: all 0.3s ease;
|
|
4099
|
-
}
|
|
4100
|
-
|
|
4101
|
-
.create-env-link:hover {
|
|
4102
|
-
border-bottom-color: var(--accent-color);
|
|
4103
|
-
opacity: 0.8;
|
|
4104
|
-
}
|
|
4105
|
-
|
|
4106
|
-
/* Action buttons */
|
|
4107
|
-
.actions {
|
|
4108
|
-
display: flex;
|
|
4109
|
-
gap: 10px;
|
|
4110
|
-
margin-top: 28px;
|
|
4111
|
-
}
|
|
4112
|
-
|
|
4113
|
-
.btn {
|
|
4114
|
-
flex: 1;
|
|
4115
|
-
padding: 12px 20px;
|
|
4116
|
-
border: none;
|
|
4117
|
-
border-radius: 10px;
|
|
4118
|
-
font-size: 13px;
|
|
4119
|
-
font-weight: 500;
|
|
4120
|
-
cursor: pointer;
|
|
4121
|
-
transition: all 0.3s ease;
|
|
4122
|
-
display: flex;
|
|
4123
|
-
align-items: center;
|
|
4124
|
-
justify-content: center;
|
|
4125
|
-
gap: 6px;
|
|
4126
|
-
font-family: var(--font-mono);
|
|
4127
|
-
letter-spacing: -0.01em;
|
|
4128
|
-
}
|
|
4129
|
-
|
|
4130
|
-
.btn-primary {
|
|
4131
|
-
background: var(--accent-color);
|
|
4132
|
-
color: var(--primary-color);
|
|
4133
|
-
border: 1px solid var(--accent-color);
|
|
4134
|
-
}
|
|
4135
|
-
|
|
4136
|
-
.btn-primary:hover:not(:disabled) {
|
|
4137
|
-
background: var(--accent-hover);
|
|
4138
|
-
box-shadow: 0 4px 12px rgba(103, 233, 233, 0.3);
|
|
4139
|
-
}
|
|
4140
|
-
|
|
4141
|
-
.btn-primary:disabled {
|
|
4142
|
-
background: var(--bg-secondary);
|
|
4143
|
-
color: var(--text-secondary);
|
|
4144
|
-
cursor: not-allowed;
|
|
4145
|
-
opacity: 0.5;
|
|
4146
|
-
border-color: var(--border-color);
|
|
4147
|
-
}
|
|
4148
|
-
|
|
4149
|
-
.btn-secondary {
|
|
4150
|
-
background: transparent;
|
|
4151
|
-
color: var(--text-primary);
|
|
4152
|
-
border: 1px solid var(--border-color);
|
|
4153
|
-
}
|
|
4154
|
-
|
|
4155
|
-
.btn-secondary:hover {
|
|
4156
|
-
background: var(--bg-secondary);
|
|
4157
|
-
border-color: rgba(255, 255, 255, 0.3);
|
|
4158
|
-
}
|
|
4159
|
-
|
|
4160
|
-
/* Help links */
|
|
4161
|
-
.help-links {
|
|
4162
|
-
display: flex;
|
|
4163
|
-
gap: 20px;
|
|
4164
|
-
justify-content: center;
|
|
4165
|
-
margin-top: 24px;
|
|
4166
|
-
padding-top: 24px;
|
|
4167
|
-
border-top: 1px solid rgba(255, 255, 255, 0.06);
|
|
4168
|
-
}
|
|
4169
|
-
|
|
4170
|
-
.help-link {
|
|
4171
|
-
display: flex;
|
|
4172
|
-
align-items: center;
|
|
4173
|
-
gap: 6px;
|
|
4174
|
-
color: var(--text-secondary);
|
|
4175
|
-
text-decoration: none;
|
|
4176
|
-
font-size: 12px;
|
|
4177
|
-
padding: 6px 10px;
|
|
4178
|
-
border-radius: 6px;
|
|
4179
|
-
transition: all 0.3s ease;
|
|
4180
|
-
opacity: 0.8;
|
|
4181
|
-
}
|
|
4182
|
-
|
|
4183
|
-
.help-link:hover {
|
|
4184
|
-
background: rgba(103, 233, 233, 0.08);
|
|
4185
|
-
color: var(--accent-color);
|
|
4186
|
-
opacity: 1;
|
|
4187
|
-
}
|
|
4188
|
-
|
|
4189
|
-
/* Loading state */
|
|
4190
|
-
.loading {
|
|
4191
|
-
display: none;
|
|
4192
|
-
flex-direction: column;
|
|
4193
|
-
align-items: center;
|
|
4194
|
-
justify-content: center;
|
|
4195
|
-
padding: 40px;
|
|
4196
|
-
gap: 20px;
|
|
4197
|
-
}
|
|
4198
|
-
|
|
4199
|
-
.spinner {
|
|
4200
|
-
width: 40px;
|
|
4201
|
-
height: 40px;
|
|
4202
|
-
border: 3px solid var(--border-color);
|
|
4203
|
-
border-top-color: var(--accent-color);
|
|
4204
|
-
border-radius: 50%;
|
|
4205
|
-
animation: spin 0.8s linear infinite;
|
|
4206
|
-
}
|
|
4207
|
-
|
|
4208
|
-
@keyframes spin {
|
|
4209
|
-
to { transform: rotate(360deg); }
|
|
4210
|
-
}
|
|
4211
|
-
|
|
4212
|
-
.loading span {
|
|
4213
|
-
color: var(--text-secondary);
|
|
4214
|
-
font-size: 14px;
|
|
4215
|
-
}
|
|
4216
|
-
|
|
4217
|
-
/* Success state */
|
|
4218
|
-
.success-state {
|
|
4219
|
-
text-align: center;
|
|
4220
|
-
padding: 40px 20px;
|
|
4221
|
-
animation: fadeInUp 0.8s ease-out both;
|
|
4222
|
-
}
|
|
4223
|
-
|
|
4224
|
-
.success-icon {
|
|
4225
|
-
margin-bottom: 20px;
|
|
4226
|
-
color: var(--accent-color);
|
|
4227
|
-
animation: successPulse 2s ease-in-out infinite;
|
|
4228
|
-
}
|
|
4229
|
-
|
|
4230
|
-
@keyframes successPulse {
|
|
4231
|
-
0%, 100% {
|
|
4232
|
-
transform: scale(1);
|
|
4233
|
-
filter: drop-shadow(0 0 8px rgba(103, 233, 233, 0.3));
|
|
4234
|
-
}
|
|
4235
|
-
50% {
|
|
4236
|
-
transform: scale(1.1);
|
|
4237
|
-
filter: drop-shadow(0 0 16px rgba(103, 233, 233, 0.6));
|
|
4238
|
-
}
|
|
4239
|
-
}
|
|
4240
|
-
|
|
4241
|
-
.success-title {
|
|
4242
|
-
font-size: 24px;
|
|
4243
|
-
font-weight: 700;
|
|
4244
|
-
color: var(--text-primary);
|
|
4245
|
-
margin-bottom: 12px;
|
|
4246
|
-
}
|
|
4247
|
-
|
|
4248
|
-
.success-message {
|
|
4249
|
-
color: var(--text-secondary);
|
|
4250
|
-
font-size: 16px;
|
|
4251
|
-
line-height: 1.5;
|
|
4252
|
-
}
|
|
4253
|
-
|
|
4254
|
-
.selected-env-info {
|
|
4255
|
-
margin-top: 20px;
|
|
4256
|
-
padding: 16px;
|
|
4257
|
-
background: rgba(103, 233, 233, 0.1);
|
|
4258
|
-
border: 1px solid var(--accent-color);
|
|
4259
|
-
border-radius: 12px;
|
|
4260
|
-
display: flex;
|
|
4261
|
-
align-items: center;
|
|
4262
|
-
gap: 12px;
|
|
4263
|
-
}
|
|
4264
|
-
|
|
4265
|
-
.env-label {
|
|
4266
|
-
color: var(--text-secondary);
|
|
4267
|
-
font-size: 14px;
|
|
4268
|
-
font-weight: 500;
|
|
4269
|
-
}
|
|
4270
|
-
|
|
4271
|
-
.env-value {
|
|
4272
|
-
color: var(--accent-color);
|
|
4273
|
-
font-size: 16px;
|
|
4274
|
-
font-weight: 600;
|
|
4275
|
-
font-family: var(--font-mono);
|
|
4276
|
-
}
|
|
4277
|
-
|
|
4278
|
-
/* Info banner (for non-critical notifications) */
|
|
4279
|
-
.info-banner {
|
|
4280
|
-
margin-bottom: 20px;
|
|
4281
|
-
padding: 16px;
|
|
4282
|
-
background: rgba(103, 233, 233, 0.08);
|
|
4283
|
-
border: 1px solid rgba(103, 233, 233, 0.2);
|
|
4284
|
-
border-radius: 12px;
|
|
4285
|
-
}
|
|
4286
|
-
|
|
4287
|
-
.info-item {
|
|
4288
|
-
margin-bottom: 16px;
|
|
4289
|
-
}
|
|
4290
|
-
|
|
4291
|
-
.info-item:last-child {
|
|
4292
|
-
margin-bottom: 0;
|
|
4293
|
-
}
|
|
4294
|
-
|
|
4295
|
-
.info-header {
|
|
4296
|
-
display: flex;
|
|
4297
|
-
align-items: center;
|
|
4298
|
-
gap: 8px;
|
|
4299
|
-
margin-bottom: 8px;
|
|
4300
|
-
}
|
|
4301
|
-
|
|
4302
|
-
.info-header svg {
|
|
4303
|
-
color: var(--accent-color);
|
|
4304
|
-
}
|
|
4305
|
-
|
|
4306
|
-
.info-title {
|
|
4307
|
-
color: var(--text-primary);
|
|
4308
|
-
font-weight: 600;
|
|
4309
|
-
font-size: 14px;
|
|
4310
|
-
}
|
|
4311
|
-
|
|
4312
|
-
.info-message {
|
|
4313
|
-
color: var(--text-secondary);
|
|
4314
|
-
font-size: 13px;
|
|
4315
|
-
line-height: 1.5;
|
|
4316
|
-
margin-bottom: 12px;
|
|
4317
|
-
}
|
|
4318
|
-
|
|
4319
|
-
.info-details {
|
|
4320
|
-
display: flex;
|
|
4321
|
-
gap: 16px;
|
|
4322
|
-
flex-wrap: wrap;
|
|
4323
|
-
margin-bottom: 12px;
|
|
4324
|
-
font-size: 12px;
|
|
4325
|
-
color: var(--text-secondary);
|
|
4326
|
-
opacity: 0.7;
|
|
4327
|
-
}
|
|
4328
|
-
|
|
4329
|
-
.detail-label {
|
|
4330
|
-
font-weight: 500;
|
|
4331
|
-
}
|
|
4332
|
-
|
|
4333
|
-
.detail-value {
|
|
4334
|
-
font-family: var(--font-mono);
|
|
4335
|
-
opacity: 0.8;
|
|
4336
|
-
}
|
|
4337
|
-
|
|
4338
|
-
.error-action {
|
|
4339
|
-
display: flex;
|
|
4340
|
-
gap: 8px;
|
|
4341
|
-
flex-wrap: wrap;
|
|
4342
|
-
}
|
|
4343
|
-
|
|
4344
|
-
.error-link {
|
|
4345
|
-
display: inline-flex;
|
|
4346
|
-
align-items: center;
|
|
4347
|
-
gap: 6px;
|
|
4348
|
-
padding: 6px 12px;
|
|
4349
|
-
background: rgba(103, 233, 233, 0.15);
|
|
4350
|
-
border: 1px solid rgba(103, 233, 233, 0.3);
|
|
4351
|
-
border-radius: 6px;
|
|
4352
|
-
color: var(--accent-color);
|
|
4353
|
-
text-decoration: none;
|
|
4354
|
-
font-size: 13px;
|
|
4355
|
-
cursor: pointer;
|
|
4356
|
-
transition: all 0.3s ease;
|
|
4357
|
-
}
|
|
4358
|
-
|
|
4359
|
-
.error-link:hover {
|
|
4360
|
-
background: rgba(103, 233, 233, 0.25);
|
|
4361
|
-
border-color: var(--accent-color);
|
|
4362
|
-
}
|
|
4363
|
-
|
|
4364
|
-
.error-retry-btn {
|
|
4365
|
-
background: rgba(103, 233, 233, 0.2);
|
|
4366
|
-
border-color: var(--accent-color);
|
|
4367
|
-
color: var(--accent-color);
|
|
4368
|
-
}
|
|
4369
|
-
|
|
4370
|
-
.error-retry-btn:hover {
|
|
4371
|
-
background: rgba(103, 233, 233, 0.3);
|
|
4372
|
-
}
|
|
4373
|
-
</style>
|
|
4374
|
-
`;
|
|
4375
|
-
|
|
4376
|
-
|
|
4377
|
-
/***/ },
|
|
4378
|
-
|
|
4379
|
-
/***/ 2674
|
|
4380
|
-
(module) {
|
|
4381
|
-
|
|
4382
|
-
module.exports = __WEBPACK_EXTERNAL_MODULE_express__;
|
|
4383
|
-
|
|
4384
|
-
/***/ },
|
|
4385
|
-
|
|
4386
|
-
/***/ 1622
|
|
4387
|
-
(module) {
|
|
4388
|
-
|
|
4389
|
-
module.exports = __WEBPACK_EXTERNAL_MODULE_open__;
|
|
4390
|
-
|
|
4391
|
-
/***/ },
|
|
4392
|
-
|
|
4393
|
-
/***/ 6220
|
|
4394
|
-
(module, __unused_webpack_exports, __webpack_require__) {
|
|
4395
|
-
|
|
4396
|
-
var x = (y) => {
|
|
4397
|
-
var x = {}; __webpack_require__.d(x, y); return x
|
|
4398
|
-
}
|
|
4399
|
-
var y = (x) => (() => (x))
|
|
4400
|
-
module.exports = x({ ["WebSocket"]: () => (__WEBPACK_EXTERNAL_MODULE_ws__.WebSocket), ["WebSocketServer"]: () => (__WEBPACK_EXTERNAL_MODULE_ws__.WebSocketServer) });
|
|
4401
|
-
|
|
4402
|
-
/***/ }
|
|
4403
|
-
|
|
4404
|
-
};
|