memory-eternal 0.3.1 → 0.3.3
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.en.md +1 -1
- package/README.md +18 -2
- package/index.js +15 -1
- package/lib/api.js +6 -0
- package/lib/client.js +5 -5
- package/lib/db.js +28 -0
- package/lib/vault.js +10 -3
- package/package.json +1 -1
- package/web/app.js +7 -7
package/lib/db.js
CHANGED
|
@@ -176,3 +176,31 @@ export function closeDb(root) {
|
|
|
176
176
|
const db = instances.get(resolved)
|
|
177
177
|
if (db) { db.close(); instances.delete(resolved) }
|
|
178
178
|
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* SQLite 备份:用 VACUUM INTO 创建紧凑副本。
|
|
182
|
+
* 保留最近 maxKeep 个备份,自动清理旧的。
|
|
183
|
+
*/
|
|
184
|
+
export async function backupDb(root, { maxKeep = 7 } = {}) {
|
|
185
|
+
const resolved = path.resolve(root)
|
|
186
|
+
const dbPath = path.join(resolved, DB_FILE)
|
|
187
|
+
const backupDir = path.join(resolved, 'backups')
|
|
188
|
+
await fs.mkdir(backupDir, { recursive: true })
|
|
189
|
+
const now = new Date()
|
|
190
|
+
const date = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}-${String(now.getDate()).padStart(2, '0')}`
|
|
191
|
+
const time = `${String(now.getHours()).padStart(2, '0')}${String(now.getMinutes()).padStart(2, '0')}`
|
|
192
|
+
const backupPath = path.join(backupDir, `memory-eternal-${date}_${time}.db`)
|
|
193
|
+
try {
|
|
194
|
+
const db = getDb(root)
|
|
195
|
+
db.exec(`VACUUM INTO '${backupPath.replace(/'/g, "''")}'`)
|
|
196
|
+
// 清理旧备份:保留最近 maxKeep 个
|
|
197
|
+
const files = await fs.readdir(backupDir)
|
|
198
|
+
const dbBackups = files.filter(f => f.startsWith('memory-eternal-') && f.endsWith('.db')).sort().reverse()
|
|
199
|
+
for (const old of dbBackups.slice(maxKeep)) {
|
|
200
|
+
try { await fs.unlink(path.join(backupDir, old)) } catch {}
|
|
201
|
+
}
|
|
202
|
+
return { ok: true, path: backupPath, kept: Math.min(dbBackups.length, maxKeep) }
|
|
203
|
+
} catch (e) {
|
|
204
|
+
return { ok: false, error: String(e.message || e) }
|
|
205
|
+
}
|
|
206
|
+
}
|
package/lib/vault.js
CHANGED
|
@@ -447,10 +447,17 @@ export async function exportCards(root) {
|
|
|
447
447
|
/** 图谱:节点 = 卡片;边 = [[wikilink]] 或共享标签。 */
|
|
448
448
|
export async function graph(root) {
|
|
449
449
|
const db = getDb(root)
|
|
450
|
-
|
|
451
|
-
const cards =
|
|
450
|
+
// 只选图谱需要的列,不读 body(大幅减少内存和传输量)
|
|
451
|
+
const rows = db.prepare("SELECT id, path, kind, title, tags, summary, status, updated_at FROM cards WHERE status = 'approved' ORDER BY updated_at DESC").all()
|
|
452
|
+
// body 单独按需读取(只在需要 wikilink 匹配时)
|
|
453
|
+
const bodyRows = db.prepare("SELECT path, body FROM cards WHERE status = 'approved'").all()
|
|
454
|
+
const bodyMap = new Map(bodyRows.map((r) => [r.path, r.body]))
|
|
455
|
+
const cards = rows.map((r) => ({
|
|
456
|
+
path: r.path, kind: r.kind, title: r.title, tags: JSON.parse(r.tags || '[]'),
|
|
457
|
+
summary: r.summary || '', updated: r.updated_at, mtime: new Date(r.updated_at).getTime(), status: r.status,
|
|
458
|
+
}))
|
|
459
|
+
|
|
452
460
|
const byPath = new Map(cards.map((c) => [c.path, c]))
|
|
453
|
-
const bodyMap = new Map(rows.map((r) => [r.path, r.body]))
|
|
454
461
|
|
|
455
462
|
const nodes = cards.map((c) => ({
|
|
456
463
|
id: c.path,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "memory-eternal",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.3",
|
|
4
4
|
"description": "记忆核心(Memory Eternal):自研的 DeepSeek Harness 记忆插件,不移植任何既有记忆框架——对话结束后自动沉淀知识卡到本地 Markdown Vault(自研去重、自研 CJK 检索、可 git 管理),设置页提供图形化知识库(统计 / 搜索 / 知识图谱 + 侧边栏一键弹窗),Agent 通过 memory_recall 工具按需召回历史上下文。零人工干预。",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|