dsh-meili-search 0.2.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/AGENTS.md ADDED
@@ -0,0 +1,90 @@
1
+ # AGENTS.md — 给其他 Agent 的维护手册
2
+
3
+ 本文件面向接手修改 dsh-meili-search 的 AI Agent。所有信息自包含,无需原对话上下文即可照做。
4
+
5
+ ## 0. 这是什么
6
+
7
+ DSH(DeepSeek Harness)web 插件的会话**内容搜索**增强:
8
+
9
+ - 服务端插件(`index.js` + `core.mjs` + `indexer.mjs`):把会话 zstd 日志索引进 Meilisearch(增量同步),并提供同源 `/dsh-meili/v1/*` API。
10
+ - 客户端插件(`client.js`):在浏览器里 hook 原版侧栏搜索的内容检索、接管内容命中行的点击(跳转到消息 + 高亮)、并在 Settings→General 渲染配置卡。
11
+ - 索引/状态/设置文件(默认,跟随 `DSH_HOME=/root/.dsh`):
12
+ - 会话源: `/root/.dsh/sessions/**/session.jsonl.zstd`(每行 `{type,seq,time,data,surfaceOp}`,只索引 `user/message` 与 `assistant/message`)
13
+ - 游标状态: `/root/.dsh/meili-index-state.json`(schema v2)
14
+ - 设置: `/root/.dsh/dsh-meili-settings.json`
15
+ - Meili: `http://192.168.43.2:7700`,索引 `dsh_sessions`(无 master key)
16
+
17
+ ## 1. 文件职责与改动指引
18
+
19
+ ### core.mjs(无副作用核心)
20
+ - `scanSession(file, root, {chunk, fromSeq, chunkStart})` — 一次扫文件产出分块文档与游标量。**文档字段**: `{id: sid-chunk, sessionId, workspace, seq, mseq, role, text}`。
21
+ - `mseq` = 该消息行顶层 `o.seq`(跳转定位用,勿丢)。
22
+ - 分块按码元且不切断代理对;text 经 `sanitizeText`(孤立代理项→U+FFFD,Meili 会拒收非法码元)。
23
+ - `syncOnce(cfg, {dry, log})` — 同步一轮。规则:
24
+ - 未变(mtime+size)→ 跳过;
25
+ - **增长且 v2 游标** → `appendSession`: 只解析 `seq>rec.lastSeq` 尾部,新分块 `id` 从 `rec.lastChunkSeq+1` 续号;
26
+ - 其它(首次/旧版/文件缩小=compaction 重写)→ `replaceSession` 整会话重灌(先删 sessionId 再全量 add);
27
+ - gone 文件按 sessionId 删;dry-run 不回写状态。
28
+ - 改动后注意: **新增/改名文档字段需全量重灌**(删状态文件 + `node indexer.mjs --once`);chunk 大小或 id 规则变化同理。
29
+
30
+ ### indexer.mjs(CLI)
31
+ - `node indexer.mjs --once|--watch|--dry-run`;env: `MEILI_URL / MEILI_MASTER_KEY / MEILI_INDEX / SESSIONS_ROOT / MEILI_STATE / MEILI_INTERVAL_MS`(watch 默认 300000)。
32
+ - 常驻定时可选: 服务端插件 `autoSyncMs` 已覆盖,一般无需外部 --watch。
33
+
34
+ ### index.js(服务端插件)
35
+ - 路由前缀 `/dsh-meili/v1`(同源校验): health / status / settings(GET/PUT) / reindex(POST) / search(GET)。
36
+ - 设置持久化在**文件**(不用 ctx.settings,避免第三方 import 依赖):读 `readSettingsFile()`、写 `writeSettingsFile()`,PUT 后 `schedule()` 热重排定时器。
37
+ - 生效优先级: 设置文件 > cordis config > 环境变量 > 内置默认(`ENV`)。
38
+ - 组合里的插件 **id 是 `meili-search`**(`cordis.patch.yml` 的 insert),不是 `dsh-meili-search`——profile 层 patch 要用前者。
39
+ - **服务端改动必须重启 DSH 才生效**(ESM 模块缓存);用 `restart_harness` 工具(见 §4)。
40
+
41
+ ### client.js(浏览器客户端,三块独立功能)
42
+ 1. **搜索 hook**: `apply` 里补丁共享服务 `ctx.sessions.search`(rootCtx 提供,`inject: ["sessions"]`),内容检索改请求 `/dsh-meili/v1/search`;失败回退原实现。标题匹配是 workspace 本地逻辑,不碰。
43
+ 2. **点击跳转**: document 捕获阶段 click;用"我们生成的摘要文本"命中 `hitsByQuery` 映射 → `ctx.sessions.open` + 轮询 `[data-chat-anchor-key]` 按文本指纹找消息行 → scrollIntoView + 行闪烁 + CSS.highlights 分词高亮(`_matchesPosition` 是 **UTF-8 字节偏移**,需 `TextEncoder/Decoder` 换算成词)。
44
+ 3. **Settings 卡**: `ctx.slots.inject("settings.general.item", …)` 注册,读 `/settings`,轮询 `/status` 3s。
45
+
46
+ - `exports.inject` 必须是数组且含用到的服务(`["sessions","slots"]`);`exports.apply` 里 `activeCtx=ctx` 供工厂级函数使用(注意作用域: 工厂级函数拿不到 apply 的 ctx)。
47
+ - **客户端改动重启后生效**(启动载荷带 rev);无 pnpm dev:web 时无 HMR。
48
+
49
+ ## 2. 测试 SOP
50
+
51
+ ```bash
52
+ # 1) 语法
53
+ node --check core.mjs index.js indexer.mjs client.js
54
+
55
+ # 2) 增量语义合成测试(临时索引+目录, 不碰真实数据)
56
+ # 建 /tmp/synctest/--demo--/session-AAAA.../session.jsonl.zstd(2 条消息) →
57
+ # MEILI_INDEX=dsh_sync_test SESSIONS_ROOT=/tmp/synctest MEILI_STATE=/tmp/synctest/state.json \
58
+ # node indexer.mjs --once # 期望 full
59
+ # 换更大文件(追加 2 条)再跑 # 期望 append, added=新消息分块数
60
+ # 再跑一次 # 期望 unchanged
61
+ # 收尾: curl -X DELETE <meili>/indexes/dsh_sync_test; rm -rf /tmp/synctest
62
+
63
+ # 3) API
64
+ curl -s 'http://127.0.0.1:3080/dsh-meili/v1/status'
65
+ curl -s 'http://127.0.0.1:3080/dsh-meili/v1/search?q=<词>&limit=3' # hits 应含 mseq/role/_matchesPosition
66
+
67
+ # 4) GUI 冒烟(浏览器)
68
+ # - 侧栏搜索框输入只出现在消息内容里的词 → 出摘要行(不再显示内容搜索不可用)
69
+ # - 点内容命中行 → 会话打开并滚动到目标消息(出现 Back to bottom 说明离开了底部)
70
+ # - Settings → General → 会话内容检索卡片: 改 autoSyncMs 落盘生效; 实时状态刷新
71
+ ```
72
+
73
+ ## 3. 已知坑(改代码前必读)
74
+
75
+ - **compaction 重写**: 会话文件可能被 DSH 压缩重写(变小),该轮会整会话重灌;旧消息若被裁剪会退出索引——不是 bug。
76
+ - **treeitem 嵌套**: 侧栏 treeitem 可能叠多个会话行;点击判定用摘要文本包含 + `closest('[role=treeitem]')`,嵌套行存在误判(未根治,见 README 局限 3)。
77
+ - **命中未加载**: 超大会话里过深的命中靠自动点 "Load earlier"(≤6 次)尽力定位,可能失败退化为普通打开。
78
+ - **`_matchesPosition` 是字节偏移**: 中文 1 字=3 字节;JS 侧必须用 TextEncoder 换算再截词,否则高亮错位。
79
+ - **config id**: patch/组合 id 是 `meili-search`;profile patch 写错 id 会静默不生效(autoSyncMs=0)。
80
+ - **模块缓存**: server/client 代码改动都要重启才生效;重启用工具 `restart_harness`(勿在会话内直接 systemctl 重启宿主 dsh-web 以外的服务)。
81
+ - **不要在 monorepo(/codebase)里嵌套 init git**: 会变成 gitlink 子仓库,污染主仓库;独立发布用镜像目录。
82
+ - **设置文件与状态文件分离**: 一个在 `dsh-meili-settings.json`,一个在 `meili-index-state.json`;删状态文件=强制全量重灌,删设置=回落默认配置。
83
+
84
+ ## 4. 发布与重启
85
+
86
+ - 本仓库 origin: `git@github.com:zglinus-for-agent/dsh-meili-search.git`(账号 zglinus-for-agent,公开)。
87
+ - 本地镜像维护在 `/codebase/js/dsh-meili-search`(monorepo,开发与留档);发布目录 `/root/user/dsh-meili-search`(独立 git)。
88
+ - 改代码后: 提交 monorepo → 同步镜像目录 → commit+push 到 GitHub(分支 `main`)。
89
+ - **npm 发布走 trusted publishing(OIDC,tokenless)**: 仓库内 `.github/workflows/publish.yml`;npmjs.com 账号绑定本仓库为 trusted publisher 后,手动运行该工作流或推送 `v*` tag 即发布(带 provenance)。发布前先 `npm pack --dry-run` 检查清单;`package.json` 的 `files` 白名单与 `publishConfig` 已就位。
90
+ - 重启 DSH 使插件改动生效: 调用 `restart_harness` 工具(会延迟重启并返回日志路径),重启后 `GET /dsh-meili/v1/status` 确认。