feishu-mcp 0.0.6 → 0.0.8

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.
@@ -0,0 +1,257 @@
1
+ /**
2
+ * 日志级别枚举
3
+ */
4
+ export var LogLevel;
5
+ (function (LogLevel) {
6
+ LogLevel[LogLevel["DEBUG"] = 0] = "DEBUG";
7
+ LogLevel[LogLevel["INFO"] = 1] = "INFO";
8
+ LogLevel[LogLevel["LOG"] = 2] = "LOG";
9
+ LogLevel[LogLevel["WARN"] = 3] = "WARN";
10
+ LogLevel[LogLevel["ERROR"] = 4] = "ERROR";
11
+ LogLevel[LogLevel["NONE"] = 5] = "NONE";
12
+ })(LogLevel || (LogLevel = {}));
13
+ // 导入文件系统模块
14
+ import * as fs from 'fs';
15
+ import * as path from 'path';
16
+ /**
17
+ * 增强的日志管理器类
18
+ * 提供可配置的日志记录功能,支持不同日志级别和格式化
19
+ */
20
+ export class Logger {
21
+ /**
22
+ * 配置日志管理器
23
+ * @param config 日志配置项
24
+ */
25
+ static configure(config) {
26
+ this.config = { ...this.config, ...config };
27
+ // 确保日志目录存在
28
+ if (this.config.logToFile) {
29
+ const logDir = path.dirname(this.config.logFilePath);
30
+ if (!fs.existsSync(logDir)) {
31
+ fs.mkdirSync(logDir, { recursive: true });
32
+ }
33
+ }
34
+ }
35
+ /**
36
+ * 格式化日志消息
37
+ * @param level 日志级别
38
+ * @param args 日志参数
39
+ * @returns 格式化后的日志字符串数组
40
+ */
41
+ static formatLogMessage(level, args) {
42
+ const result = [];
43
+ // 添加时间戳
44
+ if (this.config.showTimestamp) {
45
+ const now = new Date();
46
+ const timestamp = this.formatDate(now, this.config.timestampFormat || 'yyyy-MM-dd HH:mm:ss.SSS');
47
+ result.push(`[${timestamp}]`);
48
+ }
49
+ // 添加日志级别
50
+ if (this.config.showLevel) {
51
+ const levelStr = LogLevel[level].padEnd(5, ' ');
52
+ result.push(`[${levelStr}]`);
53
+ }
54
+ // 添加原始日志内容
55
+ return [...result, ...args];
56
+ }
57
+ /**
58
+ * 将日志写入文件
59
+ * @param logParts 日志内容部分
60
+ */
61
+ static writeToFile(logParts) {
62
+ if (!this.config.logToFile)
63
+ return;
64
+ try {
65
+ // 将日志内容转换为字符串
66
+ let logString = '';
67
+ for (const part of logParts) {
68
+ if (typeof part === 'object') {
69
+ try {
70
+ // 简化对象序列化
71
+ logString += this.safeStringify(part) + ' ';
72
+ }
73
+ catch (e) {
74
+ logString += '[Object] ';
75
+ }
76
+ }
77
+ else {
78
+ logString += part + ' ';
79
+ }
80
+ }
81
+ // 添加换行符
82
+ logString += '\n';
83
+ // 以追加模式写入文件
84
+ fs.appendFileSync(this.config.logFilePath, logString);
85
+ }
86
+ catch (error) {
87
+ console.error('写入日志文件失败:', error);
88
+ }
89
+ }
90
+ /**
91
+ * 安全的对象序列化,限制深度和长度
92
+ * @param obj 要序列化的对象
93
+ * @returns 序列化后的字符串
94
+ */
95
+ static safeStringify(obj) {
96
+ const seen = new Set();
97
+ const stringified = JSON.stringify(obj, (key, value) => {
98
+ // 处理循环引用
99
+ if (typeof value === 'object' && value !== null) {
100
+ if (seen.has(value)) {
101
+ return '[Circular]';
102
+ }
103
+ seen.add(value);
104
+ }
105
+ // 处理请求/响应对象
106
+ if (key === 'request' || key === 'socket' || key === 'agent' ||
107
+ key === '_events' || key === '_eventsCount' || key === '_maxListeners' ||
108
+ key === 'rawHeaders' || key === 'rawTrailers') {
109
+ return '[Object]';
110
+ }
111
+ return value;
112
+ }, 2);
113
+ if (stringified && stringified.length > this.config.maxObjectStringLength) {
114
+ return stringified.substring(0, this.config.maxObjectStringLength) + '... [截断]';
115
+ }
116
+ return stringified;
117
+ }
118
+ /**
119
+ * 格式化日期
120
+ * @param date 日期对象
121
+ * @param format 格式字符串
122
+ * @returns 格式化后的日期字符串
123
+ */
124
+ static formatDate(date, format) {
125
+ const year = date.getFullYear().toString();
126
+ const month = (date.getMonth() + 1).toString().padStart(2, '0');
127
+ const day = date.getDate().toString().padStart(2, '0');
128
+ const hours = date.getHours().toString().padStart(2, '0');
129
+ const minutes = date.getMinutes().toString().padStart(2, '0');
130
+ const seconds = date.getSeconds().toString().padStart(2, '0');
131
+ const milliseconds = date.getMilliseconds().toString().padStart(3, '0');
132
+ return format
133
+ .replace('yyyy', year)
134
+ .replace('MM', month)
135
+ .replace('dd', day)
136
+ .replace('HH', hours)
137
+ .replace('mm', minutes)
138
+ .replace('ss', seconds)
139
+ .replace('SSS', milliseconds);
140
+ }
141
+ /**
142
+ * 记录调试级别日志
143
+ * @param args 日志参数
144
+ */
145
+ static debug(...args) {
146
+ if (this.config.minLevel <= LogLevel.DEBUG) {
147
+ const formattedMessage = this.formatLogMessage(LogLevel.DEBUG, args);
148
+ console.debug(...formattedMessage);
149
+ this.writeToFile(formattedMessage);
150
+ }
151
+ }
152
+ /**
153
+ * 记录信息级别日志
154
+ * @param args 日志参数
155
+ */
156
+ static info(...args) {
157
+ if (this.config.minLevel <= LogLevel.INFO) {
158
+ const formattedMessage = this.formatLogMessage(LogLevel.INFO, args);
159
+ console.info(...formattedMessage);
160
+ this.writeToFile(formattedMessage);
161
+ }
162
+ }
163
+ /**
164
+ * 记录普通级别日志
165
+ * @param args 日志参数
166
+ */
167
+ static log(...args) {
168
+ if (this.config.minLevel <= LogLevel.LOG) {
169
+ const formattedMessage = this.formatLogMessage(LogLevel.LOG, args);
170
+ console.log(...formattedMessage);
171
+ this.writeToFile(formattedMessage);
172
+ }
173
+ }
174
+ /**
175
+ * 记录警告级别日志
176
+ * @param args 日志参数
177
+ */
178
+ static warn(...args) {
179
+ if (this.config.minLevel <= LogLevel.WARN) {
180
+ const formattedMessage = this.formatLogMessage(LogLevel.WARN, args);
181
+ console.warn(...formattedMessage);
182
+ this.writeToFile(formattedMessage);
183
+ }
184
+ }
185
+ /**
186
+ * 记录错误级别日志
187
+ * @param args 日志参数
188
+ */
189
+ static error(...args) {
190
+ if (this.config.minLevel <= LogLevel.ERROR) {
191
+ const formattedMessage = this.formatLogMessage(LogLevel.ERROR, args);
192
+ console.error(...formattedMessage);
193
+ this.writeToFile(formattedMessage);
194
+ }
195
+ }
196
+ /**
197
+ * 记录请求和响应的详细信息
198
+ * @param method 请求方法
199
+ * @param url 请求URL
200
+ * @param data 请求数据
201
+ * @param response 响应数据
202
+ * @param statusCode 响应状态码
203
+ */
204
+ static logApiCall(method, url, data, response, statusCode) {
205
+ if (this.config.minLevel <= LogLevel.DEBUG) {
206
+ this.debug('API调用详情:');
207
+ this.debug(`请求: ${method} ${url}`);
208
+ // 简化请求数据记录
209
+ if (data) {
210
+ try {
211
+ if (typeof data === 'string') {
212
+ // 尝试解析JSON字符串
213
+ const parsedData = JSON.parse(data);
214
+ this.debug('请求数据:', parsedData);
215
+ }
216
+ else {
217
+ this.debug('请求数据:', data);
218
+ }
219
+ }
220
+ catch (e) {
221
+ this.debug('请求数据:', data);
222
+ }
223
+ }
224
+ else {
225
+ this.debug('请求数据: None');
226
+ }
227
+ this.debug(`响应状态: ${statusCode}`);
228
+ // 简化响应数据记录
229
+ if (response) {
230
+ // 只记录关键信息
231
+ const simplifiedResponse = response.data ? { data: response.data } : response;
232
+ this.debug('响应数据:', simplifiedResponse);
233
+ }
234
+ else {
235
+ this.debug('响应数据: None');
236
+ }
237
+ }
238
+ else {
239
+ this.info(`API调用: ${method} ${url} - 状态码: ${statusCode}`);
240
+ }
241
+ }
242
+ }
243
+ Object.defineProperty(Logger, "config", {
244
+ enumerable: true,
245
+ configurable: true,
246
+ writable: true,
247
+ value: {
248
+ minLevel: LogLevel.DEBUG, // 修改为DEBUG级别,确保捕获所有日志
249
+ showTimestamp: true,
250
+ showLevel: true,
251
+ timestampFormat: 'yyyy-MM-dd HH:mm:ss.SSS',
252
+ logToFile: false,
253
+ logFilePath: 'log/log.txt',
254
+ maxObjectDepth: 2, // 限制对象序列化深度
255
+ maxObjectStringLength: 5000000 // 限制序列化后字符串长度
256
+ }
257
+ });
@@ -0,0 +1,193 @@
1
+ import { normalizeDocumentId, normalizeWikiToken } from './document.js';
2
+ import { Logger } from './logger.js';
3
+ import { formatErrorMessage } from './error.js';
4
+ /**
5
+ * 参数验证错误
6
+ */
7
+ export class ParamValidationError extends Error {
8
+ constructor(param, message) {
9
+ super(message);
10
+ Object.defineProperty(this, "param", {
11
+ enumerable: true,
12
+ configurable: true,
13
+ writable: true,
14
+ value: void 0
15
+ });
16
+ this.name = 'ParamValidationError';
17
+ this.param = param;
18
+ }
19
+ }
20
+ /**
21
+ * 参数处理工具类
22
+ * 提供参数验证、转换和处理功能
23
+ */
24
+ export class ParamUtils {
25
+ /**
26
+ * 处理文档ID参数
27
+ * 验证并规范化文档ID
28
+ *
29
+ * @param documentId 文档ID或URL
30
+ * @returns 规范化的文档ID
31
+ * @throws 如果文档ID无效则抛出错误
32
+ */
33
+ static processDocumentId(documentId) {
34
+ if (!documentId) {
35
+ throw new ParamValidationError('documentId', '文档ID不能为空');
36
+ }
37
+ try {
38
+ return normalizeDocumentId(documentId);
39
+ }
40
+ catch (error) {
41
+ throw new ParamValidationError('documentId', formatErrorMessage(error));
42
+ }
43
+ }
44
+ /**
45
+ * 处理Wiki Token参数
46
+ * 验证并规范化Wiki Token
47
+ *
48
+ * @param wikiUrl Wiki URL或Token
49
+ * @returns 规范化的Wiki Token
50
+ * @throws 如果Wiki Token无效则抛出错误
51
+ */
52
+ static processWikiToken(wikiUrl) {
53
+ if (!wikiUrl) {
54
+ throw new ParamValidationError('wikiUrl', 'Wiki URL不能为空');
55
+ }
56
+ try {
57
+ return normalizeWikiToken(wikiUrl);
58
+ }
59
+ catch (error) {
60
+ throw new ParamValidationError('wikiUrl', formatErrorMessage(error));
61
+ }
62
+ }
63
+ /**
64
+ * 处理块ID参数
65
+ * 验证块ID是否有效
66
+ *
67
+ * @param blockId 块ID
68
+ * @returns 验证后的块ID
69
+ * @throws 如果块ID无效则抛出错误
70
+ */
71
+ static processBlockId(blockId) {
72
+ if (!blockId) {
73
+ throw new ParamValidationError('blockId', '块ID不能为空');
74
+ }
75
+ if (!/^[a-zA-Z0-9_-]{5,}$/.test(blockId)) {
76
+ throw new ParamValidationError('blockId', '块ID格式无效');
77
+ }
78
+ return blockId;
79
+ }
80
+ /**
81
+ * 处理父块ID参数
82
+ * 验证父块ID是否有效
83
+ *
84
+ * @param parentBlockId 父块ID
85
+ * @returns 验证后的父块ID
86
+ * @throws 如果父块ID无效则抛出错误
87
+ */
88
+ static processParentBlockId(parentBlockId) {
89
+ if (!parentBlockId) {
90
+ throw new ParamValidationError('parentBlockId', '父块ID不能为空');
91
+ }
92
+ if (!/^[a-zA-Z0-9_-]{5,}$/.test(parentBlockId)) {
93
+ throw new ParamValidationError('parentBlockId', '父块ID格式无效');
94
+ }
95
+ return parentBlockId;
96
+ }
97
+ /**
98
+ * 处理插入位置索引参数
99
+ * 验证并规范化索引值
100
+ *
101
+ * @param index 插入位置索引
102
+ * @returns 验证后的索引值
103
+ * @throws 如果索引无效则抛出错误
104
+ */
105
+ static processIndex(index) {
106
+ if (index === undefined || index === null) {
107
+ return 0; // 默认值
108
+ }
109
+ if (!Number.isInteger(index) || index < 0) {
110
+ throw new ParamValidationError('index', '索引必须是非负整数');
111
+ }
112
+ return index;
113
+ }
114
+ /**
115
+ * 处理对齐方式参数
116
+ * 验证并规范化对齐方式值
117
+ *
118
+ * @param align 对齐方式
119
+ * @returns 验证后的对齐方式值
120
+ */
121
+ static processAlign(align) {
122
+ if (align === undefined || align === null) {
123
+ return 1; // 默认左对齐
124
+ }
125
+ if (![1, 2, 3].includes(align)) {
126
+ Logger.warn(`对齐方式值 ${align} 无效,使用默认值1(左对齐)`);
127
+ return 1;
128
+ }
129
+ return align;
130
+ }
131
+ /**
132
+ * 处理语言类型参数
133
+ * 验证并规范化语言类型值
134
+ *
135
+ * @param language 语言类型
136
+ * @returns 验证后的语言类型值
137
+ */
138
+ static processLanguage(language) {
139
+ if (language === undefined || language === null) {
140
+ return 1; // 默认纯文本
141
+ }
142
+ if (!Number.isInteger(language) || language < 1 || language > 71) {
143
+ Logger.warn(`语言类型值 ${language} 无效,使用默认值1(纯文本)`);
144
+ return 1;
145
+ }
146
+ return language;
147
+ }
148
+ /**
149
+ * 处理标题级别参数
150
+ * 验证并规范化标题级别值
151
+ *
152
+ * @param level 标题级别
153
+ * @returns 验证后的标题级别值
154
+ */
155
+ static processHeadingLevel(level) {
156
+ if (level === undefined || level === null) {
157
+ return 1; // 默认一级标题
158
+ }
159
+ // 限制在1-9范围内
160
+ return Math.max(1, Math.min(9, level));
161
+ }
162
+ /**
163
+ * 批量处理通用参数
164
+ * 验证并规范化常用参数集
165
+ *
166
+ * @param params 通用参数对象
167
+ * @returns 处理后的参数对象
168
+ */
169
+ static processCommonParams(params) {
170
+ const result = { ...params };
171
+ // 处理文档ID
172
+ if (params.documentId) {
173
+ result.documentId = ParamUtils.processDocumentId(params.documentId);
174
+ }
175
+ // 处理块ID
176
+ if (params.blockId) {
177
+ result.blockId = ParamUtils.processBlockId(params.blockId);
178
+ }
179
+ // 处理父块ID
180
+ if (params.parentBlockId) {
181
+ result.parentBlockId = ParamUtils.processParentBlockId(params.parentBlockId);
182
+ }
183
+ // 处理索引
184
+ if (params.index !== undefined) {
185
+ result.index = ParamUtils.processIndex(params.index);
186
+ }
187
+ // 处理起始索引
188
+ if (params.startIndex !== undefined) {
189
+ result.startIndex = ParamUtils.processIndex(params.startIndex);
190
+ }
191
+ return result;
192
+ }
193
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "feishu-mcp",
3
- "version": "0.0.6",
3
+ "version": "0.0.8",
4
4
  "description": "Model Context Protocol server for Feishu integration",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",