dsh-tiddlywiki 0.24.0 → 0.24.1
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 +2 -0
- package/lib/index.js +72 -12
- package/lib/index.js.map +1 -1
- package/package.json +2 -2
- package/src/host/admin.ts +26 -6
- package/src/host/routes.ts +21 -9
- package/src/host/seeds.ts +47 -0
- package/src/index.ts +15 -3
package/README.md
CHANGED
|
@@ -32,6 +32,7 @@
|
|
|
32
32
|
| 🛡️ **不会被覆盖的写入** | 所有写入路径(agent 工具 **与** 快速笔记/编辑器路由)都**先读后写**:不传 tags 就保留原有标签、自定义字段与**内容类型**(`text/css`/wikitext 等不会被重置成 Markdown,v0.20.1);`tiddlywiki_put(..., expectedModified/expectedRevision)` 与 `tiddlywiki_delete(..., expectedModified/expectedRevision)` 乐观并发——读取后若有人(在 TW 编辑器里)改过,写入/删除被拒绝(HTTP 409)而不是静默覆盖或丢进回收站;**给了哪个令牌就必须匹配哪个**(v0.23.5:两个令牌是 AND 不是 OR——`revision` 是 TW 的内存计数器、重启会复位,旧逻辑会因此静默放行跨重启的覆盖);`tiddlywiki_attach` 的同名标题**默认拒绝**(要覆盖必须 `force: true`);`tiddlywiki_delete` 默认**软删除进回收站**,`tiddlywiki_trash` 可恢复(v0.19.0 / v0.19.1 / v0.19.5 / v0.23.5) |
|
|
33
33
|
| 🏷 **工作区感知的读写** | **新建**笔记自动带 `ws/<项目名>` 标签 + `workspace` 字段(项目名取自会话工作目录,`note.workspaceMark` 可关);`tiddlywiki_search` 因此默认**先在本项目里找、区内 0 条才自动扩大到全库**并在回执里写明实际范围,`query` 按空白切词、**全部词命中**才算(AND)。工作区只是**附加**标记,绝不改写笔记原有的标签/字段,也**只在新建时**打(v0.24.0) |
|
|
34
34
|
| 🕰 **时效性内容只提示不删** | 阶段性笔记(版本记录 / 部署步骤 / 排期 / 临时方案)可声明 `valid-until`(硬过期)或 `review-after`(该复查),被取代时写 `superseded-by` + `superseded` 标签并**保留旧笔记**;`tiddlywiki_lint` 的 `stale` 检查**只报告**(含「版本号或 done 标签 + 长期未改动」的**候选**,明确标注非判定)——**淘汰永远由人决定**,插件不会自动删除或归档任何笔记(v0.24.0) |
|
|
35
|
+
| 🚰 **重启不丢写入** | 停/重启 TW 只有一条路:`drainThenStop()`——**排干 syncer 队列之后才停子进程**,`/restart`、`/admin/restart`、插件/主题变更后的重启、知识库切换、`/sync`、seed 全部经它(此前其中三条直接杀进程,在一秒内点「重启 TW」会静默丢掉刚写的笔记);排干不完整会在回包里如实报 `drained:false`,而不是假装没事(v0.24.1) |
|
|
35
36
|
| 🕒 **时间戳不再丢** | 插件写入的每条笔记都会带上 TW 的 `created`/`modified`(17 位紧凑 UTC,与 TW 编辑器逐字节一致):新建 = 两者都取当前时刻,覆盖 = **保留原 `created`、刷新 `modified`**。此前这两个字段被当成「TW 服务端会补」而丢弃,而服务端**从不补**——缺 `modified` 的条目会被 TW 的 `sortTiddlers` 当空串,在 `+[!sort[modified]]` 页面上**直接沉到最后一名**(表现为「新日记没被收录」)。`TiddlyWebClient.put()` 还有一道兜底,任何写路径都不会写出无时间戳的条目(v0.22.5) |
|
|
36
37
|
| 🧼 **渲染片段净化** | 回复流卡片与会话汇总注入的 TW 片段先经 **host 白名单净化**(丢 `iframe`/`script`/`svg`/`on*`/`javascript:`/`data:text/html` 等)——TW 自己的解析器只剥 `on*`,`<iframe src="javascript:…">` 会原样通过并在 DSH 页面里执行(v0.19.1 修复的存储型 XSS) |
|
|
37
38
|
| 🔒 **写路由方法校验** | 每个写路由只接受自己的 HTTP 方法:跨站 `GET /sync`、`GET /restart`、`GET /upload` 一律 405 且无副作用(v0.19.0 修复了「任意网页一张 `<img>` 即可触发 pull/commit/push」的 CSRF 面) |
|
|
@@ -412,6 +413,7 @@ lib/ # 预构建产物(发布含 lib/**,提交入库;
|
|
|
412
413
|
|
|
413
414
|
> 最近几个主要版本的一句话记录(完整变更见 [Releases](https://github.com/bbqisbbq/dsh-tiddlywiki/releases) / git log)。
|
|
414
415
|
|
|
416
|
+
- **v0.24.1**(2026-09-20):**修「重启/停止 TW 的四个路径里有三个没排干 syncer 队列」——一类静默丢写入的缺陷**。本仓库铁律第一条要求「重启 TW 前必须把 syncer 队列排干」,但那条规则**只写在文档里**:`POST /restart`、`POST /admin/restart`、`POST /admin/info`(改插件/主题后重启)与「知识库切换」的 `stopServer` 全都是**直接杀 TW 子进程**,而 TW 的 REST PUT 返回 204 时条目还在内存里、文件系统 syncer 要 ~250ms 后才落盘 —— 在那一秒内点「重启 TW」,刚写的笔记就随子进程一起消失(与 v0.19.0 丢 `tw-web-host`、v0.22.0 才发现 throttle 竞态同一类)。只有启动自举 / seed / `/sync` 三处走了 `flushPendingWrites`。**根因不是谁写错了某一行,而是「规则」和「代码」之间没有强制关系** —— 所以这一版不是补四个调用点,而是把不变量变成**原语**:新增 `drainThenStop()`(`src/host/seeds.ts`,**排干发生在它内部**,签名是 `{client, tiddlersDir, stop}`),四个路径全部改为经它停/重启,调用方**没法**绕过;`stop` 的返回类型放宽为 `Promise<unknown>`(`restart()` 回状态视图、`stop()` 回 void,排干都不关心)。`/restart` 与 `/admin/restart` 的回包新增 `drained`,排干不完整时如实上报而不是静默。**守门**:新增 `scripts/verify-restart-drain.mjs`(进 `verify:unit`)——① 顺序断言:`drainThenStop` 内部「排干的 await」必须早于「stop 的 await」,且排干失败必须走显式分支后**仍然 stop**(best-effort:不能因为排不干就把用户锁死在起不来的 TW 上);② 源码级断言:`routes.ts`/`admin.ts` 里**每个** `deps.server.restart()` 都必须作为 `drainThenStop({stop: …})` 的参数出现(裸 restart 即红),`index.ts` 的 `stopServer` 必须包在它里面;③ 行为断言:用桩 client 真跑一遍,确认「写入真的落盘之后才 stop」「client 缺失(TW 没在跑)是安全 no-op」「排干失败也继续 stop 并把警告交出去」。**已反向验证**:分别把 `/sync` 路径、知识库切换还原成裸调用、并删掉 `drainThenStop` 里的 `stop`,三次都稳定变红。顺带把 `/sync` 与 seed 两条**本来就在 flush** 的路径也收敛到同一原语,从此「重启前排干」只有一个实现。
|
|
415
417
|
- **v0.24.0**(2026-09-20):**三处「Agent 读写 wiki 的规矩」按用户要求改进 + 一个 lint 误报修复**。① **新建笔记自动标工作区**:`put`/`batch_put`/`append` 新建条目时自动带上 **`ws/<项目名>` 标签 + `workspace` 字段**,项目名取自调用会话的工作目录(新 `src/host/workspace.ts` 纯函数:Windows/Unix 路径、尾斜杠、CJK、非法字符规范化;`home`/`tmp`/`desktop` 这类通用目录与**驱动器根目录**一律拒绝——后者是写这个模块的守门脚本抓出来的真缺陷:`C:\` 会被算成 `C:`,在盘根开会话时全盘笔记都挂上 `ws/C:`)。标记是**附加式**(调用方标签在前)、**仅新建**(覆盖不追打,也不会改写已有 `workspace` 字段)、`$:/` 豁免、`note.workspaceMark: false` 可关、调用方显式 `fields.workspace` 优先;`exec` 缺失 / 会话无 `cwd` / 未知会话一律**安全降级为不标记**(不猜、不抛错)。为什么标签必须带 `ws/` 前缀:裸项目名与真实业务标签撞车——作者库里 `dsh-tiddlywiki` 已挂着 2510 篇导入的书籍章节。② **检索先窄后宽 + 多词 AND**:`tiddlywiki_search` 先在工作区内查、**区内 0 条才自动扩大到全库**,回执明确写出「已在工作区 ws/X 内缩小范围」或「工作区 ws/X 内 0 条,已扩大到全库」(**「工作区内 0 条」不等于库里没有**,这是把「搜不到」错判成「不存在」的主要来源);`query` 从**整串子串匹配**改为按空白切词、**全部词命中**才算(旧行为连 `alphaprobe betaprobe` 这种自然的双词查询都永远匹配不到);调用方显式传 `tag`/`field`/`value` 时**不自动收窄**(显式范围不被二次猜测)。③ **过期内容淘汰策略(只提示,绝不自动删)**:阶段性笔记写 `valid-until`(硬过期)/ `review-after`(该复查),被取代时写 `superseded-by` + `superseded` 标签、**保留旧笔记**;`tiddlywiki_lint` 新增 `stale` 检查(默认开启、`staleAfterDays` 默认 180),报 `stale-expired` / `stale-review` 两类**硬判定**加一类 `stale-candidates`(带版本号或 `done` 标签且长期未改动,**明确标注「候选,非判定」**)。**淘汰只由人决定**——报告全程只读,不删不改不归档。④ **修 lint 死链误报**(v0.23.6 的内容并入本版):死链正则 `\{\{([^}]+)\}\}` 会把 `{{{ [tag[todo]count[]] }}}` 这类**三花括号过滤器表达式**从第 1 个 `{` 开始捕获成「转写目标」并报死链(实测线上 480 条死链报告里 8 条属此类),现在用前后瞻 + 捕获内容守卫排除。**明确不改的**:≈440 条真实死链**保留不修**——多数是用户手工删掉的笔记,属预期状态(只读报告是对的,自动「修复」是错的)。**守门**:新增 `scripts/verify-workspace.mjs`(进 `verify:unit`,9 组纯函数断言,含「任何 cwd 算出的标签都不得是垃圾标签 / 含空白 / 丢前缀」这条与 `isJunkTag()` 的跨模块一致性检查,以及 `ws/` 前缀不得被「简化」掉的回归),`verify-tools.mjs` +12 条真实 TW 断言(工作区标记六种形态 + 多词 AND + 先窄后宽 + `fellBack` + `stale` 三类),`verify-prompt.mjs` +1 条「三条新治理规则必须在场」并把 slim 长度预算从 1800 调至 2100(**预算上调与新增的逐条断言绑定**:删掉任何一条规则即红)。**所有新守门都做过反向验证**:分别还原旧行为(整串子串检索 / 关掉工作区收窄 / 关掉自动标记 / 关掉 stale 检查 / 去掉驱动器根目录判定 / 从规范化字符集里删掉 `|`)后,对应断言稳定变红。slim 正文实测 1918 字符(full 2722)。
|
|
416
418
|
|
|
417
419
|
- **v0.23.5**(2026-09-18):**修 15 处审计缺陷**(第三轮全面审查列出的 18 条,逐条核实后修掉其中 15 条真缺陷;跳过 3 条纯理论性的信息泄露。完整审计报告见知识库笔记「dsh-tiddlywiki 第三轮代码审计」——审计报告是工作文档,不再进 git)。按性质分四组。**① 数据安全(最重)**:`tiddlywiki_delete` 原先**先删原条目、后读回收站索引**,于是「快照已写 → 原条目已删 → 索引读失败」时报"已中止",但笔记其实已经没了、且没进索引——`trash list` 看不见、`restore` 找不到、`empty` 清不掉,成为永久孤儿(只能从 git 捞);现在**读校验索引排在所有破坏性步骤之前**,中止即等于"没删"。同一处还有两个洞:`$:/` 系统条目会被**软删除**(删掉 `trash-index` 自己 → 下一次读 404 → 被当成"回收站为空" → 索引被重写成 1 条,丢掉全部记录),现在系统条目一律走永久删除分支,且**删回收站索引被显式拒绝**(要清空请用 `trash action=empty`)。`tiddlywiki_put` 不再把**二进制附件**写成文本(保留基底 `type: image/png` 会让 base64 解不出、图裂而回执说"类型没变";要转换得显式 `fields.type`)。`git pull` 不再**无条件 `rebase --abort`**——此前只要 pull 失败(含没配 remote、离线等与被 rebase 无关的原因),用户在 wiki 仓库里正在做的 rebase 与冲突解决就被清掉;现在只 abort「我们自己发起」的那个。乐观并发的两个令牌从 **OR 改回 AND**:`revision` 是 TW 的内存计数器(重启/拉取后复位),旧逻辑「`modified` 命中就放行、否则看 `revision`」会让跨重启的写入被判"一致"而**静默覆盖**人类改动,同时传两个令牌也**并不更安全**。配置 tiddler 的 `modified` 不再被钉死成 `created`(`ensureTiddlerTimestamps` 只补缺的那一半,用 now)。**② 崩溃与资源泄漏**:`fs.watch` 补上常驻 `error` 监听(Node 在无监听器时把 `error` 抛成未捕获异常 → 整个 `dsh web` 进程退出;Windows EPERM / Linux ENOSPC 都能触发);`WikiServer.startOnce()` 在 `spawn` 前**复查 `stopping`**(`stop()` 可在 `await findFreePort()` 期间跑完,此后 spawn 的子进程没人杀 = 孤儿 TW);知识库切换**回滚失败也必须重装 extras**(此前从 catch 直接返回、跳过 `setupExtras()`,此后所有 wiki 写入都不再被提交,而"迟到就绪"复探让 `/status` 看着一切正常——静默停摆);`runSwitch` 补 `disposed` 守卫(热重载/关闭期间的在途切换不再重新挂上 committer/watcher,也不在 teardown 之后再 spawn TW)。**③ 行为瑕疵**:`escapePromptBraces` 对**连续花括号**失效(`{{{name}}}` 会残留 `{{`,而 DSH 对未知变量直接抛错、能炸掉整个系统提示词装配正是该函数存在的理由);`?limit=` 空值不再被夹成 1(`Number('' ?? fallback)` 是 0,`/recent?limit=` 曾只返回 1 条而不是默认 15);`tiddlywiki_append` 在 `mode=prepend` 时不再回执谎报「写进了某段落」(`heading` 只对 append 生效);`POST /render` 的超限 body 从 400 改为 **413**。**④ 两处零成本的安全收口**:代理路径判定不再只看字面量 `/tiddlers/`——TW core 的 `get-tiddler-html` 路由是**单段**(`/^\/([^\/]+)$/`),把 `/` 编码成 `%2F` 即可绕过旧守卫(本机实测:修复前 `/tw/...%2Fconfig` 与 `/api/...%2Fconfig` 都返回 **200 + 715 字节配置 JSON**);`/render` 的 **`text` 分支**也不再是旁路(TW 会解析 `{{…}}` 转写,实测修复前 `{"text":"{{$:/plugins/dsh-tiddlywiki/config}}"}` 返回 200 + 3123 字节配置正文,净化器只管标签、管不住正文),`contextTitle` 一并校验。**守门**:`verify-write-policy` +6 条(令牌 AND 语义 +「证据不足判冲突」+ `onlyCreated` 不再钉死 modified)、`verify-prompt` +1 条连续花括号、`verify-frame-guards` +4 条源码级生命周期断言(`fs.watch` error 监听 / spawn 前复查 `stopping` / 回滚 finally / `runSwitch` disposed)、`verify-audit-fixes` +4 条 E2E(删除顺序:索引读失败后原条目必须仍在、`$:/` 与索引不得被软删、二进制附件拒写、`?limit=` 空值)、`verify-conflict-config-guards` +1 条**真 git E2E**(在真 rebase 中途 pull 失败,用户的 rebase 必须原样保留)、selftest +8 条(编码/双编码代理路径 403、`/render` 转写与 `contextTitle` 403、普通转写仍 200)。**全部 15 条修复都做过反向验证**:把修复还原后,新断言稳定变红。
|
package/lib/index.js
CHANGED
|
@@ -4331,6 +4331,34 @@ function needsRestartAfterSeeds(results) {
|
|
|
4331
4331
|
return results.some((r) => r.ok && r.wrote && RESTART_REQUIRED_SEED_IDS.includes(r.id));
|
|
4332
4332
|
}
|
|
4333
4333
|
/**
|
|
4334
|
+
* THE ONLY SANCTIONED WAY TO STOP OR RESTART THE TW CHILD (v0.24.1).
|
|
4335
|
+
*
|
|
4336
|
+
* WHY THIS WRAPPER EXISTS
|
|
4337
|
+
* -----------------------
|
|
4338
|
+
* Ironclad rule #1 ("drain the syncer queue before restarting TW") had four
|
|
4339
|
+
* call sites and only three of them obeyed it. The two manual restart routes
|
|
4340
|
+
* (`POST /restart`, `POST /admin/restart`), the plugins/themes restart in
|
|
4341
|
+
* `POST /admin/info` and the knowledge-base switch (`stopServer`) all killed the
|
|
4342
|
+
* child with writes still queued — and a queued write is simply lost, silently,
|
|
4343
|
+
* exactly like the v0.19.0 `tw-web-host` loss. A rule that lives only in prose
|
|
4344
|
+
* gets re-broken by the next person who adds a restart.
|
|
4345
|
+
*
|
|
4346
|
+
* So the drain is now INSIDE the primitive: a caller cannot stop TW without
|
|
4347
|
+
* going through it, and `scripts/verify-restart-drain.mjs` asserts that no
|
|
4348
|
+
* `server.restart()` / `server.stop()` call site exists outside this wrapper.
|
|
4349
|
+
*
|
|
4350
|
+
* Best-effort by design: a failed drain logs and still stops (refusing to
|
|
4351
|
+
* restart would leave the user unable to recover from a wedged TW). Returns
|
|
4352
|
+
* whether the store was provably quiescent — callers surface it in their
|
|
4353
|
+
* response so an incomplete drain is observable rather than silent.
|
|
4354
|
+
*/
|
|
4355
|
+
async function drainThenStop(options) {
|
|
4356
|
+
const drained = options.client === void 0 ? true : await flushPendingWrites(options.client, options.tiddlersDir).catch(() => false);
|
|
4357
|
+
if (!drained) options.log?.("syncer queue may not be fully drained — writes still queued at restart time could be lost");
|
|
4358
|
+
await options.stop();
|
|
4359
|
+
return drained;
|
|
4360
|
+
}
|
|
4361
|
+
/**
|
|
4334
4362
|
* Presence probe for a seed's `check`. A 404 means "missing"; every other
|
|
4335
4363
|
* failure PROPAGATES (the caller `checkAllSeeds` turns it into a failed check)
|
|
4336
4364
|
* — swallowing it here would report "缺失" for a service that is merely
|
|
@@ -6885,14 +6913,21 @@ function registerRoutes(ctx, deps) {
|
|
|
6885
6913
|
}, 429);
|
|
6886
6914
|
return;
|
|
6887
6915
|
}
|
|
6916
|
+
let drained = true;
|
|
6888
6917
|
try {
|
|
6889
|
-
await
|
|
6918
|
+
drained = await drainThenStop({
|
|
6919
|
+
client: deps.getClient(),
|
|
6920
|
+
tiddlersDir: join(deps.getWikiPath(), "tiddlers"),
|
|
6921
|
+
stop: () => deps.server.restart(),
|
|
6922
|
+
log: (message) => console.warn("[dsh-tiddlywiki]", message)
|
|
6923
|
+
});
|
|
6890
6924
|
} finally {
|
|
6891
6925
|
endMutation();
|
|
6892
6926
|
}
|
|
6893
6927
|
json(res, {
|
|
6894
6928
|
ok: true,
|
|
6895
|
-
status: deps.server.status().status
|
|
6929
|
+
status: deps.server.status().status,
|
|
6930
|
+
drained
|
|
6896
6931
|
});
|
|
6897
6932
|
} catch (err) {
|
|
6898
6933
|
json(res, {
|
|
@@ -6940,9 +6975,12 @@ function registerRoutes(ctx, deps) {
|
|
|
6940
6975
|
let restarted = false;
|
|
6941
6976
|
let restartError;
|
|
6942
6977
|
if (pulled.changed === true) try {
|
|
6943
|
-
|
|
6944
|
-
|
|
6945
|
-
|
|
6978
|
+
if (!await drainThenStop({
|
|
6979
|
+
client: deps.getClient(),
|
|
6980
|
+
tiddlersDir: join(dir, "tiddlers"),
|
|
6981
|
+
stop: () => deps.server.restart(),
|
|
6982
|
+
log: (message) => console.warn("[dsh-tiddlywiki]", message)
|
|
6983
|
+
})) console.warn("[dsh-tiddlywiki] sync: syncer queue may not be drained before restart");
|
|
6946
6984
|
restarted = true;
|
|
6947
6985
|
} catch (err) {
|
|
6948
6986
|
restartError = err instanceof Error ? err.message : String(err);
|
|
@@ -8076,7 +8114,12 @@ function registerAdminRoutes(ctx, deps) {
|
|
|
8076
8114
|
const changed = JSON.stringify(info) !== beforeInfo;
|
|
8077
8115
|
if (changed) {
|
|
8078
8116
|
await writeWikiInfo(wikiPath, info);
|
|
8079
|
-
await
|
|
8117
|
+
await drainThenStop({
|
|
8118
|
+
client: deps.getClient(),
|
|
8119
|
+
tiddlersDir: join(deps.getWikiPath(), "tiddlers"),
|
|
8120
|
+
stop: () => deps.server.restart(),
|
|
8121
|
+
log: (message) => console.warn("[dsh-tiddlywiki]", message)
|
|
8122
|
+
});
|
|
8080
8123
|
}
|
|
8081
8124
|
if (Array.isArray(body.themes) && activatedTheme !== void 0) {
|
|
8082
8125
|
const client = deps.getClient();
|
|
@@ -8157,10 +8200,16 @@ function registerAdminRoutes(ctx, deps) {
|
|
|
8157
8200
|
const handleRestart = async (req, res) => {
|
|
8158
8201
|
try {
|
|
8159
8202
|
if (rejectCrossSiteWrite(req, res, ["POST"])) return;
|
|
8160
|
-
await
|
|
8203
|
+
const drained = await drainThenStop({
|
|
8204
|
+
client: deps.getClient(),
|
|
8205
|
+
tiddlersDir: join(deps.getWikiPath(), "tiddlers"),
|
|
8206
|
+
stop: () => deps.server.restart(),
|
|
8207
|
+
log: (message) => console.warn("[dsh-tiddlywiki]", message)
|
|
8208
|
+
});
|
|
8161
8209
|
json(res, {
|
|
8162
8210
|
ok: true,
|
|
8163
|
-
status: deps.server.status().status
|
|
8211
|
+
status: deps.server.status().status,
|
|
8212
|
+
drained
|
|
8164
8213
|
});
|
|
8165
8214
|
} catch (err) {
|
|
8166
8215
|
json(res, {
|
|
@@ -8221,8 +8270,12 @@ function registerAdminRoutes(ctx, deps) {
|
|
|
8221
8270
|
let restartError;
|
|
8222
8271
|
if (needsRestartAfterSeeds(results)) try {
|
|
8223
8272
|
if (!await waitForFileWrite(join(deps.getWikiPath(), "tiddlers", "$__plugins_dsh_render.json"), 8e3, 150, seedStartedAt)) console.warn("[dsh-tiddlywiki] seeded render plugin file not seen on disk before restart");
|
|
8224
|
-
if (!await
|
|
8225
|
-
|
|
8273
|
+
if (!await drainThenStop({
|
|
8274
|
+
client,
|
|
8275
|
+
tiddlersDir: join(deps.getWikiPath(), "tiddlers"),
|
|
8276
|
+
stop: () => deps.server.restart(),
|
|
8277
|
+
log: (message) => console.warn("[dsh-tiddlywiki]", message)
|
|
8278
|
+
})) console.warn("[dsh-tiddlywiki] seed writes may not have been flushed before restart");
|
|
8226
8279
|
restarted = true;
|
|
8227
8280
|
} catch (err) {
|
|
8228
8281
|
restartError = err instanceof Error ? err.message : String(err);
|
|
@@ -11857,7 +11910,14 @@ function apply(ctx, rawConfig = {}) {
|
|
|
11857
11910
|
const result = await switchWiki({
|
|
11858
11911
|
currentLocation: () => server.currentLocation,
|
|
11859
11912
|
currentPath: () => wikiPath,
|
|
11860
|
-
stopServer: () =>
|
|
11913
|
+
stopServer: async () => {
|
|
11914
|
+
await drainThenStop({
|
|
11915
|
+
client: client(),
|
|
11916
|
+
tiddlersDir: join(wikiPath, "tiddlers"),
|
|
11917
|
+
stop: () => server.stop(),
|
|
11918
|
+
log: (message) => console.warn("[dsh-tiddlywiki]", message)
|
|
11919
|
+
});
|
|
11920
|
+
},
|
|
11861
11921
|
applyLocation: (nextLocation) => {
|
|
11862
11922
|
server.setLocation({
|
|
11863
11923
|
root: nextLocation.root,
|
|
@@ -11999,6 +12059,6 @@ function apply(ctx, rawConfig = {}) {
|
|
|
11999
12059
|
}, "dsh-tiddlywiki: host teardown");
|
|
12000
12060
|
}
|
|
12001
12061
|
//#endregion
|
|
12002
|
-
export { AGENT_WRITTEN_TAG, ALL_ARTICLES_MARKER_TITLE, ALL_ARTICLES_TEXT, ALL_ARTICLES_TITLE, ANON_USERNAME, AutoCommitter, CLIP_BRIDGE_BOOKMARKLET, CLIP_BRIDGE_DEFAULT_PORT, CLIP_BRIDGE_DOC_TEXT, CLIP_BRIDGE_DOC_TITLE, CLIP_BRIDGE_DRAG_HREF, CLIP_BRIDGE_MARKER_TITLE, ClipBridge, ConfigStore, ConfigUnreadableError, DEFAULT_NOTE_TYPE, DEFAULT_PROMPT_MODE, DEFAULT_WECHAT_ADAPTER, DEFAULT_WECHAT_COMMAND, DOC_NOTE_TAG, DOC_NOTE_TEXT, DOC_NOTE_TITLE, DSH_DOCS_TAG, GitConflictStateError, GitFace, HOME_DEFAULT_TIDDLERS, HOME_INDEX_ITEMS, HOME_INDEX_MARKER_TITLE, HUMAN_EDITED_TAG, LOCATION_STATE_VERSION, MARKDOWN_PLUGIN, MASKED_SECRET, MENUBAR_THEME_MARKER_TITLE, MENUBAR_THEME_TEXT, MENUBAR_THEME_TIDDLER, MISSING_TYPE_FILTER, PATH_PREFIX, PROMPT_GOVERNANCE_BLOCKS, PROMPT_MODES, PROMPT_SECTION_NAME, PROMPT_SECTION_ORDER, READY_HARD_FACTOR, READY_POLL_MS, READY_SLOW_POLL_MS, READY_TIMEOUT_DEFAULT_MS, READY_TIMEOUT_MAX_MS, READY_TIMEOUT_MIN_MS, RENDER_BUNDLE_TEXT, RENDER_MARKER_TITLE, RENDER_PLUGIN_FILE, RENDER_PLUGIN_TITLE, SEED_DEFS, SEED_MARKER_VERSION, SEND_TO_AGENT_BUNDLE_TEXT, SEND_TO_AGENT_MARKER_TITLE, SEND_TO_AGENT_PLUGIN_TITLE, SESSION_SUMMARY_PREFIX, STARTER_DOCS_ITEMS, STARTER_DOCS_MARKER_TITLE, TEXT_LIST_FILTER, TRASH_INDEX_TITLE, TRASH_PREFIX, TW_PROXY_PATH, TW_PROXY_PREFIX, TW_WEB_HOST_DEFAULT, TW_WEB_HOST_TIDDLER, TiddlyWebClient, TrashIndexUnavailableError, UI_STYLES_MARKER_TITLE, UI_STYLE_ITEMS, WECHAT_ADAPTERS, WECHAT_ADAPTER_FILES, WECHAT_ADAPTER_MARKER, WECHAT_DOCS_MARKER_TITLE, WECHAT_DOCS_TEXT, WECHAT_DOCS_TITLE, WECHAT_PUBLISH_BUNDLE_TEXT, WECHAT_PUBLISH_MARKER_TITLE, WECHAT_PUBLISH_PLUGIN_TITLE, WORKSPACE_FIELD, WORKSPACE_TAG_PREFIX, WechatPublishRunner, WikiServer, WriteConflictError, apply, assertNoConflict, assertPublicImageUrl, awaitReady, buildBinaryTiddler, buildClipTiddler, buildImageNoteTiddler, buildPromptText, buildPublishInvocation, buildVersionInvocation, buildWriteTiddler, bundledCatalog, capOutput, checkAllSeeds, checkWechatReady, cleanTiddler, clearLocationState, deepMerge, defaultAdaptersDir, defaultLocationStateFile, defaultWechatJobDir, defineTool, describeConflict, describePrompt, describeUnreadableConfig, docNoteText, downloadClipImage, dshHomePath, ensureLanguage, ensurePlugin, ensureTiddlerTimestamps, escapeInline, escapePromptBraces, expandEnvPath, flattenTiddlerFields, flushPendingWrites, formatTiddlerDate, hashText, hostAllowed, imageExtensionForMime, inject, interpretPublishOutcome, isBinaryType, isJunkTag, isPrivateAddress, isSafeCliValue, isSafeDsn, isSafeUrl, listWikiCandidates, locationPath, maskConfigSecrets, name, needsRestartAfterSeeds, normalizeLocation, normalizePromptMode, normalizePromptPreview, normalizeReadyTimeoutMs, normalizeThemes, normalizeWechatConfig, normalizeWorkspaceName, openInTwEditor, parseClipPayload, parseSeedMarker, parseTiddlerDate, pickImageMime, readActiveThemeName, readLocationState, readSeedMarker, readWikiInfo, readyHardTimeoutMs, registerAdminRoutes, registerRoutes, registerTiddlywikiTools, removeSeedById, resolveClipTitle, resolveTwRoot, runAllSeeds, runSeedById, sanitizeTwFragment, scanAdapterDir, seedAllArticles, seedClipBridge, seedDocNote, seedHomeIndex, seedMenubarTheme, seedRenderRoute, seedSendToAgent, seedStarterDocs, seedUiStyles, seedWechatDocs, seedWechatPublish, stripMaskedSecrets, switchWiki, tailOf, tiddlywikiToolSummary, toIsoDateString, toolSignatureLines, unseedClipBridge, unseedWechatDocs, unseedWechatPublish, waitForFileWrite, workspaceMarkFromCwd, workspaceNameFromCwd, workspaceTagName, writeLocationState, writeSeedMarker, writeSessionSummary, writeWikiInfo };
|
|
12062
|
+
export { AGENT_WRITTEN_TAG, ALL_ARTICLES_MARKER_TITLE, ALL_ARTICLES_TEXT, ALL_ARTICLES_TITLE, ANON_USERNAME, AutoCommitter, CLIP_BRIDGE_BOOKMARKLET, CLIP_BRIDGE_DEFAULT_PORT, CLIP_BRIDGE_DOC_TEXT, CLIP_BRIDGE_DOC_TITLE, CLIP_BRIDGE_DRAG_HREF, CLIP_BRIDGE_MARKER_TITLE, ClipBridge, ConfigStore, ConfigUnreadableError, DEFAULT_NOTE_TYPE, DEFAULT_PROMPT_MODE, DEFAULT_WECHAT_ADAPTER, DEFAULT_WECHAT_COMMAND, DOC_NOTE_TAG, DOC_NOTE_TEXT, DOC_NOTE_TITLE, DSH_DOCS_TAG, GitConflictStateError, GitFace, HOME_DEFAULT_TIDDLERS, HOME_INDEX_ITEMS, HOME_INDEX_MARKER_TITLE, HUMAN_EDITED_TAG, LOCATION_STATE_VERSION, MARKDOWN_PLUGIN, MASKED_SECRET, MENUBAR_THEME_MARKER_TITLE, MENUBAR_THEME_TEXT, MENUBAR_THEME_TIDDLER, MISSING_TYPE_FILTER, PATH_PREFIX, PROMPT_GOVERNANCE_BLOCKS, PROMPT_MODES, PROMPT_SECTION_NAME, PROMPT_SECTION_ORDER, READY_HARD_FACTOR, READY_POLL_MS, READY_SLOW_POLL_MS, READY_TIMEOUT_DEFAULT_MS, READY_TIMEOUT_MAX_MS, READY_TIMEOUT_MIN_MS, RENDER_BUNDLE_TEXT, RENDER_MARKER_TITLE, RENDER_PLUGIN_FILE, RENDER_PLUGIN_TITLE, SEED_DEFS, SEED_MARKER_VERSION, SEND_TO_AGENT_BUNDLE_TEXT, SEND_TO_AGENT_MARKER_TITLE, SEND_TO_AGENT_PLUGIN_TITLE, SESSION_SUMMARY_PREFIX, STARTER_DOCS_ITEMS, STARTER_DOCS_MARKER_TITLE, TEXT_LIST_FILTER, TRASH_INDEX_TITLE, TRASH_PREFIX, TW_PROXY_PATH, TW_PROXY_PREFIX, TW_WEB_HOST_DEFAULT, TW_WEB_HOST_TIDDLER, TiddlyWebClient, TrashIndexUnavailableError, UI_STYLES_MARKER_TITLE, UI_STYLE_ITEMS, WECHAT_ADAPTERS, WECHAT_ADAPTER_FILES, WECHAT_ADAPTER_MARKER, WECHAT_DOCS_MARKER_TITLE, WECHAT_DOCS_TEXT, WECHAT_DOCS_TITLE, WECHAT_PUBLISH_BUNDLE_TEXT, WECHAT_PUBLISH_MARKER_TITLE, WECHAT_PUBLISH_PLUGIN_TITLE, WORKSPACE_FIELD, WORKSPACE_TAG_PREFIX, WechatPublishRunner, WikiServer, WriteConflictError, apply, assertNoConflict, assertPublicImageUrl, awaitReady, buildBinaryTiddler, buildClipTiddler, buildImageNoteTiddler, buildPromptText, buildPublishInvocation, buildVersionInvocation, buildWriteTiddler, bundledCatalog, capOutput, checkAllSeeds, checkWechatReady, cleanTiddler, clearLocationState, deepMerge, defaultAdaptersDir, defaultLocationStateFile, defaultWechatJobDir, defineTool, describeConflict, describePrompt, describeUnreadableConfig, docNoteText, downloadClipImage, drainThenStop, dshHomePath, ensureLanguage, ensurePlugin, ensureTiddlerTimestamps, escapeInline, escapePromptBraces, expandEnvPath, flattenTiddlerFields, flushPendingWrites, formatTiddlerDate, hashText, hostAllowed, imageExtensionForMime, inject, interpretPublishOutcome, isBinaryType, isJunkTag, isPrivateAddress, isSafeCliValue, isSafeDsn, isSafeUrl, listWikiCandidates, locationPath, maskConfigSecrets, name, needsRestartAfterSeeds, normalizeLocation, normalizePromptMode, normalizePromptPreview, normalizeReadyTimeoutMs, normalizeThemes, normalizeWechatConfig, normalizeWorkspaceName, openInTwEditor, parseClipPayload, parseSeedMarker, parseTiddlerDate, pickImageMime, readActiveThemeName, readLocationState, readSeedMarker, readWikiInfo, readyHardTimeoutMs, registerAdminRoutes, registerRoutes, registerTiddlywikiTools, removeSeedById, resolveClipTitle, resolveTwRoot, runAllSeeds, runSeedById, sanitizeTwFragment, scanAdapterDir, seedAllArticles, seedClipBridge, seedDocNote, seedHomeIndex, seedMenubarTheme, seedRenderRoute, seedSendToAgent, seedStarterDocs, seedUiStyles, seedWechatDocs, seedWechatPublish, stripMaskedSecrets, switchWiki, tailOf, tiddlywikiToolSummary, toIsoDateString, toolSignatureLines, unseedClipBridge, unseedWechatDocs, unseedWechatPublish, waitForFileWrite, workspaceMarkFromCwd, workspaceNameFromCwd, workspaceTagName, writeLocationState, writeSeedMarker, writeSessionSummary, writeWikiInfo };
|
|
12003
12063
|
|
|
12004
12064
|
//# sourceMappingURL=index.js.map
|