memory-eternal 0.18.0 → 0.18.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/.claude-plugin/plugin.json +1 -1
- package/.codex-plugin/plugin.json +1 -1
- package/.cursor-plugin/plugin.json +1 -1
- package/README.md +19 -1
- package/README.zh-CN.md +19 -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/index.js +11 -1
- package/lib/api.js +1 -1
- package/lib/client.js +26 -20
- package/lib/setup.js +110 -0
- package/orca-plugin/orca-plugin.json +1 -1
- package/package.json +1 -1
- package/web/app.js +27 -21
package/lib/setup.js
CHANGED
|
@@ -100,6 +100,116 @@ export function mcpCommand(nodePath = nodeBinary()) {
|
|
|
100
100
|
return out
|
|
101
101
|
}
|
|
102
102
|
|
|
103
|
+
/**
|
|
104
|
+
* 升级自动迁移(issue #53):只重写「我们自己写过、且指向 Electron 宿主 exe」的条目。
|
|
105
|
+
* 不碰用户自定义命令;写入前备份。返回 changed / details,方便宿主日志。
|
|
106
|
+
*/
|
|
107
|
+
export async function repairLegacySetup({ homeDir = home, dryRun = false, log = () => {}, legacyCheck = isLegacyElectronCommand } = {}) {
|
|
108
|
+
const root = homeDir
|
|
109
|
+
const node = nodeBinary()
|
|
110
|
+
const isLegacy = (cmd) => Boolean(legacyCheck(cmd))
|
|
111
|
+
const want = mcpCommand(node)
|
|
112
|
+
const captureScript = path.join(PACKAGE_ROOT, 'hooks', 'capture.mjs')
|
|
113
|
+
const guardScript = path.join(PACKAGE_ROOT, 'hooks', 'secret-guard.mjs')
|
|
114
|
+
const details = []
|
|
115
|
+
let changed = 0
|
|
116
|
+
|
|
117
|
+
const rewriteHooksTree = (value, agent, marker) => {
|
|
118
|
+
let hit = 0
|
|
119
|
+
const walk = (v) => {
|
|
120
|
+
if (!v) return
|
|
121
|
+
if (Array.isArray(v)) { for (const x of v) walk(x); return }
|
|
122
|
+
if (typeof v !== 'object') return
|
|
123
|
+
if (typeof v.command === 'string' && v.command.includes(marker) && isLegacy(v.command)) {
|
|
124
|
+
v.command = marker === 'secret-guard.mjs' ? hookCommand(node, guardScript) : hookCommand(node, captureScript, agent)
|
|
125
|
+
hit++
|
|
126
|
+
}
|
|
127
|
+
for (const x of Object.values(v)) walk(x)
|
|
128
|
+
}
|
|
129
|
+
walk(value)
|
|
130
|
+
return hit
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// Claude Code MCP + hooks
|
|
134
|
+
const claudeJson = path.join(root, '.claude.json')
|
|
135
|
+
if (await exists(claudeJson)) {
|
|
136
|
+
try {
|
|
137
|
+
const cfg = JSON.parse(await fs.readFile(claudeJson, 'utf8'))
|
|
138
|
+
const entry = cfg?.mcpServers?.memory
|
|
139
|
+
if (entry && isLegacy(entry.command)) {
|
|
140
|
+
if (!dryRun) { await backupOnce(claudeJson); cfg.mcpServers.memory = { type: 'stdio', ...want }; await fs.writeFile(claudeJson, JSON.stringify(cfg, null, 2), 'utf8') }
|
|
141
|
+
details.push('claude.mcp'); changed++
|
|
142
|
+
}
|
|
143
|
+
} catch { /* 解析失败不碰 */ }
|
|
144
|
+
}
|
|
145
|
+
const claudeSettings = path.join(root, '.claude', 'settings.json')
|
|
146
|
+
if (await exists(claudeSettings)) {
|
|
147
|
+
try {
|
|
148
|
+
const cfg = JSON.parse(await fs.readFile(claudeSettings, 'utf8'))
|
|
149
|
+
const a = rewriteHooksTree(cfg.hooks, 'claude', 'capture.mjs')
|
|
150
|
+
const b = rewriteHooksTree(cfg.hooks, 'claude', 'secret-guard.mjs')
|
|
151
|
+
if (a + b) {
|
|
152
|
+
if (!dryRun) { await backupOnce(claudeSettings); await fs.writeFile(claudeSettings, JSON.stringify(cfg, null, 2), 'utf8') }
|
|
153
|
+
details.push('claude.hooks'); changed += a + b
|
|
154
|
+
}
|
|
155
|
+
} catch { /* 解析失败不碰 */ }
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// Codex MCP + hooks
|
|
159
|
+
const codexToml = path.join(root, '.codex', 'config.toml')
|
|
160
|
+
if (await exists(codexToml)) {
|
|
161
|
+
try {
|
|
162
|
+
const text = await fs.readFile(codexToml, 'utf8')
|
|
163
|
+
const entry = parseCodexMemoryEntry(text)
|
|
164
|
+
if (entry.present && isLegacy(entry.command)) {
|
|
165
|
+
const out = alignCodexMemorySection(text, want)
|
|
166
|
+
if (out.changed) {
|
|
167
|
+
if (!dryRun) { await backupOnce(codexToml); await fs.writeFile(codexToml, out.text, 'utf8') }
|
|
168
|
+
details.push('codex.mcp'); changed++
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
} catch { /* 解析失败不碰 */ }
|
|
172
|
+
}
|
|
173
|
+
const codexHooks = path.join(root, '.codex', 'hooks.json')
|
|
174
|
+
if (await exists(codexHooks)) {
|
|
175
|
+
try {
|
|
176
|
+
const cfg = JSON.parse(await fs.readFile(codexHooks, 'utf8'))
|
|
177
|
+
const n = rewriteHooksTree(cfg.hooks, 'codex', 'capture.mjs')
|
|
178
|
+
if (n) {
|
|
179
|
+
if (!dryRun) { await backupOnce(codexHooks); await fs.writeFile(codexHooks, JSON.stringify(cfg, null, 2), 'utf8') }
|
|
180
|
+
details.push('codex.hooks'); changed += n
|
|
181
|
+
}
|
|
182
|
+
} catch { /* 解析失败不碰 */ }
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// Cursor MCP + hooks
|
|
186
|
+
const cursorJson = path.join(root, '.cursor', 'mcp.json')
|
|
187
|
+
if (await exists(cursorJson)) {
|
|
188
|
+
try {
|
|
189
|
+
const cfg = JSON.parse(await fs.readFile(cursorJson, 'utf8'))
|
|
190
|
+
const entry = cfg?.mcpServers?.memory
|
|
191
|
+
if (entry && isLegacy(entry.command)) {
|
|
192
|
+
if (!dryRun) { await backupOnce(cursorJson); cfg.mcpServers.memory = want; await fs.writeFile(cursorJson, JSON.stringify(cfg, null, 2), 'utf8') }
|
|
193
|
+
details.push('cursor.mcp'); changed++
|
|
194
|
+
}
|
|
195
|
+
} catch { /* 解析失败不碰 */ }
|
|
196
|
+
}
|
|
197
|
+
const cursorHooks = path.join(root, '.cursor', 'hooks.json')
|
|
198
|
+
if (await exists(cursorHooks)) {
|
|
199
|
+
try {
|
|
200
|
+
const cfg = JSON.parse(await fs.readFile(cursorHooks, 'utf8'))
|
|
201
|
+
const n = rewriteHooksTree(cfg.hooks, 'cursor', 'capture.mjs')
|
|
202
|
+
if (n) {
|
|
203
|
+
if (!dryRun) { await backupOnce(cursorHooks); await fs.writeFile(cursorHooks, JSON.stringify(cfg, null, 2), 'utf8') }
|
|
204
|
+
details.push('cursor.hooks'); changed += n
|
|
205
|
+
}
|
|
206
|
+
} catch { /* 解析失败不碰 */ }
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
if (changed) log('↻ 已自动迁移旧 Electron 配置:' + details.join(' / '))
|
|
210
|
+
return { ok: true, changed, details, nodePath: node }
|
|
211
|
+
}
|
|
212
|
+
|
|
103
213
|
function sameMcpEntry(existing, want) {
|
|
104
214
|
if (!existing || typeof existing !== 'object') return false
|
|
105
215
|
if (existing.type === 'stdio' || existing.type === undefined) {
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"id": "memory-eternal",
|
|
4
4
|
"publisher": "eternalnight996",
|
|
5
5
|
"name": "memory-eternal",
|
|
6
|
-
"version": "0.18.
|
|
6
|
+
"version": "0.18.2",
|
|
7
7
|
"description": "Memory Atlas for Orca: open the local memory vault, pending review and knowledge graph from a sandboxed Orca panel.",
|
|
8
8
|
"author": {
|
|
9
9
|
"name": "EternalNight996",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "memory-eternal",
|
|
3
|
-
"version": "0.18.
|
|
3
|
+
"version": "0.18.2",
|
|
4
4
|
"description": "记忆核心(Memory Eternal):自研的 DeepSeek Harness 记忆插件,不移植任何既有记忆框架——对话结束后自动沉淀知识卡到本地 Markdown Vault(自研去重、自研 CJK 检索、可 git 管理),设置页提供图形化知识库(统计 / 搜索 / 知识图谱 + 侧边栏一键弹窗),Agent 通过 memory_recall 工具按需召回历史上下文。零人工干预。",
|
|
5
5
|
"icon": "assets/icon.png",
|
|
6
6
|
"license": "MIT",
|