chaimi-keep-mcp 3.3.0-beta.6 → 3.3.0-beta.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.
Files changed (3) hide show
  1. package/oauth.js +24 -90
  2. package/package.json +1 -1
  3. package/server.js +0 -16
package/oauth.js CHANGED
@@ -428,84 +428,27 @@ class FileTokenStorage extends TokenStorage {
428
428
  }
429
429
 
430
430
  async save(token) {
431
- try {
432
- const dir = this.path.dirname(this.filePath);
433
- await this.fs.mkdir(dir, { recursive: true });
434
-
435
- console.error('🔍 [DEBUG] 开始保存 Token');
436
- console.error('🔍 [DEBUG] 加密密钥 (前8位):', this.key.substring(0, 8));
437
- console.error('🔍 [DEBUG] Token 过期时间:', token.expiresAt);
438
-
439
- // 加密 Token 后保存
440
- const encrypted = encryptToken(token, this.key);
441
- const data = {
442
- version: '2.0',
443
- encrypted: encrypted,
444
- algorithm: 'aes-256-cbc',
445
- updatedAt: new Date().toISOString()
446
- };
447
-
448
- const content = JSON.stringify(data, null, 2);
449
- console.error('🔍 [DEBUG] 准备写入内容长度:', content.length, '字节');
450
-
451
- // 【修复】使用原子写入:先写临时文件,再重命名
452
- // 这样可以避免首次创建文件时的竞态条件
453
- const tempFilePath = this.filePath + '.tmp';
454
-
455
- // 写入临时文件
456
- await this.fs.writeFile(
457
- tempFilePath,
458
- content,
459
- 'utf8'
460
- );
461
-
462
- // 设置文件权限(仅所有者可读写)
463
- await this.fs.chmod(tempFilePath, 0o600);
464
-
465
- // 验证临时文件写入是否成功
466
- const tempStats = await this.fs.stat(tempFilePath);
467
- console.error('🔍 [DEBUG] 临时文件写入成功,大小:', tempStats.size, '字节');
468
-
469
- if (tempStats.size === 0) {
470
- throw new Error('临时文件写入后大小为 0,写入失败');
471
- }
472
-
473
- // 原子重命名(覆盖目标文件)
474
- await this.fs.rename(tempFilePath, this.filePath);
475
- console.error('🔍 [DEBUG] 文件重命名成功');
476
-
477
- // 验证最终文件
478
- const stats = await this.fs.stat(this.filePath);
479
- console.error('🔍 [DEBUG] Token 文件保存成功');
480
- console.error('🔍 [DEBUG] 文件路径:', this.filePath);
481
- console.error('🔍 [DEBUG] 文件大小:', stats.size, '字节');
482
-
483
- if (stats.size === 0) {
484
- throw new Error('Token 文件大小为 0,写入失败');
485
- }
486
-
487
- // 立即读取验证
488
- const readBack = await this.fs.readFile(this.filePath, 'utf8');
489
- if (readBack.length === 0) {
490
- throw new Error('Token 文件读取验证失败,内容为空');
491
- }
492
- console.error('🔍 [DEBUG] Token 文件验证成功,内容长度:', readBack.length, '字节');
493
-
494
- } catch (error) {
495
- console.error('❌ Token 保存失败:', error.message);
496
- console.error('❌ 错误详情:', error);
497
-
498
- // 清理临时文件
499
- try {
500
- const tempFilePath = this.filePath + '.tmp';
501
- await this.fs.unlink(tempFilePath);
502
- console.error('🔍 [DEBUG] 已清理临时文件');
503
- } catch (cleanupError) {
504
- // 忽略清理错误
505
- }
506
-
507
- throw error;
508
- }
431
+ const dir = this.path.dirname(this.filePath);
432
+ await this.fs.mkdir(dir, { recursive: true });
433
+
434
+ // 加密 Token 后保存
435
+ const encrypted = encryptToken(token, this.key);
436
+ const data = {
437
+ version: '2.0',
438
+ encrypted: encrypted,
439
+ algorithm: 'aes-256-cbc',
440
+ updatedAt: new Date().toISOString()
441
+ };
442
+
443
+ // 修复:正确使用 fs.writeFile 参数
444
+ await this.fs.writeFile(
445
+ this.filePath,
446
+ JSON.stringify(data, null, 2),
447
+ 'utf8'
448
+ );
449
+
450
+ // 设置文件权限
451
+ await this.fs.chmod(this.filePath, 0o600);
509
452
  }
510
453
 
