chaimi-keep-mcp 3.2.1-beta.7 → 3.3.0-beta.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.
package/README.md CHANGED
@@ -148,38 +148,6 @@ AI 会自动调用 `save_income` 工具记录收入。
148
148
 
149
149
  ## 更新日志
150
150
 
151
- ### v3.2.1-beta.7 (2026-04-26)
152
- - **新增** 自动检测 Agent 类型
153
- - 安装时根据配置文件路径自动识别 Agent 类型
154
- - 自动设置 `AGENT_TYPE` 环境变量
155
- - 支持:claude-desktop、cursor、windsurf、workbuddy、openclaw、mcporter
156
-
157
- ### v3.2.1-beta.6 (2026-04-26)
158
- - **修复** agentType 设置问题
159
- - 从环境变量 `AGENT_TYPE` 或 `MCP_AGENT_TYPE` 读取
160
- - 支持用户在 `mcporter.json` 中配置 agentType
161
-
162
- ### v3.2.1-beta.5 (2026-04-26)
163
- - **修复** 复用授权时 token 获取失败问题
164
- - 云函数 `getDeviceToken` 支持 userCode 查询作为备选
165
- - MCP Server 轮询时同时传入 userCode 和 deviceCode
166
- - 彻底解决 deviceCode 不匹配导致的授权失败
167
-
168
- ### v3.2.1-beta.4 (2026-04-26)
169
- - **修复** deviceCode 保存不一致问题
170
- - 小程序端使用授权返回的实际 deviceCode,解决复用授权时 deviceCode 不匹配
171
- - **优化** 回复模板格式
172
- - 统一使用15字符短分隔线格式
173
- - 添加消费洞察变量 `{洞察内容}`
174
- - **更新** 正能量情绪词库
175
- - 41条分类祝福语,更积极向上的结束语
176
-
177
- ### v3.2.1-beta.0 (2026-04-26)
178
- - **新增** 设备指纹机制,防止同一设备重复授权
179
- - 基于机器ID + Agent类型生成唯一设备指纹
180
- - 同一设备重复授权时复用已有记录,避免生成多个agentName
181
- - 支持设备指纹查重,自动识别已授权设备
182
-
183
151
  ### v3.1.49-beta.2 (2026-04-24)
184
152
  - **新增** period 参数支持
185
153
  - getStatistics: 支持 this_week/last_week/this_month/last_month 周期筛选
