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 +343 -42
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
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
|
|
379
|
-
import
|
|
380
|
-
import
|
|
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 (!
|
|
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: ${
|
|
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 (!
|
|
748
|
+
if (!fs3.existsSync(directory)) {
|
|
587
749
|
throw new Error(`\u76EE\u5F55\u4E0D\u5B58\u5728: ${directory}`);
|
|
588
750
|
}
|
|
589
|
-
const stat =
|
|
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 =
|
|
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 =
|
|
604
|
-
const fileStat =
|
|
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 (!
|
|
891
|
-
|
|
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 =
|
|
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 =
|
|
1063
|
+
finalFileName = path3.basename(urlObj.pathname);
|
|
902
1064
|
if (!finalFileName || finalFileName === "/") {
|
|
903
1065
|
finalFileName = `download_${Date.now()}`;
|
|
904
1066
|
}
|
|
905
1067
|
}
|
|
906
|
-
const filePath =
|
|
907
|
-
if (
|
|
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:" ?
|
|
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:") ?
|
|
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 =
|
|
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
|
-
|
|
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 =
|
|
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
|
-
|
|
1112
|
+
fs3.unlink(filePath, () => {
|
|
951
1113
|
});
|
|
952
1114
|
reject(err);
|
|
953
1115
|
});
|
|
954
1116
|
});
|
|
955
1117
|
request.on("error", (err) => {
|
|
956
|
-
|
|
1118
|
+
fs3.unlink(filePath, () => {
|
|
957
1119
|
});
|
|
958
1120
|
reject(err);
|
|
959
1121
|
});
|
|
960
1122
|
request.setTimeout(6e4, () => {
|
|
961
1123
|
request.destroy();
|
|
962
|
-
|
|
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 =
|
|
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 (!
|
|
1187
|
+
if (!fs3.existsSync(imgPath)) {
|
|
1026
1188
|
errors.push(`\u6587\u4EF6\u4E0D\u5B58\u5728: ${imgPath}`);
|
|
1027
1189
|
continue;
|
|
1028
1190
|
}
|
|
1029
|
-
const stat =
|
|
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 =
|
|
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:
|
|
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 += `- ${
|
|
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 (!
|
|
1356
|
+
if (!fs3.existsSync(imgPath)) {
|
|
1195
1357
|
errors.push(`\u6587\u4EF6\u4E0D\u5B58\u5728: ${imgPath}`);
|
|
1196
1358
|
continue;
|
|
1197
1359
|
}
|
|
1198
|
-
const stat =
|
|
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 =
|
|
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:
|
|
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 =
|
|
1276
|
-
instructions += `**${
|
|
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}/${
|
|
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}", "${
|
|
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:
|
|
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) =>
|
|
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) =>
|
|
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":"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","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":"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","names":["z","config","z","config","fs","path","resolve","body","fs","path","fs","path","https","config","z","configs","configNames","resultText","resolve","fileStream","config"]}
|