511
454
  async load() {
@@ -513,10 +456,6 @@ class FileTokenStorage extends TokenStorage {
513
456
  const data = await this.fs.readFile(this.filePath, 'utf8');
514
457
  const parsed = JSON.parse(data);
515
458
 
516
- console.error('🔍 [DEBUG] 开始加载 Token 文件');
517
- console.error('🔍 [DEBUG] 文件版本:', parsed.version);
518
- console.error('🔍 [DEBUG] 加密密钥 (前8位):', this.key.substring(0, 8));
519
-
520
459
  // 向后兼容:检测旧版明文格式
521
460
  if (!parsed.version || parsed.version === '1.0') {
522
461
  console.error('检测到旧版 Token 格式,自动升级...');
@@ -531,16 +470,11 @@ class FileTokenStorage extends TokenStorage {
531
470
  }
532
471
 
533
472
  // 新版加密格式,解密后返回
534
- console.error('🔍 [DEBUG] 开始解密 Token...');
535
- const decrypted = decryptToken(parsed.encrypted, this.key);
536
- console.error('🔍 [DEBUG] 解密成功,Token 过期时间:', decrypted.expiresAt);
537
- return decrypted;
473
+ return decryptToken(parsed.encrypted, this.key);
538
474
  } catch (err) {
539
475
  if (err.code === 'ENOENT') {
540
- console.error('🔍 [DEBUG] Token 文件不存在');
541
476
  return null;
542
477
  }
543
- console.error('🔍 [DEBUG] 加载 Token 失败:', err.message);
544
478
  throw err;
545
479
  }
546
480
  }
@@ -563,7 +497,7 @@ class FileTokenStorage extends TokenStorage {
563
497
  agentName: agentName || '柴米AI助手',
564
498
  updatedAt: new Date().toISOString()
565
499
  };
566
- await this.fs.writeFile(agentNamePath, JSON.stringify(data, null, 2), { mode: 0o600 });
500
+ await this.fs.writeFile(agentNamePath, JSON.stringify(data, null, 2), 'utf8');
567
501
  console.error('✅ Agent 名称已保存:', agentNamePath);
568
502
  } catch (err) {
569
503
  console.error('❌ 保存 Agent 名称失败:', err.message);
@@ -590,7 +524,7 @@ class FileTokenStorage extends TokenStorage {
590
524
  deviceCode,
591
525
  updatedAt: new Date().toISOString()
592
526
  };
593
- await this.fs.writeFile(deviceCodePath, JSON.stringify(data, null, 2), { mode: 0o600 });
527
+ await this.fs.writeFile(deviceCodePath, JSON.stringify(data, null, 2), 'utf8');
594
528
  } catch (err) {
595
529
  // 忽略错误
596
530
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "chaimi-keep-mcp",
3
- "version": "3.3.0-beta.6",
3
+ "version": "3.3.0-beta.8",
4
4
  "description": "柴米记账 MCP Server - 支持 Claude、Cursor、OpenClaw、WorkBuddy 等 AI 工具直接记账",
5
5
  "main": "server.js",
6
6
  "bin": {
package/server.js CHANGED
@@ -475,26 +475,11 @@ async function getToken() {
475
475
 
476
476
  // 【优化2】尝试从文件加载 token(优先级高于授权状态检查)
477
477
  try {
478
- console.error('🔍 [DEBUG] 尝试从文件加载 Token...');
479
478
  const existingToken = await oauthManager.tokenStorage.load();
480
- console.error('🔍 [DEBUG] Token 加载结果:', existingToken ? '成功' : '为空');
481
- if (existingToken) {
482
- console.error('🔍 [DEBUG] Token 字段检查:', {
483
- hasAccessToken: !!existingToken.accessToken,
484
- hasExpiresAt: !!existingToken.expiresAt,
485
- expiresAt: existingToken.expiresAt
486
- });
487
- }
488
479
  if (existingToken && existingToken.accessToken && existingToken.expiresAt) {
489
480
  const expiresAt = new Date(existingToken.expiresAt).getTime();
490
481
  const now = Date.now();
491
482
  const threshold = now + 5 * 60 * 1000;
492
- console.error('🔍 [DEBUG] 过期时间检查:', {
493
- expiresAt,
494
- now,
495
- threshold,
496
- isValid: expiresAt > threshold
497
- });
498
483
  if (expiresAt > threshold) {
499
484
  // Token 有效,直接使用
500
485
  cachedToken = existingToken.accessToken;
@@ -509,7 +494,6 @@ async function getToken() {
509
494
  }
510
495
  } catch (err) {
511
496
  console.error('⚠️ 加载保存的 Token 失败:', err.message);
512
- console.error('⚠️ 错误堆栈:', err.stack);
513
497
  }
514
498
 
515
499
  // 【优化3】Token 不存在或已过期,检查是否需要授权