@fgbg/mdocs 0.8.25 → 1.1.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.
Files changed (152) hide show
  1. package/agent-skills/changelog/SKILL.md +50 -1
  2. package/agent-skills/index.json +8 -1
  3. package/agent-skills/usage-knowledge-graph/SKILL.md +126 -0
  4. package/agent-skills/usage-onboarding-ai/SKILL.md +36 -5
  5. package/dist/server/agent/Agent/run.js +60 -34
  6. package/dist/server/agent/Agent/run.js.map +1 -1
  7. package/dist/server/agent/Agent/tools-documents.js +1 -1
  8. package/dist/server/agent/Agent/tools-documents.js.map +1 -1
  9. package/dist/server/agent/Config/config.js +234 -64
  10. package/dist/server/agent/Config/config.js.map +1 -1
  11. package/dist/server/agent/Config/config.test.js +109 -0
  12. package/dist/server/agent/Config/config.test.js.map +1 -0
  13. package/dist/server/app.js +2 -0
  14. package/dist/server/app.js.map +1 -1
  15. package/dist/server/db/repositories/agent-model-config.repo.js +58 -27
  16. package/dist/server/db/repositories/agent-model-config.repo.js.map +1 -1
  17. package/dist/server/db/schema.js +96 -1
  18. package/dist/server/db/schema.js.map +1 -1
  19. package/dist/server/documents/build-graph.e2e.test.js +65 -0
  20. package/dist/server/documents/build-graph.e2e.test.js.map +1 -0
  21. package/dist/server/documents/document.service.js +34 -2
  22. package/dist/server/documents/document.service.js.map +1 -1
  23. package/dist/server/documents/graph/change-ratio.js +29 -0
  24. package/dist/server/documents/graph/change-ratio.js.map +1 -0
  25. package/dist/server/documents/graph/change-ratio.test.js +38 -0
  26. package/dist/server/documents/graph/change-ratio.test.js.map +1 -0
  27. package/dist/server/documents/graph/graph-agent.integration.test.js +95 -0
  28. package/dist/server/documents/graph/graph-agent.integration.test.js.map +1 -0
  29. package/dist/server/documents/graph/graph-agent.js +185 -0
  30. package/dist/server/documents/graph/graph-agent.js.map +1 -0
  31. package/dist/server/documents/graph/graph-file.js +58 -0
  32. package/dist/server/documents/graph/graph-file.js.map +1 -0
  33. package/dist/server/documents/graph/graph-file.test.js +28 -0
  34. package/dist/server/documents/graph/graph-file.test.js.map +1 -0
  35. package/dist/server/documents/graph/index.js +183 -0
  36. package/dist/server/documents/graph/index.js.map +1 -0
  37. package/dist/server/documents/graph/lifecycle.js +104 -0
  38. package/dist/server/documents/graph/lifecycle.js.map +1 -0
  39. package/dist/server/documents/graph/llm-chains.js +135 -0
  40. package/dist/server/documents/graph/llm-chains.js.map +1 -0
  41. package/dist/server/documents/graph/llm-utils.js +19 -0
  42. package/dist/server/documents/graph/llm-utils.js.map +1 -0
  43. package/dist/server/documents/graph/llm-utils.test.js +34 -0
  44. package/dist/server/documents/graph/llm-utils.test.js.map +1 -0
  45. package/dist/server/documents/graph/tools/concept-relations.tool.js +49 -0
  46. package/dist/server/documents/graph/tools/concept-relations.tool.js.map +1 -0
  47. package/dist/server/documents/graph/tools/extract-knowledge-points.tool.js +45 -0
  48. package/dist/server/documents/graph/tools/extract-knowledge-points.tool.js.map +1 -0
  49. package/dist/server/documents/graph/tools/generate-contains.tool.js +41 -0
  50. package/dist/server/documents/graph/tools/generate-contains.tool.js.map +1 -0
  51. package/dist/server/documents/graph/tools/induce-concepts.tool.js +43 -0
  52. package/dist/server/documents/graph/tools/induce-concepts.tool.js.map +1 -0
  53. package/dist/server/documents/graph/types.js +12 -0
  54. package/dist/server/documents/graph/types.js.map +1 -0
  55. package/dist/server/documents/graph/utils.js +41 -0
  56. package/dist/server/documents/graph/utils.js.map +1 -0
  57. package/dist/server/documents/graph/utils.test.js +87 -0
  58. package/dist/server/documents/graph/utils.test.js.map +1 -0
  59. package/dist/server/documents/graph-cache-io.js +254 -0
  60. package/dist/server/documents/graph-cache-io.js.map +1 -0
  61. package/dist/server/documents/graph-lifecycle.js +19 -0
  62. package/dist/server/documents/graph-lifecycle.js.map +1 -0
  63. package/dist/server/documents/graph-task.js +232 -0
  64. package/dist/server/documents/graph-task.js.map +1 -0
  65. package/dist/server/documents/graph.integration.test.js +213 -0
  66. package/dist/server/documents/graph.integration.test.js.map +1 -0
  67. package/dist/server/documents/graph.service.js +296 -0
  68. package/dist/server/documents/graph.service.js.map +1 -0
  69. package/dist/server/documents/markdown-to-lexical.js +111 -13
  70. package/dist/server/documents/markdown-to-lexical.js.map +1 -1
  71. package/dist/server/documents/markdown-to-lexical.test.js +37 -0
  72. package/dist/server/documents/markdown-to-lexical.test.js.map +1 -1
  73. package/dist/server/documents/tree.service.js +12 -6
  74. package/dist/server/documents/tree.service.js.map +1 -1
  75. package/dist/server/migrations/003-folder-desc-file-type.js +43 -0
  76. package/dist/server/migrations/003-folder-desc-file-type.js.map +1 -0
  77. package/dist/server/migrations/004-api-key-to-secrets.js +90 -0
  78. package/dist/server/migrations/004-api-key-to-secrets.js.map +1 -0
  79. package/dist/server/migrations/004-folder-desc-id.js +98 -0
  80. package/dist/server/migrations/004-folder-desc-id.js.map +1 -0
  81. package/dist/server/migrations/005-special-file-id-format.js +124 -0
  82. package/dist/server/migrations/005-special-file-id-format.js.map +1 -0
  83. package/dist/server/routes/agent.routes.js +69 -31
  84. package/dist/server/routes/agent.routes.js.map +1 -1
  85. package/dist/server/routes/assets.routes.js +73 -62
  86. package/dist/server/routes/assets.routes.js.map +1 -1
  87. package/dist/server/routes/assets.routes.upload.test.js +28 -0
  88. package/dist/server/routes/assets.routes.upload.test.js.map +1 -0
  89. package/dist/server/routes/documents.routes.js +1 -1
  90. package/dist/server/routes/documents.routes.js.map +1 -1
  91. package/dist/server/routes/folders.routes.js +2 -2
  92. package/dist/server/routes/folders.routes.js.map +1 -1
  93. package/dist/server/routes/graph.routes.js +112 -0
  94. package/dist/server/routes/graph.routes.js.map +1 -0
  95. package/dist/server/routes/tree.routes.js +1 -1
  96. package/dist/server/routes/tree.routes.js.map +1 -1
  97. package/dist/server/secrets/secrets-store.js +93 -0
  98. package/dist/server/secrets/secrets-store.js.map +1 -0
  99. package/dist/server/storage/paths.js +0 -1
  100. package/dist/server/storage/paths.js.map +1 -1
  101. package/dist/server/task-queue/TaskQueue.js +258 -0
  102. package/dist/server/task-queue/TaskQueue.js.map +1 -0
  103. package/dist/server/task-queue/TaskQueue.test.js +136 -0
  104. package/dist/server/task-queue/TaskQueue.test.js.map +1 -0
  105. package/dist/server/task-queue/index.js +18 -0
  106. package/dist/server/task-queue/index.js.map +1 -0
  107. package/dist/server/task-queue/log-store.js +84 -0
  108. package/dist/server/task-queue/log-store.js.map +1 -0
  109. package/dist/server/task-queue/types.js +5 -0
  110. package/dist/server/task-queue/types.js.map +1 -0
  111. package/dist/shared/docPath.js +3 -2
  112. package/dist/shared/docPath.js.map +1 -1
  113. package/dist/shared/file-types.js +35 -0
  114. package/dist/shared/file-types.js.map +1 -0
  115. package/dist/shared/graph-files.js +31 -0
  116. package/dist/shared/graph-files.js.map +1 -0
  117. package/dist/web/assets/AgentChatPanel-KiTv5uCF.js +6 -0
  118. package/dist/web/assets/AgentSkillFormCard-CP-HN--U.js +6 -0
  119. package/dist/web/assets/AiWriteWorkbench-0wEf6qur.js +7 -0
  120. package/dist/web/assets/{DiagramEditor-NtNY3sZL-DqPXDJHY.js → DiagramEditor-NtNY3sZL-XBBS6Mu4.js} +1 -1
  121. package/dist/web/assets/DocumentEditor-CeaspJgX.js +26 -0
  122. package/dist/web/assets/{MergeView-Y3zB8V5p.js → MergeView-y24LKycd.js} +1 -1
  123. package/dist/web/assets/PlaygroundPage-CYx8X23_.js +8 -0
  124. package/dist/web/assets/SettingsPage-Bor0wh4z.js +1 -0
  125. package/dist/web/assets/{Toolbar-B36NxaTc.js → Toolbar-uyj1sA_r.js} +16 -21
  126. package/dist/web/assets/{VisitorPickerModal-drnOuPLP.js → VisitorPickerModal-DljB4USS.js} +1 -1
  127. package/dist/web/assets/{copy-CX8GOyUb.js → copy-DZj0Bj86.js} +1 -1
  128. package/dist/web/assets/diagram-CT58wBdU.js +5772 -0
  129. package/dist/web/assets/{editor-ONzd_YnO.js → editor-CL03se2B.js} +1 -1
  130. package/dist/web/assets/index-C4ESYs7f.css +1 -0
  131. package/dist/web/assets/index-CqUWQ28V.js +248 -0
  132. package/dist/web/assets/{meta2d-D3b5RRN8.js → meta2d-1-g23JOU.js} +1 -1
  133. package/dist/web/assets/{meta2dEngine-BD2XbzaV-CCgjRlVJ.js → meta2dEngine-BD2XbzaV-D2yFsZ_P.js} +1 -1
  134. package/dist/web/assets/{motion-92iQWeIW.js → motion-DX3HNq_4.js} +2 -2
  135. package/dist/web/assets/{plus-BWV299MC.js → plus-Bcg3ots1.js} +1 -1
  136. package/dist/web/assets/react-kYvt_3tR.js +2122 -0
  137. package/dist/web/assets/react-vendor-BuABiQkA.js +242 -0
  138. package/dist/web/assets/ui-BQStT3dY.js +3116 -0
  139. package/dist/web/index.html +6 -5
  140. package/package.json +3 -2
  141. package/dist/web/assets/AgentChatPanel-BTCyf9Ww.js +0 -1
  142. package/dist/web/assets/AgentSkillFormCard-CAD6QqQ0.js +0 -6
  143. package/dist/web/assets/AiWriteWorkbench-PVv90TJS.js +0 -7
  144. package/dist/web/assets/DocumentEditor-zNIJgxe0.js +0 -21
  145. package/dist/web/assets/PlaygroundPage-DAaEm887.js +0 -8
  146. package/dist/web/assets/SettingsPage-DhdiNEhO.js +0 -1
  147. package/dist/web/assets/diagram-3lhA7FYR.js +0 -6257
  148. package/dist/web/assets/index-DgM2rYI-.js +0 -185
  149. package/dist/web/assets/index-S_lCknAk.css +0 -1
  150. package/dist/web/assets/react-BUhFwPCj.js +0 -2382
  151. package/dist/web/assets/react-vendor-BJFXgntS.js +0 -242
  152. package/dist/web/assets/ui-06fxEgif.js +0 -3126
