@lutery/vision-mcp 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (122) hide show
  1. package/README.md +428 -0
  2. package/dist/adapters/base-adapter.d.ts +69 -0
  3. package/dist/adapters/base-adapter.d.ts.map +1 -0
  4. package/dist/adapters/base-adapter.js +143 -0
  5. package/dist/adapters/base-adapter.js.map +1 -0
  6. package/dist/adapters/claude-adapter.d.ts +38 -0
  7. package/dist/adapters/claude-adapter.d.ts.map +1 -0
  8. package/dist/adapters/claude-adapter.js +251 -0
  9. package/dist/adapters/claude-adapter.js.map +1 -0
  10. package/dist/adapters/glm-adapter.d.ts +15 -0
  11. package/dist/adapters/glm-adapter.d.ts.map +1 -0
  12. package/dist/adapters/glm-adapter.js +131 -0
  13. package/dist/adapters/glm-adapter.js.map +1 -0
  14. package/dist/adapters/modelscope-adapter.d.ts +20 -0
  15. package/dist/adapters/modelscope-adapter.d.ts.map +1 -0
  16. package/dist/adapters/modelscope-adapter.js +142 -0
  17. package/dist/adapters/modelscope-adapter.js.map +1 -0
  18. package/dist/adapters/openai-adapter.d.ts +20 -0
  19. package/dist/adapters/openai-adapter.d.ts.map +1 -0
  20. package/dist/adapters/openai-adapter.js +194 -0
  21. package/dist/adapters/openai-adapter.js.map +1 -0
  22. package/dist/adapters/siliconflow-adapter.d.ts +21 -0
  23. package/dist/adapters/siliconflow-adapter.d.ts.map +1 -0
  24. package/dist/adapters/siliconflow-adapter.js +145 -0
  25. package/dist/adapters/siliconflow-adapter.js.map +1 -0
  26. package/dist/config/model-config.d.ts +39 -0
  27. package/dist/config/model-config.d.ts.map +1 -0
  28. package/dist/config/model-config.js +115 -0
  29. package/dist/config/model-config.js.map +1 -0
  30. package/dist/index.d.ts +17 -0
  31. package/dist/index.d.ts.map +1 -0
  32. package/dist/index.js +186 -0
  33. package/dist/index.js.map +1 -0
  34. package/dist/prompts/system.d.ts +75 -0
  35. package/dist/prompts/system.d.ts.map +1 -0
  36. package/dist/prompts/system.js +272 -0
  37. package/dist/prompts/system.js.map +1 -0
  38. package/dist/providers/provider-registry.d.ts +58 -0
  39. package/dist/providers/provider-registry.d.ts.map +1 -0
  40. package/dist/providers/provider-registry.js +173 -0
  41. package/dist/providers/provider-registry.js.map +1 -0
  42. package/dist/src/adapters/base-adapter.d.ts +59 -0
  43. package/dist/src/adapters/base-adapter.d.ts.map +1 -0
  44. package/dist/src/adapters/base-adapter.js +83 -0
  45. package/dist/src/adapters/base-adapter.js.map +1 -0
  46. package/dist/src/adapters/glm-adapter.d.ts +15 -0
  47. package/dist/src/adapters/glm-adapter.d.ts.map +1 -0
  48. package/dist/src/adapters/glm-adapter.js +116 -0
  49. package/dist/src/adapters/glm-adapter.js.map +1 -0
  50. package/dist/src/adapters/siliconflow-adapter.d.ts +21 -0
  51. package/dist/src/adapters/siliconflow-adapter.d.ts.map +1 -0
  52. package/dist/src/adapters/siliconflow-adapter.js +130 -0
  53. package/dist/src/adapters/siliconflow-adapter.js.map +1 -0
  54. package/dist/src/config/model-config.d.ts +40 -0
  55. package/dist/src/config/model-config.d.ts.map +1 -0
  56. package/dist/src/config/model-config.js +126 -0
  57. package/dist/src/config/model-config.js.map +1 -0
  58. package/dist/src/index.d.ts +17 -0
  59. package/dist/src/index.d.ts.map +1 -0
  60. package/dist/src/index.js +188 -0
  61. package/dist/src/index.js.map +1 -0
  62. package/dist/src/prompts/system.d.ts +75 -0
  63. package/dist/src/prompts/system.d.ts.map +1 -0
  64. package/dist/src/prompts/system.js +272 -0
  65. package/dist/src/prompts/system.js.map +1 -0
  66. package/dist/src/tools/vision-tool.d.ts +91 -0
  67. package/dist/src/tools/vision-tool.d.ts.map +1 -0
  68. package/dist/src/tools/vision-tool.js +171 -0
  69. package/dist/src/tools/vision-tool.js.map +1 -0
  70. package/dist/src/utils/errors.d.ts +65 -0
  71. package/dist/src/utils/errors.d.ts.map +1 -0
  72. package/dist/src/utils/errors.js +146 -0
  73. package/dist/src/utils/errors.js.map +1 -0
  74. package/dist/src/utils/image-input.d.ts +45 -0
  75. package/dist/src/utils/image-input.d.ts.map +1 -0
  76. package/dist/src/utils/image-input.js +226 -0
  77. package/dist/src/utils/image-input.js.map +1 -0
  78. package/dist/src/utils/logger.d.ts +63 -0
  79. package/dist/src/utils/logger.d.ts.map +1 -0
  80. package/dist/src/utils/logger.js +157 -0
  81. package/dist/src/utils/logger.js.map +1 -0
  82. package/dist/test/integration.test.d.ts +10 -0
  83. package/dist/test/integration.test.d.ts.map +1 -0
  84. package/dist/test/integration.test.js +270 -0
  85. package/dist/test/integration.test.js.map +1 -0
  86. package/dist/test/test-utils.d.ts +45 -0
  87. package/dist/test/test-utils.d.ts.map +1 -0
  88. package/dist/test/test-utils.js +107 -0
  89. package/dist/test/test-utils.js.map +1 -0
  90. package/dist/test/vision-tool.test.d.ts +9 -0
  91. package/dist/test/vision-tool.test.d.ts.map +1 -0
  92. package/dist/test/vision-tool.test.js +167 -0
  93. package/dist/test/vision-tool.test.js.map +1 -0
  94. package/dist/tools/vision-tool.d.ts +91 -0
  95. package/dist/tools/vision-tool.d.ts.map +1 -0
  96. package/dist/tools/vision-tool.js +167 -0
  97. package/dist/tools/vision-tool.js.map +1 -0
  98. package/dist/utils/data-url-parser.d.ts +27 -0
  99. package/dist/utils/data-url-parser.d.ts.map +1 -0
  100. package/dist/utils/data-url-parser.js +53 -0
  101. package/dist/utils/data-url-parser.js.map +1 -0
  102. package/dist/utils/errors.d.ts +65 -0
  103. package/dist/utils/errors.d.ts.map +1 -0
  104. package/dist/utils/errors.js +146 -0
  105. package/dist/utils/errors.js.map +1 -0
  106. package/dist/utils/image-input.d.ts +45 -0
  107. package/dist/utils/image-input.d.ts.map +1 -0
  108. package/dist/utils/image-input.js +238 -0
  109. package/dist/utils/image-input.js.map +1 -0
  110. package/dist/utils/logger.d.ts +63 -0
  111. package/dist/utils/logger.d.ts.map +1 -0
  112. package/dist/utils/logger.js +157 -0
  113. package/dist/utils/logger.js.map +1 -0
  114. package/dist/utils/thinking-extractors.d.ts +34 -0
  115. package/dist/utils/thinking-extractors.d.ts.map +1 -0
  116. package/dist/utils/thinking-extractors.js +83 -0
  117. package/dist/utils/thinking-extractors.js.map +1 -0
  118. package/dist/utils/thinking-filter.d.ts +32 -0
  119. package/dist/utils/thinking-filter.d.ts.map +1 -0
  120. package/dist/utils/thinking-filter.js +147 -0
  121. package/dist/utils/thinking-filter.js.map +1 -0
  122. package/package.json +41 -0
