@dsh-libing/knowledge-base 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +16 -0
- package/README.md +80 -0
- package/cordis.patch.yml +3 -0
- package/icon.svg +6 -0
- package/lib/chunks.d.ts +8 -0
- package/lib/chunks.js +197 -0
- package/lib/client-types/client/BrowsePanel.d.ts +13 -0
- package/lib/client-types/client/Icon.d.ts +4 -0
- package/lib/client-types/client/ImageReview.d.ts +20 -0
- package/lib/client-types/client/ImportPanel.d.ts +19 -0
- package/lib/client-types/client/JobsPanel.d.ts +6 -0
- package/lib/client-types/client/KnowledgePage.d.ts +7 -0
- package/lib/client-types/client/Metadata.d.ts +10 -0
- package/lib/client-types/client/QualityReport.d.ts +6 -0
- package/lib/client-types/client/SettingsPanel.d.ts +6 -0
- package/lib/client-types/client/SourceText.d.ts +3 -0
- package/lib/client-types/client/UsageGuide.d.ts +8 -0
- package/lib/client-types/client/api.d.ts +953 -0
- package/lib/client-types/client/common.d.ts +11 -0
- package/lib/client-types/client/index.d.ts +14 -0
- package/lib/client-types/client/locales.d.ts +284 -0
- package/lib/client-types/client/searchCommand.d.ts +2 -0
- package/lib/client-types/client/useKnowledge.d.ts +251 -0
- package/lib/client-types/contract.d.ts +1197 -0
- package/lib/client-types/data.d.ts +1972 -0
- package/lib/client-types/settings.d.ts +33 -0
- package/lib/client-types/shared.d.ts +95 -0
- package/lib/client.js +305 -0
- package/lib/config.d.ts +13 -0
- package/lib/config.js +13 -0
- package/lib/contract.d.ts +1197 -0
- package/lib/contract.js +18 -0
- package/lib/data.d.ts +1972 -0
- package/lib/data.js +80 -0
- package/lib/document/convert.d.ts +5 -0
- package/lib/document/convert.js +325 -0
- package/lib/document/pdf-data.d.ts +99 -0
- package/lib/document/pdf-data.js +9 -0
- package/lib/document/pdf-layout.d.ts +24 -0
- package/lib/document/pdf-layout.js +234 -0
- package/lib/document/pdf-session.d.ts +39 -0
- package/lib/document/pdf-session.js +127 -0
- package/lib/document/pdf-vision.d.ts +26 -0
- package/lib/document/pdf-vision.js +115 -0
- package/lib/document/pdf-worker.d.ts +1 -0
- package/lib/document/pdf-worker.js +210 -0
- package/lib/document/pdf.d.ts +3 -0
- package/lib/document/pdf.js +218 -0
- package/lib/document/zip.d.ts +3 -0
- package/lib/document/zip.js +59 -0
- package/lib/figures/process.d.ts +187 -0
- package/lib/figures/process.js +159 -0
- package/lib/figures/prompts.d.ts +7 -0
- package/lib/figures/prompts.js +27 -0
- package/lib/index.d.ts +8 -0
- package/lib/index.js +42 -0
- package/lib/indexes.d.ts +38 -0
- package/lib/indexes.js +101 -0
- package/lib/jobs.d.ts +2806 -0
- package/lib/jobs.js +435 -0
- package/lib/models.d.ts +24 -0
- package/lib/models.js +120 -0
- package/lib/retrieval.d.ts +209 -0
- package/lib/retrieval.js +173 -0
- package/lib/routes.d.ts +3 -0
- package/lib/routes.js +152 -0
- package/lib/runtime.d.ts +48 -0
- package/lib/runtime.js +41 -0
- package/lib/settings.d.ts +33 -0
- package/lib/settings.js +19 -0
- package/lib/shared.d.ts +95 -0
- package/lib/shared.js +17 -0
- package/lib/skills.d.ts +3 -0
- package/lib/skills.js +21 -0
- package/lib/store.d.ts +409 -0
- package/lib/store.js +328 -0
- package/lib/tools.d.ts +3 -0
- package/lib/tools.js +72 -0
- package/lib/util.d.ts +25 -0
- package/lib/util.js +89 -0
- package/package.json +125 -0
- package/skills/libing-kb-build/SKILL.md +10 -0
- package/skills/libing-kb-search/SKILL.md +10 -0
- package/skills/libing-knowledge-base/SKILL.md +15 -0
- package/skills/libing-knowledge-base/references/build.md +23 -0
- package/skills/libing-knowledge-base/references/search.md +21 -0
package/lib/store.js
ADDED
|
@@ -0,0 +1,328 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { registrySchema, currentSchema, manifestSchema, uploadSchema, overlaySchema } from "./data.js";
|
|
3
|
+
import { code, message, required } from "./util.js";
|
|
4
|
+
import { mkdir, readFile, readdir, rename, cp, rm, stat, lstat } from 'node:fs/promises';
|
|
5
|
+
import { join, basename } from 'node:path';
|
|
6
|
+
import { randomUUID } from 'node:crypto';
|
|
7
|
+
import { atomicWrite, fileBytes, hash, id, now, readJson, safePath, text } from "./util.js";
|
|
8
|
+
export const DEFAULT_SCHEMA = `# 知识库治理约定\n\n保留原文中的型号、管脚、单位、数值、极性和适用条件。图片描述与原文分别标注;不确定的信息保留待复核。摘要用于导航,回答依据原文和可核验图像。引用包含文档版本、章节、片段和图表编号。\n`;
|
|
9
|
+
export class Store {
|
|
10
|
+
config;
|
|
11
|
+
root;
|
|
12
|
+
registryTail;
|
|
13
|
+
constructor(config) { this.config = config; this.root = config.dataRoot; this.registryTail = Promise.resolve(); }
|
|
14
|
+
async init() {
|
|
15
|
+
for (const dir of ['kbs', 'jobs', 'uploads', 'cache'])
|
|
16
|
+
await mkdir(join(this.root, dir), { recursive: true });
|
|
17
|
+
if (!await readJson(join(this.root, 'registry.json'), registrySchema, null))
|
|
18
|
+
await atomicWrite(join(this.root, 'registry.json'), { formatVersion: 1, kbs: [] });
|
|
19
|
+
}
|
|
20
|
+
async serialized(fn) {
|
|
21
|
+
const next = this.registryTail.then(fn);
|
|
22
|
+
this.registryTail = next.catch(() => { });
|
|
23
|
+
return next;
|
|
24
|
+
}
|
|
25
|
+
async list(includeDeleted = false) { return (await readJson(join(this.root, 'registry.json'), registrySchema)).kbs.filter(k => includeDeleted || !k.deletedAt); }
|
|
26
|
+
async deleted() { return (await this.list(true)).filter(k => k.deletedAt); }
|
|
27
|
+
async activities() { return (await readJson(join(this.root, 'registry.json'), registrySchema)).activities || []; }
|
|
28
|
+
record(registry, action, kb) {
|
|
29
|
+
registry.activities = [...(registry.activities || []), { eventId: randomUUID(), at: now(), action, kbId: kb.kbId, name: kb.name }].slice(-100);
|
|
30
|
+
}
|
|
31
|
+
async trash(kbId, expectedName) {
|
|
32
|
+
return this.serialized(async () => {
|
|
33
|
+
const registry = await readJson(join(this.root, 'registry.json'), registrySchema), kb = registry.kbs.find(k => k.kbId === id(kbId) && !k.deletedAt);
|
|
34
|
+
if (!kb)
|
|
35
|
+
throw new Error('知识库不存在,请刷新');
|
|
36
|
+
if (kb.name !== expectedName)
|
|
37
|
+
throw new Error('知识库名称已变化,请刷新后确认删除');
|
|
38
|
+
kb.deletedAt = now();
|
|
39
|
+
this.record(registry, 'trash', kb);
|
|
40
|
+
await atomicWrite(join(this.root, 'registry.json'), registry);
|
|
41
|
+
return kb;
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
async restore(kbId) {
|
|
45
|
+
return this.serialized(async () => {
|
|
46
|
+
const registry = await readJson(join(this.root, 'registry.json'), registrySchema), kb = registry.kbs.find(k => k.kbId === id(kbId) && k.deletedAt);
|
|
47
|
+
if (!kb)
|
|
48
|
+
throw new Error('知识库不在回收站,请刷新');
|
|
49
|
+
if (kb.purge)
|
|
50
|
+
throw new Error('知识库已开始彻底删除,不能恢复;请继续彻底删除');
|
|
51
|
+
if (registry.kbs.some(k => !k.deletedAt && k.name === kb.name))
|
|
52
|
+
throw new Error('同名知识库已存在,不能恢复;请先删除同名库');
|
|
53
|
+
delete kb.deletedAt;
|
|
54
|
+
this.record(registry, 'restore', kb);
|
|
55
|
+
await atomicWrite(join(this.root, 'registry.json'), registry);
|
|
56
|
+
return kb;
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
async purgeTarget(group, key) {
|
|
60
|
+
const parent = await safePath(this.root, group);
|
|
61
|
+
if ((await lstat(parent)).isSymbolicLink())
|
|
62
|
+
throw new Error('不能通过符号链接删除知识库数据');
|
|
63
|
+
const path = await safePath(parent, id(key), false);
|
|
64
|
+
const info = await lstat(path).catch(error => {
|
|
65
|
+
if (code(error) === 'ENOENT')
|
|
66
|
+
return null;
|
|
67
|
+
throw error;
|
|
68
|
+
});
|
|
69
|
+
if (info && (!info.isDirectory() || info.isSymbolicLink()))
|
|
70
|
+
throw new Error('不能删除非目录或符号链接对象');
|
|
71
|
+
return path;
|
|
72
|
+
}
|
|
73
|
+
async removePurgeTree(path) { await rm(path, { recursive: true, force: true, maxRetries: 2, retryDelay: 100 }); }
|
|
74
|
+
async purge(kbId, expectedName, confirmed, jobIds = []) {
|
|
75
|
+
if (confirmed !== true)
|
|
76
|
+
throw new Error('必须明确确认彻底删除不可恢复');
|
|
77
|
+
return this.serialized(async () => {
|
|
78
|
+
const registry = await readJson(join(this.root, 'registry.json'), registrySchema), kb = registry.kbs.find(k => k.kbId === id(kbId) && k.deletedAt);
|
|
79
|
+
if (!kb)
|
|
80
|
+
throw new Error('只能彻底删除回收站中的知识库');
|
|
81
|
+
if (kb.name !== expectedName)
|
|
82
|
+
throw new Error('知识库名称已变化,请重新确认彻底删除');
|
|
83
|
+
const targets = [...new Set([...(kb.purge?.jobIds || []), ...jobIds])];
|
|
84
|
+
const kbPath = await this.purgeTarget('kbs', kb.kbId), jobPaths = await Promise.all(targets.map(key => this.purgeTarget('jobs', key)));
|
|
85
|
+
// Persist intent before removing files: a crash or partial failure must not allow restoration.
|
|
86
|
+
kb.purge = { startedAt: kb.purge?.startedAt || now(), jobIds: targets };
|
|
87
|
+
await atomicWrite(join(this.root, 'registry.json'), registry);
|
|
88
|
+
try {
|
|
89
|
+
for (const path of jobPaths)
|
|
90
|
+
await this.removePurgeTree(path);
|
|
91
|
+
await this.removePurgeTree(kbPath);
|
|
92
|
+
registry.kbs = registry.kbs.filter(k => k.kbId !== kb.kbId);
|
|
93
|
+
this.record(registry, 'purge', kb);
|
|
94
|
+
await atomicWrite(join(this.root, 'registry.json'), registry);
|
|
95
|
+
return { kbId: kb.kbId, name: kb.name, purged: true };
|
|
96
|
+
}
|
|
97
|
+
catch (error) {
|
|
98
|
+
// Keep the pending entry so cleanup is retryable and the UI cannot restore incomplete data.
|
|
99
|
+
registry.kbs = registry.kbs.filter(k => k.kbId !== kb.kbId).concat(kb);
|
|
100
|
+
registry.activities = (registry.activities || []).filter(e => !(e.kbId === kb.kbId && e.action === 'purge' && e.at >= required(kb.purge).startedAt));
|
|
101
|
+
required(kb.purge).error = `彻底删除未完成,请继续删除:${message(error)}`;
|
|
102
|
+
await atomicWrite(join(this.root, 'registry.json'), registry);
|
|
103
|
+
throw new Error(required(kb.purge).error);
|
|
104
|
+
}
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
async kb(selector) {
|
|
108
|
+
const kbs = await this.list(), matches = kbs.filter(k => k.kbId === selector || k.name === selector);
|
|
109
|
+
if (matches.length !== 1)
|
|
110
|
+
throw new Error(matches.length ? '知识库名称重复,请使用 kbId' : '知识库不存在,请先创建或选择');
|
|
111
|
+
await safePath(this.root, join('kbs', id(required(matches[0]).kbId)), true);
|
|
112
|
+
return required(matches[0]);
|
|
113
|
+
}
|
|
114
|
+
async create(name) {
|
|
115
|
+
name = text(name, '知识库名', 120);
|
|
116
|
+
return this.serialized(async () => {
|
|
117
|
+
const registry = await readJson(join(this.root, 'registry.json'), registrySchema);
|
|
118
|
+
if (registry.kbs.some(k => !k.deletedAt && k.name === name))
|
|
119
|
+
throw new Error('同名知识库已存在');
|
|
120
|
+
const kb = { kbId: `kb-${randomUUID().slice(0, 12)}`, name, createdAt: now() };
|
|
121
|
+
const root = join(this.root, 'kbs', kb.kbId);
|
|
122
|
+
await mkdir(root, { recursive: true });
|
|
123
|
+
await atomicWrite(join(root, 'schema.md'), DEFAULT_SCHEMA);
|
|
124
|
+
await atomicWrite(join(root, 'log.md'), '# 构建与复核流水\n\n');
|
|
125
|
+
registry.kbs.push(kb);
|
|
126
|
+
this.record(registry, 'create', kb);
|
|
127
|
+
await atomicWrite(join(this.root, 'registry.json'), registry);
|
|
128
|
+
return kb;
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
kbRoot(kbId) { return join(this.root, 'kbs', id(kbId, 'kbId')); }
|
|
132
|
+
async inspect(kbId, revision) {
|
|
133
|
+
const kb = await this.kb(kbId), kbPath = await safePath(this.root, join('kbs', kb.kbId));
|
|
134
|
+
const current = await this.current(kb.kbId);
|
|
135
|
+
if (!revision && !current)
|
|
136
|
+
return { location: { kbPath, revision: null, wikiPath: null, indexPath: null }, summary: { revision: null, documentCount: 0, chunkCount: 0, coverageIncomplete: false } };
|
|
137
|
+
const snapshot = await this.revision(kb.kbId, revision || required(current).revision);
|
|
138
|
+
return { location: { kbPath, revision: snapshot.revision, wikiPath: await safePath(snapshot.root, 'wiki'), indexPath: await safePath(snapshot.root, 'index.md') }, summary: { revision: snapshot.revision, documentCount: snapshot.manifest.documents.length, chunkCount: snapshot.manifest.documents.reduce((sum, d) => sum + d.chunks.length, 0), coverageIncomplete: Boolean(snapshot.manifest.coverageIncomplete) } };
|
|
139
|
+
}
|
|
140
|
+
async location(kbId, revision) { return (await this.inspect(kbId, revision)).location; }
|
|
141
|
+
async schema(kbId) { return readFile(await safePath(this.kbRoot(kbId), 'schema.md'), 'utf8'); }
|
|
142
|
+
async setSchema(kbId, content, expectedHash) {
|
|
143
|
+
if (typeof content !== 'string' || content.length > 20000)
|
|
144
|
+
throw new Error('库约定不能超过 20000 字符');
|
|
145
|
+
return this.serialized(async () => {
|
|
146
|
+
const old = await this.schema(kbId);
|
|
147
|
+
if (hash(old) !== expectedHash)
|
|
148
|
+
throw new Error('库约定已变化,请刷新后再保存');
|
|
149
|
+
await atomicWrite(join(this.kbRoot(kbId), 'schema.md'), content);
|
|
150
|
+
return { content, hash: hash(content) };
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
async upload(data, filename) {
|
|
154
|
+
filename = text(basename(filename), '文件名', 200);
|
|
155
|
+
if (!/\.(?:docx?|pdf)$/i.test(filename) || data.length > this.config.maxInputBytes)
|
|
156
|
+
throw new Error('只支持大小限制内的 .docx / .doc / .pdf 文件');
|
|
157
|
+
const receiptId = `u-${randomUUID()}`, file = join(this.root, 'uploads', receiptId + '.bin');
|
|
158
|
+
await atomicWrite(file, data);
|
|
159
|
+
await atomicWrite(join(this.root, 'uploads', receiptId + '.json'), { receiptId, filename, createdAt: now(), bytes: data.length, hash: hash(data) });
|
|
160
|
+
return { receiptId, filename, bytes: data.length };
|
|
161
|
+
}
|
|
162
|
+
async uploaded(receiptId) {
|
|
163
|
+
id(receiptId, '上传收据');
|
|
164
|
+
const receipt = await readJson(await safePath(this.root, join('uploads', receiptId + '.json')), uploadSchema);
|
|
165
|
+
const data = await fileBytes(await safePath(this.root, join('uploads', receiptId + '.bin')), this.config.maxInputBytes);
|
|
166
|
+
if (data.length !== receipt.bytes || hash(data) !== receipt.hash)
|
|
167
|
+
throw new Error('上传内容已变化');
|
|
168
|
+
return { ...receipt, data };
|
|
169
|
+
}
|
|
170
|
+
async current(kbId) { return readJson(join(this.kbRoot(kbId), 'current.json'), currentSchema, null); }
|
|
171
|
+
async revision(kbId, requested) {
|
|
172
|
+
await this.kb(kbId);
|
|
173
|
+
const current = await this.current(kbId), revision = requested || current?.revision;
|
|
174
|
+
if (!revision)
|
|
175
|
+
throw new Error('知识库尚无已发布版本');
|
|
176
|
+
id(revision, 'revision');
|
|
177
|
+
const root = await safePath(this.kbRoot(kbId), join('revisions', revision));
|
|
178
|
+
const manifest = await readJson(join(root, 'manifest.json'), manifestSchema);
|
|
179
|
+
if (manifest.revision !== revision)
|
|
180
|
+
throw new Error('版本清单不一致');
|
|
181
|
+
return { root, manifest, revision };
|
|
182
|
+
}
|
|
183
|
+
async readChunk(kbId, revision, chunkId) {
|
|
184
|
+
const snapshot = await this.revision(kbId, revision);
|
|
185
|
+
const chunk = snapshot.manifest.documents.flatMap(d => d.chunks).find(c => c.chunkId === chunkId);
|
|
186
|
+
if (!chunk)
|
|
187
|
+
throw new Error('片段不在所选知识库版本中');
|
|
188
|
+
const content = await this.chunkContent(snapshot, chunk);
|
|
189
|
+
return { ...snapshot, chunk, content };
|
|
190
|
+
}
|
|
191
|
+
async chunkContent(snapshot, chunk) {
|
|
192
|
+
const content = await readFile(await safePath(snapshot.root, chunk.path), 'utf8');
|
|
193
|
+
if (hash(content) !== chunk.contentHash)
|
|
194
|
+
throw new Error('片段内容与清单不一致,请重建');
|
|
195
|
+
return content;
|
|
196
|
+
}
|
|
197
|
+
async asset(kbId, revision, docId, assetId) {
|
|
198
|
+
const snapshot = await this.revision(kbId, revision), doc = snapshot.manifest.documents.find(d => d.docId === docId);
|
|
199
|
+
const asset = doc?.assets.find(a => a.assetId === assetId);
|
|
200
|
+
if (!asset)
|
|
201
|
+
throw new Error('图片不在所选文档版本中');
|
|
202
|
+
const file = await safePath(snapshot.root, join('sources', id(docId), asset.path));
|
|
203
|
+
if (`a-${hash(await fileBytes(file, this.config.maxUncompressedBytes)).slice(0, 20)}` !== asset.assetId)
|
|
204
|
+
throw new Error('图片内容与清单不一致');
|
|
205
|
+
return { ...snapshot, asset, file, doc: required(doc) };
|
|
206
|
+
}
|
|
207
|
+
async rawSnapshot(kbId, docId, sourceHash, data, info) {
|
|
208
|
+
const root = join(this.kbRoot(kbId), 'raw', id(docId), id(sourceHash));
|
|
209
|
+
await mkdir(root, { recursive: true });
|
|
210
|
+
const file = await safePath(root, info.sourceFormat === 'pdf' ? 'source.pdf' : 'source.docx', false);
|
|
211
|
+
const previous = await readJson(join(root, 'source.json'), z.record(z.string(), z.json()), null);
|
|
212
|
+
if (previous) {
|
|
213
|
+
if (hash(await readFile(file)) !== sourceHash)
|
|
214
|
+
throw new Error('原件快照与哈希不一致');
|
|
215
|
+
}
|
|
216
|
+
else {
|
|
217
|
+
await atomicWrite(file, data);
|
|
218
|
+
await atomicWrite(join(root, 'source.json'), info);
|
|
219
|
+
}
|
|
220
|
+
return { root, file };
|
|
221
|
+
}
|
|
222
|
+
async rawSource(kbId, doc) {
|
|
223
|
+
const root = await safePath(this.kbRoot(kbId), join('raw', id(doc.docId), id(doc.sourceHash)));
|
|
224
|
+
const file = await safePath(root, doc.sourceFormat === 'pdf' ? 'source.pdf' : 'source.docx');
|
|
225
|
+
const data = await fileBytes(file, this.config.maxInputBytes);
|
|
226
|
+
if (hash(data) !== doc.sourceHash)
|
|
227
|
+
throw new Error('原件快照与版本哈希不一致');
|
|
228
|
+
return { file, data };
|
|
229
|
+
}
|
|
230
|
+
async carryPrevious(kbId, destination, replaceDocId) {
|
|
231
|
+
const current = await this.current(kbId);
|
|
232
|
+
if (!current)
|
|
233
|
+
return [];
|
|
234
|
+
const { root, manifest } = await this.revision(kbId, current.revision);
|
|
235
|
+
const docs = [];
|
|
236
|
+
for (const document of manifest.documents.filter(d => d.docId !== replaceDocId)) {
|
|
237
|
+
const chunks = [];
|
|
238
|
+
for (const chunk of document.chunks) {
|
|
239
|
+
const content = await this.chunkContent({ root, manifest, revision: manifest.revision }, chunk);
|
|
240
|
+
chunks.push({ ...chunk, content, body: content.split("\n---\n\n").slice(1).join("\n---\n\n") });
|
|
241
|
+
}
|
|
242
|
+
docs.push({ ...document, chunks });
|
|
243
|
+
}
|
|
244
|
+
for (const doc of docs) {
|
|
245
|
+
await mkdir(join(destination, 'sources'), { recursive: true });
|
|
246
|
+
await cp(await safePath(root, join('sources', doc.docId)), join(destination, 'sources', doc.docId), { recursive: true, errorOnExist: false });
|
|
247
|
+
for (const chunk of doc.chunks) {
|
|
248
|
+
const content = await readFile(await safePath(root, chunk.path), 'utf8');
|
|
249
|
+
if (hash(content) !== chunk.contentHash)
|
|
250
|
+
throw new Error('旧版本片段已变化,请先修复');
|
|
251
|
+
chunk.content = content;
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
return docs;
|
|
255
|
+
}
|
|
256
|
+
async publish(kbId, revision, stage, manifest, operation) {
|
|
257
|
+
const kbRoot = this.kbRoot(kbId), target = join(kbRoot, 'revisions', id(revision));
|
|
258
|
+
await atomicWrite(join(stage, 'manifest.json'), manifest);
|
|
259
|
+
await mkdir(join(kbRoot, 'revisions'), { recursive: true });
|
|
260
|
+
try {
|
|
261
|
+
await rename(stage, target);
|
|
262
|
+
}
|
|
263
|
+
catch (error) {
|
|
264
|
+
if (!['EEXIST', 'ENOTEMPTY'].includes(String(code(error))))
|
|
265
|
+
throw error;
|
|
266
|
+
const existing = await readJson(join(target, 'manifest.json'), manifestSchema);
|
|
267
|
+
if (existing.buildKey !== manifest.buildKey)
|
|
268
|
+
throw new Error('版本发布冲突');
|
|
269
|
+
}
|
|
270
|
+
// The pointer owns visibility. Replaying the same job after a crash is idempotent.
|
|
271
|
+
await atomicWrite(join(kbRoot, 'current.json'), { revision, publishedAt: now(), buildKey: manifest.buildKey, coverageIncomplete: manifest.coverageIncomplete });
|
|
272
|
+
await this.serialized(async () => {
|
|
273
|
+
const logFile = join(kbRoot, 'log.md'), old = await readFile(logFile, 'utf8'), line = `- ${operation.jobId} ${now()} ${operation.kind} ${revision} ← ${operation.docId}\n`;
|
|
274
|
+
if (!old.includes(`- ${operation.jobId} `))
|
|
275
|
+
await atomicWrite(logFile, old + line);
|
|
276
|
+
});
|
|
277
|
+
return target;
|
|
278
|
+
}
|
|
279
|
+
async overlays(kbId) { return readJson(join(this.kbRoot(kbId), 'overlays.json'), z.record(z.string(), overlaySchema), {}); }
|
|
280
|
+
async setOverlay(kbId, key, value, expectedHash) {
|
|
281
|
+
return this.serialized(async () => {
|
|
282
|
+
const overlays = await this.overlays(kbId);
|
|
283
|
+
if (expectedHash && hash(overlays[key] || null) !== expectedHash)
|
|
284
|
+
throw new Error('图片复核已变化,请刷新');
|
|
285
|
+
if (value === null)
|
|
286
|
+
delete overlays[key];
|
|
287
|
+
else
|
|
288
|
+
overlays[key] = { ...value, updatedAt: now() };
|
|
289
|
+
await atomicWrite(join(this.kbRoot(kbId), 'overlays.json'), overlays);
|
|
290
|
+
return overlays[key];
|
|
291
|
+
});
|
|
292
|
+
}
|
|
293
|
+
async jobDirectories() { return (await readdir(join(this.root, 'jobs'), { withFileTypes: true })).filter(d => d.isDirectory()).map(d => join(this.root, 'jobs', d.name)); }
|
|
294
|
+
async acquireOwner() {
|
|
295
|
+
const dir = join(this.root, '.owner.lock'), nonce = randomUUID();
|
|
296
|
+
try {
|
|
297
|
+
await mkdir(dir);
|
|
298
|
+
}
|
|
299
|
+
catch (error) {
|
|
300
|
+
if (code(error) !== 'EEXIST')
|
|
301
|
+
throw error;
|
|
302
|
+
const owner = await readJson(join(dir, 'owner.json'), z.object({ pid: z.number().int().positive(), startedAt: z.string(), nonce: z.string().optional() }), null);
|
|
303
|
+
if (!owner) {
|
|
304
|
+
const info = await stat(dir);
|
|
305
|
+
if (Date.now() - info.mtimeMs < 60000)
|
|
306
|
+
throw new Error('知识库任务服务正在初始化');
|
|
307
|
+
}
|
|
308
|
+
if (owner?.pid) {
|
|
309
|
+
try {
|
|
310
|
+
process.kill(owner.pid, 0);
|
|
311
|
+
throw new Error('另一个知识库任务服务正在使用此数据根目录');
|
|
312
|
+
}
|
|
313
|
+
catch (e) {
|
|
314
|
+
if (code(e) !== 'ESRCH')
|
|
315
|
+
throw e;
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
await rm(dir, { recursive: true, force: true });
|
|
319
|
+
await mkdir(dir);
|
|
320
|
+
}
|
|
321
|
+
await atomicWrite(join(dir, 'owner.json'), { pid: process.pid, startedAt: now(), nonce });
|
|
322
|
+
return async () => {
|
|
323
|
+
const owner = await readJson(join(dir, 'owner.json'), z.object({ nonce: z.string().optional() }), null);
|
|
324
|
+
if (owner?.nonce === nonce)
|
|
325
|
+
await rm(dir, { recursive: true, force: true });
|
|
326
|
+
};
|
|
327
|
+
}
|
|
328
|
+
}
|
package/lib/tools.d.ts
ADDED
package/lib/tools.js
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { defineTool } from '@deepseek-ai/dsh-tools';
|
|
2
|
+
import { AttachmentId } from '@deepseek-ai/dsh-attachment';
|
|
3
|
+
import { z } from 'zod';
|
|
4
|
+
import { prepareImage } from "./figures/process.js";
|
|
5
|
+
import { buildSchema, jobActionSchema, catalogQuerySchema, readQuerySchema, searchSchema, assetQuerySchema } from "./shared.js";
|
|
6
|
+
import { checkAbort, message } from "./util.js";
|
|
7
|
+
const str = (description) => ({ type: 'string', description });
|
|
8
|
+
const bool = (description) => ({ type: 'boolean', description });
|
|
9
|
+
const int = (description) => ({ type: 'integer', description });
|
|
10
|
+
const selector = str('已登记知识库 ID 或唯一库名'), revision = str('固定的知识库版本;首次检索可省略');
|
|
11
|
+
const canonical = (value) => z.json().parse(JSON.parse(JSON.stringify(value)));
|
|
12
|
+
function jobView(job) {
|
|
13
|
+
const { jobId, kbId, docId, revision, state, error, outputPath, progress, routes, usage, report, failures } = job;
|
|
14
|
+
return { jobId, kbId, docId, revision, state, error, outputPath, progress, models: routes, usage: { calls: usage.calls, tokens: usage.tokens, reportedCalls: usage.reportedCalls }, report: report ? { chunks: report.chunks, figures: report.figures, withinTarget: report.withinTarget, targetChars: report.targetChars, issueCount: report.issues.length, issues: report.issues.slice(0, 12), ...report.pdf ? { pdf: { pageCount: report.pdf.pageCount, selectedPages: report.pdf.selectedPages, ocrPages: report.pdf.ocrPages, matchedBookmarks: report.pdf.matchedBookmarks, needsReviewPages: report.pdf.pages.filter(p => p.status === 'needs-review' || p.status === 'failed').map(p => p.page).slice(0, 30) } } : {} } : null, failures: failures.slice(-3) };
|
|
15
|
+
}
|
|
16
|
+
const attachmentSchema = z.object({ attachmentId: z.string().transform(AttachmentId), mediaType: z.enum(['image/png', 'image/jpeg', 'image/webp', 'image/gif']), bytes: z.number(), width: z.number(), height: z.number(), name: z.string().optional(), originalDimensions: z.object({ width: z.number(), height: z.number() }).optional() }).transform(({ name, originalDimensions, ...value }) => ({ ...value, ...name === undefined ? {} : { name }, ...originalDimensions === undefined ? {} : { originalDimensions } }));
|
|
17
|
+
/** Registry validates declared parameters, then strict domain schemas reject unknown identities and options. */
|
|
18
|
+
export function tools(runtime) {
|
|
19
|
+
const { jobs, store, retrieval, ready, config } = runtime;
|
|
20
|
+
const define = (name, title, description, parameters, execute, render) => defineTool({
|
|
21
|
+
name: `libing_${name}`, description, parameters, output: { schema: { type: 'json' }, render: render ?? ((_args, value) => [{ type: 'text', text: JSON.stringify(value) }]) },
|
|
22
|
+
presentCall: args => ({ card: 'generic', title, kind: 'other', rawInput: args }),
|
|
23
|
+
execute: async (args, exec) => { await ready; checkAbort(exec.signal); return runtime.run(async (signal) => canonical(await execute(args, { ...exec, signal })), exec.signal); },
|
|
24
|
+
});
|
|
25
|
+
return [
|
|
26
|
+
define('kb_build', '构建知识库', '将指定 Word / PDF 构建为知识库;返回后台 jobId。仅明确允许时建库、跳过图片或接受缺口。', {
|
|
27
|
+
kbId: { ...selector, required: true }, createKb: bool('明确创建新库'), sourcePath: str('宿主本地 .docx/.doc/.pdf 路径,与 receiptId 二选一'), receiptId: str('页面上传收据'), title: str('文档标题'), sourceVersion: str('原文版本标签'), docId: str('更新时用原 docId'), chunkTargetChars: int('目标字符数 256–60000,默认6000'), pageStart: int('PDF 实际起始页,1起'), pageEnd: int('PDF 实际结束页,包含该页'), confirmRangeChange: bool('明确同意替换已有PDF范围'), visionProvider: str('宿主图片模型供应商'), visionModel: str('图片模型ID'), summaryProvider: str('宿主摘要供应商'), summaryModel: str('摘要模型ID'), skipImages: bool('明确跳过图片'), acceptPartial: bool('明确接受覆盖缺口'),
|
|
28
|
+
}, async (raw, exec) => {
|
|
29
|
+
let args = buildSchema.parse(raw);
|
|
30
|
+
if (args.createKb) {
|
|
31
|
+
try {
|
|
32
|
+
await store.kb(args.kbId);
|
|
33
|
+
}
|
|
34
|
+
catch (error) {
|
|
35
|
+
if (!message(error).startsWith('知识库不存在'))
|
|
36
|
+
throw error;
|
|
37
|
+
args = { ...args, kbId: (await store.create(args.kbId)).kbId };
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
const route = exec.agent?.session.requestHeader()?.config ?? exec.agent?.options;
|
|
41
|
+
return jobView(await jobs.submit(args, { sessionId: exec.agent?.session.id, ...route?.provider && route.model ? { route: { provider: route.provider, model: route.model } } : {} }));
|
|
42
|
+
}),
|
|
43
|
+
define('kb_job', '知识库任务', '查看、重试或取消任务。needs-review尚未发布,接受缺口及提高预算都需明确授权。', {
|
|
44
|
+
jobId: { ...str('构建任务ID'), required: true }, action: { type: 'string', enum: ['status', 'retry', 'cancel'] }, acceptPartial: bool('明确接受覆盖缺口'), maxModelCalls: int('新累计调用上限'), maxModelTokens: int('新累计实际token上限'),
|
|
45
|
+
}, async (raw) => { const args = jobActionSchema.parse(raw); return jobView(await jobs.action(args.jobId, args.action, args)); }),
|
|
46
|
+
define('kb_catalog', '知识库目录', '读取库清单或分页章节摘要。先选库,再按docId/nodeId导航。', {
|
|
47
|
+
kbId: selector, revision, docId: str('文档ID'), nodeId: str('章节节点ID'), page: int('索引页,从0起'),
|
|
48
|
+
}, async (raw) => retrieval.catalog(catalogQuerySchema.parse(raw))),
|
|
49
|
+
define('kb_search', '检索知识库', '用原始型号/信号检索正文;返回固定版本、来源位置、证据窗口和续读入口。', {
|
|
50
|
+
kbId: { ...selector, required: true }, revision, query: { ...str('问题或关键词'), required: true }, keywords: { type: 'array', items: { type: 'string' }, description: '原始标识或明确别名,最多32个' }, docId: str('可选文档范围'), limit: int('候选数1–30,默认6'),
|
|
51
|
+
}, async (raw) => retrieval.search(searchSchema.parse(raw))),
|
|
52
|
+
define('kb_read', '读取知识库原文', '短片段全文读取,长片段按匹配语义块与continuation续读;引用固定版本。', {
|
|
53
|
+
kbId: { ...selector, required: true }, revision, chunkId: { ...str('候选片段ID'), required: true }, query: str('长片段定位关键词'), keywords: { type: 'array', items: { type: 'string' } }, segmentIndex: int('continuation语义块编号'), offset: int('Unicode字符偏移'),
|
|
54
|
+
}, async (raw) => retrieval.read(readQuerySchema.parse(raw))),
|
|
55
|
+
define('kb_read_image', '核对知识库原图', '读取所选文档版本原图及裁片。当前模型须声明图片输入能力;忽略的图片不可作为证据。', {
|
|
56
|
+
kbId: { ...selector, required: true }, revision, docId: { ...str('文档ID'), required: true }, assetId: { ...str('图片ID'), required: true },
|
|
57
|
+
}, async (raw, exec) => {
|
|
58
|
+
const args = assetQuerySchema.parse(raw), kb = await store.kb(args.kbId), asset = await store.asset(kb.kbId, args.revision, args.docId, args.assetId);
|
|
59
|
+
const figures = asset.doc.figures.filter(f => f.assetId === args.assetId && f.status !== 'ignored'), region = asset.asset.source && asset.doc.sourceFormat === 'pdf';
|
|
60
|
+
if (!figures.length && (!region || asset.doc.figures.some(f => f.assetId === args.assetId && f.status === 'ignored')))
|
|
61
|
+
throw new Error('此版本的图片已忽略,请在图片复核恢复导入');
|
|
62
|
+
const route = exec.agent?.session.requestHeader()?.config ?? exec.agent?.options;
|
|
63
|
+
if (!route?.provider || !route.model)
|
|
64
|
+
throw new Error('当前会话模型路由不可用');
|
|
65
|
+
const info = await runtime.models.ctx.llm.resolveModelInfo(route.provider, route.model, exec.signal);
|
|
66
|
+
if (!info.inputModalities?.includes('image'))
|
|
67
|
+
throw new Error('当前模型未声明图片输入能力,请切换图像模型');
|
|
68
|
+
const prepared = await prepareImage(runtime.models.ctx, asset.file, config, exec.signal);
|
|
69
|
+
return { info: { kbId: kb.kbId, revision: asset.revision, docId: args.docId, assetId: args.assetId, ...region ? { source: asset.asset.source, selectedPages: asset.doc.selectedPages } : {}, figures: figures.map(f => ({ occurrenceId: f.occurrenceId, caption: f.caption, status: f.status, source: f.source })) }, images: prepared.images };
|
|
70
|
+
}, (_args, value) => { const result = z.object({ info: z.json(), images: z.array(z.object({ attachment: attachmentSchema })) }).parse(value); return [{ type: 'text', text: JSON.stringify(result.info) }, ...result.images.map(image => ({ type: 'image', attachment: image.attachment }))]; }),
|
|
71
|
+
];
|
|
72
|
+
}
|
package/lib/util.d.ts
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { z } from 'zod';
|
|
2
|
+
import type { TokenUsage } from '@deepseek-ai/dsh-llm';
|
|
3
|
+
export declare const hash: (value: unknown) => string;
|
|
4
|
+
export declare const chars: (value: unknown) => number;
|
|
5
|
+
export declare const clip: (value: unknown, length: number) => string;
|
|
6
|
+
export declare const escapeHtml: (value: unknown) => string;
|
|
7
|
+
export declare const mdText: (value: unknown) => string;
|
|
8
|
+
export declare const now: () => string;
|
|
9
|
+
export declare const message: (error: unknown) => string;
|
|
10
|
+
export declare const code: (error: unknown) => unknown;
|
|
11
|
+
export declare function id(value: unknown, label?: string): string;
|
|
12
|
+
export declare function text(value: unknown, label: string, max?: number): string;
|
|
13
|
+
export declare function required<T>(value: T | null | undefined, label?: string): T;
|
|
14
|
+
export declare function readJson<T>(file: string, schema: z.ZodType<T>): Promise<T>;
|
|
15
|
+
export declare function readJson<T, F>(file: string, schema: z.ZodType<T>, fallback: F): Promise<T | F>;
|
|
16
|
+
export declare function atomicWrite(file: string, value: unknown): Promise<void>;
|
|
17
|
+
export declare function safePath(root: string, rel?: string, existing?: boolean): Promise<string>;
|
|
18
|
+
export declare function fileBytes(file: string, max: number): Promise<Buffer>;
|
|
19
|
+
export declare function checkAbort(signal: AbortSignal | undefined): void;
|
|
20
|
+
export declare function tokensUsed(usage: TokenUsage | undefined): number;
|
|
21
|
+
export declare function deferred<T>(): {
|
|
22
|
+
resolve: (value: T) => void;
|
|
23
|
+
reject: (error: unknown) => void;
|
|
24
|
+
promise: Promise<T>;
|
|
25
|
+
};
|
package/lib/util.js
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { createHash, randomUUID } from 'node:crypto';
|
|
2
|
+
import { mkdir, readFile, writeFile, rename, realpath, stat, rm } from 'node:fs/promises';
|
|
3
|
+
import { dirname, resolve, relative, isAbsolute, sep } from 'node:path';
|
|
4
|
+
export const hash = (value) => createHash('sha256').update(typeof value === 'string' || Buffer.isBuffer(value) ? value : JSON.stringify(value)).digest('hex');
|
|
5
|
+
export const chars = (value) => Array.from(String(value)).length;
|
|
6
|
+
export const clip = (value, length) => Array.from(String(value)).slice(0, Math.max(0, length)).join('');
|
|
7
|
+
export const escapeHtml = (value) => String(value).replace(/[&<>"']/gu, ch => ({ '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' }[ch] ?? ch));
|
|
8
|
+
export const mdText = (value) => String(value).replace(/[\r\n]/gu, ' ').replace(/[\\[\]<>*`|]/gu, ch => `\\${ch}`);
|
|
9
|
+
export const now = () => new Date().toISOString();
|
|
10
|
+
export const message = (error) => error instanceof Error ? error.message : String(error);
|
|
11
|
+
export const code = (error) => typeof error === 'object' && error !== null && 'code' in error ? error.code : undefined;
|
|
12
|
+
export function id(value, label = 'ID') {
|
|
13
|
+
if (typeof value !== 'string' || !/^[a-z0-9][a-z0-9-]{0,95}$/u.test(value))
|
|
14
|
+
throw new Error(`${label} 无效`);
|
|
15
|
+
return value;
|
|
16
|
+
}
|
|
17
|
+
export function text(value, label, max = 2000) {
|
|
18
|
+
if (typeof value !== 'string' || !value.trim() || chars(value) > max)
|
|
19
|
+
throw new Error(`${label} 必须是 1–${max} 字符的文本`);
|
|
20
|
+
return value.trim();
|
|
21
|
+
}
|
|
22
|
+
export function required(value, label = '数据') {
|
|
23
|
+
if (value === null || value === undefined)
|
|
24
|
+
throw new Error(`${label} 缺失`);
|
|
25
|
+
return value;
|
|
26
|
+
}
|
|
27
|
+
export async function readJson(file, schema, fallback) {
|
|
28
|
+
try {
|
|
29
|
+
return schema.parse(JSON.parse(await readFile(file, 'utf8')));
|
|
30
|
+
}
|
|
31
|
+
catch (error) {
|
|
32
|
+
if (code(error) === 'ENOENT' && fallback !== undefined)
|
|
33
|
+
return fallback;
|
|
34
|
+
throw error;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
export async function atomicWrite(file, value) {
|
|
38
|
+
await mkdir(dirname(file), { recursive: true });
|
|
39
|
+
const temp = `${file}.${randomUUID()}.tmp`;
|
|
40
|
+
try {
|
|
41
|
+
await writeFile(temp, typeof value === 'string' || Buffer.isBuffer(value) ? value : JSON.stringify(value, null, 2) + '\n', { mode: 0o600 });
|
|
42
|
+
await rename(temp, file);
|
|
43
|
+
}
|
|
44
|
+
finally {
|
|
45
|
+
await rm(temp, { force: true });
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
export async function safePath(root, rel = '.', existing = true) {
|
|
49
|
+
if (typeof rel !== 'string' || rel.includes('\0') || isAbsolute(rel))
|
|
50
|
+
throw new Error('无效相对路径');
|
|
51
|
+
const base = await realpath(root), target = resolve(base, rel);
|
|
52
|
+
const inside = (value) => { const r = relative(base, value); return r !== '..' && !r.startsWith('..' + sep) && !isAbsolute(r); };
|
|
53
|
+
if (!inside(target))
|
|
54
|
+
throw new Error('路径越界');
|
|
55
|
+
let ancestor = target;
|
|
56
|
+
for (;;) {
|
|
57
|
+
try {
|
|
58
|
+
if (!inside(await realpath(ancestor)))
|
|
59
|
+
throw new Error('符号链接越界');
|
|
60
|
+
break;
|
|
61
|
+
}
|
|
62
|
+
catch (error) {
|
|
63
|
+
if (code(error) !== 'ENOENT' || existing)
|
|
64
|
+
throw error;
|
|
65
|
+
ancestor = dirname(ancestor);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return target;
|
|
69
|
+
}
|
|
70
|
+
export async function fileBytes(file, max) {
|
|
71
|
+
const info = await stat(file);
|
|
72
|
+
if (!info.isFile() || info.size > max)
|
|
73
|
+
throw new Error(`文件必须是普通文件且不超过 ${max} 字节`);
|
|
74
|
+
const value = await readFile(file);
|
|
75
|
+
if (value.length > max)
|
|
76
|
+
throw new Error('文件读取期间超过大小限制');
|
|
77
|
+
return value;
|
|
78
|
+
}
|
|
79
|
+
export function checkAbort(signal) { signal?.throwIfAborted(); }
|
|
80
|
+
export function tokensUsed(usage) {
|
|
81
|
+
if (!usage)
|
|
82
|
+
return 0;
|
|
83
|
+
return [usage.inputTokens, usage.outputTokens, usage.cacheReadTokens, usage.cacheWriteTokens].reduce((n, k) => n + (Number.isFinite(k) ? Math.max(0, k ?? 0) : 0), 0);
|
|
84
|
+
}
|
|
85
|
+
export function deferred() {
|
|
86
|
+
let result;
|
|
87
|
+
const promise = new Promise((resolve, reject) => { result = { resolve, reject }; });
|
|
88
|
+
return { promise, ...required(result) };
|
|
89
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@dsh-libing/knowledge-base",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "知识治理:Word/PDF 导入、图表复核、可恢复任务与固定版本证据检索",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"icon": "icon.svg",
|
|
7
|
+
"main": "lib/index.js",
|
|
8
|
+
"types": "lib/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./lib/index.d.ts",
|
|
12
|
+
"default": "./lib/index.js"
|
|
13
|
+
},
|
|
14
|
+
"./client": {
|
|
15
|
+
"types": "./lib/client-types/client/index.d.ts",
|
|
16
|
+
"default": "./lib/client.js"
|
|
17
|
+
},
|
|
18
|
+
"./package.json": "./package.json"
|
|
19
|
+
},
|
|
20
|
+
"files": [
|
|
21
|
+
"icon.svg",
|
|
22
|
+
"lib",
|
|
23
|
+
"cordis.patch.yml",
|
|
24
|
+
"README.md",
|
|
25
|
+
"CHANGELOG.md",
|
|
26
|
+
"skills"
|
|
27
|
+
],
|
|
28
|
+
"license": "UNLICENSED",
|
|
29
|
+
"engines": {
|
|
30
|
+
"node": "^22.19.0 || >=24.0.0"
|
|
31
|
+
},
|
|
32
|
+
"repository": {
|
|
33
|
+
"type": "git",
|
|
34
|
+
"url": "git+https://gitcode.com/LiBing-DSH/dsh-libing.git",
|
|
35
|
+
"directory": "plugins/knowledge-base"
|
|
36
|
+
},
|
|
37
|
+
"publishConfig": {
|
|
38
|
+
"access": "public",
|
|
39
|
+
"registry": "https://registry.npmjs.org/"
|
|
40
|
+
},
|
|
41
|
+
"dependencies": {
|
|
42
|
+
"@deepseek-ai/schemastery": "3.18.4",
|
|
43
|
+
"zod": "4.4.3",
|
|
44
|
+
"@napi-rs/canvas": "1.0.9",
|
|
45
|
+
"fast-xml-parser": "5.11.1",
|
|
46
|
+
"fflate": "0.8.3",
|
|
47
|
+
"image-size": "2.0.4",
|
|
48
|
+
"jimp": "1.6.1",
|
|
49
|
+
"mammoth": "1.13.0",
|
|
50
|
+
"parse5": "8.0.1",
|
|
51
|
+
"pdfjs-dist": "6.3.289"
|
|
52
|
+
},
|
|
53
|
+
"peerDependencies": {
|
|
54
|
+
"@deepseek-ai/cordis": "4.0.4",
|
|
55
|
+
"@deepseek-ai/dsh-client-connection": "0.2.0-rc.2",
|
|
56
|
+
"@deepseek-ai/dsh-client-locale": "0.2.0-rc.2",
|
|
57
|
+
"@deepseek-ai/dsh-client-ui-layout": "0.2.0-rc.2",
|
|
58
|
+
"@deepseek-ai/dsh-client-ui-sidebar": "0.2.0-rc.2",
|
|
59
|
+
"@deepseek-ai/dsh-client-ui-slots": "0.2.0-rc.2",
|
|
60
|
+
"@deepseek-ai/dsh-client-ui-renderer": "0.2.0-rc.2",
|
|
61
|
+
"@deepseek-ai/dsh-client-ui-primitives": "0.2.0-rc.2",
|
|
62
|
+
"@deepseek-ai/dsh-client-ui-session": "0.2.0-rc.2",
|
|
63
|
+
"@deepseek-ai/dsh-tools": "0.2.0-rc.2",
|
|
64
|
+
"@deepseek-ai/dsh-skill": "0.2.0-rc.2",
|
|
65
|
+
"@deepseek-ai/dsh-llm": "0.2.0-rc.2",
|
|
66
|
+
"@deepseek-ai/dsh-attachment": "0.2.0-rc.2",
|
|
67
|
+
"@deepseek-ai/dsh-agent": "0.2.0-rc.2",
|
|
68
|
+
"@deepseek-ai/dsh-agent-default-model": "0.2.0-rc.2",
|
|
69
|
+
"@deepseek-ai/dsh-session": "0.2.0-rc.2",
|
|
70
|
+
"@deepseek-ai/dsh-api-session-controller": "0.2.0-rc.2"
|
|
71
|
+
},
|
|
72
|
+
"devDependencies": {
|
|
73
|
+
"@deepseek-ai/cordis": "4.0.4",
|
|
74
|
+
"@deepseek-ai/dsh-client-connection": "0.2.0-rc.2",
|
|
75
|
+
"@deepseek-ai/dsh-client-locale": "0.2.0-rc.2",
|
|
76
|
+
"@deepseek-ai/dsh-client-ui-layout": "0.2.0-rc.2",
|
|
77
|
+
"@deepseek-ai/dsh-client-ui-sidebar": "0.2.0-rc.2",
|
|
78
|
+
"@deepseek-ai/dsh-client-ui-slots": "0.2.0-rc.2",
|
|
79
|
+
"@deepseek-ai/dsh-client-ui-renderer": "0.2.0-rc.2",
|
|
80
|
+
"@deepseek-ai/dsh-client-ui-primitives": "0.2.0-rc.2",
|
|
81
|
+
"@deepseek-ai/dsh-client-ui-session": "0.2.0-rc.2",
|
|
82
|
+
"@deepseek-ai/dsh-tools": "0.2.0-rc.2",
|
|
83
|
+
"@deepseek-ai/dsh-skill": "0.2.0-rc.2",
|
|
84
|
+
"@deepseek-ai/dsh-llm": "0.2.0-rc.2",
|
|
85
|
+
"@deepseek-ai/dsh-attachment": "0.2.0-rc.2",
|
|
86
|
+
"@deepseek-ai/dsh-agent": "0.2.0-rc.2",
|
|
87
|
+
"@deepseek-ai/dsh-agent-default-model": "0.2.0-rc.2",
|
|
88
|
+
"@deepseek-ai/dsh-session": "0.2.0-rc.2",
|
|
89
|
+
"@deepseek-ai/dsh-api-session-controller": "0.2.0-rc.2"
|
|
90
|
+
},
|
|
91
|
+
"dsh": {
|
|
92
|
+
"bundle": {
|
|
93
|
+
"patch": "./cordis.patch.yml"
|
|
94
|
+
},
|
|
95
|
+
"client": {
|
|
96
|
+
"platform": "web",
|
|
97
|
+
"inject": [
|
|
98
|
+
"@deepseek-ai/dsh-client-locale",
|
|
99
|
+
"@deepseek-ai/dsh-client-ui-layout",
|
|
100
|
+
"@deepseek-ai/dsh-client-ui-sidebar",
|
|
101
|
+
"@deepseek-ai/dsh-client-ui-session",
|
|
102
|
+
"@deepseek-ai/dsh-api-session-controller"
|
|
103
|
+
]
|
|
104
|
+
},
|
|
105
|
+
"market": {
|
|
106
|
+
"displayName": {
|
|
107
|
+
"zh": "知识治理",
|
|
108
|
+
"en": "Knowledge Base"
|
|
109
|
+
},
|
|
110
|
+
"category": "utilities",
|
|
111
|
+
"icon": "icon.svg",
|
|
112
|
+
"maintainers": [
|
|
113
|
+
"@lujinxing"
|
|
114
|
+
],
|
|
115
|
+
"compatibility": {
|
|
116
|
+
"dsh": "0.2.0-rc.2"
|
|
117
|
+
},
|
|
118
|
+
"allowedBuilds": []
|
|
119
|
+
}
|
|
120
|
+
},
|
|
121
|
+
"scripts": {
|
|
122
|
+
"build": "tsc -p tsconfig.build.json && tsc -p tsconfig.client.build.json && node --import tsx/esm ../../scripts/build-client.ts knowledge-base",
|
|
123
|
+
"typecheck:client": "tsc -p tsconfig.client.json --noEmit"
|
|
124
|
+
}
|
|
125
|
+
}
|