chaimi-keep-mcp 3.3.0-beta.0 → 3.3.0-beta.2

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 (3) hide show
  1. package/oauth.js +15 -1
  2. package/package.json +1 -1
  3. package/server.js +45 -19
package/oauth.js CHANGED
@@ -431,6 +431,10 @@ class FileTokenStorage extends TokenStorage {
431
431
  const dir = this.path.dirname(this.filePath);
432
432
  await this.fs.mkdir(dir, { recursive: true });
433
433
 
434
+ console.error('🔍 [DEBUG] 开始保存 Token');
435
+ console.error('🔍 [DEBUG] 加密密钥 (前8位):', this.key.substring(0, 8));
436
+ console.error('🔍 [DEBUG] Token 过期时间:', token.expiresAt);
437
+
434
438
  // 加密 Token 后保存
435
439
  const encrypted = encryptToken(token, this.key);
436
440
  const data = {
@@ -445,6 +449,7 @@ class FileTokenStorage extends TokenStorage {
445
449
  JSON.stringify(data, null, 2),
446
450
  { mode: 0o600 }
447
451
  );
452
+ console.error('🔍 [DEBUG] Token 保存成功:', this.filePath);
448
453
  }
449
454
 
450
455
  async load() {
@@ -452,6 +457,10 @@ class FileTokenStorage extends TokenStorage {
452
457
  const data = await this.fs.readFile(this.filePath, 'utf8');
453
458
  const parsed = JSON.parse(data);
454
459
 
460
+ console.error('🔍 [DEBUG] 开始加载 Token 文件');
461
+ console.error('🔍 [DEBUG] 文件版本:', parsed.version);
462
+ console.error('🔍 [DEBUG] 加密密钥 (前8位):', this.key.substring(0, 8));
463
+
455
464
  // 向后兼容:检测旧版明文格式
456
465
  if (!parsed.version || parsed.version === '1.0') {
457
466
  console.error('检测到旧版 Token 格式,自动升级...');
@@ -466,11 +475,16 @@ class FileTokenStorage extends TokenStorage {
466
475
  }
467
476
 
468
477
  // 新版加密格式,解密后返回
469
- return decryptToken(parsed.encrypted, this.key);
478
+ console.error('🔍 [DEBUG] 开始解密 Token...');
479
+ const decrypted = decryptToken(parsed.encrypted, this.key);
480
+ console.error('🔍 [DEBUG] 解密成功,Token 过期时间:', decrypted.expiresAt);
481
+ return decrypted;
470
482
  } catch (err) {
471
483
  if (err.code === 'ENOENT') {
484
+ console.error('🔍 [DEBUG] Token 文件不存在');
472
485
  return null;
473
486
  }
487
+ console.error('🔍 [DEBUG] 加载 Token 失败:', err.message);
474
488
  throw err;
475
489
  }
476
490
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "chaimi-keep-mcp",
3
- "version": "3.3.0-beta.0",
3
+ "version": "3.3.0-beta.2",
4
4
  "description": "柴米记账 MCP Server - 支持 Claude、Cursor、OpenClaw、WorkBuddy 等 AI 工具直接记账",
5
5
  "main": "server.js",
6
6
  "bin": {
package/server.js CHANGED
@@ -466,7 +466,35 @@ const TOKEN_REFRESH_INTERVAL = 2 * 60 * 60 * 1000;
466
466
  let lastRefreshTime = 0;
467
467
 
468
468
  async function getToken() {
469
- // 检查是否已授权
469
+ // 【优化1】优先检查内存中的 token 是否有效
470
+ if (cachedToken && tokenExpireTime > Date.now() + 5 * 60 * 1000) {
471
+ if (Date.now() - lastRefreshTime < TOKEN_REFRESH_INTERVAL) {
472
+ return cachedToken;
473
+ }
474
+ }
475
+
476
+ // 【优化2】尝试从文件加载 token(优先级高于授权状态检查)
477
+ try {
478
+ const existingToken = await oauthManager.tokenStorage.load();
479
+ if (existingToken && existingToken.accessToken && existingToken.expiresAt) {
480
+ const expiresAt = new Date(existingToken.expiresAt).getTime();
481
+ if (expiresAt > Date.now() + 5 * 60 * 1000) {
482
+ // Token 有效,直接使用
483
+ cachedToken = existingToken.accessToken;
484
+ tokenExpireTime = expiresAt;
485
+ authState.isAuthorized = true;
486
+ lastRefreshTime = Date.now();
487
+ console.error('✅ 已从文件加载有效 Token,无需重新授权');
488
+ return cachedToken;
489
+ } else {
490
+ console.error('⏰ 保存的 Token 已过期,需要重新授权');
491
+ }
492
+ }
493
+ } catch (err) {
494
+ console.error('⚠️ 加载保存的 Token 失败:', err.message);
495
+ }
496
+
497
+ // 【优化3】Token 不存在或已过期,检查是否需要授权
470
498
  if (!authState.isAuthorized) {
471
499
  // 先检查是否有保存的授权状态(从文件加载)
472
500
  const savedAuthState = await oauthManager.loadAuthState();
@@ -507,20 +535,15 @@ async function getToken() {
507
535
  }
508
536
  }
509
537
  }
510
-
538
+
511
539
  // 没有保存的授权状态或已过期,启动新的授权流程
512
540
  await startAuthFlow();
513
-
541
+
514
542
  // 返回验证码给Agent
515
543
  throw new Error(`NEED_AUTH:${authState.userCode || '等待生成验证码'}`);
516
544
  }
517
545
 
518
- if (cachedToken && tokenExpireTime > Date.now() + 5 * 60 * 1000) {
519
- if (Date.now() - lastRefreshTime < TOKEN_REFRESH_INTERVAL) {
520
- return cachedToken;
521
- }
522
- }
523
-
546
+ // 【优化4】已授权,尝试刷新 token
524
547
  if (!oauthManager) {
525
548
  throw new Error('OAuth 管理器未初始化,请检查 MCP_OAUTH_URL 配置');
526
549
  }
@@ -2158,21 +2181,23 @@ async function pollForAuthInBackground(deviceCode, interval) {
2158
2181
  tokenExpireTime = token.expiresAt;
2159
2182
  authState.isAuthorized = true;
2160
2183
  authState.isWaiting = false;
2161
-
2184
+
2162
2185
  // 保存token到文件
2163
2186
  await oauthManager.tokenStorage.save(token);
2164
-
2187
+
2165
2188
  // 【新增】保存默认 Agent 名称和 deviceCode(首次记账时会获取真实名称)
2166
2189
  await oauthManager.tokenStorage.saveAgentName('柴米AI助手');
2167
2190
  await oauthManager.tokenStorage.saveDeviceCode(authState.deviceCode);
2168
-
2191
+
2169
2192
  // 清除授权状态
2170
2193
  await oauthManager.clearAuthState();
2171
-
2172
- console.error('✅ 授权成功!下次调用将快速启动');
2173
-
2174
- // 主动退出进程,让 mcporter 下次重新启动
2175
- process.exit(0);
2194
+
2195
+ console.error('✅ 授权成功!可以继续使用记账功能');
2196
+
2197
+ // 【修复】不退出进程,让 Agent 可以在同一会话中继续执行记账操作
2198
+ // 这样 Agent 调用 get_skill 授权后,可以立即调用 save_expense 记账
2199
+ // process.exit(0); // 注释掉,避免授权成功后进程退出
2200
+ return; // 直接返回,停止轮询
2176
2201
  }
2177
2202
  } catch (err) {
2178
2203
  if (err.message.includes('expired') || err.message.includes('invalid')) {
@@ -2187,8 +2212,9 @@ async function pollForAuthInBackground(deviceCode, interval) {
2187
2212
 
2188
2213
  // 3分钟超时
2189
2214
  authState.isWaiting = false;
2190
- console.error('⏰ 授权超时,请再次调用获取新验证码');
2191
- process.exit(0);
2215
+ console.error('⏰ 授权超时(3分钟),请重新调用获取新验证码');
2216
+ // 【修复】不退出进程,让用户可以重新尝试
2217
+ // process.exit(0);
2192
2218
  }
2193
2219
 
2194
2220
  // 延迟函数