@@ -0,0 +1,146 @@
1
+ /**
2
+ * Vision MCP Error Types
3
+ *
4
+ * @description 统一错误处理系统,提供清晰的错误分类和信息
5
+ */
6
+ export var VisionMCPErrorCode;
7
+ (function (VisionMCPErrorCode) {
8
+ VisionMCPErrorCode["INVALID_INPUT"] = "INVALID_INPUT";
9
+ VisionMCPErrorCode["MODEL_CONFIG_ERROR"] = "MODEL_CONFIG_ERROR";
10
+ VisionMCPErrorCode["IMAGE_LOAD_ERROR"] = "IMAGE_LOAD_ERROR";
11
+ VisionMCPErrorCode["MODEL_API_ERROR"] = "MODEL_API_ERROR";
12
+ VisionMCPErrorCode["TIMEOUT_ERROR"] = "TIMEOUT_ERROR";
13
+ VisionMCPErrorCode["UNKNOWN_ERROR"] = "UNKNOWN_ERROR";
14
+ })(VisionMCPErrorCode || (VisionMCPErrorCode = {}));
15
+ export class VisionMCPError extends Error {
16
+ code;
17
+ details;
18
+ timestamp;
19
+ constructor({ message, code, details, cause }) {
20
+ super(message);
21
+ this.name = 'VisionMCPError';
22
+ this.code = code;
23
+ this.details = details;
24
+ this.timestamp = new Date();
25
+ if (cause) {
26
+ this.cause = cause;
27
+ }
28
+ // 保持正确的错误堆栈
29
+ if (Error.captureStackTrace) {
30
+ Error.captureStackTrace(this, VisionMCPError);
31
+ }
32
+ }
33
+ toJSON() {
34
+ // 从错误响应中移除堆栈跟踪(仅保留在日志中)
35
+ const { stack, ...detailsWithoutStack } = this.details || {};
36
+ return {
37
+ error: {
38
+ message: this.message,
39
+ code: this.code,
40
+ details: detailsWithoutStack,
41
+ timestamp: this.timestamp.toISOString()
42
+ }
43
+ };
44
+ }
45
+ }
46
+ export class InvalidInputError extends VisionMCPError {
47
+ constructor(message, details, cause) {
48
+ super({
49
+ message: `Invalid input: ${message}`,
50
+ code: VisionMCPErrorCode.INVALID_INPUT,
51
+ details,
52
+ cause
53
+ });
54
+ this.name = 'InvalidInputError';
55
+ }
56
+ }
57
+ export class ModelConfigError extends VisionMCPError {
58
+ constructor(message, details, cause) {
59
+ super({
60
+ message: `Model configuration error: ${message}`,
61
+ code: VisionMCPErrorCode.MODEL_CONFIG_ERROR,
62
+ details,
63
+ cause
64
+ });
65
+ this.name = 'ModelConfigError';
66
+ }
67
+ }
68
+ export class ImageLoadError extends VisionMCPError {
69
+ constructor(message, details, cause) {
70
+ super({
71
+ message: `Failed to load image: ${message}`,
72
+ code: VisionMCPErrorCode.IMAGE_LOAD_ERROR,
73
+ details,
74
+ cause
75
+ });
76
+ this.name = 'ImageLoadError';
77
+ }
78
+ }
79
+ export class ModelAPIError extends VisionMCPError {
80
+ constructor(message, details, cause) {
81
+ super({
82
+ message: `Model API error: ${message}`,
83
+ code: VisionMCPErrorCode.MODEL_API_ERROR,
84
+ details,
85
+ cause
86
+ });
87
+ this.name = 'ModelAPIError';
88
+ }
89
+ }
90
+ export class TimeoutError extends VisionMCPError {
91
+ constructor(message, details, cause) {
92
+ super({
93
+ message: `Request timeout: ${message}`,
94
+ code: VisionMCPErrorCode.TIMEOUT_ERROR,
95
+ details,
96
+ cause
97
+ });
98
+ this.name = 'TimeoutError';
99
+ }
100
+ }
101
+ /**
102
+ * 将未知错误转换为 VisionMCPError
103
+ */
104
+ export function toVisionMCPError(error, context) {
105
+ if (error instanceof VisionMCPError) {
106
+ return error;
107
+ }
108
+ if (error instanceof Error) {
109
+ // 堆栈跟踪只记录在日志中,不包含在错误详情中
110
+ const details = {
111
+ originalError: error.name
112
+ };
113
+ // 仅当 LOG_LEVEL 为 debug 时才包含堆栈跟踪
114
+ if (process.env.LOG_LEVEL === 'debug') {
115
+ details.stack = error.stack;
116
+ }
117
+ return new VisionMCPError({
118
+ message: context ? `${context}: ${error.message}` : error.message,
119
+ code: VisionMCPErrorCode.UNKNOWN_ERROR,
120
+ details,
121
+ cause: error
122
+ });
123
+ }
124
+ return new VisionMCPError({
125
+ message: context || 'An unknown error occurred',
126
+ code: VisionMCPErrorCode.UNKNOWN_ERROR,
127
+ details: {
128
+ error: String(error)
129
+ }
130
+ });
131
+ }
132
+ /**
133
+ * 将错误映射为 MCP 工具响应格式
134
+ */
135
+ export function toMCPErrorResponse(error) {
136
+ return {
137
+ content: [
138
+ {
139
+ type: 'text',
140
+ text: JSON.stringify(error.toJSON(), null, 2)
141
+ }
142
+ ],
143
+ isError: true
144
+ };
145
+ }
146
+ //# sourceMappingURL=errors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../../../src/utils/errors.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Image Input Normalization Utility
3
+ *
4
+ * @description 统一处理图片输入:支持 URL、base64(data URL) 和本地路径
5
+ */
6
+ /**
7
+ * 图片输入类型
8
+ */
9
+ export type ImageInputType = 'url' | 'base64' | 'local';
10
+ /**
11
+ * 规范化后的图片输入格式(总是转换为 base64 data URL)
12
+ */
13
+ export interface NormalizedImageInput {
14
+ type: ImageInputType;
15
+ originalInput: string;
16
+ dataUrl: string;
17
+ mimeType: string;
18
+ }
19
+ /**
20
+ * 检测输入类型
21
+ */
22
+ export declare function detectInputType(input: string): ImageInputType;
23
+ /**
24
+ * 从文件扩展名获取 MIME 类型
25
+ */
26
+ export declare function getMimeTypeFromFileName(filename: string): string;
27
+ /**
28
+ * 从 data URL 提取 MIME 类型
29
+ */
30
+ export declare function getMimeTypeFromDataUrl(dataUrl: string): string;
31
+ /**
32
+ * 检测 URL 是否指向图片文件
33
+ */
34
+ export declare function isImageUrl(url: string): boolean;
35
+ /**
36
+ * 规范化图片输入为统一的 data URL 格式
37
+ */
38
+ export declare function normalizeImageInput(input: string): Promise<NormalizedImageInput>;
39
+ /**
40
+ * 获取图片大小信息(用于日志)
41
+ */
42
+ export declare function getImageSizeInfo(dataUrl: string): {
43
+ size: number;
44
+ };
45
+ //# sourceMappingURL=image-input.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"image-input.d.ts","sourceRoot":"","sources":["../../../src/utils/image-input.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAMH;;GAEG;AACH,MAAM,MAAM,cAAc,GAAG,KAAK,GAAG,QAAQ,GAAG,OAAO,CAAC;AAExD;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,cAAc,CAAC;IACrB,aAAa,EAAE,MAAM,CAAC;IACtB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAyBD;;GAEG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,cAAc,CAa7D;AAED;;GAEG;AACH,wBAAgB,uBAAuB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAYhE;AAED;;GAEG;AACH,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAoB9D;AAED;;GAEG;AACH,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAY/C;AAED;;GAEG;AACH,wBAAsB,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAgCtF;AA0JD;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,CAalE"}
@@ -0,0 +1,226 @@
1
+ /**
2
+ * Image Input Normalization Utility
3
+ *
4
+ * @description 统一处理图片输入:支持 URL、base64(data URL) 和本地路径
5
+ */
6
+ import { readFile } from 'fs/promises';
7
+ import { InvalidInputError, ImageLoadError, VisionMCPError } from './errors.js';
8
+ import { logger } from './logger.js';
9
+ /**
10
+ * 支持的图片 MIME 类型
11
+ * 仅支持 GLM 和 SiliconFlow 明确支持的格式(保守列表)
12
+ * D-002: Only support image input types supported by GLM/SiliconFlow
13
+ */
14
+ const SUPPORTED_MIME_TYPES = [
15
+ 'image/jpeg',
16
+ 'image/jpg',
17
+ 'image/png',
18
+ 'image/webp'
19
+ ];
20
+ /**
21
+ * 文件扩展名到 MIME 类型的映射
22
+ * 仅支持 GLM 和 SiliconFlow 明确支持的格式
23
+ */
24
+ const EXT_TO_MIME_TYPE = {
25
+ '.jpg': 'image/jpeg',
26
+ '.jpeg': 'image/jpeg',
27
+ '.png': 'image/png',
28
+ '.webp': 'image/webp'
29
+ };
30
+ /**
31
+ * 检测输入类型
32
+ */
33
+ export function detectInputType(input) {
34
+ // 检测 base64 data URL
35
+ if (input.startsWith('data:image/') && input.includes(';base64,')) {
36
+ return 'base64';
37
+ }
38
+ // 检测 URL(http 或 https)
39
+ if (input.startsWith('http://') || input.startsWith('https://')) {
40
+ return 'url';
41
+ }
42
+ // 其他情况视为本地路径
43
+ return 'local';
44
+ }
45
+ /**
46
+ * 从文件扩展名获取 MIME 类型
47
+ */
48
+ export function getMimeTypeFromFileName(filename) {
49
+ const ext = filename.toLowerCase().substring(filename.lastIndexOf('.'));
50
+ const mimeType = EXT_TO_MIME_TYPE[ext];
51
+ if (!mimeType) {
52
+ throw new InvalidInputError(`Unsupported file extension: ${ext}`, { filename, supportedExtensions: Object.keys(EXT_TO_MIME_TYPE) });
53
+ }
54
+ return mimeType;
55
+ }
56
+ /**
57
+ * 从 data URL 提取 MIME 类型
58
+ */
59
+ export function getMimeTypeFromDataUrl(dataUrl) {
60
+ const match = dataUrl.match(/^data:(image\/[^;]+);base64,/i);
61
+ if (!match) {
62
+ throw new InvalidInputError('Invalid data URL format. Expected: data:image/*;base64,...', { dataUrl });
63
+ }
64
+ const mimeType = match[1].toLowerCase();
65
+ if (!SUPPORTED_MIME_TYPES.includes(mimeType)) {
66
+ throw new InvalidInputError(`Unsupported image MIME type: ${mimeType}`, { supportedTypes: SUPPORTED_MIME_TYPES });
67
+ }
68
+ return mimeType;
69
+ }
70
+ /**
71
+ * 检测 URL 是否指向图片文件
72
+ */
73
+ export function isImageUrl(url) {
74
+ try {
75
+ const urlObj = new URL(url);
76
+ const pathname = urlObj.pathname.toLowerCase();
77
+ return Object.keys(EXT_TO_MIME_TYPE).some(ext => pathname.endsWith(ext));
78
+ }
79
+ catch {
80
+ // 无效的 URL
81
+ return false;
82
+ }
83
+ }
84
+ /**
85
+ * 规范化图片输入为统一的 data URL 格式
86
+ */
87
+ export async function normalizeImageInput(input) {
88
+ const type = detectInputType(input);
89
+ logger.debug('Normalizing image input', { type, inputLength: input.length });
90
+ try {
91
+ switch (type) {
92
+ case 'base64':
93
+ return await normalizeBase64Input(input);
94
+ case 'url':
95
+ return await normalizeUrlInput(input);
96
+ case 'local':
97
+ return await normalizeLocalInput(input);
98
+ default:
99
+ throw new InvalidInputError(`Unsupported input type: ${type}`, { input, detectedType: type });
100
+ }
101
+ }
102
+ catch (error) {
103
+ if (error instanceof VisionMCPError) {
104
+ throw error;
105
+ }
106
+ throw new ImageLoadError(`Failed to normalize image input`, { input, type }, error);
107
+ }
108
+ }
109
+ /**
110
+ * 规范化 base64 data URL 输入
111
+ */
112
+ async function normalizeBase64Input(input) {
113
+ try {
114
+ // 验证格式
115
+ if (!input.includes(',') || input.split(',').length !== 2) {
116
+ throw new InvalidInputError('Invalid data URL format. Expected: data:image/*;base64,DATA');
117
+ }
118
+ const mimeType = getMimeTypeFromDataUrl(input);
119
+ const dataPart = input.split(',')[1];
120
+ // 验证 base64 数据
121
+ if (!dataPart || dataPart.length === 0) {
122
+ throw new InvalidInputError('No base64 data found in data URL');
123
+ }
124
+ logger.debug('Base64 input validated', { mimeType, dataLength: dataPart.length });
125
+ return {
126
+ type: 'base64',
127
+ originalInput: input,
128
+ dataUrl: input,
129
+ mimeType
130
+ };
131
+ }
132
+ catch (error) {
133
+ throw new ImageLoadError('Invalid base64 data URL', { input }, error);
134
+ }
135
+ }
136
+ /**
137
+ * 规范化 URL 输入
138
+ *
139
+ * @description 当前直接返回 URL,实际场景中可以下载并转换为 base64
140
+ */
141
+ async function normalizeUrlInput(input) {
142
+ try {
143
+ // 验证 URL 格式
144
+ const url = new URL(input);
145
+ if (!url.protocol.startsWith('http')) {
146
+ throw new InvalidInputError('Only HTTP/HTTPS URLs are supported', { protocol: url.protocol });
147
+ }
148
+ // 检测是否为图片 URL(可选)
149
+ if (!isImageUrl(input)) {
150
+ logger.warn('URL does not appear to point to an image file', { url: input });
151
+ }
152
+ logger.debug('URL input validated', { url: input });
153
+ // 注意:SiliconFlow 和 GLM 都直接支持 URL
154
+ // 这里我们保持为 URL 格式,适配器会处理
155
+ return {
156
+ type: 'url',
157
+ originalInput: input,
158
+ dataUrl: input, // 保持为 URL
159
+ mimeType: 'image/*' // 实际类型由模型确定
160
+ };
161
+ }
162
+ catch (error) {
163
+ if (error instanceof TypeError) {
164
+ throw new InvalidInputError('Invalid URL format', { input }, error);
165
+ }
166
+ throw new ImageLoadError('Failed to process URL input', { input }, error);
167
+ }
168
+ }
169
+ /**
170
+ * 规范化本地文件路径输入
171
+ *
172
+ * @description 读取本地文件并转换为 base64 data URL
173
+ */
174
+ async function normalizeLocalInput(input) {
175
+ try {
176
+ logger.debug('Reading local file', { path: input });
177
+ // 读取文件
178
+ const fileBuffer = await readFile(input);
179
+ if (fileBuffer.length === 0) {
180
+ throw new ImageLoadError('File is empty', { path: input });
181
+ }
182
+ // 检测 MIME 类型
183
+ const mimeType = getMimeTypeFromFileName(input);
184
+ // 转换为 base64
185
+ const base64Data = fileBuffer.toString('base64');
186
+ const dataUrl = `data:${mimeType};base64,${base64Data}`;
187
+ logger.debug('Local file converted to base64', {
188
+ path: input,
189
+ mimeType,
190
+ size: fileBuffer.length,
191
+ dataUrlLength: dataUrl.length
192
+ });
193
+ return {
194
+ type: 'local',
195
+ originalInput: input,
196
+ dataUrl,
197
+ mimeType
198
+ };
199
+ }
200
+ catch (error) {
201
+ if (error.code === 'ENOENT') {
202
+ throw new ImageLoadError('File not found', { path: input }, error);
203
+ }
204
+ if (error.code === 'EACCES') {
205
+ throw new ImageLoadError('Permission denied reading file', { path: input }, error);
206
+ }
207
+ throw new ImageLoadError('Failed to read local file', { path: input }, error);
208
+ }
209
+ }
210
+ /**
211
+ * 获取图片大小信息(用于日志)
212
+ */
213
+ export function getImageSizeInfo(dataUrl) {
214
+ try {
215
+ const base64Data = dataUrl.includes(',')
216
+ ? dataUrl.split(',')[1]
217
+ : dataUrl;
218
+ // 计算大小(base64 编码的原始数据)
219
+ const decodedSize = Math.floor(base64Data.length * 0.75);
220
+ return { size: decodedSize };
221
+ }
222
+ catch {
223
+ return { size: 0 };
224
+ }
225
+ }
226
+ //# sourceMappingURL=image-input.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"image-input.js","sourceRoot":"","sources":["../../../src/utils/image-input.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,63 @@
1
+ /**
2
+ * Logger Utility
3
+ *
4
+ * @description 统一的日志工具,确保只使用 stdout 输出
5
+ * 符合 MCP 协议要求:stdout 用于 JSON-RPC 通信,日志使用 stderr
6
+ */
7
+ export declare enum LogLevel {
8
+ DEBUG = 0,
9
+ INFO = 1,
10
+ WARN = 2,
11
+ ERROR = 3
12
+ }
13
+ export interface LogEntry {
14
+ timestamp: string;
15
+ level: keyof typeof LogLevel;
16
+ message: string;
17
+ requestId?: string;
18
+ modelType?: string;
19
+ latency?: number;
20
+ [key: string]: any;
21
+ }
22
+ export declare class Logger {
23
+ private static instance;
24
+ private logLevel;
25
+ private constructor();
26
+ static getInstance(): Logger;
27
+ /**
28
+ * 写入日志到 stderr
29
+ * @param entry 日志条目
30
+ */
31
+ private write;
32
+ /**
33
+ * 记录 DEBUG 级别日志
34
+ */
35
+ debug(message: string, meta?: Record<string, any>): void;
36
+ /**
37
+ * 记录 INFO 级别日志
38
+ */
39
+ info(message: string, meta?: Record<string, any>): void;
40
+ /**
41
+ * 记录 WARN 级别日志
42
+ */
43
+ warn(message: string, meta?: Record<string, any>): void;
44
+ /**
45
+ * 记录 ERROR 级别日志
46
+ */
47
+ error(message: string, error?: Error | unknown, meta?: Record<string, any>): void;
48
+ /**
49
+ * 为请求添加上下文信息的日志记录
50
+ */
51
+ logRequest(message: string, context: {
52
+ requestId?: string;
53
+ modelType?: string;
54
+ latency?: number;
55
+ [key: string]: any;
56
+ }): void;
57
+ }
58
+ export declare const logger: Logger;
59
+ /**
60
+ * 装饰器:记录函数执行时间和错误
61
+ */
62
+ export declare function withLogging(target: any, propertyKey: string, descriptor: PropertyDescriptor): PropertyDescriptor;
63
+ //# sourceMappingURL=logger.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../../../src/utils/logger.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,oBAAY,QAAQ;IAClB,KAAK,IAAI;IACT,IAAI,IAAI;IACR,IAAI,IAAI;IACR,KAAK,IAAI;CACV;AAED,MAAM,WAAW,QAAQ;IACvB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,OAAO,QAAQ,CAAC;IAC7B,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC;CACpB;AAED,qBAAa,MAAM;IACjB,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAW;IAE3B,OAAO;WAkBO,WAAW,IAAI,MAAM;IAOnC;;;OAGG;IACH,OAAO,CAAC,KAAK;IAKb;;OAEG;IACI,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,IAAI;IAW/D;;OAEG;IACI,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,IAAI;IAW9D;;OAEG;IACI,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,IAAI;IAW9D;;OAEG;IACI,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,KAAK,GAAG,OAAO,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,IAAI;IAyBxF;;OAEG;IACI,UAAU,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE;QAC1C,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC;KACpB,GAAG,IAAI;CAUT;AAGD,eAAO,MAAM,MAAM,QAAuB,CAAC;AAE3C;;GAEG;AACH,wBAAgB,WAAW,CAAC,MAAM,EAAE,GAAG,EAAE,WAAW,EAAE,MAAM,EAAE,UAAU,EAAE,kBAAkB,sBAgC3F"}
@@ -0,0 +1,157 @@
1
+ /**
2
+ * Logger Utility
3
+ *
4
+ * @description 统一的日志工具,确保只使用 stdout 输出
5
+ * 符合 MCP 协议要求:stdout 用于 JSON-RPC 通信,日志使用 stderr
6
+ */
7
+ export var LogLevel;
8
+ (function (LogLevel) {
9
+ LogLevel[LogLevel["DEBUG"] = 0] = "DEBUG";
10
+ LogLevel[LogLevel["INFO"] = 1] = "INFO";
11
+ LogLevel[LogLevel["WARN"] = 2] = "WARN";
12
+ LogLevel[LogLevel["ERROR"] = 3] = "ERROR";
13
+ })(LogLevel || (LogLevel = {}));
14
+ export class Logger {
15
+ static instance;
16
+ logLevel;
17
+ constructor() {
18
+ // 从环境变量读取日志级别,默认为 INFO
19
+ const envLevel = process.env.LOG_LEVEL?.toUpperCase();
20
+ switch (envLevel) {
21
+ case 'DEBUG':
22
+ this.logLevel = LogLevel.DEBUG;
23
+ break;
24
+ case 'WARN':
25
+ this.logLevel = LogLevel.WARN;
26
+ break;
27
+ case 'ERROR':
28
+ this.logLevel = LogLevel.ERROR;
29
+ break;
30
+ default:
31
+ this.logLevel = LogLevel.INFO;
32
+ }
33
+ }
34
+ static getInstance() {
35
+ if (!Logger.instance) {
36
+ Logger.instance = new Logger();
37
+ }
38
+ return Logger.instance;
39
+ }
40
+ /**
41
+ * 写入日志到 stderr
42
+ * @param entry 日志条目
43
+ */
44
+ write(entry) {
45
+ // MCP 协议要求:stdout 用于 JSON-RPC,日志必须使用 stderr
46
+ console.error(JSON.stringify(entry));
47
+ }
48
+ /**
49
+ * 记录 DEBUG 级别日志
50
+ */
51
+ debug(message, meta) {
52
+ if (this.logLevel <= LogLevel.DEBUG) {
53
+ this.write({
54
+ timestamp: new Date().toISOString(),
55
+ level: 'DEBUG',
56
+ message,
57
+ ...meta
58
+ });
59
+ }
60
+ }
61
+ /**
62
+ * 记录 INFO 级别日志
63
+ */
64
+ info(message, meta) {
65
+ if (this.logLevel <= LogLevel.INFO) {
66
+ this.write({
67
+ timestamp: new Date().toISOString(),
68
+ level: 'INFO',
69
+ message,
70
+ ...meta
71
+ });
72
+ }
73
+ }
74
+ /**
75
+ * 记录 WARN 级别日志
76
+ */
77
+ warn(message, meta) {
78
+ if (this.logLevel <= LogLevel.WARN) {
79
+ this.write({
80
+ timestamp: new Date().toISOString(),
81
+ level: 'WARN',
82
+ message,
83
+ ...meta
84
+ });
85
+ }
86
+ }
87
+ /**
88
+ * 记录 ERROR 级别日志
89
+ */
90
+ error(message, error, meta) {
91
+ if (this.logLevel <= LogLevel.ERROR) {
92
+ const logEntry = {
93
+ timestamp: new Date().toISOString(),
94
+ level: 'ERROR',
95
+ message,
96
+ ...meta
97
+ };
98
+ if (error) {
99
+ if (error instanceof Error) {
100
+ logEntry.error = {
101
+ name: error.name,
102
+ message: error.message,
103
+ stack: error.stack
104
+ };
105
+ }
106
+ else {
107
+ logEntry.error = String(error);
108
+ }
109
+ }
110
+ this.write(logEntry);
111
+ }
112
+ }
113
+ /**
114
+ * 为请求添加上下文信息的日志记录
115
+ */
116
+ logRequest(message, context) {
117
+ const { requestId, modelType, latency, ...rest } = context;
118
+ this.info(message, {
119
+ ...(requestId && { requestId }),
120
+ ...(modelType && { modelType }),
121
+ ...(latency !== undefined && { latency }),
122
+ ...rest
123
+ });
124
+ }
125
+ }
126
+ // 导出单例实例
127
+ export const logger = Logger.getInstance();
128
+ /**
129
+ * 装饰器:记录函数执行时间和错误
130
+ */
131
+ export function withLogging(target, propertyKey, descriptor) {
132
+ const originalMethod = descriptor.value;
133
+ descriptor.value = async function (...args) {
134
+ const startTime = Date.now();
135
+ const requestId = Math.random().toString(36).substring(7);
136
+ logger.debug(`[${propertyKey}] Starting execution`, { requestId });
137
+ try {
138
+ const result = await originalMethod.apply(this, args);
139
+ const latency = Date.now() - startTime;
140
+ logger.logRequest(`[${propertyKey}] Completed successfully`, {
141
+ requestId,
142
+ latency
143
+ });
144
+ return result;
145
+ }
146
+ catch (error) {
147
+ const latency = Date.now() - startTime;
148
+ logger.error(`[${propertyKey}] Execution failed`, error, {
149
+ requestId,
150
+ latency
151
+ });
152
+ throw error;
153
+ }
154
+ };
155
+ return descriptor;
156
+ }
157
+ //# sourceMappingURL=logger.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logger.js","sourceRoot":"","sources":["../../../src/utils/logger.ts"],"names":[],"mappings":"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"}