chaimi-keep-mcp 3.3.0-beta.3 → 3.3.0-beta.5

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 +75 -22
  2. package/package.json +1 -1
  3. package/server.js +15 -3
package/oauth.js CHANGED
@@ -428,28 +428,81 @@ class FileTokenStorage extends TokenStorage {
428
428
  }
429
429
 
430
430
  async save(token) {
431
- const dir = this.path.dirname(this.filePath);
432
- await this.fs.mkdir(dir, { recursive: true });
433
-
434
- console.error('🔍 [DEBUG] 开始保存 Token');
435
- console.error('🔍 [DEBUG] 加密密钥 (前8位):', this.key.substring(0, 8));
436
- console.error('🔍 [DEBUG] Token 过期时间:', token.expiresAt);
437
-
438
- // 加密 Token 后保存
439
- const encrypted = encryptToken(token, this.key);
440
- const data = {
441
- version: '2.0',
442
- encrypted: encrypted,
443
- algorithm: 'aes-256-cbc',
444
- updatedAt: new Date().toISOString()
445
- };
446
-
447
- await this.fs.writeFile(
448
- this.filePath,
449
- JSON.stringify(data, null, 2),
450
- { mode: 0o600 }
451
- );
452
- console.error('🔍 [DEBUG] Token 保存成功:', this.filePath);
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
+ { mode: 0o600 }
460
+ );
461
+
462
+ // 验证临时文件写入是否成功
463
+ const tempStats = await this.fs.stat(tempFilePath);
464
+ console.error('🔍 [DEBUG] 临时文件写入成功,大小:', tempStats.size, '字节');
465
+
466
+ if (tempStats.size === 0) {
467
+ throw new Error('临时文件写入后大小为 0,写入失败');
468
+ }
469
+
470
+ // 原子重命名(覆盖目标文件)
471
+ await this.fs.rename(tempFilePath, this.filePath);
472
+ console.error('🔍 [DEBUG] 文件重命名成功');
473
+
474
+ // 验证最终文件
475
+ const stats = await this.fs.stat(this.filePath);
476
+ console.error('🔍 [DEBUG] Token 文件保存成功');
477
+ console.error('🔍 [DEBUG] 文件路径:', this.filePath);
478
+ console.error('🔍 [DEBUG] 文件大小:', stats.size, '字节');
479
+
480
+ if (stats.size === 0) {
481
+ throw new Error('Token 文件大小为 0,写入失败');
482
+ }
483
+
484
+ // 立即读取验证
485
+ const readBack = await this.fs.readFile(this.filePath, 'utf8');
486
+ if (readBack.length === 0) {
487
+ throw new Error('Token 文件读取验证失败,内容为空');
488
+ }
489
+ console.error('🔍 [DEBUG] Token 文件验证成功,内容长度:', readBack.length, '字节');
490
+
491
+ } catch (error) {
492
+ console.error('❌ Token 保存失败:', error.message);
493
+ console.error('❌ 错误详情:', error);
494
+
495
+ // 清理临时文件
496
+ try {
497
+ const tempFilePath = this.filePath + '.tmp';
498
+ await this.fs.unlink(tempFilePath);
499
+ console.error('🔍 [DEBUG] 已清理临时文件');
500
+ } catch (cleanupError) {
501
+ // 忽略清理错误
502
+ }
503
+
504
+ throw error;
505
+ }
453
506
  }
454
507
 
455
508
  async load() {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "chaimi-keep-mcp",
3
- "version": "3.3.0-beta.3",
3
+ "version": "3.3.0-beta.5",
4
4
  "description": "柴米记账 MCP Server - 支持 Claude、Cursor、OpenClaw、WorkBuddy 等 AI 工具直接记账",
5
5
  "main": "server.js",
6
6
  "bin": {
package/server.js CHANGED
@@ -2201,11 +2201,23 @@ async function pollForAuthInBackground(deviceCode, interval) {
2201
2201
  authState.isWaiting = false;
2202
2202
 
2203
2203
  // 保存token到文件
2204
- await oauthManager.tokenStorage.save(token);
2204
+ try {
2205
+ await oauthManager.tokenStorage.save(token);
2206
+ console.error('🔍 [DEBUG] Token 保存完成');
2207
+ } catch (error) {
2208
+ console.error('❌ Token 保存失败:', error.message);
2209
+ throw error;
2210
+ }
2205
2211
 
2206
2212
  // 【新增】保存默认 Agent 名称和 deviceCode(首次记账时会获取真实名称)
2207
- await oauthManager.tokenStorage.saveAgentName('柴米AI助手');
2208
- await oauthManager.tokenStorage.saveDeviceCode(authState.deviceCode);
2213
+ try {
2214
+ await oauthManager.tokenStorage.saveAgentName('柴米AI助手');
2215
+ await oauthManager.tokenStorage.saveDeviceCode(authState.deviceCode);
2216
+ console.error('🔍 [DEBUG] Agent 名称和 deviceCode 保存完成');
2217
+ } catch (error) {
2218
+ console.error('❌ Agent 信息保存失败:', error.message);
2219
+ // 这个失败不影响授权,只记录日志
2220
+ }
2209
2221
 
2210
2222
  // 清除授权状态
2211
2223
  await oauthManager.clearAuthState();