Skip to main content
Glama

Scratchpad MCP

by pc035860
live-sync.cjs8.55 kB
#!/usr/bin/env node /** * 實時雲端同步工具 - 無需斷線 MCP Server * * 特點: * 1. 使用 PASSIVE checkpoint,不干擾現有連接 * 2. 只同步主 .db 檔案(包含最新資料) * 3. 支援 WAL 模式下的安全同步 * 4. 可定期執行或手動觸發 * * 使用方法: * node scripts/live-sync.cjs --db=scratchpad.v6.db --cloud-dir=~/Dropbox/scratchpad */ const Database = require('better-sqlite3'); const path = require('path'); const fs = require('fs'); // 命令行參數解析 const args = process.argv.slice(2); const config = { dbPath: 'scratchpad.v6.db', cloudDir: null, keepBackups: 5, dryRun: false, verbose: false }; args.forEach(arg => { if (arg.startsWith('--db=')) { config.dbPath = arg.substring(5); } else if (arg.startsWith('--cloud-dir=')) { config.cloudDir = arg.substring(12); } else if (arg.startsWith('--keep=')) { config.keepBackups = parseInt(arg.substring(7)); } else if (arg === '--dry-run') { config.dryRun = true; } else if (arg === '--verbose') { config.verbose = true; } }); // 驗證參數 if (!config.cloudDir) { console.error('❌ 請指定雲端同步目錄:'); console.error('使用方法:'); console.error(' node scripts/live-sync.cjs --cloud-dir=~/Dropbox/scratchpad'); console.error(' [--db=scratchpad.v6.db] # 資料庫檔案'); console.error(' [--keep=5] # 保留備份數量'); console.error(' [--dry-run] # 測試模式'); console.error(' [--verbose] # 詳細輸出'); process.exit(1); } function log(message, force = false) { if (config.verbose || force) { console.log(message); } } function expandPath(filePath) { if (filePath.startsWith('~/')) { return path.join(process.env.HOME || process.env.USERPROFILE, filePath.slice(2)); } return path.resolve(filePath); } // 執行非阻塞檢查點 function performLiveCheckpoint(dbPath) { log('\n🔄 執行實時檢查點(不影響 MCP Server)...', true); if (config.dryRun) { log('🔍 測試模式:跳過檢查點'); return { success: true, pages: 0 }; } try { // 使用只讀連接避免干擾 const db = new Database(dbPath, { readonly: false }); // 檢查當前模式和狀態 const currentMode = db.prepare('PRAGMA journal_mode').get(); log(`📊 當前模式: ${currentMode.journal_mode}`); if (currentMode.journal_mode === 'wal') { // 執行被動檢查點 - 不會阻塞其他連接 const result = db.prepare('PRAGMA wal_checkpoint(PASSIVE)').get(); log(`📈 檢查點結果:`); log(` Busy: ${result.busy} (應該是 0)`); log(` Log pages: ${result.log}`); log(` Checkpointed: ${result.checkpointed}`); if (result.busy > 0) { log('⚠️ 有忙碌頁面,但不影響主檔案同步', true); } if (result.checkpointed > 0) { log(`✅ 成功檢查點 ${result.checkpointed} 頁到主檔案`, true); } else { log('✅ 主檔案已是最新狀態', true); } db.close(); return { success: true, pages: result.checkpointed }; } else { log('📋 DELETE 模式,無需檢查點', true); db.close(); return { success: true, pages: 0 }; } } catch (error) { console.error(`❌ 檢查點失敗: ${error.message}`); return { success: false, error: error.message }; } } // 智慧同步到雲端 function smartCloudSync(dbPath, cloudDir) { log('\n☁️ 智慧雲端同步...', true); const expandedCloudDir = expandPath(cloudDir); const dbName = path.basename(dbPath, '.db'); // 確保雲端目錄存在 if (!config.dryRun) { try { fs.mkdirSync(expandedCloudDir, { recursive: true }); } catch (error) { console.error(`❌ 無法建立雲端目錄: ${error.message}`); return false; } } // 檢查主檔案狀態 const dbStats = fs.statSync(dbPath); const cloudCurrentPath = path.join(expandedCloudDir, `${dbName}.db`); let needsSync = true; if (fs.existsSync(cloudCurrentPath)) { const cloudStats = fs.statSync(cloudCurrentPath); needsSync = dbStats.mtime > cloudStats.mtime || dbStats.size !== cloudStats.size; log(`📊 檔案比較:`); log(` 本地: ${dbStats.size} bytes, ${dbStats.mtime.toISOString()}`); log(` 雲端: ${cloudStats.size} bytes, ${cloudStats.mtime.toISOString()}`); log(` 需要同步: ${needsSync ? '是' : '否'}`); } else { log('📋 雲端檔案不存在,需要初始同步', true); } if (!needsSync) { log('✅ 雲端已是最新版本,跳過同步', true); return true; } if (config.dryRun) { log('🔍 測試模式:跳過實際同步'); return true; } try { // 建立帶時間戳的備份 const timestamp = new Date().toISOString().replace(/[:.]/g, '-').slice(0, 19); const backupPath = path.join(expandedCloudDir, `${dbName}_${timestamp}.db`); // 同步主檔案(只複製 .db,不複製 WAL/SHM) fs.copyFileSync(dbPath, cloudCurrentPath); log(`✅ 已同步主檔案: ${cloudCurrentPath}`, true); // 建立備份 fs.copyFileSync(dbPath, backupPath); log(`📦 已建立備份: ${path.basename(backupPath)}`); // 清理舊備份 cleanupOldBackups(expandedCloudDir, dbName); return true; } catch (error) { console.error(`❌ 同步失敗: ${error.message}`); return false; } } // 清理舊備份 function cleanupOldBackups(cloudDir, dbName) { log(`\n🧹 清理舊備份(保留 ${config.keepBackups} 個)...`); try { const backupPattern = new RegExp(`^${dbName}_\\d{4}-\\d{2}-\\d{2}T\\d{2}-\\d{2}-\\d{2}\\.db$`); const files = fs.readdirSync(cloudDir) .filter(file => backupPattern.test(file)) .map(file => ({ name: file, path: path.join(cloudDir, file), mtime: fs.statSync(path.join(cloudDir, file)).mtime })) .sort((a, b) => b.mtime - a.mtime); // 新到舊排序 if (files.length <= config.keepBackups) { log(`✅ 當前 ${files.length} 個備份,無需清理`); return; } const toDelete = files.slice(config.keepBackups); toDelete.forEach(file => { fs.unlinkSync(file.path); log(`🗑️ 已刪除: ${file.name}`); }); log(`✅ 已清理 ${toDelete.length} 個舊備份`); } catch (error) { log(`⚠️ 清理備份時出錯: ${error.message}`); } } // 顯示使用指南 function showLiveUsageGuide() { log('\n📚 實時同步使用指南:', true); log('', true); log('🔄 同步特點:', true); log(' ✅ MCP Server 可保持連線', true); log(' ✅ 使用被動檢查點,不阻塞操作', true); log(' ✅ 只同步主 .db 檔案(最可靠)', true); log(' ✅ 智慧檢測,避免無謂同步', true); log('', true); log('⚡ 自動化選項:', true); log(' # 每 30 分鐘自動同步', true); log(' */30 * * * * cd /path/to/project && node scripts/live-sync.cjs --cloud-dir=~/Dropbox/scratchpad', true); log('', true); log('📱 其他機器使用:', true); log(' cp ~/Dropbox/scratchpad/scratchpad.v6.db ./scratchpad.v6.db', true); } // 主程序 async function main() { console.log('⚡ 實時雲端同步工具 - 無需斷線'); log(`📁 資料庫: ${config.dbPath}`, true); log(`☁️ 雲端目錄: ${config.cloudDir}`, true); if (config.dryRun) { log('🔍 測試模式', true); } try { // 檢查檔案存在 if (!fs.existsSync(config.dbPath)) { console.error(`❌ 資料庫檔案不存在:${config.dbPath}`); process.exit(1); } // 1. 執行實時檢查點 const checkpointResult = performLiveCheckpoint(config.dbPath); if (!checkpointResult.success) { console.error('❌ 檢查點失敗,中止同步'); process.exit(1); } // 2. 智慧同步到雲端 const syncSuccess = smartCloudSync(config.dbPath, config.cloudDir); if (!syncSuccess) { console.error('❌ 雲端同步失敗'); process.exit(1); } // 3. 顯示使用指南 if (!config.dryRun) { showLiveUsageGuide(); } console.log('\n🎉 實時同步完成!MCP Server 無需重啟'); } catch (error) { console.error('\n❌ 執行過程發生錯誤:'); console.error(error.message); process.exit(1); } } main().catch(console.error);

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/pc035860/scratchpad-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server