package/bin/cli.js CHANGED
@@ -125,24 +125,6 @@ function writeJSON(filePath, data) {
125
125
  * 为单个 Agent 安装配置
126
126
  * 原则:只添加缺失的配置,绝不覆盖用户已有配置
127
127
  */
128
- /**
129
- * 根据配置文件路径自动检测 Agent 类型
130
- * @param {string} configPath - 配置文件路径
131
- * @returns {string} Agent 类型标识
132
- */
133
- function detectAgentTypeFromPath(configPath) {
134
- const lowerPath = configPath.toLowerCase();
135
-
136
- if (lowerPath.includes('claude')) return 'claude-desktop';
137
- if (lowerPath.includes('cursor')) return 'cursor';
138
- if (lowerPath.includes('windsurf') || lowerPath.includes('codeium')) return 'windsurf';
139
- if (lowerPath.includes('workbuddy')) return 'workbuddy';
140
- if (lowerPath.includes('openclaw')) return 'openclaw';
141
- if (lowerPath.includes('mcporter') || lowerPath.includes('.mcporter')) return 'mcporter';
142
-
143
- return 'unknown';
144
- }
145
-
146
128
  function installToAgent(agent) {
147
129
  try {
148
130
  // 平台检查
@@ -178,14 +160,7 @@ function installToAgent(agent) {
178
160
  }
179
161
 
180
162
  // 添加柴米记账配置
181
- const chaimiConfig = JSON.parse(JSON.stringify(DEFAULT_CHAIMI_CONFIG));
182
-
183
- // 【新增】自动检测并设置 Agent 类型
184
- const agentType = detectAgentTypeFromPath(agent.configPath);
185
- chaimiConfig.env.AGENT_TYPE = agentType;
186
- console.log(`[配置] 自动设置 Agent 类型: ${agentType} (${agent.name})`);
187
-
188
- config.mcpServers['柴米记账'] = chaimiConfig;
163
+ config.mcpServers['柴米记账'] = JSON.parse(JSON.stringify(DEFAULT_CHAIMI_CONFIG));
189
164
 
190
165
  // 写入配置
191
166
  writeJSON(agent.configPath, config);
package/oauth.js CHANGED
@@ -64,10 +64,8 @@ class OAuthManager {
64
64
  console.error('⏳ 等待用户授权,请勿关闭窗口...');
65
65
  console.error(' (请在手机微信中完成授权操作)');
66
66
  console.error('');
67
- // 【新增】同时传入 userCode,用于复用授权场景
68
67
  const token = await this.pollForToken(
69
68
  deviceCodeRes.deviceCode,
70
- deviceCodeRes.userCode,
71
69
  deviceCodeRes.interval * 1000 || 5000
72
70
  );
73
71
 
@@ -148,7 +146,7 @@ class OAuthManager {
148
146
  await execPromise(command);
149
147
  }
150
148
 
151
- async requestDeviceCode(useUrlScheme = false, extraParams = {}) {
149
+ async requestDeviceCode(useUrlScheme = false) {
152
150
  const response = await fetch(this.mcpOAuthUrl, {
153
151
  method: 'POST',
154
152
  headers: {
@@ -158,8 +156,7 @@ class OAuthManager {
158
156
  tool: 'deviceCode',
159
157
  params: {
160
158
  clientId: 'chaihuo-mcp-client',
161
- useUrlScheme: useUrlScheme,
162
- ...extraParams
159
+ useUrlScheme: useUrlScheme
163
160
  }
164
161
  })
165
162
  });
@@ -174,14 +171,8 @@ class OAuthManager {
174
171
  }
175
172
 
176
173
  // 单次轮询(用于后台轮询)
177
- async pollForTokenOnce(deviceCode, userCode) {
174
+ async pollForTokenOnce(deviceCode) {
178
175
  try {
179
- const params = { deviceCode };
180
- // 【新增】如果有 userCode,一并传入(用于复用授权场景)
181
- if (userCode) {
182
- params.userCode = userCode;
183
- }
184
-
185
176
  const response = await fetch(this.mcpOAuthUrl, {
186
177
  method: 'POST',
187
178
  headers: {
@@ -189,7 +180,9 @@ class OAuthManager {
189
180
  },
190
181
  body: JSON.stringify({
191
182
  tool: 'deviceToken',
192
- params
183
+ params: {
184
+ deviceCode
185
+ }
193
186
  })
194
187
  });
195
188
 
@@ -223,17 +216,17 @@ class OAuthManager {
223
216
  }
224
217
  }
225
218
 
226
- async pollForToken(deviceCode, userCode, interval) {
219
+ async pollForToken(deviceCode, interval) {
227
220
  const maxAttempts = 60;
228
221
  let attempts = 0;
229
222
 
230
223
  while (attempts < maxAttempts) {
231
224
  attempts++;
225
+
232
226
  await this.delay(interval);
233
227
 
234
228
  try {
235
- // 【新增】传入 userCode,用于复用授权场景
236
- const token = await this.pollForTokenOnce(deviceCode, userCode);
229
+ const token = await this.pollForTokenOnce(deviceCode);
237
230
  if (token) {
238
231
  return token;
239
232
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "chaimi-keep-mcp",
3
- "version": "3.2.1-beta.7",
3
+ "version": "3.3.0-beta.0",
4
4
  "description": "柴米记账 MCP Server - 支持 Claude、Cursor、OpenClaw、WorkBuddy 等 AI 工具直接记账",
5
5
  "main": "server.js",
6
6
  "bin": {
package/server.js CHANGED
@@ -33,9 +33,6 @@ const { OAuthManager, FileTokenStorage } = require('./oauth.js');
33
33
  // 导入校验工具
34
34
  const { validateDate } = require('./utils/validators.js');
35
35
 
36
- // 导入设备指纹模块
37
- const { getDeviceFingerprint, getAgentType, saveAgentType, getMacAddresses } = require('./utils/machine-id.js');
38
-
39
36
  // API 签名密钥(用于验证请求来自合法 MCP Server)
40
37
  const CHAIMI_API_SECRET = 'chaimi-mcp-secret-2024';
41
38
 
@@ -482,8 +479,7 @@ async function getToken() {
482
479
  } else {
483
480
  // 验证码未过期,尝试直接获取token(用户可能已授权)
484
481
  try {
485
- // 【新增】同时传入 userCode,用于复用授权场景
486
- const token = await oauthManager.pollForTokenOnce(savedAuthState.deviceCode, savedAuthState.userCode);
482
+ const token = await oauthManager.pollForTokenOnce(savedAuthState.deviceCode);
487
483
  if (token) {
488
484
  // 授权成功
489
485
  cachedToken = token.accessToken;
@@ -491,7 +487,6 @@ async function getToken() {
491
487
  authState.isAuthorized = true;
492
488
  await oauthManager.tokenStorage.save(token);
493
489
  // 【新增】保存默认 Agent 名称和 deviceCode
494
- // 【修复】使用实际授权的 deviceCode
495
490
  await oauthManager.tokenStorage.saveAgentName('柴米AI助手');
496
491
  await oauthManager.tokenStorage.saveDeviceCode(savedAuthState.deviceCode);
497
492
  await oauthManager.clearAuthState();
@@ -981,7 +976,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
981
976
  // 按 SKILL 规范格式:新的回复模板
982
977
  const displayStoreText = displayStore ? `【${displayStore}】` : '';
983
978
  const dateStr = result.data?.date ? new Date(result.data.date).toLocaleDateString('zh-CN') : new Date().toLocaleDateString('zh-CN');
984
- userMessage = `✅ 「${agentName}」已帮您录入「柴米AI记账」\n\n商品:${displayName}${displayStoreText}\n金额:¥${displayAmount}\n类型:支出\n分类:${result.data?.categoryName || displayCategory}\n时间:${dateStr}\n\n${friendlyEnding}${insightsText}${achievementsText}\n\n---\n柴米AI记账 v${MCP_VERSION}`;
979
+ userMessage = `✅ 「${agentName}」已帮您录入「柴米AI记账」\n · 商品/店名:${displayName}${displayStoreText} 💰¥${displayAmount}\n · 收支类型: 支出\n · 分类:${result.data?.categoryName || displayCategory}\n · 时间:${dateStr}\n${friendlyEnding}${insightsText}${achievementsText}\n-------------\nchaimi-keep-mcp: v${MCP_VERSION}`;
985
980
 
986
981
  if (!processedArgs.agentType || !processedArgs.apiProvider) {
987
982
  userMessage += '\n\n💡 提示:传递agentType和apiProvider参数可解锁小程序记录来源统计功能,示例:agentType="openclaw", apiProvider="doubao"';
@@ -1225,7 +1220,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
1225
1220
 
1226
1221
  // 按 SKILL 规范格式:新的回复模板
1227
1222
  const dateStr = result.data?.date ? new Date(result.data.date).toLocaleDateString('zh-CN') : new Date().toLocaleDateString('zh-CN');
1228
- userMessage = `✅ 「${agentName}」已帮您录入「柴米AI记账」\n\n店名:${storeName}\n金额:¥${totalAmount}\n类型:支出\n分类:${result.data?.storeCategory || category}\n商品:${itemCount}件\n时间:${dateStr}\n\n${friendlyEnding}${insightsText}${achievementsText}\n\n---\n柴米AI记账 v${MCP_VERSION}`;
1223
+ userMessage = `✅ 「${agentName}」已帮您录入「柴米AI记账」\n · 商品/店名:${storeName} 💰¥${totalAmount}\n · 收支类型: 支出\n · 分类:${result.data?.storeCategory || category}\n · 商品数量:${itemCount}件\n · 时间:${dateStr}\n${friendlyEnding}${insightsText}${achievementsText}\n-------------\nchaimi-keep-mcp: v${MCP_VERSION}`;
1229
1224
 
1230
1225
  if (!processedArgs.agentType || !processedArgs.apiProvider) {
1231
1226
  userMessage += '\n\n💡 提示:传递agentType和apiProvider参数可解锁小程序记录来源统计功能,示例:agentType="openclaw", apiProvider="doubao"';
@@ -1455,7 +1450,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
1455
1450
 
1456
1451
  // 按 SKILL 规范格式:新的回复模板
1457
1452
  const dateStr = result.data?.date ? new Date(result.data.date).toLocaleDateString('zh-CN') : new Date().toLocaleDateString('zh-CN');
1458
- userMessage = `✅ 「${agentName}」已帮您录入「柴米AI记账」\n\n商品:${displayName}${displayStoreText}\n金额:¥${displayAmount}\n类型:收入\n分类:${result.data?.categoryName || displayCategory}\n时间:${dateStr}\n\n入账顺利!💰\n\n---\n柴米AI记账 v${MCP_VERSION}`;
1453
+ userMessage = `✅ 「${agentName}」已帮您录入「柴米AI记账」\n · 商品/店名:${displayName}${displayStoreText} 💰¥${displayAmount}\n · 收支类型: 收入\n · 分类:${result.data?.categoryName || displayCategory}\n · 时间:${dateStr}\n入账顺利!💰\n-------------\nchaimi-keep-mcp: v${MCP_VERSION}`;
1459
1454
  }
1460
1455
  break;
1461
1456
  }
@@ -2111,33 +2106,13 @@ async function startAuthFlow() {
2111
2106
  authState.isWaiting = true;
2112
2107
  console.error(`🔑 使用已保存的验证码:${savedAuthState.userCode}`);
2113
2108
  // 启动后台轮询
2114
- // 【新增】同时传入 userCode,用于复用授权场景
2115
- pollForAuthInBackground(savedAuthState.deviceCode, savedAuthState.userCode, 5000);
2109
+ pollForAuthInBackground(savedAuthState.deviceCode, 5000);
2116
2110
  return;
2117
2111
  }
2118
2112
  }
2119
2113
 
2120
- // 【新增】获取或设置Agent类型
2121
- let agentType = await getAgentType();
2122
- if (!agentType) {
2123
- agentType = process.env.AGENT_TYPE || 'unknown';
2124
- await saveAgentType(agentType);
2125
- console.error(`[Auth] 首次使用,设置Agent类型: ${agentType}`);
2126
- }
2127
-
2128
- // 【新增】生成设备指纹
2129
- const deviceFingerprint = await getDeviceFingerprint(agentType);
2130
- const macAddresses = await getMacAddresses();
2131
- console.error(`[Auth] 设备指纹: ${deviceFingerprint}`);
2132
- console.error(`[Auth] MAC地址: ${macAddresses.join(', ') || 'unknown'}`);
2133
-
2134
- // 获取新的设备码(带上设备指纹)
2135
- const deviceCodeRes = await oauthManager.requestDeviceCode(false, {
2136
- deviceFingerprint,
2137
- agentType,
2138
- macAddress: macAddresses[0] || 'unknown'
2139
- });
2140
-
2114
+ // 获取新的设备码
2115
+ const deviceCodeRes = await oauthManager.requestDeviceCode(false);
2141
2116
  authState.deviceCode = deviceCodeRes.deviceCode;
2142
2117
  authState.userCode = deviceCodeRes.userCode;
2143
2118
  authState.isWaiting = true;
@@ -2153,8 +2128,7 @@ async function startAuthFlow() {
2153
2128
  console.error('⏳ 请在小程序中完成授权,然后再次调用');
2154
2129
 
2155
2130
  // 启动后台轮询(3分钟)
2156
- // 【新增】同时传入 userCode,用于复用授权场景
2157
- pollForAuthInBackground(deviceCodeRes.deviceCode, deviceCodeRes.userCode, 5000);
2131
+ pollForAuthInBackground(deviceCodeRes.deviceCode, 5000);
2158
2132
 
2159
2133
  } catch (err) {
2160
2134
  authState.error = err.message;
@@ -2164,7 +2138,7 @@ async function startAuthFlow() {
2164
2138
  }
2165
2139
 
2166
2140
  // 后台轮询授权状态(3分钟)
2167
- async function pollForAuthInBackground(deviceCode, userCode, interval) {
2141
+ async function pollForAuthInBackground(deviceCode, interval) {
2168
2142
  const maxAttempts = 36; // 3分钟(36次 × 5秒)
2169
2143
  let attempts = 0;
2170
2144
 
@@ -2176,8 +2150,7 @@ async function pollForAuthInBackground(deviceCode, userCode, interval) {
2176
2150
 
2177
2151
  try {
2178
2152
  // 调用云函数检查授权状态
2179
- // 【新增】同时传入 userCode,用于复用授权场景
2180
- const token = await oauthManager.pollForTokenOnce(deviceCode, userCode);
2153
+ const token = await oauthManager.pollForTokenOnce(deviceCode);
2181
2154
 
2182
2155
  if (token) {
2183
2156
  // 授权成功
@@ -2190,9 +2163,8 @@ async function pollForAuthInBackground(deviceCode, userCode, interval) {
2190
2163
  await oauthManager.tokenStorage.save(token);
2191
2164
 
2192
2165
  // 【新增】保存默认 Agent 名称和 deviceCode(首次记账时会获取真实名称)
2193
- // 【修复】使用传入的 deviceCode,而不是 authState.deviceCode(可能不一致)
2194
2166
  await oauthManager.tokenStorage.saveAgentName('柴米AI助手');
2195
- await oauthManager.tokenStorage.saveDeviceCode(deviceCode);
2167
+ await oauthManager.tokenStorage.saveDeviceCode(authState.deviceCode);
2196
2168
 
2197
2169
  // 清除授权状态
2198
2170
  await oauthManager.clearAuthState();
@@ -23,200 +23,3 @@ const MACHINE_ID_FILE = path.join(
23
23
  'machine-id'
24
24
  );
25
25
 
26
- // Agent类型文件路径
27
- const AGENT_TYPE_FILE = path.join(
28
- process.env.HOME || process.env.USERPROFILE,
29
- '.mcporter',
30
- 'agent-type.json'
31
- );
32
-
33
- /**
34
- * 获取或生成机器ID
35
- * @returns {Promise<string>} 16位机器ID
36
- */
37
- async function getMachineId() {
38
- try {
39
- // 1. 尝试读取已有ID
40
- const existing = await fs.readFile(MACHINE_ID_FILE, 'utf8');
41
- if (existing.trim()) {
42
- console.error('[MachineId] 使用已有机器ID:', existing.trim());
43
- return existing.trim();
44
- }
45
- } catch (err) {
46
- // 文件不存在,继续生成
47
- console.error('[MachineId] 首次使用,生成新机器ID');
48
- }
49
-
50
- // 2. 生成新ID
51
- const networkInterfaces = os.networkInterfaces();
52
- const macAddresses = [];
53
-
54
- for (const name in networkInterfaces) {
55
- for (const iface of networkInterfaces[name]) {
56
- if (!iface.internal && iface.mac && iface.mac !== '00:00:00:00:00:00') {
57
- macAddresses.push(iface.mac);
58
- }
59
- }
60
- }
61
-
62
- // 排序后合并(避免网卡顺序影响)
63
- macAddresses.sort();
64
- const machineId = crypto
65
- .createHash('sha256')
66
- .update(macAddresses.join(':') + os.hostname())
67
- .digest('hex')
68
- .substring(0, 16);
69
-
70
- console.error('[MachineId] 生成新机器ID:', machineId);
71
-
72
- // 3. 保存到文件
73
- try {
74
- await fs.mkdir(path.dirname(MACHINE_ID_FILE), { recursive: true });
75
- await fs.writeFile(MACHINE_ID_FILE, machineId, { mode: 0o600 });
76
- console.error('[MachineId] 机器ID已保存到:', MACHINE_ID_FILE);
77
- } catch (err) {
78
- console.error('[MachineId] 保存机器ID失败:', err.message);
79
- }
80
-
81
- return machineId;
82
- }
83
-
84
- /**
85
- * 获取设备指纹
86
- * @param {string} agentType - Agent类型(claude/cursor等)
87
- * @returns {Promise<string>} 16位设备指纹
88
- */
89
- async function getDeviceFingerprint(agentType) {
90
- const machineId = await getMachineId();
91
- const fingerprint = crypto
92
- .createHash('sha256')
93
- .update(`${machineId}:${agentType}`)
94
- .digest('hex')
95
- .substring(0, 16);
96
-
97
- console.error('[MachineId] 设备指纹:', fingerprint, '(agentType:', agentType + ')');
98
- return fingerprint;
99
- }
100
-
101
- /**
102
- * 获取Agent类型
103
- * @returns {Promise<string|null>} Agent类型,首次返回null
104
- */
105
- async function getAgentType() {
106
- try {
107
- const data = await fs.readFile(AGENT_TYPE_FILE, 'utf8');
108
- const parsed = JSON.parse(data);
109
- if (parsed.agentType) {
110
- console.error('[MachineId] 使用已有Agent类型:', parsed.agentType);
111
- return parsed.agentType;
112
- }
113
- } catch (err) {
114
- console.error('[MachineId] 首次使用,需要设置Agent类型');
115
- }
116
- return null;
117
- }
118
-
119
- /**
120
- * 保存Agent类型
121
- * @param {string} agentType - Agent类型
122
- */
123
- async function saveAgentType(agentType) {
124
- try {
125
- await fs.mkdir(path.dirname(AGENT_TYPE_FILE), { recursive: true });
126
- await fs.writeFile(
127
- AGENT_TYPE_FILE,
128
- JSON.stringify({
129
- agentType,
130
- updatedAt: new Date().toISOString()
131
- }),
132
- { mode: 0o600 }
133
- );
134
- console.error('[MachineId] Agent类型已保存:', agentType);
135
- } catch (err) {
136
- console.error('[MachineId] 保存Agent类型失败:', err.message);
137
- }
138
- }
139
-
140
- /**
141
- * 获取MAC地址列表(用于调试)
142
- * @returns {Promise<string[]>} MAC地址列表
143
- */
144
- async function getMacAddresses() {
145
- const networkInterfaces = os.networkInterfaces();
146
- const macAddresses = [];
147
-
148
- for (const name in networkInterfaces) {
149
- for (const iface of networkInterfaces[name]) {
150
- if (!iface.internal && iface.mac && iface.mac !== '00:00:00:00:00:00') {
151
- macAddresses.push(iface.mac);
152
- }
153
- }
154
- }
155
-
156
- return macAddresses.sort();
157
- }
158
-
159
- /**
160
- * 自动检测Agent类型
161
- * 根据运行环境自动识别:trae、cursor、claude-desktop等
162
- * @returns {string} 检测到的Agent类型
163
- */
164
- function detectAgentType() {
165
- // 检测环境变量
166
- const envVars = {
167
- TRAE: process.env.TRAE || process.env.TRAEPATH,
168
- CURSOR: process.env.CURSOR || process.env.CURSOR_PATH,
169
- CLAUDE: process.env.CLAUDE_DESKTOP || process.env.CLAUDE_APP,
170
- VSCODE: process.env.VSCODE_CWD || process.env.VSCODE_PID,
171
- WINDSURF: process.env.WINDSURF || process.env.WINDSURF_PATH
172
- };
173
-
174
- // 根据环境变量判断
175
- if (envVars.TRAEPATH || process.argv.some(arg => arg.includes('trae'))) {
176
- return 'trae';
177
- }
178
- if (envVars.CURSOR || process.argv.some(arg => arg.includes('cursor'))) {
179
- return 'cursor';
180
- }
181
- if (envVars.CLAUDE || process.argv.some(arg => arg.includes('claude'))) {
182
- return 'claude-desktop';
183
- }
184
- if (envVars.WINDSURF) {
185
- return 'windsurf';
186
- }
187
- if (envVars.VSCODE) {
188
- return 'vscode';
189
- }
190
-
191
- // 检测进程信息
192
- try {
193
- const ppid = process.ppid;
194
- // 在 macOS/Linux 上可以通过父进程检测
195
- if (process.platform === 'darwin' || process.platform === 'linux') {
196
- try {
197
- const { execSync } = require('child_process');
198
- const parentCmd = execSync(`ps -p ${ppid} -o comm=`, { encoding: 'utf8' }).trim();
199
- if (parentCmd.includes('trae')) return 'trae';
200
- if (parentCmd.includes('cursor')) return 'cursor';
201
- if (parentCmd.includes('claude')) return 'claude-desktop';
202
- if (parentCmd.includes('windsurf')) return 'windsurf';
203
- } catch (e) {
204
- // 忽略错误
205
- }
206
- }
207
- } catch (e) {
208
- // 忽略错误
209
- }
210
-
211
- // 默认返回 unknown
212
- return 'unknown';
213
- }
214
-
215
- module.exports = {
216
- getMachineId,
217
- getDeviceFingerprint,
218
- getAgentType,
219
- saveAgentType,
220
- getMacAddresses,
221
- detectAgentType
222
- };
package/.env.example DELETED
@@ -1,26 +0,0 @@
1
- # 柴米记账 MCP Server 环境变量配置
2
- # OAuth 2.0 Device Flow 认证(行业标准方案)
3
-
4
- # ========================================
5
- # 必需配置
6
- # ========================================
7
-
8
- # OAuth 云函数地址
9
- # 首次启动会自动引导完成授权,Token 存储在 ~/.mcporter/oauth-token.json
10
- MCP_OAUTH_URL=https://cloud1-2gfe5jhjef06b85d-1412172089.ap-shanghai.app.tcloudbase.com/mcpOAuth
11
-
12
- # mcpHub 云函数地址
13
- MCP_HUB_URL=https://cloud1-2gfe5jhjef06b85d-1412172089.ap-shanghai.app.tcloudbase.com/mcpHub-mcp
14
-
15
- # mcpPrompt 云函数地址
16
- MCP_PROMPT_URL=https://cloud1-2gfe5jhjef06b85d-1412172089.ap-shanghai.app.tcloudbase.com/mcpPrompt
17
-
18
- # ========================================
19
- # 可选配置
20
- # ========================================
21
-
22
- # AI API Key(用于本地解析)
23
- AI_API_KEY=
24
-
25
- # AI 提供商(openai/anthropic/deepseek)
26
- AI_PROVIDER=deepseek
@@ -1,243 +0,0 @@
1
- ---
2
- name: "chaimi-keep-response-templates-test"
3
- description: 记账回复模板测试方案 - 供Agent测试不同格式的显示效果
4
- version: "1.1.0"
5
- updated: "2026-04-26"
6
- ---
7
-
8
- # 记账回复模板测试方案
9
-
10
- > 用于测试不同分隔符和排版在手机/PC端的显示效果
11
-
12
- ---
13
-
14
- ## 测试说明
15
-
16
- 将以下各方案的模板复制到Agent的skill中使用,观察:
17
- 1. 换行是否正常
18
- 2. 分隔线显示效果
19
- 3. 底部三行对齐效果
20
- 4. 整体美观度
21
-
22
- ---
23
-
24
- ## 方案1:双分隔线短版(15字符)
25
-
26
- ```
27
- ✅ 「{agentName}」已帮您记账成功
28
- ═══════════════
29
- 💰 金额:¥{金额}
30
- ═══════════════
31
-
32
- 📦 商品:{商品名}
33
- 🏷️ 分类:{分类}
34
- 🏪 商家:{商家}
35
- 🕐 时间:{日期时间}
36
-
37
- ───────────────
38
- 🎉 {正能量祝福语}!
39
- 柴米AI记账
40
- chaimi-keep-mcp v{版本号}
41
- ───────────────
42
- ```
43
-
44
- **填充示例:**
45
- ```
46
- ✅ 「你的小可爱」已帮您记账成功
47
- ═══════════════
48
- 💰 金额:¥35.00
49
- ═══════════════
50
-
51
- 📦 商品:午餐
52
- 🏷️ 分类:餐饮
53
- 🏪 商家:麦当劳
54
- 🕐 时间:2026-04-24 12:30
55
-
56
- ───────────────
57
- 🎉 美食为梦想充电,继续向前冲!🍚
58
- 柴米AI记账
59
- chaimi-keep-mcp v3.2.0
60
- ───────────────
61
- ```
62
-
63
- ---
64
-
65
- ## 方案2:单分隔线极简版
66
-
67
- ```
68
- ✅ 「{agentName}」已帮您记账成功
69
-
70
- 💰 金额:¥{金额}
71
-
72
- 📦 商品:{商品名}
73
- 🏷️ 分类:{分类}
74
- 🏪 商家:{商家}
75
- 🕐 时间:{日期时间}
76
-
77
- ---
78
- 🎉 {正能量祝福语}!
79
- 柴米AI记账
80
- chaimi-keep-mcp v{版本号}
81
- ```
82
-
83
- **填充示例:**
84
- ```
85
- ✅ 「你的小可爱」已帮您记账成功
86
-
87
- 💰 金额:¥35.00
88
-
89
- 📦 商品:午餐
90
- 🏷️ 分类:餐饮
91
- 🏪 商家:麦当劳
92
- 🕐 时间:2026-04-24 12:30
93
-
94
- ---
95
- 🎉 美食为梦想充电,继续向前冲!🍚
96
- 柴米AI记账
97
- chaimi-keep-mcp v3.2.0
98
- ```
99
-
100
- ---
101
-
102
- ## 方案3:信息紧凑单行版(已优化)
103
-
104
- ```
105
- ✅ 「{agentName}」已帮您记账成功 💰¥{金额}
106
-
107
- 📦{商品名} · 🏷️{分类} · 🏪{商家} · 🕐{日期时间}
108
-
109
- ═══════════════
110
- 🎉 {正能量祝福语}!
111
- 柴米AI记账
112
- chaimi-keep-mcp v{版本号}
113
- ═══════════════
114
- ```
115
-
116
- **填充示例:**
117
- ```
118
- ✅ 「你的小可爱」已帮您记账成功 💰¥35.00
119
-
120
- 📦午餐 · 🏷️餐饮 · 🏪麦当劳 · 🕐12:30
121
-
122
- ═══════════════
123
- 🎉 祝您用餐愉快!
124
- 柴米AI记账
125
- chaimi-keep-mcp v3.2.0
126
- ═══════════════
127
- ```
128
-
129
- ---
130
-
131
- ## 测试记录表
132
-
133
- | 方案 | 手机端效果 | PC端效果 | 推荐场景 |
134
- |:-----|:-----------|:---------|:---------|
135
- | 方案1:双分隔线短版 | 待测试 | 待测试 | 默认推荐 |
136
- | 方案2:单分隔线极简版 | 待测试 | 待测试 | 快速记账 |
137
- | 方案3:信息紧凑单行版 | 待测试 | 待测试 | 空间受限 |
138
-
139
- ---
140
-
141
- ## 关键检查点
142
-
143
- ### 1. 分隔线显示
144
- - [ ] 方案1/5的 `═` 符号是否正常显示
145
- - [ ] 方案2的 `---` 是否被识别为分隔线
146
-
147
- ### 2. 底部对齐
148
- - [ ] 底部三行文字是否左对齐(🎉 不占对齐位)
149
- - [ ] "祝"、"柴"、"c" 三字符是否在同一列
150
-
151
- ### 3. 换行效果
152
- - [ ] 空行是否被正确保留
153
- - [ ] 信息项之间是否有适当间距
154
-
155
- ### 4. Emoji显示
156
- - [ ] ✅ 💰 📦 🏷️ 🏪 🕐 🎉 是否正常显示
157
- - [ ] 是否有乱码或方框
158
-
159
- ### 5. 方案5特殊检查
160
- - [ ] 单行信息是否可读
161
- - [ ] 分隔符 `·` 是否正常显示
162
-
163
- ---
164
-
165
- ## 正能量情绪词库
166
-
167
- 根据消费分类自动匹配正能量结束语:
168
-
169
- ### 餐饮类
170
- | 场景 | 结束语 |
171
- |:-----|:-------|
172
- | 早餐 | 早餐吃得香,元气满满迎朝阳!☀️ |
173
- | 午餐 | 美食为梦想充电,继续向前冲!🍚 |
174
- | 晚餐 | 晚餐后放松身心,明天更精彩!🌙 |
175
- | 零食 | 生活要有仪式感,小确幸也是幸福!😋 |
176
- | 外卖 | 再忙也要爱自己,好好吃饭!💕 |
177
- | 聚餐 | 朋友相聚是福气,友谊地久天长!🎉 |
178
- | 咖啡 | 一杯咖啡提神醒脑,效率倍增!☕ |
179
- | 奶茶 | 甜蜜相伴,工作生活都精彩!🧋 |
180
-
181
- ### 交通类
182
- | 场景 | 结束语 |
183
- |:-----|:-------|
184
- | 地铁 | 城市动脉连接梦想,未来可期!🚇 |
185
- | 打车 | 一路顺风,事事顺心!🚗 |
186
- | 加油 | 加满油向梦想出发,勇往直前!⛽ |
187
- | 停车 | 车位到手,好运相伴!🅿️ |
188
- | 公交 | 绿色出行低碳环保,为地球助力!🚌 |
189
- | 高铁 | 速度与梦想同行,前程似锦!🚄 |
190
-
191
- ### 购物类
192
- | 场景 | 结束语 |
193
- |:-----|:-------|
194
- | 日用品 | 精明消费品质生活,越买越聪明!✨ |
195
- | 服装 | 新衣新气象,自信每一天!👗 |
196
- | 电子产品 | 科技赋能,效率翻倍!💻 |
197
- | 超市 | 满载而归,生活美满!🛒 |
198
- | 网购 | 好物即将到家,期待美好!📦 |
199
- | 美妆 | 美丽自信,由内而外散发光彩!💄 |
200
-
201
- ### 居住类
202
- | 场景 | 结束语 |
203
- |:-----|:-------|
204
- | 房租 | 投资未来,房子是温馨的港湾!🏠 |
205
- | 水电 | 节约环保,绿色生活从我做起!💡 |
206
- | 物业 | 安心居住,幸福生活有保障!🏢 |
207
- | 装修 | 亲手打造温馨小窝,成就感满满!🔨 |
208
- | 家具 | 新家具新生活,每一天都舒心!🛋️ |
209
-
210
- ### 娱乐类
211
- | 场景 | 结束语 |
212
- |:-----|:-------|
213
- | 电影 | 光影世界丰富心灵,生活更精彩!🎬 |
214
- | 游戏 | 适度娱乐放松身心,工作生活两不误!🎮 |
215
- | 旅游 | 读万卷书行万里路,拓宽人生视野!✈️ |
216
- | 健身 | 投资健康是最大的财富,活力满满!💪 |
217
- | 书籍 | 知识改变命运,智慧点亮人生!📚 |
218
-
219
- ### 收入类
220
- | 场景 | 结束语 |
221
- |:-----|:-------|
222
- | 工资 | 劳动创造价值,努力终有回报!🎉 |
223
- | 奖金 | 努力被看见,未来更辉煌!💪 |
224
- | 兼职 | 多元化发展,人生更多精彩!✨ |
225
- | 理财 | 钱生钱利滚利,财富自由更进一步!📈 |
226
- | 红包 | 好运连连,福气满满!🧧 |
227
-
228
- ### 通用类
229
- | 情绪 | 结束语 |
230
- |:-----|:-------|
231
- | 鼓励 | 今天的你特别棒,继续发光发热!👍 |
232
- | 温暖 | 记录生活点滴,美好永留存!📝 |
233
- | 正能量 | 每一笔记录都是对生活的热爱!💕 |
234
- | 轻松 | 记账完成,又是充实的一天!✨ |
235
- | 感恩 | 感恩生活,感恩有你!🙏 |
236
- | 期待 | 未来可期,一起加油!🌟 |
237
-
238
- ---
239
-
240
- **测试完成后的最佳方案将更新到 response-templates.md 正式文档中**
241
-
242
- 文档版本:v1.2.0
243
- 最后更新:2026-04-26