memory-eternal 0.27.0 → 0.28.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 +1 -1
- package/README.zh-CN.md +1 -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/api.js +1 -1
- package/lib/client.js +21 -21
- package/lib/news-follows.js +176 -19
- package/orca-plugin/orca-plugin.json +1 -1
- package/package.json +2 -2
- package/web/app.js +25 -25
- package/cursor-extension/icon.png +0 -0
- package/orca-plugin/assets/icon.png +0 -0
- package/web/icon.png +0 -0
package/lib/news-follows.js
CHANGED
|
@@ -27,7 +27,11 @@ const UA = 'memory-eternal/news-follows (+https://github.com/EternalNight996/mem
|
|
|
27
27
|
export const FOLLOW_DOMAINS = [
|
|
28
28
|
{ id: 'news', label: 'AI 资讯', hint: '现有 9 个白名单媒体(量子位 / 雷峰 / 钛媒体 / InfoQ / TC / Verge / MITTR / DeepMind / HN)' },
|
|
29
29
|
{ id: 'dsh', label: 'DSH 插件', hint: '官方插件清单(约 9 千个仓库,24 小时刷新;断网时回退本机快照)' },
|
|
30
|
-
|
|
30
|
+
// 「热门库」= 新热(近 30 天创建、按 star)+ 老牌热门(近 14 天有推送、star>500)两路合并。
|
|
31
|
+
// 原来只看「近 30 天创建」,于是 transformers / langchain 这类常青库永远进不来。
|
|
32
|
+
{ id: 'github', label: 'GitHub 热库', hint: '新热仓库 + 近两周活跃的高星仓库(按 star 排序)' },
|
|
33
|
+
{ id: 'skills', label: 'Agent 技能', hint: 'GitHub 上的 agent skills 仓库(topic:claude-skills / agent-skills,按 star)' },
|
|
34
|
+
{ id: 'plugins', label: 'Agent 插件', hint: 'Claude / MCP 生态插件仓库(topic:claude-plugins / mcp-server,按 star)' },
|
|
31
35
|
{ id: 'hn', label: 'Hacker News', hint: 'HN 全站全文检索' },
|
|
32
36
|
{ id: 'arxiv', label: 'arXiv', hint: '论文检索(无需 API Key)' },
|
|
33
37
|
]
|
|
@@ -37,7 +41,10 @@ const DOMAIN_IDS = new Set(FOLLOW_DOMAINS.map((d) => d.id))
|
|
|
37
41
|
const DOMAIN_HINTS = [
|
|
38
42
|
// 放在 github 之前:「dsh 插件」不该被 github 的「仓库」类词抢走
|
|
39
43
|
// 注意:中文不能用 \b(JS 里 CJK 属 non-word,边界判断不可靠),直接匹配即可
|
|
40
|
-
{ id: 'dsh', re: /(dsh|deepseek\s*harness
|
|
44
|
+
{ id: 'dsh', re: /(dsh|deepseek\s*harness|插件市场)/i },
|
|
45
|
+
// 技能/插件:用户说「agent 技能」「claude 插件」「mcp 服务」时,应该是生态域而不是 GitHub 泛搜
|
|
46
|
+
{ id: 'skills', re: /(技能|skills?|skill\.md|subagent(s)?)/i },
|
|
47
|
+
{ id: 'plugins', re: /(插件|plugins?|mcp\s*(server|服务)s?)/i },
|
|
41
48
|
{ id: 'github', re: /(github|仓库|开源项目|trending|热门项目|热库|repo(sitory)?)/i },
|
|
42
49
|
{ id: 'arxiv', re: /(arxiv|论文|paper|文献|预印本)/i },
|
|
43
50
|
{ id: 'hn', re: /(hacker\s*news|\bhn\b)/i },
|
|
@@ -45,14 +52,16 @@ const DOMAIN_HINTS = [
|
|
|
45
52
|
|
|
46
53
|
/** 清洗用的 /g 正则(注意:带 g 的 lastIndex 会游走,只能用于 replace,不能拿来做域判断)。 */
|
|
47
54
|
const CLEAN_DOMAIN_RES = [
|
|
48
|
-
/(dsh|deepseek\s*harness
|
|
55
|
+
/(dsh|deepseek\s*harness|插件市场)/gi,
|
|
56
|
+
/(技能|skills?|skill\.md|subagent(s)?)/gi,
|
|
57
|
+
/(插件|plugins?|mcp\s*(server|服务)s?)/gi,
|
|
49
58
|
/(github|仓库|开源项目|trending|热门项目|热库|repositor(y|ies)|repo)/gi,
|
|
50
59
|
/(arxiv|论文|paper|文献|预印本)/gi,
|
|
51
60
|
/(hacker\s*news|\bhn\b)/gi,
|
|
52
61
|
]
|
|
53
62
|
|
|
54
63
|
/** 指令性噪音词:从主题里剥掉,剩下的才是真正要搜的东西。 */
|
|
55
|
-
const NOISE_RE = /(
|
|
64
|
+
const NOISE_RE = /(帮我|请|麻烦|推送|订阅|关注|跟踪|最新|热门|热库|最热|每天|每日|定时|一下|一些|几个|有什么|有啥|给我|看看|多显示|显示|最近|库|关于|相关|新闻|消息|资讯|动态|更新|上的|里的|方面|讨论)/g
|
|
56
65
|
// 只剥句尾虚词/单字(「新」只在结尾时剥:新能源这类词不会被误伤)
|
|
57
66
|
const STOP_TAIL_RE = /[的了呢吧吗新]$/g
|
|
58
67
|
|
|
@@ -114,6 +123,12 @@ export function parseFollowIntent(text) {
|
|
|
114
123
|
const notes = []
|
|
115
124
|
const domains = []
|
|
116
125
|
for (const hint of DOMAIN_HINTS) if (hint.re.test(raw)) domains.push(hint.id)
|
|
126
|
+
// 「dsh 插件」归 DSH,不再额外拉一遍 Agent 插件域(DSH 清单本身就是插件);
|
|
127
|
+
// 只有明确说到 claude / codex / agent / mcp / 技能时,才认为用户要的是更广的生态。
|
|
128
|
+
if (domains.includes('dsh') && !/(claude|codex|agent|mcp|技能|skills?)/i.test(raw)) {
|
|
129
|
+
const i = domains.indexOf('plugins')
|
|
130
|
+
if (i >= 0) domains.splice(i, 1)
|
|
131
|
+
}
|
|
117
132
|
if (!domains.length) domains.push('news')
|
|
118
133
|
const query = cleanFollowQuery(raw)
|
|
119
134
|
if (!query && domains.length === 1 && domains[0] === 'news') {
|
|
@@ -175,18 +190,65 @@ function shape(extra) {
|
|
|
175
190
|
// ── 域适配器 ─────────────────────────────────────────────────────────────
|
|
176
191
|
// 每个适配器的出站地址都是常量模板;用户输入只进 query 参数。
|
|
177
192
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
const
|
|
181
|
-
const url = 'https://api.github.com/search/repositories?sort=stars&order=desc&per_page=' + MAX_ITEMS_PER_FOLLOW +
|
|
193
|
+
/** GitHub 仓库检索(常量模板;无 token 时每分钟 10 次,调用方必须容错)。 */
|
|
194
|
+
async function githubSearch(q, perPage = 20) {
|
|
195
|
+
const url = 'https://api.github.com/search/repositories?sort=stars&order=desc&per_page=' + perPage +
|
|
182
196
|
'&q=' + encodeURIComponent(q)
|
|
183
197
|
const data = JSON.parse(await fetchText(url, 'application/vnd.github+json'))
|
|
184
|
-
|
|
185
|
-
|
|
198
|
+
return Array.isArray(data.items) ? data.items : []
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* GitHub Trending 页面(daily):不消耗 API 配额,且比「按 star 排」更贴近「热门」。
|
|
203
|
+
* 结果在进程内缓存 10 分钟,多个关注/多个域共用一次抓取。
|
|
204
|
+
*/
|
|
205
|
+
let trendingCache = { at: 0, items: [] }
|
|
206
|
+
|
|
207
|
+
export function parseTrending(html) {
|
|
208
|
+
const out = []
|
|
209
|
+
const chunks = String(html).split('<h2 class="h3 lh-condensed">').slice(1)
|
|
210
|
+
for (const chunk of chunks) {
|
|
211
|
+
const repo = /href="\/([A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+)"/.exec(chunk)
|
|
212
|
+
if (!repo) continue
|
|
213
|
+
const desc = /<p class="col-9[^"]*"[^>]*>([\s\S]*?)<\/p>/.exec(chunk)
|
|
214
|
+
const lang = /itemprop="programmingLanguage">([^<]+)</.exec(chunk)
|
|
215
|
+
const today = /([\d,]+)\s+stars today/.exec(chunk)
|
|
216
|
+
out.push({
|
|
217
|
+
repo: repo[1],
|
|
218
|
+
url: 'https://github.com/' + repo[1],
|
|
219
|
+
desc: desc ? desc[1].replace(/<[^>]+>/g, '').trim() : '',
|
|
220
|
+
lang: lang ? lang[1].trim() : '',
|
|
221
|
+
// 榜单页只给「今日新增 star」,没有总数 —— 就拿它当热度,比解析一个不存在的字段诚实
|
|
222
|
+
today: today ? Number(today[1].replace(/,/g, '')) || 0 : 0,
|
|
223
|
+
})
|
|
224
|
+
}
|
|
225
|
+
return out
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
async function trendingRepos() {
|
|
229
|
+
if (nowMs() - trendingCache.at < 10 * 60 * 1000 && trendingCache.items.length) return trendingCache.items
|
|
230
|
+
const html = await fetchText('https://github.com/trending?since=daily')
|
|
231
|
+
const items = parseTrending(html)
|
|
232
|
+
if (items.length) trendingCache = { at: nowMs(), items }
|
|
233
|
+
return items
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
function dedupeRepos(list) {
|
|
237
|
+
const seen = new Map()
|
|
238
|
+
for (const repo of list) {
|
|
239
|
+
if (!repo || !repo.id) continue
|
|
240
|
+
if (!seen.has(repo.id)) seen.set(repo.id, repo)
|
|
241
|
+
}
|
|
242
|
+
return [...seen.values()]
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
function repoToItem(repo, follow) {
|
|
246
|
+
const desc = String(repo.description || '')
|
|
247
|
+
return shape({
|
|
186
248
|
id: 'gh-' + repo.id,
|
|
187
|
-
title: repo.full_name + (
|
|
249
|
+
title: repo.full_name + (desc ? ' — ' + desc : ''),
|
|
188
250
|
url: repo.html_url,
|
|
189
|
-
summary: [
|
|
251
|
+
summary: [desc, repo.language, (repo.topics || []).slice(0, 4).join(' / ')].filter(Boolean).join(' · '),
|
|
190
252
|
source: 'GitHub',
|
|
191
253
|
sourceId: 'github',
|
|
192
254
|
sourceWeight: 4,
|
|
@@ -194,10 +256,75 @@ async function fetchGithub({ query, follow }) {
|
|
|
194
256
|
points: Number(repo.stargazers_count) || 0,
|
|
195
257
|
comments: Number(repo.forks_count) || 0,
|
|
196
258
|
type: 'opensource',
|
|
197
|
-
lang: 'en',
|
|
259
|
+
lang: /[\u4e00-\u9fff]/.test(desc) ? 'zh' : 'en',
|
|
198
260
|
followId: follow.id,
|
|
199
261
|
followLabel: follow.label,
|
|
200
|
-
})
|
|
262
|
+
})
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
function trendingItem(t, follow) {
|
|
266
|
+
return shape({
|
|
267
|
+
id: 'gh-trend-' + t.repo,
|
|
268
|
+
title: t.repo + (t.desc ? ' — ' + t.desc : ''),
|
|
269
|
+
url: t.url,
|
|
270
|
+
summary: [t.desc, t.lang, t.today ? '+' + t.today + ' stars today' : ''].filter(Boolean).join(' · '),
|
|
271
|
+
source: 'GitHub Trending',
|
|
272
|
+
sourceId: 'github',
|
|
273
|
+
sourceWeight: 5,
|
|
274
|
+
publishedAt: nowMs(),
|
|
275
|
+
points: t.today,
|
|
276
|
+
comments: t.today,
|
|
277
|
+
type: 'opensource',
|
|
278
|
+
lang: /[\u4e00-\u9fff]/.test(t.desc) ? 'zh' : 'en',
|
|
279
|
+
followId: follow.id,
|
|
280
|
+
followLabel: follow.label,
|
|
281
|
+
})
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
function dedupeItems(items) {
|
|
285
|
+
const seen = new Map()
|
|
286
|
+
for (const it of items) {
|
|
287
|
+
if (!it || !it.url) continue
|
|
288
|
+
const prev = seen.get(it.url)
|
|
289
|
+
if (!prev || (it.points || 0) > (prev.points || 0)) seen.set(it.url, it)
|
|
290
|
+
}
|
|
291
|
+
return [...seen.values()].sort((a, b) => (b.points || 0) - (a.points || 0))
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
/**
|
|
295
|
+
* GitHub 热库 = Trending 榜单(真·今日热门)。
|
|
296
|
+
* 有关键词时再补一次定向搜索;没有关键词就只吃榜单,避免浪费 API 配额。
|
|
297
|
+
*/
|
|
298
|
+
async function fetchGithub({ query, follow }) {
|
|
299
|
+
const trend = await trendingRepos().catch(() => [])
|
|
300
|
+
let items = trend.slice(0, 25).map((t) => trendingItem(t, follow))
|
|
301
|
+
if (query) {
|
|
302
|
+
const activeSince = new Date(nowMs() - 14 * 86400000).toISOString().slice(0, 10)
|
|
303
|
+
const found = await githubSearch(`${query} in:name,description,readme pushed:>${activeSince}`, 20).catch(() => [])
|
|
304
|
+
items = items.concat(found.map((r) => repoToItem(r, follow)))
|
|
305
|
+
}
|
|
306
|
+
return dedupeItems(items).slice(0, MAX_ITEMS_PER_FOLLOW)
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
/** Agent 技能:今日榜单里像 skill 的 + topic:claude-skills 的搜索结果(各按 star)。 */
|
|
310
|
+
async function fetchSkills({ query, follow }) {
|
|
311
|
+
const trend = await trendingRepos().catch(() => [])
|
|
312
|
+
const local = trend.filter((t) => /skill/i.test(t.repo + ' ' + t.desc)).map((t) => trendingItem(t, follow))
|
|
313
|
+
const base = query ? `${query} in:name,description,readme ` : ''
|
|
314
|
+
const found = await githubSearch(`${base}topic:claude-skills stars:>50`, 20).catch(() => [])
|
|
315
|
+
// 只认「名字或描述里真的有 skill」的仓库:单看 topic 会被 SEO 蹭词的高星仓库带偏
|
|
316
|
+
const search = found.filter((r) => /skill/i.test(String(r.name || '') + ' ' + String(r.description || '')))
|
|
317
|
+
return dedupeItems([...local, ...search.map((r) => repoToItem(r, follow))]).slice(0, MAX_ITEMS_PER_FOLLOW)
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
/** Agent 插件:今日榜单里像插件/MCP 的 + topic:claude-plugins 的搜索结果。 */
|
|
321
|
+
async function fetchPlugins({ query, follow }) {
|
|
322
|
+
const trend = await trendingRepos().catch(() => [])
|
|
323
|
+
const local = trend.filter((t) => /plugin|mcp/i.test(t.repo + ' ' + t.desc)).map((t) => trendingItem(t, follow))
|
|
324
|
+
const base = query ? `${query} in:name,description,readme ` : ''
|
|
325
|
+
const found = await githubSearch(`${base}topic:claude-plugins stars:>20`, 20).catch(() => [])
|
|
326
|
+
const search = found.filter((r) => /plugin|mcp/i.test(String(r.name || '') + ' ' + String(r.description || '')))
|
|
327
|
+
return dedupeItems([...local, ...search.map((r) => repoToItem(r, follow))]).slice(0, MAX_ITEMS_PER_FOLLOW)
|
|
201
328
|
}
|
|
202
329
|
|
|
203
330
|
async function fetchHn({ query, follow }) {
|
|
@@ -410,7 +537,15 @@ async function fetchDsh({ query, follow, root }) {
|
|
|
410
537
|
})
|
|
411
538
|
}
|
|
412
539
|
|
|
413
|
-
const ADAPTERS = {
|
|
540
|
+
const ADAPTERS = {
|
|
541
|
+
github: fetchGithub,
|
|
542
|
+
skills: fetchSkills,
|
|
543
|
+
plugins: fetchPlugins,
|
|
544
|
+
hn: fetchHn,
|
|
545
|
+
arxiv: fetchArxiv,
|
|
546
|
+
news: fetchNewsDomain,
|
|
547
|
+
dsh: fetchDsh,
|
|
548
|
+
}
|
|
414
549
|
|
|
415
550
|
/**
|
|
416
551
|
* 执行一次关注抓取:按域并发,逐域收集错误(不因一个域失败就整体失败)。
|
|
@@ -472,21 +607,43 @@ export async function listFollows(root) {
|
|
|
472
607
|
}
|
|
473
608
|
|
|
474
609
|
export async function saveFollow(root, input) {
|
|
475
|
-
const
|
|
610
|
+
const data = await readFileJson(followsPath(root), { version: 1, follows: [] })
|
|
611
|
+
const follows = Array.isArray(data.follows) ? data.follows.map(normalizeFollow) : []
|
|
476
612
|
if (follows.length >= MAX_FOLLOWS) throw new Error('关注数量已达上限(' + MAX_FOLLOWS + ' 个)')
|
|
477
613
|
const f = normalizeFollow(input)
|
|
478
614
|
// 同一个 query + 同一组域 = 重复关注,直接返回已有的,避免堆叠。
|
|
479
615
|
const dup = follows.find((x) => x.query === f.query && x.domains.join(',') === f.domains.join(',') && (x.sourceIds || []).join(',') === f.sourceIds.join(','))
|
|
480
616
|
if (dup) return { ok: true, follow: dup, duplicate: true, follows }
|
|
481
617
|
follows.push(f)
|
|
482
|
-
|
|
618
|
+
// 保留 defaultSeeded 等元数据:丢掉它会让「生态速览」被删掉后又重新播种
|
|
619
|
+
await writeFileJson(followsPath(root), { ...data, version: 1, follows })
|
|
483
620
|
return { ok: true, follow: f, duplicate: false, follows }
|
|
484
621
|
}
|
|
485
622
|
|
|
623
|
+
/**
|
|
624
|
+
* 首次打开新闻页时自动建一个「生态速览」关注(GitHub 热库 + Agent 技能 + Agent 插件)。
|
|
625
|
+
*
|
|
626
|
+
* 为什么用 defaultSeeded 标记而不是「列表为空就建」:用户删掉它之后不该反复被塞回来。
|
|
627
|
+
* 只在第一次真正初始化时建一次。
|
|
628
|
+
*/
|
|
629
|
+
export async function ensureDefaultFollow(root) {
|
|
630
|
+
const data = await readFileJson(followsPath(root), { version: 1, follows: [] })
|
|
631
|
+
const follows = Array.isArray(data.follows) ? data.follows.map(normalizeFollow) : []
|
|
632
|
+
if (data.defaultSeeded) return follows
|
|
633
|
+
if (follows.length) {
|
|
634
|
+
await writeFileJson(followsPath(root), { ...data, follows, defaultSeeded: true })
|
|
635
|
+
return follows
|
|
636
|
+
}
|
|
637
|
+
const seeded = normalizeFollow({ label: '生态速览', query: '', domains: ['github', 'skills', 'plugins'] })
|
|
638
|
+
await writeFileJson(followsPath(root), { version: 1, follows: [seeded], defaultSeeded: true })
|
|
639
|
+
return [seeded]
|
|
640
|
+
}
|
|
641
|
+
|
|
486
642
|
export async function removeFollow(root, id) {
|
|
487
|
-
const
|
|
643
|
+
const data = await readFileJson(followsPath(root), { version: 1, follows: [] })
|
|
644
|
+
const follows = Array.isArray(data.follows) ? data.follows.map(normalizeFollow) : []
|
|
488
645
|
const next = follows.filter((f) => f.id !== id)
|
|
489
|
-
await writeFileJson(followsPath(root), { version: 1, follows: next })
|
|
646
|
+
await writeFileJson(followsPath(root), { ...data, version: 1, follows: next })
|
|
490
647
|
return { ok: true, removed: follows.length - next.length, follows: next }
|
|
491
648
|
}
|
|
492
649
|
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"id": "memory-eternal",
|
|
4
4
|
"publisher": "eternalnight996",
|
|
5
5
|
"name": "memory-eternal",
|
|
6
|
-
"version": "0.
|
|
6
|
+
"version": "0.28.0",
|
|
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.
|
|
3
|
+
"version": "0.28.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",
|
|
@@ -70,7 +70,7 @@
|
|
|
70
70
|
"release": "node scripts/release.mjs",
|
|
71
71
|
"release:dry": "node scripts/release.mjs --dry-run",
|
|
72
72
|
"panel-check": "node scripts/panel-check.mjs",
|
|
73
|
-
"test": "node build.mjs && node tests/vault.test.mjs && node tests/capture.test.mjs && node tests/capture-route.test.mjs && node tests/stall.test.mjs && node tests/vault-path.test.mjs && node tests/vault-resolve.test.mjs && node tests/minhash.test.mjs && node tests/settings-save.test.mjs && node tests/config-coverage.test.mjs && node tests/config-sync.test.mjs && node tests/config-merge.test.mjs && node tests/sse.test.mjs && node tests/api.test.mjs && node tests/import-roundtrip.test.mjs && node tests/graph-perf.test.mjs && node tests/graph-force.test.mjs && node tests/settings-compat.test.mjs && node tests/node-bin.test.mjs && node tests/watchdog-lock.test.mjs && node tests/markdown.test.mjs && node tests/feedback.test.mjs && node tests/graph-lod.test.mjs && node tests/graph-warmup.test.mjs && node tests/loading-hints.test.mjs && node tests/web-entry.test.mjs && node tests/tui-scroll.test.mjs && node tests/tui-audit-scene.test.mjs && node tests/tui-plugin-boot.test.mjs && node tests/capture-session-id.test.mjs && node tests/ssr.test.mjs && node tests/window-controls.test.mjs && node tests/web-assets.test.mjs && node tests/manifests.test.mjs && node tests/two-store.test.mjs && node tests/noise-gate.test.mjs && node tests/e2e-recall.test.mjs && node tests/issue-fixes.test.mjs && node tests/issue-fixes-2.test.mjs && node tests/issue-fixes-3.test.mjs && node tests/watchdog-orphans.test.mjs && node tests/watchdog-restart.test.mjs && node tests/readme-i18n.test.mjs && node tests/issue-fixes-4.test.mjs && node tests/host-drain.test.mjs && node tests/e2e-host-drain.mjs && node tests/issue-fixes-5.test.mjs && node tests/issue-fixes-6.test.mjs && node tests/capture-json-repair.test.mjs && node tests/csrf-origin.test.mjs && node tests/watchdog-orphan-web.test.mjs && node tests/config-validate.test.mjs && node tests/issue-fixes-8.test.mjs && node tests/issue-fixes-9.test.mjs && node tests/release-verify.test.mjs && node tests/refine.test.mjs && node tests/db-restore.test.mjs && node tests/align.test.mjs && node tests/tags-normalize.test.mjs && node tests/resident-state.test.mjs && node tests/llm-route.test.mjs && node tests/host-managed.test.mjs && node tests/replace-claim.test.mjs && node tests/version-check-self.test.mjs && node tests/setup-codex.test.mjs && node tests/listener-netstat.test.mjs && node tests/issue-46-overlay.test.mjs && node tests/graph-initial-zoom.test.mjs && node tests/card-head-actions.test.mjs && node tests/card-reader-ux.test.mjs && node tests/tag-filter.test.mjs && node tests/ui-overflow.test.mjs && node tests/version-card-state.test.mjs && node tests/llm-routes-proxy.test.mjs && node tests/model-strikes.test.mjs && node tests/support-bundle.test.mjs && node tests/health-panel.test.mjs && node tests/issue-48-timeout.test.mjs && node tests/setup-node-path.test.mjs && node tests/capture-kind.test.mjs && node tests/core-memory.test.mjs && node tests/skill-cards.test.mjs && node tests/audit-search.test.mjs && node tests/news.test.mjs && node tests/news-feed.test.mjs && node tests/ledger.test.mjs && node tests/issue-55-timezone.test.mjs && node tests/recycle-center.test.mjs && node tests/news-follows.test.mjs && node tests/news-digest.test.mjs && node tests/search-index.test.mjs && node tests/star-prompt.test.mjs && node tests/
|
|
73
|
+
"test": "node build.mjs && node tests/vault.test.mjs && node tests/capture.test.mjs && node tests/capture-route.test.mjs && node tests/stall.test.mjs && node tests/vault-path.test.mjs && node tests/vault-resolve.test.mjs && node tests/minhash.test.mjs && node tests/settings-save.test.mjs && node tests/config-coverage.test.mjs && node tests/config-sync.test.mjs && node tests/config-merge.test.mjs && node tests/sse.test.mjs && node tests/api.test.mjs && node tests/import-roundtrip.test.mjs && node tests/graph-perf.test.mjs && node tests/graph-force.test.mjs && node tests/settings-compat.test.mjs && node tests/node-bin.test.mjs && node tests/watchdog-lock.test.mjs && node tests/markdown.test.mjs && node tests/feedback.test.mjs && node tests/graph-lod.test.mjs && node tests/graph-warmup.test.mjs && node tests/loading-hints.test.mjs && node tests/web-entry.test.mjs && node tests/tui-scroll.test.mjs && node tests/tui-audit-scene.test.mjs && node tests/tui-plugin-boot.test.mjs && node tests/capture-session-id.test.mjs && node tests/ssr.test.mjs && node tests/window-controls.test.mjs && node tests/web-assets.test.mjs && node tests/manifests.test.mjs && node tests/two-store.test.mjs && node tests/noise-gate.test.mjs && node tests/e2e-recall.test.mjs && node tests/issue-fixes.test.mjs && node tests/issue-fixes-2.test.mjs && node tests/issue-fixes-3.test.mjs && node tests/watchdog-orphans.test.mjs && node tests/watchdog-restart.test.mjs && node tests/readme-i18n.test.mjs && node tests/issue-fixes-4.test.mjs && node tests/host-drain.test.mjs && node tests/e2e-host-drain.mjs && node tests/issue-fixes-5.test.mjs && node tests/issue-fixes-6.test.mjs && node tests/capture-json-repair.test.mjs && node tests/csrf-origin.test.mjs && node tests/watchdog-orphan-web.test.mjs && node tests/config-validate.test.mjs && node tests/issue-fixes-8.test.mjs && node tests/issue-fixes-9.test.mjs && node tests/release-verify.test.mjs && node tests/refine.test.mjs && node tests/db-restore.test.mjs && node tests/align.test.mjs && node tests/tags-normalize.test.mjs && node tests/resident-state.test.mjs && node tests/llm-route.test.mjs && node tests/host-managed.test.mjs && node tests/replace-claim.test.mjs && node tests/version-check-self.test.mjs && node tests/setup-codex.test.mjs && node tests/listener-netstat.test.mjs && node tests/issue-46-overlay.test.mjs && node tests/graph-initial-zoom.test.mjs && node tests/card-head-actions.test.mjs && node tests/card-reader-ux.test.mjs && node tests/tag-filter.test.mjs && node tests/ui-overflow.test.mjs && node tests/version-card-state.test.mjs && node tests/llm-routes-proxy.test.mjs && node tests/model-strikes.test.mjs && node tests/support-bundle.test.mjs && node tests/health-panel.test.mjs && node tests/issue-48-timeout.test.mjs && node tests/setup-node-path.test.mjs && node tests/capture-kind.test.mjs && node tests/core-memory.test.mjs && node tests/skill-cards.test.mjs && node tests/audit-search.test.mjs && node tests/news.test.mjs && node tests/news-feed.test.mjs && node tests/ledger.test.mjs && node tests/issue-55-timezone.test.mjs && node tests/recycle-center.test.mjs && node tests/news-follows.test.mjs && node tests/news-digest.test.mjs && node tests/search-index.test.mjs && node tests/star-prompt.test.mjs && node tests/working-memory.test.mjs && node tests/mcp-manifests.test.mjs"
|
|
74
74
|
},
|
|
75
75
|
"dependencies": {
|
|
76
76
|
"@deepseek-ai/schemastery": "^3.18.1"
|