dsh-session-index-lau 2.5.1 → 2.5.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/README.md +32 -14
- package/package.json +1 -1
- package/plugin.json +3 -3
- package/server.mjs +1 -1
- package/smoke.mjs +2 -2
- package/tools/push-doc.mjs +93 -6
- package//346/223/215/344/275/234/344/275/277/347/224/250/346/211/213/345/206/214.html +18 -9
package/README.md
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
|
|
6
6
|
| 项目 | 值 |
|
|
7
7
|
|---|---|
|
|
8
|
-
| 版本 | **v2.5.
|
|
8
|
+
| 版本 | **v2.5.2** |
|
|
9
9
|
| 运行要求 | Node **≥ 22.15**(需要 `zlib.zstdDecompressSync`) |
|
|
10
10
|
| MCP 能力 | **14 个工具** / 4 个提示词 / 8 个资源 |
|
|
11
11
|
| 第三方依赖 | **0**(只用 Node 标准库) |
|
|
@@ -270,6 +270,7 @@ node tools/push-doc.mjs --from-workbuddy --send # 全部投递到待
|
|
|
270
270
|
node tools/push-doc.mjs --from-workbuddy --no-exclude # 连临时/备份目录一起
|
|
271
271
|
node tools/push-doc.mjs --from-workbuddy --include-txt # 连 .txt 一起
|
|
272
272
|
node tools/push-doc.mjs --from-workbuddy --source PresentFiles --limit 10 --send
|
|
273
|
+
node tools/push-doc.mjs --from-workbuddy --seed # 只记状态基线,不投递任何文档
|
|
273
274
|
```
|
|
274
275
|
|
|
275
276
|
数据源是 `~/.workbuddy/artifact-index/<会话id>.json` 里 `contentType: document` 的条目
|
|
@@ -278,13 +279,22 @@ node tools/push-doc.mjs --from-workbuddy --source PresentFiles --limit 10 --send
|
|
|
278
279
|
避免同一份 README 被推十几遍。每篇标题带相对路径、备注带来源会话,落点统一可在界面改。
|
|
279
280
|
|
|
280
281
|
实测(2026-11-14):70 条文档命中 → 排除 26 条临时/依赖 → 文件已不存在 4 →
|
|
281
|
-
路径或内容重复 18 → **唯一 22 篇全部投递成功**,队列 23
|
|
282
|
-
|
|
282
|
+
路径或内容重复 18 → **唯一 22 篇全部投递成功**,队列 23 篇(含此前单推的手册)。
|
|
283
|
+
可用 `--limit`(默认 60)与 `--since` 收窄。
|
|
284
|
+
|
|
285
|
+
**判重:对齐思源桥 `autoStage` 的 `.auto-staged.json`,但丢弃后不重新获取。**
|
|
286
|
+
|
|
287
|
+
状态文件 `~/.dsh/session-index/push-doc-state.json`(`PUSH_DOC_STATE` 可改)按
|
|
288
|
+
「文档路径 + 内容 sha256」记录已推送过的文档,**判重不看队列里还在不在**:
|
|
289
|
+
|
|
290
|
+
- 在思源桥里「丢弃」某篇 → 下次自动化**不会**把它重新推送回来
|
|
291
|
+
- 队列被清空、服务重启、定时任务重跑 → 同样不会重复入队
|
|
292
|
+
- 内容真的改了 → 作为新版本投递一次,之后又不再重复
|
|
293
|
+
- `--force` 可强制绕过;`--seed` 只写状态基线、不投递(首次启用或迁移时用)
|
|
283
294
|
|
|
284
295
|
- **默认只预览**,加 `--send` 才入队;预览写到 `dist/push-doc-preview.md`
|
|
285
296
|
- HTML 会先裁掉侧栏导航、复制按钮、脚本、样式与统计卡,再用思源桥自带的
|
|
286
297
|
`/api/paste/html`(只读)转成 Markdown,不会把界面文字塞进笔记
|
|
287
|
-
- 队列里已有同名稿时**默认不重复投递**;手册更新后要再投一份需显式 `--force`
|
|
288
298
|
- 同样**只入队、不 commit**,写入仍由你在思源桥界面确认
|
|
289
299
|
|
|
290
300
|
### 插件怎么更新(DSH 插件页有没有「更新」按钮)
|
|
@@ -298,19 +308,26 @@ node tools/push-doc.mjs --from-workbuddy --source PresentFiles --limit 10 --send
|
|
|
298
308
|
| 官方按需插件 | 有显式**更新**(版本或开发链接来源不同时) |
|
|
299
309
|
| **本地路径 / `link:` / 压缩包安装的** | **不显示版本区块**,文档写明「通过卸载后重新安装升级」 |
|
|
300
310
|
|
|
301
|
-
|
|
302
|
-
|
|
311
|
+
**本机当前是 npm registry 来源**:profile 依赖 `dsh-session-index-lau: ^2.5.2`,
|
|
312
|
+
属于上表第一行,所以插件页**有「版本」区块与「更新」按钮**——以后发新版后
|
|
313
|
+
**点更新即可,不需要卸载重装**。命令行 `pnpm update <包名>` 与点按钮是同一条路径。
|
|
314
|
+
|
|
315
|
+
发布侧走 `dist/publish/` 下的标准流程(`插件发布标准流程.md`):
|
|
303
316
|
|
|
304
317
|
```bash
|
|
305
|
-
|
|
306
|
-
|
|
318
|
+
node sync-publish.mjs session-index # 生成改名后的发布副本并自检
|
|
319
|
+
cd dsh-session-index-lau && npm publish --access public
|
|
320
|
+
# 等 npm staged publishing 生效(实测约 1 分钟)
|
|
321
|
+
cd ~/.dsh/profiles/desktop && npx -y pnpm@11.28.5 update dsh-session-index-lau@<新版本>
|
|
307
322
|
```
|
|
308
323
|
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
324
|
+
注意 `pnpm-workspace.yaml` 的 `minimumReleaseAgeExclude` 要同步登记新版本,
|
|
325
|
+
否则供应链冷却期会让 pnpm 静默回退旧版。更新后**完全退出 DSH 再打开**,
|
|
326
|
+
运行中的 MCP 子进程才会换成新版。
|
|
312
327
|
|
|
313
|
-
>
|
|
328
|
+
> 只有从本地路径 / `link:` / 压缩包安装的才没有更新按钮(需卸载重装);
|
|
329
|
+
> 这类挂载因为直接指向仓库目录,`git pull` + 重启即生效,同样不用重装。
|
|
330
|
+
> 发布给别人用需要 `git push` + `npm publish`;本机开发目录则 `git pull` 即可。
|
|
314
331
|
|
|
315
332
|
---
|
|
316
333
|
|
|
@@ -341,8 +358,8 @@ node smoke.mjs # 80 项端到端自测
|
|
|
341
358
|
|
|
342
359
|
| 文件 | 内容 | 用途 |
|
|
343
360
|
|---|---|---|
|
|
344
|
-
| `dist/dsh-session-index-v2.5.
|
|
345
|
-
| `dist/session-index-skill-v2.5.
|
|
361
|
+
| `dist/dsh-session-index-v2.5.2.zip` | 全部源码 + 清单 + 文档 + 手册 + 脚本 + `MANIFEST.json` + `INSTALL.md` | 完整分发,解压后双击 `install.cmd` |
|
|
362
|
+
| `dist/session-index-skill-v2.5.2.zip` | 根 `SKILL.md` + 说明 | 直接导入 DSH「MCP 与技能」 |
|
|
346
363
|
|
|
347
364
|
包内**排除**:索引分片 `*.jsonl.gz`、`meta/cards/notes/vectors.json`、备份与临时文件、已有 zip。
|
|
348
365
|
已验证包内无任何会话数据与密钥,可安全分发。也可随时 `node pack.mjs` 重打,或调 MCP 工具 `session_index_package`。
|
|
@@ -433,6 +450,7 @@ dsh-session-index/ # 仓库根 = 插件根
|
|
|
433
450
|
|
|
434
451
|
| 版本 | 变更 |
|
|
435
452
|
|---|---|
|
|
453
|
+
| **v2.5.2** | 发布流程标准化:新增发布前/后核验门禁(`dist/publish/check-release.mjs`),把「检查并核验 → 确认版本可用 → 才推进标准流程」固化为强制步骤;标准流程文档同步新机制(批量推送、持久化判重、定时自动化)与版本统一矩阵。把源仓库已生效的持久化判重脚本同步进发布包与 DSH 安装,消除「同版本号、内容不同」的分叉。 |
|
|
436
454
|
| **v2.5.1** | `tools/push-doc.mjs` 增加 **`--from-workbuddy` 批量模式**:扫描 WorkBuddy 全部会话产出的文档,按内容哈希去重、排除临时/依赖目录、逐篇查重后一次性投递到思源桥待入库;默认只读预览,`--send` 才入队,`--no-exclude` / `--include-txt` / `--source` / `--limit` / `--since` / `--force` 可控。修掉按 `chars` 判重不稳的问题(思源桥会规整正文)。README 补「插件怎么更新」章节。 |
|
|
437
455
|
| **v2.5.0** | 新增第 14 个工具 `session_index_stage`:**会话整理文档 → 思源桥「待入库」队列**。两步确认(预览返回令牌,原样回传 `confirmWith` 才投递)、按标题+字数去重、支持主题/指定会话/三种模板/落点配置,**绝不自动写入思源笔记库**。读取状态与队列可传 `status: true`。 |
|
|
438
456
|
| **v2.4.1** | 新增 `install.mjs --doctor` **环境体检**(7 项,只读),覆盖插件页安装失败的四个真实成因:git URL 重写镜像、pnpm `fetch-timeout` 超上限、远端大 tarball、lockfile 残留 `git+ssh`。README 增加「插件页装不上」排障章节。 |
|
package/package.json
CHANGED
package/plugin.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-session-index-lau",
|
|
3
|
-
"version": "2.5.
|
|
3
|
+
"version": "2.5.2",
|
|
4
4
|
"icon": "icon.svg",
|
|
5
5
|
"displayName": {
|
|
6
6
|
"zh": "全会话索引与跨会话记忆",
|
|
@@ -84,8 +84,8 @@
|
|
|
84
84
|
},
|
|
85
85
|
"packages": {
|
|
86
86
|
"directory": "dist",
|
|
87
|
-
"plugin": "dsh-session-index-lau-v2.5.
|
|
88
|
-
"skill": "session-index-skill-v2.5.
|
|
87
|
+
"plugin": "dsh-session-index-lau-v2.5.2.zip",
|
|
88
|
+
"skill": "session-index-skill-v2.5.2.zip",
|
|
89
89
|
"generator": "lib/pack.mjs (zero dependency ZIP writer)",
|
|
90
90
|
"contents": ["MANIFEST.json", "INSTALL.md"],
|
|
91
91
|
"excludes": ["会话索引分片 *.jsonl.gz", "meta/cards/notes/vectors.json", "备份与临时文件", "已有 zip"]
|
package/server.mjs
CHANGED
|
@@ -29,7 +29,7 @@ import { pack, packAll, listPacks } from './lib/pack.mjs';
|
|
|
29
29
|
import { stageConfig, buildStageDocument, stageToken, bridgeOverview, findDuplicate, postStage, StageError } from './lib/stage.mjs';
|
|
30
30
|
|
|
31
31
|
const { sessionsRoot, indexDir, workbuddyProjectsDir } = defaultPaths();
|
|
32
|
-
const VERSION = '2.5.
|
|
32
|
+
const VERSION = '2.5.2';
|
|
33
33
|
const SUPPORTED_PROTOCOLS = new Set(['2024-11-05', '2025-03-26', '2025-06-18']);
|
|
34
34
|
const log = (...parts) => process.stderr.write(`[dsh-session-index-lau] ${parts.join(' ')}\n`);
|
|
35
35
|
|
package/smoke.mjs
CHANGED
|
@@ -128,9 +128,9 @@ try {
|
|
|
128
128
|
const init = await request('initialize', {
|
|
129
129
|
protocolVersion: '2024-11-05',
|
|
130
130
|
capabilities: {},
|
|
131
|
-
clientInfo: { name: 'smoke-test', version: '2.5.
|
|
131
|
+
clientInfo: { name: 'smoke-test', version: '2.5.2' },
|
|
132
132
|
});
|
|
133
|
-
check('initialize 握手与能力声明', init.result?.serverInfo?.version === '2.5.
|
|
133
|
+
check('initialize 握手与能力声明', init.result?.serverInfo?.version === '2.5.2' && Boolean(init.result?.capabilities?.prompts) && Boolean(init.result?.capabilities?.resources), init.result?.serverInfo?.version);
|
|
134
134
|
check('initialize 返回自描述 instructions', (init.result?.instructions || '').includes('session_index_brief'));
|
|
135
135
|
check('声明 listChanged 通知能力', init.result?.capabilities?.resources?.listChanged === true && init.result?.capabilities?.prompts?.listChanged === true);
|
|
136
136
|
notify('notifications/initialized');
|
package/tools/push-doc.mjs
CHANGED
|
@@ -6,13 +6,19 @@
|
|
|
6
6
|
* node tools/push-doc.mjs --file README.md --send 推送任意单个文档
|
|
7
7
|
* node tools/push-doc.mjs --from-workbuddy 批量:列出 WorkBuddy 会话文档(只读)
|
|
8
8
|
* node tools/push-doc.mjs --from-workbuddy --send 批量:全部投递到待入库
|
|
9
|
+
* node tools/push-doc.mjs --from-workbuddy --seed 批量:把当前文档记为「已推送」(不投递)
|
|
9
10
|
*
|
|
10
11
|
* 批量数据源:~/.workbuddy/artifact-index/<会话id>.json 的 contentType=document 条目。
|
|
11
12
|
* 默认排除临时验证目录/依赖目录/旧开发副本,按内容哈希去重,避免把同一份 README
|
|
12
13
|
* 推十几遍。用 --no-exclude 关闭排除,--source 按生成方式收窄,--limit 设上限。
|
|
13
14
|
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
15
|
+
* 判重(对齐思源桥 autoStage 的 .auto-staged.json):状态文件按「文档路径 + 内容哈希」
|
|
16
|
+
* 持久化记录已推送过的文档,**不依赖队列当前状态**。所以在思源桥里「丢弃」某篇后,
|
|
17
|
+
* 下次自动化不会把它重新推送回来;只有内容真的变了才会作为新版本再投一次。
|
|
18
|
+
* --force 可绕过状态与队列检查;--seed 用于首次启用时把历史文档补进状态基线。
|
|
19
|
+
*
|
|
20
|
+
* 可控点:默认 dry-run;--send 才入队;**永远不调用 /api/stage/commit**,
|
|
21
|
+
* 写入思源笔记库由人在界面确认。
|
|
16
22
|
*/
|
|
17
23
|
import fs from 'node:fs';
|
|
18
24
|
import path from 'node:path';
|
|
@@ -28,6 +34,7 @@ const arg = (name, fallback) => {
|
|
|
28
34
|
};
|
|
29
35
|
const has = (...names) => names.some((n) => argv.includes(n));
|
|
30
36
|
const SEND = has('--send');
|
|
37
|
+
const SEED = has('--seed');
|
|
31
38
|
const BATCH = has('--from-workbuddy');
|
|
32
39
|
const NO_EXCLUDE = has('--no-exclude');
|
|
33
40
|
const FORCE = has('--force');
|
|
@@ -41,6 +48,44 @@ const HPATH = arg('--hpath', process.env.SESSION_INDEX_STAGE_HPATH || '/会话
|
|
|
41
48
|
const NOTEBOOK = arg('--notebook', '思源桥');
|
|
42
49
|
const TITLE_OVERRIDE = arg('--title', '');
|
|
43
50
|
const OUT_DIR = path.join(ROOT, 'dist');
|
|
51
|
+
// 和思源桥 .auto-staged.json 同类的持久化状态:判重只看这里,不看队列里还在不在
|
|
52
|
+
const STATE_FILE = process.env.PUSH_DOC_STATE
|
|
53
|
+
|| path.join(process.env.SESSION_INDEX_DIR || path.join(os.homedir(), '.dsh', 'session-index'), 'push-doc-state.json');
|
|
54
|
+
|
|
55
|
+
function loadState() {
|
|
56
|
+
try {
|
|
57
|
+
const j = JSON.parse(fs.readFileSync(STATE_FILE, 'utf8'));
|
|
58
|
+
if (j && typeof j.pushed === 'object' && j.pushed) return j;
|
|
59
|
+
} catch {
|
|
60
|
+
// 首次运行或文件损坏,按空状态处理
|
|
61
|
+
}
|
|
62
|
+
return { version: 1, pushed: {} };
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function saveState(state) {
|
|
66
|
+
state.version = 1;
|
|
67
|
+
state.updatedAt = new Date().toISOString();
|
|
68
|
+
fs.mkdirSync(path.dirname(STATE_FILE), { recursive: true });
|
|
69
|
+
const tmp = `${STATE_FILE}.tmp-${process.pid}`;
|
|
70
|
+
fs.writeFileSync(tmp, JSON.stringify(state, null, 2), 'utf8');
|
|
71
|
+
// Windows 上目标被另一进程短暂占用时 rename 会 EPERM,做短退避
|
|
72
|
+
let last;
|
|
73
|
+
for (const delay of [0, 40, 120, 300]) {
|
|
74
|
+
if (delay) Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, delay);
|
|
75
|
+
try {
|
|
76
|
+
fs.renameSync(tmp, STATE_FILE);
|
|
77
|
+
return;
|
|
78
|
+
} catch (err) {
|
|
79
|
+
last = err;
|
|
80
|
+
if (!['EPERM', 'EACCES', 'EBUSY'].includes(err.code)) break;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
try { fs.unlinkSync(tmp); } catch { /* ignore */ }
|
|
84
|
+
throw last;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/** 稳定键:文档相对路径;内容哈希单独比较,内容变了才算新版本。 */
|
|
88
|
+
const stateKey = (relShort) => String(relShort).replace(/\\/g, '/').toLowerCase();
|
|
44
89
|
|
|
45
90
|
/** 默认排除:临时验证副本、依赖目录、旧开发副本、UI 源码。 */
|
|
46
91
|
const EXCLUDE = [
|
|
@@ -158,6 +203,8 @@ function collectWorkbuddyDocs() {
|
|
|
158
203
|
docs.push({
|
|
159
204
|
abs, raw, conv,
|
|
160
205
|
relShort,
|
|
206
|
+
contentHash: h,
|
|
207
|
+
stateKey: stateKey(relShort),
|
|
161
208
|
title: cleanTitle(TITLE_OVERRIDE || `${label.replace(/\.[^.]+$/, '')} · ${relShort}`),
|
|
162
209
|
origin: relShort,
|
|
163
210
|
});
|
|
@@ -166,27 +213,40 @@ function collectWorkbuddyDocs() {
|
|
|
166
213
|
return { docs, stats: { dir, rawCount, excluded, missing, dup, unique: docs.length } };
|
|
167
214
|
}
|
|
168
215
|
|
|
169
|
-
async function deliver(doc, queue) {
|
|
216
|
+
async function deliver(doc, queue, state) {
|
|
170
217
|
if (doc.problems.length) return { skipped: true, why: '含杂质:' + doc.problems.join(', ') };
|
|
171
218
|
if (doc.chars > 256 * 1024) return { skipped: true, why: '超过 256 KiB' };
|
|
172
219
|
if (!SEND) return { skipped: true, why: 'dry-run(未写入)' };
|
|
220
|
+
|
|
221
|
+
// 第一道:持久化状态。丢弃 / 清空队列 / 服务重启都不会让已推过的文档再出现。
|
|
222
|
+
const prev = state && doc.stateKey ? state.pushed[doc.stateKey] : null;
|
|
223
|
+
if (prev && prev.hash === doc.contentHash && !FORCE) {
|
|
224
|
+
return { skipped: true, why: '已推送过(丢弃也不重推)' };
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
// 第二道:队列同名(状态文件丢失时的兜底,不影响「丢弃后不重推」)
|
|
173
228
|
const same = queue.filter((it) => String(it.title).trim() === doc.title);
|
|
174
229
|
if (same.length && !FORCE) {
|
|
175
230
|
// 思源桥会规整 Markdown,队列里的 chars 与发送时未必相等,所以按标题判重更可靠
|
|
176
231
|
return { skipped: true, why: '队列中已有同名稿(--force 可再投)' };
|
|
177
232
|
}
|
|
178
|
-
if (!SEND) return { skipped: true, why: 'dry-run' };
|
|
179
233
|
const r = await post('/api/stage', {
|
|
180
234
|
title: doc.title, markdown: doc.markdown, notebook: NOTEBOOK, hpath: HPATH,
|
|
181
235
|
note: `WorkBuddy 会话产物 · ${doc.conv.slice(0, 8)} · v${VERSION} · 待人工确认`,
|
|
182
236
|
});
|
|
183
237
|
queue.push({ title: doc.title, chars: doc.chars });
|
|
238
|
+
if (state && doc.stateKey) {
|
|
239
|
+
state.pushed[doc.stateKey] = {
|
|
240
|
+
hash: doc.contentHash, title: doc.title, id: r.id || null, at: new Date().toISOString(),
|
|
241
|
+
};
|
|
242
|
+
}
|
|
184
243
|
return { staged: true, id: r.id, chars: r.chars };
|
|
185
244
|
}
|
|
186
245
|
|
|
187
246
|
// ------------------------------------------------------------------ 主流程
|
|
188
247
|
|
|
189
|
-
const
|
|
248
|
+
const state = BATCH ? loadState() : null;
|
|
249
|
+
const queue = (SEND && !SEED) ? ((await api('/api/stage')).items || []) : [];
|
|
190
250
|
let jobs;
|
|
191
251
|
|
|
192
252
|
if (BATCH) {
|
|
@@ -203,6 +263,25 @@ if (BATCH) {
|
|
|
203
263
|
console.log('\n清单:');
|
|
204
264
|
jobs.forEach((d, i) => console.log(` ${String(i + 1).padStart(2)}. [${d.conv.slice(0, 8)}] ${d.title}`));
|
|
205
265
|
fs.mkdirSync(OUT_DIR, { recursive: true });
|
|
266
|
+
|
|
267
|
+
// --seed:纯本地建立状态基线,不碰思源桥、不投递任何文档。
|
|
268
|
+
if (SEED) {
|
|
269
|
+
let added = 0;
|
|
270
|
+
let same = 0;
|
|
271
|
+
for (const j of jobs) {
|
|
272
|
+
const prev = state.pushed[j.stateKey];
|
|
273
|
+
if (prev && prev.hash === j.contentHash && !FORCE) { same++; continue; }
|
|
274
|
+
state.pushed[j.stateKey] = {
|
|
275
|
+
hash: j.contentHash, title: j.title, id: prev?.id || 'seeded', at: new Date().toISOString(),
|
|
276
|
+
};
|
|
277
|
+
added++;
|
|
278
|
+
}
|
|
279
|
+
saveState(state);
|
|
280
|
+
console.log(`\n状态基线:新增 ${added} 条 / 已存在 ${same} 条 / 共 ${Object.keys(state.pushed).length} 条`);
|
|
281
|
+
console.log(`状态文件:${STATE_FILE}`);
|
|
282
|
+
console.log('未投递任何文档,未写入思源笔记库。');
|
|
283
|
+
process.exit(0);
|
|
284
|
+
}
|
|
206
285
|
} else {
|
|
207
286
|
if (!fs.existsSync(FILE)) throw new Error(`找不到文件:${FILE}`);
|
|
208
287
|
const raw = fs.readFileSync(FILE, 'utf8');
|
|
@@ -226,9 +305,16 @@ for (const j of jobs) {
|
|
|
226
305
|
console.log(`预览:${path.join(OUT_DIR, 'push-doc-preview.md')}`);
|
|
227
306
|
console.log(`\n--- 前 12 行 ---\n${doc.markdown.split('\n').slice(0, 12).join('\n')}`);
|
|
228
307
|
}
|
|
229
|
-
results.push({
|
|
308
|
+
results.push({
|
|
309
|
+
title: doc.title,
|
|
310
|
+
...(await deliver({
|
|
311
|
+
...doc, conv: j.conv || '', stateKey: j.stateKey, contentHash: j.contentHash,
|
|
312
|
+
}, queue, state)),
|
|
313
|
+
});
|
|
230
314
|
}
|
|
231
315
|
|
|
316
|
+
if (BATCH && SEND && state && results.some((r) => r.staged)) saveState(state);
|
|
317
|
+
|
|
232
318
|
if (BATCH) {
|
|
233
319
|
const staged = results.filter((r) => r.staged);
|
|
234
320
|
const skipped = results.filter((r) => r.skipped);
|
|
@@ -238,6 +324,7 @@ if (BATCH) {
|
|
|
238
324
|
console.log(`投递成功 ${staged.length} 篇 / 跳过 ${skipped.length} 篇 / 队列现有 ${queue.length} 篇`);
|
|
239
325
|
for (const [why, n] of reasons) console.log(` ${why}: ${n}`);
|
|
240
326
|
if (staged.length) console.log(` 首篇 id=${staged[0].id} · 落点=${HPATH}`);
|
|
327
|
+
console.log(` 状态基线 ${Object.keys(state.pushed).length} 篇 · ${STATE_FILE}`);
|
|
241
328
|
if (!SEND) console.log('\n[只读预览] 未产生任何写入。确认清单后加 --send 才批量投递。');
|
|
242
329
|
else console.log('\n未调用 commit;请在思源桥「待入库」界面逐篇或批量勾选写入。');
|
|
243
330
|
} else {
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
<head>
|
|
4
4
|
<meta charset="UTF-8">
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
-
<title>dsh-session-index 操作使用手册 v2.5.
|
|
6
|
+
<title>dsh-session-index 操作使用手册 v2.5.2</title>
|
|
7
7
|
<style>
|
|
8
8
|
:root{
|
|
9
9
|
--bg:#f7f9fc; --panel:#ffffff; --panel-2:#f1f5fb; --line:#e2e8f2;
|
|
@@ -74,7 +74,7 @@
|
|
|
74
74
|
<body>
|
|
75
75
|
<div class="layout">
|
|
76
76
|
<aside>
|
|
77
|
-
<div class="brand">dsh-session-index<small>操作使用手册 · v2.5.
|
|
77
|
+
<div class="brand">dsh-session-index<small>操作使用手册 · v2.5.2</small></div>
|
|
78
78
|
<input id="q" type="search" placeholder="筛选章节 / 关键词…" autocomplete="off">
|
|
79
79
|
<nav id="nav">
|
|
80
80
|
<div class="grp">开始</div>
|
|
@@ -102,7 +102,7 @@
|
|
|
102
102
|
<h1>dsh-session-index 操作使用手册</h1>
|
|
103
103
|
<p>完全化的 MCP 插件:一条命令安装,之后自动配置、自动建索引、<b>新会话由文件事件自动识别(无需重启 DSH)</b>、索引变更主动通知客户端,并自动生成可分发插件包与技能包。零第三方依赖,源会话只读。</p>
|
|
104
104
|
<div class="badges">
|
|
105
|
-
<span class="badge">版本 <b>v2.5.
|
|
105
|
+
<span class="badge">版本 <b>v2.5.2</b></span>
|
|
106
106
|
<span class="badge">安装命令 <b>1 条</b></span>
|
|
107
107
|
<span class="badge">会话 <b>32</b>(自动增长)</span>
|
|
108
108
|
<span class="badge">工具 <b>14</b> / 提示词 <b>4</b> / 资源 <b>7+1</b></span>
|
|
@@ -237,8 +237,8 @@ node pack.mjs verify # 校验 zip 结构
|
|
|
237
237
|
session_index_package { "mode": "all" } # MCP 工具,Agent 也能触发</code></pre>
|
|
238
238
|
<table>
|
|
239
239
|
<tr><th>包</th><th>内容</th><th>用途</th></tr>
|
|
240
|
-
<tr><td><code>dsh-session-index-v2.5.
|
|
241
|
-
<tr><td><code>session-index-skill-v2.5.
|
|
240
|
+
<tr><td><code>dsh-session-index-v2.5.2.zip</code></td><td>全部源码 + plugin.json + README + 手册 + SKILL.md + 一键脚本 + MANIFEST.json + INSTALL.md</td><td>完整分发 / 迁移到另一台机器</td></tr>
|
|
241
|
+
<tr><td><code>session-index-skill-v2.5.2.zip</code></td><td>根 <code>SKILL.md</code> + README + MANIFEST(3 文件)</td><td>直接导入 DSH「MCP 与技能」→ Skills</td></tr>
|
|
242
242
|
</table>
|
|
243
243
|
<div class="warn">打包器<b>自动排除</b>所有会话索引分片、<code>meta/cards/notes/vectors.json</code>、备份、临时文件与已有 zip,并用系统解压工具验证过:包内 0 个敏感文件,可安全分发。接收方解压后运行 <code>node install.mjs</code> 即完成安装。</div>
|
|
244
244
|
</section>
|
|
@@ -406,7 +406,14 @@ session_index_refresh // 仅改规则或需即时生效时
|
|
|
406
406
|
<tr><td><code>node smoke.mjs</code> / <code>npm run check</code></td><td>80 项自测 / 语法检查</td></tr>
|
|
407
407
|
<tr><td><code>node tools/push-doc.mjs [--send]</code></td><td>把手册 / 任意 md·html 推到思源桥待入库(默认只读预览,<code>--send</code> 才入队,绝不写库)</td></tr>
|
|
408
408
|
<tr><td><code>node tools/push-doc.mjs --from-workbuddy [--send]</code></td><td><b>批量</b>:扫描 WorkBuddy 全部会话生成的文档,去排除/去重后一次性投递;<code>--no-exclude</code> 含临时目录、<code>--include-txt</code> 含 txt、<code>--source</code> 按生成方式、<code>--limit</code> 设上限</td></tr>
|
|
409
|
+
<tr><td><code>node tools/push-doc.mjs --from-workbuddy --seed</code></td><td>只写「已推送」状态基线,<b>不投递任何文档</b>;首次启用或迁移状态文件时用</td></tr>
|
|
409
410
|
</table>
|
|
411
|
+
<div class="note"><b>判重对齐思源桥 autoStage,但丢弃后不重新获取。</b>状态文件
|
|
412
|
+
<code>~/.dsh/session-index/push-doc-state.json</code>(<code>PUSH_DOC_STATE</code> 可改)按
|
|
413
|
+
「文档路径 + 内容 sha256」记录已推送过的文档,判重<b>不依赖队列当前状态</b>:在思源桥里丢弃某篇、
|
|
414
|
+
清空队列、服务重启或定时任务重跑,都不会把它重新推回来;只有内容真的改了才作为新版本投一次。
|
|
415
|
+
<code>--force</code> 可强制绕过。实测:模拟「22 篇全部被丢弃」时 POST 次数为 0;内容更新后投 1 次、
|
|
416
|
+
再跑 0 次。</div>
|
|
410
417
|
|
|
411
418
|
<h3>会话整理 → 思源桥待入库(v2.5.0 起)</h3>
|
|
412
419
|
<p>DSH 插件之间<b>不共享会话数据</b>:会话本体只有一份磁盘文件,各插件各自解析、各存各的目录。会话卡与结论已在 session-index 手里,思源桥又是独立的 HTTP 服务(<code>127.0.0.1:5599</code>),所以一次接口调用即可对接,<b>不必新建第三个插件</b>,也不用再解一遍 zstd。</p>
|
|
@@ -502,7 +509,7 @@ pnpm install --offline --frozen-lockfile --config.autoInstallPeers=false</code><
|
|
|
502
509
|
├── smoke.mjs 80 项端到端自测
|
|
503
510
|
├── icon.svg 插件页图标(卡片 + 放大镜,64×64 SVG,938 字节)
|
|
504
511
|
├── plugin.json 机器可读插件能力清单(含安装方式与仓库信息)
|
|
505
|
-
├── package.json v2.5.
|
|
512
|
+
├── package.json v2.5.2 / icon / dsh.bundle.patch / dsh.plugin / 脚本
|
|
506
513
|
├── 操作使用手册.html 本文档
|
|
507
514
|
├── README.md 仓库首页 = 安装与使用说明
|
|
508
515
|
├── skill/SKILL.md 自动接入技能
|
|
@@ -541,7 +548,8 @@ pnpm install --offline --frozen-lockfile --config.autoInstallPeers=false</code><
|
|
|
541
548
|
<tr><td>stage 两步确认(v2.5.0)</td><td>本地起假思源桥(<code>node:http</code>,不碰真实 5599 队列):只读探测零写入 → 默认预览不投递 → 错误令牌 <code>TOKEN_MISMATCH</code> 拒绝 → 回传 <code>confirmWith</code> 成功入队 → 重复投递被去重 → 全程<b>零次 commit</b>,投递正文含「未写入笔记库」声明</td></tr>
|
|
542
549
|
<tr><td>stage 真实服务只读</td><td><code>GET /api/status</code> / <code>/api/stage</code> / <code>/api/autostage</code> 实测可达:内核 3.8.9、待入库 0 篇、<code>autoStage=true</code>;本轮未产生任何写入</td></tr>
|
|
543
550
|
<tr><td>索引并发写加固</td><td>多进程同时写索引时 Windows <code>rename</code> 偶发 <code>EPERM</code> → 原子写加 0/40/120/300ms 退避重试;<b>80/80 冒烟全绿</b></td></tr>
|
|
544
|
-
<tr><td>WorkBuddy 文档批量投递(v2.5.1)</td><td>70 条文档命中 → 排除临时/依赖 26 → 文件缺失 4 → 内容/路径重复 18 → <b>22 篇全部投递成功</b>,队列 23 篇 / 16.6 万字;标题唯一、全部带 note
|
|
551
|
+
<tr><td>WorkBuddy 文档批量投递(v2.5.1)</td><td>70 条文档命中 → 排除临时/依赖 26 → 文件缺失 4 → 内容/路径重复 18 → <b>22 篇全部投递成功</b>,队列 23 篇 / 16.6 万字;标题唯一、全部带 note 与预览、落点一致;全程零 commit</td></tr>
|
|
552
|
+
<tr><td>丢弃后不重推(v2.5.1 修订)</td><td><code>--seed</code> 建立 22 条状态基线;用假思源桥模拟「队列被全部丢弃」(返回空队列 + discarded=22)→ <b>POST 次数 0</b>、22 篇全部因「已推送过」跳过;隔离环境验证内容未变 0 次、内容更新 1 次、再跑 0 次;真实队列下重跑 0 新增、仍 23 篇</td></tr>
|
|
545
553
|
</table>
|
|
546
554
|
<h3>版本演进</h3>
|
|
547
555
|
<ul>
|
|
@@ -552,11 +560,12 @@ pnpm install --offline --frozen-lockfile --config.autoInstallPeers=false</code><
|
|
|
552
560
|
<li><b>v2.4.0</b> <b>插件页直接安装</b>:声明 <code>dsh.bundle.patch</code> + 新增 <code>cordis.patch.yml</code>(<code>!!js</code> 自解析命令与路径)+ 新增宿主半身 <code>lib/index.mjs</code>;装到哪都不用改配置,一次重启生效。仓库转公开。<code>install.mjs</code> 自动识别 bundle 模式,避免重复注册。</li>
|
|
553
561
|
<li><b>v2.4.1</b> 新增 <code>install.mjs --doctor</code> <b>环境体检</b>(7 项,只读),把「插件页装不上」的四个真实成因变成可自动定位的检查项:git URL 重写镜像、pnpm <code>fetch-timeout</code> 超上限、远端大 tarball、lockfile 残留 <code>git+ssh</code>。手册第 13 章补充三条实测坑与修法。</li>
|
|
554
562
|
<li><b>v2.5.0</b> 新增第 14 个工具 <code>session_index_stage</code>:<b>会话整理文档 → 思源桥待入库</b>。两步确认(预览给令牌,回传 <code>confirmWith</code> 才投递)、标题+字数去重、三种模板与落点可配;只入队,<b>绝不自动写库</b>。第 12 章补完整用法。</li>
|
|
555
|
-
<li><b>v2.5.1</b> <code>tools/push-doc.mjs</code> 新增 <code>--from-workbuddy</code> <b>批量模式</b>:扫描 WorkBuddy 全部会话生成的文档,内容哈希去重 + 排除临时目录 + 同名查重后一次投递;默认只读预览。实测 22 篇全部入队、重复执行全被拦截。README 补「插件怎么更新」:本机为
|
|
563
|
+
<li><b>v2.5.1</b> <code>tools/push-doc.mjs</code> 新增 <code>--from-workbuddy</code> <b>批量模式</b>:扫描 WorkBuddy 全部会话生成的文档,内容哈希去重 + 排除临时目录 + 同名查重后一次投递;默认只读预览。实测 22 篇全部入队、重复执行全被拦截。README 补「插件怎么更新」:本机为 npm registry 来源(<code>dsh-session-index-lau</code>),插件页<b>有「更新」按钮、无需卸载重装</b>。<b>同版修订</b>:批量判重改为持久化状态文件(<code>push-doc-state.json</code>),丢弃后不重新推送、内容更新才投新版;新增 <code>--seed</code>。</li>
|
|
564
|
+
<li><b>v2.5.2</b> <b>发布流程标准化</b>:新增发布前 / 安装后核验门禁 <code>dist/publish/check-release.mjs</code>(语法、80 项冒烟、功能核验、四处版本统一、MCP 握手),核验未全绿禁止推进;标准流程文档同步批量推送 / 持久化判重 / 定时自动化机制与版本统一矩阵;把源仓库的判重脚本同步进发布包与 DSH 安装,消除「同号不同内容」分叉。</li>
|
|
556
565
|
</ul>
|
|
557
566
|
</section>
|
|
558
567
|
|
|
559
|
-
<footer>dsh-session-index v2.5.
|
|
568
|
+
<footer>dsh-session-index v2.5.2 · 操作使用手册 · 生成于 2026-11-14 · 零第三方依赖</footer>
|
|
560
569
|
</main>
|
|
561
570
|
</div>
|
|
562
571
|
<script>
|