dsh-tiddlywiki 0.16.11 → 0.16.13
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/client.bundle.js +26 -84
- package/lib/client.js +26 -84
- package/lib/index.js +300 -477
- package/lib/index.js.map +1 -1
- package/package.json +1 -1
- package/src/client/editor-popup.ts +21 -12
- package/src/client/endpoints.ts +10 -0
- package/src/client/index.ts +0 -4
- package/src/client/knowledge-fab.ts +14 -8
- package/src/client/note-widget.ts +28 -9
- package/src/client/panel.ts +11 -6
- package/src/client/quick-note-dock.ts +1 -4
- package/src/client/session-summary.ts +29 -1
- package/src/client/settings-page.ts +7 -2
- package/src/client/sidebar-entry.ts +2 -1
- package/src/client/styles.ts +0 -58
- package/src/client/sync-button.ts +5 -3
- package/src/client/ui-config.ts +1 -1
- package/src/host/admin.ts +2 -24
- package/src/host/http.ts +57 -0
- package/src/host/routes.ts +25 -92
- package/src/host/seeds.ts +114 -167
- package/src/host/session-summary.ts +35 -15
- package/src/host/tools.ts +41 -33
- package/src/index.ts +20 -6
package/lib/index.js
CHANGED
|
@@ -6,18 +6,6 @@ import { execFile, spawn } from "node:child_process";
|
|
|
6
6
|
import { promisify } from "node:util";
|
|
7
7
|
import { createServer } from "node:net";
|
|
8
8
|
import { homedir } from "node:os";
|
|
9
|
-
//#region \0rolldown/runtime.js
|
|
10
|
-
var __defProp = Object.defineProperty;
|
|
11
|
-
var __exportAll = (all, no_symbols) => {
|
|
12
|
-
let target = {};
|
|
13
|
-
for (var name in all) __defProp(target, name, {
|
|
14
|
-
get: all[name],
|
|
15
|
-
enumerable: true
|
|
16
|
-
});
|
|
17
|
-
if (!no_symbols) __defProp(target, Symbol.toStringTag, { value: "Module" });
|
|
18
|
-
return target;
|
|
19
|
-
};
|
|
20
|
-
//#endregion
|
|
21
9
|
//#region src/host/git.ts
|
|
22
10
|
/**
|
|
23
11
|
* Git face (design doc §7, D11) — the ONLY place dsh-tiddlywiki shells out to
|
|
@@ -36,10 +24,6 @@ var __exportAll = (all, no_symbols) => {
|
|
|
36
24
|
*
|
|
37
25
|
* @module dsh-tiddlywiki/host/git
|
|
38
26
|
*/
|
|
39
|
-
var git_exports = /* @__PURE__ */ __exportAll({
|
|
40
|
-
AutoCommitter: () => AutoCommitter,
|
|
41
|
-
GitFace: () => GitFace
|
|
42
|
-
});
|
|
43
27
|
const execFileP = promisify(execFile);
|
|
44
28
|
/** Timeout for quick read-only queries. */
|
|
45
29
|
const QUICK_TIMEOUT_MS = 5e3;
|
|
@@ -706,9 +690,23 @@ function collectDescendantIds(nodes, out) {
|
|
|
706
690
|
}
|
|
707
691
|
}
|
|
708
692
|
}
|
|
693
|
+
/**
|
|
694
|
+
* 汇总条目会直接写进 `[[标题]]` wikitext——标题既要能安全渲染、又不能制造死链。
|
|
695
|
+
* 会话日志里常混入模型的示例/残缺写法(`#…`、`#标题`、带反引号的截断链接、
|
|
696
|
+
* 控制字符等),这类收进来会被 TW 渲染成「佚失条目」;一律过滤。
|
|
697
|
+
*/
|
|
698
|
+
function isPlausibleTitle(title) {
|
|
699
|
+
if (title.length === 0 || title.length > 300) return false;
|
|
700
|
+
if (title.trim().length === 0) return false;
|
|
701
|
+
if (/[\u0000-\u001f\u007f]/.test(title)) return false;
|
|
702
|
+
if (/[\]|`#]/.test(title)) return false;
|
|
703
|
+
if (title.startsWith("…") || title === "标题") return false;
|
|
704
|
+
return true;
|
|
705
|
+
}
|
|
709
706
|
/** 记录一篇笔记的触达;同标题合并(时间取最新、detail 取最新、subagent 取 AND)。 */
|
|
710
707
|
function record(map, entry) {
|
|
711
708
|
if (entry.title.startsWith("$:/")) return;
|
|
709
|
+
if (!isPlausibleTitle(entry.title)) return;
|
|
712
710
|
const prev = map.get(entry.title);
|
|
713
711
|
if (prev !== void 0) {
|
|
714
712
|
prev.time = Math.max(prev.time, entry.time);
|
|
@@ -720,7 +718,7 @@ function record(map, entry) {
|
|
|
720
718
|
}
|
|
721
719
|
/** 扫助手回复文本里的 `/dsh-tiddlywiki/tw/#标题` 引用链接 → 读取。 */
|
|
722
720
|
function scanRefs(text, time, subagent, collected) {
|
|
723
|
-
const re = /\/dsh-tiddlywiki\/tw\/#([
|
|
721
|
+
const re = /\/dsh-tiddlywiki\/tw\/#([^#)\s\]]+)/g;
|
|
724
722
|
let m;
|
|
725
723
|
while ((m = re.exec(text)) !== null) {
|
|
726
724
|
const raw = m[1] ?? "";
|
|
@@ -829,29 +827,33 @@ function scanSnapshot(snap, subagent, collected) {
|
|
|
829
827
|
}
|
|
830
828
|
}
|
|
831
829
|
}
|
|
832
|
-
/**
|
|
830
|
+
/** 并发(8 路)查询每篇笔记的当前状态(404 → 不存在/已删除)。 */
|
|
833
831
|
async function enrichTitles(client, titles) {
|
|
834
832
|
const out = /* @__PURE__ */ new Map();
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
833
|
+
const enquire = async (title) => {
|
|
834
|
+
try {
|
|
835
|
+
const t = await client.get(title);
|
|
836
|
+
if (t === void 0) return [title, {
|
|
837
|
+
exists: false,
|
|
838
|
+
tags: []
|
|
839
|
+
}];
|
|
842
840
|
const flat = typeof t.text === "string" ? t.text.replace(/\s+/g, " ").trim() : "";
|
|
843
|
-
|
|
841
|
+
return [title, {
|
|
844
842
|
exists: true,
|
|
845
843
|
tags: t.tags ?? [],
|
|
846
844
|
modified: typeof t.modified === "string" ? t.modified : void 0,
|
|
847
845
|
snippet: flat.length > 48 ? `${flat.slice(0, 48)}…` : flat
|
|
848
|
-
}
|
|
846
|
+
}];
|
|
847
|
+
} catch {
|
|
848
|
+
return [title, {
|
|
849
|
+
exists: false,
|
|
850
|
+
tags: []
|
|
851
|
+
}];
|
|
849
852
|
}
|
|
850
|
-
}
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
});
|
|
853
|
+
};
|
|
854
|
+
for (let i = 0; i < titles.length; i += 8) {
|
|
855
|
+
const batch = titles.slice(i, i + 8);
|
|
856
|
+
for (const [title, state] of await Promise.all(batch.map(enquire))) out.set(title, state);
|
|
855
857
|
}
|
|
856
858
|
return out;
|
|
857
859
|
}
|
|
@@ -978,19 +980,14 @@ async function writeSessionSummary(client, sq, sessionId) {
|
|
|
978
980
|
};
|
|
979
981
|
}
|
|
980
982
|
//#endregion
|
|
981
|
-
//#region src/host/
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
/** Max passthrough body (tiddler content can be large). */
|
|
983
|
+
//#region src/host/http.ts
|
|
984
|
+
/** Default cap for small JSON bodies (note/restart/config…). */
|
|
985
|
+
const MAX_JSON_BODY_BYTES = 2 * 1024 * 1024;
|
|
986
|
+
/** Cap for the /api passthrough body (tiddler content can be large). */
|
|
986
987
|
const MAX_PROXY_BODY_BYTES = 16 * 1024 * 1024;
|
|
987
|
-
/**
|
|
988
|
+
/** Cap for uploaded file bodies (/tw proxy, /upload). */
|
|
988
989
|
const MAX_UPLOAD_BYTES = 64 * 1024 * 1024;
|
|
989
|
-
|
|
990
|
-
* any Markdown in the note actually render in TW (a type-less tiddler is
|
|
991
|
-
* treated as plain wiki text and shows raw `![..]`/`[..]` instead). */
|
|
992
|
-
const NOTE_TYPE = "text/markdown";
|
|
993
|
-
function readBody$1(req, limit = MAX_BODY_BYTES) {
|
|
990
|
+
function readBodyStream(req, limit, encoding) {
|
|
994
991
|
return new Promise((resolveP, rejectP) => {
|
|
995
992
|
let size = 0;
|
|
996
993
|
const chunks = [];
|
|
@@ -1003,28 +1000,36 @@ function readBody$1(req, limit = MAX_BODY_BYTES) {
|
|
|
1003
1000
|
}
|
|
1004
1001
|
chunks.push(chunk);
|
|
1005
1002
|
});
|
|
1006
|
-
req.on("end", () =>
|
|
1003
|
+
req.on("end", () => {
|
|
1004
|
+
const buf = Buffer.concat(chunks);
|
|
1005
|
+
resolveP(encoding ? buf.toString("utf8") : buf);
|
|
1006
|
+
});
|
|
1007
1007
|
req.on("error", rejectP);
|
|
1008
1008
|
});
|
|
1009
1009
|
}
|
|
1010
|
-
/** Read a
|
|
1010
|
+
/** Read a JSON (utf8) request body up to `limit` bytes (default MAX_JSON_BODY_BYTES). */
|
|
1011
|
+
function readBody(req, limit = MAX_JSON_BODY_BYTES) {
|
|
1012
|
+
return readBodyStream(req, limit, true);
|
|
1013
|
+
}
|
|
1014
|
+
/** Read a raw (binary-safe) request body up to `limit` bytes (default MAX_UPLOAD_BYTES). */
|
|
1011
1015
|
function readBodyBuffer(req, limit = MAX_UPLOAD_BYTES) {
|
|
1012
|
-
return
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
req.destroy();
|
|
1020
|
-
return;
|
|
1021
|
-
}
|
|
1022
|
-
chunks.push(chunk);
|
|
1023
|
-
});
|
|
1024
|
-
req.on("end", () => resolveP(Buffer.concat(chunks)));
|
|
1025
|
-
req.on("error", rejectP);
|
|
1016
|
+
return readBodyStream(req, limit, false);
|
|
1017
|
+
}
|
|
1018
|
+
/** Write a JSON response (no-store, utf-8). */
|
|
1019
|
+
function json(res, payload, status = 200) {
|
|
1020
|
+
res.writeHead(status, {
|
|
1021
|
+
"content-type": "application/json; charset=utf-8",
|
|
1022
|
+
"cache-control": "no-store"
|
|
1026
1023
|
});
|
|
1024
|
+
res.end(JSON.stringify(payload));
|
|
1027
1025
|
}
|
|
1026
|
+
//#endregion
|
|
1027
|
+
//#region src/host/routes.ts
|
|
1028
|
+
const ROUTE_PREFIX = PATH_PREFIX;
|
|
1029
|
+
/** Tiddler type for quick-notes: Markdown, so the uploaded images/links and
|
|
1030
|
+
* any Markdown in the note actually render in TW (a type-less tiddler is
|
|
1031
|
+
* treated as plain wiki text and shows raw `![..]`/`[..]` instead). */
|
|
1032
|
+
const NOTE_TYPE = "text/markdown";
|
|
1028
1033
|
/** Header names forwarded to the upstream TW service by the proxy routes. */
|
|
1029
1034
|
const FORWARD_HEADER_NAMES = [
|
|
1030
1035
|
"accept",
|
|
@@ -1059,14 +1064,6 @@ function sanitizeUploadName(input) {
|
|
|
1059
1064
|
if (name.length > 160) return name.slice(0, 160);
|
|
1060
1065
|
return name;
|
|
1061
1066
|
}
|
|
1062
|
-
function json$1(res, payload, status = 200) {
|
|
1063
|
-
const body = JSON.stringify(payload);
|
|
1064
|
-
res.writeHead(status, {
|
|
1065
|
-
"content-type": "application/json; charset=utf-8",
|
|
1066
|
-
"cache-control": "no-store"
|
|
1067
|
-
});
|
|
1068
|
-
res.end(body);
|
|
1069
|
-
}
|
|
1070
1067
|
function pad(n) {
|
|
1071
1068
|
return n < 10 ? `0${n}` : String(n);
|
|
1072
1069
|
}
|
|
@@ -1138,7 +1135,7 @@ function registerRoutes(ctx, deps) {
|
|
|
1138
1135
|
} catch {
|
|
1139
1136
|
gitSummary = null;
|
|
1140
1137
|
}
|
|
1141
|
-
json
|
|
1138
|
+
json(res, {
|
|
1142
1139
|
ok: true,
|
|
1143
1140
|
...view,
|
|
1144
1141
|
twProxy: TW_PROXY_PATH,
|
|
@@ -1158,11 +1155,11 @@ function registerRoutes(ctx, deps) {
|
|
|
1158
1155
|
try {
|
|
1159
1156
|
let body = {};
|
|
1160
1157
|
try {
|
|
1161
|
-
body = JSON.parse(await readBody
|
|
1158
|
+
body = JSON.parse(await readBody(req));
|
|
1162
1159
|
} catch {}
|
|
1163
1160
|
const session = typeof body.session === "string" && body.session.trim().length > 0 ? body.session.trim() : "";
|
|
1164
1161
|
if (session.length === 0) {
|
|
1165
|
-
json
|
|
1162
|
+
json(res, {
|
|
1166
1163
|
ok: false,
|
|
1167
1164
|
error: "session is required"
|
|
1168
1165
|
}, 400);
|
|
@@ -1170,7 +1167,7 @@ function registerRoutes(ctx, deps) {
|
|
|
1170
1167
|
}
|
|
1171
1168
|
const client = deps.getClient();
|
|
1172
1169
|
if (client === void 0) {
|
|
1173
|
-
json
|
|
1170
|
+
json(res, {
|
|
1174
1171
|
ok: false,
|
|
1175
1172
|
error: "wiki service is not running"
|
|
1176
1173
|
}, 503);
|
|
@@ -1178,19 +1175,19 @@ function registerRoutes(ctx, deps) {
|
|
|
1178
1175
|
}
|
|
1179
1176
|
const sq = deps.getSessionQuery();
|
|
1180
1177
|
if (sq === void 0) {
|
|
1181
|
-
json
|
|
1178
|
+
json(res, {
|
|
1182
1179
|
ok: false,
|
|
1183
1180
|
error: "session query service unavailable"
|
|
1184
1181
|
}, 503);
|
|
1185
1182
|
return;
|
|
1186
1183
|
}
|
|
1187
|
-
json
|
|
1184
|
+
json(res, {
|
|
1188
1185
|
ok: true,
|
|
1189
1186
|
...await writeSessionSummary(client, sq, session),
|
|
1190
1187
|
twUrl: TW_PROXY_PATH
|
|
1191
1188
|
});
|
|
1192
1189
|
} catch (err) {
|
|
1193
|
-
json
|
|
1190
|
+
json(res, {
|
|
1194
1191
|
ok: false,
|
|
1195
1192
|
error: err instanceof Error ? err.message : String(err)
|
|
1196
1193
|
}, 500);
|
|
@@ -1207,7 +1204,7 @@ function registerRoutes(ctx, deps) {
|
|
|
1207
1204
|
try {
|
|
1208
1205
|
const sc = deps.getSessionController();
|
|
1209
1206
|
if (sc === void 0) {
|
|
1210
|
-
json
|
|
1207
|
+
json(res, {
|
|
1211
1208
|
ok: false,
|
|
1212
1209
|
error: "session service unavailable"
|
|
1213
1210
|
}, 503);
|
|
@@ -1220,7 +1217,7 @@ function registerRoutes(ctx, deps) {
|
|
|
1220
1217
|
const headers = await pers.list(AbortSignal.timeout(5e3));
|
|
1221
1218
|
for (const h of headers) if (typeof h.agentPreset === "string" && h.agentPreset.length > 0) presetById[h.id] = h.agentPreset;
|
|
1222
1219
|
} catch {}
|
|
1223
|
-
json
|
|
1220
|
+
json(res, {
|
|
1224
1221
|
ok: true,
|
|
1225
1222
|
items: (list.items ?? []).filter((s) => s.parentSessionId === void 0).map((s) => ({
|
|
1226
1223
|
sessionId: s.sessionId,
|
|
@@ -1232,40 +1229,47 @@ function registerRoutes(ctx, deps) {
|
|
|
1232
1229
|
})).sort((a, b) => b.updatedAt - a.updatedAt)
|
|
1233
1230
|
});
|
|
1234
1231
|
} catch (err) {
|
|
1235
|
-
json
|
|
1232
|
+
json(res, {
|
|
1236
1233
|
ok: false,
|
|
1237
1234
|
error: err instanceof Error ? err.message : String(err)
|
|
1238
1235
|
}, 500);
|
|
1239
1236
|
}
|
|
1240
1237
|
};
|
|
1241
1238
|
/**
|
|
1239
|
+
* Shared gate for the TW-side send-to-agent routes: feature switch
|
|
1240
|
+
* (`ui.sendToAgent.enabled`) then, when a shared token is configured, the
|
|
1241
|
+
* `x-send-to-agent-token` header must match. Returns false after writing the
|
|
1242
|
+
* error response — the caller just does `if (!guard(...)) return`.
|
|
1243
|
+
*/
|
|
1244
|
+
const guardSendToAgent = (req, res) => {
|
|
1245
|
+
if (!deps.sendToAgentEnabled()) {
|
|
1246
|
+
json(res, {
|
|
1247
|
+
ok: false,
|
|
1248
|
+
error: "send-to-agent is disabled"
|
|
1249
|
+
}, 403);
|
|
1250
|
+
return false;
|
|
1251
|
+
}
|
|
1252
|
+
const token = deps.sendToAgentToken().trim();
|
|
1253
|
+
if (token.length === 0) return true;
|
|
1254
|
+
const got = req.headers["x-send-to-agent-token"];
|
|
1255
|
+
if ((typeof got === "string" ? got : Array.isArray(got) ? got[0] ?? "" : "") === token) return true;
|
|
1256
|
+
json(res, {
|
|
1257
|
+
ok: false,
|
|
1258
|
+
error: "unauthorized"
|
|
1259
|
+
}, 401);
|
|
1260
|
+
return false;
|
|
1261
|
+
};
|
|
1262
|
+
/**
|
|
1242
1263
|
* GET /dsh-tiddlywiki/agent/modes — available "工作模式" (Agent presets) for
|
|
1243
1264
|
* the TW picker: id/name/description per preset plus the deployment default.
|
|
1244
1265
|
* Guards mirror the other agent routes (feature switch + optional token).
|
|
1245
1266
|
*/
|
|
1246
1267
|
const handleAgentModes = async (req, res) => {
|
|
1247
1268
|
try {
|
|
1248
|
-
if (!
|
|
1249
|
-
json$1(res, {
|
|
1250
|
-
ok: false,
|
|
1251
|
-
error: "send-to-agent is disabled"
|
|
1252
|
-
}, 403);
|
|
1253
|
-
return;
|
|
1254
|
-
}
|
|
1255
|
-
const token = deps.sendToAgentToken().trim();
|
|
1256
|
-
if (token.length > 0) {
|
|
1257
|
-
const got = req.headers["x-send-to-agent-token"];
|
|
1258
|
-
if ((typeof got === "string" ? got : Array.isArray(got) ? got[0] ?? "" : "") !== token) {
|
|
1259
|
-
json$1(res, {
|
|
1260
|
-
ok: false,
|
|
1261
|
-
error: "unauthorized"
|
|
1262
|
-
}, 401);
|
|
1263
|
-
return;
|
|
1264
|
-
}
|
|
1265
|
-
}
|
|
1269
|
+
if (!guardSendToAgent(req, res)) return;
|
|
1266
1270
|
const ap = deps.getAgentPresets();
|
|
1267
1271
|
if (ap === void 0) {
|
|
1268
|
-
json
|
|
1272
|
+
json(res, {
|
|
1269
1273
|
ok: false,
|
|
1270
1274
|
error: "agent presets service unavailable"
|
|
1271
1275
|
}, 503);
|
|
@@ -1288,7 +1292,7 @@ function registerRoutes(ctx, deps) {
|
|
|
1288
1292
|
} catch {
|
|
1289
1293
|
permissions = null;
|
|
1290
1294
|
}
|
|
1291
|
-
json
|
|
1295
|
+
json(res, {
|
|
1292
1296
|
ok: true,
|
|
1293
1297
|
defaultId: defaultId ?? null,
|
|
1294
1298
|
items: presets.map((p) => ({
|
|
@@ -1302,7 +1306,7 @@ function registerRoutes(ctx, deps) {
|
|
|
1302
1306
|
permissions
|
|
1303
1307
|
});
|
|
1304
1308
|
} catch (err) {
|
|
1305
|
-
json
|
|
1309
|
+
json(res, {
|
|
1306
1310
|
ok: false,
|
|
1307
1311
|
error: err instanceof Error ? err.message : String(err)
|
|
1308
1312
|
}, 500);
|
|
@@ -1315,29 +1319,12 @@ function registerRoutes(ctx, deps) {
|
|
|
1315
1319
|
*/
|
|
1316
1320
|
const handleAgentSend = async (req, res) => {
|
|
1317
1321
|
try {
|
|
1318
|
-
if (!
|
|
1319
|
-
|
|
1320
|
-
ok: false,
|
|
1321
|
-
error: "send-to-agent is disabled"
|
|
1322
|
-
}, 403);
|
|
1323
|
-
return;
|
|
1324
|
-
}
|
|
1325
|
-
const token = deps.sendToAgentToken().trim();
|
|
1326
|
-
if (token.length > 0) {
|
|
1327
|
-
const got = req.headers["x-send-to-agent-token"];
|
|
1328
|
-
if ((typeof got === "string" ? got : Array.isArray(got) ? got[0] ?? "" : "") !== token) {
|
|
1329
|
-
json$1(res, {
|
|
1330
|
-
ok: false,
|
|
1331
|
-
error: "unauthorized"
|
|
1332
|
-
}, 401);
|
|
1333
|
-
return;
|
|
1334
|
-
}
|
|
1335
|
-
}
|
|
1336
|
-
const body = JSON.parse(await readBody$1(req));
|
|
1322
|
+
if (!guardSendToAgent(req, res)) return;
|
|
1323
|
+
const body = JSON.parse(await readBody(req));
|
|
1337
1324
|
const sessionId = typeof body.sessionId === "string" && body.sessionId.trim().length > 0 ? body.sessionId.trim() : "";
|
|
1338
1325
|
const text = typeof body.text === "string" && body.text.trim().length > 0 ? body.text.trim() : "";
|
|
1339
1326
|
if (sessionId.length === 0 || text.length === 0) {
|
|
1340
|
-
json
|
|
1327
|
+
json(res, {
|
|
1341
1328
|
ok: false,
|
|
1342
1329
|
error: "sessionId and text are required"
|
|
1343
1330
|
}, 400);
|
|
@@ -1345,7 +1332,7 @@ function registerRoutes(ctx, deps) {
|
|
|
1345
1332
|
}
|
|
1346
1333
|
const sc = deps.getSessionController();
|
|
1347
1334
|
if (sc === void 0) {
|
|
1348
|
-
json
|
|
1335
|
+
json(res, {
|
|
1349
1336
|
ok: false,
|
|
1350
1337
|
error: "session service unavailable"
|
|
1351
1338
|
}, 503);
|
|
@@ -1361,13 +1348,13 @@ function registerRoutes(ctx, deps) {
|
|
|
1361
1348
|
text
|
|
1362
1349
|
}]
|
|
1363
1350
|
}, AbortSignal.timeout(2e4));
|
|
1364
|
-
json
|
|
1351
|
+
json(res, {
|
|
1365
1352
|
ok: true,
|
|
1366
1353
|
requestId,
|
|
1367
1354
|
sessionId
|
|
1368
1355
|
});
|
|
1369
1356
|
} catch (err) {
|
|
1370
|
-
json
|
|
1357
|
+
json(res, {
|
|
1371
1358
|
ok: false,
|
|
1372
1359
|
error: err instanceof Error ? err.message : String(err)
|
|
1373
1360
|
}, 500);
|
|
@@ -1395,39 +1382,22 @@ function registerRoutes(ctx, deps) {
|
|
|
1395
1382
|
*/
|
|
1396
1383
|
const handleAgentCreate = async (req, res) => {
|
|
1397
1384
|
try {
|
|
1398
|
-
if (!
|
|
1399
|
-
|
|
1400
|
-
ok: false,
|
|
1401
|
-
error: "send-to-agent is disabled"
|
|
1402
|
-
}, 403);
|
|
1403
|
-
return;
|
|
1404
|
-
}
|
|
1405
|
-
const token = deps.sendToAgentToken().trim();
|
|
1406
|
-
if (token.length > 0) {
|
|
1407
|
-
const got = req.headers["x-send-to-agent-token"];
|
|
1408
|
-
if ((typeof got === "string" ? got : Array.isArray(got) ? got[0] ?? "" : "") !== token) {
|
|
1409
|
-
json$1(res, {
|
|
1410
|
-
ok: false,
|
|
1411
|
-
error: "unauthorized"
|
|
1412
|
-
}, 401);
|
|
1413
|
-
return;
|
|
1414
|
-
}
|
|
1415
|
-
}
|
|
1416
|
-
const body = JSON.parse(await readBody$1(req));
|
|
1385
|
+
if (!guardSendToAgent(req, res)) return;
|
|
1386
|
+
const body = JSON.parse(await readBody(req));
|
|
1417
1387
|
const cwd = typeof body.cwd === "string" ? body.cwd.trim() : "";
|
|
1418
1388
|
const mode = typeof body.mode === "string" && body.mode.trim().length > 0 ? body.mode.trim() : void 0;
|
|
1419
1389
|
const permission = typeof body.permission === "string" && body.permission.trim().length > 0 ? body.permission.trim() : void 0;
|
|
1420
1390
|
const pp = deps.getPermissionPresets();
|
|
1421
1391
|
if (permission !== void 0) {
|
|
1422
1392
|
if (pp === void 0) {
|
|
1423
|
-
json
|
|
1393
|
+
json(res, {
|
|
1424
1394
|
ok: false,
|
|
1425
1395
|
error: "permission selected but the permission-presets service is unavailable"
|
|
1426
1396
|
}, 503);
|
|
1427
1397
|
return;
|
|
1428
1398
|
}
|
|
1429
1399
|
if (!pp.names.includes(permission)) {
|
|
1430
|
-
json
|
|
1400
|
+
json(res, {
|
|
1431
1401
|
ok: false,
|
|
1432
1402
|
error: `unknown permission preset "${permission}" (available: ${pp.names.join(", ")})`
|
|
1433
1403
|
}, 400);
|
|
@@ -1436,7 +1406,7 @@ function registerRoutes(ctx, deps) {
|
|
|
1436
1406
|
}
|
|
1437
1407
|
const sc = deps.getSessionController();
|
|
1438
1408
|
if (sc === void 0) {
|
|
1439
|
-
json
|
|
1409
|
+
json(res, {
|
|
1440
1410
|
ok: false,
|
|
1441
1411
|
error: "session service unavailable"
|
|
1442
1412
|
}, 503);
|
|
@@ -1467,7 +1437,7 @@ function registerRoutes(ctx, deps) {
|
|
|
1467
1437
|
}
|
|
1468
1438
|
} catch {}
|
|
1469
1439
|
}
|
|
1470
|
-
json
|
|
1440
|
+
json(res, {
|
|
1471
1441
|
ok: true,
|
|
1472
1442
|
sessionId: created.sessionId,
|
|
1473
1443
|
cwd: cwd || null,
|
|
@@ -1477,7 +1447,7 @@ function registerRoutes(ctx, deps) {
|
|
|
1477
1447
|
permissionApplied
|
|
1478
1448
|
});
|
|
1479
1449
|
} catch (err) {
|
|
1480
|
-
json
|
|
1450
|
+
json(res, {
|
|
1481
1451
|
ok: false,
|
|
1482
1452
|
error: err instanceof Error ? err.message : String(err)
|
|
1483
1453
|
}, 500);
|
|
@@ -1485,10 +1455,10 @@ function registerRoutes(ctx, deps) {
|
|
|
1485
1455
|
};
|
|
1486
1456
|
const handleNote = async (req, res) => {
|
|
1487
1457
|
try {
|
|
1488
|
-
const body = JSON.parse(await readBody
|
|
1458
|
+
const body = JSON.parse(await readBody(req));
|
|
1489
1459
|
const text = typeof body.text === "string" && body.text.trim().length > 0 ? body.text.trim() : null;
|
|
1490
1460
|
if (text === null) {
|
|
1491
|
-
json
|
|
1461
|
+
json(res, {
|
|
1492
1462
|
ok: false,
|
|
1493
1463
|
error: "text is required"
|
|
1494
1464
|
}, 400);
|
|
@@ -1496,7 +1466,7 @@ function registerRoutes(ctx, deps) {
|
|
|
1496
1466
|
}
|
|
1497
1467
|
const client = deps.getClient();
|
|
1498
1468
|
if (client === void 0) {
|
|
1499
|
-
json
|
|
1469
|
+
json(res, {
|
|
1500
1470
|
ok: false,
|
|
1501
1471
|
error: "wiki service is not running"
|
|
1502
1472
|
}, 503);
|
|
@@ -1511,7 +1481,7 @@ function registerRoutes(ctx, deps) {
|
|
|
1511
1481
|
type: NOTE_TYPE
|
|
1512
1482
|
});
|
|
1513
1483
|
deps.autoCommit();
|
|
1514
|
-
json
|
|
1484
|
+
json(res, {
|
|
1515
1485
|
ok: true,
|
|
1516
1486
|
title,
|
|
1517
1487
|
tag: tags.join(" "),
|
|
@@ -1520,7 +1490,7 @@ function registerRoutes(ctx, deps) {
|
|
|
1520
1490
|
type: NOTE_TYPE
|
|
1521
1491
|
});
|
|
1522
1492
|
} catch (err) {
|
|
1523
|
-
json
|
|
1493
|
+
json(res, {
|
|
1524
1494
|
ok: false,
|
|
1525
1495
|
error: err instanceof Error ? err.message : String(err)
|
|
1526
1496
|
}, 500);
|
|
@@ -1528,10 +1498,10 @@ function registerRoutes(ctx, deps) {
|
|
|
1528
1498
|
};
|
|
1529
1499
|
const handleEdit = async (req, res) => {
|
|
1530
1500
|
try {
|
|
1531
|
-
const body = JSON.parse(await readBody
|
|
1501
|
+
const body = JSON.parse(await readBody(req));
|
|
1532
1502
|
const client = deps.getClient();
|
|
1533
1503
|
if (client === void 0) {
|
|
1534
|
-
json
|
|
1504
|
+
json(res, {
|
|
1535
1505
|
ok: false,
|
|
1536
1506
|
error: "wiki service is not running"
|
|
1537
1507
|
}, 503);
|
|
@@ -1541,13 +1511,13 @@ function registerRoutes(ctx, deps) {
|
|
|
1541
1511
|
const tags = resolveTags(body, deps.noteDefaults().tag);
|
|
1542
1512
|
const result = await openInTwEditor(client, title, typeof body.text === "string" ? body.text : "", tags);
|
|
1543
1513
|
deps.autoCommit();
|
|
1544
|
-
json
|
|
1514
|
+
json(res, {
|
|
1545
1515
|
ok: true,
|
|
1546
1516
|
...result,
|
|
1547
1517
|
twUrl: TW_PROXY_PATH
|
|
1548
1518
|
});
|
|
1549
1519
|
} catch (err) {
|
|
1550
|
-
json
|
|
1520
|
+
json(res, {
|
|
1551
1521
|
ok: false,
|
|
1552
1522
|
error: err instanceof Error ? err.message : String(err)
|
|
1553
1523
|
}, 500);
|
|
@@ -1560,7 +1530,7 @@ function registerRoutes(ctx, deps) {
|
|
|
1560
1530
|
const handleTags = async (_req, res) => {
|
|
1561
1531
|
const client = deps.getClient();
|
|
1562
1532
|
if (client === void 0) {
|
|
1563
|
-
json
|
|
1533
|
+
json(res, {
|
|
1564
1534
|
ok: false,
|
|
1565
1535
|
error: "wiki service is not running"
|
|
1566
1536
|
}, 503);
|
|
@@ -1571,7 +1541,7 @@ function registerRoutes(ctx, deps) {
|
|
|
1571
1541
|
const counts = /* @__PURE__ */ new Map();
|
|
1572
1542
|
for (const item of items) for (const tag of item.tags ?? []) if (tag.length > 0 && !tag.startsWith("$:/")) counts.set(tag, (counts.get(tag) ?? 0) + 1);
|
|
1573
1543
|
const tags = [...counts.keys()].sort((a, b) => a.localeCompare(b, "zh"));
|
|
1574
|
-
json
|
|
1544
|
+
json(res, {
|
|
1575
1545
|
ok: true,
|
|
1576
1546
|
tags,
|
|
1577
1547
|
items: tags.map((tag) => ({
|
|
@@ -1580,7 +1550,7 @@ function registerRoutes(ctx, deps) {
|
|
|
1580
1550
|
}))
|
|
1581
1551
|
});
|
|
1582
1552
|
} catch (err) {
|
|
1583
|
-
json
|
|
1553
|
+
json(res, {
|
|
1584
1554
|
ok: false,
|
|
1585
1555
|
error: err instanceof Error ? err.message : String(err)
|
|
1586
1556
|
}, 500);
|
|
@@ -1590,7 +1560,7 @@ function registerRoutes(ctx, deps) {
|
|
|
1590
1560
|
const handleRecent = async (req, res) => {
|
|
1591
1561
|
const client = deps.getClient();
|
|
1592
1562
|
if (client === void 0) {
|
|
1593
|
-
json
|
|
1563
|
+
json(res, {
|
|
1594
1564
|
ok: false,
|
|
1595
1565
|
error: "wiki service is not running"
|
|
1596
1566
|
}, 503);
|
|
@@ -1600,7 +1570,7 @@ function registerRoutes(ctx, deps) {
|
|
|
1600
1570
|
const url = new URL(req.url ?? "/", "http://127.0.0.1");
|
|
1601
1571
|
const limitRaw = Number(url.searchParams.get("limit") ?? 15);
|
|
1602
1572
|
const limit = Number.isFinite(limitRaw) ? Math.max(1, Math.min(Math.floor(limitRaw), 200)) : 15;
|
|
1603
|
-
json
|
|
1573
|
+
json(res, {
|
|
1604
1574
|
ok: true,
|
|
1605
1575
|
limit,
|
|
1606
1576
|
items: (await client.recent(limit)).map((t) => ({
|
|
@@ -1611,7 +1581,7 @@ function registerRoutes(ctx, deps) {
|
|
|
1611
1581
|
}))
|
|
1612
1582
|
});
|
|
1613
1583
|
} catch (err) {
|
|
1614
|
-
json
|
|
1584
|
+
json(res, {
|
|
1615
1585
|
ok: false,
|
|
1616
1586
|
error: err instanceof Error ? err.message : String(err)
|
|
1617
1587
|
}, 500);
|
|
@@ -1621,7 +1591,7 @@ function registerRoutes(ctx, deps) {
|
|
|
1621
1591
|
const handleGet = async (req, res) => {
|
|
1622
1592
|
const client = deps.getClient();
|
|
1623
1593
|
if (client === void 0) {
|
|
1624
|
-
json
|
|
1594
|
+
json(res, {
|
|
1625
1595
|
ok: false,
|
|
1626
1596
|
error: "wiki service is not running"
|
|
1627
1597
|
}, 503);
|
|
@@ -1630,7 +1600,7 @@ function registerRoutes(ctx, deps) {
|
|
|
1630
1600
|
try {
|
|
1631
1601
|
const title = new URL(req.url ?? "/", "http://127.0.0.1").searchParams.get("title") ?? "";
|
|
1632
1602
|
if (title.length === 0) {
|
|
1633
|
-
json
|
|
1603
|
+
json(res, {
|
|
1634
1604
|
ok: false,
|
|
1635
1605
|
error: "missing title"
|
|
1636
1606
|
}, 400);
|
|
@@ -1638,7 +1608,7 @@ function registerRoutes(ctx, deps) {
|
|
|
1638
1608
|
}
|
|
1639
1609
|
const t = await client.get(title);
|
|
1640
1610
|
if (t === void 0) {
|
|
1641
|
-
json
|
|
1611
|
+
json(res, {
|
|
1642
1612
|
ok: false,
|
|
1643
1613
|
notFound: true,
|
|
1644
1614
|
title
|
|
@@ -1650,7 +1620,7 @@ function registerRoutes(ctx, deps) {
|
|
|
1650
1620
|
if (k === "title" || k === "text" || k === "tags") continue;
|
|
1651
1621
|
fields[k] = v;
|
|
1652
1622
|
}
|
|
1653
|
-
json
|
|
1623
|
+
json(res, {
|
|
1654
1624
|
ok: true,
|
|
1655
1625
|
title: t.title,
|
|
1656
1626
|
text: t.text ?? "",
|
|
@@ -1660,7 +1630,7 @@ function registerRoutes(ctx, deps) {
|
|
|
1660
1630
|
fields
|
|
1661
1631
|
});
|
|
1662
1632
|
} catch (err) {
|
|
1663
|
-
json
|
|
1633
|
+
json(res, {
|
|
1664
1634
|
ok: false,
|
|
1665
1635
|
error: err instanceof Error ? err.message : String(err)
|
|
1666
1636
|
}, 500);
|
|
@@ -1675,7 +1645,7 @@ function registerRoutes(ctx, deps) {
|
|
|
1675
1645
|
const handleSearch = async (req, res) => {
|
|
1676
1646
|
const client = deps.getClient();
|
|
1677
1647
|
if (client === void 0) {
|
|
1678
|
-
json
|
|
1648
|
+
json(res, {
|
|
1679
1649
|
ok: false,
|
|
1680
1650
|
error: "wiki service is not running"
|
|
1681
1651
|
}, 503);
|
|
@@ -1697,7 +1667,7 @@ function registerRoutes(ctx, deps) {
|
|
|
1697
1667
|
type,
|
|
1698
1668
|
limit
|
|
1699
1669
|
});
|
|
1700
|
-
json
|
|
1670
|
+
json(res, {
|
|
1701
1671
|
ok: true,
|
|
1702
1672
|
query,
|
|
1703
1673
|
tags,
|
|
@@ -1714,7 +1684,7 @@ function registerRoutes(ctx, deps) {
|
|
|
1714
1684
|
}))
|
|
1715
1685
|
});
|
|
1716
1686
|
} catch (err) {
|
|
1717
|
-
json
|
|
1687
|
+
json(res, {
|
|
1718
1688
|
ok: false,
|
|
1719
1689
|
error: err instanceof Error ? err.message : String(err)
|
|
1720
1690
|
}, 500);
|
|
@@ -1723,12 +1693,12 @@ function registerRoutes(ctx, deps) {
|
|
|
1723
1693
|
const handleRestart = async (_req, res) => {
|
|
1724
1694
|
try {
|
|
1725
1695
|
await deps.server.restart();
|
|
1726
|
-
json
|
|
1696
|
+
json(res, {
|
|
1727
1697
|
ok: true,
|
|
1728
1698
|
status: deps.server.status().status
|
|
1729
1699
|
});
|
|
1730
1700
|
} catch (err) {
|
|
1731
|
-
json
|
|
1701
|
+
json(res, {
|
|
1732
1702
|
ok: false,
|
|
1733
1703
|
error: err instanceof Error ? err.message : String(err)
|
|
1734
1704
|
}, 500);
|
|
@@ -1752,7 +1722,7 @@ function registerRoutes(ctx, deps) {
|
|
|
1752
1722
|
try {
|
|
1753
1723
|
const pulled = await deps.git.pull(dir);
|
|
1754
1724
|
if (!pulled.ok) {
|
|
1755
|
-
json
|
|
1725
|
+
json(res, {
|
|
1756
1726
|
ok: false,
|
|
1757
1727
|
action: "sync",
|
|
1758
1728
|
message: pulled.message,
|
|
@@ -1772,7 +1742,7 @@ function registerRoutes(ctx, deps) {
|
|
|
1772
1742
|
const committed = await deps.git.commit(dir, `sync ${(/* @__PURE__ */ new Date()).toISOString()}`);
|
|
1773
1743
|
const pushed = await deps.git.push(dir);
|
|
1774
1744
|
const fresh = await status();
|
|
1775
|
-
json
|
|
1745
|
+
json(res, {
|
|
1776
1746
|
ok: pushed.ok,
|
|
1777
1747
|
action: "sync",
|
|
1778
1748
|
message: pushed.ok ? "同步完成" : pushed.message,
|
|
@@ -1786,7 +1756,7 @@ function registerRoutes(ctx, deps) {
|
|
|
1786
1756
|
lastSync: (/* @__PURE__ */ new Date()).toISOString()
|
|
1787
1757
|
}, pushed.ok ? 200 : 502);
|
|
1788
1758
|
} catch (err) {
|
|
1789
|
-
json
|
|
1759
|
+
json(res, {
|
|
1790
1760
|
ok: false,
|
|
1791
1761
|
error: err instanceof Error ? err.message : String(err)
|
|
1792
1762
|
}, 500);
|
|
@@ -1812,7 +1782,7 @@ function registerRoutes(ctx, deps) {
|
|
|
1812
1782
|
name = sanitizeUploadName(decoded);
|
|
1813
1783
|
}
|
|
1814
1784
|
if (name.length === 0) {
|
|
1815
|
-
json
|
|
1785
|
+
json(res, {
|
|
1816
1786
|
ok: false,
|
|
1817
1787
|
error: "missing or invalid filename"
|
|
1818
1788
|
}, 400);
|
|
@@ -1833,7 +1803,7 @@ function registerRoutes(ctx, deps) {
|
|
|
1833
1803
|
}
|
|
1834
1804
|
await writeFile(join(filesDir, candidate), buf);
|
|
1835
1805
|
deps.autoCommit();
|
|
1836
|
-
json
|
|
1806
|
+
json(res, {
|
|
1837
1807
|
ok: true,
|
|
1838
1808
|
name: candidate,
|
|
1839
1809
|
path: `files/${candidate}`,
|
|
@@ -1842,7 +1812,7 @@ function registerRoutes(ctx, deps) {
|
|
|
1842
1812
|
type: req.headers["content-type"] ?? "application/octet-stream"
|
|
1843
1813
|
});
|
|
1844
1814
|
} catch (err) {
|
|
1845
|
-
json
|
|
1815
|
+
json(res, {
|
|
1846
1816
|
ok: false,
|
|
1847
1817
|
error: err instanceof Error ? err.message : String(err)
|
|
1848
1818
|
}, err instanceof Error && /too large/.test(err.message) ? 413 : 500);
|
|
@@ -1851,7 +1821,7 @@ function registerRoutes(ctx, deps) {
|
|
|
1851
1821
|
/** Passthrough /dsh-tiddlywiki/api/<rest> → TW root /<rest>. */
|
|
1852
1822
|
const handleApiProxy = async (req, res) => {
|
|
1853
1823
|
if (deps.getClient() === void 0) {
|
|
1854
|
-
json
|
|
1824
|
+
json(res, {
|
|
1855
1825
|
ok: false,
|
|
1856
1826
|
error: "wiki service is not running"
|
|
1857
1827
|
}, 503);
|
|
@@ -1870,7 +1840,7 @@ function registerRoutes(ctx, deps) {
|
|
|
1870
1840
|
headers,
|
|
1871
1841
|
signal: AbortSignal.timeout(15e3)
|
|
1872
1842
|
};
|
|
1873
|
-
if (method === "PUT" || method === "POST") init.body = await readBody
|
|
1843
|
+
if (method === "PUT" || method === "POST") init.body = await readBody(req, MAX_PROXY_BODY_BYTES);
|
|
1874
1844
|
const upstream = await fetch(`${deps.server.url}${rest}${url.search}`, init);
|
|
1875
1845
|
const data = await upstream.text();
|
|
1876
1846
|
res.writeHead(upstream.status, {
|
|
@@ -1879,7 +1849,7 @@ function registerRoutes(ctx, deps) {
|
|
|
1879
1849
|
});
|
|
1880
1850
|
res.end(data);
|
|
1881
1851
|
} catch (err) {
|
|
1882
|
-
json
|
|
1852
|
+
json(res, {
|
|
1883
1853
|
ok: false,
|
|
1884
1854
|
error: err instanceof Error ? err.message : String(err)
|
|
1885
1855
|
}, 502);
|
|
@@ -1896,7 +1866,7 @@ function registerRoutes(ctx, deps) {
|
|
|
1896
1866
|
*/
|
|
1897
1867
|
const handleTwProxy = async (req, res) => {
|
|
1898
1868
|
if (deps.getClient() === void 0) {
|
|
1899
|
-
json
|
|
1869
|
+
json(res, {
|
|
1900
1870
|
ok: false,
|
|
1901
1871
|
error: "wiki service is not running"
|
|
1902
1872
|
}, 503);
|
|
@@ -1935,7 +1905,7 @@ function registerRoutes(ctx, deps) {
|
|
|
1935
1905
|
res.end();
|
|
1936
1906
|
return;
|
|
1937
1907
|
}
|
|
1938
|
-
json
|
|
1908
|
+
json(res, {
|
|
1939
1909
|
ok: false,
|
|
1940
1910
|
error: err instanceof Error ? err.message : String(err)
|
|
1941
1911
|
}, 502);
|
|
@@ -2296,30 +2266,6 @@ async function ensureLanguage(wikiPath, twRoot, lang) {
|
|
|
2296
2266
|
await writeWikiInfo(wikiPath, info);
|
|
2297
2267
|
return true;
|
|
2298
2268
|
}
|
|
2299
|
-
function json(res, payload, status = 200) {
|
|
2300
|
-
res.writeHead(status, {
|
|
2301
|
-
"content-type": "application/json; charset=utf-8",
|
|
2302
|
-
"cache-control": "no-store"
|
|
2303
|
-
});
|
|
2304
|
-
res.end(JSON.stringify(payload));
|
|
2305
|
-
}
|
|
2306
|
-
async function readBody(req, limit = 1024 * 1024) {
|
|
2307
|
-
return new Promise((resolveP, rejectP) => {
|
|
2308
|
-
let size = 0;
|
|
2309
|
-
const chunks = [];
|
|
2310
|
-
req.on("data", (chunk) => {
|
|
2311
|
-
size += chunk.length;
|
|
2312
|
-
if (size > limit) {
|
|
2313
|
-
rejectP(/* @__PURE__ */ new Error("body too large"));
|
|
2314
|
-
req.destroy();
|
|
2315
|
-
return;
|
|
2316
|
-
}
|
|
2317
|
-
chunks.push(chunk);
|
|
2318
|
-
});
|
|
2319
|
-
req.on("end", () => resolveP(Buffer.concat(chunks).toString("utf8")));
|
|
2320
|
-
req.on("error", rejectP);
|
|
2321
|
-
});
|
|
2322
|
-
}
|
|
2323
2269
|
function registerAdminRoutes(ctx, deps) {
|
|
2324
2270
|
const handleState = async (_req, res) => {
|
|
2325
2271
|
try {
|
|
@@ -2327,7 +2273,6 @@ function registerAdminRoutes(ctx, deps) {
|
|
|
2327
2273
|
const [info, catalog] = await Promise.all([readWikiInfo(wikiPath), bundledCatalog(deps.twRoot())]);
|
|
2328
2274
|
let git = null;
|
|
2329
2275
|
try {
|
|
2330
|
-
const { GitFace } = await Promise.resolve().then(() => git_exports);
|
|
2331
2276
|
git = await new GitFace().status(wikiPath);
|
|
2332
2277
|
} catch {
|
|
2333
2278
|
git = null;
|
|
@@ -3154,130 +3099,107 @@ const removedDetail = (id, removed) => ({
|
|
|
3154
3099
|
wrote: false,
|
|
3155
3100
|
detail: removed.length > 0 ? `已移除:${removed.join("、")}` : "本就不存在,无需移除"
|
|
3156
3101
|
});
|
|
3102
|
+
/**
|
|
3103
|
+
* Define a seed from a small spec, killing the id/title/description
|
|
3104
|
+
* triplication and the try/catch boilerplate every registry entry used to
|
|
3105
|
+
* repeat by hand:
|
|
3106
|
+
* - `presentTitle` → default check: is that tiddler present?
|
|
3107
|
+
* - `check` → custom presence check (overrides presentTitle).
|
|
3108
|
+
* - `write` → built-in content writer; true = (re)written this call.
|
|
3109
|
+
* - `run` → custom runner (overrides the standard write wrapper;
|
|
3110
|
+
* e.g. tw-web-host honors a user-chosen host value).
|
|
3111
|
+
* - `unseed` → remove the seeded tiddlers + markers (removable seeds).
|
|
3112
|
+
*/
|
|
3113
|
+
function defineSeed(meta, impl) {
|
|
3114
|
+
const { id, title, description, core } = meta;
|
|
3115
|
+
const removable = !core;
|
|
3116
|
+
const check = impl.check ?? (async (ctx) => {
|
|
3117
|
+
const present = await presentOf(ctx, impl.presentTitle ?? "");
|
|
3118
|
+
return {
|
|
3119
|
+
id,
|
|
3120
|
+
title,
|
|
3121
|
+
description,
|
|
3122
|
+
present,
|
|
3123
|
+
removable,
|
|
3124
|
+
detail: present ? "已存在" : "缺失"
|
|
3125
|
+
};
|
|
3126
|
+
});
|
|
3127
|
+
const run = impl.run ?? (async (ctx, force) => {
|
|
3128
|
+
try {
|
|
3129
|
+
const wrote = await impl.write(ctx.client, { force });
|
|
3130
|
+
return {
|
|
3131
|
+
id,
|
|
3132
|
+
ok: true,
|
|
3133
|
+
wrote,
|
|
3134
|
+
detail: wrote ? force ? "已重新初始化" : "已写入" : force ? "内容已是最新(未重写)" : "已存在,跳过"
|
|
3135
|
+
};
|
|
3136
|
+
} catch (err) {
|
|
3137
|
+
return {
|
|
3138
|
+
id,
|
|
3139
|
+
ok: false,
|
|
3140
|
+
wrote: false,
|
|
3141
|
+
error: err instanceof Error ? err.message : String(err)
|
|
3142
|
+
};
|
|
3143
|
+
}
|
|
3144
|
+
});
|
|
3145
|
+
const remove = impl.unseed === void 0 ? void 0 : async (ctx) => {
|
|
3146
|
+
try {
|
|
3147
|
+
return removedDetail(id, (await impl.unseed(ctx.client)).removed);
|
|
3148
|
+
} catch (err) {
|
|
3149
|
+
return {
|
|
3150
|
+
id,
|
|
3151
|
+
ok: false,
|
|
3152
|
+
wrote: false,
|
|
3153
|
+
error: err instanceof Error ? err.message : String(err)
|
|
3154
|
+
};
|
|
3155
|
+
}
|
|
3156
|
+
};
|
|
3157
|
+
return {
|
|
3158
|
+
id,
|
|
3159
|
+
title,
|
|
3160
|
+
description,
|
|
3161
|
+
core,
|
|
3162
|
+
check,
|
|
3163
|
+
run,
|
|
3164
|
+
...remove === void 0 ? {} : { remove }
|
|
3165
|
+
};
|
|
3166
|
+
}
|
|
3157
3167
|
/** The full registry, in display order. */
|
|
3158
3168
|
const SEED_DEFS = [
|
|
3159
|
-
{
|
|
3169
|
+
defineSeed({
|
|
3160
3170
|
id: "doc-note",
|
|
3161
3171
|
title: "插件说明笔记",
|
|
3162
3172
|
description: "「dsh-tiddlywiki 插件说明」——新 wiki 首启自动写入的入门说明(ONE-SHOT,用户可改可删)。",
|
|
3163
|
-
core: false
|
|
3164
|
-
|
|
3165
|
-
|
|
3166
|
-
|
|
3167
|
-
|
|
3168
|
-
|
|
3169
|
-
|
|
3170
|
-
present,
|
|
3171
|
-
removable: true,
|
|
3172
|
-
detail: present ? "已存在" : "缺失"
|
|
3173
|
-
};
|
|
3174
|
-
},
|
|
3175
|
-
run: async (ctx, force) => {
|
|
3176
|
-
try {
|
|
3177
|
-
const wrote = await seedDocNote(ctx.client, { force });
|
|
3178
|
-
return {
|
|
3179
|
-
id: "doc-note",
|
|
3180
|
-
ok: true,
|
|
3181
|
-
wrote,
|
|
3182
|
-
detail: wrote ? force ? "已重新初始化" : "已写入" : force ? "内容已是最新(未重写)" : "已存在,跳过"
|
|
3183
|
-
};
|
|
3184
|
-
} catch (err) {
|
|
3185
|
-
return {
|
|
3186
|
-
id: "doc-note",
|
|
3187
|
-
ok: false,
|
|
3188
|
-
wrote: false,
|
|
3189
|
-
error: err instanceof Error ? err.message : String(err)
|
|
3190
|
-
};
|
|
3191
|
-
}
|
|
3192
|
-
},
|
|
3193
|
-
remove: async (ctx) => {
|
|
3194
|
-
try {
|
|
3195
|
-
return removedDetail("doc-note", (await unseedDocNote(ctx.client)).removed);
|
|
3196
|
-
} catch (err) {
|
|
3197
|
-
return {
|
|
3198
|
-
id: "doc-note",
|
|
3199
|
-
ok: false,
|
|
3200
|
-
wrote: false,
|
|
3201
|
-
error: err instanceof Error ? err.message : String(err)
|
|
3202
|
-
};
|
|
3203
|
-
}
|
|
3204
|
-
}
|
|
3205
|
-
},
|
|
3206
|
-
{
|
|
3173
|
+
core: false
|
|
3174
|
+
}, {
|
|
3175
|
+
presentTitle: DOC_NOTE_TITLE,
|
|
3176
|
+
write: seedDocNote,
|
|
3177
|
+
unseed: unseedDocNote
|
|
3178
|
+
}),
|
|
3179
|
+
defineSeed({
|
|
3207
3180
|
id: "send-to-agent",
|
|
3208
3181
|
title: "「发送给 Agent」按钮",
|
|
3209
3182
|
description: "TW 笔记工具栏「发送给 Agent」按钮插件($:/plugins/dsh/send-to-agent)——把笔记一键注入 DSH 会话。",
|
|
3210
|
-
core: true
|
|
3211
|
-
|
|
3212
|
-
|
|
3213
|
-
|
|
3214
|
-
|
|
3215
|
-
|
|
3216
|
-
description: "TW 笔记工具栏「发送给 Agent」按钮插件($:/plugins/dsh/send-to-agent)——把笔记一键注入 DSH 会话。",
|
|
3217
|
-
present,
|
|
3218
|
-
removable: false,
|
|
3219
|
-
detail: present ? "已存在" : "缺失"
|
|
3220
|
-
};
|
|
3221
|
-
},
|
|
3222
|
-
run: async (ctx, force) => {
|
|
3223
|
-
try {
|
|
3224
|
-
const wrote = await seedSendToAgent(ctx.client, { force });
|
|
3225
|
-
return {
|
|
3226
|
-
id: "send-to-agent",
|
|
3227
|
-
ok: true,
|
|
3228
|
-
wrote,
|
|
3229
|
-
detail: wrote ? force ? "已重新初始化" : "已写入" : force ? "内容已是最新(未重写)" : "已存在,跳过"
|
|
3230
|
-
};
|
|
3231
|
-
} catch (err) {
|
|
3232
|
-
return {
|
|
3233
|
-
id: "send-to-agent",
|
|
3234
|
-
ok: false,
|
|
3235
|
-
wrote: false,
|
|
3236
|
-
error: err instanceof Error ? err.message : String(err)
|
|
3237
|
-
};
|
|
3238
|
-
}
|
|
3239
|
-
}
|
|
3240
|
-
},
|
|
3241
|
-
{
|
|
3183
|
+
core: true
|
|
3184
|
+
}, {
|
|
3185
|
+
presentTitle: SEND_TO_AGENT_PLUGIN_TITLE,
|
|
3186
|
+
write: seedSendToAgent
|
|
3187
|
+
}),
|
|
3188
|
+
defineSeed({
|
|
3242
3189
|
id: "render-route",
|
|
3243
3190
|
title: "原生渲染路由(/render)",
|
|
3244
3191
|
description: "TW 服务端路由插件($:/plugins/dsh/render,server-routes/render.js)——把 wiki 文本在运行中的 TW 里原生渲染成 HTML 片段,回复流工具卡与 wiki 链接跳转依赖它。seed 写入后需重启 TW 使路由生效。",
|
|
3245
|
-
core: true
|
|
3246
|
-
|
|
3247
|
-
|
|
3248
|
-
|
|
3249
|
-
|
|
3250
|
-
|
|
3251
|
-
description: "TW 服务端路由插件($:/plugins/dsh/render,server-routes/render.js)——把 wiki 文本在运行中的 TW 里原生渲染成 HTML 片段,回复流工具卡与 wiki 链接跳转依赖它。seed 写入后需重启 TW 使路由生效。",
|
|
3252
|
-
present,
|
|
3253
|
-
removable: false,
|
|
3254
|
-
detail: present ? "已存在" : "缺失"
|
|
3255
|
-
};
|
|
3256
|
-
},
|
|
3257
|
-
run: async (ctx, force) => {
|
|
3258
|
-
try {
|
|
3259
|
-
const wrote = await seedRenderRoute(ctx.client, { force });
|
|
3260
|
-
return {
|
|
3261
|
-
id: "render-route",
|
|
3262
|
-
ok: true,
|
|
3263
|
-
wrote,
|
|
3264
|
-
detail: wrote ? force ? "已重新初始化" : "已写入" : force ? "内容已是最新(未重写)" : "已存在,跳过"
|
|
3265
|
-
};
|
|
3266
|
-
} catch (err) {
|
|
3267
|
-
return {
|
|
3268
|
-
id: "render-route",
|
|
3269
|
-
ok: false,
|
|
3270
|
-
wrote: false,
|
|
3271
|
-
error: err instanceof Error ? err.message : String(err)
|
|
3272
|
-
};
|
|
3273
|
-
}
|
|
3274
|
-
}
|
|
3275
|
-
},
|
|
3276
|
-
{
|
|
3192
|
+
core: true
|
|
3193
|
+
}, {
|
|
3194
|
+
presentTitle: RENDER_PLUGIN_TITLE,
|
|
3195
|
+
write: seedRenderRoute
|
|
3196
|
+
}),
|
|
3197
|
+
defineSeed({
|
|
3277
3198
|
id: "home-index",
|
|
3278
3199
|
title: "首页(主页 / 所有标签 / 标签笔记)",
|
|
3279
3200
|
description: "默认主页:四象限待办 + 「所有标签」「所有文章」入口;所有标签:标签统计 + Agent 区块(纯 Agent / Agent+人工);标签笔记:按标签浏览。系统提示承诺的首页由这里 seed,主页同时写入 $:/DefaultTiddlers。",
|
|
3280
|
-
core: false
|
|
3201
|
+
core: false
|
|
3202
|
+
}, {
|
|
3281
3203
|
check: async (ctx) => {
|
|
3282
3204
|
const missing = [];
|
|
3283
3205
|
for (const item of HOME_INDEX_ITEMS) if (!await presentOf(ctx, item.title)) missing.push(item.title);
|
|
@@ -3290,136 +3212,35 @@ const SEED_DEFS = [
|
|
|
3290
3212
|
detail: missing.length === 0 ? "已存在" : `缺失:${missing.join("、")}`
|
|
3291
3213
|
};
|
|
3292
3214
|
},
|
|
3293
|
-
|
|
3294
|
-
|
|
3295
|
-
|
|
3296
|
-
|
|
3297
|
-
id: "home-index",
|
|
3298
|
-
ok: true,
|
|
3299
|
-
wrote,
|
|
3300
|
-
detail: wrote ? force ? "已重新初始化" : "已写入" : force ? "内容已是最新(未重写)" : "已存在,跳过"
|
|
3301
|
-
};
|
|
3302
|
-
} catch (err) {
|
|
3303
|
-
return {
|
|
3304
|
-
id: "home-index",
|
|
3305
|
-
ok: false,
|
|
3306
|
-
wrote: false,
|
|
3307
|
-
error: err instanceof Error ? err.message : String(err)
|
|
3308
|
-
};
|
|
3309
|
-
}
|
|
3310
|
-
},
|
|
3311
|
-
remove: async (ctx) => {
|
|
3312
|
-
try {
|
|
3313
|
-
return removedDetail("home-index", (await unseedHomeIndex(ctx.client)).removed);
|
|
3314
|
-
} catch (err) {
|
|
3315
|
-
return {
|
|
3316
|
-
id: "home-index",
|
|
3317
|
-
ok: false,
|
|
3318
|
-
wrote: false,
|
|
3319
|
-
error: err instanceof Error ? err.message : String(err)
|
|
3320
|
-
};
|
|
3321
|
-
}
|
|
3322
|
-
}
|
|
3323
|
-
},
|
|
3324
|
-
{
|
|
3215
|
+
write: seedHomeIndex,
|
|
3216
|
+
unseed: unseedHomeIndex
|
|
3217
|
+
}),
|
|
3218
|
+
defineSeed({
|
|
3325
3219
|
id: "all-articles",
|
|
3326
3220
|
title: "所有文章(两列分页总览)",
|
|
3327
3221
|
description: "「所有文章」——全部条目分两列(🤖 Agent 撰写 / 👤 人工·人类)各自分页展示。每页条数取插件设置 ui.allArticles.pageSize(默认 10)。",
|
|
3328
|
-
core: false
|
|
3329
|
-
|
|
3330
|
-
|
|
3331
|
-
|
|
3332
|
-
|
|
3333
|
-
|
|
3334
|
-
|
|
3335
|
-
present,
|
|
3336
|
-
removable: true,
|
|
3337
|
-
detail: present ? "已存在" : "缺失"
|
|
3338
|
-
};
|
|
3339
|
-
},
|
|
3340
|
-
run: async (ctx, force) => {
|
|
3341
|
-
try {
|
|
3342
|
-
const wrote = await seedAllArticles(ctx.client, { force });
|
|
3343
|
-
return {
|
|
3344
|
-
id: "all-articles",
|
|
3345
|
-
ok: true,
|
|
3346
|
-
wrote,
|
|
3347
|
-
detail: wrote ? force ? "已重新初始化" : "已写入" : force ? "内容已是最新(未重写)" : "已存在,跳过"
|
|
3348
|
-
};
|
|
3349
|
-
} catch (err) {
|
|
3350
|
-
return {
|
|
3351
|
-
id: "all-articles",
|
|
3352
|
-
ok: false,
|
|
3353
|
-
wrote: false,
|
|
3354
|
-
error: err instanceof Error ? err.message : String(err)
|
|
3355
|
-
};
|
|
3356
|
-
}
|
|
3357
|
-
},
|
|
3358
|
-
remove: async (ctx) => {
|
|
3359
|
-
try {
|
|
3360
|
-
return removedDetail("all-articles", (await unseedAllArticles(ctx.client)).removed);
|
|
3361
|
-
} catch (err) {
|
|
3362
|
-
return {
|
|
3363
|
-
id: "all-articles",
|
|
3364
|
-
ok: false,
|
|
3365
|
-
wrote: false,
|
|
3366
|
-
error: err instanceof Error ? err.message : String(err)
|
|
3367
|
-
};
|
|
3368
|
-
}
|
|
3369
|
-
}
|
|
3370
|
-
},
|
|
3371
|
-
{
|
|
3222
|
+
core: false
|
|
3223
|
+
}, {
|
|
3224
|
+
presentTitle: ALL_ARTICLES_TITLE,
|
|
3225
|
+
write: seedAllArticles,
|
|
3226
|
+
unseed: unseedAllArticles
|
|
3227
|
+
}),
|
|
3228
|
+
defineSeed({
|
|
3372
3229
|
id: "menubar-theme",
|
|
3373
3230
|
title: "menubar 顶栏主题自适应",
|
|
3374
3231
|
description: "样式表覆盖($:/plugins/dsh-tiddlywiki/menubar-theme,tag $:/tags/Stylesheet)——把 tiddlywiki/menubar 顶栏从「默认色映射的蓝色」改为跟随当前 palette 的 background/foreground,随 DSH 主题切换($:/palette 翻转)自动换色。",
|
|
3375
|
-
core: false
|
|
3376
|
-
|
|
3377
|
-
|
|
3378
|
-
|
|
3379
|
-
|
|
3380
|
-
|
|
3381
|
-
|
|
3382
|
-
present,
|
|
3383
|
-
removable: true,
|
|
3384
|
-
detail: present ? "已存在" : "缺失"
|
|
3385
|
-
};
|
|
3386
|
-
},
|
|
3387
|
-
run: async (ctx, force) => {
|
|
3388
|
-
try {
|
|
3389
|
-
const wrote = await seedMenubarTheme(ctx.client, { force });
|
|
3390
|
-
return {
|
|
3391
|
-
id: "menubar-theme",
|
|
3392
|
-
ok: true,
|
|
3393
|
-
wrote,
|
|
3394
|
-
detail: wrote ? force ? "已重新初始化" : "已写入" : force ? "内容已是最新(未重写)" : "已存在,跳过"
|
|
3395
|
-
};
|
|
3396
|
-
} catch (err) {
|
|
3397
|
-
return {
|
|
3398
|
-
id: "menubar-theme",
|
|
3399
|
-
ok: false,
|
|
3400
|
-
wrote: false,
|
|
3401
|
-
error: err instanceof Error ? err.message : String(err)
|
|
3402
|
-
};
|
|
3403
|
-
}
|
|
3404
|
-
},
|
|
3405
|
-
remove: async (ctx) => {
|
|
3406
|
-
try {
|
|
3407
|
-
return removedDetail("menubar-theme", (await unseedMenubarTheme(ctx.client)).removed);
|
|
3408
|
-
} catch (err) {
|
|
3409
|
-
return {
|
|
3410
|
-
id: "menubar-theme",
|
|
3411
|
-
ok: false,
|
|
3412
|
-
wrote: false,
|
|
3413
|
-
error: err instanceof Error ? err.message : String(err)
|
|
3414
|
-
};
|
|
3415
|
-
}
|
|
3416
|
-
}
|
|
3417
|
-
},
|
|
3418
|
-
{
|
|
3232
|
+
core: false
|
|
3233
|
+
}, {
|
|
3234
|
+
presentTitle: MENUBAR_THEME_TIDDLER,
|
|
3235
|
+
write: seedMenubarTheme,
|
|
3236
|
+
unseed: unseedMenubarTheme
|
|
3237
|
+
}),
|
|
3238
|
+
defineSeed({
|
|
3419
3239
|
id: "tw-web-host",
|
|
3420
3240
|
title: "TW 前端 API 基址(同源代理)",
|
|
3421
3241
|
description: "把 $:/config/tiddlyweb/host 指向 DSH 同源代理,嵌入式 TW 才能经 DSH origin 访问(远程访问模式的前提)。",
|
|
3422
|
-
core: true
|
|
3242
|
+
core: true
|
|
3243
|
+
}, {
|
|
3423
3244
|
check: async (ctx) => {
|
|
3424
3245
|
let current;
|
|
3425
3246
|
try {
|
|
@@ -3472,7 +3293,7 @@ const SEED_DEFS = [
|
|
|
3472
3293
|
};
|
|
3473
3294
|
}
|
|
3474
3295
|
}
|
|
3475
|
-
}
|
|
3296
|
+
})
|
|
3476
3297
|
];
|
|
3477
3298
|
/** Check every seed, returning statuses in registry order. */
|
|
3478
3299
|
async function checkAllSeeds(ctx) {
|
|
@@ -3965,6 +3786,13 @@ function registerTiddlywikiTools(ctx, deps) {
|
|
|
3965
3786
|
const register = (tool) => {
|
|
3966
3787
|
disposers.push(ctx.tools.register(tool));
|
|
3967
3788
|
};
|
|
3789
|
+
/** Every read/write tool needs a live TW client — shared guard for the 8
|
|
3790
|
+
* wiki-facing tools (git tools operate on the repo path instead). */
|
|
3791
|
+
const requireWiki = () => {
|
|
3792
|
+
const wiki = deps.wiki();
|
|
3793
|
+
if (wiki === void 0) throw new Error("TiddlyWiki 服务未运行(tiddlywiki_status 可查)");
|
|
3794
|
+
return wiki;
|
|
3795
|
+
};
|
|
3968
3796
|
register(defineTool({
|
|
3969
3797
|
name: "tiddlywiki_search",
|
|
3970
3798
|
description: "检索 TiddlyWiki 持久知识库:按关键词(可选 tags 数组 / since 修改时间 / type / limit)搜索非系统 tiddler,返回标题、标签、修改时间与摘要片段。",
|
|
@@ -4019,9 +3847,7 @@ function registerTiddlywikiTools(ctx, deps) {
|
|
|
4019
3847
|
}
|
|
4020
3848
|
},
|
|
4021
3849
|
execute: async (args) => {
|
|
4022
|
-
const
|
|
4023
|
-
if (wiki === void 0) throw new Error("TiddlyWiki 服务未运行(tiddlywiki_status 可查)");
|
|
4024
|
-
const { items, total } = await wiki.search(args.query, {
|
|
3850
|
+
const { items, total } = await requireWiki().search(args.query, {
|
|
4025
3851
|
tags: args.tags,
|
|
4026
3852
|
tag: args.tag,
|
|
4027
3853
|
since: args.since,
|
|
@@ -4073,9 +3899,7 @@ function registerTiddlywikiTools(ctx, deps) {
|
|
|
4073
3899
|
}
|
|
4074
3900
|
},
|
|
4075
3901
|
execute: async (args) => {
|
|
4076
|
-
const
|
|
4077
|
-
if (wiki === void 0) throw new Error("TiddlyWiki 服务未运行(tiddlywiki_status 可查)");
|
|
4078
|
-
const items = await wiki.recent(args.limit ?? 15, args.since);
|
|
3902
|
+
const items = await requireWiki().recent(args.limit ?? 15, args.since);
|
|
4079
3903
|
return {
|
|
4080
3904
|
since: args.since ?? null,
|
|
4081
3905
|
results: items.map((t) => ({
|
|
@@ -4107,9 +3931,7 @@ function registerTiddlywikiTools(ctx, deps) {
|
|
|
4107
3931
|
}
|
|
4108
3932
|
},
|
|
4109
3933
|
execute: async () => {
|
|
4110
|
-
const
|
|
4111
|
-
if (wiki === void 0) throw new Error("TiddlyWiki 服务未运行(tiddlywiki_status 可查)");
|
|
4112
|
-
const tags = await wiki.listTags();
|
|
3934
|
+
const tags = await requireWiki().listTags();
|
|
4113
3935
|
return {
|
|
4114
3936
|
count: tags.length,
|
|
4115
3937
|
tags
|
|
@@ -4145,9 +3967,7 @@ function registerTiddlywikiTools(ctx, deps) {
|
|
|
4145
3967
|
}
|
|
4146
3968
|
},
|
|
4147
3969
|
execute: async (args) => {
|
|
4148
|
-
const
|
|
4149
|
-
if (wiki === void 0) throw new Error("TiddlyWiki 服务未运行(tiddlywiki_status 可查)");
|
|
4150
|
-
const t = await wiki.get(args.title);
|
|
3970
|
+
const t = await requireWiki().get(args.title);
|
|
4151
3971
|
if (t === void 0) return {
|
|
4152
3972
|
notFound: true,
|
|
4153
3973
|
title: args.title,
|
|
@@ -4206,8 +4026,7 @@ function registerTiddlywikiTools(ctx, deps) {
|
|
|
4206
4026
|
}
|
|
4207
4027
|
},
|
|
4208
4028
|
execute: async (args) => {
|
|
4209
|
-
const wiki =
|
|
4210
|
-
if (wiki === void 0) throw new Error("TiddlyWiki 服务未运行(tiddlywiki_status 可查)");
|
|
4029
|
+
const wiki = requireWiki();
|
|
4211
4030
|
const existing = await wiki.get(args.title).catch(() => void 0);
|
|
4212
4031
|
const tags = finalTagsForWrite(args.title, existing, Array.isArray(args.tags) ? args.tags.filter((t) => typeof t === "string" && t.trim().length > 0) : []);
|
|
4213
4032
|
const tiddler = {
|
|
@@ -4276,8 +4095,7 @@ function registerTiddlywikiTools(ctx, deps) {
|
|
|
4276
4095
|
}
|
|
4277
4096
|
},
|
|
4278
4097
|
execute: async (args) => {
|
|
4279
|
-
const wiki =
|
|
4280
|
-
if (wiki === void 0) throw new Error("TiddlyWiki 服务未运行(tiddlywiki_status 可查)");
|
|
4098
|
+
const wiki = requireWiki();
|
|
4281
4099
|
const list = Array.isArray(args.items) ? args.items : [];
|
|
4282
4100
|
if (list.length === 0) return {
|
|
4283
4101
|
ok: true,
|
|
@@ -4358,8 +4176,7 @@ function registerTiddlywikiTools(ctx, deps) {
|
|
|
4358
4176
|
}
|
|
4359
4177
|
},
|
|
4360
4178
|
execute: async (args) => {
|
|
4361
|
-
const wiki =
|
|
4362
|
-
if (wiki === void 0) throw new Error("TiddlyWiki 服务未运行(tiddlywiki_status 可查)");
|
|
4179
|
+
const wiki = requireWiki();
|
|
4363
4180
|
const { oldTitle, newTitle } = args;
|
|
4364
4181
|
if (oldTitle === newTitle) return {
|
|
4365
4182
|
ok: true,
|
|
@@ -4425,9 +4242,7 @@ function registerTiddlywikiTools(ctx, deps) {
|
|
|
4425
4242
|
}]
|
|
4426
4243
|
},
|
|
4427
4244
|
execute: async (args) => {
|
|
4428
|
-
|
|
4429
|
-
if (wiki === void 0) throw new Error("TiddlyWiki 服务未运行(tiddlywiki_status 可查)");
|
|
4430
|
-
await wiki.delete(args.title);
|
|
4245
|
+
await requireWiki().delete(args.title);
|
|
4431
4246
|
deps.autoCommit();
|
|
4432
4247
|
return {
|
|
4433
4248
|
ok: true,
|
|
@@ -4545,15 +4360,7 @@ function registerTiddlywikiTools(ctx, deps) {
|
|
|
4545
4360
|
lines.push(` ${value.message}`);
|
|
4546
4361
|
if (value.files !== void 0 && value.files.length > 0) lines.push(`涉及文件: ${value.files.join(", ")}`);
|
|
4547
4362
|
if (value.commit !== void 0) lines.push(`本地 commit: ${value.commit}`);
|
|
4548
|
-
if (value.status !== void 0) {
|
|
4549
|
-
const s = value.status;
|
|
4550
|
-
const bits = [`分支 ${s.branch}`];
|
|
4551
|
-
if (s.ahead !== void 0) bits.push(`领先 ${s.ahead}`);
|
|
4552
|
-
if (s.behind !== void 0) bits.push(`落后 ${s.behind}`);
|
|
4553
|
-
if (s.dirty) bits.push(`工作区有 ${s.dirtyFiles.length} 个未提交改动`);
|
|
4554
|
-
if (s.lastCommit !== void 0) bits.push(`最近提交 ${s.lastCommit}`);
|
|
4555
|
-
lines.push(`状态: ${bits.join(" · ")}`);
|
|
4556
|
-
}
|
|
4363
|
+
if (value.status !== void 0) lines.push(`状态: ${gitStatusBits(value.status)}`);
|
|
4557
4364
|
return [{
|
|
4558
4365
|
type: "text",
|
|
4559
4366
|
text: lines.join("\n")
|
|
@@ -4612,6 +4419,15 @@ function registerTiddlywikiTools(ctx, deps) {
|
|
|
4612
4419
|
}));
|
|
4613
4420
|
return disposers;
|
|
4614
4421
|
}
|
|
4422
|
+
/** One-line 状态 summary: 分支 … 领先 … 落后 … 工作区未提交 … 最近提交. */
|
|
4423
|
+
function gitStatusBits(s) {
|
|
4424
|
+
const bits = [`分支 ${s.branch}`];
|
|
4425
|
+
if (s.ahead !== void 0) bits.push(`领先 ${s.ahead}`);
|
|
4426
|
+
if (s.behind !== void 0) bits.push(`落后 ${s.behind}`);
|
|
4427
|
+
if (s.dirty) bits.push(`工作区有 ${s.dirtyFiles.length} 个未提交改动`);
|
|
4428
|
+
if (s.lastCommit !== void 0) bits.push(`最近提交 ${s.lastCommit}`);
|
|
4429
|
+
return bits.join(" · ");
|
|
4430
|
+
}
|
|
4615
4431
|
function renderSync(value) {
|
|
4616
4432
|
const lines = [`git ${value.action}: ${value.ok ? "成功" : "失败"}`];
|
|
4617
4433
|
lines.push(` ${value.message}`);
|
|
@@ -4625,13 +4441,8 @@ function renderSync(value) {
|
|
|
4625
4441
|
if (value.changed === true) lines.push(value.restarted === true ? "本次 pull 拉取了新内容,TW 服务已自动重启(同端口),读取/搜索均为最新快照。" : "本次 pull 拉取了新内容,但 TW 服务未能自动重启(如需最新快照,请手动重启 TW)。");
|
|
4626
4442
|
if (value.restartError !== void 0) lines.push(`TW 重启失败: ${value.restartError}`);
|
|
4627
4443
|
if (value.status !== void 0) {
|
|
4444
|
+
lines.push(`状态: ${gitStatusBits(value.status)}`);
|
|
4628
4445
|
const s = value.status;
|
|
4629
|
-
const bits = [`分支 ${s.branch}`];
|
|
4630
|
-
if (s.ahead !== void 0) bits.push(`领先 ${s.ahead}`);
|
|
4631
|
-
if (s.behind !== void 0) bits.push(`落后 ${s.behind}`);
|
|
4632
|
-
if (s.dirty) bits.push(`工作区有 ${s.dirtyFiles.length} 个未提交改动`);
|
|
4633
|
-
if (s.lastCommit !== void 0) bits.push(`最近提交 ${s.lastCommit}`);
|
|
4634
|
-
lines.push(`状态: ${bits.join(" · ")}`);
|
|
4635
4446
|
if (s.dirty && s.dirtyFiles.length > 0) lines.push(` 未提交: ${s.dirtyFiles.join(", ")}`);
|
|
4636
4447
|
}
|
|
4637
4448
|
return [{
|
|
@@ -4686,8 +4497,10 @@ const DEFAULTS = {
|
|
|
4686
4497
|
darkPalette: DARK_PALETTE_DEFAULT,
|
|
4687
4498
|
tabLabel: "知识库",
|
|
4688
4499
|
showSessionTab: true,
|
|
4689
|
-
sendToAgent: { enabled: true }
|
|
4500
|
+
sendToAgent: { enabled: true },
|
|
4501
|
+
allArticles: { pageSize: 10 }
|
|
4690
4502
|
},
|
|
4503
|
+
uiLanguage: "",
|
|
4691
4504
|
auth: {
|
|
4692
4505
|
username: "",
|
|
4693
4506
|
password: ""
|
|
@@ -4815,8 +4628,17 @@ function apply(ctx, rawConfig = {}) {
|
|
|
4815
4628
|
},
|
|
4816
4629
|
ui: {
|
|
4817
4630
|
...DEFAULTS.ui,
|
|
4818
|
-
...rawConfig.ui ?? {}
|
|
4631
|
+
...rawConfig.ui ?? {},
|
|
4632
|
+
sendToAgent: {
|
|
4633
|
+
...DEFAULTS.ui.sendToAgent,
|
|
4634
|
+
...rawConfig.ui?.sendToAgent ?? {}
|
|
4635
|
+
},
|
|
4636
|
+
allArticles: {
|
|
4637
|
+
...DEFAULTS.ui.allArticles,
|
|
4638
|
+
...rawConfig.ui?.allArticles ?? {}
|
|
4639
|
+
}
|
|
4819
4640
|
},
|
|
4641
|
+
uiLanguage: typeof rawConfig.uiLanguage === "string" ? rawConfig.uiLanguage.trim() : DEFAULTS.uiLanguage,
|
|
4820
4642
|
auth: {
|
|
4821
4643
|
...DEFAULTS.auth,
|
|
4822
4644
|
...rawConfig.auth ?? {}
|
|
@@ -4827,7 +4649,8 @@ function apply(ctx, rawConfig = {}) {
|
|
|
4827
4649
|
const configStore = new ConfigStore({
|
|
4828
4650
|
note: config.note,
|
|
4829
4651
|
git: config.git,
|
|
4830
|
-
ui: config.ui
|
|
4652
|
+
ui: config.ui,
|
|
4653
|
+
uiLanguage: config.uiLanguage
|
|
4831
4654
|
});
|
|
4832
4655
|
const eff = () => configStore.get();
|
|
4833
4656
|
const effectiveNoteTag = () => {
|