@embrs/user-feedback 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.
@@ -0,0 +1,189 @@
1
+ /**
2
+ * MCP Feedback Collector - 日誌工具
3
+ */
4
+ import fs from 'fs';
5
+ import path from 'path';
6
+ // 日誌級別優先級
7
+ const LOG_LEVELS = {
8
+ error: 0,
9
+ warn: 1,
10
+ info: 2,
11
+ debug: 3,
12
+ silent: 999
13
+ };
14
+ // 日誌顏色
15
+ const LOG_COLORS = {
16
+ error: '\x1b[31m', // 紅色
17
+ warn: '\x1b[33m', // 黃色
18
+ info: '\x1b[36m', // 青色
19
+ debug: '\x1b[37m', // 白色
20
+ silent: ''
21
+ };
22
+ const RESET_COLOR = '\x1b[0m';
23
+ class Logger {
24
+ currentLevel = 'info';
25
+ logFile;
26
+ fileLoggingEnabled = false;
27
+ colorsDisabled = false;
28
+ /**
29
+ * 設置日誌級別
30
+ */
31
+ setLevel(level) {
32
+ this.currentLevel = level;
33
+ }
34
+ /**
35
+ * 獲取當前日誌級別
36
+ */
37
+ getLevel() {
38
+ return this.currentLevel;
39
+ }
40
+ /**
41
+ * 禁用顏色輸出(用於 MCP 模式)
42
+ */
43
+ disableColors() {
44
+ this.colorsDisabled = true;
45
+ }
46
+ /**
47
+ * 啟用檔案日誌記錄
48
+ */
49
+ enableFileLogging(logDir = 'logs') {
50
+ try {
51
+ if (!fs.existsSync(logDir)) {
52
+ fs.mkdirSync(logDir, { recursive: true });
53
+ }
54
+ const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
55
+ this.logFile = path.join(logDir, `mcp-debug-${timestamp}.log`);
56
+ this.fileLoggingEnabled = true;
57
+ const header = `=== MCP Feedback Collector Debug Log ===\n` +
58
+ `Start Time: ${new Date().toISOString()}\n` +
59
+ `Log Level: ${this.currentLevel}\n` +
60
+ `==========================================\n\n`;
61
+ fs.writeFileSync(this.logFile, header);
62
+ console.log(`日誌檔案已建立: ${this.logFile}`);
63
+ }
64
+ catch (error) {
65
+ console.error('無法建立日誌檔案:', error);
66
+ this.fileLoggingEnabled = false;
67
+ }
68
+ }
69
+ /**
70
+ * 檢查是否應該輸出指定級別的日誌
71
+ */
72
+ shouldLog(level) {
73
+ if (this.currentLevel === 'silent') {
74
+ return false;
75
+ }
76
+ return LOG_LEVELS[level] <= LOG_LEVELS[this.currentLevel];
77
+ }
78
+ /**
79
+ * 格式化時間戳
80
+ */
81
+ formatTimestamp() {
82
+ return new Date().toISOString();
83
+ }
84
+ /**
85
+ * 格式化日誌訊息
86
+ */
87
+ formatMessage(level, message, ...args) {
88
+ const timestamp = this.formatTimestamp();
89
+ const levelStr = level.toUpperCase().padEnd(5);
90
+ let formattedMessage;
91
+ if (this.colorsDisabled) {
92
+ formattedMessage = `[${timestamp}] ${levelStr} ${message}`;
93
+ }
94
+ else {
95
+ const color = LOG_COLORS[level];
96
+ formattedMessage = `${color}[${timestamp}] ${levelStr}${RESET_COLOR} ${message}`;
97
+ }
98
+ if (args.length > 0) {
99
+ const argsStr = args.map(arg => typeof arg === 'object' ? JSON.stringify(arg, null, 2) : String(arg)).join(' ');
100
+ formattedMessage += ` ${argsStr}`;
101
+ }
102
+ return formattedMessage;
103
+ }
104
+ /**
105
+ * 輸出日誌
106
+ */
107
+ log(level, message, ...args) {
108
+ if (!this.shouldLog(level))
109
+ return;
110
+ const formattedMessage = this.formatMessage(level, message, ...args);
111
+ if (level === 'error') {
112
+ console.error(formattedMessage);
113
+ }
114
+ else if (level === 'warn') {
115
+ console.warn(formattedMessage);
116
+ }
117
+ else {
118
+ console.log(formattedMessage);
119
+ }
120
+ if (this.fileLoggingEnabled && this.logFile) {
121
+ try {
122
+ const cleanMessage = this.removeColorCodes(formattedMessage);
123
+ fs.appendFileSync(this.logFile, cleanMessage + '\n');
124
+ }
125
+ catch (error) {
126
+ console.error('寫入日誌檔案失敗:', error);
127
+ }
128
+ }
129
+ }
130
+ /**
131
+ * 移除顏色代碼
132
+ */
133
+ removeColorCodes(text) {
134
+ return text.replace(/\x1b\[[0-9;]*m/g, '');
135
+ }
136
+ /**
137
+ * 錯誤日誌
138
+ */
139
+ error(message, ...args) {
140
+ this.log('error', message, ...args);
141
+ }
142
+ /**
143
+ * 警告日誌
144
+ */
145
+ warn(message, ...args) {
146
+ this.log('warn', message, ...args);
147
+ }
148
+ /**
149
+ * 資訊日誌
150
+ */
151
+ info(message, ...args) {
152
+ this.log('info', message, ...args);
153
+ }
154
+ /**
155
+ * 除錯日誌
156
+ */
157
+ debug(message, ...args) {
158
+ this.log('debug', message, ...args);
159
+ }
160
+ /**
161
+ * 記錄 HTTP 請求
162
+ */
163
+ request(method, url, statusCode, duration) {
164
+ const parts = [method.toUpperCase(), url];
165
+ if (statusCode !== undefined)
166
+ parts.push(`${statusCode}`);
167
+ if (duration !== undefined)
168
+ parts.push(`${duration}ms`);
169
+ this.info(`HTTP ${parts.join(' ')}`);
170
+ }
171
+ /**
172
+ * 記錄 WebSocket 事件
173
+ */
174
+ socket(event, sessionId, data) {
175
+ const parts = ['WebSocket', event];
176
+ if (sessionId)
177
+ parts.push(`session:${sessionId}`);
178
+ this.debug(parts.join(' '), data);
179
+ }
180
+ /**
181
+ * 記錄 MCP 工具調用
182
+ */
183
+ mcp(tool, params, result) {
184
+ this.info(`MCP Tool: ${tool}`, { params, result });
185
+ }
186
+ }
187
+ // 建立全域日誌實例
188
+ export const logger = new Logger();
189
+ //# 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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"}
package/package.json ADDED
@@ -0,0 +1,79 @@
1
+ {
2
+ "name": "@embrs/user-feedback",
3
+ "version": "1.0.0",
4
+ "description": "基於 Node.js 的 MCP 反饋收集器 - 支援 AI 工作彙報和用戶反饋收集",
5
+ "main": "dist/index.js",
6
+ "bin": {
7
+ "user-feedback": "dist/cli.js"
8
+ },
9
+ "repository": {
10
+ "type": "git",
11
+ "url": "git+https://github.com/Embrs/user-feedback.git"
12
+ },
13
+ "homepage": "https://github.com/Embrs/user-feedback#readme",
14
+ "bugs": {
15
+ "url": "https://github.com/Embrs/user-feedback/issues"
16
+ },
17
+ "type": "module",
18
+ "engines": {
19
+ "node": ">=18.0.0"
20
+ },
21
+ "keywords": [
22
+ "mcp",
23
+ "feedback",
24
+ "ai",
25
+ "chat",
26
+ "nodejs",
27
+ "model-context-protocol",
28
+ "claude",
29
+ "anthropic"
30
+ ],
31
+ "author": "Embrs",
32
+ "license": "MIT",
33
+ "scripts": {
34
+ "build": "tsc && npm run copy-static",
35
+ "copy-static": "node -e \"const fs=require('fs'); const path=require('path'); if(fs.existsSync('src/static')){fs.cpSync('src/static', 'dist/static', {recursive: true})}\"",
36
+ "dev": "tsx watch --clear-screen=false src/cli.ts",
37
+ "start": "node dist/cli.js",
38
+ "test": "jest",
39
+ "test:watch": "jest --watch",
40
+ "lint": "eslint src/ --ext .ts",
41
+ "lint:fix": "eslint src/ --ext .ts --fix",
42
+ "clean": "node -e \"const fs=require('fs'); if(fs.existsSync('dist')){fs.rmSync('dist', {recursive: true, force: true})}\"",
43
+ "prepublishOnly": "npm run clean && npm run build"
44
+ },
45
+ "dependencies": {
46
+ "@modelcontextprotocol/sdk": "^1.12.1",
47
+ "express": "^4.18.2",
48
+ "socket.io": "^4.7.2",
49
+ "jimp": "^0.22.10",
50
+ "open": "^9.1.0",
51
+ "find-free-port": "^2.0.0",
52
+ "dotenv": "^16.3.1",
53
+ "cors": "^2.8.5",
54
+ "helmet": "^7.1.0",
55
+ "compression": "^1.7.4",
56
+ "commander": "^11.1.0",
57
+ "node-fetch": "^3.3.2",
58
+ "zod": "^3.22.4"
59
+ },
60
+ "devDependencies": {
61
+ "@types/node": "^20.8.0",
62
+ "@types/express": "^4.17.17",
63
+ "@types/cors": "^2.8.14",
64
+ "@types/compression": "^1.7.3",
65
+ "@types/jest": "^29.5.5",
66
+ "@typescript-eslint/eslint-plugin": "^6.7.0",
67
+ "@typescript-eslint/parser": "^6.7.0",
68
+ "eslint": "^8.50.0",
69
+ "jest": "^29.7.0",
70
+ "ts-jest": "^29.1.1",
71
+ "tsx": "^3.14.0",
72
+ "typescript": "^5.2.2"
73
+ },
74
+ "files": [
75
+ "dist",
76
+ "README.md",
77
+ "LICENSE"
78
+ ]
79
+ }