memory-eternal 0.20.0 → 0.22.0
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/.claude-plugin/plugin.json +1 -1
- package/.codex-plugin/plugin.json +1 -1
- package/.cursor-plugin/plugin.json +1 -1
- package/README.md +3 -1
- package/README.zh-CN.md +3 -1
- package/cursor-extension/package.json +1 -1
- package/dsh-tui-plugin/dsh-plugin.json +1 -1
- package/dsh-tui-plugin/package.json +1 -1
- package/lib/align.js +16 -2
- package/lib/api.js +99 -5
- package/lib/client.js +36 -20
- package/lib/db.js +12 -0
- package/lib/mcp.js +41 -2
- package/lib/node-bin.js +17 -12
- package/lib/vault.js +780 -4
- package/orca-plugin/orca-plugin.json +1 -1
- package/package.json +2 -2
- package/web/app.js +38 -22
package/lib/db.js
CHANGED
|
@@ -213,6 +213,18 @@ function initTables(db) {
|
|
|
213
213
|
|
|
214
214
|
CREATE INDEX IF NOT EXISTS idx_card_updates_card ON card_updates(card_id);
|
|
215
215
|
|
|
216
|
+
-- 审核制修改:待审副本 → 原卡。agent 只能提交副本,通过后由 setCardStatus 覆盖原卡。
|
|
217
|
+
-- 单独建表(而不是给两张卡表加列):旧库打开时 CREATE TABLE IF NOT EXISTS 即可补上,无需迁移。
|
|
218
|
+
CREATE TABLE IF NOT EXISTS card_revisions (
|
|
219
|
+
revision_path TEXT PRIMARY KEY,
|
|
220
|
+
target_path TEXT NOT NULL,
|
|
221
|
+
target_id INTEGER,
|
|
222
|
+
created_at TEXT DEFAULT (datetime('now')),
|
|
223
|
+
updated_at TEXT DEFAULT (datetime('now'))
|
|
224
|
+
);
|
|
225
|
+
|
|
226
|
+
CREATE INDEX IF NOT EXISTS idx_card_revisions_target ON card_revisions(target_path);
|
|
227
|
+
|
|
216
228
|
CREATE TABLE IF NOT EXISTS feedback (
|
|
217
229
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
218
230
|
query TEXT NOT NULL,
|
package/lib/mcp.js
CHANGED
|
@@ -5,7 +5,8 @@
|
|
|
5
5
|
// (stdout 是协议通道,被 console.log 污染会破坏握手)。
|
|
6
6
|
//
|
|
7
7
|
// 支持的协议版本:2025-06-18 / 2025-03-26 / 2024-11-05(协商取请求版本)。
|
|
8
|
-
// 工具:memory_recall / memory_capture / memory_stats / memory_audit_list。
|
|
8
|
+
// 工具:memory_recall / memory_capture / memory_stats / memory_audit_list / memory_revise。
|
|
9
|
+
// memory_revise 只**提交**修改副本进审核(不会直接改库、不能批准),与 issue #17 的审核边界一致。
|
|
9
10
|
//
|
|
10
11
|
// 审核边界(issue #17):MCP 侧**只读**。新增的 memory_audit_list 只列待审/驳回,
|
|
11
12
|
// 不提供任何 approve/reject —— 否则任何接了 MCP 的智能体都能批准自己刚写进去的卡,
|
|
@@ -14,7 +15,7 @@
|
|
|
14
15
|
// 启动:node bin/dsh-memory.mjs mcp (或 node lib/mcp.js)
|
|
15
16
|
|
|
16
17
|
import readline from 'node:readline'
|
|
17
|
-
import { search, overview, stats, ensureVault, listCards, countCards } from './vault.js'
|
|
18
|
+
import { search, overview, stats, ensureVault, listCards, countCards, reviseCard } from './vault.js'
|
|
18
19
|
import { runStandaloneCapture, defaultVaultDir } from './capture-run.js'
|
|
19
20
|
import { recordLedger } from './ledger.js'
|
|
20
21
|
|
|
@@ -72,6 +73,25 @@ export const MCP_TOOLS = [
|
|
|
72
73
|
},
|
|
73
74
|
},
|
|
74
75
|
},
|
|
76
|
+
{
|
|
77
|
+
name: 'memory_revise',
|
|
78
|
+
description:
|
|
79
|
+
'提交「审核制修改」:把一张已批准的知识卡复制成待审副本(可改标题/标签/正文),进入审核中心。' +
|
|
80
|
+
'人工批准后才会覆盖原卡;agent 不能直接改已入库内容,也不能自己批准。' +
|
|
81
|
+
'用于修正过期/错误的知识 —— 比再写一张新卡更干净(审核界面会标成「修改」,通过即覆盖)。',
|
|
82
|
+
inputSchema: {
|
|
83
|
+
type: 'object',
|
|
84
|
+
properties: {
|
|
85
|
+
path: { type: 'string', description: '要修改的原卡 path(memory_recall 结果里的「路径:…」)' },
|
|
86
|
+
title: { type: 'string', description: '新标题(不改就不传)' },
|
|
87
|
+
tags: { type: 'array', items: { type: 'string' }, description: '新标签(不改就不传)' },
|
|
88
|
+
body: { type: 'string', description: '新正文(不改就不传)' },
|
|
89
|
+
reason: { type: 'string', description: '修改原因,会显示在审核中心' },
|
|
90
|
+
source: { type: 'string', description: '来源标记,如 claude-code / codex' },
|
|
91
|
+
},
|
|
92
|
+
required: ['path'],
|
|
93
|
+
},
|
|
94
|
+
},
|
|
75
95
|
]
|
|
76
96
|
|
|
77
97
|
function toolResult(text, isError = false) {
|
|
@@ -108,6 +128,25 @@ export async function callTool(name, args, vaultRoot, meta = {}) {
|
|
|
108
128
|
if (!out.ok && out.action === 'failed') return toolResult(`沉淀失败:${out.reason}`, true)
|
|
109
129
|
return toolResult(JSON.stringify(out))
|
|
110
130
|
}
|
|
131
|
+
case 'memory_revise': {
|
|
132
|
+
const rel = String(args?.path || '').trim()
|
|
133
|
+
if (!rel) return toolResult('(未提供要修改的卡片 path)')
|
|
134
|
+
try {
|
|
135
|
+
const src = String(args?.source || meta.submittedBy || 'mcp')
|
|
136
|
+
const out = await reviseCard(vaultRoot, {
|
|
137
|
+
target: rel,
|
|
138
|
+
title: args?.title === undefined ? undefined : String(args.title),
|
|
139
|
+
tags: Array.isArray(args?.tags) ? args.tags : undefined,
|
|
140
|
+
body: args?.body === undefined ? undefined : String(args.body),
|
|
141
|
+
reason: String(args?.reason || ''),
|
|
142
|
+
source: src,
|
|
143
|
+
submittedBy: src,
|
|
144
|
+
})
|
|
145
|
+
return toolResult('已提交修改,待人工审核通过后覆盖原卡。\n修改副本:' + out.path + '\n原卡:' + out.target + (out.reused ? '\n(已存在待审修改,本次就地更新,未新增条目)' : ''))
|
|
146
|
+
} catch (e) {
|
|
147
|
+
return toolResult('提交修改失败:' + ((e && e.message) || e), true)
|
|
148
|
+
}
|
|
149
|
+
}
|
|
111
150
|
case 'memory_audit_list': {
|
|
112
151
|
await ensureVault(vaultRoot)
|
|
113
152
|
const wanted = String(args?.status || 'pending').toLowerCase()
|
package/lib/node-bin.js
CHANGED
|
@@ -11,8 +11,9 @@
|
|
|
11
11
|
//
|
|
12
12
|
// 本模块把「找出一个真正能跑 node 脚本的子进程入口」收敛到一处:
|
|
13
13
|
// 1. MEMORY_ETERNAL_NODE 显式指定(逃生口,便于测试与非常规宿主);
|
|
14
|
-
// 2. 非 Electron
|
|
15
|
-
//
|
|
14
|
+
// 2. 非 Electron 宿主:**优先从 PATH 找 node**(issue #56:brew/nvm/volta 在 PATH 上的入口
|
|
15
|
+
// 是稳定 shim,而 process.execPath 是带版本号的真实路径),execPath 降为兜底;
|
|
16
|
+
// 3. Electron 宿主:同样优先从 PATH 找真正的 node;
|
|
16
17
|
// 4. 都找不到时退回 process.execPath,并由 childEnv() 打开
|
|
17
18
|
// ELECTRON_RUN_AS_NODE,让 Electron 二进制以纯 node 模式运行。
|
|
18
19
|
|
|
@@ -22,8 +23,8 @@ import path from 'node:path'
|
|
|
22
23
|
let cachedBinary = null
|
|
23
24
|
let cachedSource = null
|
|
24
25
|
|
|
25
|
-
/** 在 PATH 中查找真正的 node 可执行文件;找不到返回 null
|
|
26
|
-
function findNodeOnPath() {
|
|
26
|
+
/** 在 PATH 中查找真正的 node 可执行文件;找不到返回 null。(导出便于测试断言 PATH 优先) */
|
|
27
|
+
export function findNodeOnPath() {
|
|
27
28
|
const names = process.platform === 'win32' ? ['node.exe', 'node.cmd', 'node'] : ['node', 'node.exe']
|
|
28
29
|
const dirs = String(process.env.PATH || '').split(path.delimiter)
|
|
29
30
|
for (const dir of dirs) {
|
|
@@ -72,17 +73,21 @@ export function nodeBinary() {
|
|
|
72
73
|
}
|
|
73
74
|
|
|
74
75
|
if (!process.versions.electron) {
|
|
75
|
-
//
|
|
76
|
-
//
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
}
|
|
76
|
+
// issue #56:非 Electron 下**必须 PATH 优先**。process.execPath 是「这一次装的那份 node」的
|
|
77
|
+
// 真实路径(macOS Homebrew:/opt/homebrew/Cellar/node/26.13.0_2/bin/node),它会被写进
|
|
78
|
+
// ~/.claude.json 等外部客户端的 MCP 条目;brew upgrade node 一删旧目录,这些条目就 ENOENT。
|
|
79
|
+
// PATH 上的入口(/opt/homebrew/bin/node、~/.volta/bin/node、nvm 的 shim)才是稳定的。
|
|
80
|
+
// 同时它决定了 align 的 currentNodePath —— 写入侧与校验侧一起收敛到 shim,才不会互相打脸。
|
|
81
|
+
// execPath 仍保留为兜底(PATH 上没有 node 时行为与 #44 之前一致)。
|
|
82
82
|
const viaPath = findNodeOnPath()
|
|
83
83
|
if (viaPath) {
|
|
84
84
|
cachedBinary = viaPath
|
|
85
|
-
cachedSource = 'path
|
|
85
|
+
cachedSource = 'path'
|
|
86
|
+
return cachedBinary
|
|
87
|
+
}
|
|
88
|
+
if (exists(process.execPath)) {
|
|
89
|
+
cachedBinary = process.execPath
|
|
90
|
+
cachedSource = 'execPath'
|
|
86
91
|
return cachedBinary
|
|
87
92
|
}
|
|
88
93
|
cachedBinary = process.execPath
|