memory-eternal 0.3.1 → 0.3.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.
- package/README.en.md +1 -1
- package/README.md +18 -2
- package/index.js +15 -1
- package/lib/api.js +6 -0
- package/lib/db.js +28 -0
- package/package.json +1 -1
package/README.en.md
CHANGED
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
</p>
|
|
14
14
|
|
|
15
15
|
> **Auto-captures knowledge after every conversation and survives across sessions; recall fetches only the relevant chunks — saves tokens, less noise.**
|
|
16
|
-
> Fully self-built, zero third-party memory framework, no DSH source changes, one vault shared by every Agent,
|
|
16
|
+
> Fully self-built, zero third-party memory framework, no DSH source changes, one vault shared by every Agent, SQLite persistent storage, zero external dependencies.
|
|
17
17
|
|
|
18
18
|
<p align="center"><strong>⭐ Star it if you like it!</strong> <br/><sub>DSH one-liner: <code>dsh plugin --profile web add memory-eternal</code></sub></p>
|
|
19
19
|
|
package/README.md
CHANGED
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
</p>
|
|
14
14
|
|
|
15
15
|
> **对话结束自动沉淀,跨会话不失忆;召回只取相关小块,省 token 少噪音。**
|
|
16
|
-
> 全自研、零第三方记忆框架、不改 DSH 源码、一个记忆库所有 Agent
|
|
16
|
+
> 全自研、零第三方记忆框架、不改 DSH 源码、一个记忆库所有 Agent 共享,SQLite 持久存储,零外部依赖。
|
|
17
17
|
|
|
18
18
|
<p align="center"><strong>⭐ 觉得好用就点个 Star</strong>! <br/><sub>DSH 一条命令:<code>dsh plugin --profile web add memory-eternal</code></sub></p>
|
|
19
19
|
|
|
@@ -183,6 +183,8 @@ dsh-memory watchdog [--port 7799] # 看门狗保活 web(独立进程)
|
|
|
183
183
|
|
|
184
184
|
新卡默认进**审核中心**(`pending`),由你确认后才入主库;驳回的进「已驳回」,可恢复或删除进回收站。命中免审条件(审核模式=全部免审 / 免审智能体 / 免审类型)的新卡直接入库;回收站软删卡 30 天内可恢复,超期自动永久删除。
|
|
185
185
|
|
|
186
|
+
> **v0.3.0 起:审核守卫下沉到数据库层。** 所有写入路径(自动沉淀 / MCP / UI 新建 / 导入)强制经过 `enforceAudit()`,调用方无法绕过。`setCardStatus()` 是唯一审核操作入口,每次变更写入 `audit_log` 审计日志(who/what/when/why),不可篡改。
|
|
187
|
+
|
|
186
188
|
<p align="center">
|
|
187
189
|
<img src="https://raw.githubusercontent.com/EternalNight996/memory-eternal/main/assets/screen/audit-center.png" width="880" alt="审核中心" />
|
|
188
190
|
</p>
|
|
@@ -215,7 +217,8 @@ memory-eternal 走**人机协同审核**,只让可信内容进主库:
|
|
|
215
217
|
| 去重 | 词法 Jaccard bigram(0.62)+ 语义去重 | 防重复卡 |
|
|
216
218
|
| 检索 | CJK 感知:中文整词 + 字符 bigram | 无需全文搜索引擎 |
|
|
217
219
|
| 图谱 | 力导向 + `[[wikilink]]`/共享标签连边 | 知识关联一眼看清 |
|
|
218
|
-
| 存储 |
|
|
220
|
+
| 存储 | **SQLite**(`node:sqlite` 内置,零依赖)| 不锁库、分表存储、SQL 查询 |
|
|
221
|
+
| 审核 | **数据库层审核守卫** + 审计日志 | 杜绝越权,所有写入强制走审核 |
|
|
219
222
|
|
|
220
223
|
> 与热门项目同向(总结→存储→按需召回),但定位不同:**本地、自研、零依赖、可读可控**。如果你已在用 mem0/Zep 等,也能把它当「本地持久记忆底座」叠加使用。
|
|
221
224
|
|
|
@@ -231,6 +234,19 @@ npm run build # 构建 lib/client.js(DSH 内嵌)+ web/app.js(独立 web
|
|
|
231
234
|
|
|
232
235
|
---
|
|
233
236
|
|
|
237
|
+
## 📋 更新日志
|
|
238
|
+
|
|
239
|
+
| 版本 | 日期 | 关键改动 |
|
|
240
|
+
|---|---|---|
|
|
241
|
+
| **v0.3.1** | 2026-09-03 | 知识图谱按需加载(点击时 fetch,关闭时注销) |
|
|
242
|
+
| **v0.3.0** | 2026-09-03 | **SQLite 化存储层**(`node:sqlite` 零依赖)+ 数据库层审核守卫 `enforceAudit()` + `audit_log` 审计日志 + 自动从 .md 迁移 |
|
|
243
|
+
| v0.2.0 | 2026-09-03 | recall 含正文修复(`search()` 返回 excerpt 字段) |
|
|
244
|
+
| v0.1.2 | 2026-09-03 | 审核守卫加固(`writeCard` 默认 `pending`,`mergeCards` 走审核) |
|
|
245
|
+
| v0.1.1 | 2026-09-03 | recall 含正文修复(excerpt 字段 + body 逻辑修正) |
|
|
246
|
+
| v0.1.0 | 2026-08-31 | 首发:自动沉淀 + 自动召回 + 图形化知识库 + 知识图谱 + 审核中心 + 回收中心 |
|
|
247
|
+
|
|
248
|
+
---
|
|
249
|
+
|
|
234
250
|
## 📄 License
|
|
235
251
|
|
|
236
252
|
MIT
|
package/index.js
CHANGED
|
@@ -26,7 +26,7 @@ const versionRef = (() => { try { const p = JSON.parse(readFileSync(path.join(PA
|
|
|
26
26
|
import z from '@deepseek-ai/schemastery'
|
|
27
27
|
import { defineTool } from '@deepseek-ai/dsh-tools'
|
|
28
28
|
import { ensureVault, search, generateDailyBrief } from './lib/vault.js'
|
|
29
|
-
import { migrateFromMarkdown, setAuditConfig } from './lib/db.js'
|
|
29
|
+
import { migrateFromMarkdown, setAuditConfig, backupDb } from './lib/db.js'
|
|
30
30
|
import { summarizeTurn, extractLastTurn, sliceNewEvents, resolveRoute, captureCard, captureUpdate, pickNeighbors } from './lib/capture.js'
|
|
31
31
|
import { createApi, json } from './lib/api.js'
|
|
32
32
|
|
|
@@ -352,6 +352,20 @@ export function apply(ctx, config) {
|
|
|
352
352
|
}, 30 * 60 * 1000)
|
|
353
353
|
ctx.effect(() => () => clearInterval(purgeTimer), 'memory-eternal: recycle purge timer')
|
|
354
354
|
|
|
355
|
+
// SQLite 定时备份:每天凌晨 3 点自动备份,保留最近 7 天。
|
|
356
|
+
let lastBackupDate = ''
|
|
357
|
+
const backupTimer = setInterval(async () => {
|
|
358
|
+
const d = new Date()
|
|
359
|
+
const date = `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}-${String(d.getDate()).padStart(2, '0')}`
|
|
360
|
+
if (lastBackupDate === date || d.getHours() !== 3) return
|
|
361
|
+
lastBackupDate = date
|
|
362
|
+
try {
|
|
363
|
+
const r = await backupDb(vaultDir(), { maxKeep: 7 })
|
|
364
|
+
if (r.ok) console.error(`[memory-eternal] backup done: ${r.path}`)
|
|
365
|
+
} catch (e) { console.error('[memory-eternal] backup failed:', e?.message || e) }
|
|
366
|
+
}, 10 * 60 * 1000) // 每 10 分钟检查一次,命中凌晨 3 点才执行
|
|
367
|
+
ctx.effect(() => () => clearInterval(backupTimer), 'memory-eternal: backup timer')
|
|
368
|
+
|
|
355
369
|
// -- 3. 知识库 JSON API(客户端设置页数据源) ----------------------------
|
|
356
370
|
// 多宿主状态:web server 常驻地址(ensureWebServer 的结果,client 壳经
|
|
357
371
|
// /web-info 读取后用 iframe 渲染 web 端 UI——DSH 渲染也走 web,单一真源)。
|
package/lib/api.js
CHANGED
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
import { listCards, readCard, search, searchAll, graph, graphAll, overview, exportCards, deleteCard, writeCard, parseCard, stats, optimizeCandidates, optimizeApply, addFeedback, dailyBrief, dailyCounts, mergeCards, ensureVault, auditQueue, recycleList, purgeExpired, setCardStatus, restoreCard } from './vault.js'
|
|
14
14
|
import { compressExcerpt } from './capture.js'
|
|
15
15
|
import { getSetupStatus, runSetup, mcpAgentAction } from './setup.js'
|
|
16
|
+
import { backupDb } from './db.js'
|
|
16
17
|
|
|
17
18
|
export const API_PREFIX = '/memory-eternal/api'
|
|
18
19
|
|
|
@@ -295,6 +296,11 @@ export function createApi(deps = {}) {
|
|
|
295
296
|
json(res, 200, { ok: true, compressed, budgetChars: cfg.sessionBudgetChars ?? 80000 })
|
|
296
297
|
return
|
|
297
298
|
}
|
|
299
|
+
case '/backup': {
|
|
300
|
+
const r = await backupDb(vaultRoot, { maxKeep: 7 })
|
|
301
|
+
json(res, r.ok ? 200 : 500, r)
|
|
302
|
+
return
|
|
303
|
+
}
|
|
298
304
|
default:
|
|
299
305
|
json(res, 404, { ok: false, error: '未知接口' })
|
|
300
306
|
}
|
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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "memory-eternal",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.2",
|
|
4
4
|
"description": "记忆核心(Memory Eternal):自研的 DeepSeek Harness 记忆插件,不移植任何既有记忆框架——对话结束后自动沉淀知识卡到本地 Markdown Vault(自研去重、自研 CJK 检索、可 git 管理),设置页提供图形化知识库(统计 / 搜索 / 知识图谱 + 侧边栏一键弹窗),Agent 通过 memory_recall 工具按需召回历史上下文。零人工干预。",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|