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.
- package/README.md +1 -6
- package/dist/config.js +22 -100
- package/dist/index.js +14 -13
- package/dist/server.js +512 -449
- package/dist/services/baseService.js +204 -0
- package/dist/services/blockFactory.js +184 -0
- package/dist/services/feishuApiService.js +630 -0
- package/dist/services/feishuBlockService.js +179 -0
- package/dist/services/feishuService.js +475 -0
- package/dist/types/feishuSchema.js +119 -0
- package/dist/utils/cache.js +221 -0
- package/dist/utils/config.js +363 -0
- package/dist/utils/document.js +112 -0
- package/dist/utils/error.js +154 -0
- package/dist/utils/logger.js +257 -0
- package/dist/utils/paramUtils.js +193 -0
- package/package.json +1 -1
|
@@ -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
|
+
}
|