@@ -0,0 +1,258 @@
1
+ import { TaskLogStore } from './log-store.js';
2
+ export class TaskQueue {
3
+ taskTypes = new Map();
4
+ queue = [];
5
+ concurrency;
6
+ timeSliceMs;
7
+ logStore;
8
+ scheduling = false; // 防止 schedule 重入
9
+ constructor(options) {
10
+ this.concurrency = options.concurrency ?? 2;
11
+ this.timeSliceMs = options.timeSliceMs ?? 30_000;
12
+ this.logStore = new TaskLogStore(options.logDir);
13
+ }
14
+ /* ── 注册任务类型 ── */
15
+ registerTask(type, def) {
16
+ this.taskTypes.set(type, def);
17
+ }
18
+ /* ── 入队 ── */
19
+ enqueue(type, payload) {
20
+ const def = this.taskTypes.get(type);
21
+ if (!def) {
22
+ throw new Error(`未知任务类型:${type}`);
23
+ }
24
+ const taskId = def.makeId(payload);
25
+ // 检查是否已有未完成的同 ID 任务(去重)
26
+ const existing = this.queue.find((t) => t.id === taskId && t.state !== 'completed' && t.state !== 'failed');
27
+ if (existing) {
28
+ return {
29
+ taskId,
30
+ status: this.getPublicStatus(existing),
31
+ position: this.getPosition(existing),
32
+ deduped: true,
33
+ };
34
+ }
35
+ // 移除旧的 completed/failed 任务(重新生成,覆盖)
36
+ this.queue = this.queue.filter((t) => t.id !== taskId);
37
+ // 创建新任务
38
+ const task = {
39
+ id: taskId,
40
+ type,
41
+ payload,
42
+ state: 'queued',
43
+ createdAt: Date.now(),
44
+ };
45
+ this.queue.push(task);
46
+ // 重置日志文件(重新开始写)
47
+ this.logStore.reset(taskId);
48
+ // 触发调度
49
+ this.schedule();
50
+ return {
51
+ taskId,
52
+ status: this.getPublicStatus(task),
53
+ position: this.getPosition(task),
54
+ deduped: false,
55
+ };
56
+ }
57
+ /* ── 查询任务状态 ── */
58
+ getTask(taskId) {
59
+ const task = this.queue.find((t) => t.id === taskId);
60
+ // 从日志推断进度
61
+ const logs = this.logStore.readTail(taskId, 50);
62
+ const progress = this.extractProgress(logs);
63
+ const lastLog = logs[logs.length - 1];
64
+ // 任务在队列中
65
+ if (task) {
66
+ return {
67
+ id: task.id,
68
+ type: task.type,
69
+ status: this.getPublicStatus(task),
70
+ position: this.getPosition(task),
71
+ progress,
72
+ logs,
73
+ error: task.error,
74
+ createdAt: new Date(task.createdAt).toISOString(),
75
+ startedAt: task.startedAt ? new Date(task.startedAt).toISOString() : undefined,
76
+ finishedAt: task.finishedAt ? new Date(task.finishedAt).toISOString() : undefined,
77
+ };
78
+ }
79
+ // 任务不在队列中(可能重启了,或已完成很久了)
80
+ const mtime = this.logStore.getMtime(taskId);
81
+ // 日志都没有 → not_found
82
+ if (!mtime) {
83
+ return {
84
+ id: taskId,
85
+ type: '',
86
+ status: 'not_found',
87
+ position: -1,
88
+ logs: [],
89
+ createdAt: '',
90
+ };
91
+ }
92
+ // 从最后一条日志判断状态
93
+ let status = 'stale';
94
+ let error;
95
+ if (lastLog?.event === 'task.completed') {
96
+ status = 'completed';
97
+ }
98
+ else if (lastLog?.event === 'task.failed') {
99
+ status = 'failed';
100
+ error = lastLog.data?.error;
101
+ }
102
+ else if (Date.now() - mtime > 5 * 60 * 1000) {
103
+ // 超过 5 分钟没更新 → stale
104
+ status = 'stale';
105
+ }
106
+ else {
107
+ // 5 分钟内有更新,但队列里没了 → 大概率刚重启,也算 stale
108
+ status = 'stale';
109
+ }
110
+ return {
111
+ id: taskId,
112
+ type: '',
113
+ status,
114
+ position: -1,
115
+ progress,
116
+ logs,
117
+ error,
118
+ createdAt: '',
119
+ startedAt: '',
120
+ finishedAt: lastLog ? lastLog.ts : undefined,
121
+ };
122
+ }
123
+ /* ── 调度核心 ── */
124
+ schedule() {
125
+ // 防重入:同步代码中多次调用 schedule,只执行一次
126
+ if (this.scheduling)
127
+ return;
128
+ this.scheduling = true;
129
+ // 用 setImmediate 让当前同步代码先跑完,再统一调度
130
+ setImmediate(() => {
131
+ this.scheduling = false;
132
+ this.doSchedule();
133
+ });
134
+ }
135
+ doSchedule() {
136
+ // 1. 先清理已完成/失败的任务
137
+ this.queue = this.queue.filter((t) => t.state !== 'completed' && t.state !== 'failed');
138
+ // 2. 确保前 concurrency 个位置上的任务都在跑
139
+ for (let i = 0; i < this.concurrency && i < this.queue.length; i++) {
140
+ const task = this.queue[i];
141
+ if (!task)
142
+ continue;
143
+ if (task.state === 'queued') {
144
+ // 还没开始过 → 启动
145
+ this.startTask(task);
146
+ }
147
+ else if (task.state === 'suspended') {
148
+ // yield 挂起了 → 恢复
149
+ this.resumeTask(task);
150
+ }
151
+ // running 不用管,它自己在跑
152
+ }
153
+ }
154
+ /* ── 启动 / 恢复任务 ── */
155
+ startTask(task) {
156
+ const def = this.taskTypes.get(task.type);
157
+ if (!def)
158
+ return;
159
+ task.state = 'running';
160
+ task.startedAt = task.startedAt ?? Date.now();
161
+ task.lastRunAt = Date.now();
162
+ const ctx = this.createContext(task);
163
+ task.run = def.run;
164
+ def.run(task.payload, ctx)
165
+ .then(() => {
166
+ task.state = 'completed';
167
+ task.finishedAt = Date.now();
168
+ this.logStore.append(task.id, 'task.completed');
169
+ this.schedule();
170
+ })
171
+ .catch((err) => {
172
+ task.state = 'failed';
173
+ task.error = err.message;
174
+ task.finishedAt = Date.now();
175
+ // 写一条失败日志
176
+ this.logStore.append(task.id, 'task.failed', { error: err.message }, 'error');
177
+ this.schedule();
178
+ });
179
+ }
180
+ resumeTask(task) {
181
+ if (task.state !== 'suspended' || !task.resume)
182
+ return;
183
+ task.state = 'running';
184
+ task.lastRunAt = Date.now();
185
+ const resume = task.resume;
186
+ task.resume = undefined;
187
+ resume();
188
+ }
189
+ /* ── 创建上下文 ── */
190
+ createContext(task) {
191
+ const scheduler = this.createScheduler(task);
192
+ return {
193
+ taskId: task.id,
194
+ scheduler,
195
+ emit: (event, data, level) => {
196
+ this.logStore.append(task.id, event, data, level);
197
+ },
198
+ };
199
+ }
200
+ createScheduler(task) {
201
+ return {
202
+ shouldYield: () => {
203
+ // 没人排队 → 没必要让
204
+ if (this.queue.length <= this.concurrency)
205
+ return false;
206
+ // 时间片还没到 → 不让
207
+ if (!task.lastRunAt || Date.now() - task.lastRunAt < this.timeSliceMs)
208
+ return false;
209
+ return true;
210
+ },
211
+ yield: () => {
212
+ return new Promise((resolve) => {
213
+ // 存 resume 回调
214
+ task.resume = resolve;
215
+ task.state = 'suspended';
216
+ // 写一条 yielded 日志
217
+ this.logStore.append(task.id, 'task.yielded', { reason: 'time-slice' });
218
+ // 从当前位置移除,塞到队尾
219
+ this.queue = this.queue.filter((t) => t.id !== task.id);
220
+ this.queue.push(task);
221
+ // 触发调度(前面空位会被顶上)
222
+ this.schedule();
223
+ });
224
+ },
225
+ };
226
+ }
227
+ /* ── 辅助方法 ── */
228
+ getPublicStatus(task) {
229
+ if (!task)
230
+ return 'pending';
231
+ if (task.state === 'completed')
232
+ return 'completed';
233
+ if (task.state === 'failed')
234
+ return 'failed';
235
+ const index = this.queue.indexOf(task);
236
+ if (index < this.concurrency)
237
+ return 'running';
238
+ return 'pending';
239
+ }
240
+ getPosition(task) {
241
+ if (!task)
242
+ return -1;
243
+ const index = this.queue.indexOf(task);
244
+ if (index < this.concurrency)
245
+ return 0; // 0 表示正在跑
246
+ return index - this.concurrency + 1; // 排队位置:1 = 下一个就轮到
247
+ }
248
+ extractProgress(logs) {
249
+ // 从日志里找最后一条 task.progress
250
+ for (let i = logs.length - 1; i >= 0; i--) {
251
+ if (logs[i].event === 'task.progress' && logs[i].data) {
252
+ return { current: logs[i].data.current, total: logs[i].data.total };
253
+ }
254
+ }
255
+ return undefined;
256
+ }
257
+ }
258
+ //# sourceMappingURL=TaskQueue.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TaskQueue.js","sourceRoot":"","sources":["../../../src/server/task-queue/TaskQueue.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,136 @@
1
+ /**
2
+ * TaskQueue 单元测试。
3
+ *
4
+ * 测试要点:
5
+ * 1. 基本入队、执行、完成
6
+ * 2. 并发控制(不超过 concurrency)
7
+ * 3. 去重
8
+ * 4. yield 让出 + 恢复
9
+ * 5. 失败处理
10
+ */
11
+ import { describe, it, expect, beforeEach, afterEach } from 'vitest';
12
+ import fs from 'node:fs';
13
+ import os from 'node:os';
14
+ import path from 'node:path';
15
+ import { TaskQueue } from './TaskQueue.js';
16
+ describe('TaskQueue', () => {
17
+ let tmpDir;
18
+ let queue;
19
+ beforeEach(() => {
20
+ tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'task-queue-test-'));
21
+ queue = new TaskQueue({ concurrency: 2, timeSliceMs: 100, logDir: tmpDir });
22
+ });
23
+ afterEach(() => {
24
+ fs.rmSync(tmpDir, { recursive: true, force: true });
25
+ });
26
+ /* ── 辅助:注册一个简单的测试任务 ── */
27
+ function registerSimpleTask(name, work) {
28
+ queue.registerTask(name, {
29
+ makeId: (payload) => `${name}:${payload.id}`,
30
+ run: async (payload, ctx) => {
31
+ await work(ctx);
32
+ },
33
+ });
34
+ }
35
+ it('基本流程:入队 → 执行 → 完成', async () => {
36
+ let ran = false;
37
+ registerSimpleTask('test', async () => {
38
+ ran = true;
39
+ });
40
+ const result = queue.enqueue('test', { id: '1' });
41
+ expect(result.deduped).toBe(false);
42
+ expect(result.taskId).toBe('test:1');
43
+ // 等 setImmediate 里的调度执行完
44
+ await new Promise((r) => setTimeout(r, 10));
45
+ expect(ran).toBe(true);
46
+ const status = queue.getTask('test:1');
47
+ expect(status.status).toBe('completed');
48
+ });
49
+ it('并发控制:同时最多跑 concurrency 个', async () => {
50
+ let running = 0;
51
+ let maxRunning = 0;
52
+ const tasks = [];
53
+ queue.registerTask('test', {
54
+ makeId: (p) => `test:${p.id}`,
55
+ run: async () => {
56
+ running++;
57
+ maxRunning = Math.max(maxRunning, running);
58
+ await new Promise((r) => setTimeout(r, 50));
59
+ running--;
60
+ },
61
+ });
62
+ // 入队 5 个
63
+ for (let i = 0; i < 5; i++) {
64
+ queue.enqueue('test', { id: String(i) });
65
+ }
66
+ // 等所有任务跑完
67
+ await new Promise((r) => setTimeout(r, 200));
68
+ expect(maxRunning).toBe(2);
69
+ });
70
+ it('去重:相同 ID 不重复入队', async () => {
71
+ let count = 0;
72
+ registerSimpleTask('test', async () => {
73
+ count++;
74
+ });
75
+ queue.enqueue('test', { id: 'same' });
76
+ const result = queue.enqueue('test', { id: 'same' });
77
+ expect(result.deduped).toBe(true);
78
+ expect(result.taskId).toBe('test:same');
79
+ await new Promise((r) => setTimeout(r, 10));
80
+ expect(count).toBe(1); // 只执行了一次
81
+ });
82
+ it('yield:任务可以让出,后面的顶上', async () => {
83
+ // 用并发 1 来测,确保 A yield 后 B 能顶上
84
+ queue = new TaskQueue({ concurrency: 1, timeSliceMs: 100, logDir: tmpDir });
85
+ const order = [];
86
+ queue.registerTask('test', {
87
+ makeId: (p) => `test:${p.id}`,
88
+ run: async (payload, ctx) => {
89
+ order.push(`${payload.id}-start`);
90
+ if (payload.yield) {
91
+ await ctx.scheduler.yield();
92
+ }
93
+ order.push(`${payload.id}-end`);
94
+ },
95
+ });
96
+ // A yield,B 不 yield
97
+ queue.enqueue('test', { id: 'A', yield: true });
98
+ queue.enqueue('test', { id: 'B', yield: false });
99
+ await new Promise((r) => setTimeout(r, 100));
100
+ // 顺序应该是:
101
+ // A-start → A yield → B-start → B-end → A-end
102
+ expect(order).toEqual(['A-start', 'B-start', 'B-end', 'A-end']);
103
+ });
104
+ it('失败:任务异常标记为 failed,不影响其他任务', async () => {
105
+ queue.registerTask('test', {
106
+ makeId: (p) => `test:${p.id}`,
107
+ run: async (payload) => {
108
+ if (payload.fail) {
109
+ throw new Error('boom');
110
+ }
111
+ },
112
+ });
113
+ queue.enqueue('test', { id: 'bad', fail: true });
114
+ queue.enqueue('test', { id: 'good', fail: false });
115
+ await new Promise((r) => setTimeout(r, 50));
116
+ const bad = queue.getTask('test:bad');
117
+ const good = queue.getTask('test:good');
118
+ expect(bad.status).toBe('failed');
119
+ expect(bad.error).toBe('boom');
120
+ expect(good.status).toBe('completed');
121
+ });
122
+ it('shouldYield:没人排队时不让', async () => {
123
+ let capturedShouldYield = true;
124
+ queue.registerTask('test', {
125
+ makeId: (p) => `test:${p.id}`,
126
+ run: async (_payload, ctx) => {
127
+ capturedShouldYield = ctx.scheduler.shouldYield();
128
+ },
129
+ });
130
+ queue.enqueue('test', { id: '1' });
131
+ await new Promise((r) => setTimeout(r, 10));
132
+ // 只有一个任务,没人排队 → 不让
133
+ expect(capturedShouldYield).toBe(false);
134
+ });
135
+ });
136
+ //# sourceMappingURL=TaskQueue.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TaskQueue.test.js","sourceRoot":"","sources":["../../../src/server/task-queue/TaskQueue.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,18 @@
1
+ /**
2
+ * 任务队列单例。
3
+ *
4
+ * 全局只有一个 TaskQueue 实例,所有任务类型共享同一个并发池。
5
+ */
6
+ import path from 'node:path';
7
+ import os from 'node:os';
8
+ import { TaskQueue } from './TaskQueue.js';
9
+ const dataDir = process.env.MDOCS_DATA_DIR || path.join(os.homedir(), '.mdocs');
10
+ const logDir = path.join(dataDir, 'task-logs');
11
+ export const taskQueue = new TaskQueue({
12
+ concurrency: 2,
13
+ timeSliceMs: 30_000,
14
+ logDir,
15
+ });
16
+ export { TaskQueue } from './TaskQueue.js';
17
+ export * from './types.js';
18
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/server/task-queue/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAE3C,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,cAAc,IAAI,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,QAAQ,CAAC,CAAC;AAChF,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,WAAW,CAAC,CAAC;AAE/C,MAAM,CAAC,MAAM,SAAS,GAAG,IAAI,SAAS,CAAC;IACrC,WAAW,EAAE,CAAC;IACd,WAAW,EAAE,MAAM;IACnB,MAAM;CACP,CAAC,CAAC;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAC3C,cAAc,YAAY,CAAC"}
@@ -0,0 +1,84 @@
1
+ /**
2
+ * 任务日志存储:JSON Lines 文件,每个任务一个文件。
3
+ */
4
+ import fs from 'node:fs';
5
+ import path from 'node:path';
6
+ export class TaskLogStore {
7
+ dir;
8
+ constructor(dir) {
9
+ this.dir = dir;
10
+ fs.mkdirSync(dir, { recursive: true });
11
+ }
12
+ filePath(taskId) {
13
+ // 把 / 替换成 _,避免路径穿越
14
+ const safeId = taskId.replace(/\//g, '_').replace(/\.\./g, '');
15
+ return path.join(this.dir, `${safeId}.log`);
16
+ }
17
+ /** 清空并重新开始写(任务开始时调用) */
18
+ reset(taskId) {
19
+ const file = this.filePath(taskId);
20
+ fs.writeFileSync(file, '');
21
+ }
22
+ /** 追加一条日志 */
23
+ append(taskId, event, data, level = 'info') {
24
+ const entry = {
25
+ ts: new Date().toISOString(),
26
+ event,
27
+ level,
28
+ data,
29
+ };
30
+ const line = JSON.stringify(entry) + '\n';
31
+ try {
32
+ fs.appendFileSync(this.filePath(taskId), line);
33
+ }
34
+ catch {
35
+ // 写日志失败不影响任务执行
36
+ }
37
+ }
38
+ /** 读取最后 N 条日志 */
39
+ readTail(taskId, limit = 50) {
40
+ const file = this.filePath(taskId);
41
+ if (!fs.existsSync(file))
42
+ return [];
43
+ try {
44
+ const content = fs.readFileSync(file, 'utf-8');
45
+ const lines = content.split('\n').filter(Boolean);
46
+ const tail = lines.slice(-limit);
47
+ return tail
48
+ .map((line) => {
49
+ try {
50
+ return JSON.parse(line);
51
+ }
52
+ catch {
53
+ return null;
54
+ }
55
+ })
56
+ .filter((e) => e !== null);
57
+ }
58
+ catch {
59
+ return [];
60
+ }
61
+ }
62
+ /** 读取最后一条日志 */
63
+ readLast(taskId) {
64
+ const entries = this.readTail(taskId, 1);
65
+ return entries[0] ?? null;
66
+ }
67
+ /** 获取日志文件 mtime(用于 stale 判断) */
68
+ getMtime(taskId) {
69
+ const file = this.filePath(taskId);
70
+ if (!fs.existsSync(file))
71
+ return null;
72
+ try {
73
+ return fs.statSync(file).mtimeMs;
74
+ }
75
+ catch {
76
+ return null;
77
+ }
78
+ }
79
+ /** 日志文件是否存在 */
80
+ exists(taskId) {
81
+ return fs.existsSync(this.filePath(taskId));
82
+ }
83
+ }
84
+ //# sourceMappingURL=log-store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"log-store.js","sourceRoot":"","sources":["../../../src/server/task-queue/log-store.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ /**
2
+ * 任务队列核心类型定义。
3
+ */
4
+ export {};
5
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/server/task-queue/types.ts"],"names":[],"mappings":"AAAA;;GAEG"}
@@ -30,8 +30,9 @@ export function normaliseDocRelativePath(input) {
30
30
  throw new DocPathError("use forward slashes");
31
31
  if (raw.includes(".."))
32
32
  throw new DocPathError("path must not contain ..");
33
- if (!raw.toLowerCase().endsWith(".md")) {
34
- throw new DocPathError("document path must end with .md");
33
+ const lower = raw.toLowerCase();
34
+ if (!lower.endsWith(".md") && !lower.endsWith(".json")) {
35
+ throw new DocPathError("document path must end with .md or .json");
35
36
  }
36
37
  if (!RELATIVE_PATH_RX.test(raw)) {
37
38
  throw new DocPathError("path contains unsupported characters");
@@ -1 +1 @@
1
- {"version":3,"file":"docPath.js","sourceRoot":"","sources":["../../src/shared/docPath.ts"],"names":[],"mappings":"AAAA,mFAAmF;AAEnF,MAAM,OAAO,YAAa,SAAQ,KAAK;IACrC,YAAY,OAAe;QACzB,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,cAAc,CAAC;IAC7B,CAAC;CACF;AAED,MAAM,gBAAgB,GAAG,kCAAkC,CAAC;AAE5D,SAAS,cAAc,CAAC,GAAW;IACjC,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,KAAK,MAAM,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC;QACjC,IAAI,GAAG,KAAK,EAAE,IAAI,GAAG,KAAK,GAAG;YAAE,SAAS;QACxC,IAAI,GAAG,KAAK,IAAI,EAAE,CAAC;YACjB,IAAI,KAAK,CAAC,MAAM;gBAAE,KAAK,CAAC,GAAG,EAAE,CAAC;QAChC,CAAC;;YAAM,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACzB,CAAC;IACD,OAAO,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AACzB,CAAC;AAED,MAAM,UAAU,wBAAwB,CAAC,KAAa;IACpD,MAAM,GAAG,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC;IACzB,IAAI,CAAC,GAAG;QAAE,MAAM,IAAI,YAAY,CAAC,2BAA2B,CAAC,CAAC;IAC9D,IAAI,GAAG,CAAC,UAAU,CAAC,GAAG,CAAC;QAAE,MAAM,IAAI,YAAY,CAAC,uBAAuB,CAAC,CAAC;IACzE,IAAI,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC;QAAE,MAAM,IAAI,YAAY,CAAC,qBAAqB,CAAC,CAAC;IACtE,IAAI,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC;QAAE,MAAM,IAAI,YAAY,CAAC,0BAA0B,CAAC,CAAC;IAC3E,IAAI,CAAC,GAAG,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;QACvC,MAAM,IAAI,YAAY,CAAC,iCAAiC,CAAC,CAAC;IAC5D,CAAC;IACD,IAAI,CAAC,gBAAgB,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;QAChC,MAAM,IAAI,YAAY,CAAC,sCAAsC,CAAC,CAAC;IACjE,CAAC;IACD,MAAM,IAAI,GAAG,cAAc,CAAC,GAAG,CAAC,CAAC;IACjC,IAAI,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;QAClD,MAAM,IAAI,YAAY,CAAC,wBAAwB,CAAC,CAAC;IACnD,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC"}
1
+ {"version":3,"file":"docPath.js","sourceRoot":"","sources":["../../src/shared/docPath.ts"],"names":[],"mappings":"AAAA,mFAAmF;AAEnF,MAAM,OAAO,YAAa,SAAQ,KAAK;IACrC,YAAY,OAAe;QACzB,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,cAAc,CAAC;IAC7B,CAAC;CACF;AAED,MAAM,gBAAgB,GAAG,kCAAkC,CAAC;AAE5D,SAAS,cAAc,CAAC,GAAW;IACjC,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,KAAK,MAAM,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC;QACjC,IAAI,GAAG,KAAK,EAAE,IAAI,GAAG,KAAK,GAAG;YAAE,SAAS;QACxC,IAAI,GAAG,KAAK,IAAI,EAAE,CAAC;YACjB,IAAI,KAAK,CAAC,MAAM;gBAAE,KAAK,CAAC,GAAG,EAAE,CAAC;QAChC,CAAC;;YAAM,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACzB,CAAC;IACD,OAAO,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AACzB,CAAC;AAED,MAAM,UAAU,wBAAwB,CAAC,KAAa;IACpD,MAAM,GAAG,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC;IACzB,IAAI,CAAC,GAAG;QAAE,MAAM,IAAI,YAAY,CAAC,2BAA2B,CAAC,CAAC;IAC9D,IAAI,GAAG,CAAC,UAAU,CAAC,GAAG,CAAC;QAAE,MAAM,IAAI,YAAY,CAAC,uBAAuB,CAAC,CAAC;IACzE,IAAI,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC;QAAE,MAAM,IAAI,YAAY,CAAC,qBAAqB,CAAC,CAAC;IACtE,IAAI,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC;QAAE,MAAM,IAAI,YAAY,CAAC,0BAA0B,CAAC,CAAC;IAC3E,MAAM,KAAK,GAAG,GAAG,CAAC,WAAW,EAAE,CAAC;IAChC,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC;QACvD,MAAM,IAAI,YAAY,CAAC,0CAA0C,CAAC,CAAC;IACrE,CAAC;IACD,IAAI,CAAC,gBAAgB,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;QAChC,MAAM,IAAI,YAAY,CAAC,sCAAsC,CAAC,CAAC;IACjE,CAAC;IACD,MAAM,IAAI,GAAG,cAAc,CAAC,GAAG,CAAC,CAAC;IACjC,IAAI,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;QAClD,MAAM,IAAI,YAAY,CAAC,wBAAwB,CAAC,CAAC;IACnD,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC"}
@@ -0,0 +1,35 @@
1
+ /**
2
+ * 统一的 file_type 定义。
3
+ *
4
+ * mdocs 文档系统中,不同类型的文件/目录通过 file_type 区分:
5
+ * - md / dir:普通文档和目录(文档树中可见)
6
+ * - folder_desc:目录描述文件(___desc___.md,文档树中不可见,用于目录展示)
7
+ * - graph_file / graph_dir:图谱相关文件(文档树中不可见)
8
+ *
9
+ * 新增文件类型在这里统一定义,不要散落在各处。
10
+ */
11
+ export const FILE_TYPE = {
12
+ /** 普通 Markdown 文章(文档树中可见) */
13
+ DOCUMENT: 'md',
14
+ /** 普通目录(文档树中可见) */
15
+ FOLDER: 'dir',
16
+ /** 目录描述文件 ___desc___.md(文档树中不可见,用于展示目录介绍) */
17
+ FOLDER_DESC: 'folder_desc',
18
+ /** 图谱 JSON 文件(文档树中不可见) */
19
+ GRAPH_FILE: 'graph_file',
20
+ /** 图谱缓存目录 __graph__(文档树中不可见) */
21
+ GRAPH_DIR: 'graph_dir',
22
+ };
23
+ /** 判断是否是文档树中可见的类型(普通 md 文件 + 目录) */
24
+ export function isVisibleFileType(fileType) {
25
+ return fileType === FILE_TYPE.DOCUMENT || fileType === FILE_TYPE.FOLDER;
26
+ }
27
+ /** 判断是否是目录类型(普通目录或特殊目录) */
28
+ export function isFolderFileType(fileType) {
29
+ return fileType === FILE_TYPE.FOLDER || fileType === FILE_TYPE.GRAPH_DIR;
30
+ }
31
+ /** 判断是否是图谱相关的文件类型 */
32
+ export function isGraphFileType(fileType) {
33
+ return fileType === FILE_TYPE.GRAPH_FILE || fileType === FILE_TYPE.GRAPH_DIR;
34
+ }
35
+ //# sourceMappingURL=file-types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-types.js","sourceRoot":"","sources":["../../src/shared/file-types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,MAAM,CAAC,MAAM,SAAS,GAAG;IACvB,6BAA6B;IAC7B,QAAQ,EAAE,IAAI;IACd,mBAAmB;IACnB,MAAM,EAAE,KAAK;IACb,6CAA6C;IAC7C,WAAW,EAAE,aAAa;IAC1B,0BAA0B;IAC1B,UAAU,EAAE,YAAY;IACxB,gCAAgC;IAChC,SAAS,EAAE,WAAW;CACd,CAAC;AAIX,oCAAoC;AACpC,MAAM,UAAU,iBAAiB,CAAC,QAAgB;IAChD,OAAO,QAAQ,KAAK,SAAS,CAAC,QAAQ,IAAI,QAAQ,KAAK,SAAS,CAAC,MAAM,CAAC;AAC1E,CAAC;AAED,2BAA2B;AAC3B,MAAM,UAAU,gBAAgB,CAAC,QAAgB;IAC/C,OAAO,QAAQ,KAAK,SAAS,CAAC,MAAM,IAAI,QAAQ,KAAK,SAAS,CAAC,SAAS,CAAC;AAC3E,CAAC;AAED,qBAAqB;AACrB,MAAM,UAAU,eAAe,CAAC,QAAgB;IAC9C,OAAO,QAAQ,KAAK,SAAS,CAAC,UAAU,IAAI,QAAQ,KAAK,SAAS,CAAC,SAAS,CAAC;AAC/E,CAAC"}
@@ -0,0 +1,31 @@
1
+ /**
2
+ * 知识图谱相关的特殊文件名与工具函数。
3
+ *
4
+ * 图谱隐藏文件/目录在文档树中不可见,file_type 统一在 shared/file-types.ts 定义。
5
+ * 本文件只放文件名常量和路径判断函数。
6
+ */
7
+ import { FILE_TYPE } from './file-types.js';
8
+ /** 目录级图谱文件名(每个目录一个) */
9
+ export const DIR_GRAPH_FILENAME = '___graph___.json';
10
+ /** 文章级图谱缓存目录名(每个目录下一个) */
11
+ export const ARTICLE_GRAPH_DIRNAME = '__graph__';
12
+ /** 文章级图谱文件后缀 */
13
+ export const ARTICLE_GRAPH_FILE_SUFFIX = '.graph.json';
14
+ /** 兼容旧引用:graph_file / graph_dir 常量 */
15
+ export const GRAPH_FILE_TYPE = {
16
+ FILE: FILE_TYPE.GRAPH_FILE,
17
+ DIR: FILE_TYPE.GRAPH_DIR,
18
+ };
19
+ /** 判断文件名是否是目录级图谱文件 */
20
+ export function isDirGraphFile(name) {
21
+ return name.toLowerCase() === DIR_GRAPH_FILENAME.toLowerCase();
22
+ }
23
+ /** 判断目录名是否是文章级图谱缓存目录 */
24
+ export function isArticleGraphDir(name) {
25
+ return name.toLowerCase() === ARTICLE_GRAPH_DIRNAME.toLowerCase();
26
+ }
27
+ /** 生成文章级图谱文件的文件名 */
28
+ export function articleGraphFileName(documentId) {
29
+ return `${documentId}${ARTICLE_GRAPH_FILE_SUFFIX}`;
30
+ }
31
+ //# sourceMappingURL=graph-files.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"graph-files.js","sourceRoot":"","sources":["../../src/shared/graph-files.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAE5C,uBAAuB;AACvB,MAAM,CAAC,MAAM,kBAAkB,GAAG,kBAAkB,CAAC;AAErD,0BAA0B;AAC1B,MAAM,CAAC,MAAM,qBAAqB,GAAG,WAAW,CAAC;AAEjD,gBAAgB;AAChB,MAAM,CAAC,MAAM,yBAAyB,GAAG,aAAa,CAAC;AAEvD,sCAAsC;AACtC,MAAM,CAAC,MAAM,eAAe,GAAG;IAC7B,IAAI,EAAE,SAAS,CAAC,UAAU;IAC1B,GAAG,EAAE,SAAS,CAAC,SAAS;CAChB,CAAC;AAIX,sBAAsB;AACtB,MAAM,UAAU,cAAc,CAAC,IAAY;IACzC,OAAO,IAAI,CAAC,WAAW,EAAE,KAAK,kBAAkB,CAAC,WAAW,EAAE,CAAC;AACjE,CAAC;AAED,wBAAwB;AACxB,MAAM,UAAU,iBAAiB,CAAC,IAAY;IAC5C,OAAO,IAAI,CAAC,WAAW,EAAE,KAAK,qBAAqB,CAAC,WAAW,EAAE,CAAC;AACpE,CAAC;AAED,oBAAoB;AACpB,MAAM,UAAU,oBAAoB,CAAC,UAAkB;IACrD,OAAO,GAAG,UAAU,GAAG,yBAAyB,EAAE,CAAC;AACrD,CAAC"}