memory-eternal 0.11.0 → 0.13.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.en.md +22 -4
- package/README.md +22 -4
- package/cursor-extension/README.md +41 -0
- package/cursor-extension/extension.js +58 -0
- package/cursor-extension/icon.png +0 -0
- package/cursor-extension/package.json +54 -0
- package/dsh-tui-plugin/README.md +119 -0
- package/dsh-tui-plugin/cordis.patch.yml +3 -0
- package/dsh-tui-plugin/demo-audit.mjs +535 -0
- package/dsh-tui-plugin/demo.mjs +743 -0
- package/dsh-tui-plugin/dsh-plugin.json +42 -0
- package/dsh-tui-plugin/main.js +97 -0
- package/dsh-tui-plugin/package.json +31 -0
- package/dsh-tui-plugin/scene.js +151 -0
- package/lib/client.js +1 -1
- package/orca-plugin/README.md +58 -0
- package/orca-plugin/assets/icon.png +0 -0
- package/orca-plugin/orca-plugin.json +36 -0
- package/orca-plugin/panel.html +215 -0
- package/package.json +4 -1
- package/skills/memory-open/SKILL.md +22 -0
- package/web/app.js +1 -1
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="zh-CN">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
6
|
+
<title>Memory Atlas</title>
|
|
7
|
+
<style>
|
|
8
|
+
:root { color-scheme: dark; }
|
|
9
|
+
* { box-sizing: border-box; }
|
|
10
|
+
body {
|
|
11
|
+
margin: 0;
|
|
12
|
+
min-height: 100vh;
|
|
13
|
+
padding: 18px 16px 22px;
|
|
14
|
+
font-family: var(--font-sans, system-ui, -apple-system, "Segoe UI", "Microsoft YaHei", sans-serif);
|
|
15
|
+
font-size: 12.5px;
|
|
16
|
+
line-height: 1.6;
|
|
17
|
+
color: var(--foreground, #f3f5f8);
|
|
18
|
+
background:
|
|
19
|
+
radial-gradient(120% 80% at 100% 0%, rgba(255,122,69,.10), transparent 55%),
|
|
20
|
+
var(--background, #1e2228);
|
|
21
|
+
}
|
|
22
|
+
.brand { display: flex; align-items: center; gap: 10px; margin-bottom: 16px; }
|
|
23
|
+
.mark {
|
|
24
|
+
width: 38px; height: 38px; flex: none;
|
|
25
|
+
display: flex; align-items: center; justify-content: center;
|
|
26
|
+
border-radius: 11px; font-size: 21px;
|
|
27
|
+
background: rgba(255,122,69,.10);
|
|
28
|
+
border: 1px solid rgba(255,122,69,.24);
|
|
29
|
+
}
|
|
30
|
+
.brand h1 { margin: 0; font-size: 16px; line-height: 1.2; font-weight: 650; }
|
|
31
|
+
.brand p { margin: 2px 0 0; font-size: 10px; letter-spacing: .12em; color: var(--muted-foreground, #99a1ac); }
|
|
32
|
+
.card {
|
|
33
|
+
padding: 14px;
|
|
34
|
+
border: 1px solid var(--border, #3a404a);
|
|
35
|
+
border-radius: 12px;
|
|
36
|
+
background: var(--card, rgba(38,43,51,.92));
|
|
37
|
+
}
|
|
38
|
+
.card h2 { margin: 0 0 5px; font-size: 12.5px; font-weight: 600; }
|
|
39
|
+
.card p { margin: 0; color: var(--muted-foreground, #b0b7c1); font-size: 11px; }
|
|
40
|
+
.row { display: flex; flex-direction: column; gap: 5px; margin-top: 13px; }
|
|
41
|
+
.row label { font-size: 10px; color: var(--muted-foreground, #99a1ac); }
|
|
42
|
+
select {
|
|
43
|
+
width: 100%;
|
|
44
|
+
padding: 7px 8px;
|
|
45
|
+
border-radius: 7px;
|
|
46
|
+
border: 1px solid var(--border, #3a404a);
|
|
47
|
+
background: var(--secondary, #2a2f37);
|
|
48
|
+
color: var(--foreground, #f3f5f8);
|
|
49
|
+
font: inherit;
|
|
50
|
+
font-size: 11px;
|
|
51
|
+
}
|
|
52
|
+
button {
|
|
53
|
+
width: 100%;
|
|
54
|
+
margin-top: 13px;
|
|
55
|
+
padding: 9px 10px;
|
|
56
|
+
border: 1px solid transparent;
|
|
57
|
+
border-radius: 8px;
|
|
58
|
+
background: var(--primary, #ff7a45);
|
|
59
|
+
color: var(--primary-foreground, #1b1f25);
|
|
60
|
+
font: inherit;
|
|
61
|
+
font-size: 12px;
|
|
62
|
+
font-weight: 650;
|
|
63
|
+
cursor: pointer;
|
|
64
|
+
}
|
|
65
|
+
button:hover { filter: brightness(1.06); }
|
|
66
|
+
button:disabled { opacity: .45; cursor: default; }
|
|
67
|
+
button.secondary { margin-top: 7px; background: transparent; color: var(--foreground, #f3f5f8); border-color: var(--border, #3a404a); }
|
|
68
|
+
.status { min-height: 1.6em; margin: 10px 0 0; font-size: 10.5px; color: var(--muted-foreground, #99a1ac); }
|
|
69
|
+
.fallback { margin-top: 14px; padding-top: 12px; border-top: 1px solid var(--border, #3a404a); font-size: 10.5px; color: var(--muted-foreground, #99a1ac); }
|
|
70
|
+
.fallback code {
|
|
71
|
+
display: inline-block;
|
|
72
|
+
margin-top: 4px;
|
|
73
|
+
padding: 2px 6px;
|
|
74
|
+
border-radius: 4px;
|
|
75
|
+
background: rgba(127,127,127,.12);
|
|
76
|
+
color: var(--foreground, #f3f5f8);
|
|
77
|
+
font-family: ui-monospace, "Cascadia Code", Consolas, monospace;
|
|
78
|
+
font-size: 10px;
|
|
79
|
+
}
|
|
80
|
+
.warn { color: var(--destructive, #f87171); }
|
|
81
|
+
.ok { color: #4ade80; }
|
|
82
|
+
</style>
|
|
83
|
+
</head>
|
|
84
|
+
<body>
|
|
85
|
+
<div class="brand">
|
|
86
|
+
<div class="mark">🧠</div>
|
|
87
|
+
<div>
|
|
88
|
+
<h1>Memory Atlas</h1>
|
|
89
|
+
<p>MEMORY ETERNAL</p>
|
|
90
|
+
</div>
|
|
91
|
+
</div>
|
|
92
|
+
|
|
93
|
+
<div class="card">
|
|
94
|
+
<h2>本地记忆库</h2>
|
|
95
|
+
<p>记忆库、待审、读卡器与知识星图都在 Web 面板里;Orca 只负责一键打开,不复制第二套 UI。</p>
|
|
96
|
+
|
|
97
|
+
<div class="row">
|
|
98
|
+
<label for="terminal">发送到终端</label>
|
|
99
|
+
<select id="terminal"><option value="">正在读取工作区…</option></select>
|
|
100
|
+
</div>
|
|
101
|
+
|
|
102
|
+
<button id="openOrca" disabled>在 Orca 浏览器打开</button>
|
|
103
|
+
<button id="openBrowser" class="secondary" disabled>外部浏览器打开</button>
|
|
104
|
+
<p id="status" class="status"></p>
|
|
105
|
+
|
|
106
|
+
<div class="fallback">
|
|
107
|
+
“在 Orca 浏览器打开”会执行 <code>orca tab create</code>,需要 <code>orca</code> CLI 在 PATH;失败时用外部浏览器:
|
|
108
|
+
<code>dsh-memory open</code>
|
|
109
|
+
<br/>Web 地址:<code>http://127.0.0.1:7999/</code>
|
|
110
|
+
</div>
|
|
111
|
+
</div>
|
|
112
|
+
|
|
113
|
+
<script>
|
|
114
|
+
'use strict'
|
|
115
|
+
|
|
116
|
+
var seq = 0
|
|
117
|
+
var pending = new Map()
|
|
118
|
+
var statusEl = document.getElementById('status')
|
|
119
|
+
var terminalEl = document.getElementById('terminal')
|
|
120
|
+
var openOrcaBtn = document.getElementById('openOrca')
|
|
121
|
+
var openBrowserBtn = document.getElementById('openBrowser')
|
|
122
|
+
|
|
123
|
+
function setStatus(text, cls) {
|
|
124
|
+
statusEl.textContent = text || ''
|
|
125
|
+
statusEl.className = 'status' + (cls ? ' ' + cls : '')
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function call(action, params) {
|
|
129
|
+
return new Promise(function (resolve, reject) {
|
|
130
|
+
var requestId = 'req-' + (++seq)
|
|
131
|
+
pending.set(requestId, { resolve: resolve, reject: reject })
|
|
132
|
+
window.parent.postMessage({
|
|
133
|
+
type: 'orca-panel-action',
|
|
134
|
+
requestId: requestId,
|
|
135
|
+
action: action,
|
|
136
|
+
params: params || {}
|
|
137
|
+
}, '*')
|
|
138
|
+
})
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
window.addEventListener('message', function (event) {
|
|
142
|
+
var data = event.data
|
|
143
|
+
if (!data || data.type !== 'orca-panel-action-result') return
|
|
144
|
+
var entry = pending.get(data.requestId)
|
|
145
|
+
if (!entry) return
|
|
146
|
+
pending.delete(data.requestId)
|
|
147
|
+
if (data.ok) entry.resolve(data.value)
|
|
148
|
+
else entry.reject(new Error(data.error || data.errorCode || 'action failed'))
|
|
149
|
+
})
|
|
150
|
+
|
|
151
|
+
async function loadTerminals() {
|
|
152
|
+
try {
|
|
153
|
+
var ctx = await call('workspace.readContext')
|
|
154
|
+
var terminals = (ctx && ctx.terminals) || []
|
|
155
|
+
terminalEl.innerHTML = ''
|
|
156
|
+
if (!terminals.length) {
|
|
157
|
+
var opt = document.createElement('option')
|
|
158
|
+
opt.value = ''
|
|
159
|
+
opt.textContent = '请先打开一个 Orca 终端'
|
|
160
|
+
terminalEl.appendChild(opt)
|
|
161
|
+
openOrcaBtn.disabled = true
|
|
162
|
+
openBrowserBtn.disabled = true
|
|
163
|
+
setStatus('需要一个终端来打开 Memory Atlas。', 'warn')
|
|
164
|
+
return
|
|
165
|
+
}
|
|
166
|
+
terminals.forEach(function (term, index) {
|
|
167
|
+
var opt = document.createElement('option')
|
|
168
|
+
opt.value = term.id
|
|
169
|
+
opt.textContent = '终端 ' + (index + 1)
|
|
170
|
+
terminalEl.appendChild(opt)
|
|
171
|
+
})
|
|
172
|
+
openOrcaBtn.disabled = false
|
|
173
|
+
openBrowserBtn.disabled = false
|
|
174
|
+
setStatus('已就绪。')
|
|
175
|
+
} catch (err) {
|
|
176
|
+
setStatus('无法读取工作区终端:' + err.message, 'warn')
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
async function sendCommand(text, okText) {
|
|
181
|
+
var terminalId = terminalEl.value
|
|
182
|
+
if (!terminalId) {
|
|
183
|
+
setStatus('请先选择一个终端。', 'warn')
|
|
184
|
+
return
|
|
185
|
+
}
|
|
186
|
+
openOrcaBtn.disabled = true
|
|
187
|
+
openBrowserBtn.disabled = true
|
|
188
|
+
setStatus('正在打开 Memory Atlas…')
|
|
189
|
+
try {
|
|
190
|
+
await call('terminal.sendText', {
|
|
191
|
+
terminalId: terminalId,
|
|
192
|
+
text: text,
|
|
193
|
+
enter: true
|
|
194
|
+
})
|
|
195
|
+
setStatus(okText, 'ok')
|
|
196
|
+
} catch (err) {
|
|
197
|
+
setStatus('打开失败:' + err.message + '。请在终端手动执行 dsh-memory open。', 'warn')
|
|
198
|
+
} finally {
|
|
199
|
+
openOrcaBtn.disabled = false
|
|
200
|
+
openBrowserBtn.disabled = false
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
openOrcaBtn.addEventListener('click', function () {
|
|
205
|
+
sendCommand('orca tab create --url http://127.0.0.1:7999/', '已发送 orca tab create。若 Orca 内置浏览器打开,即可在 Orca 内使用 Memory Atlas。')
|
|
206
|
+
})
|
|
207
|
+
|
|
208
|
+
openBrowserBtn.addEventListener('click', function () {
|
|
209
|
+
sendCommand('dsh-memory open', '已发送 dsh-memory open。浏览器打开后即可使用 Memory Atlas。')
|
|
210
|
+
})
|
|
211
|
+
|
|
212
|
+
loadTerminals()
|
|
213
|
+
</script>
|
|
214
|
+
</body>
|
|
215
|
+
</html>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "memory-eternal",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.13.0",
|
|
4
4
|
"description": "记忆核心(Memory Eternal):自研的 DeepSeek Harness 记忆插件,不移植任何既有记忆框架——对话结束后自动沉淀知识卡到本地 Markdown Vault(自研去重、自研 CJK 检索、可 git 管理),设置页提供图形化知识库(统计 / 搜索 / 知识图谱 + 侧边栏一键弹窗),Agent 通过 memory_recall 工具按需召回历史上下文。零人工干预。",
|
|
5
5
|
"icon": "assets/icon.png",
|
|
6
6
|
"license": "MIT",
|
|
@@ -34,6 +34,9 @@
|
|
|
34
34
|
"hooks",
|
|
35
35
|
"skills",
|
|
36
36
|
"assets/icon.png",
|
|
37
|
+
"orca-plugin",
|
|
38
|
+
"dsh-tui-plugin",
|
|
39
|
+
"cursor-extension",
|
|
37
40
|
".claude-plugin",
|
|
38
41
|
".codex-plugin",
|
|
39
42
|
".cursor-plugin",
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: memory-open
|
|
3
|
+
description: Open the local Memory Atlas Web UI (memory-eternal) from Codex or Claude Code. Use when the user asks to open the memory library, review queue or knowledge graph.
|
|
4
|
+
allowed-tools: Bash(dsh-memory open)
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Open Memory Atlas
|
|
8
|
+
|
|
9
|
+
Run this once, from the workspace root:
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
dsh-memory open
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
It ensures the local memory Web UI is running on `http://127.0.0.1:7999/` and opens the default browser.
|
|
16
|
+
|
|
17
|
+
Rules:
|
|
18
|
+
|
|
19
|
+
- Do not approve or reject cards from here. Review is done by the person in the Web UI.
|
|
20
|
+
- Do not write or mutate memory cards.
|
|
21
|
+
- If `dsh-memory` is not on PATH, read the repository `README.md` for the CLI install path, or ask the user to run the command manually.
|
|
22
|
+
- After opening, tell the user the review queue is available in the Web UI; keep the reply short.
|
package/web/app.js
CHANGED
|
@@ -703,7 +703,7 @@ Option A, because:
|
|
|
703
703
|
.mc-audit-batchcard > .mc-btn { width: 100%; }
|
|
704
704
|
.mc-audit-batchcard > div[style*="flex: 1"] { display: none; }
|
|
705
705
|
}
|
|
706
|
-
`;var ro="0.
|
|
706
|
+
`;var ro="0.13.0";function mf({t:e,inModal:t,onClose:n}){let r=typeof window<"u"&&(!!window.__DSH_BOOT__||String(location.pathname||"").includes("/memory-eternal/api/ui/app")),[a,i]=(0,g.useState)(null),[l,s]=(0,g.useState)([]),[d,p]=(0,g.useState)(!0),[v,h]=(0,g.useState)(null),[u,S]=(0,g.useState)(""),[N,M]=(0,g.useState)("all"),[_,f]=(0,g.useState)(""),[c,y]=(0,g.useState)("all"),[C,L]=(0,g.useState)("all"),[j,P]=(0,g.useState)(()=>{try{return new Set(JSON.parse(localStorage.getItem("me-pinned-v5")||"[]"))}catch{return new Set}}),[H,ne]=(0,g.useState)(!1),B=(0,g.useCallback)(E=>{P(x=>{let D=new Set(x);D.has(E)?D.delete(E):D.add(E);try{localStorage.setItem("me-pinned-v5",JSON.stringify([...D]))}catch{}return D})},[]),[J,Ve]=(0,g.useState)(""),[X,Ye]=(0,g.useState)(()=>{try{let E=new URLSearchParams(typeof location<"u"?location.search:"").get("tab"),x={cards:"cards",graph:"graph",usage:"stats",stats:"stats",audit:"audit",recycle:"optimize",optimize:"optimize",config:"config"};if(E&&x[E])return x[E]}catch{}return"cards"}),[T,Y]=(0,g.useState)(()=>new Set([X])),q=(0,g.useCallback)(E=>{Y(x=>x.has(E)?x:new Set(x).add(E)),Ye(E)},[]),[U,De]=(0,g.useState)("recent"),[Be,ye]=(0,g.useState)(null),Ce=(0,g.useRef)(null),R=(0,g.useRef)(null),[le,ae]=(0,g.useState)(!1),[_e,se]=(0,g.useState)(null),[Ne,kt]=(0,g.useState)(!1),[k,W]=(0,g.useState)(!1),[$,re]=(0,g.useState)(!1),[Ee,fe]=(0,g.useState)("group"),[Q,ze]=(0,g.useState)(!1),be=(0,g.useCallback)(()=>{ze(!1);try{localStorage.setItem("me-onboard-v2","1")}catch{}},[]),z=(0,g.useCallback)((E="group")=>{fe(E),re(!0)},[]);(0,g.useEffect)(()=>{try{if(localStorage.getItem("me-onboard-v2")||X!=="cards")return;let E=setTimeout(()=>ze(!0),900);return()=>clearTimeout(E)}catch{}},[X]);let[w,oe]=(0,g.useState)(!0),[ke,Ie]=(0,g.useState)(null),[je,m]=(0,g.useState)(0),gn=(0,g.useCallback)(()=>m(E=>E+1),[]),Ln=a?.quarantine?.pending??0,St=a?.quarantine?.deleted??0,et=(0,g.useRef)(null),[Gt,wr]=(0,g.useState)(0),[hn,Fe]=(0,g.useState)(!1),on=(0,g.useRef)(null),tt=(0,g.useRef)(null),b=(0,g.useCallback)(async(E=N,x=_,D=C,F={})=>{let O=F.append===!0;try{O?Fe(!0):(p(!0),S(""));let K=new URLSearchParams;E&&E!=="all"&&K.set("kind",E),x.trim()&&K.set("q",x.trim());let ie=F.tag!==void 0?F.tag:J;ie&&K.set("tag",ie),K.set("status",D||"all"),K.set("limit",String(ah)),K.set("offset",String(F.offset||0)),U==="title"&&K.set("sort","title");let xe=await(await fetch(`${ee}/cards?${K.toString()}`)).json();if(xe.ok){let Qe=xe.cards||[];s(Ue=>O?[...Ue,...Qe.filter(Se=>!Ue.some(Te=>Te.path===Se.path))]:Qe),wr(Number(xe.total)||Qe.length)}}catch(K){S(String(K&&K.message?K.message:K))}finally{O?Fe(!1):p(!1)}},[N,_,C,U,J]),V=(0,g.useCallback)(async()=>{try{let[E,x]=await Promise.all([fetch(`${ee}/overview`).then(D=>D.json()),fetch(`${ee}/capture-log`).then(D=>D.json()).catch(()=>null)]);E.ok&&i(E),h(x&&x.ok&&x.available&&x.health||null)}catch(E){S(String(E&&E.message?E.message:E))}finally{p(!1)}},[]),Z=async E=>{try{let D=await(await fetch(`${ee}/delete?path=${encodeURIComponent(E)}`)).json();D.ok?(ye({ok:!0,msg:e("deleted")}),await V(),gn()):ye({ok:!1,msg:D.error||e("deleteFail")})}catch{ye({ok:!1,msg:e("deleteFail")})}Ce.current&&clearTimeout(Ce.current),Ce.current=setTimeout(()=>ye(null),2e3)};(0,g.useEffect)(()=>(V(),()=>{et.current&&clearTimeout(et.current)}),[V]);let ce=E=>{f(E),et.current&&clearTimeout(et.current),et.current=setTimeout(()=>b(N,E,C),280)},ue=E=>{M(E),b(E,_,C)},te=E=>{let x=String(E||"");Ve(x),se(null),b(N,_,C,{tag:x})};(0,g.useEffect)(()=>{X==="cards"&&b(N,_,C)},[C,X,je,U,J]),(0,g.useEffect)(()=>{if(X!=="cards"||d||hn||l.length>=Gt)return;let E=on.current,x=tt.current;if(!E)return;let D=new IntersectionObserver(F=>{F[0]&&F[0].isIntersecting&&b(N,_,C,{append:!0,offset:l.length})},{root:x||null,rootMargin:"400px"});return D.observe(E),()=>D.disconnect()},[X,l,Gt,d,hn,N,_,C,b]);let pe=async E=>{try{let x=E.status;if(!x&&E.path)try{let ie=((await fetch(`${ee}/cards?status=all&q=${encodeURIComponent(E.title||"")}&limit=50`).then(me=>me.json())).cards||[]).find(me=>me.path===E.path);ie&&(x=ie.status)}catch{}let D=x&&x!=="approved"?`&status=${encodeURIComponent(x)}`:"",F=await fetch(`${ee}/card?path=${encodeURIComponent(E.path||E.id)}${D}`),O=await F.json();O.ok?se({path:O.path,title:E.title,text:O.text}):S(O.error||`HTTP ${F.status}`)}catch(x){S(String(x&&x.message?x.message:x))}},nt=async(E,x)=>{ae(!0);try{let D=await fetch(`${ee}/export`);if(!D.ok)throw new Error("HTTP "+D.status);let F=await D.json();if(!F.ok)throw new Error(F.error||"export failed");let O,K,ie;if(E==="json"){K="memory-vault.json",ie="application/json";let Qe=F.cards.map(Ue=>({path:Ue.path,title:Ue.title,kind:Ue.kind,status:Ue.status,store:Ue.store,text:Ue.text}));O=JSON.stringify({format:F.format||"memory-eternal-vault",formatVersion:F.formatVersion||1,exportedAt:F.exportedAt||new Date().toISOString(),count:Qe.length,cards:Qe},null,2)}else K="memory-vault.md",ie="text/markdown;charset=utf-8",O=F.cards.map(Qe=>Qe.text.trim()).filter(Boolean).join(`
|
|
707
707
|
|
|
708
708
|
---
|
|
709
709
|
|