oss-mcp-plus 1.0.13 → 1.0.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -39,6 +39,10 @@ function getServerConfig(isStdioMode = false) {
39
39
  type: "number",
40
40
  description: "\u670D\u52A1\u5668\u8FD0\u884C\u7AEF\u53E3",
41
41
  default: 3e3
42
+ },
43
+ "figma-token": {
44
+ type: "string",
45
+ description: "Figma Personal Access Token\uFF0C\u7528\u4E8E\u5BFC\u51FA\u591A\u500D\u56FE"
42
46
  }
43
47
  }).help().version("1.0.0").parseSync();
44
48
  const config3 = {
@@ -46,7 +50,8 @@ function getServerConfig(isStdioMode = false) {
46
50
  ossConfig: {},
47
51
  configSources: {
48
52
  port: "default",
49
- ossConfig: "default"
53
+ ossConfig: "default",
54
+ figmaToken: "none"
50
55
  }
51
56
  };
52
57
  if (argv.port) {
@@ -82,6 +87,13 @@ function getServerConfig(isStdioMode = false) {
82
87
  }
83
88
  }
84
89
  });
90
+ if (argv["figma-token"]) {
91
+ config3.figmaToken = argv["figma-token"];
92
+ config3.configSources.figmaToken = "cli";
93
+ } else if (process.env.FIGMA_TOKEN) {
94
+ config3.figmaToken = process.env.FIGMA_TOKEN;
95
+ config3.configSources.figmaToken = "env";
96
+ }
85
97
  if (Object.keys(config3.ossConfig).length === 0) {
86
98
  console.warn("\u672A\u627E\u5230\u6709\u6548\u7684OSS\u914D\u7F6E\u3002\u670D\u52A1\u5668\u5C06\u542F\u52A8\uFF0C\u4F46\u4E0A\u4F20\u529F\u80FD\u5C06\u4E0D\u53EF\u7528\u3002");
87
99
  }
@@ -101,10 +113,22 @@ function getServerConfig(isStdioMode = false) {
101
113
  } else {
102
114
  console.log("- OSS\u914D\u7F6E: \u672A\u627E\u5230");
103
115
  }
116
+ if (config3.figmaToken) {
117
+ console.log(`- Figma Token: ${maskSecret(config3.figmaToken)} (\u6765\u6E90: ${config3.configSources.figmaToken})`);
118
+ } else {
119
+ console.log("- Figma Token: \u672A\u914D\u7F6E\uFF08Figma \u5BFC\u51FA\u529F\u80FD\u4E0D\u53EF\u7528\uFF09");
120
+ }
104
121
  console.log();
105
122
  }
106
123
  return config3;
107
124
  }
125
+ var cachedFigmaToken;
126
+ function getFigmaToken() {
127
+ if (cachedFigmaToken !== void 0) return cachedFigmaToken || void 0;
128
+ const { figmaToken } = getServerConfig(true);
129
+ cachedFigmaToken = figmaToken || "";
130
+ return figmaToken;
131
+ }
108
132
  function getAllOssConfigs() {
109
133
  const { ossConfig } = getServerConfig(true);
110
134
  return ossConfig;
@@ -372,12 +396,150 @@ var OssService = class {
372
396
  };
373
397
  var ossService = new OssService();
374
398
 
399
+ // src/services/figma.service.ts
400
+ import https from "https";
401
+ import fs2 from "fs";
402
+ import path2 from "path";
403
+ var FIGMA_API_BASE = "https://api.figma.com/v1";
404
+ function httpsGet(url, headers = {}) {
405
+ return new Promise((resolve2, reject) => {
406
+ const urlObj = new URL(url);
407
+ const options = {
408
+ hostname: urlObj.hostname,
409
+ path: urlObj.pathname + urlObj.search,
410
+ method: "GET",
411
+ headers
412
+ };
413
+ const req = https.request(options, (res) => {
414
+ if (res.statusCode === 301 || res.statusCode === 302) {
415
+ const redirectUrl = res.headers.location;
416
+ if (redirectUrl) {
417
+ httpsGet(redirectUrl, headers).then(resolve2).catch(reject);
418
+ return;
419
+ }
420
+ }
421
+ if (res.statusCode !== 200) {
422
+ let body2 = "";
423
+ res.on("data", (chunk) => body2 += chunk);
424
+ res.on("end", () => reject(new Error(`Figma API \u8BF7\u6C42\u5931\u8D25 (${res.statusCode}): ${body2}`)));
425
+ return;
426
+ }
427
+ let body = "";
428
+ res.on("data", (chunk) => body += chunk);
429
+ res.on("end", () => resolve2(body));
430
+ });
431
+ req.on("error", reject);
432
+ req.setTimeout(3e4, () => {
433
+ req.destroy();
434
+ reject(new Error("Figma API \u8BF7\u6C42\u8D85\u65F6\uFF0830\u79D2\uFF09"));
435
+ });
436
+ req.end();
437
+ });
438
+ }
439
+ function httpsDownload(url, destPath) {
440
+ return new Promise((resolve2, reject) => {
441
+ const urlObj = new URL(url);
442
+ const protocol = https;
443
+ const request = protocol.get(url, (response) => {
444
+ if (response.statusCode === 301 || response.statusCode === 302) {
445
+ const redirectUrl = response.headers.location;
446
+ if (redirectUrl) {
447
+ httpsDownload(redirectUrl, destPath).then(resolve2).catch(reject);
448
+ return;
449
+ }
450
+ }
451
+ if (response.statusCode !== 200) {
452
+ reject(new Error(`\u4E0B\u8F7D\u5931\u8D25\uFF0CHTTP \u72B6\u6001\u7801: ${response.statusCode}`));
453
+ return;
454
+ }
455
+ const fileStream = fs2.createWriteStream(destPath);
456
+ response.pipe(fileStream);
457
+ fileStream.on("finish", () => {
458
+ fileStream.close();
459
+ resolve2();
460
+ });
461
+ fileStream.on("error", (err) => {
462
+ fs2.unlink(destPath, () => {
463
+ });
464
+ reject(err);
465
+ });
466
+ });
467
+ request.on("error", (err) => {
468
+ fs2.unlink(destPath, () => {
469
+ });
470
+ reject(err);
471
+ });
472
+ request.setTimeout(6e4, () => {
473
+ request.destroy();
474
+ fs2.unlink(destPath, () => {
475
+ });
476
+ reject(new Error("\u4E0B\u8F7D\u8D85\u65F6\uFF0860\u79D2\uFF09"));
477
+ });
478
+ });
479
+ }
480
+ var FigmaService = class {
481
+ /**
482
+ * 通过 Figma REST API 获取指定 scale 的图片导出 URL
483
+ */
484
+ async getImageUrls(fileKey, nodeId, scale, format, token) {
485
+ const encodedNodeId = encodeURIComponent(nodeId);
486
+ const url = `${FIGMA_API_BASE}/images/${fileKey}?ids=${encodedNodeId}&scale=${scale}&format=${format}`;
487
+ const responseBody = await httpsGet(url, {
488
+ "X-Figma-Token": token
489
+ });
490
+ const data = JSON.parse(responseBody);
491
+ if (data.err) {
492
+ throw new Error(`Figma API \u9519\u8BEF: ${data.err}`);
493
+ }
494
+ return data.images || {};
495
+ }
496
+ /**
497
+ * 导出 Figma 多倍图到本地
498
+ */
499
+ async exportToLocal(options, targetDir, fileNamePrefix) {
500
+ const token = getFigmaToken();
501
+ if (!token) {
502
+ throw new Error("\u672A\u914D\u7F6E Figma Token\u3002\u8BF7\u901A\u8FC7 --figma-token \u53C2\u6570\u6216 FIGMA_TOKEN \u73AF\u5883\u53D8\u91CF\u914D\u7F6E\u3002");
503
+ }
504
+ if (!fs2.existsSync(targetDir)) {
505
+ fs2.mkdirSync(targetDir, { recursive: true });
506
+ }
507
+ const { fileKey, nodeId, scales, format } = options;
508
+ const results = [];
509
+ for (const scale of scales) {
510
+ const images = await this.getImageUrls(fileKey, nodeId, scale, format, token);
511
+ const imageEntries = Object.entries(images);
512
+ if (imageEntries.length === 0) {
513
+ throw new Error(`Figma \u672A\u8FD4\u56DE scale=${scale} \u7684\u56FE\u7247 URL\uFF0C\u8BF7\u68C0\u67E5 fileKey \u548C nodeId \u662F\u5426\u6B63\u786E`);
514
+ }
515
+ for (const [_id, imageUrl] of imageEntries) {
516
+ if (!imageUrl) {
517
+ throw new Error(`Figma \u8FD4\u56DE\u4E86\u7A7A\u7684\u56FE\u7247 URL (scale=${scale})\uFF0C\u8282\u70B9\u53EF\u80FD\u4E0D\u652F\u6301\u5BFC\u51FA`);
518
+ }
519
+ const suffix = scale === 1 ? "" : `@${scale}x`;
520
+ const prefix = fileNamePrefix || nodeId.replace(/[:/]/g, "-");
521
+ const fileName = `${prefix}${suffix}.${format}`;
522
+ const localPath = path2.join(targetDir, fileName);
523
+ await httpsDownload(imageUrl, localPath);
524
+ results.push({
525
+ scale,
526
+ imageUrl,
527
+ localPath,
528
+ fileName
529
+ });
530
+ }
531
+ }
532
+ return results;
533
+ }
534
+ };
535
+ var figmaService = new FigmaService();
536
+
375
537
  // src/server.ts
376
538
  import express from "express";
377
539
  import { SSEServerTransport } from "@modelcontextprotocol/sdk/server/sse.js";
378
- import fs2 from "fs";
379
- import path2 from "path";
380
- import https from "https";
540
+ import fs3 from "fs";
541
+ import path3 from "path";
542
+ import https2 from "https";
381
543
  import http from "http";
382
544
  var Logger = {
383
545
  log: (...args) => {
@@ -426,7 +588,7 @@ var OssMcpServer = class {
426
588
  if (!filePath) {
427
589
  throw new Error("\u6587\u4EF6\u8DEF\u5F84\u662F\u5FC5\u9700\u7684");
428
590
  }
429
- if (!fs2.existsSync(filePath)) {
591
+ if (!fs3.existsSync(filePath)) {
430
592
  throw new Error(`\u6587\u4EF6\u4E0D\u5B58\u5728: ${filePath}`);
431
593
  }
432
594
  const result = await ossService.uploadFile({
@@ -441,7 +603,7 @@ var OssMcpServer = class {
441
603
  content: [{
442
604
  type: "text",
443
605
  text: `\u6587\u4EF6\u4E0A\u4F20\u6210\u529F!
444
- \u6587\u4EF6\u540D: ${path2.basename(filePath)}
606
+ \u6587\u4EF6\u540D: ${path3.basename(filePath)}
445
607
  \u76EE\u6807\u4F4D\u7F6E: ${targetDir || "\u6839\u76EE\u5F55"}
446
608
  URL: ${result.url}
447
609
  \u914D\u7F6E\u540D\u79F0: ${result.ossConfigName}`
@@ -583,14 +745,14 @@ ${configNames2.map((name) => `- ${name}`).join("\n")}`
583
745
  async ({ directory, pattern }) => {
584
746
  try {
585
747
  Logger.log(`\u5217\u51FA\u76EE\u5F55\u6587\u4EF6: ${directory}, \u8FC7\u6EE4: ${pattern || "\u65E0"}`);
586
- if (!fs2.existsSync(directory)) {
748
+ if (!fs3.existsSync(directory)) {
587
749
  throw new Error(`\u76EE\u5F55\u4E0D\u5B58\u5728: ${directory}`);
588
750
  }
589
- const stat = fs2.statSync(directory);
751
+ const stat = fs3.statSync(directory);
590
752
  if (!stat.isDirectory()) {
591
753
  throw new Error(`\u8DEF\u5F84\u4E0D\u662F\u76EE\u5F55: ${directory}`);
592
754
  }
593
- let files = fs2.readdirSync(directory);
755
+ let files = fs3.readdirSync(directory);
594
756
  files = files.filter((f) => !f.startsWith("."));
595
757
  if (pattern) {
596
758
  const regex = new RegExp(
@@ -600,8 +762,8 @@ ${configNames2.map((name) => `- ${name}`).join("\n")}`
600
762
  files = files.filter((f) => regex.test(f));
601
763
  }
602
764
  const fileInfos = files.map((f) => {
603
- const filePath = path2.join(directory, f);
604
- const fileStat = fs2.statSync(filePath);
765
+ const filePath = path3.join(directory, f);
766
+ const fileStat = fs3.statSync(filePath);
605
767
  return {
606
768
  name: f,
607
769
  isDirectory: fileStat.isDirectory(),
@@ -887,48 +1049,48 @@ ${configNames2.map((name) => `- ${name}`).join("\n")}`
887
1049
  async ({ url, targetDir, fileName }) => {
888
1050
  try {
889
1051
  Logger.log(`\u4E0B\u8F7D\u6587\u4EF6: ${url} \u5230 ${targetDir}`);
890
- if (!fs2.existsSync(targetDir)) {
891
- fs2.mkdirSync(targetDir, { recursive: true });
1052
+ if (!fs3.existsSync(targetDir)) {
1053
+ fs3.mkdirSync(targetDir, { recursive: true });
892
1054
  Logger.log(`\u521B\u5EFA\u76EE\u5F55: ${targetDir}`);
893
1055
  }
894
- const stat = fs2.statSync(targetDir);
1056
+ const stat = fs3.statSync(targetDir);
895
1057
  if (!stat.isDirectory()) {
896
1058
  throw new Error(`\u8DEF\u5F84\u4E0D\u662F\u76EE\u5F55: ${targetDir}`);
897
1059
  }
898
1060
  let finalFileName = fileName;
899
1061
  if (!finalFileName) {
900
1062
  const urlObj = new URL(url);
901
- finalFileName = path2.basename(urlObj.pathname);
1063
+ finalFileName = path3.basename(urlObj.pathname);
902
1064
  if (!finalFileName || finalFileName === "/") {
903
1065
  finalFileName = `download_${Date.now()}`;
904
1066
  }
905
1067
  }
906
- const filePath = path2.join(targetDir, finalFileName);
907
- if (fs2.existsSync(filePath)) {
1068
+ const filePath = path3.join(targetDir, finalFileName);
1069
+ if (fs3.existsSync(filePath)) {
908
1070
  throw new Error(`\u6587\u4EF6\u5DF2\u5B58\u5728: ${filePath}`);
909
1071
  }
910
1072
  await new Promise((resolve2, reject) => {
911
1073
  const urlObj = new URL(url);
912
- const protocol = urlObj.protocol === "https:" ? https : http;
1074
+ const protocol = urlObj.protocol === "https:" ? https2 : http;
913
1075
  const request = protocol.get(url, (response) => {
914
1076
  if (response.statusCode === 301 || response.statusCode === 302) {
915
1077
  const redirectUrl = response.headers.location;
916
1078
  if (redirectUrl) {
917
1079
  Logger.log(`\u91CD\u5B9A\u5411\u5230: ${redirectUrl}`);
918
- const redirectProtocol = redirectUrl.startsWith("https:") ? https : http;
1080
+ const redirectProtocol = redirectUrl.startsWith("https:") ? https2 : http;
919
1081
  redirectProtocol.get(redirectUrl, (redirectResponse) => {
920
1082
  if (redirectResponse.statusCode !== 200) {
921
1083
  reject(new Error(`\u4E0B\u8F7D\u5931\u8D25\uFF0CHTTP \u72B6\u6001\u7801: ${redirectResponse.statusCode}`));
922
1084
  return;
923
1085
  }
924
- const fileStream2 = fs2.createWriteStream(filePath);
1086
+ const fileStream2 = fs3.createWriteStream(filePath);
925
1087
  redirectResponse.pipe(fileStream2);
926
1088
  fileStream2.on("finish", () => {
927
1089
  fileStream2.close();
928
1090
  resolve2();
929
1091
  });
930
1092
  fileStream2.on("error", (err) => {
931
- fs2.unlink(filePath, () => {
1093
+ fs3.unlink(filePath, () => {
932
1094
  });
933
1095
  reject(err);
934
1096
  });
@@ -940,31 +1102,31 @@ ${configNames2.map((name) => `- ${name}`).join("\n")}`
940
1102
  reject(new Error(`\u4E0B\u8F7D\u5931\u8D25\uFF0CHTTP \u72B6\u6001\u7801: ${response.statusCode}`));
941
1103
  return;
942
1104
  }
943
- const fileStream = fs2.createWriteStream(filePath);
1105
+ const fileStream = fs3.createWriteStream(filePath);
944
1106
  response.pipe(fileStream);
945
1107
  fileStream.on("finish", () => {
946
1108
  fileStream.close();
947
1109
  resolve2();
948
1110
  });
949
1111
  fileStream.on("error", (err) => {
950
- fs2.unlink(filePath, () => {
1112
+ fs3.unlink(filePath, () => {
951
1113
  });
952
1114
  reject(err);
953
1115
  });
954
1116
  });
955
1117
  request.on("error", (err) => {
956
- fs2.unlink(filePath, () => {
1118
+ fs3.unlink(filePath, () => {
957
1119
  });
958
1120
  reject(err);
959
1121
  });
960
1122
  request.setTimeout(6e4, () => {
961
1123
  request.destroy();
962
- fs2.unlink(filePath, () => {
1124
+ fs3.unlink(filePath, () => {
963
1125
  });
964
1126
  reject(new Error("\u4E0B\u8F7D\u8D85\u65F6\uFF0860\u79D2\uFF09"));
965
1127
  });
966
1128
  });
967
- const downloadedStat = fs2.statSync(filePath);
1129
+ const downloadedStat = fs3.statSync(filePath);
968
1130
  const sizeStr = downloadedStat.size < 1024 ? `${downloadedStat.size}B` : downloadedStat.size < 1024 * 1024 ? `${(downloadedStat.size / 1024).toFixed(1)}KB` : `${(downloadedStat.size / 1024 / 1024).toFixed(1)}MB`;
969
1131
  return {
970
1132
  content: [{
@@ -1022,23 +1184,23 @@ ${configNames2.map((name) => `- ${name}`).join("\n")}`
1022
1184
  const validImages = [];
1023
1185
  const errors = [];
1024
1186
  for (const imgPath of images) {
1025
- if (!fs2.existsSync(imgPath)) {
1187
+ if (!fs3.existsSync(imgPath)) {
1026
1188
  errors.push(`\u6587\u4EF6\u4E0D\u5B58\u5728: ${imgPath}`);
1027
1189
  continue;
1028
1190
  }
1029
- const stat = fs2.statSync(imgPath);
1191
+ const stat = fs3.statSync(imgPath);
1030
1192
  if (stat.size > 5 * 1024 * 1024) {
1031
1193
  errors.push(`\u6587\u4EF6\u8D85\u8FC7 5MB \u9650\u5236: ${imgPath}`);
1032
1194
  continue;
1033
1195
  }
1034
- const ext = path2.extname(imgPath).toLowerCase().slice(1);
1196
+ const ext = path3.extname(imgPath).toLowerCase().slice(1);
1035
1197
  if (!["png", "jpg", "jpeg", "webp", "gif", "bmp", "tiff"].includes(ext)) {
1036
1198
  errors.push(`\u4E0D\u652F\u6301\u7684\u683C\u5F0F: ${imgPath}`);
1037
1199
  continue;
1038
1200
  }
1039
1201
  validImages.push({
1040
1202
  path: imgPath,
1041
- name: path2.basename(imgPath, path2.extname(imgPath)),
1203
+ name: path3.basename(imgPath, path3.extname(imgPath)),
1042
1204
  ext: ext === "jpg" ? "jpeg" : ext,
1043
1205
  size: stat.size
1044
1206
  });
@@ -1092,7 +1254,7 @@ ${errors.join("\n")}`
1092
1254
  resultText += `### \u2705 \u6709\u6548\u56FE\u7247 (${validImages.length} \u4E2A)
1093
1255
  `;
1094
1256
  for (const img of validImages) {
1095
- resultText += `- ${path2.basename(img.path)} (${sizeStr(img.size)})
1257
+ resultText += `- ${path3.basename(img.path)} (${sizeStr(img.size)})
1096
1258
  `;
1097
1259
  }
1098
1260
  if (errors.length > 0) {
@@ -1191,23 +1353,23 @@ ${JSON.stringify(questions, null, 2)}
1191
1353
  const validImages = [];
1192
1354
  const errors = [];
1193
1355
  for (const imgPath of images) {
1194
- if (!fs2.existsSync(imgPath)) {
1356
+ if (!fs3.existsSync(imgPath)) {
1195
1357
  errors.push(`\u6587\u4EF6\u4E0D\u5B58\u5728: ${imgPath}`);
1196
1358
  continue;
1197
1359
  }
1198
- const stat = fs2.statSync(imgPath);
1360
+ const stat = fs3.statSync(imgPath);
1199
1361
  if (stat.size > 5 * 1024 * 1024) {
1200
1362
  errors.push(`\u6587\u4EF6\u8D85\u8FC7 5MB \u9650\u5236: ${imgPath}`);
1201
1363
  continue;
1202
1364
  }
1203
- const ext = path2.extname(imgPath).toLowerCase().slice(1);
1365
+ const ext = path3.extname(imgPath).toLowerCase().slice(1);
1204
1366
  if (!["png", "jpg", "jpeg", "webp", "gif", "bmp", "tiff"].includes(ext)) {
1205
1367
  errors.push(`\u4E0D\u652F\u6301\u7684\u683C\u5F0F: ${imgPath}`);
1206
1368
  continue;
1207
1369
  }
1208
1370
  validImages.push({
1209
1371
  path: imgPath,
1210
- name: path2.basename(imgPath, path2.extname(imgPath)),
1372
+ name: path3.basename(imgPath, path3.extname(imgPath)),
1211
1373
  ext: ext === "jpg" ? "jpeg" : ext,
1212
1374
  size: stat.size
1213
1375
  });
@@ -1272,8 +1434,8 @@ ${errors.join("\n")}`
1272
1434
  const newExt = actualOutputFormat || img.ext;
1273
1435
  const isFormatChange = newExt !== img.ext;
1274
1436
  const newFileName = `${img.name}.${newExt}`;
1275
- const downloadPath = path2.join(path2.dirname(img.path), `${img.name}-compressed.${newExt}`);
1276
- instructions += `**${path2.basename(img.path)}**:
1437
+ const downloadPath = path3.join(path3.dirname(img.path), `${img.name}-compressed.${newExt}`);
1438
+ instructions += `**${path3.basename(img.path)}**:
1277
1439
  `;
1278
1440
  instructions += `1. \u4E0B\u8F7D\u538B\u7F29\u7ED3\u679C\u5230: \`${downloadPath}\`
1279
1441
  `;
@@ -1282,11 +1444,11 @@ ${errors.join("\n")}`
1282
1444
  instructions += `2. \u4E0A\u4F20\u5230 OSS: \`upload_to_oss("${downloadPath}", "${ossDirectory}", "${newFileName}", "${configName}")\`
1283
1445
  `;
1284
1446
  if (deleteOriginal) {
1285
- instructions += `3. \u5220\u9664\u539F\u6587\u4EF6: \u5728 OSS \u4E0A\u5220\u9664 \`${ossDirectory}/${path2.basename(img.path)}\`
1447
+ instructions += `3. \u5220\u9664\u539F\u6587\u4EF6: \u5728 OSS \u4E0A\u5220\u9664 \`${ossDirectory}/${path3.basename(img.path)}\`
1286
1448
  `;
1287
1449
  }
1288
1450
  } else {
1289
- instructions += `2. \u8986\u76D6\u4E0A\u4F20\u5230 OSS: \`upload_to_oss("${downloadPath}", "${ossDirectory}", "${path2.basename(img.path)}", "${configName}")\`
1451
+ instructions += `2. \u8986\u76D6\u4E0A\u4F20\u5230 OSS: \`upload_to_oss("${downloadPath}", "${ossDirectory}", "${path3.basename(img.path)}", "${configName}")\`
1290
1452
  `;
1291
1453
  }
1292
1454
  }
@@ -1304,7 +1466,7 @@ ${errors.join("\n")}`
1304
1466
  batchSize,
1305
1467
  images: validImages.map((img) => ({
1306
1468
  originalPath: img.path,
1307
- originalName: path2.basename(img.path),
1469
+ originalName: path3.basename(img.path),
1308
1470
  originalExt: img.ext,
1309
1471
  originalSize: img.size,
1310
1472
  newExt: actualOutputFormat || img.ext,
@@ -1340,6 +1502,145 @@ ${JSON.stringify(resultInfo, null, 2)}
1340
1502
  }
1341
1503
  }
1342
1504
  );
1505
+ const figmaTokenConfigured = !!getFigmaToken();
1506
+ this.server.tool(
1507
+ "export_figma_images",
1508
+ `\u4ECE Figma \u5BFC\u51FA\u591A\u500D\u56FE\uFF08\u652F\u6301 1x/2x/3x/4x\uFF09\u3002\u4F7F\u7528 Figma REST API \u76F4\u63A5\u5BFC\u51FA\u6307\u5B9A\u500D\u7387\u7684\u56FE\u7247\uFF0C\u5F25\u8865 Figma MCP \u4E0D\u652F\u6301\u591A\u500D\u56FE\u5BFC\u51FA\u7684\u4E0D\u8DB3\u3002
1509
+
1510
+ \u652F\u6301\u4E24\u79CD\u6A21\u5F0F\uFF1A
1511
+ - \u5BFC\u51FA\u5230\u672C\u5730\u76EE\u5F55
1512
+ - \u5BFC\u51FA\u540E\u76F4\u63A5\u4E0A\u4F20\u5230\u963F\u91CC\u4E91 OSS
1513
+
1514
+ \u5F53\u524D\u72B6\u6001: ${figmaTokenConfigured ? "\u2705 Figma Token \u5DF2\u914D\u7F6E" : "\u274C \u672A\u914D\u7F6E Figma Token\uFF08\u9700\u8981 --figma-token \u53C2\u6570\u6216 FIGMA_TOKEN \u73AF\u5883\u53D8\u91CF\uFF09"}
1515
+
1516
+ \u3010\u4F7F\u7528\u8BF4\u660E\u3011
1517
+ - fileKey \u548C nodeId \u53EF\u4ECE Figma URL \u4E2D\u63D0\u53D6\uFF1A
1518
+ figma.com/design/:fileKey/:fileName?node-id=:nodeId
1519
+ \u6CE8\u610F\uFF1AURL \u4E2D\u7684 node-id \u7528 "-" \u5206\u9694\uFF0C\u9700\u8981\u66FF\u6362\u4E3A ":"\uFF08\u5982 "14-123" \u2192 "14:123"\uFF09
1520
+ - scales \u652F\u6301 1/2/3/4\uFF0C\u53EF\u540C\u65F6\u5BFC\u51FA\u591A\u4E2A\u500D\u7387
1521
+ - \u5BFC\u51FA\u5230\u672C\u5730\u65F6\u6587\u4EF6\u547D\u540D\u683C\u5F0F\uFF1A{prefix}.png, {prefix}@2x.png, {prefix}@3x.png
1522
+ - \u5982\u679C\u540C\u65F6\u6307\u5B9A\u4E86 ossTargetDir\uFF0C\u5BFC\u51FA\u540E\u4F1A\u81EA\u52A8\u4E0A\u4F20\u5230 OSS`,
1523
+ {
1524
+ fileKey: z3.string().describe("Figma \u6587\u4EF6 Key\uFF08\u4ECE URL \u4E2D\u63D0\u53D6\uFF09"),
1525
+ nodeId: z3.string().describe("Figma \u8282\u70B9 ID\uFF08\u683C\u5F0F\u5982 '14:123'\uFF0CURL \u4E2D\u7684 '-' \u9700\u66FF\u6362\u4E3A ':'\uFF09"),
1526
+ scales: z3.array(z3.number().min(0.01).max(4)).default([1, 2]).describe("\u5BFC\u51FA\u500D\u7387\u6570\u7EC4\uFF0C\u9ED8\u8BA4 [1, 2]\u3002\u5E38\u7528\u503C: [1, 2], [1, 2, 3]"),
1527
+ format: z3.enum(["png", "jpg", "svg", "pdf"]).default("png").describe("\u5BFC\u51FA\u683C\u5F0F\uFF0C\u9ED8\u8BA4 png"),
1528
+ localTargetDir: z3.string().describe("\u672C\u5730\u4FDD\u5B58\u76EE\u5F55\u8DEF\u5F84"),
1529
+ fileNamePrefix: z3.string().optional().describe("\u6587\u4EF6\u540D\u524D\u7F00\uFF08\u53EF\u9009\uFF0C\u9ED8\u8BA4\u4F7F\u7528 nodeId \u751F\u6210\uFF09"),
1530
+ ossTargetDir: z3.string().optional().describe("OSS \u76EE\u6807\u76EE\u5F55\uFF08\u53EF\u9009\uFF0C\u586B\u5199\u5219\u5BFC\u51FA\u540E\u81EA\u52A8\u4E0A\u4F20\u5230 OSS\uFF09"),
1531
+ configName: z3.string().optional().describe(`OSS\u914D\u7F6E\u540D\u79F0\uFF08\u9ED8\u8BA4\u4E3A'default'\uFF0C\u4EC5\u4E0A\u4F20\u5230 OSS \u65F6\u9700\u8981\uFF09\u3002\u53EF\u7528\u914D\u7F6E: ${configNames.join(", ") || "\u65E0"}`)
1532
+ },
1533
+ async ({ fileKey, nodeId, scales, format, localTargetDir, fileNamePrefix, ossTargetDir, configName = "default" }) => {
1534
+ try {
1535
+ if (!figmaTokenConfigured) {
1536
+ return {
1537
+ isError: true,
1538
+ content: [{
1539
+ type: "text",
1540
+ text: `\u274C \u672A\u914D\u7F6E Figma Token\uFF01
1541
+
1542
+ \u8BF7\u5728 MCP \u914D\u7F6E\u4E2D\u6DFB\u52A0 Figma Token\uFF1A
1543
+
1544
+ \u65B9\u5F0F\u4E00\uFF1ACLI \u53C2\u6570
1545
+ {
1546
+ "command": "npx",
1547
+ "args": ["oss-mcp-plus", "--figma-token=figd_xxxxx", "--oss-config='{...}'", "--stdio"]
1548
+ }
1549
+
1550
+ \u65B9\u5F0F\u4E8C\uFF1A\u73AF\u5883\u53D8\u91CF
1551
+ {
1552
+ "command": "npx",
1553
+ "args": ["oss-mcp-plus", "--oss-config='{...}'", "--stdio"],
1554
+ "env": {
1555
+ "FIGMA_TOKEN": "figd_xxxxx"
1556
+ }
1557
+ }
1558
+
1559
+ \u83B7\u53D6 Token: Figma \u2192 Settings \u2192 Personal Access Tokens`
1560
+ }]
1561
+ };
1562
+ }
1563
+ Logger.log(`\u5BFC\u51FA Figma \u591A\u500D\u56FE: fileKey=${fileKey}, nodeId=${nodeId}, scales=${scales.join(",")}, format=${format}`);
1564
+ const results = await figmaService.exportToLocal(
1565
+ { fileKey, nodeId, scales, format },
1566
+ localTargetDir,
1567
+ fileNamePrefix
1568
+ );
1569
+ if (ossTargetDir) {
1570
+ for (const result of results) {
1571
+ if (!result.localPath) continue;
1572
+ const uploadResult = await ossService.uploadFile({
1573
+ filePath: result.localPath,
1574
+ targetDir: ossTargetDir,
1575
+ fileName: result.fileName,
1576
+ configName
1577
+ });
1578
+ if (uploadResult.success) {
1579
+ result.ossUrl = uploadResult.url;
1580
+ } else {
1581
+ Logger.error(`\u4E0A\u4F20 ${result.fileName} \u5230 OSS \u5931\u8D25: ${uploadResult.error}`);
1582
+ }
1583
+ }
1584
+ }
1585
+ const sizeStr = (size) => size < 1024 ? `${size}B` : size < 1024 * 1024 ? `${(size / 1024).toFixed(1)}KB` : `${(size / 1024 / 1024).toFixed(1)}MB`;
1586
+ let resultText = `## Figma \u591A\u500D\u56FE\u5BFC\u51FA\u5B8C\u6210
1587
+
1588
+ `;
1589
+ resultText += `**\u6587\u4EF6 Key**: ${fileKey}
1590
+ `;
1591
+ resultText += `**\u8282\u70B9 ID**: ${nodeId}
1592
+ `;
1593
+ resultText += `**\u683C\u5F0F**: ${format}
1594
+ `;
1595
+ resultText += `**\u500D\u7387**: ${scales.join("x, ")}x
1596
+
1597
+ `;
1598
+ resultText += `### \u5BFC\u51FA\u7ED3\u679C
1599
+
1600
+ `;
1601
+ for (const r of results) {
1602
+ const localSize = r.localPath && fs3.existsSync(r.localPath) ? sizeStr(fs3.statSync(r.localPath).size) : "\u672A\u77E5";
1603
+ resultText += `**${r.fileName}** (${r.scale}x, ${localSize})
1604
+ `;
1605
+ resultText += `- \u672C\u5730\u8DEF\u5F84: \`${r.localPath}\`
1606
+ `;
1607
+ if (r.ossUrl) {
1608
+ resultText += `- OSS URL: ${r.ossUrl}
1609
+ `;
1610
+ }
1611
+ resultText += `
1612
+ `;
1613
+ }
1614
+ if (ossTargetDir) {
1615
+ const uploadedCount = results.filter((r) => r.ossUrl).length;
1616
+ resultText += `
1617
+ ### OSS \u4E0A\u4F20
1618
+ `;
1619
+ resultText += `- \u76EE\u5F55: ${ossTargetDir}
1620
+ `;
1621
+ resultText += `- \u914D\u7F6E: ${configName}
1622
+ `;
1623
+ resultText += `- \u6210\u529F: ${uploadedCount}/${results.length}
1624
+ `;
1625
+ }
1626
+ return {
1627
+ content: [{
1628
+ type: "text",
1629
+ text: resultText
1630
+ }]
1631
+ };
1632
+ } catch (error) {
1633
+ Logger.error(`\u5BFC\u51FA Figma \u591A\u500D\u56FE\u51FA\u9519:`, error);
1634
+ return {
1635
+ isError: true,
1636
+ content: [{
1637
+ type: "text",
1638
+ text: `\u5BFC\u51FA Figma \u591A\u500D\u56FE\u5931\u8D25: ${error instanceof Error ? error.message : error}`
1639
+ }]
1640
+ };
1641
+ }
1642
+ }
1643
+ );
1343
1644
  }
1344
1645
  // 生成 TinyPNG 自动化指令
1345
1646
  generateTinyPngInstructions(batches, outputFormat) {
@@ -1362,7 +1663,7 @@ ${JSON.stringify(resultInfo, null, 2)}
1362
1663
  instructions += `#### \u6279\u6B21 ${i + 1}/${batches.length}
1363
1664
 
1364
1665
  `;
1365
- instructions += `**\u6587\u4EF6**: ${batch.map((img) => path2.basename(img.path)).join(", ")}
1666
+ instructions += `**\u6587\u4EF6**: ${batch.map((img) => path3.basename(img.path)).join(", ")}
1366
1667
 
1367
1668
  `;
1368
1669
  let stepNum = 1;
@@ -1429,7 +1730,7 @@ ${JSON.stringify(resultInfo, null, 2)}
1429
1730
  instructions += `#### \u6279\u6B21 ${i + 1}/${batches.length}
1430
1731
 
1431
1732
  `;
1432
- instructions += `**\u6587\u4EF6**: ${batch.map((img) => path2.basename(img.path)).join(", ")}
1733
+ instructions += `**\u6587\u4EF6**: ${batch.map((img) => path3.basename(img.path)).join(", ")}
1433
1734
 
1434
1735
  `;
1435
1736
  let stepNum = 1;
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/server.ts","../src/services/oss.service.ts","../src/config/oss.config.ts"],"sourcesContent":["#!/usr/bin/env node\n/**\n * 实现阿里云OSS文件上传功能。\n * - 上传文件到阿里云OSS\n * - 获取可用的OSS配置\n */\n\nimport { StdioServerTransport } from \"@modelcontextprotocol/sdk/server/stdio.js\";\nimport { OssMcpServer } from \"./server.js\";\nimport { getServerConfig } from \"./config/oss.config.js\";\nimport { resolve } from \"path\";\nimport { config } from \"dotenv\";\n\n// 加载当前工作目录中的.env文件\nconfig({ path: resolve(process.cwd(), \".env\") });\n\nexport async function startServer(): Promise<void> {\n // 检查是否在stdio模式下运行\n const isStdioMode = process.env.NODE_ENV === \"cli\" || process.argv.includes(\"--stdio\");\n\n // 获取服务器配置\n const serverConfig = getServerConfig(isStdioMode);\n\n // 创建OSS MCP服务器\n const server = new OssMcpServer();\n\n if (isStdioMode) {\n // 在stdio模式下运行\n const transport = new StdioServerTransport();\n await server.connect(transport);\n } else {\n // 在HTTP模式下运行\n console.log(`初始化OSS MCP服务器,HTTP模式,端口: ${serverConfig.port}...`);\n await server.startHttpServer(serverConfig.port);\n }\n}\n\n// 启动服务器\nstartServer().catch((error) => {\n console.error(\"启动服务器失败:\", error);\n process.exit(1);\n});\n","import { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { z } from \"zod\";\nimport { ossService } from \"./services/oss.service.js\";\nimport express, { Request, Response } from \"express\";\nimport { SSEServerTransport } from \"@modelcontextprotocol/sdk/server/sse.js\";\nimport { IncomingMessage, ServerResponse } from \"http\";\nimport { Transport } from \"@modelcontextprotocol/sdk/shared/transport.js\";\nimport fs from 'fs';\nimport path from 'path';\nimport https from 'https';\nimport http from 'http';\n\nexport const Logger = {\n log: (...args: any[]) => {\n console.log(...args);\n },\n error: (...args: any[]) => {\n console.error(...args);\n }\n};\n\nexport class OssMcpServer {\n private readonly server: McpServer;\n private sseTransport: SSEServerTransport | null = null;\n\n constructor() {\n this.server = new McpServer(\n {\n name: \"@yhy2001/oss-mcp\",\n version: \"1.0.0\",\n },\n // 使用正确格式的capabilities配置\n {\n capabilities: {\n tools: { listChanged: true },\n resources: { listChanged: true },\n prompts: { listChanged: true },\n logging: {}\n }\n }\n );\n\n this.registerTools();\n }\n\n private registerTools(): void {\n // 获取可用的OSS配置\n const configs = ossService.getConfigs();\n const configNames = configs.map(config => config.id);\n\n // 工具:上传文件到OSS\n this.server.tool(\n \"upload_to_oss\",\n \"将文件上传到阿里云OSS\",\n {\n filePath: z.string().describe(\"要上传的本地文件路径\"),\n targetDir: z.string().optional().describe(\"OSS中的目标目录路径(可选)\"),\n fileName: z.string().optional().describe(\"上传后的文件名(可选,默认使用原文件名)\"),\n configName: z.string().optional().describe(`OSS配置名称(可选,默认为'default')。可用配置: ${configNames.join(', ') || '无'}`)\n },\n async ({ filePath, targetDir, fileName, configName }) => {\n try {\n Logger.log(`准备上传: ${filePath} 到 ${targetDir || '根目录'}`);\n\n if (!filePath) {\n throw new Error(\"文件路径是必需的\");\n }\n\n // 检查文件是否存在\n if (!fs.existsSync(filePath)) {\n throw new Error(`文件不存在: ${filePath}`);\n }\n\n // 执行上传\n const result = await ossService.uploadFile({\n filePath,\n targetDir,\n fileName,\n configName\n });\n\n if (result.success) {\n Logger.log(`上传成功: ${result.url}`);\n return {\n content: [{\n type: \"text\",\n text: `文件上传成功!\\n文件名: ${path.basename(filePath)}\\n目标位置: ${targetDir || '根目录'}\\nURL: ${result.url}\\n配置名称: ${result.ossConfigName}`\n }]\n };\n } else {\n Logger.error(`上传失败: ${result.error}`);\n return {\n isError: true,\n content: [{\n type: \"text\",\n text: `上传失败: ${result.error}`\n }]\n };\n }\n } catch (error) {\n Logger.error(`上传过程中出错:`, error);\n return {\n isError: true,\n content: [{\n type: \"text\",\n text: `上传出错: ${error}`\n }]\n };\n }\n }\n );\n\n // 工具:列出可用的OSS配置\n this.server.tool(\n \"list_oss_configs\",\n \"列出可用的阿里云OSS配置\",\n {},\n async () => {\n try {\n const configs = ossService.getConfigs();\n const configNames = configs.map(config => config.id);\n\n if (configNames.length === 0) {\n return {\n content: [{\n type: \"text\",\n text: \"未找到OSS配置。请检查环境变量设置。\"\n }]\n };\n }\n\n return {\n content: [{\n type: \"text\",\n text: `可用的OSS配置:\\n${configNames.map(name => `- ${name}`).join('\\n')}`\n }]\n };\n } catch (error) {\n Logger.error(`获取OSS配置列表时出错:`, error);\n return {\n isError: true,\n content: [{\n type: \"text\",\n text: `获取配置列表失败: ${error}`\n }]\n };\n }\n }\n );\n\n // 工具:批量重命名OSS文件\n this.server.tool(\n \"batch_rename_files\",\n \"批量重命名阿里云OSS文件。通过copy+delete实现。【重要】首次调用必须使用dryRun=true预览,展示给用户确认后,用户同意才能用dryRun=false执行实际重命名。禁止跳过预览直接执行!\",\n {\n directory: z.string().describe(\"OSS中的目录路径(如 'images/icons',根目录传空字符串 '')\"),\n renameRules: z.array(z.object({\n oldName: z.string().describe(\"原文件名\"),\n newName: z.string().describe(\"新文件名\")\n })).describe(\"重命名规则数组,每项包含原文件名和新文件名\"),\n configName: z.string().optional().describe(`OSS配置名称(默认为'default')。可用配置: ${configNames.join(', ') || '无'}`),\n dryRun: z.boolean().optional().describe(\"是否为预览模式(默认false)。为true时只返回将要执行的操作,不实际重命名\")\n },\n async ({ directory, renameRules, configName = 'default', dryRun = false }) => {\n try {\n Logger.log(`OSS批量重命名: 目录=${directory}, 规则数=${renameRules.length}, 配置=${configName}, 预览模式=${dryRun}`);\n\n let results: { oldName: string; newName: string; success: boolean; error?: string }[];\n\n if (dryRun) {\n // 预览模式:只返回将要执行的操作\n results = renameRules.map(rule => ({\n oldName: rule.oldName,\n newName: rule.newName,\n success: true\n }));\n } else {\n // 实际执行OSS重命名\n results = await ossService.batchRenameFiles(renameRules, directory, configName);\n }\n\n const successCount = results.filter(r => r.success).length;\n const failCount = results.filter(r => !r.success).length;\n\n let resultText = dryRun ? `【预览模式】以下是将要执行的OSS文件重命名操作:\\n\\n` : `OSS文件批量重命名完成:\\n\\n`;\n resultText += `配置: ${configName}\\n`;\n resultText += `目录: ${directory || '根目录'}\\n`;\n resultText += `成功: ${successCount} 个, 失败: ${failCount} 个\\n\\n`;\n\n if (results.length > 0) {\n resultText += '详细结果:\\n';\n for (const r of results) {\n if (r.success) {\n resultText += `✅ ${r.oldName} → ${r.newName}\\n`;\n } else {\n resultText += `❌ ${r.oldName} → ${r.newName} (${r.error})\\n`;\n }\n }\n }\n\n return {\n content: [{\n type: \"text\",\n text: resultText\n }]\n };\n } catch (error) {\n Logger.error(`OSS批量重命名出错:`, error);\n return {\n isError: true,\n content: [{\n type: \"text\",\n text: `OSS批量重命名失败: ${error}`\n }]\n };\n }\n }\n );\n\n // 工具:列出本地目录文件\n this.server.tool(\n \"list_directory_files\",\n \"列出本地文件系统中指定目录下的所有文件。注意:此工具仅支持本地路径,如果要列出 OSS 中的文件,请使用 list_oss_files 工具。\",\n {\n directory: z.string().describe(\"要查看的目录路径\"),\n pattern: z.string().optional().describe(\"文件名过滤模式(可选),如 '*.png' 或 'icon_*'\")\n },\n async ({ directory, pattern }) => {\n try {\n Logger.log(`列出目录文件: ${directory}, 过滤: ${pattern || '无'}`);\n\n // 检查目录是否存在\n if (!fs.existsSync(directory)) {\n throw new Error(`目录不存在: ${directory}`);\n }\n\n const stat = fs.statSync(directory);\n if (!stat.isDirectory()) {\n throw new Error(`路径不是目录: ${directory}`);\n }\n\n let files = fs.readdirSync(directory);\n\n // 过滤掉隐藏文件\n files = files.filter(f => !f.startsWith('.'));\n\n // 如果有 pattern,进行简单的通配符匹配\n if (pattern) {\n const regex = new RegExp(\n '^' + pattern\n .replace(/\\./g, '\\\\.')\n .replace(/\\*/g, '.*')\n .replace(/\\?/g, '.') + '$',\n 'i'\n );\n files = files.filter(f => regex.test(f));\n }\n\n // 获取文件信息\n const fileInfos = files.map(f => {\n const filePath = path.join(directory, f);\n const fileStat = fs.statSync(filePath);\n return {\n name: f,\n isDirectory: fileStat.isDirectory(),\n size: fileStat.size\n };\n });\n\n // 排序:目录在前,文件在后,按名称排序\n fileInfos.sort((a, b) => {\n if (a.isDirectory !== b.isDirectory) {\n return a.isDirectory ? -1 : 1;\n }\n return a.name.localeCompare(b.name);\n });\n\n if (fileInfos.length === 0) {\n return {\n content: [{\n type: \"text\",\n text: `目录 ${directory} 下没有找到匹配的文件${pattern ? ` (过滤: ${pattern})` : ''}`\n }]\n };\n }\n\n let resultText = `目录: ${directory}\\n`;\n if (pattern) {\n resultText += `过滤: ${pattern}\\n`;\n }\n resultText += `共 ${fileInfos.length} 个项目:\\n\\n`;\n\n for (const f of fileInfos) {\n if (f.isDirectory) {\n resultText += `📁 ${f.name}/\\n`;\n } else {\n const sizeStr = f.size < 1024\n ? `${f.size}B`\n : f.size < 1024 * 1024\n ? `${(f.size / 1024).toFixed(1)}KB`\n : `${(f.size / 1024 / 1024).toFixed(1)}MB`;\n resultText += `📄 ${f.name} (${sizeStr})\\n`;\n }\n }\n\n return {\n content: [{\n type: \"text\",\n text: resultText\n }]\n };\n } catch (error) {\n Logger.error(`列出目录文件出错:`, error);\n return {\n isError: true,\n content: [{\n type: \"text\",\n text: `列出目录失败: ${error}`\n }]\n };\n }\n }\n );\n\n // 工具:列出OSS目录文件\n this.server.tool(\n \"list_oss_files\",\n \"列出阿里云OSS指定目录下的所有文件。用于查看 OSS 中的文件以便进行重命名或其他操作。注意:如果要列出本地文件,请使用 list_directory_files 工具。\",\n {\n directory: z.string().describe(\"OSS中的目录路径(如 'images/icons',根目录传空字符串 '')\"),\n pattern: z.string().optional().describe(\"文件名过滤模式(可选),如 '*.png' 或 'icon_*'\"),\n configName: z.string().optional().describe(`OSS配置名称(默认为'default')。可用配置: ${configNames.join(', ') || '无'}`)\n },\n async ({ directory, pattern, configName = 'default' }) => {\n try {\n Logger.log(`列出OSS目录文件: ${directory || '根目录'}, 过滤: ${pattern || '无'}, 配置: ${configName}`);\n\n const result = await ossService.listFiles(directory, configName, pattern);\n\n if (!result.success) {\n return {\n isError: true,\n content: [{\n type: \"text\",\n text: `列出OSS文件失败: ${result.error}`\n }]\n };\n }\n\n const files = result.files || [];\n\n if (files.length === 0) {\n return {\n content: [{\n type: \"text\",\n text: `OSS目录 ${directory || '根目录'} 下没有找到匹配的文件${pattern ? ` (过滤: ${pattern})` : ''}\\n配置: ${configName}`\n }]\n };\n }\n\n const sizeStr = (size: number) => size < 1024\n ? `${size}B`\n : size < 1024 * 1024\n ? `${(size / 1024).toFixed(1)}KB`\n : `${(size / 1024 / 1024).toFixed(1)}MB`;\n\n let resultText = `OSS目录: ${directory || '根目录'}\\n`;\n resultText += `配置: ${configName}\\n`;\n if (pattern) {\n resultText += `过滤: ${pattern}\\n`;\n }\n resultText += `共 ${files.length} 个文件:\\n\\n`;\n\n for (const f of files) {\n resultText += `📄 ${f.name} (${sizeStr(f.size)})\\n`;\n }\n\n return {\n content: [{\n type: \"text\",\n text: resultText\n }]\n };\n } catch (error) {\n Logger.error(`列出OSS目录文件出错:`, error);\n return {\n isError: true,\n content: [{\n type: \"text\",\n text: `列出OSS目录失败: ${error}`\n }]\n };\n }\n }\n );\n\n // 工具:删除OSS文件\n // 检查环境变量是否允许删除操作\n const allowDeleteOperation = process.env.ALLOW_DELETE_OPERATION === 'true';\n\n this.server.tool(\n \"delete_oss_files\",\n `删除阿里云OSS中的文件。支持单个删除、批量删除和通配符匹配。\n\n【⚠️ 安全限制】此工具需要配置环境变量 ALLOW_DELETE_OPERATION=true 才能使用。\n当前状态: ${allowDeleteOperation ? '✅ 已启用' : '❌ 未启用(需要在 MCP 配置中添加 \"env\": { \"ALLOW_DELETE_OPERATION\": \"true\" })'}\n\n【重要】首次调用必须使用 dryRun=true 预览,展示给用户确认后,用户同意才能用 dryRun=false 执行实际删除。禁止跳过预览直接执行!`,\n {\n directory: z.string().describe(\"OSS中的目录路径(如 'images/icons',根目录传空字符串 '')\"),\n fileNames: z.array(z.string()).optional().describe(\"要删除的文件名数组(与 pattern 二选一)\"),\n pattern: z.string().optional().describe(\"文件名通配符模式(如 '*.tmp' 或 'test_*'),与 fileNames 二选一\"),\n configName: z.string().optional().describe(`OSS配置名称(默认为'default')。可用配置: ${configNames.join(', ') || '无'}`),\n dryRun: z.boolean().optional().describe(\"是否为预览模式(默认false)。为true时只返回将要删除的文件列表,不实际删除\")\n },\n async ({ directory, fileNames, pattern, configName = 'default', dryRun = false }) => {\n try {\n // 检查是否允许删除操作\n if (!allowDeleteOperation) {\n return {\n isError: true,\n content: [{\n type: \"text\",\n text: `❌ 删除操作被禁止!\n\n要启用删除功能,请在 MCP 配置中添加环境变量:\n\n{\n \"mcpServers\": {\n \"oss-mcp-plus\": {\n \"command\": \"npx\",\n \"args\": [\"oss-mcp-plus\", ...],\n \"env\": {\n \"ALLOW_DELETE_OPERATION\": \"true\"\n }\n }\n }\n}\n\n这是一个安全措施,防止误删除文件。`\n }]\n };\n }\n\n Logger.log(`删除OSS文件: 目录=${directory}, 配置=${configName}, 预览模式=${dryRun}`);\n\n // 必须提供 fileNames 或 pattern 之一\n if (!fileNames && !pattern) {\n return {\n isError: true,\n content: [{\n type: \"text\",\n text: \"请提供 fileNames(文件名数组)或 pattern(通配符模式)之一\"\n }]\n };\n }\n\n let filesToDelete: string[] = [];\n\n if (fileNames && fileNames.length > 0) {\n // 直接使用提供的文件名\n filesToDelete = fileNames;\n } else if (pattern) {\n // 使用通配符匹配文件\n const listResult = await ossService.listFiles(directory, configName, pattern);\n if (!listResult.success) {\n return {\n isError: true,\n content: [{\n type: \"text\",\n text: `列出文件失败: ${listResult.error}`\n }]\n };\n }\n filesToDelete = (listResult.files || []).map(f => f.name);\n }\n\n if (filesToDelete.length === 0) {\n return {\n content: [{\n type: \"text\",\n text: `没有找到匹配的文件${pattern ? ` (模式: ${pattern})` : ''}\\n目录: ${directory || '根目录'}\\n配置: ${configName}`\n }]\n };\n }\n\n if (dryRun) {\n // 预览模式:只返回将要删除的文件列表\n let resultText = `【预览模式】以下是将要删除的文件:\\n\\n`;\n resultText += `配置: ${configName}\\n`;\n resultText += `目录: ${directory || '根目录'}\\n`;\n if (pattern) {\n resultText += `匹配模式: ${pattern}\\n`;\n }\n resultText += `文件数量: ${filesToDelete.length}\\n\\n`;\n resultText += `文件列表:\\n`;\n for (const fileName of filesToDelete) {\n resultText += `🗑️ ${fileName}\\n`;\n }\n resultText += `\\n⚠️ 确认要删除这些文件后,请使用 dryRun=false 执行实际删除。`;\n\n return {\n content: [{\n type: \"text\",\n text: resultText\n }]\n };\n }\n\n // 实际执行删除\n const results = await ossService.batchDeleteFiles(filesToDelete, directory, configName);\n\n const successCount = results.filter(r => r.success).length;\n const failCount = results.filter(r => !r.success).length;\n\n let resultText = `OSS文件删除完成:\\n\\n`;\n resultText += `配置: ${configName}\\n`;\n resultText += `目录: ${directory || '根目录'}\\n`;\n resultText += `成功: ${successCount} 个, 失败: ${failCount} 个\\n\\n`;\n\n if (results.length > 0) {\n resultText += '详细结果:\\n';\n for (const r of results) {\n if (r.success) {\n resultText += `✅ ${r.fileName} 已删除\\n`;\n } else {\n resultText += `❌ ${r.fileName} (${r.error})\\n`;\n }\n }\n }\n\n return {\n content: [{\n type: \"text\",\n text: resultText\n }]\n };\n } catch (error) {\n Logger.error(`删除OSS文件出错:`, error);\n return {\n isError: true,\n content: [{\n type: \"text\",\n text: `删除OSS文件失败: ${error}`\n }]\n };\n }\n }\n );\n\n // 工具:下载文件\n this.server.tool(\n \"download_file\",\n \"从 URL 下载文件到本地目录。支持 HTTP/HTTPS 链接,可自定义保存文件名。\",\n {\n url: z.string().describe(\"要下载的文件 URL\"),\n targetDir: z.string().describe(\"保存文件的本地目录路径\"),\n fileName: z.string().optional().describe(\"保存的文件名(可选,默认从 URL 提取)\")\n },\n async ({ url, targetDir, fileName }) => {\n try {\n Logger.log(`下载文件: ${url} 到 ${targetDir}`);\n\n // 检查目录是否存在,不存在则创建\n if (!fs.existsSync(targetDir)) {\n fs.mkdirSync(targetDir, { recursive: true });\n Logger.log(`创建目录: ${targetDir}`);\n }\n\n const stat = fs.statSync(targetDir);\n if (!stat.isDirectory()) {\n throw new Error(`路径不是目录: ${targetDir}`);\n }\n\n // 从 URL 提取文件名\n let finalFileName = fileName;\n if (!finalFileName) {\n const urlObj = new URL(url);\n finalFileName = path.basename(urlObj.pathname);\n // 如果 URL 没有文件名,生成一个\n if (!finalFileName || finalFileName === '/') {\n finalFileName = `download_${Date.now()}`;\n }\n }\n\n const filePath = path.join(targetDir, finalFileName);\n\n // 检查文件是否已存在\n if (fs.existsSync(filePath)) {\n throw new Error(`文件已存在: ${filePath}`);\n }\n\n // 下载文件\n await new Promise<void>((resolve, reject) => {\n const urlObj = new URL(url);\n const protocol = urlObj.protocol === 'https:' ? https : http;\n\n const request = protocol.get(url, (response) => {\n // 处理重定向\n if (response.statusCode === 301 || response.statusCode === 302) {\n const redirectUrl = response.headers.location;\n if (redirectUrl) {\n Logger.log(`重定向到: ${redirectUrl}`);\n const redirectProtocol = redirectUrl.startsWith('https:') ? https : http;\n redirectProtocol.get(redirectUrl, (redirectResponse) => {\n if (redirectResponse.statusCode !== 200) {\n reject(new Error(`下载失败,HTTP 状态码: ${redirectResponse.statusCode}`));\n return;\n }\n const fileStream = fs.createWriteStream(filePath);\n redirectResponse.pipe(fileStream);\n fileStream.on('finish', () => {\n fileStream.close();\n resolve();\n });\n fileStream.on('error', (err) => {\n fs.unlink(filePath, () => {});\n reject(err);\n });\n }).on('error', reject);\n return;\n }\n }\n\n if (response.statusCode !== 200) {\n reject(new Error(`下载失败,HTTP 状态码: ${response.statusCode}`));\n return;\n }\n\n const fileStream = fs.createWriteStream(filePath);\n response.pipe(fileStream);\n\n fileStream.on('finish', () => {\n fileStream.close();\n resolve();\n });\n\n fileStream.on('error', (err) => {\n fs.unlink(filePath, () => {});\n reject(err);\n });\n });\n\n request.on('error', (err) => {\n fs.unlink(filePath, () => {});\n reject(err);\n });\n\n request.setTimeout(60000, () => {\n request.destroy();\n fs.unlink(filePath, () => {});\n reject(new Error('下载超时(60秒)'));\n });\n });\n\n // 获取文件大小\n const downloadedStat = fs.statSync(filePath);\n const sizeStr = downloadedStat.size < 1024\n ? `${downloadedStat.size}B`\n : downloadedStat.size < 1024 * 1024\n ? `${(downloadedStat.size / 1024).toFixed(1)}KB`\n : `${(downloadedStat.size / 1024 / 1024).toFixed(1)}MB`;\n\n return {\n content: [{\n type: \"text\",\n text: `文件下载成功!\\n源URL: ${url}\\n保存路径: ${filePath}\\n文件大小: ${sizeStr}`\n }]\n };\n } catch (error) {\n Logger.error(`下载文件出错:`, error);\n return {\n isError: true,\n content: [{\n type: \"text\",\n text: `下载失败: ${error}`\n }]\n };\n }\n }\n );\n\n // 工具:检查压缩图片的前置条件\n this.server.tool(\n \"check_compress_prerequisites\",\n `检查图片压缩的前置条件并返回用户询问模板。\n\n【⚠️ 极其重要 - AI 必须严格按以下顺序执行】\n\n第 1 步:检查 Playwright MCP(必须在调用本工具之前完成!)\n- 调用 browser_snapshot 工具\n- 如果成功:继续执行\n- 如果失败/报错:立即停止,告知用户需要启用 Playwright MCP\n\n第 2 步:如果图片在 OSS 上(必须在调用本工具之前完成!)\n- 先使用 list_oss_files 列出文件\n- ⚠️ 重要:下载到【项目目录】下的 .tmp-compress/ 文件夹,不能用 /tmp/!\n (Playwright MCP 只能访问项目目录内的文件)\n- 使用 download_file 将图片下载到项目目录下\n- 只有下载到本地后才能调用本工具\n\n第 3 步:调用本工具\n- 传入本地图片路径数组\n- 获取用户询问模板\n\n第 4 步:询问用户\n- 使用 AskUserQuestion 询问压缩引擎、输出格式等\n\n第 5 步:执行压缩\n- 调用 compress_images 工具`,\n {\n images: z.array(z.string()).describe(\"要压缩的【本地】图片路径数组。如果图片在 OSS 上,必须先用 download_file 下载到本地!\")\n },\n async ({ images }) => {\n try {\n // 验证图片文件\n const validImages: { path: string; name: string; ext: string; size: number }[] = [];\n const errors: string[] = [];\n\n for (const imgPath of images) {\n if (!fs.existsSync(imgPath)) {\n errors.push(`文件不存在: ${imgPath}`);\n continue;\n }\n const stat = fs.statSync(imgPath);\n if (stat.size > 5 * 1024 * 1024) {\n errors.push(`文件超过 5MB 限制: ${imgPath}`);\n continue;\n }\n const ext = path.extname(imgPath).toLowerCase().slice(1);\n if (!['png', 'jpg', 'jpeg', 'webp', 'gif', 'bmp', 'tiff'].includes(ext)) {\n errors.push(`不支持的格式: ${imgPath}`);\n continue;\n }\n validImages.push({\n path: imgPath,\n name: path.basename(imgPath, path.extname(imgPath)),\n ext: ext === 'jpg' ? 'jpeg' : ext,\n size: stat.size\n });\n }\n\n if (validImages.length === 0) {\n return {\n isError: true,\n content: [{\n type: \"text\",\n text: `没有有效的图片可处理:\\n${errors.join('\\n')}`\n }]\n };\n }\n\n // 构建需要询问用户的问题\n const questions = {\n playwrightCheck: {\n instruction: \"请先使用 browser_snapshot 工具测试 Playwright MCP 是否可用。如果报错说明未配置。\"\n },\n engineQuestion: {\n question: \"请选择压缩引擎\",\n header: \"压缩引擎\",\n options: [\n { label: \"TinyPNG (推荐)\", description: \"支持 PNG/JPEG/WebP 输出,压缩质量高,每批最多 3 个文件\" },\n { label: \"AnyWebP\", description: \"固定输出 WebP 格式,每批最多 20 个文件\" }\n ]\n },\n formatQuestion: {\n question: \"是否需要转换输出格式?\",\n header: \"输出格式\",\n options: [\n { label: \"保持原格式\", description: \"不转换格式,仅压缩\" },\n { label: \"转换为 WebP\", description: \"转换为 WebP 格式,体积更小\" },\n { label: \"转换为 JPEG\", description: \"转换为 JPEG 格式(仅 TinyPNG)\" },\n { label: \"转换为 PNG\", description: \"转换为 PNG 格式(仅 TinyPNG)\" }\n ]\n },\n deleteOriginalQuestion: {\n question: \"转换格式后是否删除原文件?\",\n header: \"删除原文件\",\n options: [\n { label: \"保留原文件\", description: \"在 OSS 上保留原格式文件\" },\n { label: \"删除原文件\", description: \"转换后删除 OSS 上的原格式文件\" }\n ],\n condition: \"仅当选择了转换格式时才需要询问\"\n }\n };\n\n const sizeStr = (size: number) => size < 1024\n ? `${size}B`\n : size < 1024 * 1024\n ? `${(size / 1024).toFixed(1)}KB`\n : `${(size / 1024 / 1024).toFixed(1)}MB`;\n\n let resultText = `## 图片压缩前置检查\\n\\n`;\n resultText += `### ✅ 有效图片 (${validImages.length} 个)\\n`;\n for (const img of validImages) {\n resultText += `- ${path.basename(img.path)} (${sizeStr(img.size)})\\n`;\n }\n\n if (errors.length > 0) {\n resultText += `\\n### ⚠️ 跳过的文件\\n`;\n for (const err of errors) {\n resultText += `- ${err}\\n`;\n }\n }\n\n resultText += `\\n### 📋 AI 执行步骤\\n\\n`;\n resultText += `1. **检查 Playwright**: 调用 \\`browser_snapshot\\` 测试是否可用\\n`;\n resultText += ` - 如果报错,提示用户需要配置 Playwright MCP\\n`;\n resultText += `2. **询问用户**: 使用 AskUserQuestion 一次性询问以下问题:\\n`;\n resultText += ` - 选择压缩引擎 (TinyPNG / AnyWebP)\\n`;\n resultText += ` - 是否转换格式 (保持原格式 / WebP / JPEG / PNG)\\n`;\n resultText += ` - 如果转格式,是否删除原文件\\n`;\n resultText += `3. **执行压缩**: 根据用户选择调用 \\`compress_images\\`\\n`;\n\n return {\n content: [\n {\n type: \"text\",\n text: resultText\n },\n {\n type: \"text\",\n text: `\\n---\\n**询问模板 (JSON)**:\\n\\`\\`\\`json\\n${JSON.stringify(questions, null, 2)}\\n\\`\\`\\``\n }\n ]\n };\n } catch (error) {\n Logger.error(`检查前置条件出错:`, error);\n return {\n isError: true,\n content: [{\n type: \"text\",\n text: `检查失败: ${error}`\n }]\n };\n }\n }\n );\n\n // 工具:压缩图片(生成压缩指令,由 AI 调用 Playwright MCP 执行)\n this.server.tool(\n \"compress_images\",\n `压缩图片工具。生成 Playwright 自动化压缩指令。\n\n【⚠️ 禁止直接调用!必须先完成以下步骤】\n\n✅ 第 1 步:验证 Playwright MCP 可用\n → 调用 browser_snapshot,如果报错则停止并提示用户启用\n\n✅ 第 2 步:确保图片在项目目录内\n → OSS 图片必须先用 download_file 下载到【项目目录】下的 .tmp-compress/ 文件夹\n → ⚠️ 不能用 /tmp/,Playwright 无法访问项目外的路径!\n\n✅ 第 3 步:调用 check_compress_prerequisites\n → 验证文件并获取询问模板\n\n✅ 第 4 步:询问用户偏好\n → 使用 AskUserQuestion 询问引擎、格式、是否删除原文件\n\n✅ 第 5 步:调用本工具\n → 传入用户选择的参数\n\n【后续流程】\n1. 按返回的指令使用 Playwright MCP 执行网页自动化\n2. 下载压缩结果\n3. 使用 upload_to_oss 上传回 OSS`,\n {\n images: z.array(z.string()).describe(\"要压缩的本地图片路径数组\"),\n engine: z.enum(['tinypng', 'anywebp']).describe(\"压缩引擎 (必须先询问用户选择)\"),\n outputFormat: z.enum(['png', 'jpeg', 'webp']).optional().describe(\"输出格式 (必须先询问用户选择,仅 tinypng 支持多格式)\"),\n deleteOriginal: z.boolean().optional().describe(\"转格式时是否删除原文件 (必须先询问用户选择)\"),\n ossDirectory: z.string().optional().describe(\"OSS 目标目录 (用于上传压缩后的文件)\"),\n configName: z.string().optional().describe(`OSS配置名称(默认为'default')。可用配置: ${configNames.join(', ') || '无'}`)\n },\n async ({ images, engine, outputFormat, deleteOriginal = false, ossDirectory, configName = 'default' }) => {\n try {\n Logger.log(`压缩图片: 引擎=${engine}, 格式=${outputFormat || '原格式'}, 图片数=${images.length}`);\n\n // 验证图片文件存在\n const validImages: { path: string; name: string; ext: string; size: number }[] = [];\n const errors: string[] = [];\n\n for (const imgPath of images) {\n if (!fs.existsSync(imgPath)) {\n errors.push(`文件不存在: ${imgPath}`);\n continue;\n }\n const stat = fs.statSync(imgPath);\n if (stat.size > 5 * 1024 * 1024) {\n errors.push(`文件超过 5MB 限制: ${imgPath}`);\n continue;\n }\n const ext = path.extname(imgPath).toLowerCase().slice(1);\n if (!['png', 'jpg', 'jpeg', 'webp', 'gif', 'bmp', 'tiff'].includes(ext)) {\n errors.push(`不支持的格式: ${imgPath}`);\n continue;\n }\n validImages.push({\n path: imgPath,\n name: path.basename(imgPath, path.extname(imgPath)),\n ext: ext === 'jpg' ? 'jpeg' : ext,\n size: stat.size\n });\n }\n\n if (validImages.length === 0) {\n return {\n isError: true,\n content: [{\n type: \"text\",\n text: `没有有效的图片可处理:\\n${errors.join('\\n')}`\n }]\n };\n }\n\n // 生成压缩指令\n const actualOutputFormat = engine === 'anywebp' ? 'webp' : (outputFormat || null);\n const batchSize = engine === 'tinypng' ? 3 : 20;\n const batches: typeof validImages[] = [];\n\n for (let i = 0; i < validImages.length; i += batchSize) {\n batches.push(validImages.slice(i, i + batchSize));\n }\n\n // 构建指令文本\n let instructions = `## 图片压缩指令\\n\\n`;\n instructions += `**引擎**: ${engine === 'tinypng' ? 'TinyPNG (https://tinypng.com/)' : 'AnyWebP (https://anywebp.com/convert-to-webp)'}\\n`;\n instructions += `**输出格式**: ${actualOutputFormat || '保持原格式'}\\n`;\n instructions += `**总图片数**: ${validImages.length}\\n`;\n instructions += `**批次数**: ${batches.length} (每批最多 ${batchSize} 个)\\n\\n`;\n\n if (errors.length > 0) {\n instructions += `### ⚠️ 跳过的文件\\n`;\n for (const err of errors) {\n instructions += `- ${err}\\n`;\n }\n instructions += `\\n`;\n }\n\n instructions += `### 📋 执行步骤\\n\\n`;\n instructions += `**前置检查**: 请确认 Playwright MCP 已配置并可用\\n\\n`;\n\n if (engine === 'tinypng') {\n instructions += this.generateTinyPngInstructions(batches, actualOutputFormat);\n } else {\n instructions += this.generateAnyWebPInstructions(batches);\n }\n\n // 添加后续处理指令\n instructions += `\\n### 📤 后续处理\\n\\n`;\n instructions += `压缩完成后,请执行以下操作:\\n\\n`;\n\n for (const img of validImages) {\n const newExt = actualOutputFormat || img.ext;\n const isFormatChange = newExt !== img.ext;\n const newFileName = `${img.name}.${newExt}`;\n const downloadPath = path.join(path.dirname(img.path), `${img.name}-compressed.${newExt}`);\n\n instructions += `**${path.basename(img.path)}**:\\n`;\n instructions += `1. 下载压缩结果到: \\`${downloadPath}\\`\\n`;\n\n if (ossDirectory) {\n if (isFormatChange) {\n instructions += `2. 上传到 OSS: \\`upload_to_oss(\"${downloadPath}\", \"${ossDirectory}\", \"${newFileName}\", \"${configName}\")\\`\\n`;\n if (deleteOriginal) {\n instructions += `3. 删除原文件: 在 OSS 上删除 \\`${ossDirectory}/${path.basename(img.path)}\\`\\n`;\n }\n } else {\n instructions += `2. 覆盖上传到 OSS: \\`upload_to_oss(\"${downloadPath}\", \"${ossDirectory}\", \"${path.basename(img.path)}\", \"${configName}\")\\`\\n`;\n }\n }\n instructions += `\\n`;\n }\n\n // 返回信息\n const resultInfo = {\n engine,\n outputFormat: actualOutputFormat,\n deleteOriginal: actualOutputFormat ? deleteOriginal : false,\n ossDirectory,\n configName,\n totalImages: validImages.length,\n batches: batches.length,\n batchSize,\n images: validImages.map(img => ({\n originalPath: img.path,\n originalName: path.basename(img.path),\n originalExt: img.ext,\n originalSize: img.size,\n newExt: actualOutputFormat || img.ext,\n isFormatChange: (actualOutputFormat || img.ext) !== img.ext\n }))\n };\n\n return {\n content: [\n {\n type: \"text\",\n text: instructions\n },\n {\n type: \"text\",\n text: `\\n---\\n**压缩任务数据 (JSON)**:\\n\\`\\`\\`json\\n${JSON.stringify(resultInfo, null, 2)}\\n\\`\\`\\``\n }\n ]\n };\n } catch (error) {\n Logger.error(`生成压缩指令出错:`, error);\n return {\n isError: true,\n content: [{\n type: \"text\",\n text: `生成压缩指令失败: ${error}`\n }]\n };\n }\n }\n );\n }\n\n // 生成 TinyPNG 自动化指令\n private generateTinyPngInstructions(batches: { path: string; name: string; ext: string; size: number }[][], outputFormat: string | null): string {\n let instructions = '';\n\n // 添加重要提示\n instructions += `### ⚠️ 重要提示\\n\\n`;\n instructions += `1. **图片必须在项目目录内**:Playwright MCP 只能访问项目目录下的文件。\\n`;\n instructions += ` - 不能使用 \\`/tmp/\\` 等系统临时目录\\n`;\n instructions += ` - 请将图片下载到项目根目录下的 \\`.tmp-compress/\\` 文件夹\\n`;\n instructions += `2. **上传前必须先触发文件选择框**:先点击上传区域,等待 Modal state 显示 \"File chooser\" 后再调用 \\`browser_file_upload\\`\\n\\n`;\n\n const needsFormatConversion = outputFormat && outputFormat !== 'png';\n\n for (let i = 0; i < batches.length; i++) {\n const batch = batches[i];\n instructions += `#### 批次 ${i + 1}/${batches.length}\\n\\n`;\n instructions += `**文件**: ${batch.map(img => path.basename(img.path)).join(', ')}\\n\\n`;\n\n let stepNum = 1;\n instructions += `${stepNum++}. **打开网站**: 使用 \\`browser_navigate\\` 访问 \\`https://tinypng.com/\\`\\n`;\n instructions += `${stepNum++}. **等待加载**: 使用 \\`browser_snapshot\\` 确认页面加载完成\\n`;\n\n // 如果需要转换格式(WebP/JPEG),先开启开关\n if (needsFormatConversion) {\n instructions += `${stepNum++}. **开启格式转换开关**: \\n`;\n instructions += ` - 在页面底部找到 \"Convert my images automatically\" 开关\\n`;\n instructions += ` - 使用 \\`browser_click\\` 点击开关开启它(如果是关闭状态)\\n`;\n instructions += ` - 开启后会出现格式选择选项\\n`;\n instructions += `${stepNum++}. **选择输出格式**: \\n`;\n instructions += ` - 点击选择 \"${outputFormat!.toUpperCase()}\" 格式\\n`;\n }\n\n instructions += `${stepNum++}. **触发文件选择框**: \\n`;\n instructions += ` - 使用 \\`browser_click\\` 点击上传区域(\"Drop your .webp, .png or .jpg files here!\" 文字区域)\\n`;\n instructions += ` - 等待 \\`browser_snapshot\\` 返回结果中 Modal state 显示 \"[File chooser]\"\\n`;\n instructions += `${stepNum++}. **上传文件**: 使用 \\`browser_file_upload\\` 上传以下文件:\\n`;\n for (const img of batch) {\n instructions += ` - \\`${img.path}\\`\\n`;\n }\n instructions += `${stepNum++}. **等待压缩**: 使用 \\`browser_wait_for\\` 等待 \"Download all\" 或各文件的 \"download\" 按钮出现\\n`;\n instructions += `${stepNum++}. **下载结果**: 点击 \"Download all\" 或逐个下载\\n`;\n\n if (i < batches.length - 1) {\n instructions += `${stepNum++}. **刷新页面**: 使用 \\`browser_navigate\\` 重新访问 \\`https://tinypng.com/\\` 准备下一批\\n`;\n }\n instructions += `\\n`;\n }\n\n return instructions;\n }\n\n // 生成 AnyWebP 自动化指令\n private generateAnyWebPInstructions(batches: { path: string; name: string; ext: string; size: number }[][]): string {\n let instructions = '';\n\n // 添加重要提示\n instructions += `### ⚠️ 重要提示\\n\\n`;\n instructions += `1. **图片必须在项目目录内**:Playwright MCP 只能访问项目目录下的文件。\\n`;\n instructions += ` - 不能使用 \\`/tmp/\\` 等系统临时目录\\n`;\n instructions += ` - 请将图片下载到项目根目录下的 \\`.tmp-compress/\\` 文件夹\\n`;\n instructions += `2. **上传前必须先触发文件选择框**:先点击上传区域,等待 Modal state 显示 \"File chooser\" 后再调用 \\`browser_file_upload\\`\\n\\n`;\n\n for (let i = 0; i < batches.length; i++) {\n const batch = batches[i];\n instructions += `#### 批次 ${i + 1}/${batches.length}\\n\\n`;\n instructions += `**文件**: ${batch.map(img => path.basename(img.path)).join(', ')}\\n\\n`;\n\n let stepNum = 1;\n instructions += `${stepNum++}. **打开网站**: 使用 \\`browser_navigate\\` 访问 \\`https://anywebp.com/convert-to-webp.html\\`\\n`;\n instructions += `${stepNum++}. **等待加载**: 使用 \\`browser_snapshot\\` 确认页面加载完成\\n`;\n instructions += `${stepNum++}. **触发文件选择框**: \\n`;\n instructions += ` - 使用 \\`browser_click\\` 点击 \"Drop your images here!\" 上传区域\\n`;\n instructions += ` - 等待 \\`browser_snapshot\\` 返回结果中 Modal state 显示 \"[File chooser]\"\\n`;\n instructions += `${stepNum++}. **上传文件**: 使用 \\`browser_file_upload\\` 上传以下文件:\\n`;\n for (const img of batch) {\n instructions += ` - \\`${img.path}\\`\\n`;\n }\n instructions += `${stepNum++}. **等待转换**: 使用 \\`browser_wait_for\\` 等待转换完成,出现 \"Download\" 按钮\\n`;\n instructions += `${stepNum++}. **下载结果**: 点击 \"Download All\" 或逐个下载 WebP 文件\\n`;\n\n if (i < batches.length - 1) {\n instructions += `${stepNum++}. **刷新页面**: 使用 \\`browser_navigate\\` 重新访问准备下一批\\n`;\n }\n instructions += `\\n`;\n }\n\n return instructions;\n }\n\n async connect(transport: Transport): Promise<void> {\n try {\n await this.server.connect(transport);\n\n Logger.log = (...args: any[]) => {\n try {\n this.server.server.sendLoggingMessage({\n level: \"info\",\n data: args,\n });\n } catch (error) {\n console.log(...args);\n }\n };\n\n Logger.error = (...args: any[]) => {\n try {\n this.server.server.sendLoggingMessage({\n level: \"error\",\n data: args,\n });\n } catch (error) {\n console.error(...args);\n }\n };\n\n Logger.log(\"OSS MCP服务器已连接并准备处理请求\");\n } catch (error) {\n console.error(\"连接到传输时出错:\", error);\n }\n }\n\n async startHttpServer(port: number): Promise<void> {\n const app = express();\n\n // SSE连接端点 - 修复头部发送冲突\n app.get(\"/sse\", (req: Request, res: Response) => {\n // 初始化SSE传输,不再自己设置头部,而是让SDK处理\n this.sseTransport = new SSEServerTransport(\n \"/messages\",\n res as unknown as ServerResponse<IncomingMessage>\n );\n\n try {\n // 连接到传输层\n this.server.connect(this.sseTransport)\n .catch((err) => {\n console.error(\"连接到SSE传输时出错:\", err);\n });\n\n // 处理客户端断开连接\n req.on('close', () => {\n console.log('SSE客户端断开连接');\n this.sseTransport = null;\n });\n } catch (error) {\n console.error(\"建立SSE连接时出错:\", error);\n // 如果连接失败,关闭响应\n if (!res.writableEnded) {\n res.status(500).end();\n }\n }\n });\n\n // 消息端点\n app.post(\"/messages\", async (req: Request, res: Response) => {\n if (!this.sseTransport) {\n console.log(\"尝试发送消息,但SSE传输未初始化\");\n res.status(400).json({\n error: 'SSE连接未建立',\n message: '请先连接到/sse端点'\n });\n return;\n }\n\n try {\n await this.sseTransport.handlePostMessage(\n req as unknown as IncomingMessage,\n res as unknown as ServerResponse<IncomingMessage>\n );\n } catch (error) {\n console.error(\"处理消息时出错:\", error);\n if (!res.writableEnded) {\n res.status(500).json({\n error: \"内部服务器错误\",\n message: String(error)\n });\n }\n }\n });\n\n // 启动服务器\n app.listen(port, () => {\n Logger.log = console.log;\n Logger.error = console.error;\n\n Logger.log(`HTTP服务器监听端口: ${port}`);\n Logger.log(`SSE端点: http://localhost:${port}/sse`);\n Logger.log(`消息端点: http://localhost:${port}/messages`);\n });\n }\n}\n","import OSS from 'ali-oss';\nimport fs from 'fs';\nimport path from 'path';\nimport { OssConfig, getOssConfig, getAllOssConfigs } from '../config/oss.config.js';\nimport { z } from 'zod';\n\n// 上传文件参数验证Schema\nexport const UploadFileParamsSchema = z.object({\n filePath: z.string(),\n targetDir: z.string().optional(),\n fileName: z.string().optional(),\n configName: z.string().optional(),\n});\n\n// 导出上传文件参数类型\nexport type UploadFileParams = z.infer<typeof UploadFileParamsSchema>;\n\n// 上传结果验证Schema\nexport const UploadResultSchema = z.object({\n success: z.boolean(),\n url: z.string().optional(),\n error: z.string().optional(),\n ossConfigName: z.string().optional(),\n});\n\n// 导出上传结果类型\nexport type UploadResult = z.infer<typeof UploadResultSchema>;\n\n/**\n * OSS配置接口(包含ID和名称)\n */\nexport interface OssConfigWithMeta extends OssConfig {\n id: string;\n name: string;\n}\n\n/**\n * 阿里云OSS服务类\n */\nexport class OssService {\n private clients: Map<string, OSS> = new Map();\n\n /**\n * 获取所有OSS配置\n * @returns OSS配置列表\n */\n getConfigs(): OssConfigWithMeta[] {\n const configs: OssConfigWithMeta[] = [];\n const allConfigs = getAllOssConfigs();\n\n for (const [id, config] of Object.entries(allConfigs)) {\n configs.push({\n id,\n name: `${id.charAt(0).toUpperCase()}${id.slice(1)} 配置`,\n ...config\n });\n }\n\n return configs;\n }\n\n /**\n * 获取OSS客户端\n * @param configName 配置名称\n * @returns OSS客户端实例\n */\n private getClient(configName: string = 'default'): OSS | null {\n // 检查缓存中是否已有客户端\n if (this.clients.has(configName)) {\n return this.clients.get(configName) as OSS;\n }\n\n // 获取配置并创建客户端\n const config = getOssConfig(configName);\n if (!config) {\n return null;\n }\n\n try {\n const client = new OSS({\n region: config.region,\n accessKeyId: config.accessKeyId,\n accessKeySecret: config.accessKeySecret,\n bucket: config.bucket,\n endpoint: config.endpoint\n });\n\n // 缓存客户端实例\n this.clients.set(configName, client);\n return client;\n } catch (error) {\n console.error(`Failed to create OSS client for ${configName}:`, error);\n return null;\n }\n }\n\n /**\n * 上传文件到OSS\n * @param params 上传参数\n * @returns 上传结果\n */\n async uploadFile(params: UploadFileParams): Promise<UploadResult> {\n // 验证并解析参数\n const validParams = UploadFileParamsSchema.parse(params);\n const { filePath, targetDir = '', fileName, configName = 'default' } = validParams;\n\n try {\n // 检查文件是否存在\n if (!fs.existsSync(filePath)) {\n return UploadResultSchema.parse({\n success: false,\n error: `File not found: ${filePath}`,\n ossConfigName: configName\n });\n }\n\n // 获取OSS客户端\n const client = this.getClient(configName);\n if (!client) {\n return UploadResultSchema.parse({\n success: false,\n error: `OSS config not found for: ${configName}`,\n ossConfigName: configName\n });\n }\n\n // 确定文件名\n const actualFileName = fileName || path.basename(filePath);\n\n // 构建OSS路径,确保正斜杠格式\n let ossPath = actualFileName;\n if (targetDir) {\n // 规范化目标目录:移除头尾斜杠,然后加上结尾斜杠\n const normalizedDir = targetDir.replace(/^\\/+|\\/+$/g, '');\n ossPath = normalizedDir ? `${normalizedDir}/${actualFileName}` : actualFileName;\n }\n\n // 上传文件\n const result = await client.put(ossPath, filePath);\n\n return UploadResultSchema.parse({\n success: true,\n url: result.url,\n ossConfigName: configName\n });\n } catch (error) {\n return UploadResultSchema.parse({\n success: false,\n error: `Upload failed: ${(error as Error).message}`,\n ossConfigName: configName\n });\n }\n }\n\n /**\n * 重命名OSS文件(通过 copy + delete 实现)\n * @param oldKey 原文件路径\n * @param newKey 新文件路径\n * @param configName 配置名称\n * @returns 重命名结果\n */\n async renameFile(oldKey: string, newKey: string, configName: string = 'default'): Promise<{ success: boolean; error?: string }> {\n try {\n const client = this.getClient(configName);\n if (!client) {\n return { success: false, error: `OSS config not found for: ${configName}` };\n }\n\n // 规范化路径:移除开头的斜杠\n const normalizedOldKey = oldKey.replace(/^\\/+/, '');\n const normalizedNewKey = newKey.replace(/^\\/+/, '');\n\n // 检查源文件是否存在\n try {\n await client.head(normalizedOldKey);\n } catch (_e) {\n return { success: false, error: `源文件不存在: ${normalizedOldKey}` };\n }\n\n // 检查目标文件是否已存在\n try {\n await client.head(normalizedNewKey);\n // 如果到这里说明文件存在\n if (normalizedOldKey !== normalizedNewKey) {\n return { success: false, error: `目标文件已存在: ${normalizedNewKey}` };\n }\n } catch (_e) {\n // 文件不存在,可以继续\n }\n\n // Step 1: 复制文件到新位置\n await client.copy(normalizedNewKey, normalizedOldKey);\n\n // Step 2: 删除原文件\n await client.delete(normalizedOldKey);\n\n return { success: true };\n } catch (error) {\n return { success: false, error: `重命名失败: ${(error as Error).message}` };\n }\n }\n\n /**\n * 列出OSS目录下的文件\n * @param directory OSS目录路径\n * @param configName 配置名称\n * @param pattern 文件名过滤模式(可选)\n * @returns 文件列表\n */\n async listFiles(\n directory: string = '',\n configName: string = 'default',\n pattern?: string\n ): Promise<{ success: boolean; files?: Array<{ name: string; size: number; lastModified: Date }>; error?: string }> {\n try {\n const client = this.getClient(configName);\n if (!client) {\n return { success: false, error: `OSS config not found for: ${configName}` };\n }\n\n // 规范化目录路径\n const normalizedDir = directory.replace(/^\\/+|\\/+$/g, '');\n const prefix = normalizedDir ? `${normalizedDir}/` : '';\n\n // 列出文件\n const result = await client.list({\n prefix,\n delimiter: '/',\n 'max-keys': 1000\n }, {});\n\n const files: Array<{ name: string; size: number; lastModified: Date }> = [];\n\n // 处理文件对象\n if (result.objects) {\n for (const obj of result.objects) {\n // 跳过目录本身(以 / 结尾的)\n if (obj.name.endsWith('/')) continue;\n\n // 提取文件名(去掉目录前缀)\n const fileName = obj.name.replace(prefix, '');\n if (!fileName) continue;\n\n // 如果有 pattern,进行简单的通配符匹配\n if (pattern) {\n const regex = new RegExp(\n '^' + pattern\n .replace(/\\./g, '\\\\.')\n .replace(/\\*/g, '.*')\n .replace(/\\?/g, '.') + '$',\n 'i'\n );\n if (!regex.test(fileName)) continue;\n }\n\n files.push({\n name: fileName,\n size: obj.size,\n lastModified: new Date(obj.lastModified)\n });\n }\n }\n\n // 按文件名排序\n files.sort((a, b) => a.name.localeCompare(b.name));\n\n return { success: true, files };\n } catch (error) {\n return { success: false, error: `列出文件失败: ${(error as Error).message}` };\n }\n }\n\n /**\n * 批量重命名OSS文件\n * @param rules 重命名规则数组\n * @param directory OSS目录路径\n * @param configName 配置名称\n * @returns 批量重命名结果\n */\n async batchRenameFiles(\n rules: Array<{ oldName: string; newName: string }>,\n directory: string = '',\n configName: string = 'default'\n ): Promise<Array<{ oldName: string; newName: string; success: boolean; error?: string }>> {\n const results: Array<{ oldName: string; newName: string; success: boolean; error?: string }> = [];\n\n // 规范化目录路径\n const normalizedDir = directory.replace(/^\\/+|\\/+$/g, '');\n const dirPrefix = normalizedDir ? `${normalizedDir}/` : '';\n\n for (const rule of rules) {\n const oldKey = `${dirPrefix}${rule.oldName}`;\n const newKey = `${dirPrefix}${rule.newName}`;\n\n const result = await this.renameFile(oldKey, newKey, configName);\n results.push({\n oldName: rule.oldName,\n newName: rule.newName,\n success: result.success,\n error: result.error\n });\n }\n\n return results;\n }\n\n /**\n * 删除单个OSS文件\n * @param key 文件路径\n * @param configName 配置名称\n * @returns 删除结果\n */\n async deleteFile(key: string, configName: string = 'default'): Promise<{ success: boolean; error?: string }> {\n try {\n const client = this.getClient(configName);\n if (!client) {\n return { success: false, error: `OSS config not found for: ${configName}` };\n }\n\n // 规范化路径:移除开头的斜杠\n const normalizedKey = key.replace(/^\\/+/, '');\n\n // 检查文件是否存在\n try {\n await client.head(normalizedKey);\n } catch (_e) {\n return { success: false, error: `文件不存在: ${normalizedKey}` };\n }\n\n // 删除文件\n await client.delete(normalizedKey);\n\n return { success: true };\n } catch (error) {\n return { success: false, error: `删除失败: ${(error as Error).message}` };\n }\n }\n\n /**\n * 批量删除OSS文件\n * @param fileNames 文件名数组\n * @param directory OSS目录路径\n * @param configName 配置名称\n * @returns 批量删除结果\n */\n async batchDeleteFiles(\n fileNames: string[],\n directory: string = '',\n configName: string = 'default'\n ): Promise<Array<{ fileName: string; success: boolean; error?: string }>> {\n const results: Array<{ fileName: string; success: boolean; error?: string }> = [];\n\n // 规范化目录路径\n const normalizedDir = directory.replace(/^\\/+|\\/+$/g, '');\n const dirPrefix = normalizedDir ? `${normalizedDir}/` : '';\n\n for (const fileName of fileNames) {\n const key = `${dirPrefix}${fileName}`;\n const result = await this.deleteFile(key, configName);\n results.push({\n fileName,\n success: result.success,\n error: result.error\n });\n }\n\n return results;\n }\n}\n\n// 导出单例实例\nexport const ossService = new OssService();\n","import { config } from \"dotenv\";\nimport yargs from \"yargs\";\nimport { hideBin } from \"yargs/helpers\";\nimport { z } from \"zod\";\n\nconfig();\n\n// OSS配置验证Schema\nexport const OssConfigSchema = z.object({\n region: z.string(),\n accessKeyId: z.string(),\n accessKeySecret: z.string(),\n bucket: z.string(),\n endpoint: z.string(),\n});\n\n// 导出OSS配置类型\nexport type OssConfig = z.infer<typeof OssConfigSchema>;\n\n// 服务器配置接口\nexport interface ServerConfig {\n port: number;\n ossConfig: Record<string, OssConfig>;\n configSources: {\n port: \"cli\" | \"env\" | \"default\";\n ossConfig: \"cli\" | \"env\" | \"default\";\n };\n}\n\n// 掩码函数,用于打印敏感信息\nfunction maskSecret(secret: string): string {\n if (secret.length <= 4) return \"****\";\n return `${secret.substring(0, 4)}****${secret.slice(-4)}`;\n}\n\n// 获取服务器配置\nexport function getServerConfig(isStdioMode: boolean = false): ServerConfig {\n // 解析命令行参数\n const argv = yargs(hideBin(process.argv))\n .options({\n \"oss-config\": {\n type: \"string\",\n description: \"OSS配置JSON字符串\",\n },\n port: {\n type: \"number\",\n description: \"服务器运行端口\",\n default: 3000,\n },\n })\n .help()\n .version(\"1.0.0\")\n .parseSync();\n\n const config: ServerConfig = {\n port: 3000,\n ossConfig: {},\n configSources: {\n port: \"default\",\n ossConfig: \"default\",\n },\n };\n\n // 处理端口配置\n if (argv.port) {\n config.port = argv.port;\n config.configSources.port = \"cli\";\n } else if (process.env.PORT) {\n config.port = parseInt(process.env.PORT, 10);\n config.configSources.port = \"env\";\n }\n\n // 处理OSS配置 - 首先检查命令行参数\n if (argv[\"oss-config\"]) {\n const allOssConfigs = JSON.parse(argv[\"oss-config\"] as string);\n\n if (allOssConfigs.region && allOssConfigs.accessKeyId) {\n config.ossConfig.default = OssConfigSchema.parse(allOssConfigs);\n } else {\n Object.entries(allOssConfigs).forEach(([name, cfg]) => {\n config.ossConfig[name.toLowerCase()] = OssConfigSchema.parse(cfg);\n });\n }\n config.configSources.ossConfig = \"cli\";\n } else if (process.env.OSS_CONFIG_DEFAULT) {\n const ossConfig = JSON.parse(process.env.OSS_CONFIG_DEFAULT)\n config.ossConfig.default = OssConfigSchema.parse(ossConfig);\n config.configSources.ossConfig = \"env\";\n }\n\n // 检查其他命名的OSS配置\n Object.entries(process.env).forEach(([key, value]) => {\n if (key.startsWith(\"OSS_CONFIG_\") && key !== \"OSS_CONFIG_DEFAULT\" && value) {\n try {\n const configName = key.replace(\"OSS_CONFIG_\", \"\").toLowerCase();\n const ossConfig = JSON.parse(value);\n config.ossConfig[configName] = OssConfigSchema.parse(ossConfig);\n } catch (error) {\n console.error(`解析环境变量${key}失败:`, error);\n }\n }\n });\n\n // 验证配置\n if (Object.keys(config.ossConfig).length === 0) {\n console.warn(\"未找到有效的OSS配置。服务器将启动,但上传功能将不可用。\");\n }\n\n // 打印配置信息(非stdio模式下)\n if (!isStdioMode) {\n console.log(\"\\n配置信息:\");\n console.log(`- 端口: ${config.port} (来源: ${config.configSources.port})`);\n\n if (Object.keys(config.ossConfig).length > 0) {\n console.log(\"- OSS配置:\");\n Object.entries(config.ossConfig).forEach(([name, cfg]) => {\n console.log(` - ${name}:`);\n console.log(` Region: ${cfg.region}`);\n console.log(` Endpoint: ${cfg.endpoint}`);\n console.log(` Bucket: ${cfg.bucket}`);\n console.log(` AccessKeyId: ${maskSecret(cfg.accessKeyId)}`);\n console.log(` AccessKeySecret: ${maskSecret(cfg.accessKeySecret)}`);\n });\n } else {\n console.log(\"- OSS配置: 未找到\");\n }\n console.log(); // 空行,增加可读性\n }\n\n return config;\n}\n\n// 获取所有OSS配置\nexport function getAllOssConfigs(): Record<string, OssConfig> {\n const { ossConfig } = getServerConfig(true);\n return ossConfig;\n}\n\n// 获取特定名称的OSS配置\nexport function getOssConfig(name: string = 'default'): OssConfig | null {\n const configs = getAllOssConfigs();\n const normalizedName = name.toLowerCase();\n return configs[normalizedName] || null;\n}\n\n// 获取可用的OSS配置名称列表\nexport function getAvailableOssConfigNames(): string[] {\n return Object.keys(getAllOssConfigs());\n}\n"],"mappings":";;;AAOA,SAAS,4BAA4B;;;ACPrC,SAAS,iBAAiB;AAC1B,SAAS,KAAAA,UAAS;;;ACDlB,OAAO,SAAS;AAChB,OAAO,QAAQ;AACf,OAAO,UAAU;;;ACFjB,SAAS,cAAc;AACvB,OAAO,WAAW;AAClB,SAAS,eAAe;AACxB,SAAS,SAAS;AAElB,OAAO;AAGA,IAAM,kBAAkB,EAAE,OAAO;AAAA,EACtC,QAAQ,EAAE,OAAO;AAAA,EACjB,aAAa,EAAE,OAAO;AAAA,EACtB,iBAAiB,EAAE,OAAO;AAAA,EAC1B,QAAQ,EAAE,OAAO;AAAA,EACjB,UAAU,EAAE,OAAO;AACrB,CAAC;AAgBD,SAAS,WAAW,QAAwB;AAC1C,MAAI,OAAO,UAAU,EAAG,QAAO;AAC/B,SAAO,GAAG,OAAO,UAAU,GAAG,CAAC,CAAC,OAAO,OAAO,MAAM,EAAE,CAAC;AACzD;AAGO,SAAS,gBAAgB,cAAuB,OAAqB;AAE1E,QAAM,OAAO,MAAM,QAAQ,QAAQ,IAAI,CAAC,EACrC,QAAQ;AAAA,IACP,cAAc;AAAA,MACZ,MAAM;AAAA,MACN,aAAa;AAAA,IACf;AAAA,IACA,MAAM;AAAA,MACJ,MAAM;AAAA,MACN,aAAa;AAAA,MACb,SAAS;AAAA,IACX;AAAA,EACF,CAAC,EACA,KAAK,EACL,QAAQ,OAAO,EACf,UAAU;AAEb,QAAMC,UAAuB;AAAA,IAC3B,MAAM;AAAA,IACN,WAAW,CAAC;AAAA,IACZ,eAAe;AAAA,MACb,MAAM;AAAA,MACN,WAAW;AAAA,IACb;AAAA,EACF;AAGA,MAAI,KAAK,MAAM;AACb,IAAAA,QAAO,OAAO,KAAK;AACnB,IAAAA,QAAO,cAAc,OAAO;AAAA,EAC9B,WAAW,QAAQ,IAAI,MAAM;AAC3B,IAAAA,QAAO,OAAO,SAAS,QAAQ,IAAI,MAAM,EAAE;AAC3C,IAAAA,QAAO,cAAc,OAAO;AAAA,EAC9B;AAGA,MAAI,KAAK,YAAY,GAAG;AACtB,UAAM,gBAAgB,KAAK,MAAM,KAAK,YAAY,CAAW;AAE5D,QAAI,cAAc,UAAU,cAAc,aAAa;AACrD,MAAAA,QAAO,UAAU,UAAU,gBAAgB,MAAM,aAAa;AAAA,IAChE,OAAO;AACL,aAAO,QAAQ,aAAa,EAAE,QAAQ,CAAC,CAAC,MAAM,GAAG,MAAM;AACrD,QAAAA,QAAO,UAAU,KAAK,YAAY,CAAC,IAAI,gBAAgB,MAAM,GAAG;AAAA,MAClE,CAAC;AAAA,IACH;AACA,IAAAA,QAAO,cAAc,YAAY;AAAA,EACpC,WAAW,QAAQ,IAAI,oBAAoB;AACzC,UAAM,YAAY,KAAK,MAAM,QAAQ,IAAI,kBAAkB;AAC3D,IAAAA,QAAO,UAAU,UAAU,gBAAgB,MAAM,SAAS;AAC1D,IAAAA,QAAO,cAAc,YAAY;AAAA,EACnC;AAGA,SAAO,QAAQ,QAAQ,GAAG,EAAE,QAAQ,CAAC,CAAC,KAAK,KAAK,MAAM;AACpD,QAAI,IAAI,WAAW,aAAa,KAAK,QAAQ,wBAAwB,OAAO;AAC1E,UAAI;AACF,cAAM,aAAa,IAAI,QAAQ,eAAe,EAAE,EAAE,YAAY;AAC9D,cAAM,YAAY,KAAK,MAAM,KAAK;AAClC,QAAAA,QAAO,UAAU,UAAU,IAAI,gBAAgB,MAAM,SAAS;AAAA,MAChE,SAAS,OAAO;AACd,gBAAQ,MAAM,uCAAS,GAAG,iBAAO,KAAK;AAAA,MACxC;AAAA,IACF;AAAA,EACF,CAAC;AAGD,MAAI,OAAO,KAAKA,QAAO,SAAS,EAAE,WAAW,GAAG;AAC9C,YAAQ,KAAK,iKAA+B;AAAA,EAC9C;AAGA,MAAI,CAAC,aAAa;AAChB,YAAQ,IAAI,6BAAS;AACrB,YAAQ,IAAI,mBAASA,QAAO,IAAI,mBAASA,QAAO,cAAc,IAAI,GAAG;AAErE,QAAI,OAAO,KAAKA,QAAO,SAAS,EAAE,SAAS,GAAG;AAC5C,cAAQ,IAAI,oBAAU;AACtB,aAAO,QAAQA,QAAO,SAAS,EAAE,QAAQ,CAAC,CAAC,MAAM,GAAG,MAAM;AACxD,gBAAQ,IAAI,OAAO,IAAI,GAAG;AAC1B,gBAAQ,IAAI,eAAe,IAAI,MAAM,EAAE;AACvC,gBAAQ,IAAI,iBAAiB,IAAI,QAAQ,EAAE;AAC3C,gBAAQ,IAAI,eAAe,IAAI,MAAM,EAAE;AACvC,gBAAQ,IAAI,oBAAoB,WAAW,IAAI,WAAW,CAAC,EAAE;AAC7D,gBAAQ,IAAI,wBAAwB,WAAW,IAAI,eAAe,CAAC,EAAE;AAAA,MACvE,CAAC;AAAA,IACH,OAAO;AACL,cAAQ,IAAI,uCAAc;AAAA,IAC5B;AACA,YAAQ,IAAI;AAAA,EACd;AAEA,SAAOA;AACT;AAGO,SAAS,mBAA8C;AAC5D,QAAM,EAAE,UAAU,IAAI,gBAAgB,IAAI;AAC1C,SAAO;AACT;AAGO,SAAS,aAAa,OAAe,WAA6B;AACvE,QAAM,UAAU,iBAAiB;AACjC,QAAM,iBAAiB,KAAK,YAAY;AACxC,SAAO,QAAQ,cAAc,KAAK;AACpC;;;AD3IA,SAAS,KAAAC,UAAS;AAGX,IAAM,yBAAyBA,GAAE,OAAO;AAAA,EAC7C,UAAUA,GAAE,OAAO;AAAA,EACnB,WAAWA,GAAE,OAAO,EAAE,SAAS;AAAA,EAC/B,UAAUA,GAAE,OAAO,EAAE,SAAS;AAAA,EAC9B,YAAYA,GAAE,OAAO,EAAE,SAAS;AAClC,CAAC;AAMM,IAAM,qBAAqBA,GAAE,OAAO;AAAA,EACzC,SAASA,GAAE,QAAQ;AAAA,EACnB,KAAKA,GAAE,OAAO,EAAE,SAAS;AAAA,EACzB,OAAOA,GAAE,OAAO,EAAE,SAAS;AAAA,EAC3B,eAAeA,GAAE,OAAO,EAAE,SAAS;AACrC,CAAC;AAgBM,IAAM,aAAN,MAAiB;AAAA,EACd,UAA4B,oBAAI,IAAI;AAAA;AAAA;AAAA;AAAA;AAAA,EAM5C,aAAkC;AAChC,UAAM,UAA+B,CAAC;AACtC,UAAM,aAAa,iBAAiB;AAEpC,eAAW,CAAC,IAAIC,OAAM,KAAK,OAAO,QAAQ,UAAU,GAAG;AACrD,cAAQ,KAAK;AAAA,QACX;AAAA,QACA,MAAM,GAAG,GAAG,OAAO,CAAC,EAAE,YAAY,CAAC,GAAG,GAAG,MAAM,CAAC,CAAC;AAAA,QACjD,GAAGA;AAAA,MACL,CAAC;AAAA,IACH;AAEA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOQ,UAAU,aAAqB,WAAuB;AAE5D,QAAI,KAAK,QAAQ,IAAI,UAAU,GAAG;AAChC,aAAO,KAAK,QAAQ,IAAI,UAAU;AAAA,IACpC;AAGA,UAAMA,UAAS,aAAa,UAAU;AACtC,QAAI,CAACA,SAAQ;AACX,aAAO;AAAA,IACT;AAEA,QAAI;AACF,YAAM,SAAS,IAAI,IAAI;AAAA,QACrB,QAAQA,QAAO;AAAA,QACf,aAAaA,QAAO;AAAA,QACpB,iBAAiBA,QAAO;AAAA,QACxB,QAAQA,QAAO;AAAA,QACf,UAAUA,QAAO;AAAA,MACnB,CAAC;AAGD,WAAK,QAAQ,IAAI,YAAY,MAAM;AACnC,aAAO;AAAA,IACT,SAAS,OAAO;AACd,cAAQ,MAAM,mCAAmC,UAAU,KAAK,KAAK;AACrE,aAAO;AAAA,IACT;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,WAAW,QAAiD;AAEhE,UAAM,cAAc,uBAAuB,MAAM,MAAM;AACvD,UAAM,EAAE,UAAU,YAAY,IAAI,UAAU,aAAa,UAAU,IAAI;AAEvE,QAAI;AAEF,UAAI,CAAC,GAAG,WAAW,QAAQ,GAAG;AAC5B,eAAO,mBAAmB,MAAM;AAAA,UAC9B,SAAS;AAAA,UACT,OAAO,mBAAmB,QAAQ;AAAA,UAClC,eAAe;AAAA,QACjB,CAAC;AAAA,MACH;AAGA,YAAM,SAAS,KAAK,UAAU,UAAU;AACxC,UAAI,CAAC,QAAQ;AACX,eAAO,mBAAmB,MAAM;AAAA,UAC9B,SAAS;AAAA,UACT,OAAO,6BAA6B,UAAU;AAAA,UAC9C,eAAe;AAAA,QACjB,CAAC;AAAA,MACH;AAGA,YAAM,iBAAiB,YAAY,KAAK,SAAS,QAAQ;AAGzD,UAAI,UAAU;AACd,UAAI,WAAW;AAEb,cAAM,gBAAgB,UAAU,QAAQ,cAAc,EAAE;AACxD,kBAAU,gBAAgB,GAAG,aAAa,IAAI,cAAc,KAAK;AAAA,MACnE;AAGA,YAAM,SAAS,MAAM,OAAO,IAAI,SAAS,QAAQ;AAEjD,aAAO,mBAAmB,MAAM;AAAA,QAC9B,SAAS;AAAA,QACT,KAAK,OAAO;AAAA,QACZ,eAAe;AAAA,MACjB,CAAC;AAAA,IACH,SAAS,OAAO;AACd,aAAO,mBAAmB,MAAM;AAAA,QAC9B,SAAS;AAAA,QACT,OAAO,kBAAmB,MAAgB,OAAO;AAAA,QACjD,eAAe;AAAA,MACjB,CAAC;AAAA,IACH;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,MAAM,WAAW,QAAgB,QAAgB,aAAqB,WAA0D;AAC9H,QAAI;AACF,YAAM,SAAS,KAAK,UAAU,UAAU;AACxC,UAAI,CAAC,QAAQ;AACX,eAAO,EAAE,SAAS,OAAO,OAAO,6BAA6B,UAAU,GAAG;AAAA,MAC5E;AAGA,YAAM,mBAAmB,OAAO,QAAQ,QAAQ,EAAE;AAClD,YAAM,mBAAmB,OAAO,QAAQ,QAAQ,EAAE;AAGlD,UAAI;AACF,cAAM,OAAO,KAAK,gBAAgB;AAAA,MACpC,SAAS,IAAI;AACX,eAAO,EAAE,SAAS,OAAO,OAAO,yCAAW,gBAAgB,GAAG;AAAA,MAChE;AAGA,UAAI;AACF,cAAM,OAAO,KAAK,gBAAgB;AAElC,YAAI,qBAAqB,kBAAkB;AACzC,iBAAO,EAAE,SAAS,OAAO,OAAO,+CAAY,gBAAgB,GAAG;AAAA,QACjE;AAAA,MACF,SAAS,IAAI;AAAA,MAEb;AAGA,YAAM,OAAO,KAAK,kBAAkB,gBAAgB;AAGpD,YAAM,OAAO,OAAO,gBAAgB;AAEpC,aAAO,EAAE,SAAS,KAAK;AAAA,IACzB,SAAS,OAAO;AACd,aAAO,EAAE,SAAS,OAAO,OAAO,mCAAW,MAAgB,OAAO,GAAG;AAAA,IACvE;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,MAAM,UACJ,YAAoB,IACpB,aAAqB,WACrB,SACkH;AAClH,QAAI;AACF,YAAM,SAAS,KAAK,UAAU,UAAU;AACxC,UAAI,CAAC,QAAQ;AACX,eAAO,EAAE,SAAS,OAAO,OAAO,6BAA6B,UAAU,GAAG;AAAA,MAC5E;AAGA,YAAM,gBAAgB,UAAU,QAAQ,cAAc,EAAE;AACxD,YAAM,SAAS,gBAAgB,GAAG,aAAa,MAAM;AAGrD,YAAM,SAAS,MAAM,OAAO,KAAK;AAAA,QAC/B;AAAA,QACA,WAAW;AAAA,QACX,YAAY;AAAA,MACd,GAAG,CAAC,CAAC;AAEL,YAAM,QAAmE,CAAC;AAG1E,UAAI,OAAO,SAAS;AAClB,mBAAW,OAAO,OAAO,SAAS;AAEhC,cAAI,IAAI,KAAK,SAAS,GAAG,EAAG;AAG5B,gBAAM,WAAW,IAAI,KAAK,QAAQ,QAAQ,EAAE;AAC5C,cAAI,CAAC,SAAU;AAGf,cAAI,SAAS;AACX,kBAAM,QAAQ,IAAI;AAAA,cAChB,MAAM,QACH,QAAQ,OAAO,KAAK,EACpB,QAAQ,OAAO,IAAI,EACnB,QAAQ,OAAO,GAAG,IAAI;AAAA,cACzB;AAAA,YACF;AACA,gBAAI,CAAC,MAAM,KAAK,QAAQ,EAAG;AAAA,UAC7B;AAEA,gBAAM,KAAK;AAAA,YACT,MAAM;AAAA,YACN,MAAM,IAAI;AAAA,YACV,cAAc,IAAI,KAAK,IAAI,YAAY;AAAA,UACzC,CAAC;AAAA,QACH;AAAA,MACF;AAGA,YAAM,KAAK,CAAC,GAAG,MAAM,EAAE,KAAK,cAAc,EAAE,IAAI,CAAC;AAEjD,aAAO,EAAE,SAAS,MAAM,MAAM;AAAA,IAChC,SAAS,OAAO;AACd,aAAO,EAAE,SAAS,OAAO,OAAO,yCAAY,MAAgB,OAAO,GAAG;AAAA,IACxE;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,MAAM,iBACJ,OACA,YAAoB,IACpB,aAAqB,WACmE;AACxF,UAAM,UAAyF,CAAC;AAGhG,UAAM,gBAAgB,UAAU,QAAQ,cAAc,EAAE;AACxD,UAAM,YAAY,gBAAgB,GAAG,aAAa,MAAM;AAExD,eAAW,QAAQ,OAAO;AACxB,YAAM,SAAS,GAAG,SAAS,GAAG,KAAK,OAAO;AAC1C,YAAM,SAAS,GAAG,SAAS,GAAG,KAAK,OAAO;AAE1C,YAAM,SAAS,MAAM,KAAK,WAAW,QAAQ,QAAQ,UAAU;AAC/D,cAAQ,KAAK;AAAA,QACX,SAAS,KAAK;AAAA,QACd,SAAS,KAAK;AAAA,QACd,SAAS,OAAO;AAAA,QAChB,OAAO,OAAO;AAAA,MAChB,CAAC;AAAA,IACH;AAEA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAM,WAAW,KAAa,aAAqB,WAA0D;AAC3G,QAAI;AACF,YAAM,SAAS,KAAK,UAAU,UAAU;AACxC,UAAI,CAAC,QAAQ;AACX,eAAO,EAAE,SAAS,OAAO,OAAO,6BAA6B,UAAU,GAAG;AAAA,MAC5E;AAGA,YAAM,gBAAgB,IAAI,QAAQ,QAAQ,EAAE;AAG5C,UAAI;AACF,cAAM,OAAO,KAAK,aAAa;AAAA,MACjC,SAAS,IAAI;AACX,eAAO,EAAE,SAAS,OAAO,OAAO,mCAAU,aAAa,GAAG;AAAA,MAC5D;AAGA,YAAM,OAAO,OAAO,aAAa;AAEjC,aAAO,EAAE,SAAS,KAAK;AAAA,IACzB,SAAS,OAAO;AACd,aAAO,EAAE,SAAS,OAAO,OAAO,6BAAU,MAAgB,OAAO,GAAG;AAAA,IACtE;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,MAAM,iBACJ,WACA,YAAoB,IACpB,aAAqB,WACmD;AACxE,UAAM,UAAyE,CAAC;AAGhF,UAAM,gBAAgB,UAAU,QAAQ,cAAc,EAAE;AACxD,UAAM,YAAY,gBAAgB,GAAG,aAAa,MAAM;AAExD,eAAW,YAAY,WAAW;AAChC,YAAM,MAAM,GAAG,SAAS,GAAG,QAAQ;AACnC,YAAM,SAAS,MAAM,KAAK,WAAW,KAAK,UAAU;AACpD,cAAQ,KAAK;AAAA,QACX;AAAA,QACA,SAAS,OAAO;AAAA,QAChB,OAAO,OAAO;AAAA,MAChB,CAAC;AAAA,IACH;AAEA,WAAO;AAAA,EACT;AACF;AAGO,IAAM,aAAa,IAAI,WAAW;;;ADhXzC,OAAO,aAAoC;AAC3C,SAAS,0BAA0B;AAGnC,OAAOC,SAAQ;AACf,OAAOC,WAAU;AACjB,OAAO,WAAW;AAClB,OAAO,UAAU;AAEV,IAAM,SAAS;AAAA,EACpB,KAAK,IAAI,SAAgB;AACvB,YAAQ,IAAI,GAAG,IAAI;AAAA,EACrB;AAAA,EACA,OAAO,IAAI,SAAgB;AACzB,YAAQ,MAAM,GAAG,IAAI;AAAA,EACvB;AACF;AAEO,IAAM,eAAN,MAAmB;AAAA,EACP;AAAA,EACT,eAA0C;AAAA,EAElD,cAAc;AACZ,SAAK,SAAS,IAAI;AAAA,MAChB;AAAA,QACE,MAAM;AAAA,QACN,SAAS;AAAA,MACX;AAAA;AAAA,MAEA;AAAA,QACE,cAAc;AAAA,UACZ,OAAO,EAAE,aAAa,KAAK;AAAA,UAC3B,WAAW,EAAE,aAAa,KAAK;AAAA,UAC/B,SAAS,EAAE,aAAa,KAAK;AAAA,UAC7B,SAAS,CAAC;AAAA,QACZ;AAAA,MACF;AAAA,IACF;AAEA,SAAK,cAAc;AAAA,EACrB;AAAA,EAEQ,gBAAsB;AAE5B,UAAM,UAAU,WAAW,WAAW;AACtC,UAAM,cAAc,QAAQ,IAAI,CAAAC,YAAUA,QAAO,EAAE;AAGnD,SAAK,OAAO;AAAA,MACV;AAAA,MACA;AAAA,MACA;AAAA,QACE,UAAUC,GAAE,OAAO,EAAE,SAAS,8DAAY;AAAA,QAC1C,WAAWA,GAAE,OAAO,EAAE,SAAS,EAAE,SAAS,6EAAiB;AAAA,QAC3D,UAAUA,GAAE,OAAO,EAAE,SAAS,EAAE,SAAS,0HAAsB;AAAA,QAC/D,YAAYA,GAAE,OAAO,EAAE,SAAS,EAAE,SAAS,uHAAkC,YAAY,KAAK,IAAI,KAAK,QAAG,EAAE;AAAA,MAC9G;AAAA,MACA,OAAO,EAAE,UAAU,WAAW,UAAU,WAAW,MAAM;AACvD,YAAI;AACF,iBAAO,IAAI,6BAAS,QAAQ,WAAM,aAAa,oBAAK,EAAE;AAEtD,cAAI,CAAC,UAAU;AACb,kBAAM,IAAI,MAAM,kDAAU;AAAA,UAC5B;AAGA,cAAI,CAACH,IAAG,WAAW,QAAQ,GAAG;AAC5B,kBAAM,IAAI,MAAM,mCAAU,QAAQ,EAAE;AAAA,UACtC;AAGA,gBAAM,SAAS,MAAM,WAAW,WAAW;AAAA,YACzC;AAAA,YACA;AAAA,YACA;AAAA,YACA;AAAA,UACF,CAAC;AAED,cAAI,OAAO,SAAS;AAClB,mBAAO,IAAI,6BAAS,OAAO,GAAG,EAAE;AAChC,mBAAO;AAAA,cACL,SAAS,CAAC;AAAA,gBACR,MAAM;AAAA,gBACN,MAAM;AAAA,sBAAiBC,MAAK,SAAS,QAAQ,CAAC;AAAA,4BAAW,aAAa,oBAAK;AAAA,OAAU,OAAO,GAAG;AAAA,4BAAW,OAAO,aAAa;AAAA,cAChI,CAAC;AAAA,YACH;AAAA,UACF,OAAO;AACL,mBAAO,MAAM,6BAAS,OAAO,KAAK,EAAE;AACpC,mBAAO;AAAA,cACL,SAAS;AAAA,cACT,SAAS,CAAC;AAAA,gBACR,MAAM;AAAA,gBACN,MAAM,6BAAS,OAAO,KAAK;AAAA,cAC7B,CAAC;AAAA,YACH;AAAA,UACF;AAAA,QACF,SAAS,OAAO;AACd,iBAAO,MAAM,+CAAY,KAAK;AAC9B,iBAAO;AAAA,YACL,SAAS;AAAA,YACT,SAAS,CAAC;AAAA,cACR,MAAM;AAAA,cACN,MAAM,6BAAS,KAAK;AAAA,YACtB,CAAC;AAAA,UACH;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAGA,SAAK,OAAO;AAAA,MACV;AAAA,MACA;AAAA,MACA,CAAC;AAAA,MACD,YAAY;AACV,YAAI;AACF,gBAAMG,WAAU,WAAW,WAAW;AACtC,gBAAMC,eAAcD,SAAQ,IAAI,CAAAF,YAAUA,QAAO,EAAE;AAEnD,cAAIG,aAAY,WAAW,GAAG;AAC5B,mBAAO;AAAA,cACL,SAAS,CAAC;AAAA,gBACR,MAAM;AAAA,gBACN,MAAM;AAAA,cACR,CAAC;AAAA,YACH;AAAA,UACF;AAEA,iBAAO;AAAA,YACL,SAAS,CAAC;AAAA,cACR,MAAM;AAAA,cACN,MAAM;AAAA,EAAcA,aAAY,IAAI,UAAQ,KAAK,IAAI,EAAE,EAAE,KAAK,IAAI,CAAC;AAAA,YACrE,CAAC;AAAA,UACH;AAAA,QACF,SAAS,OAAO;AACd,iBAAO,MAAM,8DAAiB,KAAK;AACnC,iBAAO;AAAA,YACL,SAAS;AAAA,YACT,SAAS,CAAC;AAAA,cACR,MAAM;AAAA,cACN,MAAM,qDAAa,KAAK;AAAA,YAC1B,CAAC;AAAA,UACH;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAGA,SAAK,OAAO;AAAA,MACV;AAAA,MACA;AAAA,MACA;AAAA,QACE,WAAWF,GAAE,OAAO,EAAE,SAAS,mIAAyC;AAAA,QACxE,aAAaA,GAAE,MAAMA,GAAE,OAAO;AAAA,UAC5B,SAASA,GAAE,OAAO,EAAE,SAAS,0BAAM;AAAA,UACnC,SAASA,GAAE,OAAO,EAAE,SAAS,0BAAM;AAAA,QACrC,CAAC,CAAC,EAAE,SAAS,gIAAuB;AAAA,QACpC,YAAYA,GAAE,OAAO,EAAE,SAAS,EAAE,SAAS,qGAA+B,YAAY,KAAK,IAAI,KAAK,QAAG,EAAE;AAAA,QACzG,QAAQA,GAAE,QAAQ,EAAE,SAAS,EAAE,SAAS,qMAA0C;AAAA,MACpF;AAAA,MACA,OAAO,EAAE,WAAW,aAAa,aAAa,WAAW,SAAS,MAAM,MAAM;AAC5E,YAAI;AACF,iBAAO,IAAI,mDAAgB,SAAS,wBAAS,YAAY,MAAM,kBAAQ,UAAU,8BAAU,MAAM,EAAE;AAEnG,cAAI;AAEJ,cAAI,QAAQ;AAEV,sBAAU,YAAY,IAAI,WAAS;AAAA,cACjC,SAAS,KAAK;AAAA,cACd,SAAS,KAAK;AAAA,cACd,SAAS;AAAA,YACX,EAAE;AAAA,UACJ,OAAO;AAEL,sBAAU,MAAM,WAAW,iBAAiB,aAAa,WAAW,UAAU;AAAA,UAChF;AAEA,gBAAM,eAAe,QAAQ,OAAO,OAAK,EAAE,OAAO,EAAE;AACpD,gBAAM,YAAY,QAAQ,OAAO,OAAK,CAAC,EAAE,OAAO,EAAE;AAElD,cAAI,aAAa,SAAS;AAAA;AAAA,IAAkC;AAAA;AAAA;AAC5D,wBAAc,iBAAO,UAAU;AAAA;AAC/B,wBAAc,iBAAO,aAAa,oBAAK;AAAA;AACvC,wBAAc,iBAAO,YAAY,0BAAW,SAAS;AAAA;AAAA;AAErD,cAAI,QAAQ,SAAS,GAAG;AACtB,0BAAc;AACd,uBAAW,KAAK,SAAS;AACvB,kBAAI,EAAE,SAAS;AACb,8BAAc,UAAK,EAAE,OAAO,WAAM,EAAE,OAAO;AAAA;AAAA,cAC7C,OAAO;AACL,8BAAc,UAAK,EAAE,OAAO,WAAM,EAAE,OAAO,KAAK,EAAE,KAAK;AAAA;AAAA,cACzD;AAAA,YACF;AAAA,UACF;AAEA,iBAAO;AAAA,YACL,SAAS,CAAC;AAAA,cACR,MAAM;AAAA,cACN,MAAM;AAAA,YACR,CAAC;AAAA,UACH;AAAA,QACF,SAAS,OAAO;AACd,iBAAO,MAAM,kDAAe,KAAK;AACjC,iBAAO;AAAA,YACL,SAAS;AAAA,YACT,SAAS,CAAC;AAAA,cACR,MAAM;AAAA,cACN,MAAM,kDAAe,KAAK;AAAA,YAC5B,CAAC;AAAA,UACH;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAGA,SAAK,OAAO;AAAA,MACV;AAAA,MACA;AAAA,MACA;AAAA,QACE,WAAWA,GAAE,OAAO,EAAE,SAAS,kDAAU;AAAA,QACzC,SAASA,GAAE,OAAO,EAAE,SAAS,EAAE,SAAS,wGAAkC;AAAA,MAC5E;AAAA,MACA,OAAO,EAAE,WAAW,QAAQ,MAAM;AAChC,YAAI;AACF,iBAAO,IAAI,yCAAW,SAAS,mBAAS,WAAW,QAAG,EAAE;AAGxD,cAAI,CAACH,IAAG,WAAW,SAAS,GAAG;AAC7B,kBAAM,IAAI,MAAM,mCAAU,SAAS,EAAE;AAAA,UACvC;AAEA,gBAAM,OAAOA,IAAG,SAAS,SAAS;AAClC,cAAI,CAAC,KAAK,YAAY,GAAG;AACvB,kBAAM,IAAI,MAAM,yCAAW,SAAS,EAAE;AAAA,UACxC;AAEA,cAAI,QAAQA,IAAG,YAAY,SAAS;AAGpC,kBAAQ,MAAM,OAAO,OAAK,CAAC,EAAE,WAAW,GAAG,CAAC;AAG5C,cAAI,SAAS;AACX,kBAAM,QAAQ,IAAI;AAAA,cAChB,MAAM,QACH,QAAQ,OAAO,KAAK,EACpB,QAAQ,OAAO,IAAI,EACnB,QAAQ,OAAO,GAAG,IAAI;AAAA,cACzB;AAAA,YACF;AACA,oBAAQ,MAAM,OAAO,OAAK,MAAM,KAAK,CAAC,CAAC;AAAA,UACzC;AAGA,gBAAM,YAAY,MAAM,IAAI,OAAK;AAC/B,kBAAM,WAAWC,MAAK,KAAK,WAAW,CAAC;AACvC,kBAAM,WAAWD,IAAG,SAAS,QAAQ;AACrC,mBAAO;AAAA,cACL,MAAM;AAAA,cACN,aAAa,SAAS,YAAY;AAAA,cAClC,MAAM,SAAS;AAAA,YACjB;AAAA,UACF,CAAC;AAGD,oBAAU,KAAK,CAAC,GAAG,MAAM;AACvB,gBAAI,EAAE,gBAAgB,EAAE,aAAa;AACnC,qBAAO,EAAE,cAAc,KAAK;AAAA,YAC9B;AACA,mBAAO,EAAE,KAAK,cAAc,EAAE,IAAI;AAAA,UACpC,CAAC;AAED,cAAI,UAAU,WAAW,GAAG;AAC1B,mBAAO;AAAA,cACL,SAAS,CAAC;AAAA,gBACR,MAAM;AAAA,gBACN,MAAM,gBAAM,SAAS,gEAAc,UAAU,mBAAS,OAAO,MAAM,EAAE;AAAA,cACvE,CAAC;AAAA,YACH;AAAA,UACF;AAEA,cAAI,aAAa,iBAAO,SAAS;AAAA;AACjC,cAAI,SAAS;AACX,0BAAc,iBAAO,OAAO;AAAA;AAAA,UAC9B;AACA,wBAAc,UAAK,UAAU,MAAM;AAAA;AAAA;AAEnC,qBAAW,KAAK,WAAW;AACzB,gBAAI,EAAE,aAAa;AACjB,4BAAc,aAAM,EAAE,IAAI;AAAA;AAAA,YAC5B,OAAO;AACL,oBAAM,UAAU,EAAE,OAAO,OACrB,GAAG,EAAE,IAAI,MACT,EAAE,OAAO,OAAO,OACd,IAAI,EAAE,OAAO,MAAM,QAAQ,CAAC,CAAC,OAC7B,IAAI,EAAE,OAAO,OAAO,MAAM,QAAQ,CAAC,CAAC;AAC1C,4BAAc,aAAM,EAAE,IAAI,KAAK,OAAO;AAAA;AAAA,YACxC;AAAA,UACF;AAEA,iBAAO;AAAA,YACL,SAAS,CAAC;AAAA,cACR,MAAM;AAAA,cACN,MAAM;AAAA,YACR,CAAC;AAAA,UACH;AAAA,QACF,SAAS,OAAO;AACd,iBAAO,MAAM,qDAAa,KAAK;AAC/B,iBAAO;AAAA,YACL,SAAS;AAAA,YACT,SAAS,CAAC;AAAA,cACR,MAAM;AAAA,cACN,MAAM,yCAAW,KAAK;AAAA,YACxB,CAAC;AAAA,UACH;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAGA,SAAK,OAAO;AAAA,MACV;AAAA,MACA;AAAA,MACA;AAAA,QACE,WAAWG,GAAE,OAAO,EAAE,SAAS,mIAAyC;AAAA,QACxE,SAASA,GAAE,OAAO,EAAE,SAAS,EAAE,SAAS,wGAAkC;AAAA,QAC1E,YAAYA,GAAE,OAAO,EAAE,SAAS,EAAE,SAAS,qGAA+B,YAAY,KAAK,IAAI,KAAK,QAAG,EAAE;AAAA,MAC3G;AAAA,MACA,OAAO,EAAE,WAAW,SAAS,aAAa,UAAU,MAAM;AACxD,YAAI;AACF,iBAAO,IAAI,4CAAc,aAAa,oBAAK,mBAAS,WAAW,QAAG,mBAAS,UAAU,EAAE;AAEvF,gBAAM,SAAS,MAAM,WAAW,UAAU,WAAW,YAAY,OAAO;AAExE,cAAI,CAAC,OAAO,SAAS;AACnB,mBAAO;AAAA,cACL,SAAS;AAAA,cACT,SAAS,CAAC;AAAA,gBACR,MAAM;AAAA,gBACN,MAAM,4CAAc,OAAO,KAAK;AAAA,cAClC,CAAC;AAAA,YACH;AAAA,UACF;AAEA,gBAAM,QAAQ,OAAO,SAAS,CAAC;AAE/B,cAAI,MAAM,WAAW,GAAG;AACtB,mBAAO;AAAA,cACL,SAAS,CAAC;AAAA,gBACR,MAAM;AAAA,gBACN,MAAM,mBAAS,aAAa,oBAAK,gEAAc,UAAU,mBAAS,OAAO,MAAM,EAAE;AAAA,gBAAS,UAAU;AAAA,cACtG,CAAC;AAAA,YACH;AAAA,UACF;AAEA,gBAAM,UAAU,CAAC,SAAiB,OAAO,OACrC,GAAG,IAAI,MACP,OAAO,OAAO,OACZ,IAAI,OAAO,MAAM,QAAQ,CAAC,CAAC,OAC3B,IAAI,OAAO,OAAO,MAAM,QAAQ,CAAC,CAAC;AAExC,cAAI,aAAa,oBAAU,aAAa,oBAAK;AAAA;AAC7C,wBAAc,iBAAO,UAAU;AAAA;AAC/B,cAAI,SAAS;AACX,0BAAc,iBAAO,OAAO;AAAA;AAAA,UAC9B;AACA,wBAAc,UAAK,MAAM,MAAM;AAAA;AAAA;AAE/B,qBAAW,KAAK,OAAO;AACrB,0BAAc,aAAM,EAAE,IAAI,KAAK,QAAQ,EAAE,IAAI,CAAC;AAAA;AAAA,UAChD;AAEA,iBAAO;AAAA,YACL,SAAS,CAAC;AAAA,cACR,MAAM;AAAA,cACN,MAAM;AAAA,YACR,CAAC;AAAA,UACH;AAAA,QACF,SAAS,OAAO;AACd,iBAAO,MAAM,wDAAgB,KAAK;AAClC,iBAAO;AAAA,YACL,SAAS;AAAA,YACT,SAAS,CAAC;AAAA,cACR,MAAM;AAAA,cACN,MAAM,4CAAc,KAAK;AAAA,YAC3B,CAAC;AAAA,UACH;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAIA,UAAM,uBAAuB,QAAQ,IAAI,2BAA2B;AAEpE,SAAK,OAAO;AAAA,MACV;AAAA,MACA;AAAA;AAAA;AAAA,4BAGE,uBAAuB,8BAAU,wIAAkE;AAAA;AAAA;AAAA,MAGrG;AAAA,QACE,WAAWA,GAAE,OAAO,EAAE,SAAS,mIAAyC;AAAA,QACxE,WAAWA,GAAE,MAAMA,GAAE,OAAO,CAAC,EAAE,SAAS,EAAE,SAAS,qGAA0B;AAAA,QAC7E,SAASA,GAAE,OAAO,EAAE,SAAS,EAAE,SAAS,qIAAgD;AAAA,QACxF,YAAYA,GAAE,OAAO,EAAE,SAAS,EAAE,SAAS,qGAA+B,YAAY,KAAK,IAAI,KAAK,QAAG,EAAE;AAAA,QACzG,QAAQA,GAAE,QAAQ,EAAE,SAAS,EAAE,SAAS,2MAA2C;AAAA,MACrF;AAAA,MACA,OAAO,EAAE,WAAW,WAAW,SAAS,aAAa,WAAW,SAAS,MAAM,MAAM;AACnF,YAAI;AAEF,cAAI,CAAC,sBAAsB;AACzB,mBAAO;AAAA,cACL,SAAS;AAAA,cACT,SAAS,CAAC;AAAA,gBACR,MAAM;AAAA,gBACN,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,cAiBR,CAAC;AAAA,YACH;AAAA,UACF;AAEA,iBAAO,IAAI,6CAAe,SAAS,kBAAQ,UAAU,8BAAU,MAAM,EAAE;AAGvE,cAAI,CAAC,aAAa,CAAC,SAAS;AAC1B,mBAAO;AAAA,cACL,SAAS;AAAA,cACT,SAAS,CAAC;AAAA,gBACR,MAAM;AAAA,gBACN,MAAM;AAAA,cACR,CAAC;AAAA,YACH;AAAA,UACF;AAEA,cAAI,gBAA0B,CAAC;AAE/B,cAAI,aAAa,UAAU,SAAS,GAAG;AAErC,4BAAgB;AAAA,UAClB,WAAW,SAAS;AAElB,kBAAM,aAAa,MAAM,WAAW,UAAU,WAAW,YAAY,OAAO;AAC5E,gBAAI,CAAC,WAAW,SAAS;AACvB,qBAAO;AAAA,gBACL,SAAS;AAAA,gBACT,SAAS,CAAC;AAAA,kBACR,MAAM;AAAA,kBACN,MAAM,yCAAW,WAAW,KAAK;AAAA,gBACnC,CAAC;AAAA,cACH;AAAA,YACF;AACA,6BAAiB,WAAW,SAAS,CAAC,GAAG,IAAI,OAAK,EAAE,IAAI;AAAA,UAC1D;AAEA,cAAI,cAAc,WAAW,GAAG;AAC9B,mBAAO;AAAA,cACL,SAAS,CAAC;AAAA,gBACR,MAAM;AAAA,gBACN,MAAM,yDAAY,UAAU,mBAAS,OAAO,MAAM,EAAE;AAAA,gBAAS,aAAa,oBAAK;AAAA,gBAAS,UAAU;AAAA,cACpG,CAAC;AAAA,YACH;AAAA,UACF;AAEA,cAAI,QAAQ;AAEV,gBAAIG,cAAa;AAAA;AAAA;AACjB,YAAAA,eAAc,iBAAO,UAAU;AAAA;AAC/B,YAAAA,eAAc,iBAAO,aAAa,oBAAK;AAAA;AACvC,gBAAI,SAAS;AACX,cAAAA,eAAc,6BAAS,OAAO;AAAA;AAAA,YAChC;AACA,YAAAA,eAAc,6BAAS,cAAc,MAAM;AAAA;AAAA;AAC3C,YAAAA,eAAc;AAAA;AACd,uBAAW,YAAY,eAAe;AACpC,cAAAA,eAAc,mBAAO,QAAQ;AAAA;AAAA,YAC/B;AACA,YAAAA,eAAc;AAAA;AAEd,mBAAO;AAAA,cACL,SAAS,CAAC;AAAA,gBACR,MAAM;AAAA,gBACN,MAAMA;AAAA,cACR,CAAC;AAAA,YACH;AAAA,UACF;AAGA,gBAAM,UAAU,MAAM,WAAW,iBAAiB,eAAe,WAAW,UAAU;AAEtF,gBAAM,eAAe,QAAQ,OAAO,OAAK,EAAE,OAAO,EAAE;AACpD,gBAAM,YAAY,QAAQ,OAAO,OAAK,CAAC,EAAE,OAAO,EAAE;AAElD,cAAI,aAAa;AAAA;AAAA;AACjB,wBAAc,iBAAO,UAAU;AAAA;AAC/B,wBAAc,iBAAO,aAAa,oBAAK;AAAA;AACvC,wBAAc,iBAAO,YAAY,0BAAW,SAAS;AAAA;AAAA;AAErD,cAAI,QAAQ,SAAS,GAAG;AACtB,0BAAc;AACd,uBAAW,KAAK,SAAS;AACvB,kBAAI,EAAE,SAAS;AACb,8BAAc,UAAK,EAAE,QAAQ;AAAA;AAAA,cAC/B,OAAO;AACL,8BAAc,UAAK,EAAE,QAAQ,KAAK,EAAE,KAAK;AAAA;AAAA,cAC3C;AAAA,YACF;AAAA,UACF;AAEA,iBAAO;AAAA,YACL,SAAS,CAAC;AAAA,cACR,MAAM;AAAA,cACN,MAAM;AAAA,YACR,CAAC;AAAA,UACH;AAAA,QACF,SAAS,OAAO;AACd,iBAAO,MAAM,4CAAc,KAAK;AAChC,iBAAO;AAAA,YACL,SAAS;AAAA,YACT,SAAS,CAAC;AAAA,cACR,MAAM;AAAA,cACN,MAAM,4CAAc,KAAK;AAAA,YAC3B,CAAC;AAAA,UACH;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAGA,SAAK,OAAO;AAAA,MACV;AAAA,MACA;AAAA,MACA;AAAA,QACE,KAAKH,GAAE,OAAO,EAAE,SAAS,0CAAY;AAAA,QACrC,WAAWA,GAAE,OAAO,EAAE,SAAS,oEAAa;AAAA,QAC5C,UAAUA,GAAE,OAAO,EAAE,SAAS,EAAE,SAAS,uGAAuB;AAAA,MAClE;AAAA,MACA,OAAO,EAAE,KAAK,WAAW,SAAS,MAAM;AACtC,YAAI;AACF,iBAAO,IAAI,6BAAS,GAAG,WAAM,SAAS,EAAE;AAGxC,cAAI,CAACH,IAAG,WAAW,SAAS,GAAG;AAC7B,YAAAA,IAAG,UAAU,WAAW,EAAE,WAAW,KAAK,CAAC;AAC3C,mBAAO,IAAI,6BAAS,SAAS,EAAE;AAAA,UACjC;AAEA,gBAAM,OAAOA,IAAG,SAAS,SAAS;AAClC,cAAI,CAAC,KAAK,YAAY,GAAG;AACvB,kBAAM,IAAI,MAAM,yCAAW,SAAS,EAAE;AAAA,UACxC;AAGA,cAAI,gBAAgB;AACpB,cAAI,CAAC,eAAe;AAClB,kBAAM,SAAS,IAAI,IAAI,GAAG;AAC1B,4BAAgBC,MAAK,SAAS,OAAO,QAAQ;AAE7C,gBAAI,CAAC,iBAAiB,kBAAkB,KAAK;AAC3C,8BAAgB,YAAY,KAAK,IAAI,CAAC;AAAA,YACxC;AAAA,UACF;AAEA,gBAAM,WAAWA,MAAK,KAAK,WAAW,aAAa;AAGnD,cAAID,IAAG,WAAW,QAAQ,GAAG;AAC3B,kBAAM,IAAI,MAAM,mCAAU,QAAQ,EAAE;AAAA,UACtC;AAGA,gBAAM,IAAI,QAAc,CAACO,UAAS,WAAW;AAC3C,kBAAM,SAAS,IAAI,IAAI,GAAG;AAC1B,kBAAM,WAAW,OAAO,aAAa,WAAW,QAAQ;AAExD,kBAAM,UAAU,SAAS,IAAI,KAAK,CAAC,aAAa;AAE9C,kBAAI,SAAS,eAAe,OAAO,SAAS,eAAe,KAAK;AAC9D,sBAAM,cAAc,SAAS,QAAQ;AACrC,oBAAI,aAAa;AACf,yBAAO,IAAI,6BAAS,WAAW,EAAE;AACjC,wBAAM,mBAAmB,YAAY,WAAW,QAAQ,IAAI,QAAQ;AACpE,mCAAiB,IAAI,aAAa,CAAC,qBAAqB;AACtD,wBAAI,iBAAiB,eAAe,KAAK;AACvC,6BAAO,IAAI,MAAM,0DAAkB,iBAAiB,UAAU,EAAE,CAAC;AACjE;AAAA,oBACF;AACA,0BAAMC,cAAaR,IAAG,kBAAkB,QAAQ;AAChD,qCAAiB,KAAKQ,WAAU;AAChC,oBAAAA,YAAW,GAAG,UAAU,MAAM;AAC5B,sBAAAA,YAAW,MAAM;AACjB,sBAAAD,SAAQ;AAAA,oBACV,CAAC;AACD,oBAAAC,YAAW,GAAG,SAAS,CAAC,QAAQ;AAC9B,sBAAAR,IAAG,OAAO,UAAU,MAAM;AAAA,sBAAC,CAAC;AAC5B,6BAAO,GAAG;AAAA,oBACZ,CAAC;AAAA,kBACH,CAAC,EAAE,GAAG,SAAS,MAAM;AACrB;AAAA,gBACF;AAAA,cACF;AAEA,kBAAI,SAAS,eAAe,KAAK;AAC/B,uBAAO,IAAI,MAAM,0DAAkB,SAAS,UAAU,EAAE,CAAC;AACzD;AAAA,cACF;AAEA,oBAAM,aAAaA,IAAG,kBAAkB,QAAQ;AAChD,uBAAS,KAAK,UAAU;AAExB,yBAAW,GAAG,UAAU,MAAM;AAC5B,2BAAW,MAAM;AACjB,gBAAAO,SAAQ;AAAA,cACV,CAAC;AAED,yBAAW,GAAG,SAAS,CAAC,QAAQ;AAC9B,gBAAAP,IAAG,OAAO,UAAU,MAAM;AAAA,gBAAC,CAAC;AAC5B,uBAAO,GAAG;AAAA,cACZ,CAAC;AAAA,YACH,CAAC;AAED,oBAAQ,GAAG,SAAS,CAAC,QAAQ;AAC3B,cAAAA,IAAG,OAAO,UAAU,MAAM;AAAA,cAAC,CAAC;AAC5B,qBAAO,GAAG;AAAA,YACZ,CAAC;AAED,oBAAQ,WAAW,KAAO,MAAM;AAC9B,sBAAQ,QAAQ;AAChB,cAAAA,IAAG,OAAO,UAAU,MAAM;AAAA,cAAC,CAAC;AAC5B,qBAAO,IAAI,MAAM,8CAAW,CAAC;AAAA,YAC/B,CAAC;AAAA,UACH,CAAC;AAGD,gBAAM,iBAAiBA,IAAG,SAAS,QAAQ;AAC3C,gBAAM,UAAU,eAAe,OAAO,OAClC,GAAG,eAAe,IAAI,MACtB,eAAe,OAAO,OAAO,OAC3B,IAAI,eAAe,OAAO,MAAM,QAAQ,CAAC,CAAC,OAC1C,IAAI,eAAe,OAAO,OAAO,MAAM,QAAQ,CAAC,CAAC;AAEvD,iBAAO;AAAA,YACL,SAAS,CAAC;AAAA,cACR,MAAM;AAAA,cACN,MAAM;AAAA,aAAkB,GAAG;AAAA,4BAAW,QAAQ;AAAA,4BAAW,OAAO;AAAA,YAClE,CAAC;AAAA,UACH;AAAA,QACF,SAAS,OAAO;AACd,iBAAO,MAAM,yCAAW,KAAK;AAC7B,iBAAO;AAAA,YACL,SAAS;AAAA,YACT,SAAS,CAAC;AAAA,cACR,MAAM;AAAA,cACN,MAAM,6BAAS,KAAK;AAAA,YACtB,CAAC;AAAA,UACH;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAGA,SAAK,OAAO;AAAA,MACV;AAAA,MACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MAyBA;AAAA,QACE,QAAQG,GAAE,MAAMA,GAAE,OAAO,CAAC,EAAE,SAAS,sNAAsD;AAAA,MAC7F;AAAA,MACA,OAAO,EAAE,OAAO,MAAM;AACpB,YAAI;AAEF,gBAAM,cAA2E,CAAC;AAClF,gBAAM,SAAmB,CAAC;AAE1B,qBAAW,WAAW,QAAQ;AAC5B,gBAAI,CAACH,IAAG,WAAW,OAAO,GAAG;AAC3B,qBAAO,KAAK,mCAAU,OAAO,EAAE;AAC/B;AAAA,YACF;AACA,kBAAM,OAAOA,IAAG,SAAS,OAAO;AAChC,gBAAI,KAAK,OAAO,IAAI,OAAO,MAAM;AAC/B,qBAAO,KAAK,8CAAgB,OAAO,EAAE;AACrC;AAAA,YACF;AACA,kBAAM,MAAMC,MAAK,QAAQ,OAAO,EAAE,YAAY,EAAE,MAAM,CAAC;AACvD,gBAAI,CAAC,CAAC,OAAO,OAAO,QAAQ,QAAQ,OAAO,OAAO,MAAM,EAAE,SAAS,GAAG,GAAG;AACvE,qBAAO,KAAK,yCAAW,OAAO,EAAE;AAChC;AAAA,YACF;AACA,wBAAY,KAAK;AAAA,cACf,MAAM;AAAA,cACN,MAAMA,MAAK,SAAS,SAASA,MAAK,QAAQ,OAAO,CAAC;AAAA,cAClD,KAAK,QAAQ,QAAQ,SAAS;AAAA,cAC9B,MAAM,KAAK;AAAA,YACb,CAAC;AAAA,UACH;AAEA,cAAI,YAAY,WAAW,GAAG;AAC5B,mBAAO;AAAA,cACL,SAAS;AAAA,cACT,SAAS,CAAC;AAAA,gBACR,MAAM;AAAA,gBACN,MAAM;AAAA,EAAgB,OAAO,KAAK,IAAI,CAAC;AAAA,cACzC,CAAC;AAAA,YACH;AAAA,UACF;AAGA,gBAAM,YAAY;AAAA,YAChB,iBAAiB;AAAA,cACf,aAAa;AAAA,YACf;AAAA,YACA,gBAAgB;AAAA,cACd,UAAU;AAAA,cACV,QAAQ;AAAA,cACR,SAAS;AAAA,gBACP,EAAE,OAAO,0BAAgB,aAAa,iIAAuC;AAAA,gBAC7E,EAAE,OAAO,WAAW,aAAa,iGAA2B;AAAA,cAC9D;AAAA,YACF;AAAA,YACA,gBAAgB;AAAA,cACd,UAAU;AAAA,cACV,QAAQ;AAAA,cACR,SAAS;AAAA,gBACP,EAAE,OAAO,kCAAS,aAAa,yDAAY;AAAA,gBAC3C,EAAE,OAAO,2BAAY,aAAa,qEAAmB;AAAA,gBACrD,EAAE,OAAO,2BAAY,aAAa,iEAAyB;AAAA,gBAC3D,EAAE,OAAO,0BAAW,aAAa,gEAAwB;AAAA,cAC3D;AAAA,YACF;AAAA,YACA,wBAAwB;AAAA,cACtB,UAAU;AAAA,cACV,QAAQ;AAAA,cACR,SAAS;AAAA,gBACP,EAAE,OAAO,kCAAS,aAAa,8DAAiB;AAAA,gBAChD,EAAE,OAAO,kCAAS,aAAa,gFAAoB;AAAA,cACrD;AAAA,cACA,WAAW;AAAA,YACb;AAAA,UACF;AAEA,gBAAM,UAAU,CAAC,SAAiB,OAAO,OACrC,GAAG,IAAI,MACP,OAAO,OAAO,OACZ,IAAI,OAAO,MAAM,QAAQ,CAAC,CAAC,OAC3B,IAAI,OAAO,OAAO,MAAM,QAAQ,CAAC,CAAC;AAExC,cAAI,aAAa;AAAA;AAAA;AACjB,wBAAc,wCAAe,YAAY,MAAM;AAAA;AAC/C,qBAAW,OAAO,aAAa;AAC7B,0BAAc,KAAKA,MAAK,SAAS,IAAI,IAAI,CAAC,KAAK,QAAQ,IAAI,IAAI,CAAC;AAAA;AAAA,UAClE;AAEA,cAAI,OAAO,SAAS,GAAG;AACrB,0BAAc;AAAA;AAAA;AACd,uBAAW,OAAO,QAAQ;AACxB,4BAAc,KAAK,GAAG;AAAA;AAAA,YACxB;AAAA,UACF;AAEA,wBAAc;AAAA;AAAA;AAAA;AACd,wBAAc;AAAA;AACd,wBAAc;AAAA;AACd,wBAAc;AAAA;AACd,wBAAc;AAAA;AACd,wBAAc;AAAA;AACd,wBAAc;AAAA;AACd,wBAAc;AAAA;AAEd,iBAAO;AAAA,YACL,SAAS;AAAA,cACP;AAAA,gBACE,MAAM;AAAA,gBACN,MAAM;AAAA,cACR;AAAA,cACA;AAAA,gBACE,MAAM;AAAA,gBACN,MAAM;AAAA;AAAA;AAAA;AAAA,EAAwC,KAAK,UAAU,WAAW,MAAM,CAAC,CAAC;AAAA;AAAA,cAClF;AAAA,YACF;AAAA,UACF;AAAA,QACF,SAAS,OAAO;AACd,iBAAO,MAAM,qDAAa,KAAK;AAC/B,iBAAO;AAAA,YACL,SAAS;AAAA,YACT,SAAS,CAAC;AAAA,cACR,MAAM;AAAA,cACN,MAAM,6BAAS,KAAK;AAAA,YACtB,CAAC;AAAA,UACH;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAGA,SAAK,OAAO;AAAA,MACV;AAAA,MACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MAwBA;AAAA,QACE,QAAQE,GAAE,MAAMA,GAAE,OAAO,CAAC,EAAE,SAAS,0EAAc;AAAA,QACnD,QAAQA,GAAE,KAAK,CAAC,WAAW,SAAS,CAAC,EAAE,SAAS,mFAAkB;AAAA,QAClE,cAAcA,GAAE,KAAK,CAAC,OAAO,QAAQ,MAAM,CAAC,EAAE,SAAS,EAAE,SAAS,sIAAkC;AAAA,QACpG,gBAAgBA,GAAE,QAAQ,EAAE,SAAS,EAAE,SAAS,6HAAyB;AAAA,QACzE,cAAcA,GAAE,OAAO,EAAE,SAAS,EAAE,SAAS,6FAAuB;AAAA,QACpE,YAAYA,GAAE,OAAO,EAAE,SAAS,EAAE,SAAS,qGAA+B,YAAY,KAAK,IAAI,KAAK,QAAG,EAAE;AAAA,MAC3G;AAAA,MACA,OAAO,EAAE,QAAQ,QAAQ,cAAc,iBAAiB,OAAO,cAAc,aAAa,UAAU,MAAM;AACxG,YAAI;AACF,iBAAO,IAAI,0CAAY,MAAM,kBAAQ,gBAAgB,oBAAK,wBAAS,OAAO,MAAM,EAAE;AAGlF,gBAAM,cAA2E,CAAC;AAClF,gBAAM,SAAmB,CAAC;AAE1B,qBAAW,WAAW,QAAQ;AAC5B,gBAAI,CAACH,IAAG,WAAW,OAAO,GAAG;AAC3B,qBAAO,KAAK,mCAAU,OAAO,EAAE;AAC/B;AAAA,YACF;AACA,kBAAM,OAAOA,IAAG,SAAS,OAAO;AAChC,gBAAI,KAAK,OAAO,IAAI,OAAO,MAAM;AAC/B,qBAAO,KAAK,8CAAgB,OAAO,EAAE;AACrC;AAAA,YACF;AACA,kBAAM,MAAMC,MAAK,QAAQ,OAAO,EAAE,YAAY,EAAE,MAAM,CAAC;AACvD,gBAAI,CAAC,CAAC,OAAO,OAAO,QAAQ,QAAQ,OAAO,OAAO,MAAM,EAAE,SAAS,GAAG,GAAG;AACvE,qBAAO,KAAK,yCAAW,OAAO,EAAE;AAChC;AAAA,YACF;AACA,wBAAY,KAAK;AAAA,cACf,MAAM;AAAA,cACN,MAAMA,MAAK,SAAS,SAASA,MAAK,QAAQ,OAAO,CAAC;AAAA,cAClD,KAAK,QAAQ,QAAQ,SAAS;AAAA,cAC9B,MAAM,KAAK;AAAA,YACb,CAAC;AAAA,UACH;AAEA,cAAI,YAAY,WAAW,GAAG;AAC5B,mBAAO;AAAA,cACL,SAAS;AAAA,cACT,SAAS,CAAC;AAAA,gBACR,MAAM;AAAA,gBACN,MAAM;AAAA,EAAgB,OAAO,KAAK,IAAI,CAAC;AAAA,cACzC,CAAC;AAAA,YACH;AAAA,UACF;AAGA,gBAAM,qBAAqB,WAAW,YAAY,SAAU,gBAAgB;AAC5E,gBAAM,YAAY,WAAW,YAAY,IAAI;AAC7C,gBAAM,UAAgC,CAAC;AAEvC,mBAAS,IAAI,GAAG,IAAI,YAAY,QAAQ,KAAK,WAAW;AACtD,oBAAQ,KAAK,YAAY,MAAM,GAAG,IAAI,SAAS,CAAC;AAAA,UAClD;AAGA,cAAI,eAAe;AAAA;AAAA;AACnB,0BAAgB,qBAAW,WAAW,YAAY,mCAAmC,+CAA+C;AAAA;AACpI,0BAAgB,iCAAa,sBAAsB,gCAAO;AAAA;AAC1D,0BAAgB,iCAAa,YAAY,MAAM;AAAA;AAC/C,0BAAgB,2BAAY,QAAQ,MAAM,8BAAU,SAAS;AAAA;AAAA;AAE7D,cAAI,OAAO,SAAS,GAAG;AACrB,4BAAgB;AAAA;AAChB,uBAAW,OAAO,QAAQ;AACxB,8BAAgB,KAAK,GAAG;AAAA;AAAA,YAC1B;AACA,4BAAgB;AAAA;AAAA,UAClB;AAEA,0BAAgB;AAAA;AAAA;AAChB,0BAAgB;AAAA;AAAA;AAEhB,cAAI,WAAW,WAAW;AACxB,4BAAgB,KAAK,4BAA4B,SAAS,kBAAkB;AAAA,UAC9E,OAAO;AACL,4BAAgB,KAAK,4BAA4B,OAAO;AAAA,UAC1D;AAGA,0BAAgB;AAAA;AAAA;AAAA;AAChB,0BAAgB;AAAA;AAAA;AAEhB,qBAAW,OAAO,aAAa;AAC7B,kBAAM,SAAS,sBAAsB,IAAI;AACzC,kBAAM,iBAAiB,WAAW,IAAI;AACtC,kBAAM,cAAc,GAAG,IAAI,IAAI,IAAI,MAAM;AACzC,kBAAM,eAAeA,MAAK,KAAKA,MAAK,QAAQ,IAAI,IAAI,GAAG,GAAG,IAAI,IAAI,eAAe,MAAM,EAAE;AAEzF,4BAAgB,KAAKA,MAAK,SAAS,IAAI,IAAI,CAAC;AAAA;AAC5C,4BAAgB,oDAAiB,YAAY;AAAA;AAE7C,gBAAI,cAAc;AAChB,kBAAI,gBAAgB;AAClB,gCAAgB,+CAAgC,YAAY,OAAO,YAAY,OAAO,WAAW,OAAO,UAAU;AAAA;AAClH,oBAAI,gBAAgB;AAClB,kCAAgB,sEAAyB,YAAY,IAAIA,MAAK,SAAS,IAAI,IAAI,CAAC;AAAA;AAAA,gBAClF;AAAA,cACF,OAAO;AACL,gCAAgB,2DAAkC,YAAY,OAAO,YAAY,OAAOA,MAAK,SAAS,IAAI,IAAI,CAAC,OAAO,UAAU;AAAA;AAAA,cAClI;AAAA,YACF;AACA,4BAAgB;AAAA;AAAA,UAClB;AAGA,gBAAM,aAAa;AAAA,YACjB;AAAA,YACA,cAAc;AAAA,YACd,gBAAgB,qBAAqB,iBAAiB;AAAA,YACtD;AAAA,YACA;AAAA,YACA,aAAa,YAAY;AAAA,YACzB,SAAS,QAAQ;AAAA,YACjB;AAAA,YACA,QAAQ,YAAY,IAAI,UAAQ;AAAA,cAC9B,cAAc,IAAI;AAAA,cAClB,cAAcA,MAAK,SAAS,IAAI,IAAI;AAAA,cACpC,aAAa,IAAI;AAAA,cACjB,cAAc,IAAI;AAAA,cAClB,QAAQ,sBAAsB,IAAI;AAAA,cAClC,iBAAiB,sBAAsB,IAAI,SAAS,IAAI;AAAA,YAC1D,EAAE;AAAA,UACJ;AAEA,iBAAO;AAAA,YACL,SAAS;AAAA,cACP;AAAA,gBACE,MAAM;AAAA,gBACN,MAAM;AAAA,cACR;AAAA,cACA;AAAA,gBACE,MAAM;AAAA,gBACN,MAAM;AAAA;AAAA;AAAA;AAAA,EAA0C,KAAK,UAAU,YAAY,MAAM,CAAC,CAAC;AAAA;AAAA,cACrF;AAAA,YACF;AAAA,UACF;AAAA,QACF,SAAS,OAAO;AACd,iBAAO,MAAM,qDAAa,KAAK;AAC/B,iBAAO;AAAA,YACL,SAAS;AAAA,YACT,SAAS,CAAC;AAAA,cACR,MAAM;AAAA,cACN,MAAM,qDAAa,KAAK;AAAA,YAC1B,CAAC;AAAA,UACH;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA;AAAA,EAGQ,4BAA4B,SAAwE,cAAqC;AAC/I,QAAI,eAAe;AAGnB,oBAAgB;AAAA;AAAA;AAChB,oBAAgB;AAAA;AAChB,oBAAgB;AAAA;AAChB,oBAAgB;AAAA;AAChB,oBAAgB;AAAA;AAAA;AAEhB,UAAM,wBAAwB,gBAAgB,iBAAiB;AAE/D,aAAS,IAAI,GAAG,IAAI,QAAQ,QAAQ,KAAK;AACvC,YAAM,QAAQ,QAAQ,CAAC;AACvB,sBAAgB,qBAAW,IAAI,CAAC,IAAI,QAAQ,MAAM;AAAA;AAAA;AAClD,sBAAgB,qBAAW,MAAM,IAAI,SAAOA,MAAK,SAAS,IAAI,IAAI,CAAC,EAAE,KAAK,IAAI,CAAC;AAAA;AAAA;AAE/E,UAAI,UAAU;AACd,sBAAgB,GAAG,SAAS;AAAA;AAC5B,sBAAgB,GAAG,SAAS;AAAA;AAG5B,UAAI,uBAAuB;AACzB,wBAAgB,GAAG,SAAS;AAAA;AAC5B,wBAAgB;AAAA;AAChB,wBAAgB;AAAA;AAChB,wBAAgB;AAAA;AAChB,wBAAgB,GAAG,SAAS;AAAA;AAC5B,wBAAgB,kCAAc,aAAc,YAAY,CAAC;AAAA;AAAA,MAC3D;AAEA,sBAAgB,GAAG,SAAS;AAAA;AAC5B,sBAAgB;AAAA;AAChB,sBAAgB;AAAA;AAChB,sBAAgB,GAAG,SAAS;AAAA;AAC5B,iBAAW,OAAO,OAAO;AACvB,wBAAgB,UAAU,IAAI,IAAI;AAAA;AAAA,MACpC;AACA,sBAAgB,GAAG,SAAS;AAAA;AAC5B,sBAAgB,GAAG,SAAS;AAAA;AAE5B,UAAI,IAAI,QAAQ,SAAS,GAAG;AAC1B,wBAAgB,GAAG,SAAS;AAAA;AAAA,MAC9B;AACA,sBAAgB;AAAA;AAAA,IAClB;AAEA,WAAO;AAAA,EACT;AAAA;AAAA,EAGQ,4BAA4B,SAAgF;AAClH,QAAI,eAAe;AAGnB,oBAAgB;AAAA;AAAA;AAChB,oBAAgB;AAAA;AAChB,oBAAgB;AAAA;AAChB,oBAAgB;AAAA;AAChB,oBAAgB;AAAA;AAAA;AAEhB,aAAS,IAAI,GAAG,IAAI,QAAQ,QAAQ,KAAK;AACvC,YAAM,QAAQ,QAAQ,CAAC;AACvB,sBAAgB,qBAAW,IAAI,CAAC,IAAI,QAAQ,MAAM;AAAA;AAAA;AAClD,sBAAgB,qBAAW,MAAM,IAAI,SAAOA,MAAK,SAAS,IAAI,IAAI,CAAC,EAAE,KAAK,IAAI,CAAC;AAAA;AAAA;AAE/E,UAAI,UAAU;AACd,sBAAgB,GAAG,SAAS;AAAA;AAC5B,sBAAgB,GAAG,SAAS;AAAA;AAC5B,sBAAgB,GAAG,SAAS;AAAA;AAC5B,sBAAgB;AAAA;AAChB,sBAAgB;AAAA;AAChB,sBAAgB,GAAG,SAAS;AAAA;AAC5B,iBAAW,OAAO,OAAO;AACvB,wBAAgB,UAAU,IAAI,IAAI;AAAA;AAAA,MACpC;AACA,sBAAgB,GAAG,SAAS;AAAA;AAC5B,sBAAgB,GAAG,SAAS;AAAA;AAE5B,UAAI,IAAI,QAAQ,SAAS,GAAG;AAC1B,wBAAgB,GAAG,SAAS;AAAA;AAAA,MAC9B;AACA,sBAAgB;AAAA;AAAA,IAClB;AAEA,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,QAAQ,WAAqC;AACjD,QAAI;AACF,YAAM,KAAK,OAAO,QAAQ,SAAS;AAEnC,aAAO,MAAM,IAAI,SAAgB;AAC/B,YAAI;AACF,eAAK,OAAO,OAAO,mBAAmB;AAAA,YACpC,OAAO;AAAA,YACP,MAAM;AAAA,UACR,CAAC;AAAA,QACH,SAAS,OAAO;AACd,kBAAQ,IAAI,GAAG,IAAI;AAAA,QACrB;AAAA,MACF;AAEA,aAAO,QAAQ,IAAI,SAAgB;AACjC,YAAI;AACF,eAAK,OAAO,OAAO,mBAAmB;AAAA,YACpC,OAAO;AAAA,YACP,MAAM;AAAA,UACR,CAAC;AAAA,QACH,SAAS,OAAO;AACd,kBAAQ,MAAM,GAAG,IAAI;AAAA,QACvB;AAAA,MACF;AAEA,aAAO,IAAI,uFAAsB;AAAA,IACnC,SAAS,OAAO;AACd,cAAQ,MAAM,qDAAa,KAAK;AAAA,IAClC;AAAA,EACF;AAAA,EAEA,MAAM,gBAAgB,MAA6B;AACjD,UAAM,MAAM,QAAQ;AAGpB,QAAI,IAAI,QAAQ,CAAC,KAAc,QAAkB;AAE/C,WAAK,eAAe,IAAI;AAAA,QACtB;AAAA,QACA;AAAA,MACF;AAEA,UAAI;AAEF,aAAK,OAAO,QAAQ,KAAK,YAAY,EAClC,MAAM,CAAC,QAAQ;AACd,kBAAQ,MAAM,wDAAgB,GAAG;AAAA,QACnC,CAAC;AAGH,YAAI,GAAG,SAAS,MAAM;AACpB,kBAAQ,IAAI,+CAAY;AACxB,eAAK,eAAe;AAAA,QACtB,CAAC;AAAA,MACH,SAAS,OAAO;AACd,gBAAQ,MAAM,kDAAe,KAAK;AAElC,YAAI,CAAC,IAAI,eAAe;AACtB,cAAI,OAAO,GAAG,EAAE,IAAI;AAAA,QACtB;AAAA,MACF;AAAA,IACF,CAAC;AAGD,QAAI,KAAK,aAAa,OAAO,KAAc,QAAkB;AAC3D,UAAI,CAAC,KAAK,cAAc;AACtB,gBAAQ,IAAI,yFAAmB;AAC/B,YAAI,OAAO,GAAG,EAAE,KAAK;AAAA,UACnB,OAAO;AAAA,UACP,SAAS;AAAA,QACX,CAAC;AACD;AAAA,MACF;AAEA,UAAI;AACF,cAAM,KAAK,aAAa;AAAA,UACtB;AAAA,UACA;AAAA,QACF;AAAA,MACF,SAAS,OAAO;AACd,gBAAQ,MAAM,+CAAY,KAAK;AAC/B,YAAI,CAAC,IAAI,eAAe;AACtB,cAAI,OAAO,GAAG,EAAE,KAAK;AAAA,YACnB,OAAO;AAAA,YACP,SAAS,OAAO,KAAK;AAAA,UACvB,CAAC;AAAA,QACH;AAAA,MACF;AAAA,IACF,CAAC;AAGD,QAAI,OAAO,MAAM,MAAM;AACrB,aAAO,MAAM,QAAQ;AACrB,aAAO,QAAQ,QAAQ;AAEvB,aAAO,IAAI,mDAAgB,IAAI,EAAE;AACjC,aAAO,IAAI,qCAA2B,IAAI,MAAM;AAChD,aAAO,IAAI,8CAA0B,IAAI,WAAW;AAAA,IACtD,CAAC;AAAA,EACH;AACF;;;ADhrCA,SAAS,eAAe;AACxB,SAAS,UAAAQ,eAAc;AAGvBA,QAAO,EAAE,MAAM,QAAQ,QAAQ,IAAI,GAAG,MAAM,EAAE,CAAC;AAE/C,eAAsB,cAA6B;AAEjD,QAAM,cAAc,QAAQ,IAAI,aAAa,SAAS,QAAQ,KAAK,SAAS,SAAS;AAGrF,QAAM,eAAe,gBAAgB,WAAW;AAGhD,QAAM,SAAS,IAAI,aAAa;AAEhC,MAAI,aAAa;AAEf,UAAM,YAAY,IAAI,qBAAqB;AAC3C,UAAM,OAAO,QAAQ,SAAS;AAAA,EAChC,OAAO;AAEL,YAAQ,IAAI,wFAA4B,aAAa,IAAI,KAAK;AAC9D,UAAM,OAAO,gBAAgB,aAAa,IAAI;AAAA,EAChD;AACF;AAGA,YAAY,EAAE,MAAM,CAAC,UAAU;AAC7B,UAAQ,MAAM,+CAAY,KAAK;AAC/B,UAAQ,KAAK,CAAC;AAChB,CAAC;","names":["z","config","z","config","fs","path","config","z","configs","configNames","resultText","resolve","fileStream","config"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/server.ts","../src/services/oss.service.ts","../src/config/oss.config.ts","../src/services/figma.service.ts"],"sourcesContent":["#!/usr/bin/env node\n/**\n * 实现阿里云OSS文件上传功能。\n * - 上传文件到阿里云OSS\n * - 获取可用的OSS配置\n */\n\nimport { StdioServerTransport } from \"@modelcontextprotocol/sdk/server/stdio.js\";\nimport { OssMcpServer } from \"./server.js\";\nimport { getServerConfig } from \"./config/oss.config.js\";\nimport { resolve } from \"path\";\nimport { config } from \"dotenv\";\n\n// 加载当前工作目录中的.env文件\nconfig({ path: resolve(process.cwd(), \".env\") });\n\nexport async function startServer(): Promise<void> {\n // 检查是否在stdio模式下运行\n const isStdioMode = process.env.NODE_ENV === \"cli\" || process.argv.includes(\"--stdio\");\n\n // 获取服务器配置\n const serverConfig = getServerConfig(isStdioMode);\n\n // 创建OSS MCP服务器\n const server = new OssMcpServer();\n\n if (isStdioMode) {\n // 在stdio模式下运行\n const transport = new StdioServerTransport();\n await server.connect(transport);\n } else {\n // 在HTTP模式下运行\n console.log(`初始化OSS MCP服务器,HTTP模式,端口: ${serverConfig.port}...`);\n await server.startHttpServer(serverConfig.port);\n }\n}\n\n// 启动服务器\nstartServer().catch((error) => {\n console.error(\"启动服务器失败:\", error);\n process.exit(1);\n});\n","import { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { z } from \"zod\";\nimport { ossService } from \"./services/oss.service.js\";\nimport { figmaService } from \"./services/figma.service.js\";\nimport { getFigmaToken } from \"./config/oss.config.js\";\nimport express, { Request, Response } from \"express\";\nimport { SSEServerTransport } from \"@modelcontextprotocol/sdk/server/sse.js\";\nimport { IncomingMessage, ServerResponse } from \"http\";\nimport { Transport } from \"@modelcontextprotocol/sdk/shared/transport.js\";\nimport fs from 'fs';\nimport path from 'path';\nimport https from 'https';\nimport http from 'http';\n\nexport const Logger = {\n log: (...args: any[]) => {\n console.log(...args);\n },\n error: (...args: any[]) => {\n console.error(...args);\n }\n};\n\nexport class OssMcpServer {\n private readonly server: McpServer;\n private sseTransport: SSEServerTransport | null = null;\n\n constructor() {\n this.server = new McpServer(\n {\n name: \"@yhy2001/oss-mcp\",\n version: \"1.0.0\",\n },\n // 使用正确格式的capabilities配置\n {\n capabilities: {\n tools: { listChanged: true },\n resources: { listChanged: true },\n prompts: { listChanged: true },\n logging: {}\n }\n }\n );\n\n this.registerTools();\n }\n\n private registerTools(): void {\n // 获取可用的OSS配置\n const configs = ossService.getConfigs();\n const configNames = configs.map(config => config.id);\n\n // 工具:上传文件到OSS\n this.server.tool(\n \"upload_to_oss\",\n \"将文件上传到阿里云OSS\",\n {\n filePath: z.string().describe(\"要上传的本地文件路径\"),\n targetDir: z.string().optional().describe(\"OSS中的目标目录路径(可选)\"),\n fileName: z.string().optional().describe(\"上传后的文件名(可选,默认使用原文件名)\"),\n configName: z.string().optional().describe(`OSS配置名称(可选,默认为'default')。可用配置: ${configNames.join(', ') || '无'}`)\n },\n async ({ filePath, targetDir, fileName, configName }) => {\n try {\n Logger.log(`准备上传: ${filePath} 到 ${targetDir || '根目录'}`);\n\n if (!filePath) {\n throw new Error(\"文件路径是必需的\");\n }\n\n // 检查文件是否存在\n if (!fs.existsSync(filePath)) {\n throw new Error(`文件不存在: ${filePath}`);\n }\n\n // 执行上传\n const result = await ossService.uploadFile({\n filePath,\n targetDir,\n fileName,\n configName\n });\n\n if (result.success) {\n Logger.log(`上传成功: ${result.url}`);\n return {\n content: [{\n type: \"text\",\n text: `文件上传成功!\\n文件名: ${path.basename(filePath)}\\n目标位置: ${targetDir || '根目录'}\\nURL: ${result.url}\\n配置名称: ${result.ossConfigName}`\n }]\n };\n } else {\n Logger.error(`上传失败: ${result.error}`);\n return {\n isError: true,\n content: [{\n type: \"text\",\n text: `上传失败: ${result.error}`\n }]\n };\n }\n } catch (error) {\n Logger.error(`上传过程中出错:`, error);\n return {\n isError: true,\n content: [{\n type: \"text\",\n text: `上传出错: ${error}`\n }]\n };\n }\n }\n );\n\n // 工具:列出可用的OSS配置\n this.server.tool(\n \"list_oss_configs\",\n \"列出可用的阿里云OSS配置\",\n {},\n async () => {\n try {\n const configs = ossService.getConfigs();\n const configNames = configs.map(config => config.id);\n\n if (configNames.length === 0) {\n return {\n content: [{\n type: \"text\",\n text: \"未找到OSS配置。请检查环境变量设置。\"\n }]\n };\n }\n\n return {\n content: [{\n type: \"text\",\n text: `可用的OSS配置:\\n${configNames.map(name => `- ${name}`).join('\\n')}`\n }]\n };\n } catch (error) {\n Logger.error(`获取OSS配置列表时出错:`, error);\n return {\n isError: true,\n content: [{\n type: \"text\",\n text: `获取配置列表失败: ${error}`\n }]\n };\n }\n }\n );\n\n // 工具:批量重命名OSS文件\n this.server.tool(\n \"batch_rename_files\",\n \"批量重命名阿里云OSS文件。通过copy+delete实现。【重要】首次调用必须使用dryRun=true预览,展示给用户确认后,用户同意才能用dryRun=false执行实际重命名。禁止跳过预览直接执行!\",\n {\n directory: z.string().describe(\"OSS中的目录路径(如 'images/icons',根目录传空字符串 '')\"),\n renameRules: z.array(z.object({\n oldName: z.string().describe(\"原文件名\"),\n newName: z.string().describe(\"新文件名\")\n })).describe(\"重命名规则数组,每项包含原文件名和新文件名\"),\n configName: z.string().optional().describe(`OSS配置名称(默认为'default')。可用配置: ${configNames.join(', ') || '无'}`),\n dryRun: z.boolean().optional().describe(\"是否为预览模式(默认false)。为true时只返回将要执行的操作,不实际重命名\")\n },\n async ({ directory, renameRules, configName = 'default', dryRun = false }) => {\n try {\n Logger.log(`OSS批量重命名: 目录=${directory}, 规则数=${renameRules.length}, 配置=${configName}, 预览模式=${dryRun}`);\n\n let results: { oldName: string; newName: string; success: boolean; error?: string }[];\n\n if (dryRun) {\n // 预览模式:只返回将要执行的操作\n results = renameRules.map(rule => ({\n oldName: rule.oldName,\n newName: rule.newName,\n success: true\n }));\n } else {\n // 实际执行OSS重命名\n results = await ossService.batchRenameFiles(renameRules, directory, configName);\n }\n\n const successCount = results.filter(r => r.success).length;\n const failCount = results.filter(r => !r.success).length;\n\n let resultText = dryRun ? `【预览模式】以下是将要执行的OSS文件重命名操作:\\n\\n` : `OSS文件批量重命名完成:\\n\\n`;\n resultText += `配置: ${configName}\\n`;\n resultText += `目录: ${directory || '根目录'}\\n`;\n resultText += `成功: ${successCount} 个, 失败: ${failCount} 个\\n\\n`;\n\n if (results.length > 0) {\n resultText += '详细结果:\\n';\n for (const r of results) {\n if (r.success) {\n resultText += `✅ ${r.oldName} → ${r.newName}\\n`;\n } else {\n resultText += `❌ ${r.oldName} → ${r.newName} (${r.error})\\n`;\n }\n }\n }\n\n return {\n content: [{\n type: \"text\",\n text: resultText\n }]\n };\n } catch (error) {\n Logger.error(`OSS批量重命名出错:`, error);\n return {\n isError: true,\n content: [{\n type: \"text\",\n text: `OSS批量重命名失败: ${error}`\n }]\n };\n }\n }\n );\n\n // 工具:列出本地目录文件\n this.server.tool(\n \"list_directory_files\",\n \"列出本地文件系统中指定目录下的所有文件。注意:此工具仅支持本地路径,如果要列出 OSS 中的文件,请使用 list_oss_files 工具。\",\n {\n directory: z.string().describe(\"要查看的目录路径\"),\n pattern: z.string().optional().describe(\"文件名过滤模式(可选),如 '*.png' 或 'icon_*'\")\n },\n async ({ directory, pattern }) => {\n try {\n Logger.log(`列出目录文件: ${directory}, 过滤: ${pattern || '无'}`);\n\n // 检查目录是否存在\n if (!fs.existsSync(directory)) {\n throw new Error(`目录不存在: ${directory}`);\n }\n\n const stat = fs.statSync(directory);\n if (!stat.isDirectory()) {\n throw new Error(`路径不是目录: ${directory}`);\n }\n\n let files = fs.readdirSync(directory);\n\n // 过滤掉隐藏文件\n files = files.filter(f => !f.startsWith('.'));\n\n // 如果有 pattern,进行简单的通配符匹配\n if (pattern) {\n const regex = new RegExp(\n '^' + pattern\n .replace(/\\./g, '\\\\.')\n .replace(/\\*/g, '.*')\n .replace(/\\?/g, '.') + '$',\n 'i'\n );\n files = files.filter(f => regex.test(f));\n }\n\n // 获取文件信息\n const fileInfos = files.map(f => {\n const filePath = path.join(directory, f);\n const fileStat = fs.statSync(filePath);\n return {\n name: f,\n isDirectory: fileStat.isDirectory(),\n size: fileStat.size\n };\n });\n\n // 排序:目录在前,文件在后,按名称排序\n fileInfos.sort((a, b) => {\n if (a.isDirectory !== b.isDirectory) {\n return a.isDirectory ? -1 : 1;\n }\n return a.name.localeCompare(b.name);\n });\n\n if (fileInfos.length === 0) {\n return {\n content: [{\n type: \"text\",\n text: `目录 ${directory} 下没有找到匹配的文件${pattern ? ` (过滤: ${pattern})` : ''}`\n }]\n };\n }\n\n let resultText = `目录: ${directory}\\n`;\n if (pattern) {\n resultText += `过滤: ${pattern}\\n`;\n }\n resultText += `共 ${fileInfos.length} 个项目:\\n\\n`;\n\n for (const f of fileInfos) {\n if (f.isDirectory) {\n resultText += `📁 ${f.name}/\\n`;\n } else {\n const sizeStr = f.size < 1024\n ? `${f.size}B`\n : f.size < 1024 * 1024\n ? `${(f.size / 1024).toFixed(1)}KB`\n : `${(f.size / 1024 / 1024).toFixed(1)}MB`;\n resultText += `📄 ${f.name} (${sizeStr})\\n`;\n }\n }\n\n return {\n content: [{\n type: \"text\",\n text: resultText\n }]\n };\n } catch (error) {\n Logger.error(`列出目录文件出错:`, error);\n return {\n isError: true,\n content: [{\n type: \"text\",\n text: `列出目录失败: ${error}`\n }]\n };\n }\n }\n );\n\n // 工具:列出OSS目录文件\n this.server.tool(\n \"list_oss_files\",\n \"列出阿里云OSS指定目录下的所有文件。用于查看 OSS 中的文件以便进行重命名或其他操作。注意:如果要列出本地文件,请使用 list_directory_files 工具。\",\n {\n directory: z.string().describe(\"OSS中的目录路径(如 'images/icons',根目录传空字符串 '')\"),\n pattern: z.string().optional().describe(\"文件名过滤模式(可选),如 '*.png' 或 'icon_*'\"),\n configName: z.string().optional().describe(`OSS配置名称(默认为'default')。可用配置: ${configNames.join(', ') || '无'}`)\n },\n async ({ directory, pattern, configName = 'default' }) => {\n try {\n Logger.log(`列出OSS目录文件: ${directory || '根目录'}, 过滤: ${pattern || '无'}, 配置: ${configName}`);\n\n const result = await ossService.listFiles(directory, configName, pattern);\n\n if (!result.success) {\n return {\n isError: true,\n content: [{\n type: \"text\",\n text: `列出OSS文件失败: ${result.error}`\n }]\n };\n }\n\n const files = result.files || [];\n\n if (files.length === 0) {\n return {\n content: [{\n type: \"text\",\n text: `OSS目录 ${directory || '根目录'} 下没有找到匹配的文件${pattern ? ` (过滤: ${pattern})` : ''}\\n配置: ${configName}`\n }]\n };\n }\n\n const sizeStr = (size: number) => size < 1024\n ? `${size}B`\n : size < 1024 * 1024\n ? `${(size / 1024).toFixed(1)}KB`\n : `${(size / 1024 / 1024).toFixed(1)}MB`;\n\n let resultText = `OSS目录: ${directory || '根目录'}\\n`;\n resultText += `配置: ${configName}\\n`;\n if (pattern) {\n resultText += `过滤: ${pattern}\\n`;\n }\n resultText += `共 ${files.length} 个文件:\\n\\n`;\n\n for (const f of files) {\n resultText += `📄 ${f.name} (${sizeStr(f.size)})\\n`;\n }\n\n return {\n content: [{\n type: \"text\",\n text: resultText\n }]\n };\n } catch (error) {\n Logger.error(`列出OSS目录文件出错:`, error);\n return {\n isError: true,\n content: [{\n type: \"text\",\n text: `列出OSS目录失败: ${error}`\n }]\n };\n }\n }\n );\n\n // 工具:删除OSS文件\n // 检查环境变量是否允许删除操作\n const allowDeleteOperation = process.env.ALLOW_DELETE_OPERATION === 'true';\n\n this.server.tool(\n \"delete_oss_files\",\n `删除阿里云OSS中的文件。支持单个删除、批量删除和通配符匹配。\n\n【⚠️ 安全限制】此工具需要配置环境变量 ALLOW_DELETE_OPERATION=true 才能使用。\n当前状态: ${allowDeleteOperation ? '✅ 已启用' : '❌ 未启用(需要在 MCP 配置中添加 \"env\": { \"ALLOW_DELETE_OPERATION\": \"true\" })'}\n\n【重要】首次调用必须使用 dryRun=true 预览,展示给用户确认后,用户同意才能用 dryRun=false 执行实际删除。禁止跳过预览直接执行!`,\n {\n directory: z.string().describe(\"OSS中的目录路径(如 'images/icons',根目录传空字符串 '')\"),\n fileNames: z.array(z.string()).optional().describe(\"要删除的文件名数组(与 pattern 二选一)\"),\n pattern: z.string().optional().describe(\"文件名通配符模式(如 '*.tmp' 或 'test_*'),与 fileNames 二选一\"),\n configName: z.string().optional().describe(`OSS配置名称(默认为'default')。可用配置: ${configNames.join(', ') || '无'}`),\n dryRun: z.boolean().optional().describe(\"是否为预览模式(默认false)。为true时只返回将要删除的文件列表,不实际删除\")\n },\n async ({ directory, fileNames, pattern, configName = 'default', dryRun = false }) => {\n try {\n // 检查是否允许删除操作\n if (!allowDeleteOperation) {\n return {\n isError: true,\n content: [{\n type: \"text\",\n text: `❌ 删除操作被禁止!\n\n要启用删除功能,请在 MCP 配置中添加环境变量:\n\n{\n \"mcpServers\": {\n \"oss-mcp-plus\": {\n \"command\": \"npx\",\n \"args\": [\"oss-mcp-plus\", ...],\n \"env\": {\n \"ALLOW_DELETE_OPERATION\": \"true\"\n }\n }\n }\n}\n\n这是一个安全措施,防止误删除文件。`\n }]\n };\n }\n\n Logger.log(`删除OSS文件: 目录=${directory}, 配置=${configName}, 预览模式=${dryRun}`);\n\n // 必须提供 fileNames 或 pattern 之一\n if (!fileNames && !pattern) {\n return {\n isError: true,\n content: [{\n type: \"text\",\n text: \"请提供 fileNames(文件名数组)或 pattern(通配符模式)之一\"\n }]\n };\n }\n\n let filesToDelete: string[] = [];\n\n if (fileNames && fileNames.length > 0) {\n // 直接使用提供的文件名\n filesToDelete = fileNames;\n } else if (pattern) {\n // 使用通配符匹配文件\n const listResult = await ossService.listFiles(directory, configName, pattern);\n if (!listResult.success) {\n return {\n isError: true,\n content: [{\n type: \"text\",\n text: `列出文件失败: ${listResult.error}`\n }]\n };\n }\n filesToDelete = (listResult.files || []).map(f => f.name);\n }\n\n if (filesToDelete.length === 0) {\n return {\n content: [{\n type: \"text\",\n text: `没有找到匹配的文件${pattern ? ` (模式: ${pattern})` : ''}\\n目录: ${directory || '根目录'}\\n配置: ${configName}`\n }]\n };\n }\n\n if (dryRun) {\n // 预览模式:只返回将要删除的文件列表\n let resultText = `【预览模式】以下是将要删除的文件:\\n\\n`;\n resultText += `配置: ${configName}\\n`;\n resultText += `目录: ${directory || '根目录'}\\n`;\n if (pattern) {\n resultText += `匹配模式: ${pattern}\\n`;\n }\n resultText += `文件数量: ${filesToDelete.length}\\n\\n`;\n resultText += `文件列表:\\n`;\n for (const fileName of filesToDelete) {\n resultText += `🗑️ ${fileName}\\n`;\n }\n resultText += `\\n⚠️ 确认要删除这些文件后,请使用 dryRun=false 执行实际删除。`;\n\n return {\n content: [{\n type: \"text\",\n text: resultText\n }]\n };\n }\n\n // 实际执行删除\n const results = await ossService.batchDeleteFiles(filesToDelete, directory, configName);\n\n const successCount = results.filter(r => r.success).length;\n const failCount = results.filter(r => !r.success).length;\n\n let resultText = `OSS文件删除完成:\\n\\n`;\n resultText += `配置: ${configName}\\n`;\n resultText += `目录: ${directory || '根目录'}\\n`;\n resultText += `成功: ${successCount} 个, 失败: ${failCount} 个\\n\\n`;\n\n if (results.length > 0) {\n resultText += '详细结果:\\n';\n for (const r of results) {\n if (r.success) {\n resultText += `✅ ${r.fileName} 已删除\\n`;\n } else {\n resultText += `❌ ${r.fileName} (${r.error})\\n`;\n }\n }\n }\n\n return {\n content: [{\n type: \"text\",\n text: resultText\n }]\n };\n } catch (error) {\n Logger.error(`删除OSS文件出错:`, error);\n return {\n isError: true,\n content: [{\n type: \"text\",\n text: `删除OSS文件失败: ${error}`\n }]\n };\n }\n }\n );\n\n // 工具:下载文件\n this.server.tool(\n \"download_file\",\n \"从 URL 下载文件到本地目录。支持 HTTP/HTTPS 链接,可自定义保存文件名。\",\n {\n url: z.string().describe(\"要下载的文件 URL\"),\n targetDir: z.string().describe(\"保存文件的本地目录路径\"),\n fileName: z.string().optional().describe(\"保存的文件名(可选,默认从 URL 提取)\")\n },\n async ({ url, targetDir, fileName }) => {\n try {\n Logger.log(`下载文件: ${url} 到 ${targetDir}`);\n\n // 检查目录是否存在,不存在则创建\n if (!fs.existsSync(targetDir)) {\n fs.mkdirSync(targetDir, { recursive: true });\n Logger.log(`创建目录: ${targetDir}`);\n }\n\n const stat = fs.statSync(targetDir);\n if (!stat.isDirectory()) {\n throw new Error(`路径不是目录: ${targetDir}`);\n }\n\n // 从 URL 提取文件名\n let finalFileName = fileName;\n if (!finalFileName) {\n const urlObj = new URL(url);\n finalFileName = path.basename(urlObj.pathname);\n // 如果 URL 没有文件名,生成一个\n if (!finalFileName || finalFileName === '/') {\n finalFileName = `download_${Date.now()}`;\n }\n }\n\n const filePath = path.join(targetDir, finalFileName);\n\n // 检查文件是否已存在\n if (fs.existsSync(filePath)) {\n throw new Error(`文件已存在: ${filePath}`);\n }\n\n // 下载文件\n await new Promise<void>((resolve, reject) => {\n const urlObj = new URL(url);\n const protocol = urlObj.protocol === 'https:' ? https : http;\n\n const request = protocol.get(url, (response) => {\n // 处理重定向\n if (response.statusCode === 301 || response.statusCode === 302) {\n const redirectUrl = response.headers.location;\n if (redirectUrl) {\n Logger.log(`重定向到: ${redirectUrl}`);\n const redirectProtocol = redirectUrl.startsWith('https:') ? https : http;\n redirectProtocol.get(redirectUrl, (redirectResponse) => {\n if (redirectResponse.statusCode !== 200) {\n reject(new Error(`下载失败,HTTP 状态码: ${redirectResponse.statusCode}`));\n return;\n }\n const fileStream = fs.createWriteStream(filePath);\n redirectResponse.pipe(fileStream);\n fileStream.on('finish', () => {\n fileStream.close();\n resolve();\n });\n fileStream.on('error', (err) => {\n fs.unlink(filePath, () => {});\n reject(err);\n });\n }).on('error', reject);\n return;\n }\n }\n\n if (response.statusCode !== 200) {\n reject(new Error(`下载失败,HTTP 状态码: ${response.statusCode}`));\n return;\n }\n\n const fileStream = fs.createWriteStream(filePath);\n response.pipe(fileStream);\n\n fileStream.on('finish', () => {\n fileStream.close();\n resolve();\n });\n\n fileStream.on('error', (err) => {\n fs.unlink(filePath, () => {});\n reject(err);\n });\n });\n\n request.on('error', (err) => {\n fs.unlink(filePath, () => {});\n reject(err);\n });\n\n request.setTimeout(60000, () => {\n request.destroy();\n fs.unlink(filePath, () => {});\n reject(new Error('下载超时(60秒)'));\n });\n });\n\n // 获取文件大小\n const downloadedStat = fs.statSync(filePath);\n const sizeStr = downloadedStat.size < 1024\n ? `${downloadedStat.size}B`\n : downloadedStat.size < 1024 * 1024\n ? `${(downloadedStat.size / 1024).toFixed(1)}KB`\n : `${(downloadedStat.size / 1024 / 1024).toFixed(1)}MB`;\n\n return {\n content: [{\n type: \"text\",\n text: `文件下载成功!\\n源URL: ${url}\\n保存路径: ${filePath}\\n文件大小: ${sizeStr}`\n }]\n };\n } catch (error) {\n Logger.error(`下载文件出错:`, error);\n return {\n isError: true,\n content: [{\n type: \"text\",\n text: `下载失败: ${error}`\n }]\n };\n }\n }\n );\n\n // 工具:检查压缩图片的前置条件\n this.server.tool(\n \"check_compress_prerequisites\",\n `检查图片压缩的前置条件并返回用户询问模板。\n\n【⚠️ 极其重要 - AI 必须严格按以下顺序执行】\n\n第 1 步:检查 Playwright MCP(必须在调用本工具之前完成!)\n- 调用 browser_snapshot 工具\n- 如果成功:继续执行\n- 如果失败/报错:立即停止,告知用户需要启用 Playwright MCP\n\n第 2 步:如果图片在 OSS 上(必须在调用本工具之前完成!)\n- 先使用 list_oss_files 列出文件\n- ⚠️ 重要:下载到【项目目录】下的 .tmp-compress/ 文件夹,不能用 /tmp/!\n (Playwright MCP 只能访问项目目录内的文件)\n- 使用 download_file 将图片下载到项目目录下\n- 只有下载到本地后才能调用本工具\n\n第 3 步:调用本工具\n- 传入本地图片路径数组\n- 获取用户询问模板\n\n第 4 步:询问用户\n- 使用 AskUserQuestion 询问压缩引擎、输出格式等\n\n第 5 步:执行压缩\n- 调用 compress_images 工具`,\n {\n images: z.array(z.string()).describe(\"要压缩的【本地】图片路径数组。如果图片在 OSS 上,必须先用 download_file 下载到本地!\")\n },\n async ({ images }) => {\n try {\n // 验证图片文件\n const validImages: { path: string; name: string; ext: string; size: number }[] = [];\n const errors: string[] = [];\n\n for (const imgPath of images) {\n if (!fs.existsSync(imgPath)) {\n errors.push(`文件不存在: ${imgPath}`);\n continue;\n }\n const stat = fs.statSync(imgPath);\n if (stat.size > 5 * 1024 * 1024) {\n errors.push(`文件超过 5MB 限制: ${imgPath}`);\n continue;\n }\n const ext = path.extname(imgPath).toLowerCase().slice(1);\n if (!['png', 'jpg', 'jpeg', 'webp', 'gif', 'bmp', 'tiff'].includes(ext)) {\n errors.push(`不支持的格式: ${imgPath}`);\n continue;\n }\n validImages.push({\n path: imgPath,\n name: path.basename(imgPath, path.extname(imgPath)),\n ext: ext === 'jpg' ? 'jpeg' : ext,\n size: stat.size\n });\n }\n\n if (validImages.length === 0) {\n return {\n isError: true,\n content: [{\n type: \"text\",\n text: `没有有效的图片可处理:\\n${errors.join('\\n')}`\n }]\n };\n }\n\n // 构建需要询问用户的问题\n const questions = {\n playwrightCheck: {\n instruction: \"请先使用 browser_snapshot 工具测试 Playwright MCP 是否可用。如果报错说明未配置。\"\n },\n engineQuestion: {\n question: \"请选择压缩引擎\",\n header: \"压缩引擎\",\n options: [\n { label: \"TinyPNG (推荐)\", description: \"支持 PNG/JPEG/WebP 输出,压缩质量高,每批最多 3 个文件\" },\n { label: \"AnyWebP\", description: \"固定输出 WebP 格式,每批最多 20 个文件\" }\n ]\n },\n formatQuestion: {\n question: \"是否需要转换输出格式?\",\n header: \"输出格式\",\n options: [\n { label: \"保持原格式\", description: \"不转换格式,仅压缩\" },\n { label: \"转换为 WebP\", description: \"转换为 WebP 格式,体积更小\" },\n { label: \"转换为 JPEG\", description: \"转换为 JPEG 格式(仅 TinyPNG)\" },\n { label: \"转换为 PNG\", description: \"转换为 PNG 格式(仅 TinyPNG)\" }\n ]\n },\n deleteOriginalQuestion: {\n question: \"转换格式后是否删除原文件?\",\n header: \"删除原文件\",\n options: [\n { label: \"保留原文件\", description: \"在 OSS 上保留原格式文件\" },\n { label: \"删除原文件\", description: \"转换后删除 OSS 上的原格式文件\" }\n ],\n condition: \"仅当选择了转换格式时才需要询问\"\n }\n };\n\n const sizeStr = (size: number) => size < 1024\n ? `${size}B`\n : size < 1024 * 1024\n ? `${(size / 1024).toFixed(1)}KB`\n : `${(size / 1024 / 1024).toFixed(1)}MB`;\n\n let resultText = `## 图片压缩前置检查\\n\\n`;\n resultText += `### ✅ 有效图片 (${validImages.length} 个)\\n`;\n for (const img of validImages) {\n resultText += `- ${path.basename(img.path)} (${sizeStr(img.size)})\\n`;\n }\n\n if (errors.length > 0) {\n resultText += `\\n### ⚠️ 跳过的文件\\n`;\n for (const err of errors) {\n resultText += `- ${err}\\n`;\n }\n }\n\n resultText += `\\n### 📋 AI 执行步骤\\n\\n`;\n resultText += `1. **检查 Playwright**: 调用 \\`browser_snapshot\\` 测试是否可用\\n`;\n resultText += ` - 如果报错,提示用户需要配置 Playwright MCP\\n`;\n resultText += `2. **询问用户**: 使用 AskUserQuestion 一次性询问以下问题:\\n`;\n resultText += ` - 选择压缩引擎 (TinyPNG / AnyWebP)\\n`;\n resultText += ` - 是否转换格式 (保持原格式 / WebP / JPEG / PNG)\\n`;\n resultText += ` - 如果转格式,是否删除原文件\\n`;\n resultText += `3. **执行压缩**: 根据用户选择调用 \\`compress_images\\`\\n`;\n\n return {\n content: [\n {\n type: \"text\",\n text: resultText\n },\n {\n type: \"text\",\n text: `\\n---\\n**询问模板 (JSON)**:\\n\\`\\`\\`json\\n${JSON.stringify(questions, null, 2)}\\n\\`\\`\\``\n }\n ]\n };\n } catch (error) {\n Logger.error(`检查前置条件出错:`, error);\n return {\n isError: true,\n content: [{\n type: \"text\",\n text: `检查失败: ${error}`\n }]\n };\n }\n }\n );\n\n // 工具:压缩图片(生成压缩指令,由 AI 调用 Playwright MCP 执行)\n this.server.tool(\n \"compress_images\",\n `压缩图片工具。生成 Playwright 自动化压缩指令。\n\n【⚠️ 禁止直接调用!必须先完成以下步骤】\n\n✅ 第 1 步:验证 Playwright MCP 可用\n → 调用 browser_snapshot,如果报错则停止并提示用户启用\n\n✅ 第 2 步:确保图片在项目目录内\n → OSS 图片必须先用 download_file 下载到【项目目录】下的 .tmp-compress/ 文件夹\n → ⚠️ 不能用 /tmp/,Playwright 无法访问项目外的路径!\n\n✅ 第 3 步:调用 check_compress_prerequisites\n → 验证文件并获取询问模板\n\n✅ 第 4 步:询问用户偏好\n → 使用 AskUserQuestion 询问引擎、格式、是否删除原文件\n\n✅ 第 5 步:调用本工具\n → 传入用户选择的参数\n\n【后续流程】\n1. 按返回的指令使用 Playwright MCP 执行网页自动化\n2. 下载压缩结果\n3. 使用 upload_to_oss 上传回 OSS`,\n {\n images: z.array(z.string()).describe(\"要压缩的本地图片路径数组\"),\n engine: z.enum(['tinypng', 'anywebp']).describe(\"压缩引擎 (必须先询问用户选择)\"),\n outputFormat: z.enum(['png', 'jpeg', 'webp']).optional().describe(\"输出格式 (必须先询问用户选择,仅 tinypng 支持多格式)\"),\n deleteOriginal: z.boolean().optional().describe(\"转格式时是否删除原文件 (必须先询问用户选择)\"),\n ossDirectory: z.string().optional().describe(\"OSS 目标目录 (用于上传压缩后的文件)\"),\n configName: z.string().optional().describe(`OSS配置名称(默认为'default')。可用配置: ${configNames.join(', ') || '无'}`)\n },\n async ({ images, engine, outputFormat, deleteOriginal = false, ossDirectory, configName = 'default' }) => {\n try {\n Logger.log(`压缩图片: 引擎=${engine}, 格式=${outputFormat || '原格式'}, 图片数=${images.length}`);\n\n // 验证图片文件存在\n const validImages: { path: string; name: string; ext: string; size: number }[] = [];\n const errors: string[] = [];\n\n for (const imgPath of images) {\n if (!fs.existsSync(imgPath)) {\n errors.push(`文件不存在: ${imgPath}`);\n continue;\n }\n const stat = fs.statSync(imgPath);\n if (stat.size > 5 * 1024 * 1024) {\n errors.push(`文件超过 5MB 限制: ${imgPath}`);\n continue;\n }\n const ext = path.extname(imgPath).toLowerCase().slice(1);\n if (!['png', 'jpg', 'jpeg', 'webp', 'gif', 'bmp', 'tiff'].includes(ext)) {\n errors.push(`不支持的格式: ${imgPath}`);\n continue;\n }\n validImages.push({\n path: imgPath,\n name: path.basename(imgPath, path.extname(imgPath)),\n ext: ext === 'jpg' ? 'jpeg' : ext,\n size: stat.size\n });\n }\n\n if (validImages.length === 0) {\n return {\n isError: true,\n content: [{\n type: \"text\",\n text: `没有有效的图片可处理:\\n${errors.join('\\n')}`\n }]\n };\n }\n\n // 生成压缩指令\n const actualOutputFormat = engine === 'anywebp' ? 'webp' : (outputFormat || null);\n const batchSize = engine === 'tinypng' ? 3 : 20;\n const batches: typeof validImages[] = [];\n\n for (let i = 0; i < validImages.length; i += batchSize) {\n batches.push(validImages.slice(i, i + batchSize));\n }\n\n // 构建指令文本\n let instructions = `## 图片压缩指令\\n\\n`;\n instructions += `**引擎**: ${engine === 'tinypng' ? 'TinyPNG (https://tinypng.com/)' : 'AnyWebP (https://anywebp.com/convert-to-webp)'}\\n`;\n instructions += `**输出格式**: ${actualOutputFormat || '保持原格式'}\\n`;\n instructions += `**总图片数**: ${validImages.length}\\n`;\n instructions += `**批次数**: ${batches.length} (每批最多 ${batchSize} 个)\\n\\n`;\n\n if (errors.length > 0) {\n instructions += `### ⚠️ 跳过的文件\\n`;\n for (const err of errors) {\n instructions += `- ${err}\\n`;\n }\n instructions += `\\n`;\n }\n\n instructions += `### 📋 执行步骤\\n\\n`;\n instructions += `**前置检查**: 请确认 Playwright MCP 已配置并可用\\n\\n`;\n\n if (engine === 'tinypng') {\n instructions += this.generateTinyPngInstructions(batches, actualOutputFormat);\n } else {\n instructions += this.generateAnyWebPInstructions(batches);\n }\n\n // 添加后续处理指令\n instructions += `\\n### 📤 后续处理\\n\\n`;\n instructions += `压缩完成后,请执行以下操作:\\n\\n`;\n\n for (const img of validImages) {\n const newExt = actualOutputFormat || img.ext;\n const isFormatChange = newExt !== img.ext;\n const newFileName = `${img.name}.${newExt}`;\n const downloadPath = path.join(path.dirname(img.path), `${img.name}-compressed.${newExt}`);\n\n instructions += `**${path.basename(img.path)}**:\\n`;\n instructions += `1. 下载压缩结果到: \\`${downloadPath}\\`\\n`;\n\n if (ossDirectory) {\n if (isFormatChange) {\n instructions += `2. 上传到 OSS: \\`upload_to_oss(\"${downloadPath}\", \"${ossDirectory}\", \"${newFileName}\", \"${configName}\")\\`\\n`;\n if (deleteOriginal) {\n instructions += `3. 删除原文件: 在 OSS 上删除 \\`${ossDirectory}/${path.basename(img.path)}\\`\\n`;\n }\n } else {\n instructions += `2. 覆盖上传到 OSS: \\`upload_to_oss(\"${downloadPath}\", \"${ossDirectory}\", \"${path.basename(img.path)}\", \"${configName}\")\\`\\n`;\n }\n }\n instructions += `\\n`;\n }\n\n // 返回信息\n const resultInfo = {\n engine,\n outputFormat: actualOutputFormat,\n deleteOriginal: actualOutputFormat ? deleteOriginal : false,\n ossDirectory,\n configName,\n totalImages: validImages.length,\n batches: batches.length,\n batchSize,\n images: validImages.map(img => ({\n originalPath: img.path,\n originalName: path.basename(img.path),\n originalExt: img.ext,\n originalSize: img.size,\n newExt: actualOutputFormat || img.ext,\n isFormatChange: (actualOutputFormat || img.ext) !== img.ext\n }))\n };\n\n return {\n content: [\n {\n type: \"text\",\n text: instructions\n },\n {\n type: \"text\",\n text: `\\n---\\n**压缩任务数据 (JSON)**:\\n\\`\\`\\`json\\n${JSON.stringify(resultInfo, null, 2)}\\n\\`\\`\\``\n }\n ]\n };\n } catch (error) {\n Logger.error(`生成压缩指令出错:`, error);\n return {\n isError: true,\n content: [{\n type: \"text\",\n text: `生成压缩指令失败: ${error}`\n }]\n };\n }\n }\n );\n\n // 工具:导出 Figma 多倍图\n const figmaTokenConfigured = !!getFigmaToken();\n\n this.server.tool(\n \"export_figma_images\",\n `从 Figma 导出多倍图(支持 1x/2x/3x/4x)。使用 Figma REST API 直接导出指定倍率的图片,弥补 Figma MCP 不支持多倍图导出的不足。\n\n支持两种模式:\n- 导出到本地目录\n- 导出后直接上传到阿里云 OSS\n\n当前状态: ${figmaTokenConfigured ? '✅ Figma Token 已配置' : '❌ 未配置 Figma Token(需要 --figma-token 参数或 FIGMA_TOKEN 环境变量)'}\n\n【使用说明】\n- fileKey 和 nodeId 可从 Figma URL 中提取:\n figma.com/design/:fileKey/:fileName?node-id=:nodeId\n 注意:URL 中的 node-id 用 \"-\" 分隔,需要替换为 \":\"(如 \"14-123\" → \"14:123\")\n- scales 支持 1/2/3/4,可同时导出多个倍率\n- 导出到本地时文件命名格式:{prefix}.png, {prefix}@2x.png, {prefix}@3x.png\n- 如果同时指定了 ossTargetDir,导出后会自动上传到 OSS`,\n {\n fileKey: z.string().describe(\"Figma 文件 Key(从 URL 中提取)\"),\n nodeId: z.string().describe(\"Figma 节点 ID(格式如 '14:123',URL 中的 '-' 需替换为 ':')\"),\n scales: z.array(z.number().min(0.01).max(4)).default([1, 2]).describe(\"导出倍率数组,默认 [1, 2]。常用值: [1, 2], [1, 2, 3]\"),\n format: z.enum(['png', 'jpg', 'svg', 'pdf']).default('png').describe(\"导出格式,默认 png\"),\n localTargetDir: z.string().describe(\"本地保存目录路径\"),\n fileNamePrefix: z.string().optional().describe(\"文件名前缀(可选,默认使用 nodeId 生成)\"),\n ossTargetDir: z.string().optional().describe(\"OSS 目标目录(可选,填写则导出后自动上传到 OSS)\"),\n configName: z.string().optional().describe(`OSS配置名称(默认为'default',仅上传到 OSS 时需要)。可用配置: ${configNames.join(', ') || '无'}`)\n },\n async ({ fileKey, nodeId, scales, format, localTargetDir, fileNamePrefix, ossTargetDir, configName = 'default' }) => {\n try {\n if (!figmaTokenConfigured) {\n return {\n isError: true,\n content: [{\n type: \"text\",\n text: `❌ 未配置 Figma Token!\n\n请在 MCP 配置中添加 Figma Token:\n\n方式一:CLI 参数\n{\n \"command\": \"npx\",\n \"args\": [\"oss-mcp-plus\", \"--figma-token=figd_xxxxx\", \"--oss-config='{...}'\", \"--stdio\"]\n}\n\n方式二:环境变量\n{\n \"command\": \"npx\",\n \"args\": [\"oss-mcp-plus\", \"--oss-config='{...}'\", \"--stdio\"],\n \"env\": {\n \"FIGMA_TOKEN\": \"figd_xxxxx\"\n }\n}\n\n获取 Token: Figma → Settings → Personal Access Tokens`\n }]\n };\n }\n\n Logger.log(`导出 Figma 多倍图: fileKey=${fileKey}, nodeId=${nodeId}, scales=${scales.join(',')}, format=${format}`);\n\n // Step 1: 导出到本地\n const results = await figmaService.exportToLocal(\n { fileKey, nodeId, scales, format },\n localTargetDir,\n fileNamePrefix,\n );\n\n // Step 2: 如果指定了 OSS 目录,上传到 OSS\n if (ossTargetDir) {\n for (const result of results) {\n if (!result.localPath) continue;\n const uploadResult = await ossService.uploadFile({\n filePath: result.localPath,\n targetDir: ossTargetDir,\n fileName: result.fileName,\n configName,\n });\n if (uploadResult.success) {\n result.ossUrl = uploadResult.url;\n } else {\n Logger.error(`上传 ${result.fileName} 到 OSS 失败: ${uploadResult.error}`);\n }\n }\n }\n\n // 构建结果文本\n const sizeStr = (size: number) => size < 1024\n ? `${size}B`\n : size < 1024 * 1024\n ? `${(size / 1024).toFixed(1)}KB`\n : `${(size / 1024 / 1024).toFixed(1)}MB`;\n\n let resultText = `## Figma 多倍图导出完成\\n\\n`;\n resultText += `**文件 Key**: ${fileKey}\\n`;\n resultText += `**节点 ID**: ${nodeId}\\n`;\n resultText += `**格式**: ${format}\\n`;\n resultText += `**倍率**: ${scales.join('x, ')}x\\n\\n`;\n\n resultText += `### 导出结果\\n\\n`;\n for (const r of results) {\n const localSize = r.localPath && fs.existsSync(r.localPath)\n ? sizeStr(fs.statSync(r.localPath).size)\n : '未知';\n\n resultText += `**${r.fileName}** (${r.scale}x, ${localSize})\\n`;\n resultText += `- 本地路径: \\`${r.localPath}\\`\\n`;\n if (r.ossUrl) {\n resultText += `- OSS URL: ${r.ossUrl}\\n`;\n }\n resultText += `\\n`;\n }\n\n if (ossTargetDir) {\n const uploadedCount = results.filter(r => r.ossUrl).length;\n resultText += `\\n### OSS 上传\\n`;\n resultText += `- 目录: ${ossTargetDir}\\n`;\n resultText += `- 配置: ${configName}\\n`;\n resultText += `- 成功: ${uploadedCount}/${results.length}\\n`;\n }\n\n return {\n content: [{\n type: \"text\",\n text: resultText\n }]\n };\n } catch (error) {\n Logger.error(`导出 Figma 多倍图出错:`, error);\n return {\n isError: true,\n content: [{\n type: \"text\",\n text: `导出 Figma 多倍图失败: ${error instanceof Error ? error.message : error}`\n }]\n };\n }\n }\n );\n }\n\n // 生成 TinyPNG 自动化指令\n private generateTinyPngInstructions(batches: { path: string; name: string; ext: string; size: number }[][], outputFormat: string | null): string {\n let instructions = '';\n\n // 添加重要提示\n instructions += `### ⚠️ 重要提示\\n\\n`;\n instructions += `1. **图片必须在项目目录内**:Playwright MCP 只能访问项目目录下的文件。\\n`;\n instructions += ` - 不能使用 \\`/tmp/\\` 等系统临时目录\\n`;\n instructions += ` - 请将图片下载到项目根目录下的 \\`.tmp-compress/\\` 文件夹\\n`;\n instructions += `2. **上传前必须先触发文件选择框**:先点击上传区域,等待 Modal state 显示 \"File chooser\" 后再调用 \\`browser_file_upload\\`\\n\\n`;\n\n const needsFormatConversion = outputFormat && outputFormat !== 'png';\n\n for (let i = 0; i < batches.length; i++) {\n const batch = batches[i];\n instructions += `#### 批次 ${i + 1}/${batches.length}\\n\\n`;\n instructions += `**文件**: ${batch.map(img => path.basename(img.path)).join(', ')}\\n\\n`;\n\n let stepNum = 1;\n instructions += `${stepNum++}. **打开网站**: 使用 \\`browser_navigate\\` 访问 \\`https://tinypng.com/\\`\\n`;\n instructions += `${stepNum++}. **等待加载**: 使用 \\`browser_snapshot\\` 确认页面加载完成\\n`;\n\n // 如果需要转换格式(WebP/JPEG),先开启开关\n if (needsFormatConversion) {\n instructions += `${stepNum++}. **开启格式转换开关**: \\n`;\n instructions += ` - 在页面底部找到 \"Convert my images automatically\" 开关\\n`;\n instructions += ` - 使用 \\`browser_click\\` 点击开关开启它(如果是关闭状态)\\n`;\n instructions += ` - 开启后会出现格式选择选项\\n`;\n instructions += `${stepNum++}. **选择输出格式**: \\n`;\n instructions += ` - 点击选择 \"${outputFormat!.toUpperCase()}\" 格式\\n`;\n }\n\n instructions += `${stepNum++}. **触发文件选择框**: \\n`;\n instructions += ` - 使用 \\`browser_click\\` 点击上传区域(\"Drop your .webp, .png or .jpg files here!\" 文字区域)\\n`;\n instructions += ` - 等待 \\`browser_snapshot\\` 返回结果中 Modal state 显示 \"[File chooser]\"\\n`;\n instructions += `${stepNum++}. **上传文件**: 使用 \\`browser_file_upload\\` 上传以下文件:\\n`;\n for (const img of batch) {\n instructions += ` - \\`${img.path}\\`\\n`;\n }\n instructions += `${stepNum++}. **等待压缩**: 使用 \\`browser_wait_for\\` 等待 \"Download all\" 或各文件的 \"download\" 按钮出现\\n`;\n instructions += `${stepNum++}. **下载结果**: 点击 \"Download all\" 或逐个下载\\n`;\n\n if (i < batches.length - 1) {\n instructions += `${stepNum++}. **刷新页面**: 使用 \\`browser_navigate\\` 重新访问 \\`https://tinypng.com/\\` 准备下一批\\n`;\n }\n instructions += `\\n`;\n }\n\n return instructions;\n }\n\n // 生成 AnyWebP 自动化指令\n private generateAnyWebPInstructions(batches: { path: string; name: string; ext: string; size: number }[][]): string {\n let instructions = '';\n\n // 添加重要提示\n instructions += `### ⚠️ 重要提示\\n\\n`;\n instructions += `1. **图片必须在项目目录内**:Playwright MCP 只能访问项目目录下的文件。\\n`;\n instructions += ` - 不能使用 \\`/tmp/\\` 等系统临时目录\\n`;\n instructions += ` - 请将图片下载到项目根目录下的 \\`.tmp-compress/\\` 文件夹\\n`;\n instructions += `2. **上传前必须先触发文件选择框**:先点击上传区域,等待 Modal state 显示 \"File chooser\" 后再调用 \\`browser_file_upload\\`\\n\\n`;\n\n for (let i = 0; i < batches.length; i++) {\n const batch = batches[i];\n instructions += `#### 批次 ${i + 1}/${batches.length}\\n\\n`;\n instructions += `**文件**: ${batch.map(img => path.basename(img.path)).join(', ')}\\n\\n`;\n\n let stepNum = 1;\n instructions += `${stepNum++}. **打开网站**: 使用 \\`browser_navigate\\` 访问 \\`https://anywebp.com/convert-to-webp.html\\`\\n`;\n instructions += `${stepNum++}. **等待加载**: 使用 \\`browser_snapshot\\` 确认页面加载完成\\n`;\n instructions += `${stepNum++}. **触发文件选择框**: \\n`;\n instructions += ` - 使用 \\`browser_click\\` 点击 \"Drop your images here!\" 上传区域\\n`;\n instructions += ` - 等待 \\`browser_snapshot\\` 返回结果中 Modal state 显示 \"[File chooser]\"\\n`;\n instructions += `${stepNum++}. **上传文件**: 使用 \\`browser_file_upload\\` 上传以下文件:\\n`;\n for (const img of batch) {\n instructions += ` - \\`${img.path}\\`\\n`;\n }\n instructions += `${stepNum++}. **等待转换**: 使用 \\`browser_wait_for\\` 等待转换完成,出现 \"Download\" 按钮\\n`;\n instructions += `${stepNum++}. **下载结果**: 点击 \"Download All\" 或逐个下载 WebP 文件\\n`;\n\n if (i < batches.length - 1) {\n instructions += `${stepNum++}. **刷新页面**: 使用 \\`browser_navigate\\` 重新访问准备下一批\\n`;\n }\n instructions += `\\n`;\n }\n\n return instructions;\n }\n\n async connect(transport: Transport): Promise<void> {\n try {\n await this.server.connect(transport);\n\n Logger.log = (...args: any[]) => {\n try {\n this.server.server.sendLoggingMessage({\n level: \"info\",\n data: args,\n });\n } catch (error) {\n console.log(...args);\n }\n };\n\n Logger.error = (...args: any[]) => {\n try {\n this.server.server.sendLoggingMessage({\n level: \"error\",\n data: args,\n });\n } catch (error) {\n console.error(...args);\n }\n };\n\n Logger.log(\"OSS MCP服务器已连接并准备处理请求\");\n } catch (error) {\n console.error(\"连接到传输时出错:\", error);\n }\n }\n\n async startHttpServer(port: number): Promise<void> {\n const app = express();\n\n // SSE连接端点 - 修复头部发送冲突\n app.get(\"/sse\", (req: Request, res: Response) => {\n // 初始化SSE传输,不再自己设置头部,而是让SDK处理\n this.sseTransport = new SSEServerTransport(\n \"/messages\",\n res as unknown as ServerResponse<IncomingMessage>\n );\n\n try {\n // 连接到传输层\n this.server.connect(this.sseTransport)\n .catch((err) => {\n console.error(\"连接到SSE传输时出错:\", err);\n });\n\n // 处理客户端断开连接\n req.on('close', () => {\n console.log('SSE客户端断开连接');\n this.sseTransport = null;\n });\n } catch (error) {\n console.error(\"建立SSE连接时出错:\", error);\n // 如果连接失败,关闭响应\n if (!res.writableEnded) {\n res.status(500).end();\n }\n }\n });\n\n // 消息端点\n app.post(\"/messages\", async (req: Request, res: Response) => {\n if (!this.sseTransport) {\n console.log(\"尝试发送消息,但SSE传输未初始化\");\n res.status(400).json({\n error: 'SSE连接未建立',\n message: '请先连接到/sse端点'\n });\n return;\n }\n\n try {\n await this.sseTransport.handlePostMessage(\n req as unknown as IncomingMessage,\n res as unknown as ServerResponse<IncomingMessage>\n );\n } catch (error) {\n console.error(\"处理消息时出错:\", error);\n if (!res.writableEnded) {\n res.status(500).json({\n error: \"内部服务器错误\",\n message: String(error)\n });\n }\n }\n });\n\n // 启动服务器\n app.listen(port, () => {\n Logger.log = console.log;\n Logger.error = console.error;\n\n Logger.log(`HTTP服务器监听端口: ${port}`);\n Logger.log(`SSE端点: http://localhost:${port}/sse`);\n Logger.log(`消息端点: http://localhost:${port}/messages`);\n });\n }\n}\n","import OSS from 'ali-oss';\nimport fs from 'fs';\nimport path from 'path';\nimport { OssConfig, getOssConfig, getAllOssConfigs } from '../config/oss.config.js';\nimport { z } from 'zod';\n\n// 上传文件参数验证Schema\nexport const UploadFileParamsSchema = z.object({\n filePath: z.string(),\n targetDir: z.string().optional(),\n fileName: z.string().optional(),\n configName: z.string().optional(),\n});\n\n// 导出上传文件参数类型\nexport type UploadFileParams = z.infer<typeof UploadFileParamsSchema>;\n\n// 上传结果验证Schema\nexport const UploadResultSchema = z.object({\n success: z.boolean(),\n url: z.string().optional(),\n error: z.string().optional(),\n ossConfigName: z.string().optional(),\n});\n\n// 导出上传结果类型\nexport type UploadResult = z.infer<typeof UploadResultSchema>;\n\n/**\n * OSS配置接口(包含ID和名称)\n */\nexport interface OssConfigWithMeta extends OssConfig {\n id: string;\n name: string;\n}\n\n/**\n * 阿里云OSS服务类\n */\nexport class OssService {\n private clients: Map<string, OSS> = new Map();\n\n /**\n * 获取所有OSS配置\n * @returns OSS配置列表\n */\n getConfigs(): OssConfigWithMeta[] {\n const configs: OssConfigWithMeta[] = [];\n const allConfigs = getAllOssConfigs();\n\n for (const [id, config] of Object.entries(allConfigs)) {\n configs.push({\n id,\n name: `${id.charAt(0).toUpperCase()}${id.slice(1)} 配置`,\n ...config\n });\n }\n\n return configs;\n }\n\n /**\n * 获取OSS客户端\n * @param configName 配置名称\n * @returns OSS客户端实例\n */\n private getClient(configName: string = 'default'): OSS | null {\n // 检查缓存中是否已有客户端\n if (this.clients.has(configName)) {\n return this.clients.get(configName) as OSS;\n }\n\n // 获取配置并创建客户端\n const config = getOssConfig(configName);\n if (!config) {\n return null;\n }\n\n try {\n const client = new OSS({\n region: config.region,\n accessKeyId: config.accessKeyId,\n accessKeySecret: config.accessKeySecret,\n bucket: config.bucket,\n endpoint: config.endpoint\n });\n\n // 缓存客户端实例\n this.clients.set(configName, client);\n return client;\n } catch (error) {\n console.error(`Failed to create OSS client for ${configName}:`, error);\n return null;\n }\n }\n\n /**\n * 上传文件到OSS\n * @param params 上传参数\n * @returns 上传结果\n */\n async uploadFile(params: UploadFileParams): Promise<UploadResult> {\n // 验证并解析参数\n const validParams = UploadFileParamsSchema.parse(params);\n const { filePath, targetDir = '', fileName, configName = 'default' } = validParams;\n\n try {\n // 检查文件是否存在\n if (!fs.existsSync(filePath)) {\n return UploadResultSchema.parse({\n success: false,\n error: `File not found: ${filePath}`,\n ossConfigName: configName\n });\n }\n\n // 获取OSS客户端\n const client = this.getClient(configName);\n if (!client) {\n return UploadResultSchema.parse({\n success: false,\n error: `OSS config not found for: ${configName}`,\n ossConfigName: configName\n });\n }\n\n // 确定文件名\n const actualFileName = fileName || path.basename(filePath);\n\n // 构建OSS路径,确保正斜杠格式\n let ossPath = actualFileName;\n if (targetDir) {\n // 规范化目标目录:移除头尾斜杠,然后加上结尾斜杠\n const normalizedDir = targetDir.replace(/^\\/+|\\/+$/g, '');\n ossPath = normalizedDir ? `${normalizedDir}/${actualFileName}` : actualFileName;\n }\n\n // 上传文件\n const result = await client.put(ossPath, filePath);\n\n return UploadResultSchema.parse({\n success: true,\n url: result.url,\n ossConfigName: configName\n });\n } catch (error) {\n return UploadResultSchema.parse({\n success: false,\n error: `Upload failed: ${(error as Error).message}`,\n ossConfigName: configName\n });\n }\n }\n\n /**\n * 重命名OSS文件(通过 copy + delete 实现)\n * @param oldKey 原文件路径\n * @param newKey 新文件路径\n * @param configName 配置名称\n * @returns 重命名结果\n */\n async renameFile(oldKey: string, newKey: string, configName: string = 'default'): Promise<{ success: boolean; error?: string }> {\n try {\n const client = this.getClient(configName);\n if (!client) {\n return { success: false, error: `OSS config not found for: ${configName}` };\n }\n\n // 规范化路径:移除开头的斜杠\n const normalizedOldKey = oldKey.replace(/^\\/+/, '');\n const normalizedNewKey = newKey.replace(/^\\/+/, '');\n\n // 检查源文件是否存在\n try {\n await client.head(normalizedOldKey);\n } catch (_e) {\n return { success: false, error: `源文件不存在: ${normalizedOldKey}` };\n }\n\n // 检查目标文件是否已存在\n try {\n await client.head(normalizedNewKey);\n // 如果到这里说明文件存在\n if (normalizedOldKey !== normalizedNewKey) {\n return { success: false, error: `目标文件已存在: ${normalizedNewKey}` };\n }\n } catch (_e) {\n // 文件不存在,可以继续\n }\n\n // Step 1: 复制文件到新位置\n await client.copy(normalizedNewKey, normalizedOldKey);\n\n // Step 2: 删除原文件\n await client.delete(normalizedOldKey);\n\n return { success: true };\n } catch (error) {\n return { success: false, error: `重命名失败: ${(error as Error).message}` };\n }\n }\n\n /**\n * 列出OSS目录下的文件\n * @param directory OSS目录路径\n * @param configName 配置名称\n * @param pattern 文件名过滤模式(可选)\n * @returns 文件列表\n */\n async listFiles(\n directory: string = '',\n configName: string = 'default',\n pattern?: string\n ): Promise<{ success: boolean; files?: Array<{ name: string; size: number; lastModified: Date }>; error?: string }> {\n try {\n const client = this.getClient(configName);\n if (!client) {\n return { success: false, error: `OSS config not found for: ${configName}` };\n }\n\n // 规范化目录路径\n const normalizedDir = directory.replace(/^\\/+|\\/+$/g, '');\n const prefix = normalizedDir ? `${normalizedDir}/` : '';\n\n // 列出文件\n const result = await client.list({\n prefix,\n delimiter: '/',\n 'max-keys': 1000\n }, {});\n\n const files: Array<{ name: string; size: number; lastModified: Date }> = [];\n\n // 处理文件对象\n if (result.objects) {\n for (const obj of result.objects) {\n // 跳过目录本身(以 / 结尾的)\n if (obj.name.endsWith('/')) continue;\n\n // 提取文件名(去掉目录前缀)\n const fileName = obj.name.replace(prefix, '');\n if (!fileName) continue;\n\n // 如果有 pattern,进行简单的通配符匹配\n if (pattern) {\n const regex = new RegExp(\n '^' + pattern\n .replace(/\\./g, '\\\\.')\n .replace(/\\*/g, '.*')\n .replace(/\\?/g, '.') + '$',\n 'i'\n );\n if (!regex.test(fileName)) continue;\n }\n\n files.push({\n name: fileName,\n size: obj.size,\n lastModified: new Date(obj.lastModified)\n });\n }\n }\n\n // 按文件名排序\n files.sort((a, b) => a.name.localeCompare(b.name));\n\n return { success: true, files };\n } catch (error) {\n return { success: false, error: `列出文件失败: ${(error as Error).message}` };\n }\n }\n\n /**\n * 批量重命名OSS文件\n * @param rules 重命名规则数组\n * @param directory OSS目录路径\n * @param configName 配置名称\n * @returns 批量重命名结果\n */\n async batchRenameFiles(\n rules: Array<{ oldName: string; newName: string }>,\n directory: string = '',\n configName: string = 'default'\n ): Promise<Array<{ oldName: string; newName: string; success: boolean; error?: string }>> {\n const results: Array<{ oldName: string; newName: string; success: boolean; error?: string }> = [];\n\n // 规范化目录路径\n const normalizedDir = directory.replace(/^\\/+|\\/+$/g, '');\n const dirPrefix = normalizedDir ? `${normalizedDir}/` : '';\n\n for (const rule of rules) {\n const oldKey = `${dirPrefix}${rule.oldName}`;\n const newKey = `${dirPrefix}${rule.newName}`;\n\n const result = await this.renameFile(oldKey, newKey, configName);\n results.push({\n oldName: rule.oldName,\n newName: rule.newName,\n success: result.success,\n error: result.error\n });\n }\n\n return results;\n }\n\n /**\n * 删除单个OSS文件\n * @param key 文件路径\n * @param configName 配置名称\n * @returns 删除结果\n */\n async deleteFile(key: string, configName: string = 'default'): Promise<{ success: boolean; error?: string }> {\n try {\n const client = this.getClient(configName);\n if (!client) {\n return { success: false, error: `OSS config not found for: ${configName}` };\n }\n\n // 规范化路径:移除开头的斜杠\n const normalizedKey = key.replace(/^\\/+/, '');\n\n // 检查文件是否存在\n try {\n await client.head(normalizedKey);\n } catch (_e) {\n return { success: false, error: `文件不存在: ${normalizedKey}` };\n }\n\n // 删除文件\n await client.delete(normalizedKey);\n\n return { success: true };\n } catch (error) {\n return { success: false, error: `删除失败: ${(error as Error).message}` };\n }\n }\n\n /**\n * 批量删除OSS文件\n * @param fileNames 文件名数组\n * @param directory OSS目录路径\n * @param configName 配置名称\n * @returns 批量删除结果\n */\n async batchDeleteFiles(\n fileNames: string[],\n directory: string = '',\n configName: string = 'default'\n ): Promise<Array<{ fileName: string; success: boolean; error?: string }>> {\n const results: Array<{ fileName: string; success: boolean; error?: string }> = [];\n\n // 规范化目录路径\n const normalizedDir = directory.replace(/^\\/+|\\/+$/g, '');\n const dirPrefix = normalizedDir ? `${normalizedDir}/` : '';\n\n for (const fileName of fileNames) {\n const key = `${dirPrefix}${fileName}`;\n const result = await this.deleteFile(key, configName);\n results.push({\n fileName,\n success: result.success,\n error: result.error\n });\n }\n\n return results;\n }\n}\n\n// 导出单例实例\nexport const ossService = new OssService();\n","import { config } from \"dotenv\";\nimport yargs from \"yargs\";\nimport { hideBin } from \"yargs/helpers\";\nimport { z } from \"zod\";\n\nconfig();\n\n// OSS配置验证Schema\nexport const OssConfigSchema = z.object({\n region: z.string(),\n accessKeyId: z.string(),\n accessKeySecret: z.string(),\n bucket: z.string(),\n endpoint: z.string(),\n});\n\n// 导出OSS配置类型\nexport type OssConfig = z.infer<typeof OssConfigSchema>;\n\n// 服务器配置接口\nexport interface ServerConfig {\n port: number;\n ossConfig: Record<string, OssConfig>;\n figmaToken?: string;\n configSources: {\n port: \"cli\" | \"env\" | \"default\";\n ossConfig: \"cli\" | \"env\" | \"default\";\n figmaToken: \"cli\" | \"env\" | \"none\";\n };\n}\n\n// 掩码函数,用于打印敏感信息\nfunction maskSecret(secret: string): string {\n if (secret.length <= 4) return \"****\";\n return `${secret.substring(0, 4)}****${secret.slice(-4)}`;\n}\n\n// 获取服务器配置\nexport function getServerConfig(isStdioMode: boolean = false): ServerConfig {\n // 解析命令行参数\n const argv = yargs(hideBin(process.argv))\n .options({\n \"oss-config\": {\n type: \"string\",\n description: \"OSS配置JSON字符串\",\n },\n port: {\n type: \"number\",\n description: \"服务器运行端口\",\n default: 3000,\n },\n \"figma-token\": {\n type: \"string\",\n description: \"Figma Personal Access Token,用于导出多倍图\",\n },\n })\n .help()\n .version(\"1.0.0\")\n .parseSync();\n\n const config: ServerConfig = {\n port: 3000,\n ossConfig: {},\n configSources: {\n port: \"default\",\n ossConfig: \"default\",\n figmaToken: \"none\",\n },\n };\n\n // 处理端口配置\n if (argv.port) {\n config.port = argv.port;\n config.configSources.port = \"cli\";\n } else if (process.env.PORT) {\n config.port = parseInt(process.env.PORT, 10);\n config.configSources.port = \"env\";\n }\n\n // 处理OSS配置 - 首先检查命令行参数\n if (argv[\"oss-config\"]) {\n const allOssConfigs = JSON.parse(argv[\"oss-config\"] as string);\n\n if (allOssConfigs.region && allOssConfigs.accessKeyId) {\n config.ossConfig.default = OssConfigSchema.parse(allOssConfigs);\n } else {\n Object.entries(allOssConfigs).forEach(([name, cfg]) => {\n config.ossConfig[name.toLowerCase()] = OssConfigSchema.parse(cfg);\n });\n }\n config.configSources.ossConfig = \"cli\";\n } else if (process.env.OSS_CONFIG_DEFAULT) {\n const ossConfig = JSON.parse(process.env.OSS_CONFIG_DEFAULT)\n config.ossConfig.default = OssConfigSchema.parse(ossConfig);\n config.configSources.ossConfig = \"env\";\n }\n\n // 检查其他命名的OSS配置\n Object.entries(process.env).forEach(([key, value]) => {\n if (key.startsWith(\"OSS_CONFIG_\") && key !== \"OSS_CONFIG_DEFAULT\" && value) {\n try {\n const configName = key.replace(\"OSS_CONFIG_\", \"\").toLowerCase();\n const ossConfig = JSON.parse(value);\n config.ossConfig[configName] = OssConfigSchema.parse(ossConfig);\n } catch (error) {\n console.error(`解析环境变量${key}失败:`, error);\n }\n }\n });\n\n // 处理 Figma Token\n if (argv[\"figma-token\"]) {\n config.figmaToken = argv[\"figma-token\"] as string;\n config.configSources.figmaToken = \"cli\";\n } else if (process.env.FIGMA_TOKEN) {\n config.figmaToken = process.env.FIGMA_TOKEN;\n config.configSources.figmaToken = \"env\";\n }\n\n // 验证配置\n if (Object.keys(config.ossConfig).length === 0) {\n console.warn(\"未找到有效的OSS配置。服务器将启动,但上传功能将不可用。\");\n }\n\n // 打印配置信息(非stdio模式下)\n if (!isStdioMode) {\n console.log(\"\\n配置信息:\");\n console.log(`- 端口: ${config.port} (来源: ${config.configSources.port})`);\n\n if (Object.keys(config.ossConfig).length > 0) {\n console.log(\"- OSS配置:\");\n Object.entries(config.ossConfig).forEach(([name, cfg]) => {\n console.log(` - ${name}:`);\n console.log(` Region: ${cfg.region}`);\n console.log(` Endpoint: ${cfg.endpoint}`);\n console.log(` Bucket: ${cfg.bucket}`);\n console.log(` AccessKeyId: ${maskSecret(cfg.accessKeyId)}`);\n console.log(` AccessKeySecret: ${maskSecret(cfg.accessKeySecret)}`);\n });\n } else {\n console.log(\"- OSS配置: 未找到\");\n }\n\n if (config.figmaToken) {\n console.log(`- Figma Token: ${maskSecret(config.figmaToken)} (来源: ${config.configSources.figmaToken})`);\n } else {\n console.log(\"- Figma Token: 未配置(Figma 导出功能不可用)\");\n }\n\n console.log(); // 空行,增加可读性\n }\n\n return config;\n}\n\n// 缓存的 Figma Token(避免重复解析)\nlet cachedFigmaToken: string | undefined;\n\n// 获取 Figma Token\nexport function getFigmaToken(): string | undefined {\n if (cachedFigmaToken !== undefined) return cachedFigmaToken || undefined;\n const { figmaToken } = getServerConfig(true);\n cachedFigmaToken = figmaToken || '';\n return figmaToken;\n}\n\n// 获取所有OSS配置\nexport function getAllOssConfigs(): Record<string, OssConfig> {\n const { ossConfig } = getServerConfig(true);\n return ossConfig;\n}\n\n// 获取特定名称的OSS配置\nexport function getOssConfig(name: string = 'default'): OssConfig | null {\n const configs = getAllOssConfigs();\n const normalizedName = name.toLowerCase();\n return configs[normalizedName] || null;\n}\n\n// 获取可用的OSS配置名称列表\nexport function getAvailableOssConfigNames(): string[] {\n return Object.keys(getAllOssConfigs());\n}\n","import https from 'https';\nimport fs from 'fs';\nimport path from 'path';\nimport { getFigmaToken } from '../config/oss.config.js';\n\nconst FIGMA_API_BASE = 'https://api.figma.com/v1';\n\nexport interface FigmaExportOptions {\n fileKey: string;\n nodeId: string;\n scales: number[];\n format: 'png' | 'jpg' | 'svg' | 'pdf';\n}\n\nexport interface FigmaExportResult {\n scale: number;\n imageUrl: string;\n localPath?: string;\n ossUrl?: string;\n fileName: string;\n}\n\nfunction httpsGet(url: string, headers: Record<string, string> = {}): Promise<string> {\n return new Promise((resolve, reject) => {\n const urlObj = new URL(url);\n const options = {\n hostname: urlObj.hostname,\n path: urlObj.pathname + urlObj.search,\n method: 'GET',\n headers,\n };\n\n const req = https.request(options, (res) => {\n if (res.statusCode === 301 || res.statusCode === 302) {\n const redirectUrl = res.headers.location;\n if (redirectUrl) {\n httpsGet(redirectUrl, headers).then(resolve).catch(reject);\n return;\n }\n }\n\n if (res.statusCode !== 200) {\n let body = '';\n res.on('data', (chunk) => (body += chunk));\n res.on('end', () => reject(new Error(`Figma API 请求失败 (${res.statusCode}): ${body}`)));\n return;\n }\n\n let body = '';\n res.on('data', (chunk) => (body += chunk));\n res.on('end', () => resolve(body));\n });\n\n req.on('error', reject);\n req.setTimeout(30000, () => {\n req.destroy();\n reject(new Error('Figma API 请求超时(30秒)'));\n });\n req.end();\n });\n}\n\nfunction httpsDownload(url: string, destPath: string): Promise<void> {\n return new Promise((resolve, reject) => {\n const urlObj = new URL(url);\n const protocol = https;\n\n const request = protocol.get(url, (response) => {\n if (response.statusCode === 301 || response.statusCode === 302) {\n const redirectUrl = response.headers.location;\n if (redirectUrl) {\n httpsDownload(redirectUrl, destPath).then(resolve).catch(reject);\n return;\n }\n }\n\n if (response.statusCode !== 200) {\n reject(new Error(`下载失败,HTTP 状态码: ${response.statusCode}`));\n return;\n }\n\n const fileStream = fs.createWriteStream(destPath);\n response.pipe(fileStream);\n\n fileStream.on('finish', () => {\n fileStream.close();\n resolve();\n });\n\n fileStream.on('error', (err) => {\n fs.unlink(destPath, () => {});\n reject(err);\n });\n });\n\n request.on('error', (err) => {\n fs.unlink(destPath, () => {});\n reject(err);\n });\n\n request.setTimeout(60000, () => {\n request.destroy();\n fs.unlink(destPath, () => {});\n reject(new Error('下载超时(60秒)'));\n });\n });\n}\n\nexport class FigmaService {\n /**\n * 通过 Figma REST API 获取指定 scale 的图片导出 URL\n */\n async getImageUrls(\n fileKey: string,\n nodeId: string,\n scale: number,\n format: string,\n token: string,\n ): Promise<Record<string, string>> {\n const encodedNodeId = encodeURIComponent(nodeId);\n const url = `${FIGMA_API_BASE}/images/${fileKey}?ids=${encodedNodeId}&scale=${scale}&format=${format}`;\n\n const responseBody = await httpsGet(url, {\n 'X-Figma-Token': token,\n });\n\n const data = JSON.parse(responseBody);\n\n if (data.err) {\n throw new Error(`Figma API 错误: ${data.err}`);\n }\n\n return data.images || {};\n }\n\n /**\n * 导出 Figma 多倍图到本地\n */\n async exportToLocal(\n options: FigmaExportOptions,\n targetDir: string,\n fileNamePrefix?: string,\n ): Promise<FigmaExportResult[]> {\n const token = getFigmaToken();\n if (!token) {\n throw new Error('未配置 Figma Token。请通过 --figma-token 参数或 FIGMA_TOKEN 环境变量配置。');\n }\n\n if (!fs.existsSync(targetDir)) {\n fs.mkdirSync(targetDir, { recursive: true });\n }\n\n const { fileKey, nodeId, scales, format } = options;\n const results: FigmaExportResult[] = [];\n\n for (const scale of scales) {\n const images = await this.getImageUrls(fileKey, nodeId, scale, format, token);\n const imageEntries = Object.entries(images);\n\n if (imageEntries.length === 0) {\n throw new Error(`Figma 未返回 scale=${scale} 的图片 URL,请检查 fileKey 和 nodeId 是否正确`);\n }\n\n for (const [_id, imageUrl] of imageEntries) {\n if (!imageUrl) {\n throw new Error(`Figma 返回了空的图片 URL (scale=${scale}),节点可能不支持导出`);\n }\n\n const suffix = scale === 1 ? '' : `@${scale}x`;\n const prefix = fileNamePrefix || nodeId.replace(/[:/]/g, '-');\n const fileName = `${prefix}${suffix}.${format}`;\n const localPath = path.join(targetDir, fileName);\n\n await httpsDownload(imageUrl, localPath);\n\n results.push({\n scale,\n imageUrl,\n localPath,\n fileName,\n });\n }\n }\n\n return results;\n }\n}\n\nexport const figmaService = new FigmaService();\n"],"mappings":";;;AAOA,SAAS,4BAA4B;;;ACPrC,SAAS,iBAAiB;AAC1B,SAAS,KAAAA,UAAS;;;ACDlB,OAAO,SAAS;AAChB,OAAO,QAAQ;AACf,OAAO,UAAU;;;ACFjB,SAAS,cAAc;AACvB,OAAO,WAAW;AAClB,SAAS,eAAe;AACxB,SAAS,SAAS;AAElB,OAAO;AAGA,IAAM,kBAAkB,EAAE,OAAO;AAAA,EACtC,QAAQ,EAAE,OAAO;AAAA,EACjB,aAAa,EAAE,OAAO;AAAA,EACtB,iBAAiB,EAAE,OAAO;AAAA,EAC1B,QAAQ,EAAE,OAAO;AAAA,EACjB,UAAU,EAAE,OAAO;AACrB,CAAC;AAkBD,SAAS,WAAW,QAAwB;AAC1C,MAAI,OAAO,UAAU,EAAG,QAAO;AAC/B,SAAO,GAAG,OAAO,UAAU,GAAG,CAAC,CAAC,OAAO,OAAO,MAAM,EAAE,CAAC;AACzD;AAGO,SAAS,gBAAgB,cAAuB,OAAqB;AAE1E,QAAM,OAAO,MAAM,QAAQ,QAAQ,IAAI,CAAC,EACrC,QAAQ;AAAA,IACP,cAAc;AAAA,MACZ,MAAM;AAAA,MACN,aAAa;AAAA,IACf;AAAA,IACA,MAAM;AAAA,MACJ,MAAM;AAAA,MACN,aAAa;AAAA,MACb,SAAS;AAAA,IACX;AAAA,IACA,eAAe;AAAA,MACb,MAAM;AAAA,MACN,aAAa;AAAA,IACf;AAAA,EACF,CAAC,EACA,KAAK,EACL,QAAQ,OAAO,EACf,UAAU;AAEb,QAAMC,UAAuB;AAAA,IAC3B,MAAM;AAAA,IACN,WAAW,CAAC;AAAA,IACZ,eAAe;AAAA,MACb,MAAM;AAAA,MACN,WAAW;AAAA,MACX,YAAY;AAAA,IACd;AAAA,EACF;AAGA,MAAI,KAAK,MAAM;AACb,IAAAA,QAAO,OAAO,KAAK;AACnB,IAAAA,QAAO,cAAc,OAAO;AAAA,EAC9B,WAAW,QAAQ,IAAI,MAAM;AAC3B,IAAAA,QAAO,OAAO,SAAS,QAAQ,IAAI,MAAM,EAAE;AAC3C,IAAAA,QAAO,cAAc,OAAO;AAAA,EAC9B;AAGA,MAAI,KAAK,YAAY,GAAG;AACtB,UAAM,gBAAgB,KAAK,MAAM,KAAK,YAAY,CAAW;AAE5D,QAAI,cAAc,UAAU,cAAc,aAAa;AACrD,MAAAA,QAAO,UAAU,UAAU,gBAAgB,MAAM,aAAa;AAAA,IAChE,OAAO;AACL,aAAO,QAAQ,aAAa,EAAE,QAAQ,CAAC,CAAC,MAAM,GAAG,MAAM;AACrD,QAAAA,QAAO,UAAU,KAAK,YAAY,CAAC,IAAI,gBAAgB,MAAM,GAAG;AAAA,MAClE,CAAC;AAAA,IACH;AACA,IAAAA,QAAO,cAAc,YAAY;AAAA,EACpC,WAAW,QAAQ,IAAI,oBAAoB;AACzC,UAAM,YAAY,KAAK,MAAM,QAAQ,IAAI,kBAAkB;AAC3D,IAAAA,QAAO,UAAU,UAAU,gBAAgB,MAAM,SAAS;AAC1D,IAAAA,QAAO,cAAc,YAAY;AAAA,EACnC;AAGA,SAAO,QAAQ,QAAQ,GAAG,EAAE,QAAQ,CAAC,CAAC,KAAK,KAAK,MAAM;AACpD,QAAI,IAAI,WAAW,aAAa,KAAK,QAAQ,wBAAwB,OAAO;AAC1E,UAAI;AACF,cAAM,aAAa,IAAI,QAAQ,eAAe,EAAE,EAAE,YAAY;AAC9D,cAAM,YAAY,KAAK,MAAM,KAAK;AAClC,QAAAA,QAAO,UAAU,UAAU,IAAI,gBAAgB,MAAM,SAAS;AAAA,MAChE,SAAS,OAAO;AACd,gBAAQ,MAAM,uCAAS,GAAG,iBAAO,KAAK;AAAA,MACxC;AAAA,IACF;AAAA,EACF,CAAC;AAGD,MAAI,KAAK,aAAa,GAAG;AACvB,IAAAA,QAAO,aAAa,KAAK,aAAa;AACtC,IAAAA,QAAO,cAAc,aAAa;AAAA,EACpC,WAAW,QAAQ,IAAI,aAAa;AAClC,IAAAA,QAAO,aAAa,QAAQ,IAAI;AAChC,IAAAA,QAAO,cAAc,aAAa;AAAA,EACpC;AAGA,MAAI,OAAO,KAAKA,QAAO,SAAS,EAAE,WAAW,GAAG;AAC9C,YAAQ,KAAK,iKAA+B;AAAA,EAC9C;AAGA,MAAI,CAAC,aAAa;AAChB,YAAQ,IAAI,6BAAS;AACrB,YAAQ,IAAI,mBAASA,QAAO,IAAI,mBAASA,QAAO,cAAc,IAAI,GAAG;AAErE,QAAI,OAAO,KAAKA,QAAO,SAAS,EAAE,SAAS,GAAG;AAC5C,cAAQ,IAAI,oBAAU;AACtB,aAAO,QAAQA,QAAO,SAAS,EAAE,QAAQ,CAAC,CAAC,MAAM,GAAG,MAAM;AACxD,gBAAQ,IAAI,OAAO,IAAI,GAAG;AAC1B,gBAAQ,IAAI,eAAe,IAAI,MAAM,EAAE;AACvC,gBAAQ,IAAI,iBAAiB,IAAI,QAAQ,EAAE;AAC3C,gBAAQ,IAAI,eAAe,IAAI,MAAM,EAAE;AACvC,gBAAQ,IAAI,oBAAoB,WAAW,IAAI,WAAW,CAAC,EAAE;AAC7D,gBAAQ,IAAI,wBAAwB,WAAW,IAAI,eAAe,CAAC,EAAE;AAAA,MACvE,CAAC;AAAA,IACH,OAAO;AACL,cAAQ,IAAI,uCAAc;AAAA,IAC5B;AAEA,QAAIA,QAAO,YAAY;AACrB,cAAQ,IAAI,kBAAkB,WAAWA,QAAO,UAAU,CAAC,mBAASA,QAAO,cAAc,UAAU,GAAG;AAAA,IACxG,OAAO;AACL,cAAQ,IAAI,+FAAmC;AAAA,IACjD;AAEA,YAAQ,IAAI;AAAA,EACd;AAEA,SAAOA;AACT;AAGA,IAAI;AAGG,SAAS,gBAAoC;AAClD,MAAI,qBAAqB,OAAW,QAAO,oBAAoB;AAC/D,QAAM,EAAE,WAAW,IAAI,gBAAgB,IAAI;AAC3C,qBAAmB,cAAc;AACjC,SAAO;AACT;AAGO,SAAS,mBAA8C;AAC5D,QAAM,EAAE,UAAU,IAAI,gBAAgB,IAAI;AAC1C,SAAO;AACT;AAGO,SAAS,aAAa,OAAe,WAA6B;AACvE,QAAM,UAAU,iBAAiB;AACjC,QAAM,iBAAiB,KAAK,YAAY;AACxC,SAAO,QAAQ,cAAc,KAAK;AACpC;;;AD7KA,SAAS,KAAAC,UAAS;AAGX,IAAM,yBAAyBA,GAAE,OAAO;AAAA,EAC7C,UAAUA,GAAE,OAAO;AAAA,EACnB,WAAWA,GAAE,OAAO,EAAE,SAAS;AAAA,EAC/B,UAAUA,GAAE,OAAO,EAAE,SAAS;AAAA,EAC9B,YAAYA,GAAE,OAAO,EAAE,SAAS;AAClC,CAAC;AAMM,IAAM,qBAAqBA,GAAE,OAAO;AAAA,EACzC,SAASA,GAAE,QAAQ;AAAA,EACnB,KAAKA,GAAE,OAAO,EAAE,SAAS;AAAA,EACzB,OAAOA,GAAE,OAAO,EAAE,SAAS;AAAA,EAC3B,eAAeA,GAAE,OAAO,EAAE,SAAS;AACrC,CAAC;AAgBM,IAAM,aAAN,MAAiB;AAAA,EACd,UAA4B,oBAAI,IAAI;AAAA;AAAA;AAAA;AAAA;AAAA,EAM5C,aAAkC;AAChC,UAAM,UAA+B,CAAC;AACtC,UAAM,aAAa,iBAAiB;AAEpC,eAAW,CAAC,IAAIC,OAAM,KAAK,OAAO,QAAQ,UAAU,GAAG;AACrD,cAAQ,KAAK;AAAA,QACX;AAAA,QACA,MAAM,GAAG,GAAG,OAAO,CAAC,EAAE,YAAY,CAAC,GAAG,GAAG,MAAM,CAAC,CAAC;AAAA,QACjD,GAAGA;AAAA,MACL,CAAC;AAAA,IACH;AAEA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOQ,UAAU,aAAqB,WAAuB;AAE5D,QAAI,KAAK,QAAQ,IAAI,UAAU,GAAG;AAChC,aAAO,KAAK,QAAQ,IAAI,UAAU;AAAA,IACpC;AAGA,UAAMA,UAAS,aAAa,UAAU;AACtC,QAAI,CAACA,SAAQ;AACX,aAAO;AAAA,IACT;AAEA,QAAI;AACF,YAAM,SAAS,IAAI,IAAI;AAAA,QACrB,QAAQA,QAAO;AAAA,QACf,aAAaA,QAAO;AAAA,QACpB,iBAAiBA,QAAO;AAAA,QACxB,QAAQA,QAAO;AAAA,QACf,UAAUA,QAAO;AAAA,MACnB,CAAC;AAGD,WAAK,QAAQ,IAAI,YAAY,MAAM;AACnC,aAAO;AAAA,IACT,SAAS,OAAO;AACd,cAAQ,MAAM,mCAAmC,UAAU,KAAK,KAAK;AACrE,aAAO;AAAA,IACT;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,WAAW,QAAiD;AAEhE,UAAM,cAAc,uBAAuB,MAAM,MAAM;AACvD,UAAM,EAAE,UAAU,YAAY,IAAI,UAAU,aAAa,UAAU,IAAI;AAEvE,QAAI;AAEF,UAAI,CAAC,GAAG,WAAW,QAAQ,GAAG;AAC5B,eAAO,mBAAmB,MAAM;AAAA,UAC9B,SAAS;AAAA,UACT,OAAO,mBAAmB,QAAQ;AAAA,UAClC,eAAe;AAAA,QACjB,CAAC;AAAA,MACH;AAGA,YAAM,SAAS,KAAK,UAAU,UAAU;AACxC,UAAI,CAAC,QAAQ;AACX,eAAO,mBAAmB,MAAM;AAAA,UAC9B,SAAS;AAAA,UACT,OAAO,6BAA6B,UAAU;AAAA,UAC9C,eAAe;AAAA,QACjB,CAAC;AAAA,MACH;AAGA,YAAM,iBAAiB,YAAY,KAAK,SAAS,QAAQ;AAGzD,UAAI,UAAU;AACd,UAAI,WAAW;AAEb,cAAM,gBAAgB,UAAU,QAAQ,cAAc,EAAE;AACxD,kBAAU,gBAAgB,GAAG,aAAa,IAAI,cAAc,KAAK;AAAA,MACnE;AAGA,YAAM,SAAS,MAAM,OAAO,IAAI,SAAS,QAAQ;AAEjD,aAAO,mBAAmB,MAAM;AAAA,QAC9B,SAAS;AAAA,QACT,KAAK,OAAO;AAAA,QACZ,eAAe;AAAA,MACjB,CAAC;AAAA,IACH,SAAS,OAAO;AACd,aAAO,mBAAmB,MAAM;AAAA,QAC9B,SAAS;AAAA,QACT,OAAO,kBAAmB,MAAgB,OAAO;AAAA,QACjD,eAAe;AAAA,MACjB,CAAC;AAAA,IACH;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,MAAM,WAAW,QAAgB,QAAgB,aAAqB,WAA0D;AAC9H,QAAI;AACF,YAAM,SAAS,KAAK,UAAU,UAAU;AACxC,UAAI,CAAC,QAAQ;AACX,eAAO,EAAE,SAAS,OAAO,OAAO,6BAA6B,UAAU,GAAG;AAAA,MAC5E;AAGA,YAAM,mBAAmB,OAAO,QAAQ,QAAQ,EAAE;AAClD,YAAM,mBAAmB,OAAO,QAAQ,QAAQ,EAAE;AAGlD,UAAI;AACF,cAAM,OAAO,KAAK,gBAAgB;AAAA,MACpC,SAAS,IAAI;AACX,eAAO,EAAE,SAAS,OAAO,OAAO,yCAAW,gBAAgB,GAAG;AAAA,MAChE;AAGA,UAAI;AACF,cAAM,OAAO,KAAK,gBAAgB;AAElC,YAAI,qBAAqB,kBAAkB;AACzC,iBAAO,EAAE,SAAS,OAAO,OAAO,+CAAY,gBAAgB,GAAG;AAAA,QACjE;AAAA,MACF,SAAS,IAAI;AAAA,MAEb;AAGA,YAAM,OAAO,KAAK,kBAAkB,gBAAgB;AAGpD,YAAM,OAAO,OAAO,gBAAgB;AAEpC,aAAO,EAAE,SAAS,KAAK;AAAA,IACzB,SAAS,OAAO;AACd,aAAO,EAAE,SAAS,OAAO,OAAO,mCAAW,MAAgB,OAAO,GAAG;AAAA,IACvE;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,MAAM,UACJ,YAAoB,IACpB,aAAqB,WACrB,SACkH;AAClH,QAAI;AACF,YAAM,SAAS,KAAK,UAAU,UAAU;AACxC,UAAI,CAAC,QAAQ;AACX,eAAO,EAAE,SAAS,OAAO,OAAO,6BAA6B,UAAU,GAAG;AAAA,MAC5E;AAGA,YAAM,gBAAgB,UAAU,QAAQ,cAAc,EAAE;AACxD,YAAM,SAAS,gBAAgB,GAAG,aAAa,MAAM;AAGrD,YAAM,SAAS,MAAM,OAAO,KAAK;AAAA,QAC/B;AAAA,QACA,WAAW;AAAA,QACX,YAAY;AAAA,MACd,GAAG,CAAC,CAAC;AAEL,YAAM,QAAmE,CAAC;AAG1E,UAAI,OAAO,SAAS;AAClB,mBAAW,OAAO,OAAO,SAAS;AAEhC,cAAI,IAAI,KAAK,SAAS,GAAG,EAAG;AAG5B,gBAAM,WAAW,IAAI,KAAK,QAAQ,QAAQ,EAAE;AAC5C,cAAI,CAAC,SAAU;AAGf,cAAI,SAAS;AACX,kBAAM,QAAQ,IAAI;AAAA,cAChB,MAAM,QACH,QAAQ,OAAO,KAAK,EACpB,QAAQ,OAAO,IAAI,EACnB,QAAQ,OAAO,GAAG,IAAI;AAAA,cACzB;AAAA,YACF;AACA,gBAAI,CAAC,MAAM,KAAK,QAAQ,EAAG;AAAA,UAC7B;AAEA,gBAAM,KAAK;AAAA,YACT,MAAM;AAAA,YACN,MAAM,IAAI;AAAA,YACV,cAAc,IAAI,KAAK,IAAI,YAAY;AAAA,UACzC,CAAC;AAAA,QACH;AAAA,MACF;AAGA,YAAM,KAAK,CAAC,GAAG,MAAM,EAAE,KAAK,cAAc,EAAE,IAAI,CAAC;AAEjD,aAAO,EAAE,SAAS,MAAM,MAAM;AAAA,IAChC,SAAS,OAAO;AACd,aAAO,EAAE,SAAS,OAAO,OAAO,yCAAY,MAAgB,OAAO,GAAG;AAAA,IACxE;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,MAAM,iBACJ,OACA,YAAoB,IACpB,aAAqB,WACmE;AACxF,UAAM,UAAyF,CAAC;AAGhG,UAAM,gBAAgB,UAAU,QAAQ,cAAc,EAAE;AACxD,UAAM,YAAY,gBAAgB,GAAG,aAAa,MAAM;AAExD,eAAW,QAAQ,OAAO;AACxB,YAAM,SAAS,GAAG,SAAS,GAAG,KAAK,OAAO;AAC1C,YAAM,SAAS,GAAG,SAAS,GAAG,KAAK,OAAO;AAE1C,YAAM,SAAS,MAAM,KAAK,WAAW,QAAQ,QAAQ,UAAU;AAC/D,cAAQ,KAAK;AAAA,QACX,SAAS,KAAK;AAAA,QACd,SAAS,KAAK;AAAA,QACd,SAAS,OAAO;AAAA,QAChB,OAAO,OAAO;AAAA,MAChB,CAAC;AAAA,IACH;AAEA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAM,WAAW,KAAa,aAAqB,WAA0D;AAC3G,QAAI;AACF,YAAM,SAAS,KAAK,UAAU,UAAU;AACxC,UAAI,CAAC,QAAQ;AACX,eAAO,EAAE,SAAS,OAAO,OAAO,6BAA6B,UAAU,GAAG;AAAA,MAC5E;AAGA,YAAM,gBAAgB,IAAI,QAAQ,QAAQ,EAAE;AAG5C,UAAI;AACF,cAAM,OAAO,KAAK,aAAa;AAAA,MACjC,SAAS,IAAI;AACX,eAAO,EAAE,SAAS,OAAO,OAAO,mCAAU,aAAa,GAAG;AAAA,MAC5D;AAGA,YAAM,OAAO,OAAO,aAAa;AAEjC,aAAO,EAAE,SAAS,KAAK;AAAA,IACzB,SAAS,OAAO;AACd,aAAO,EAAE,SAAS,OAAO,OAAO,6BAAU,MAAgB,OAAO,GAAG;AAAA,IACtE;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,MAAM,iBACJ,WACA,YAAoB,IACpB,aAAqB,WACmD;AACxE,UAAM,UAAyE,CAAC;AAGhF,UAAM,gBAAgB,UAAU,QAAQ,cAAc,EAAE;AACxD,UAAM,YAAY,gBAAgB,GAAG,aAAa,MAAM;AAExD,eAAW,YAAY,WAAW;AAChC,YAAM,MAAM,GAAG,SAAS,GAAG,QAAQ;AACnC,YAAM,SAAS,MAAM,KAAK,WAAW,KAAK,UAAU;AACpD,cAAQ,KAAK;AAAA,QACX;AAAA,QACA,SAAS,OAAO;AAAA,QAChB,OAAO,OAAO;AAAA,MAChB,CAAC;AAAA,IACH;AAEA,WAAO;AAAA,EACT;AACF;AAGO,IAAM,aAAa,IAAI,WAAW;;;AEnXzC,OAAO,WAAW;AAClB,OAAOC,SAAQ;AACf,OAAOC,WAAU;AAGjB,IAAM,iBAAiB;AAiBvB,SAAS,SAAS,KAAa,UAAkC,CAAC,GAAoB;AACpF,SAAO,IAAI,QAAQ,CAACC,UAAS,WAAW;AACtC,UAAM,SAAS,IAAI,IAAI,GAAG;AAC1B,UAAM,UAAU;AAAA,MACd,UAAU,OAAO;AAAA,MACjB,MAAM,OAAO,WAAW,OAAO;AAAA,MAC/B,QAAQ;AAAA,MACR;AAAA,IACF;AAEA,UAAM,MAAM,MAAM,QAAQ,SAAS,CAAC,QAAQ;AAC1C,UAAI,IAAI,eAAe,OAAO,IAAI,eAAe,KAAK;AACpD,cAAM,cAAc,IAAI,QAAQ;AAChC,YAAI,aAAa;AACf,mBAAS,aAAa,OAAO,EAAE,KAAKA,QAAO,EAAE,MAAM,MAAM;AACzD;AAAA,QACF;AAAA,MACF;AAEA,UAAI,IAAI,eAAe,KAAK;AAC1B,YAAIC,QAAO;AACX,YAAI,GAAG,QAAQ,CAAC,UAAWA,SAAQ,KAAM;AACzC,YAAI,GAAG,OAAO,MAAM,OAAO,IAAI,MAAM,uCAAmB,IAAI,UAAU,MAAMA,KAAI,EAAE,CAAC,CAAC;AACpF;AAAA,MACF;AAEA,UAAI,OAAO;AACX,UAAI,GAAG,QAAQ,CAAC,UAAW,QAAQ,KAAM;AACzC,UAAI,GAAG,OAAO,MAAMD,SAAQ,IAAI,CAAC;AAAA,IACnC,CAAC;AAED,QAAI,GAAG,SAAS,MAAM;AACtB,QAAI,WAAW,KAAO,MAAM;AAC1B,UAAI,QAAQ;AACZ,aAAO,IAAI,MAAM,wDAAqB,CAAC;AAAA,IACzC,CAAC;AACD,QAAI,IAAI;AAAA,EACV,CAAC;AACH;AAEA,SAAS,cAAc,KAAa,UAAiC;AACnE,SAAO,IAAI,QAAQ,CAACA,UAAS,WAAW;AACtC,UAAM,SAAS,IAAI,IAAI,GAAG;AAC1B,UAAM,WAAW;AAEjB,UAAM,UAAU,SAAS,IAAI,KAAK,CAAC,aAAa;AAC9C,UAAI,SAAS,eAAe,OAAO,SAAS,eAAe,KAAK;AAC9D,cAAM,cAAc,SAAS,QAAQ;AACrC,YAAI,aAAa;AACf,wBAAc,aAAa,QAAQ,EAAE,KAAKA,QAAO,EAAE,MAAM,MAAM;AAC/D;AAAA,QACF;AAAA,MACF;AAEA,UAAI,SAAS,eAAe,KAAK;AAC/B,eAAO,IAAI,MAAM,0DAAkB,SAAS,UAAU,EAAE,CAAC;AACzD;AAAA,MACF;AAEA,YAAM,aAAaE,IAAG,kBAAkB,QAAQ;AAChD,eAAS,KAAK,UAAU;AAExB,iBAAW,GAAG,UAAU,MAAM;AAC5B,mBAAW,MAAM;AACjB,QAAAF,SAAQ;AAAA,MACV,CAAC;AAED,iBAAW,GAAG,SAAS,CAAC,QAAQ;AAC9B,QAAAE,IAAG,OAAO,UAAU,MAAM;AAAA,QAAC,CAAC;AAC5B,eAAO,GAAG;AAAA,MACZ,CAAC;AAAA,IACH,CAAC;AAED,YAAQ,GAAG,SAAS,CAAC,QAAQ;AAC3B,MAAAA,IAAG,OAAO,UAAU,MAAM;AAAA,MAAC,CAAC;AAC5B,aAAO,GAAG;AAAA,IACZ,CAAC;AAED,YAAQ,WAAW,KAAO,MAAM;AAC9B,cAAQ,QAAQ;AAChB,MAAAA,IAAG,OAAO,UAAU,MAAM;AAAA,MAAC,CAAC;AAC5B,aAAO,IAAI,MAAM,8CAAW,CAAC;AAAA,IAC/B,CAAC;AAAA,EACH,CAAC;AACH;AAEO,IAAM,eAAN,MAAmB;AAAA;AAAA;AAAA;AAAA,EAIxB,MAAM,aACJ,SACA,QACA,OACA,QACA,OACiC;AACjC,UAAM,gBAAgB,mBAAmB,MAAM;AAC/C,UAAM,MAAM,GAAG,cAAc,WAAW,OAAO,QAAQ,aAAa,UAAU,KAAK,WAAW,MAAM;AAEpG,UAAM,eAAe,MAAM,SAAS,KAAK;AAAA,MACvC,iBAAiB;AAAA,IACnB,CAAC;AAED,UAAM,OAAO,KAAK,MAAM,YAAY;AAEpC,QAAI,KAAK,KAAK;AACZ,YAAM,IAAI,MAAM,2BAAiB,KAAK,GAAG,EAAE;AAAA,IAC7C;AAEA,WAAO,KAAK,UAAU,CAAC;AAAA,EACzB;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,cACJ,SACA,WACA,gBAC8B;AAC9B,UAAM,QAAQ,cAAc;AAC5B,QAAI,CAAC,OAAO;AACV,YAAM,IAAI,MAAM,gJAA2D;AAAA,IAC7E;AAEA,QAAI,CAACA,IAAG,WAAW,SAAS,GAAG;AAC7B,MAAAA,IAAG,UAAU,WAAW,EAAE,WAAW,KAAK,CAAC;AAAA,IAC7C;AAEA,UAAM,EAAE,SAAS,QAAQ,QAAQ,OAAO,IAAI;AAC5C,UAAM,UAA+B,CAAC;AAEtC,eAAW,SAAS,QAAQ;AAC1B,YAAM,SAAS,MAAM,KAAK,aAAa,SAAS,QAAQ,OAAO,QAAQ,KAAK;AAC5E,YAAM,eAAe,OAAO,QAAQ,MAAM;AAE1C,UAAI,aAAa,WAAW,GAAG;AAC7B,cAAM,IAAI,MAAM,kCAAmB,KAAK,gGAAoC;AAAA,MAC9E;AAEA,iBAAW,CAAC,KAAK,QAAQ,KAAK,cAAc;AAC1C,YAAI,CAAC,UAAU;AACb,gBAAM,IAAI,MAAM,+DAA4B,KAAK,+DAAa;AAAA,QAChE;AAEA,cAAM,SAAS,UAAU,IAAI,KAAK,IAAI,KAAK;AAC3C,cAAM,SAAS,kBAAkB,OAAO,QAAQ,SAAS,GAAG;AAC5D,cAAM,WAAW,GAAG,MAAM,GAAG,MAAM,IAAI,MAAM;AAC7C,cAAM,YAAYC,MAAK,KAAK,WAAW,QAAQ;AAE/C,cAAM,cAAc,UAAU,SAAS;AAEvC,gBAAQ,KAAK;AAAA,UACX;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,QACF,CAAC;AAAA,MACH;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AACF;AAEO,IAAM,eAAe,IAAI,aAAa;;;AHvL7C,OAAO,aAAoC;AAC3C,SAAS,0BAA0B;AAGnC,OAAOC,SAAQ;AACf,OAAOC,WAAU;AACjB,OAAOC,YAAW;AAClB,OAAO,UAAU;AAEV,IAAM,SAAS;AAAA,EACpB,KAAK,IAAI,SAAgB;AACvB,YAAQ,IAAI,GAAG,IAAI;AAAA,EACrB;AAAA,EACA,OAAO,IAAI,SAAgB;AACzB,YAAQ,MAAM,GAAG,IAAI;AAAA,EACvB;AACF;AAEO,IAAM,eAAN,MAAmB;AAAA,EACP;AAAA,EACT,eAA0C;AAAA,EAElD,cAAc;AACZ,SAAK,SAAS,IAAI;AAAA,MAChB;AAAA,QACE,MAAM;AAAA,QACN,SAAS;AAAA,MACX;AAAA;AAAA,MAEA;AAAA,QACE,cAAc;AAAA,UACZ,OAAO,EAAE,aAAa,KAAK;AAAA,UAC3B,WAAW,EAAE,aAAa,KAAK;AAAA,UAC/B,SAAS,EAAE,aAAa,KAAK;AAAA,UAC7B,SAAS,CAAC;AAAA,QACZ;AAAA,MACF;AAAA,IACF;AAEA,SAAK,cAAc;AAAA,EACrB;AAAA,EAEQ,gBAAsB;AAE5B,UAAM,UAAU,WAAW,WAAW;AACtC,UAAM,cAAc,QAAQ,IAAI,CAAAC,YAAUA,QAAO,EAAE;AAGnD,SAAK,OAAO;AAAA,MACV;AAAA,MACA;AAAA,MACA;AAAA,QACE,UAAUC,GAAE,OAAO,EAAE,SAAS,8DAAY;AAAA,QAC1C,WAAWA,GAAE,OAAO,EAAE,SAAS,EAAE,SAAS,6EAAiB;AAAA,QAC3D,UAAUA,GAAE,OAAO,EAAE,SAAS,EAAE,SAAS,0HAAsB;AAAA,QAC/D,YAAYA,GAAE,OAAO,EAAE,SAAS,EAAE,SAAS,uHAAkC,YAAY,KAAK,IAAI,KAAK,QAAG,EAAE;AAAA,MAC9G;AAAA,MACA,OAAO,EAAE,UAAU,WAAW,UAAU,WAAW,MAAM;AACvD,YAAI;AACF,iBAAO,IAAI,6BAAS,QAAQ,WAAM,aAAa,oBAAK,EAAE;AAEtD,cAAI,CAAC,UAAU;AACb,kBAAM,IAAI,MAAM,kDAAU;AAAA,UAC5B;AAGA,cAAI,CAACJ,IAAG,WAAW,QAAQ,GAAG;AAC5B,kBAAM,IAAI,MAAM,mCAAU,QAAQ,EAAE;AAAA,UACtC;AAGA,gBAAM,SAAS,MAAM,WAAW,WAAW;AAAA,YACzC;AAAA,YACA;AAAA,YACA;AAAA,YACA;AAAA,UACF,CAAC;AAED,cAAI,OAAO,SAAS;AAClB,mBAAO,IAAI,6BAAS,OAAO,GAAG,EAAE;AAChC,mBAAO;AAAA,cACL,SAAS,CAAC;AAAA,gBACR,MAAM;AAAA,gBACN,MAAM;AAAA,sBAAiBC,MAAK,SAAS,QAAQ,CAAC;AAAA,4BAAW,aAAa,oBAAK;AAAA,OAAU,OAAO,GAAG;AAAA,4BAAW,OAAO,aAAa;AAAA,cAChI,CAAC;AAAA,YACH;AAAA,UACF,OAAO;AACL,mBAAO,MAAM,6BAAS,OAAO,KAAK,EAAE;AACpC,mBAAO;AAAA,cACL,SAAS;AAAA,cACT,SAAS,CAAC;AAAA,gBACR,MAAM;AAAA,gBACN,MAAM,6BAAS,OAAO,KAAK;AAAA,cAC7B,CAAC;AAAA,YACH;AAAA,UACF;AAAA,QACF,SAAS,OAAO;AACd,iBAAO,MAAM,+CAAY,KAAK;AAC9B,iBAAO;AAAA,YACL,SAAS;AAAA,YACT,SAAS,CAAC;AAAA,cACR,MAAM;AAAA,cACN,MAAM,6BAAS,KAAK;AAAA,YACtB,CAAC;AAAA,UACH;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAGA,SAAK,OAAO;AAAA,MACV;AAAA,MACA;AAAA,MACA,CAAC;AAAA,MACD,YAAY;AACV,YAAI;AACF,gBAAMI,WAAU,WAAW,WAAW;AACtC,gBAAMC,eAAcD,SAAQ,IAAI,CAAAF,YAAUA,QAAO,EAAE;AAEnD,cAAIG,aAAY,WAAW,GAAG;AAC5B,mBAAO;AAAA,cACL,SAAS,CAAC;AAAA,gBACR,MAAM;AAAA,gBACN,MAAM;AAAA,cACR,CAAC;AAAA,YACH;AAAA,UACF;AAEA,iBAAO;AAAA,YACL,SAAS,CAAC;AAAA,cACR,MAAM;AAAA,cACN,MAAM;AAAA,EAAcA,aAAY,IAAI,UAAQ,KAAK,IAAI,EAAE,EAAE,KAAK,IAAI,CAAC;AAAA,YACrE,CAAC;AAAA,UACH;AAAA,QACF,SAAS,OAAO;AACd,iBAAO,MAAM,8DAAiB,KAAK;AACnC,iBAAO;AAAA,YACL,SAAS;AAAA,YACT,SAAS,CAAC;AAAA,cACR,MAAM;AAAA,cACN,MAAM,qDAAa,KAAK;AAAA,YAC1B,CAAC;AAAA,UACH;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAGA,SAAK,OAAO;AAAA,MACV;AAAA,MACA;AAAA,MACA;AAAA,QACE,WAAWF,GAAE,OAAO,EAAE,SAAS,mIAAyC;AAAA,QACxE,aAAaA,GAAE,MAAMA,GAAE,OAAO;AAAA,UAC5B,SAASA,GAAE,OAAO,EAAE,SAAS,0BAAM;AAAA,UACnC,SAASA,GAAE,OAAO,EAAE,SAAS,0BAAM;AAAA,QACrC,CAAC,CAAC,EAAE,SAAS,gIAAuB;AAAA,QACpC,YAAYA,GAAE,OAAO,EAAE,SAAS,EAAE,SAAS,qGAA+B,YAAY,KAAK,IAAI,KAAK,QAAG,EAAE;AAAA,QACzG,QAAQA,GAAE,QAAQ,EAAE,SAAS,EAAE,SAAS,qMAA0C;AAAA,MACpF;AAAA,MACA,OAAO,EAAE,WAAW,aAAa,aAAa,WAAW,SAAS,MAAM,MAAM;AAC5E,YAAI;AACF,iBAAO,IAAI,mDAAgB,SAAS,wBAAS,YAAY,MAAM,kBAAQ,UAAU,8BAAU,MAAM,EAAE;AAEnG,cAAI;AAEJ,cAAI,QAAQ;AAEV,sBAAU,YAAY,IAAI,WAAS;AAAA,cACjC,SAAS,KAAK;AAAA,cACd,SAAS,KAAK;AAAA,cACd,SAAS;AAAA,YACX,EAAE;AAAA,UACJ,OAAO;AAEL,sBAAU,MAAM,WAAW,iBAAiB,aAAa,WAAW,UAAU;AAAA,UAChF;AAEA,gBAAM,eAAe,QAAQ,OAAO,OAAK,EAAE,OAAO,EAAE;AACpD,gBAAM,YAAY,QAAQ,OAAO,OAAK,CAAC,EAAE,OAAO,EAAE;AAElD,cAAI,aAAa,SAAS;AAAA;AAAA,IAAkC;AAAA;AAAA;AAC5D,wBAAc,iBAAO,UAAU;AAAA;AAC/B,wBAAc,iBAAO,aAAa,oBAAK;AAAA;AACvC,wBAAc,iBAAO,YAAY,0BAAW,SAAS;AAAA;AAAA;AAErD,cAAI,QAAQ,SAAS,GAAG;AACtB,0BAAc;AACd,uBAAW,KAAK,SAAS;AACvB,kBAAI,EAAE,SAAS;AACb,8BAAc,UAAK,EAAE,OAAO,WAAM,EAAE,OAAO;AAAA;AAAA,cAC7C,OAAO;AACL,8BAAc,UAAK,EAAE,OAAO,WAAM,EAAE,OAAO,KAAK,EAAE,KAAK;AAAA;AAAA,cACzD;AAAA,YACF;AAAA,UACF;AAEA,iBAAO;AAAA,YACL,SAAS,CAAC;AAAA,cACR,MAAM;AAAA,cACN,MAAM;AAAA,YACR,CAAC;AAAA,UACH;AAAA,QACF,SAAS,OAAO;AACd,iBAAO,MAAM,kDAAe,KAAK;AACjC,iBAAO;AAAA,YACL,SAAS;AAAA,YACT,SAAS,CAAC;AAAA,cACR,MAAM;AAAA,cACN,MAAM,kDAAe,KAAK;AAAA,YAC5B,CAAC;AAAA,UACH;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAGA,SAAK,OAAO;AAAA,MACV;AAAA,MACA;AAAA,MACA;AAAA,QACE,WAAWA,GAAE,OAAO,EAAE,SAAS,kDAAU;AAAA,QACzC,SAASA,GAAE,OAAO,EAAE,SAAS,EAAE,SAAS,wGAAkC;AAAA,MAC5E;AAAA,MACA,OAAO,EAAE,WAAW,QAAQ,MAAM;AAChC,YAAI;AACF,iBAAO,IAAI,yCAAW,SAAS,mBAAS,WAAW,QAAG,EAAE;AAGxD,cAAI,CAACJ,IAAG,WAAW,SAAS,GAAG;AAC7B,kBAAM,IAAI,MAAM,mCAAU,SAAS,EAAE;AAAA,UACvC;AAEA,gBAAM,OAAOA,IAAG,SAAS,SAAS;AAClC,cAAI,CAAC,KAAK,YAAY,GAAG;AACvB,kBAAM,IAAI,MAAM,yCAAW,SAAS,EAAE;AAAA,UACxC;AAEA,cAAI,QAAQA,IAAG,YAAY,SAAS;AAGpC,kBAAQ,MAAM,OAAO,OAAK,CAAC,EAAE,WAAW,GAAG,CAAC;AAG5C,cAAI,SAAS;AACX,kBAAM,QAAQ,IAAI;AAAA,cAChB,MAAM,QACH,QAAQ,OAAO,KAAK,EACpB,QAAQ,OAAO,IAAI,EACnB,QAAQ,OAAO,GAAG,IAAI;AAAA,cACzB;AAAA,YACF;AACA,oBAAQ,MAAM,OAAO,OAAK,MAAM,KAAK,CAAC,CAAC;AAAA,UACzC;AAGA,gBAAM,YAAY,MAAM,IAAI,OAAK;AAC/B,kBAAM,WAAWC,MAAK,KAAK,WAAW,CAAC;AACvC,kBAAM,WAAWD,IAAG,SAAS,QAAQ;AACrC,mBAAO;AAAA,cACL,MAAM;AAAA,cACN,aAAa,SAAS,YAAY;AAAA,cAClC,MAAM,SAAS;AAAA,YACjB;AAAA,UACF,CAAC;AAGD,oBAAU,KAAK,CAAC,GAAG,MAAM;AACvB,gBAAI,EAAE,gBAAgB,EAAE,aAAa;AACnC,qBAAO,EAAE,cAAc,KAAK;AAAA,YAC9B;AACA,mBAAO,EAAE,KAAK,cAAc,EAAE,IAAI;AAAA,UACpC,CAAC;AAED,cAAI,UAAU,WAAW,GAAG;AAC1B,mBAAO;AAAA,cACL,SAAS,CAAC;AAAA,gBACR,MAAM;AAAA,gBACN,MAAM,gBAAM,SAAS,gEAAc,UAAU,mBAAS,OAAO,MAAM,EAAE;AAAA,cACvE,CAAC;AAAA,YACH;AAAA,UACF;AAEA,cAAI,aAAa,iBAAO,SAAS;AAAA;AACjC,cAAI,SAAS;AACX,0BAAc,iBAAO,OAAO;AAAA;AAAA,UAC9B;AACA,wBAAc,UAAK,UAAU,MAAM;AAAA;AAAA;AAEnC,qBAAW,KAAK,WAAW;AACzB,gBAAI,EAAE,aAAa;AACjB,4BAAc,aAAM,EAAE,IAAI;AAAA;AAAA,YAC5B,OAAO;AACL,oBAAM,UAAU,EAAE,OAAO,OACrB,GAAG,EAAE,IAAI,MACT,EAAE,OAAO,OAAO,OACd,IAAI,EAAE,OAAO,MAAM,QAAQ,CAAC,CAAC,OAC7B,IAAI,EAAE,OAAO,OAAO,MAAM,QAAQ,CAAC,CAAC;AAC1C,4BAAc,aAAM,EAAE,IAAI,KAAK,OAAO;AAAA;AAAA,YACxC;AAAA,UACF;AAEA,iBAAO;AAAA,YACL,SAAS,CAAC;AAAA,cACR,MAAM;AAAA,cACN,MAAM;AAAA,YACR,CAAC;AAAA,UACH;AAAA,QACF,SAAS,OAAO;AACd,iBAAO,MAAM,qDAAa,KAAK;AAC/B,iBAAO;AAAA,YACL,SAAS;AAAA,YACT,SAAS,CAAC;AAAA,cACR,MAAM;AAAA,cACN,MAAM,yCAAW,KAAK;AAAA,YACxB,CAAC;AAAA,UACH;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAGA,SAAK,OAAO;AAAA,MACV;AAAA,MACA;AAAA,MACA;AAAA,QACE,WAAWI,GAAE,OAAO,EAAE,SAAS,mIAAyC;AAAA,QACxE,SAASA,GAAE,OAAO,EAAE,SAAS,EAAE,SAAS,wGAAkC;AAAA,QAC1E,YAAYA,GAAE,OAAO,EAAE,SAAS,EAAE,SAAS,qGAA+B,YAAY,KAAK,IAAI,KAAK,QAAG,EAAE;AAAA,MAC3G;AAAA,MACA,OAAO,EAAE,WAAW,SAAS,aAAa,UAAU,MAAM;AACxD,YAAI;AACF,iBAAO,IAAI,4CAAc,aAAa,oBAAK,mBAAS,WAAW,QAAG,mBAAS,UAAU,EAAE;AAEvF,gBAAM,SAAS,MAAM,WAAW,UAAU,WAAW,YAAY,OAAO;AAExE,cAAI,CAAC,OAAO,SAAS;AACnB,mBAAO;AAAA,cACL,SAAS;AAAA,cACT,SAAS,CAAC;AAAA,gBACR,MAAM;AAAA,gBACN,MAAM,4CAAc,OAAO,KAAK;AAAA,cAClC,CAAC;AAAA,YACH;AAAA,UACF;AAEA,gBAAM,QAAQ,OAAO,SAAS,CAAC;AAE/B,cAAI,MAAM,WAAW,GAAG;AACtB,mBAAO;AAAA,cACL,SAAS,CAAC;AAAA,gBACR,MAAM;AAAA,gBACN,MAAM,mBAAS,aAAa,oBAAK,gEAAc,UAAU,mBAAS,OAAO,MAAM,EAAE;AAAA,gBAAS,UAAU;AAAA,cACtG,CAAC;AAAA,YACH;AAAA,UACF;AAEA,gBAAM,UAAU,CAAC,SAAiB,OAAO,OACrC,GAAG,IAAI,MACP,OAAO,OAAO,OACZ,IAAI,OAAO,MAAM,QAAQ,CAAC,CAAC,OAC3B,IAAI,OAAO,OAAO,MAAM,QAAQ,CAAC,CAAC;AAExC,cAAI,aAAa,oBAAU,aAAa,oBAAK;AAAA;AAC7C,wBAAc,iBAAO,UAAU;AAAA;AAC/B,cAAI,SAAS;AACX,0BAAc,iBAAO,OAAO;AAAA;AAAA,UAC9B;AACA,wBAAc,UAAK,MAAM,MAAM;AAAA;AAAA;AAE/B,qBAAW,KAAK,OAAO;AACrB,0BAAc,aAAM,EAAE,IAAI,KAAK,QAAQ,EAAE,IAAI,CAAC;AAAA;AAAA,UAChD;AAEA,iBAAO;AAAA,YACL,SAAS,CAAC;AAAA,cACR,MAAM;AAAA,cACN,MAAM;AAAA,YACR,CAAC;AAAA,UACH;AAAA,QACF,SAAS,OAAO;AACd,iBAAO,MAAM,wDAAgB,KAAK;AAClC,iBAAO;AAAA,YACL,SAAS;AAAA,YACT,SAAS,CAAC;AAAA,cACR,MAAM;AAAA,cACN,MAAM,4CAAc,KAAK;AAAA,YAC3B,CAAC;AAAA,UACH;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAIA,UAAM,uBAAuB,QAAQ,IAAI,2BAA2B;AAEpE,SAAK,OAAO;AAAA,MACV;AAAA,MACA;AAAA;AAAA;AAAA,4BAGE,uBAAuB,8BAAU,wIAAkE;AAAA;AAAA;AAAA,MAGrG;AAAA,QACE,WAAWA,GAAE,OAAO,EAAE,SAAS,mIAAyC;AAAA,QACxE,WAAWA,GAAE,MAAMA,GAAE,OAAO,CAAC,EAAE,SAAS,EAAE,SAAS,qGAA0B;AAAA,QAC7E,SAASA,GAAE,OAAO,EAAE,SAAS,EAAE,SAAS,qIAAgD;AAAA,QACxF,YAAYA,GAAE,OAAO,EAAE,SAAS,EAAE,SAAS,qGAA+B,YAAY,KAAK,IAAI,KAAK,QAAG,EAAE;AAAA,QACzG,QAAQA,GAAE,QAAQ,EAAE,SAAS,EAAE,SAAS,2MAA2C;AAAA,MACrF;AAAA,MACA,OAAO,EAAE,WAAW,WAAW,SAAS,aAAa,WAAW,SAAS,MAAM,MAAM;AACnF,YAAI;AAEF,cAAI,CAAC,sBAAsB;AACzB,mBAAO;AAAA,cACL,SAAS;AAAA,cACT,SAAS,CAAC;AAAA,gBACR,MAAM;AAAA,gBACN,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,cAiBR,CAAC;AAAA,YACH;AAAA,UACF;AAEA,iBAAO,IAAI,6CAAe,SAAS,kBAAQ,UAAU,8BAAU,MAAM,EAAE;AAGvE,cAAI,CAAC,aAAa,CAAC,SAAS;AAC1B,mBAAO;AAAA,cACL,SAAS;AAAA,cACT,SAAS,CAAC;AAAA,gBACR,MAAM;AAAA,gBACN,MAAM;AAAA,cACR,CAAC;AAAA,YACH;AAAA,UACF;AAEA,cAAI,gBAA0B,CAAC;AAE/B,cAAI,aAAa,UAAU,SAAS,GAAG;AAErC,4BAAgB;AAAA,UAClB,WAAW,SAAS;AAElB,kBAAM,aAAa,MAAM,WAAW,UAAU,WAAW,YAAY,OAAO;AAC5E,gBAAI,CAAC,WAAW,SAAS;AACvB,qBAAO;AAAA,gBACL,SAAS;AAAA,gBACT,SAAS,CAAC;AAAA,kBACR,MAAM;AAAA,kBACN,MAAM,yCAAW,WAAW,KAAK;AAAA,gBACnC,CAAC;AAAA,cACH;AAAA,YACF;AACA,6BAAiB,WAAW,SAAS,CAAC,GAAG,IAAI,OAAK,EAAE,IAAI;AAAA,UAC1D;AAEA,cAAI,cAAc,WAAW,GAAG;AAC9B,mBAAO;AAAA,cACL,SAAS,CAAC;AAAA,gBACR,MAAM;AAAA,gBACN,MAAM,yDAAY,UAAU,mBAAS,OAAO,MAAM,EAAE;AAAA,gBAAS,aAAa,oBAAK;AAAA,gBAAS,UAAU;AAAA,cACpG,CAAC;AAAA,YACH;AAAA,UACF;AAEA,cAAI,QAAQ;AAEV,gBAAIG,cAAa;AAAA;AAAA;AACjB,YAAAA,eAAc,iBAAO,UAAU;AAAA;AAC/B,YAAAA,eAAc,iBAAO,aAAa,oBAAK;AAAA;AACvC,gBAAI,SAAS;AACX,cAAAA,eAAc,6BAAS,OAAO;AAAA;AAAA,YAChC;AACA,YAAAA,eAAc,6BAAS,cAAc,MAAM;AAAA;AAAA;AAC3C,YAAAA,eAAc;AAAA;AACd,uBAAW,YAAY,eAAe;AACpC,cAAAA,eAAc,mBAAO,QAAQ;AAAA;AAAA,YAC/B;AACA,YAAAA,eAAc;AAAA;AAEd,mBAAO;AAAA,cACL,SAAS,CAAC;AAAA,gBACR,MAAM;AAAA,gBACN,MAAMA;AAAA,cACR,CAAC;AAAA,YACH;AAAA,UACF;AAGA,gBAAM,UAAU,MAAM,WAAW,iBAAiB,eAAe,WAAW,UAAU;AAEtF,gBAAM,eAAe,QAAQ,OAAO,OAAK,EAAE,OAAO,EAAE;AACpD,gBAAM,YAAY,QAAQ,OAAO,OAAK,CAAC,EAAE,OAAO,EAAE;AAElD,cAAI,aAAa;AAAA;AAAA;AACjB,wBAAc,iBAAO,UAAU;AAAA;AAC/B,wBAAc,iBAAO,aAAa,oBAAK;AAAA;AACvC,wBAAc,iBAAO,YAAY,0BAAW,SAAS;AAAA;AAAA;AAErD,cAAI,QAAQ,SAAS,GAAG;AACtB,0BAAc;AACd,uBAAW,KAAK,SAAS;AACvB,kBAAI,EAAE,SAAS;AACb,8BAAc,UAAK,EAAE,QAAQ;AAAA;AAAA,cAC/B,OAAO;AACL,8BAAc,UAAK,EAAE,QAAQ,KAAK,EAAE,KAAK;AAAA;AAAA,cAC3C;AAAA,YACF;AAAA,UACF;AAEA,iBAAO;AAAA,YACL,SAAS,CAAC;AAAA,cACR,MAAM;AAAA,cACN,MAAM;AAAA,YACR,CAAC;AAAA,UACH;AAAA,QACF,SAAS,OAAO;AACd,iBAAO,MAAM,4CAAc,KAAK;AAChC,iBAAO;AAAA,YACL,SAAS;AAAA,YACT,SAAS,CAAC;AAAA,cACR,MAAM;AAAA,cACN,MAAM,4CAAc,KAAK;AAAA,YAC3B,CAAC;AAAA,UACH;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAGA,SAAK,OAAO;AAAA,MACV;AAAA,MACA;AAAA,MACA;AAAA,QACE,KAAKH,GAAE,OAAO,EAAE,SAAS,0CAAY;AAAA,QACrC,WAAWA,GAAE,OAAO,EAAE,SAAS,oEAAa;AAAA,QAC5C,UAAUA,GAAE,OAAO,EAAE,SAAS,EAAE,SAAS,uGAAuB;AAAA,MAClE;AAAA,MACA,OAAO,EAAE,KAAK,WAAW,SAAS,MAAM;AACtC,YAAI;AACF,iBAAO,IAAI,6BAAS,GAAG,WAAM,SAAS,EAAE;AAGxC,cAAI,CAACJ,IAAG,WAAW,SAAS,GAAG;AAC7B,YAAAA,IAAG,UAAU,WAAW,EAAE,WAAW,KAAK,CAAC;AAC3C,mBAAO,IAAI,6BAAS,SAAS,EAAE;AAAA,UACjC;AAEA,gBAAM,OAAOA,IAAG,SAAS,SAAS;AAClC,cAAI,CAAC,KAAK,YAAY,GAAG;AACvB,kBAAM,IAAI,MAAM,yCAAW,SAAS,EAAE;AAAA,UACxC;AAGA,cAAI,gBAAgB;AACpB,cAAI,CAAC,eAAe;AAClB,kBAAM,SAAS,IAAI,IAAI,GAAG;AAC1B,4BAAgBC,MAAK,SAAS,OAAO,QAAQ;AAE7C,gBAAI,CAAC,iBAAiB,kBAAkB,KAAK;AAC3C,8BAAgB,YAAY,KAAK,IAAI,CAAC;AAAA,YACxC;AAAA,UACF;AAEA,gBAAM,WAAWA,MAAK,KAAK,WAAW,aAAa;AAGnD,cAAID,IAAG,WAAW,QAAQ,GAAG;AAC3B,kBAAM,IAAI,MAAM,mCAAU,QAAQ,EAAE;AAAA,UACtC;AAGA,gBAAM,IAAI,QAAc,CAACQ,UAAS,WAAW;AAC3C,kBAAM,SAAS,IAAI,IAAI,GAAG;AAC1B,kBAAM,WAAW,OAAO,aAAa,WAAWN,SAAQ;AAExD,kBAAM,UAAU,SAAS,IAAI,KAAK,CAAC,aAAa;AAE9C,kBAAI,SAAS,eAAe,OAAO,SAAS,eAAe,KAAK;AAC9D,sBAAM,cAAc,SAAS,QAAQ;AACrC,oBAAI,aAAa;AACf,yBAAO,IAAI,6BAAS,WAAW,EAAE;AACjC,wBAAM,mBAAmB,YAAY,WAAW,QAAQ,IAAIA,SAAQ;AACpE,mCAAiB,IAAI,aAAa,CAAC,qBAAqB;AACtD,wBAAI,iBAAiB,eAAe,KAAK;AACvC,6BAAO,IAAI,MAAM,0DAAkB,iBAAiB,UAAU,EAAE,CAAC;AACjE;AAAA,oBACF;AACA,0BAAMO,cAAaT,IAAG,kBAAkB,QAAQ;AAChD,qCAAiB,KAAKS,WAAU;AAChC,oBAAAA,YAAW,GAAG,UAAU,MAAM;AAC5B,sBAAAA,YAAW,MAAM;AACjB,sBAAAD,SAAQ;AAAA,oBACV,CAAC;AACD,oBAAAC,YAAW,GAAG,SAAS,CAAC,QAAQ;AAC9B,sBAAAT,IAAG,OAAO,UAAU,MAAM;AAAA,sBAAC,CAAC;AAC5B,6BAAO,GAAG;AAAA,oBACZ,CAAC;AAAA,kBACH,CAAC,EAAE,GAAG,SAAS,MAAM;AACrB;AAAA,gBACF;AAAA,cACF;AAEA,kBAAI,SAAS,eAAe,KAAK;AAC/B,uBAAO,IAAI,MAAM,0DAAkB,SAAS,UAAU,EAAE,CAAC;AACzD;AAAA,cACF;AAEA,oBAAM,aAAaA,IAAG,kBAAkB,QAAQ;AAChD,uBAAS,KAAK,UAAU;AAExB,yBAAW,GAAG,UAAU,MAAM;AAC5B,2BAAW,MAAM;AACjB,gBAAAQ,SAAQ;AAAA,cACV,CAAC;AAED,yBAAW,GAAG,SAAS,CAAC,QAAQ;AAC9B,gBAAAR,IAAG,OAAO,UAAU,MAAM;AAAA,gBAAC,CAAC;AAC5B,uBAAO,GAAG;AAAA,cACZ,CAAC;AAAA,YACH,CAAC;AAED,oBAAQ,GAAG,SAAS,CAAC,QAAQ;AAC3B,cAAAA,IAAG,OAAO,UAAU,MAAM;AAAA,cAAC,CAAC;AAC5B,qBAAO,GAAG;AAAA,YACZ,CAAC;AAED,oBAAQ,WAAW,KAAO,MAAM;AAC9B,sBAAQ,QAAQ;AAChB,cAAAA,IAAG,OAAO,UAAU,MAAM;AAAA,cAAC,CAAC;AAC5B,qBAAO,IAAI,MAAM,8CAAW,CAAC;AAAA,YAC/B,CAAC;AAAA,UACH,CAAC;AAGD,gBAAM,iBAAiBA,IAAG,SAAS,QAAQ;AAC3C,gBAAM,UAAU,eAAe,OAAO,OAClC,GAAG,eAAe,IAAI,MACtB,eAAe,OAAO,OAAO,OAC3B,IAAI,eAAe,OAAO,MAAM,QAAQ,CAAC,CAAC,OAC1C,IAAI,eAAe,OAAO,OAAO,MAAM,QAAQ,CAAC,CAAC;AAEvD,iBAAO;AAAA,YACL,SAAS,CAAC;AAAA,cACR,MAAM;AAAA,cACN,MAAM;AAAA,aAAkB,GAAG;AAAA,4BAAW,QAAQ;AAAA,4BAAW,OAAO;AAAA,YAClE,CAAC;AAAA,UACH;AAAA,QACF,SAAS,OAAO;AACd,iBAAO,MAAM,yCAAW,KAAK;AAC7B,iBAAO;AAAA,YACL,SAAS;AAAA,YACT,SAAS,CAAC;AAAA,cACR,MAAM;AAAA,cACN,MAAM,6BAAS,KAAK;AAAA,YACtB,CAAC;AAAA,UACH;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAGA,SAAK,OAAO;AAAA,MACV;AAAA,MACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MAyBA;AAAA,QACE,QAAQI,GAAE,MAAMA,GAAE,OAAO,CAAC,EAAE,SAAS,sNAAsD;AAAA,MAC7F;AAAA,MACA,OAAO,EAAE,OAAO,MAAM;AACpB,YAAI;AAEF,gBAAM,cAA2E,CAAC;AAClF,gBAAM,SAAmB,CAAC;AAE1B,qBAAW,WAAW,QAAQ;AAC5B,gBAAI,CAACJ,IAAG,WAAW,OAAO,GAAG;AAC3B,qBAAO,KAAK,mCAAU,OAAO,EAAE;AAC/B;AAAA,YACF;AACA,kBAAM,OAAOA,IAAG,SAAS,OAAO;AAChC,gBAAI,KAAK,OAAO,IAAI,OAAO,MAAM;AAC/B,qBAAO,KAAK,8CAAgB,OAAO,EAAE;AACrC;AAAA,YACF;AACA,kBAAM,MAAMC,MAAK,QAAQ,OAAO,EAAE,YAAY,EAAE,MAAM,CAAC;AACvD,gBAAI,CAAC,CAAC,OAAO,OAAO,QAAQ,QAAQ,OAAO,OAAO,MAAM,EAAE,SAAS,GAAG,GAAG;AACvE,qBAAO,KAAK,yCAAW,OAAO,EAAE;AAChC;AAAA,YACF;AACA,wBAAY,KAAK;AAAA,cACf,MAAM;AAAA,cACN,MAAMA,MAAK,SAAS,SAASA,MAAK,QAAQ,OAAO,CAAC;AAAA,cAClD,KAAK,QAAQ,QAAQ,SAAS;AAAA,cAC9B,MAAM,KAAK;AAAA,YACb,CAAC;AAAA,UACH;AAEA,cAAI,YAAY,WAAW,GAAG;AAC5B,mBAAO;AAAA,cACL,SAAS;AAAA,cACT,SAAS,CAAC;AAAA,gBACR,MAAM;AAAA,gBACN,MAAM;AAAA,EAAgB,OAAO,KAAK,IAAI,CAAC;AAAA,cACzC,CAAC;AAAA,YACH;AAAA,UACF;AAGA,gBAAM,YAAY;AAAA,YAChB,iBAAiB;AAAA,cACf,aAAa;AAAA,YACf;AAAA,YACA,gBAAgB;AAAA,cACd,UAAU;AAAA,cACV,QAAQ;AAAA,cACR,SAAS;AAAA,gBACP,EAAE,OAAO,0BAAgB,aAAa,iIAAuC;AAAA,gBAC7E,EAAE,OAAO,WAAW,aAAa,iGAA2B;AAAA,cAC9D;AAAA,YACF;AAAA,YACA,gBAAgB;AAAA,cACd,UAAU;AAAA,cACV,QAAQ;AAAA,cACR,SAAS;AAAA,gBACP,EAAE,OAAO,kCAAS,aAAa,yDAAY;AAAA,gBAC3C,EAAE,OAAO,2BAAY,aAAa,qEAAmB;AAAA,gBACrD,EAAE,OAAO,2BAAY,aAAa,iEAAyB;AAAA,gBAC3D,EAAE,OAAO,0BAAW,aAAa,gEAAwB;AAAA,cAC3D;AAAA,YACF;AAAA,YACA,wBAAwB;AAAA,cACtB,UAAU;AAAA,cACV,QAAQ;AAAA,cACR,SAAS;AAAA,gBACP,EAAE,OAAO,kCAAS,aAAa,8DAAiB;AAAA,gBAChD,EAAE,OAAO,kCAAS,aAAa,gFAAoB;AAAA,cACrD;AAAA,cACA,WAAW;AAAA,YACb;AAAA,UACF;AAEA,gBAAM,UAAU,CAAC,SAAiB,OAAO,OACrC,GAAG,IAAI,MACP,OAAO,OAAO,OACZ,IAAI,OAAO,MAAM,QAAQ,CAAC,CAAC,OAC3B,IAAI,OAAO,OAAO,MAAM,QAAQ,CAAC,CAAC;AAExC,cAAI,aAAa;AAAA;AAAA;AACjB,wBAAc,wCAAe,YAAY,MAAM;AAAA;AAC/C,qBAAW,OAAO,aAAa;AAC7B,0BAAc,KAAKA,MAAK,SAAS,IAAI,IAAI,CAAC,KAAK,QAAQ,IAAI,IAAI,CAAC;AAAA;AAAA,UAClE;AAEA,cAAI,OAAO,SAAS,GAAG;AACrB,0BAAc;AAAA;AAAA;AACd,uBAAW,OAAO,QAAQ;AACxB,4BAAc,KAAK,GAAG;AAAA;AAAA,YACxB;AAAA,UACF;AAEA,wBAAc;AAAA;AAAA;AAAA;AACd,wBAAc;AAAA;AACd,wBAAc;AAAA;AACd,wBAAc;AAAA;AACd,wBAAc;AAAA;AACd,wBAAc;AAAA;AACd,wBAAc;AAAA;AACd,wBAAc;AAAA;AAEd,iBAAO;AAAA,YACL,SAAS;AAAA,cACP;AAAA,gBACE,MAAM;AAAA,gBACN,MAAM;AAAA,cACR;AAAA,cACA;AAAA,gBACE,MAAM;AAAA,gBACN,MAAM;AAAA;AAAA;AAAA;AAAA,EAAwC,KAAK,UAAU,WAAW,MAAM,CAAC,CAAC;AAAA;AAAA,cAClF;AAAA,YACF;AAAA,UACF;AAAA,QACF,SAAS,OAAO;AACd,iBAAO,MAAM,qDAAa,KAAK;AAC/B,iBAAO;AAAA,YACL,SAAS;AAAA,YACT,SAAS,CAAC;AAAA,cACR,MAAM;AAAA,cACN,MAAM,6BAAS,KAAK;AAAA,YACtB,CAAC;AAAA,UACH;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAGA,SAAK,OAAO;AAAA,MACV;AAAA,MACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MAwBA;AAAA,QACE,QAAQG,GAAE,MAAMA,GAAE,OAAO,CAAC,EAAE,SAAS,0EAAc;AAAA,QACnD,QAAQA,GAAE,KAAK,CAAC,WAAW,SAAS,CAAC,EAAE,SAAS,mFAAkB;AAAA,QAClE,cAAcA,GAAE,KAAK,CAAC,OAAO,QAAQ,MAAM,CAAC,EAAE,SAAS,EAAE,SAAS,sIAAkC;AAAA,QACpG,gBAAgBA,GAAE,QAAQ,EAAE,SAAS,EAAE,SAAS,6HAAyB;AAAA,QACzE,cAAcA,GAAE,OAAO,EAAE,SAAS,EAAE,SAAS,6FAAuB;AAAA,QACpE,YAAYA,GAAE,OAAO,EAAE,SAAS,EAAE,SAAS,qGAA+B,YAAY,KAAK,IAAI,KAAK,QAAG,EAAE;AAAA,MAC3G;AAAA,MACA,OAAO,EAAE,QAAQ,QAAQ,cAAc,iBAAiB,OAAO,cAAc,aAAa,UAAU,MAAM;AACxG,YAAI;AACF,iBAAO,IAAI,0CAAY,MAAM,kBAAQ,gBAAgB,oBAAK,wBAAS,OAAO,MAAM,EAAE;AAGlF,gBAAM,cAA2E,CAAC;AAClF,gBAAM,SAAmB,CAAC;AAE1B,qBAAW,WAAW,QAAQ;AAC5B,gBAAI,CAACJ,IAAG,WAAW,OAAO,GAAG;AAC3B,qBAAO,KAAK,mCAAU,OAAO,EAAE;AAC/B;AAAA,YACF;AACA,kBAAM,OAAOA,IAAG,SAAS,OAAO;AAChC,gBAAI,KAAK,OAAO,IAAI,OAAO,MAAM;AAC/B,qBAAO,KAAK,8CAAgB,OAAO,EAAE;AACrC;AAAA,YACF;AACA,kBAAM,MAAMC,MAAK,QAAQ,OAAO,EAAE,YAAY,EAAE,MAAM,CAAC;AACvD,gBAAI,CAAC,CAAC,OAAO,OAAO,QAAQ,QAAQ,OAAO,OAAO,MAAM,EAAE,SAAS,GAAG,GAAG;AACvE,qBAAO,KAAK,yCAAW,OAAO,EAAE;AAChC;AAAA,YACF;AACA,wBAAY,KAAK;AAAA,cACf,MAAM;AAAA,cACN,MAAMA,MAAK,SAAS,SAASA,MAAK,QAAQ,OAAO,CAAC;AAAA,cAClD,KAAK,QAAQ,QAAQ,SAAS;AAAA,cAC9B,MAAM,KAAK;AAAA,YACb,CAAC;AAAA,UACH;AAEA,cAAI,YAAY,WAAW,GAAG;AAC5B,mBAAO;AAAA,cACL,SAAS;AAAA,cACT,SAAS,CAAC;AAAA,gBACR,MAAM;AAAA,gBACN,MAAM;AAAA,EAAgB,OAAO,KAAK,IAAI,CAAC;AAAA,cACzC,CAAC;AAAA,YACH;AAAA,UACF;AAGA,gBAAM,qBAAqB,WAAW,YAAY,SAAU,gBAAgB;AAC5E,gBAAM,YAAY,WAAW,YAAY,IAAI;AAC7C,gBAAM,UAAgC,CAAC;AAEvC,mBAAS,IAAI,GAAG,IAAI,YAAY,QAAQ,KAAK,WAAW;AACtD,oBAAQ,KAAK,YAAY,MAAM,GAAG,IAAI,SAAS,CAAC;AAAA,UAClD;AAGA,cAAI,eAAe;AAAA;AAAA;AACnB,0BAAgB,qBAAW,WAAW,YAAY,mCAAmC,+CAA+C;AAAA;AACpI,0BAAgB,iCAAa,sBAAsB,gCAAO;AAAA;AAC1D,0BAAgB,iCAAa,YAAY,MAAM;AAAA;AAC/C,0BAAgB,2BAAY,QAAQ,MAAM,8BAAU,SAAS;AAAA;AAAA;AAE7D,cAAI,OAAO,SAAS,GAAG;AACrB,4BAAgB;AAAA;AAChB,uBAAW,OAAO,QAAQ;AACxB,8BAAgB,KAAK,GAAG;AAAA;AAAA,YAC1B;AACA,4BAAgB;AAAA;AAAA,UAClB;AAEA,0BAAgB;AAAA;AAAA;AAChB,0BAAgB;AAAA;AAAA;AAEhB,cAAI,WAAW,WAAW;AACxB,4BAAgB,KAAK,4BAA4B,SAAS,kBAAkB;AAAA,UAC9E,OAAO;AACL,4BAAgB,KAAK,4BAA4B,OAAO;AAAA,UAC1D;AAGA,0BAAgB;AAAA;AAAA;AAAA;AAChB,0BAAgB;AAAA;AAAA;AAEhB,qBAAW,OAAO,aAAa;AAC7B,kBAAM,SAAS,sBAAsB,IAAI;AACzC,kBAAM,iBAAiB,WAAW,IAAI;AACtC,kBAAM,cAAc,GAAG,IAAI,IAAI,IAAI,MAAM;AACzC,kBAAM,eAAeA,MAAK,KAAKA,MAAK,QAAQ,IAAI,IAAI,GAAG,GAAG,IAAI,IAAI,eAAe,MAAM,EAAE;AAEzF,4BAAgB,KAAKA,MAAK,SAAS,IAAI,IAAI,CAAC;AAAA;AAC5C,4BAAgB,oDAAiB,YAAY;AAAA;AAE7C,gBAAI,cAAc;AAChB,kBAAI,gBAAgB;AAClB,gCAAgB,+CAAgC,YAAY,OAAO,YAAY,OAAO,WAAW,OAAO,UAAU;AAAA;AAClH,oBAAI,gBAAgB;AAClB,kCAAgB,sEAAyB,YAAY,IAAIA,MAAK,SAAS,IAAI,IAAI,CAAC;AAAA;AAAA,gBAClF;AAAA,cACF,OAAO;AACL,gCAAgB,2DAAkC,YAAY,OAAO,YAAY,OAAOA,MAAK,SAAS,IAAI,IAAI,CAAC,OAAO,UAAU;AAAA;AAAA,cAClI;AAAA,YACF;AACA,4BAAgB;AAAA;AAAA,UAClB;AAGA,gBAAM,aAAa;AAAA,YACjB;AAAA,YACA,cAAc;AAAA,YACd,gBAAgB,qBAAqB,iBAAiB;AAAA,YACtD;AAAA,YACA;AAAA,YACA,aAAa,YAAY;AAAA,YACzB,SAAS,QAAQ;AAAA,YACjB;AAAA,YACA,QAAQ,YAAY,IAAI,UAAQ;AAAA,cAC9B,cAAc,IAAI;AAAA,cAClB,cAAcA,MAAK,SAAS,IAAI,IAAI;AAAA,cACpC,aAAa,IAAI;AAAA,cACjB,cAAc,IAAI;AAAA,cAClB,QAAQ,sBAAsB,IAAI;AAAA,cAClC,iBAAiB,sBAAsB,IAAI,SAAS,IAAI;AAAA,YAC1D,EAAE;AAAA,UACJ;AAEA,iBAAO;AAAA,YACL,SAAS;AAAA,cACP;AAAA,gBACE,MAAM;AAAA,gBACN,MAAM;AAAA,cACR;AAAA,cACA;AAAA,gBACE,MAAM;AAAA,gBACN,MAAM;AAAA;AAAA;AAAA;AAAA,EAA0C,KAAK,UAAU,YAAY,MAAM,CAAC,CAAC;AAAA;AAAA,cACrF;AAAA,YACF;AAAA,UACF;AAAA,QACF,SAAS,OAAO;AACd,iBAAO,MAAM,qDAAa,KAAK;AAC/B,iBAAO;AAAA,YACL,SAAS;AAAA,YACT,SAAS,CAAC;AAAA,cACR,MAAM;AAAA,cACN,MAAM,qDAAa,KAAK;AAAA,YAC1B,CAAC;AAAA,UACH;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAGA,UAAM,uBAAuB,CAAC,CAAC,cAAc;AAE7C,SAAK,OAAO;AAAA,MACV;AAAA,MACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,4BAME,uBAAuB,0CAAsB,qIAA0D;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MASzG;AAAA,QACE,SAASG,GAAE,OAAO,EAAE,SAAS,iEAAyB;AAAA,QACtD,QAAQA,GAAE,OAAO,EAAE,SAAS,qHAA+C;AAAA,QAC3E,QAAQA,GAAE,MAAMA,GAAE,OAAO,EAAE,IAAI,IAAI,EAAE,IAAI,CAAC,CAAC,EAAE,QAAQ,CAAC,GAAG,CAAC,CAAC,EAAE,SAAS,0GAAyC;AAAA,QAC/G,QAAQA,GAAE,KAAK,CAAC,OAAO,OAAO,OAAO,KAAK,CAAC,EAAE,QAAQ,KAAK,EAAE,SAAS,gDAAa;AAAA,QAClF,gBAAgBA,GAAE,OAAO,EAAE,SAAS,kDAAU;AAAA,QAC9C,gBAAgBA,GAAE,OAAO,EAAE,SAAS,EAAE,SAAS,0GAA0B;AAAA,QACzE,cAAcA,GAAE,OAAO,EAAE,SAAS,EAAE,SAAS,kIAA8B;AAAA,QAC3E,YAAYA,GAAE,OAAO,EAAE,SAAS,EAAE,SAAS,0JAA4C,YAAY,KAAK,IAAI,KAAK,QAAG,EAAE;AAAA,MACxH;AAAA,MACA,OAAO,EAAE,SAAS,QAAQ,QAAQ,QAAQ,gBAAgB,gBAAgB,cAAc,aAAa,UAAU,MAAM;AACnH,YAAI;AACF,cAAI,CAAC,sBAAsB;AACzB,mBAAO;AAAA,cACL,SAAS;AAAA,cACT,SAAS,CAAC;AAAA,gBACR,MAAM;AAAA,gBACN,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,cAoBR,CAAC;AAAA,YACH;AAAA,UACF;AAEA,iBAAO,IAAI,kDAAyB,OAAO,YAAY,MAAM,YAAY,OAAO,KAAK,GAAG,CAAC,YAAY,MAAM,EAAE;AAG7G,gBAAM,UAAU,MAAM,aAAa;AAAA,YACjC,EAAE,SAAS,QAAQ,QAAQ,OAAO;AAAA,YAClC;AAAA,YACA;AAAA,UACF;AAGA,cAAI,cAAc;AAChB,uBAAW,UAAU,SAAS;AAC5B,kBAAI,CAAC,OAAO,UAAW;AACvB,oBAAM,eAAe,MAAM,WAAW,WAAW;AAAA,gBAC/C,UAAU,OAAO;AAAA,gBACjB,WAAW;AAAA,gBACX,UAAU,OAAO;AAAA,gBACjB;AAAA,cACF,CAAC;AACD,kBAAI,aAAa,SAAS;AACxB,uBAAO,SAAS,aAAa;AAAA,cAC/B,OAAO;AACL,uBAAO,MAAM,gBAAM,OAAO,QAAQ,6BAAc,aAAa,KAAK,EAAE;AAAA,cACtE;AAAA,YACF;AAAA,UACF;AAGA,gBAAM,UAAU,CAAC,SAAiB,OAAO,OACrC,GAAG,IAAI,MACP,OAAO,OAAO,OACZ,IAAI,OAAO,MAAM,QAAQ,CAAC,CAAC,OAC3B,IAAI,OAAO,OAAO,MAAM,QAAQ,CAAC,CAAC;AAExC,cAAI,aAAa;AAAA;AAAA;AACjB,wBAAc,yBAAe,OAAO;AAAA;AACpC,wBAAc,wBAAc,MAAM;AAAA;AAClC,wBAAc,qBAAW,MAAM;AAAA;AAC/B,wBAAc,qBAAW,OAAO,KAAK,KAAK,CAAC;AAAA;AAAA;AAE3C,wBAAc;AAAA;AAAA;AACd,qBAAW,KAAK,SAAS;AACvB,kBAAM,YAAY,EAAE,aAAaJ,IAAG,WAAW,EAAE,SAAS,IACtD,QAAQA,IAAG,SAAS,EAAE,SAAS,EAAE,IAAI,IACrC;AAEJ,0BAAc,KAAK,EAAE,QAAQ,OAAO,EAAE,KAAK,MAAM,SAAS;AAAA;AAC1D,0BAAc,iCAAa,EAAE,SAAS;AAAA;AACtC,gBAAI,EAAE,QAAQ;AACZ,4BAAc,cAAc,EAAE,MAAM;AAAA;AAAA,YACtC;AACA,0BAAc;AAAA;AAAA,UAChB;AAEA,cAAI,cAAc;AAChB,kBAAM,gBAAgB,QAAQ,OAAO,OAAK,EAAE,MAAM,EAAE;AACpD,0BAAc;AAAA;AAAA;AACd,0BAAc,mBAAS,YAAY;AAAA;AACnC,0BAAc,mBAAS,UAAU;AAAA;AACjC,0BAAc,mBAAS,aAAa,IAAI,QAAQ,MAAM;AAAA;AAAA,UACxD;AAEA,iBAAO;AAAA,YACL,SAAS,CAAC;AAAA,cACR,MAAM;AAAA,cACN,MAAM;AAAA,YACR,CAAC;AAAA,UACH;AAAA,QACF,SAAS,OAAO;AACd,iBAAO,MAAM,sDAAmB,KAAK;AACrC,iBAAO;AAAA,YACL,SAAS;AAAA,YACT,SAAS,CAAC;AAAA,cACR,MAAM;AAAA,cACN,MAAM,sDAAmB,iBAAiB,QAAQ,MAAM,UAAU,KAAK;AAAA,YACzE,CAAC;AAAA,UACH;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA;AAAA,EAGQ,4BAA4B,SAAwE,cAAqC;AAC/I,QAAI,eAAe;AAGnB,oBAAgB;AAAA;AAAA;AAChB,oBAAgB;AAAA;AAChB,oBAAgB;AAAA;AAChB,oBAAgB;AAAA;AAChB,oBAAgB;AAAA;AAAA;AAEhB,UAAM,wBAAwB,gBAAgB,iBAAiB;AAE/D,aAAS,IAAI,GAAG,IAAI,QAAQ,QAAQ,KAAK;AACvC,YAAM,QAAQ,QAAQ,CAAC;AACvB,sBAAgB,qBAAW,IAAI,CAAC,IAAI,QAAQ,MAAM;AAAA;AAAA;AAClD,sBAAgB,qBAAW,MAAM,IAAI,SAAOC,MAAK,SAAS,IAAI,IAAI,CAAC,EAAE,KAAK,IAAI,CAAC;AAAA;AAAA;AAE/E,UAAI,UAAU;AACd,sBAAgB,GAAG,SAAS;AAAA;AAC5B,sBAAgB,GAAG,SAAS;AAAA;AAG5B,UAAI,uBAAuB;AACzB,wBAAgB,GAAG,SAAS;AAAA;AAC5B,wBAAgB;AAAA;AAChB,wBAAgB;AAAA;AAChB,wBAAgB;AAAA;AAChB,wBAAgB,GAAG,SAAS;AAAA;AAC5B,wBAAgB,kCAAc,aAAc,YAAY,CAAC;AAAA;AAAA,MAC3D;AAEA,sBAAgB,GAAG,SAAS;AAAA;AAC5B,sBAAgB;AAAA;AAChB,sBAAgB;AAAA;AAChB,sBAAgB,GAAG,SAAS;AAAA;AAC5B,iBAAW,OAAO,OAAO;AACvB,wBAAgB,UAAU,IAAI,IAAI;AAAA;AAAA,MACpC;AACA,sBAAgB,GAAG,SAAS;AAAA;AAC5B,sBAAgB,GAAG,SAAS;AAAA;AAE5B,UAAI,IAAI,QAAQ,SAAS,GAAG;AAC1B,wBAAgB,GAAG,SAAS;AAAA;AAAA,MAC9B;AACA,sBAAgB;AAAA;AAAA,IAClB;AAEA,WAAO;AAAA,EACT;AAAA;AAAA,EAGQ,4BAA4B,SAAgF;AAClH,QAAI,eAAe;AAGnB,oBAAgB;AAAA;AAAA;AAChB,oBAAgB;AAAA;AAChB,oBAAgB;AAAA;AAChB,oBAAgB;AAAA;AAChB,oBAAgB;AAAA;AAAA;AAEhB,aAAS,IAAI,GAAG,IAAI,QAAQ,QAAQ,KAAK;AACvC,YAAM,QAAQ,QAAQ,CAAC;AACvB,sBAAgB,qBAAW,IAAI,CAAC,IAAI,QAAQ,MAAM;AAAA;AAAA;AAClD,sBAAgB,qBAAW,MAAM,IAAI,SAAOA,MAAK,SAAS,IAAI,IAAI,CAAC,EAAE,KAAK,IAAI,CAAC;AAAA;AAAA;AAE/E,UAAI,UAAU;AACd,sBAAgB,GAAG,SAAS;AAAA;AAC5B,sBAAgB,GAAG,SAAS;AAAA;AAC5B,sBAAgB,GAAG,SAAS;AAAA;AAC5B,sBAAgB;AAAA;AAChB,sBAAgB;AAAA;AAChB,sBAAgB,GAAG,SAAS;AAAA;AAC5B,iBAAW,OAAO,OAAO;AACvB,wBAAgB,UAAU,IAAI,IAAI;AAAA;AAAA,MACpC;AACA,sBAAgB,GAAG,SAAS;AAAA;AAC5B,sBAAgB,GAAG,SAAS;AAAA;AAE5B,UAAI,IAAI,QAAQ,SAAS,GAAG;AAC1B,wBAAgB,GAAG,SAAS;AAAA;AAAA,MAC9B;AACA,sBAAgB;AAAA;AAAA,IAClB;AAEA,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,QAAQ,WAAqC;AACjD,QAAI;AACF,YAAM,KAAK,OAAO,QAAQ,SAAS;AAEnC,aAAO,MAAM,IAAI,SAAgB;AAC/B,YAAI;AACF,eAAK,OAAO,OAAO,mBAAmB;AAAA,YACpC,OAAO;AAAA,YACP,MAAM;AAAA,UACR,CAAC;AAAA,QACH,SAAS,OAAO;AACd,kBAAQ,IAAI,GAAG,IAAI;AAAA,QACrB;AAAA,MACF;AAEA,aAAO,QAAQ,IAAI,SAAgB;AACjC,YAAI;AACF,eAAK,OAAO,OAAO,mBAAmB;AAAA,YACpC,OAAO;AAAA,YACP,MAAM;AAAA,UACR,CAAC;AAAA,QACH,SAAS,OAAO;AACd,kBAAQ,MAAM,GAAG,IAAI;AAAA,QACvB;AAAA,MACF;AAEA,aAAO,IAAI,uFAAsB;AAAA,IACnC,SAAS,OAAO;AACd,cAAQ,MAAM,qDAAa,KAAK;AAAA,IAClC;AAAA,EACF;AAAA,EAEA,MAAM,gBAAgB,MAA6B;AACjD,UAAM,MAAM,QAAQ;AAGpB,QAAI,IAAI,QAAQ,CAAC,KAAc,QAAkB;AAE/C,WAAK,eAAe,IAAI;AAAA,QACtB;AAAA,QACA;AAAA,MACF;AAEA,UAAI;AAEF,aAAK,OAAO,QAAQ,KAAK,YAAY,EAClC,MAAM,CAAC,QAAQ;AACd,kBAAQ,MAAM,wDAAgB,GAAG;AAAA,QACnC,CAAC;AAGH,YAAI,GAAG,SAAS,MAAM;AACpB,kBAAQ,IAAI,+CAAY;AACxB,eAAK,eAAe;AAAA,QACtB,CAAC;AAAA,MACH,SAAS,OAAO;AACd,gBAAQ,MAAM,kDAAe,KAAK;AAElC,YAAI,CAAC,IAAI,eAAe;AACtB,cAAI,OAAO,GAAG,EAAE,IAAI;AAAA,QACtB;AAAA,MACF;AAAA,IACF,CAAC;AAGD,QAAI,KAAK,aAAa,OAAO,KAAc,QAAkB;AAC3D,UAAI,CAAC,KAAK,cAAc;AACtB,gBAAQ,IAAI,yFAAmB;AAC/B,YAAI,OAAO,GAAG,EAAE,KAAK;AAAA,UACnB,OAAO;AAAA,UACP,SAAS;AAAA,QACX,CAAC;AACD;AAAA,MACF;AAEA,UAAI;AACF,cAAM,KAAK,aAAa;AAAA,UACtB;AAAA,UACA;AAAA,QACF;AAAA,MACF,SAAS,OAAO;AACd,gBAAQ,MAAM,+CAAY,KAAK;AAC/B,YAAI,CAAC,IAAI,eAAe;AACtB,cAAI,OAAO,GAAG,EAAE,KAAK;AAAA,YACnB,OAAO;AAAA,YACP,SAAS,OAAO,KAAK;AAAA,UACvB,CAAC;AAAA,QACH;AAAA,MACF;AAAA,IACF,CAAC;AAGD,QAAI,OAAO,MAAM,MAAM;AACrB,aAAO,MAAM,QAAQ;AACrB,aAAO,QAAQ,QAAQ;AAEvB,aAAO,IAAI,mDAAgB,IAAI,EAAE;AACjC,aAAO,IAAI,qCAA2B,IAAI,MAAM;AAChD,aAAO,IAAI,8CAA0B,IAAI,WAAW;AAAA,IACtD,CAAC;AAAA,EACH;AACF;;;ADh0CA,SAAS,eAAe;AACxB,SAAS,UAAAS,eAAc;AAGvBA,QAAO,EAAE,MAAM,QAAQ,QAAQ,IAAI,GAAG,MAAM,EAAE,CAAC;AAE/C,eAAsB,cAA6B;AAEjD,QAAM,cAAc,QAAQ,IAAI,aAAa,SAAS,QAAQ,KAAK,SAAS,SAAS;AAGrF,QAAM,eAAe,gBAAgB,WAAW;AAGhD,QAAM,SAAS,IAAI,aAAa;AAEhC,MAAI,aAAa;AAEf,UAAM,YAAY,IAAI,qBAAqB;AAC3C,UAAM,OAAO,QAAQ,SAAS;AAAA,EAChC,OAAO;AAEL,YAAQ,IAAI,wFAA4B,aAAa,IAAI,KAAK;AAC9D,UAAM,OAAO,gBAAgB,aAAa,IAAI;AAAA,EAChD;AACF;AAGA,YAAY,EAAE,MAAM,CAAC,UAAU;AAC7B,UAAQ,MAAM,+CAAY,KAAK;AAC/B,UAAQ,KAAK,CAAC;AAChB,CAAC;","names":["z","config","z","config","fs","path","resolve","body","fs","path","fs","path","https","config","z","configs","configNames","resultText","resolve","fileStream","config"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "oss-mcp-plus",
3
- "version": "1.0.13",
3
+ "version": "1.0.14",
4
4
  "description": "本地MCP服务器,用于将文件上传到阿里云OSS,支持多配置和目录指定",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",