wiki-graph-sdk 0.7.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/LICENSE +201 -0
- package/README.md +32 -0
- package/dist/archives.cjs +2799 -0
- package/dist/archives.cjs.map +1 -0
- package/dist/archives.d.ts +249 -0
- package/dist/archives.js +2814 -0
- package/dist/archives.js.map +1 -0
- package/dist/conversions.cjs +1332 -0
- package/dist/conversions.cjs.map +1 -0
- package/dist/conversions.d.ts +37 -0
- package/dist/conversions.js +1308 -0
- package/dist/conversions.js.map +1 -0
- package/dist/data/editor/clue_reviewer.jinja +410 -0
- package/dist/data/editor/clue_reviewer_generator.jinja +428 -0
- package/dist/data/editor/revision_feedback.jinja +5 -0
- package/dist/data/editor/text_compressor.jinja +131 -0
- package/dist/data/guaranteed/response_intent_classifier.jinja +8 -0
- package/dist/data/output/epub/cover.xhtml.jinja +13 -0
- package/dist/data/output/epub/nav.xhtml.jinja +12 -0
- package/dist/data/output/epub/package.opf.xml.jinja +39 -0
- package/dist/data/output/epub/section.xhtml.jinja +12 -0
- package/dist/data/topologization/book_coherence_extraction.jinja +223 -0
- package/dist/data/topologization/chunk_extraction.jinja +233 -0
- package/dist/data/topologization/evidence_choice.jinja +38 -0
- package/dist/data/topologization/translate_chunks.jinja +138 -0
- package/dist/data/topologization/user_focused_extraction.jinja +378 -0
- package/dist/default-worker.cjs +1709 -0
- package/dist/default-worker.cjs.map +1 -0
- package/dist/default-worker.d.ts +10 -0
- package/dist/default-worker.js +1705 -0
- package/dist/default-worker.js.map +1 -0
- package/dist/embedding.cjs +1091 -0
- package/dist/embedding.cjs.map +1 -0
- package/dist/embedding.d.ts +26 -0
- package/dist/embedding.js +1062 -0
- package/dist/embedding.js.map +1 -0
- package/dist/entry-context.cjs +91 -0
- package/dist/entry-context.cjs.map +1 -0
- package/dist/entry-context.d.ts +20 -0
- package/dist/entry-context.js +68 -0
- package/dist/entry-context.js.map +1 -0
- package/dist/gc.cjs +731 -0
- package/dist/gc.cjs.map +1 -0
- package/dist/gc.d.ts +14 -0
- package/dist/gc.js +707 -0
- package/dist/gc.js.map +1 -0
- package/dist/index.cjs +4760 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.ts +213 -0
- package/dist/index.js +4808 -0
- package/dist/index.js.map +1 -0
- package/dist/jobs-Dd5PMSId.d.ts +98 -0
- package/dist/llm.cjs +101 -0
- package/dist/llm.cjs.map +1 -0
- package/dist/llm.d.ts +7 -0
- package/dist/llm.js +79 -0
- package/dist/llm.js.map +1 -0
- package/dist/local-config.cjs +1024 -0
- package/dist/local-config.cjs.map +1 -0
- package/dist/local-config.d.ts +14 -0
- package/dist/local-config.js +989 -0
- package/dist/local-config.js.map +1 -0
- package/dist/maintenance.cjs +800 -0
- package/dist/maintenance.cjs.map +1 -0
- package/dist/maintenance.d.ts +24 -0
- package/dist/maintenance.js +778 -0
- package/dist/maintenance.js.map +1 -0
- package/dist/node-platform.cjs +758 -0
- package/dist/node-platform.cjs.map +1 -0
- package/dist/node-platform.d.ts +49 -0
- package/dist/node-platform.js +723 -0
- package/dist/node-platform.js.map +1 -0
- package/dist/planning.cjs +164 -0
- package/dist/planning.cjs.map +1 -0
- package/dist/planning.d.ts +43 -0
- package/dist/planning.js +137 -0
- package/dist/planning.js.map +1 -0
- package/dist/runtime-config.cjs +1155 -0
- package/dist/runtime-config.cjs.map +1 -0
- package/dist/runtime-config.d.ts +50 -0
- package/dist/runtime-config.js +1127 -0
- package/dist/runtime-config.js.map +1 -0
- package/dist/stage.cjs +1265 -0
- package/dist/stage.cjs.map +1 -0
- package/dist/stage.d.ts +16 -0
- package/dist/stage.js +1239 -0
- package/dist/stage.js.map +1 -0
- package/dist/wikispine.cjs +85 -0
- package/dist/wikispine.cjs.map +1 -0
- package/dist/wikispine.d.ts +6 -0
- package/dist/wikispine.js +63 -0
- package/dist/wikispine.js.map +1 -0
- package/dist/worker.cjs +738 -0
- package/dist/worker.cjs.map +1 -0
- package/dist/worker.d.ts +157 -0
- package/dist/worker.js +715 -0
- package/dist/worker.js.map +1 -0
- package/package.json +151 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,4808 @@
|
|
|
1
|
+
import { fileURLToPath as __WIKIGRAPH_FILE_URL_TO_PATH__ } from "url";
|
|
2
|
+
import { resolve as __WIKIGRAPH_RESOLVE__ } from "path";
|
|
3
|
+
globalThis.__WIKIGRAPH_DATA_DIR__ ??= __WIKIGRAPH_RESOLVE__(__WIKIGRAPH_FILE_URL_TO_PATH__(new URL("./data", import.meta.url)));
|
|
4
|
+
|
|
5
|
+
// src/archive/archive.ts
|
|
6
|
+
import { randomUUID as randomUUID2 } from "crypto";
|
|
7
|
+
import { link, mkdtemp, rename as rename2, rm as rm3, stat as stat2 } from "fs/promises";
|
|
8
|
+
import { tmpdir } from "os";
|
|
9
|
+
import { basename as basename2, dirname as dirname2, join as join3 } from "path";
|
|
10
|
+
import { Readable as Readable2 } from "stream";
|
|
11
|
+
import {
|
|
12
|
+
addChapter,
|
|
13
|
+
applyChapterTree,
|
|
14
|
+
assertNoActiveBuildJobConflicts,
|
|
15
|
+
assertNoActiveBuildJobs,
|
|
16
|
+
deleteArchiveSearchSessions,
|
|
17
|
+
DirectoryDocument,
|
|
18
|
+
finalizeWikiGraphLibraryArchiveWrite,
|
|
19
|
+
findArchiveObjects,
|
|
20
|
+
formatLocatedWikiGraphUri,
|
|
21
|
+
isArchiveSearchIndexCurrent as isArchiveSearchIndexCurrent2,
|
|
22
|
+
isSourceLocatorScopeUri,
|
|
23
|
+
listArchiveCollection,
|
|
24
|
+
listArchiveEvidence,
|
|
25
|
+
listArchiveQueryableChapterIds,
|
|
26
|
+
listArchiveSourceLocators,
|
|
27
|
+
listChapters as listChapters2,
|
|
28
|
+
listRelatedArchiveObjects,
|
|
29
|
+
moveChapter,
|
|
30
|
+
packArchiveContext,
|
|
31
|
+
parseLocatedWikiGraphUri,
|
|
32
|
+
parseWikiGraphLibraryUri,
|
|
33
|
+
readArchivePage,
|
|
34
|
+
readSearchIndexCapabilityStatus,
|
|
35
|
+
readWikiGraphLibraryIndexState,
|
|
36
|
+
rebuildArchiveSearchIndex,
|
|
37
|
+
rebuildWikiGraphLibraryIndex,
|
|
38
|
+
removeChapter,
|
|
39
|
+
resetChapter,
|
|
40
|
+
resolveChapterPathReadonly,
|
|
41
|
+
resolveWikiGraphLibraryArchiveFile,
|
|
42
|
+
setChapterSource,
|
|
43
|
+
setChapterSummary,
|
|
44
|
+
setChapterTitle,
|
|
45
|
+
getChapterTree,
|
|
46
|
+
WikiGraph as WikiGraph2,
|
|
47
|
+
WikiGraphArchiveFile,
|
|
48
|
+
TOC_FILE_VERSION,
|
|
49
|
+
writeWikgArchive,
|
|
50
|
+
ObjectMetadataKind
|
|
51
|
+
} from "wiki-graph-core";
|
|
52
|
+
|
|
53
|
+
// src/conversions.ts
|
|
54
|
+
import { rm as rm2 } from "fs/promises";
|
|
55
|
+
import {
|
|
56
|
+
WikiGraph
|
|
57
|
+
} from "wiki-graph-core";
|
|
58
|
+
|
|
59
|
+
// src/llm.ts
|
|
60
|
+
import { createAnthropic } from "@ai-sdk/anthropic";
|
|
61
|
+
import { createGoogleGenerativeAI } from "@ai-sdk/google";
|
|
62
|
+
import { createOpenAI } from "@ai-sdk/openai";
|
|
63
|
+
import { createOpenAICompatible } from "@ai-sdk/openai-compatible";
|
|
64
|
+
function buildWikiGraphLLMOptions(config) {
|
|
65
|
+
const llm = config.llm;
|
|
66
|
+
if (llm?.provider === void 0 || llm.model === void 0) {
|
|
67
|
+
throw new Error(
|
|
68
|
+
"Missing LLM configuration. Set --llm for one run, or configure `wikg://local/config/llm` with provider and model."
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
return {
|
|
72
|
+
model: createLanguageModel(llm.provider, llm.model, {
|
|
73
|
+
apiKey: llm.apiKey,
|
|
74
|
+
baseURL: llm.baseURL,
|
|
75
|
+
name: llm.name
|
|
76
|
+
}),
|
|
77
|
+
...config.concurrent?.request === void 0 ? {} : { concurrent: config.concurrent.request }
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
function createLanguageModel(provider, model, options) {
|
|
81
|
+
switch (provider) {
|
|
82
|
+
case "anthropic": {
|
|
83
|
+
const anthropic = createAnthropic({
|
|
84
|
+
...options.apiKey === void 0 ? {} : { apiKey: options.apiKey },
|
|
85
|
+
...options.name === void 0 ? {} : { name: options.name }
|
|
86
|
+
});
|
|
87
|
+
return anthropic(model);
|
|
88
|
+
}
|
|
89
|
+
case "google": {
|
|
90
|
+
const google = createGoogleGenerativeAI({
|
|
91
|
+
...options.apiKey === void 0 ? {} : { apiKey: options.apiKey },
|
|
92
|
+
...options.name === void 0 ? {} : { name: options.name }
|
|
93
|
+
});
|
|
94
|
+
return google(model);
|
|
95
|
+
}
|
|
96
|
+
case "openai": {
|
|
97
|
+
if (options.baseURL !== void 0) {
|
|
98
|
+
throw new Error(
|
|
99
|
+
"openai does not accept llm.baseURL or baseURL in --llm JSON. Use openai-compatible for third-party OpenAI-style APIs."
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
const openai = createOpenAI({
|
|
103
|
+
...options.apiKey === void 0 ? {} : { apiKey: options.apiKey },
|
|
104
|
+
...options.name === void 0 ? {} : { name: options.name }
|
|
105
|
+
});
|
|
106
|
+
return openai(model);
|
|
107
|
+
}
|
|
108
|
+
case "openai-compatible": {
|
|
109
|
+
if (options.baseURL === void 0) {
|
|
110
|
+
throw new Error(
|
|
111
|
+
"openai-compatible requires llm.baseURL or baseURL in --llm JSON."
|
|
112
|
+
);
|
|
113
|
+
}
|
|
114
|
+
const openaiCompatible = createOpenAICompatible({
|
|
115
|
+
baseURL: options.baseURL,
|
|
116
|
+
name: options.name ?? createOpenAICompatibleName(options.baseURL),
|
|
117
|
+
...options.apiKey === void 0 ? {} : { apiKey: options.apiKey }
|
|
118
|
+
});
|
|
119
|
+
return openaiCompatible(model);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
function createOpenAICompatibleName(baseURL) {
|
|
124
|
+
try {
|
|
125
|
+
return new URL(baseURL).hostname;
|
|
126
|
+
} catch {
|
|
127
|
+
return "openai-compatible";
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// src/node-platform.ts
|
|
132
|
+
import * as asyncHooks from "async_hooks";
|
|
133
|
+
import * as crypto from "crypto";
|
|
134
|
+
import * as fs from "fs";
|
|
135
|
+
import * as fsPromises from "fs/promises";
|
|
136
|
+
import * as os from "os";
|
|
137
|
+
import * as pathModule from "path";
|
|
138
|
+
import * as process2 from "process";
|
|
139
|
+
import { Readable, Writable } from "stream";
|
|
140
|
+
import { pipeline } from "stream/promises";
|
|
141
|
+
import * as sqlite3 from "sqlite3";
|
|
142
|
+
import * as yauzl from "yauzl";
|
|
143
|
+
import * as yazl from "yazl";
|
|
144
|
+
import { Environment, Loader } from "nunjucks";
|
|
145
|
+
import {
|
|
146
|
+
getWikiGraphStorage,
|
|
147
|
+
installWikiGraphPlatform,
|
|
148
|
+
installWikiGraphStorage,
|
|
149
|
+
withWikiGraphStorage,
|
|
150
|
+
normalizeTemplateName
|
|
151
|
+
} from "wiki-graph-core/platform";
|
|
152
|
+
var nodeProcess = process2.default ?? process2;
|
|
153
|
+
var nodeSqlite3 = sqlite3.default ?? sqlite3;
|
|
154
|
+
var NODE_RESOURCE_KIND = /* @__PURE__ */ Symbol.for("wiki-graph-sdk.node-resource.v1");
|
|
155
|
+
var NodeFile = class {
|
|
156
|
+
constructor(path, name = pathModule.basename(path)) {
|
|
157
|
+
this.path = path;
|
|
158
|
+
this.identity = encodeNodeResourceIdentity("file", path);
|
|
159
|
+
this.name = name;
|
|
160
|
+
}
|
|
161
|
+
path;
|
|
162
|
+
kind = "file";
|
|
163
|
+
[NODE_RESOURCE_KIND] = "file";
|
|
164
|
+
identity;
|
|
165
|
+
name;
|
|
166
|
+
async openReader() {
|
|
167
|
+
const handle = await fsPromises.open(this.path, "r");
|
|
168
|
+
let closed = false;
|
|
169
|
+
try {
|
|
170
|
+
const stats = await handle.stat();
|
|
171
|
+
if (!stats.isFile())
|
|
172
|
+
throw new Error("File reader requires a regular file");
|
|
173
|
+
const { size } = stats;
|
|
174
|
+
return {
|
|
175
|
+
size,
|
|
176
|
+
close: async () => {
|
|
177
|
+
if (!closed) await handle.close();
|
|
178
|
+
closed = true;
|
|
179
|
+
},
|
|
180
|
+
read: async (offset, length) => {
|
|
181
|
+
if (closed) throw new Error("Cannot read from a closed FileReader");
|
|
182
|
+
assertFileRange(offset, length, size);
|
|
183
|
+
const buffer = Buffer.allocUnsafe(length);
|
|
184
|
+
let bytesRead = 0;
|
|
185
|
+
while (bytesRead < length) {
|
|
186
|
+
const result = await handle.read(
|
|
187
|
+
buffer,
|
|
188
|
+
bytesRead,
|
|
189
|
+
length - bytesRead,
|
|
190
|
+
offset + bytesRead
|
|
191
|
+
);
|
|
192
|
+
if (result.bytesRead === 0) {
|
|
193
|
+
throw new Error("File changed while reading its byte range");
|
|
194
|
+
}
|
|
195
|
+
bytesRead += result.bytesRead;
|
|
196
|
+
}
|
|
197
|
+
return new Uint8Array(buffer.buffer, buffer.byteOffset, bytesRead);
|
|
198
|
+
}
|
|
199
|
+
};
|
|
200
|
+
} catch (error) {
|
|
201
|
+
await handle.close();
|
|
202
|
+
throw error;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
async getLastModified() {
|
|
206
|
+
try {
|
|
207
|
+
return (await fsPromises.stat(this.path)).mtimeMs;
|
|
208
|
+
} catch (error) {
|
|
209
|
+
if (typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT") {
|
|
210
|
+
return void 0;
|
|
211
|
+
}
|
|
212
|
+
throw error;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
async openWriter() {
|
|
216
|
+
await fsPromises.mkdir(pathModule.dirname(this.path), { recursive: true });
|
|
217
|
+
const temporary = `${this.path}.tmp-${crypto.randomUUID()}`;
|
|
218
|
+
const handle = await fsPromises.open(temporary, "wx");
|
|
219
|
+
let closed = false;
|
|
220
|
+
return {
|
|
221
|
+
write: async (data) => {
|
|
222
|
+
if (closed) throw new Error("Cannot write to a closed FileWriter");
|
|
223
|
+
await writeNodeFileHandle(handle, data);
|
|
224
|
+
},
|
|
225
|
+
writeAt: async (offset, data) => {
|
|
226
|
+
if (closed) throw new Error("Cannot write to a closed FileWriter");
|
|
227
|
+
assertFileOffset(offset);
|
|
228
|
+
await writeNodeFileHandle(handle, data, offset);
|
|
229
|
+
},
|
|
230
|
+
commit: async () => {
|
|
231
|
+
if (!closed) {
|
|
232
|
+
await handle.close();
|
|
233
|
+
await fsPromises.rename(temporary, this.path);
|
|
234
|
+
}
|
|
235
|
+
closed = true;
|
|
236
|
+
},
|
|
237
|
+
abort: async () => {
|
|
238
|
+
if (!closed) {
|
|
239
|
+
await handle.close();
|
|
240
|
+
await fsPromises.rm(temporary, { force: true });
|
|
241
|
+
}
|
|
242
|
+
closed = true;
|
|
243
|
+
}
|
|
244
|
+
};
|
|
245
|
+
}
|
|
246
|
+
};
|
|
247
|
+
async function writeNodeFileHandle(handle, data, position) {
|
|
248
|
+
const buffer = Buffer.from(data);
|
|
249
|
+
let written = 0;
|
|
250
|
+
while (written < buffer.byteLength) {
|
|
251
|
+
const result = await handle.write(
|
|
252
|
+
buffer,
|
|
253
|
+
written,
|
|
254
|
+
buffer.byteLength - written,
|
|
255
|
+
position === void 0 ? null : position + written
|
|
256
|
+
);
|
|
257
|
+
if (result.bytesWritten === 0) {
|
|
258
|
+
throw new Error("File writer made no progress");
|
|
259
|
+
}
|
|
260
|
+
written += result.bytesWritten;
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
function assertFileRange(offset, length, size) {
|
|
264
|
+
assertFileOffset(offset);
|
|
265
|
+
if (!Number.isSafeInteger(length) || length < 0) {
|
|
266
|
+
throw new RangeError(
|
|
267
|
+
"File read length must be a non-negative safe integer"
|
|
268
|
+
);
|
|
269
|
+
}
|
|
270
|
+
if (offset > size || length > size - offset) {
|
|
271
|
+
throw new RangeError("File read range exceeds the file size");
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
function assertFileOffset(offset) {
|
|
275
|
+
if (!Number.isSafeInteger(offset) || offset < 0) {
|
|
276
|
+
throw new RangeError("File offset must be a non-negative safe integer");
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
var NodeDirectory = class _NodeDirectory {
|
|
280
|
+
constructor(path) {
|
|
281
|
+
this.path = path;
|
|
282
|
+
this.identity = encodeNodeResourceIdentity("directory", path);
|
|
283
|
+
this.name = pathModule.basename(path);
|
|
284
|
+
}
|
|
285
|
+
path;
|
|
286
|
+
kind = "directory";
|
|
287
|
+
[NODE_RESOURCE_KIND] = "directory";
|
|
288
|
+
identity;
|
|
289
|
+
name;
|
|
290
|
+
async getFile(name) {
|
|
291
|
+
assertChildName(name);
|
|
292
|
+
const file = new NodeFile(pathModule.join(this.path, name));
|
|
293
|
+
try {
|
|
294
|
+
return (await fsPromises.stat(file.path)).isFile() ? file : void 0;
|
|
295
|
+
} catch {
|
|
296
|
+
return void 0;
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
async getLastModified() {
|
|
300
|
+
try {
|
|
301
|
+
return (await fsPromises.stat(this.path)).mtimeMs;
|
|
302
|
+
} catch (error) {
|
|
303
|
+
if (typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT") {
|
|
304
|
+
return void 0;
|
|
305
|
+
}
|
|
306
|
+
throw error;
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
async getDirectory(name) {
|
|
310
|
+
assertChildName(name);
|
|
311
|
+
const directory = new _NodeDirectory(pathModule.join(this.path, name));
|
|
312
|
+
try {
|
|
313
|
+
return (await fsPromises.stat(directory.path)).isDirectory() ? directory : void 0;
|
|
314
|
+
} catch {
|
|
315
|
+
return void 0;
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
async list() {
|
|
319
|
+
const entries = await fsPromises.readdir(this.path, {
|
|
320
|
+
withFileTypes: true
|
|
321
|
+
});
|
|
322
|
+
return entries.map(
|
|
323
|
+
(entry) => entry.isDirectory() ? new _NodeDirectory(pathModule.join(this.path, entry.name)) : new NodeFile(pathModule.join(this.path, entry.name), entry.name)
|
|
324
|
+
);
|
|
325
|
+
}
|
|
326
|
+
async createFile(name) {
|
|
327
|
+
assertChildName(name);
|
|
328
|
+
await fsPromises.mkdir(this.path, { recursive: true });
|
|
329
|
+
const file = new NodeFile(pathModule.join(this.path, name));
|
|
330
|
+
await fsPromises.writeFile(file.path, "", { flag: "wx" });
|
|
331
|
+
return file;
|
|
332
|
+
}
|
|
333
|
+
async createDirectory(name) {
|
|
334
|
+
assertChildName(name);
|
|
335
|
+
await fsPromises.mkdir(this.path, { recursive: true });
|
|
336
|
+
const directory = new _NodeDirectory(pathModule.join(this.path, name));
|
|
337
|
+
await fsPromises.mkdir(directory.path);
|
|
338
|
+
return directory;
|
|
339
|
+
}
|
|
340
|
+
async remove(name, options) {
|
|
341
|
+
assertChildName(name);
|
|
342
|
+
await fsPromises.rm(pathModule.join(this.path, name), {
|
|
343
|
+
force: true,
|
|
344
|
+
recursive: options?.recursive === true
|
|
345
|
+
});
|
|
346
|
+
}
|
|
347
|
+
};
|
|
348
|
+
function encodeNodeResourceIdentity(kind, path) {
|
|
349
|
+
return `node-${kind}:${Buffer.from(pathModule.resolve(path), "utf8").toString("base64url")}`;
|
|
350
|
+
}
|
|
351
|
+
function decodeNodeResourceIdentity(identity, kind) {
|
|
352
|
+
const prefix = `node-${kind}:`;
|
|
353
|
+
if (!identity.startsWith(prefix)) {
|
|
354
|
+
return pathModule.isAbsolute(identity) ? identity : void 0;
|
|
355
|
+
}
|
|
356
|
+
try {
|
|
357
|
+
return Buffer.from(identity.slice(prefix.length), "base64url").toString(
|
|
358
|
+
"utf8"
|
|
359
|
+
);
|
|
360
|
+
} catch {
|
|
361
|
+
return void 0;
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
function getNodeResourcePath(resource) {
|
|
365
|
+
const brandedPath = getBrandedNodeResourcePath(resource, resource.kind);
|
|
366
|
+
if (brandedPath !== void 0) return brandedPath;
|
|
367
|
+
if (typeof resource === "object" && resource !== null && "path" in resource && typeof resource.path === "string") {
|
|
368
|
+
return resource.path;
|
|
369
|
+
}
|
|
370
|
+
throw new TypeError("Expected a Node File or Directory resource");
|
|
371
|
+
}
|
|
372
|
+
function assertChildName(name) {
|
|
373
|
+
if (name.length === 0 || name === "." || name === ".." || pathModule.isAbsolute(name) || name.includes("/") || name.includes("\\")) {
|
|
374
|
+
throw new TypeError(`Directory child must be a relative name: ${name}`);
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
var NodeDatabaseConnection = class {
|
|
378
|
+
constructor(database) {
|
|
379
|
+
this.database = database;
|
|
380
|
+
}
|
|
381
|
+
database;
|
|
382
|
+
async close() {
|
|
383
|
+
await new Promise((resolve3, reject) => {
|
|
384
|
+
this.database.close((error) => {
|
|
385
|
+
if (error) reject(error);
|
|
386
|
+
else resolve3();
|
|
387
|
+
});
|
|
388
|
+
});
|
|
389
|
+
}
|
|
390
|
+
async execute(sql) {
|
|
391
|
+
await new Promise((resolve3, reject) => {
|
|
392
|
+
this.database.exec(sql, (error) => {
|
|
393
|
+
if (error) reject(error);
|
|
394
|
+
else resolve3();
|
|
395
|
+
});
|
|
396
|
+
});
|
|
397
|
+
}
|
|
398
|
+
async queryAll(sql, params = []) {
|
|
399
|
+
return await new Promise((resolve3, reject) => {
|
|
400
|
+
this.database.all(sql, [...params], (error, rows) => {
|
|
401
|
+
if (error) reject(error);
|
|
402
|
+
else resolve3(rows);
|
|
403
|
+
});
|
|
404
|
+
});
|
|
405
|
+
}
|
|
406
|
+
async queryOne(sql, params = []) {
|
|
407
|
+
return await new Promise((resolve3, reject) => {
|
|
408
|
+
this.database.get(sql, [...params], (error, row) => {
|
|
409
|
+
if (error) reject(error);
|
|
410
|
+
else resolve3(row);
|
|
411
|
+
});
|
|
412
|
+
});
|
|
413
|
+
}
|
|
414
|
+
async run(sql, params = []) {
|
|
415
|
+
await new Promise((resolve3, reject) => {
|
|
416
|
+
this.database.run(sql, [...params], (error) => {
|
|
417
|
+
if (error) reject(error);
|
|
418
|
+
else resolve3();
|
|
419
|
+
});
|
|
420
|
+
});
|
|
421
|
+
}
|
|
422
|
+
};
|
|
423
|
+
async function openNodeDatabase(file, options) {
|
|
424
|
+
const flags = (options.mode === "readonly" ? nodeSqlite3.OPEN_READONLY : nodeSqlite3.OPEN_READWRITE | (options.create ? nodeSqlite3.OPEN_CREATE : 0)) | nodeSqlite3.OPEN_FULLMUTEX;
|
|
425
|
+
return new NodeDatabaseConnection(await openNativeNodeDatabase(file, flags));
|
|
426
|
+
}
|
|
427
|
+
async function openNativeNodeDatabase(file, flags) {
|
|
428
|
+
const path = getBrandedNodeResourcePath(file, "file");
|
|
429
|
+
if (path === void 0) {
|
|
430
|
+
throw new TypeError("The Node database adapter requires a NodeFile");
|
|
431
|
+
}
|
|
432
|
+
return await new Promise((resolve3, reject) => {
|
|
433
|
+
const database = new nodeSqlite3.Database(path, flags, (error) => {
|
|
434
|
+
if (error) reject(error);
|
|
435
|
+
else resolve3(database);
|
|
436
|
+
});
|
|
437
|
+
});
|
|
438
|
+
}
|
|
439
|
+
async function openNodeZip(file) {
|
|
440
|
+
const path = getBrandedNodeResourcePath(file, "file");
|
|
441
|
+
if (path === void 0) {
|
|
442
|
+
throw new TypeError("The Node ZIP adapter requires a NodeFile");
|
|
443
|
+
}
|
|
444
|
+
const zipFile = await new Promise((resolve3, reject) => {
|
|
445
|
+
yauzl.open(
|
|
446
|
+
path,
|
|
447
|
+
{ autoClose: false, lazyEntries: true },
|
|
448
|
+
(error, opened) => {
|
|
449
|
+
if (error || opened === void 0)
|
|
450
|
+
reject(error ?? new Error("Cannot open ZIP"));
|
|
451
|
+
else resolve3(opened);
|
|
452
|
+
}
|
|
453
|
+
);
|
|
454
|
+
});
|
|
455
|
+
const entries = await new Promise(
|
|
456
|
+
(resolve3, reject) => {
|
|
457
|
+
const discovered = /* @__PURE__ */ new Map();
|
|
458
|
+
const rejectAndClose = (error) => {
|
|
459
|
+
zipFile.close();
|
|
460
|
+
reject(
|
|
461
|
+
error instanceof Error ? error : new Error("Cannot scan ZIP central directory")
|
|
462
|
+
);
|
|
463
|
+
};
|
|
464
|
+
const resolveEntries = () => {
|
|
465
|
+
zipFile.off("error", rejectAndClose);
|
|
466
|
+
resolve3(discovered);
|
|
467
|
+
};
|
|
468
|
+
zipFile.on("entry", (entry) => {
|
|
469
|
+
if (!entry.fileName.endsWith("/")) {
|
|
470
|
+
discovered.set(entry.fileName, entry);
|
|
471
|
+
}
|
|
472
|
+
zipFile.readEntry();
|
|
473
|
+
});
|
|
474
|
+
zipFile.once("error", rejectAndClose);
|
|
475
|
+
zipFile.once("end", resolveEntries);
|
|
476
|
+
zipFile.readEntry();
|
|
477
|
+
}
|
|
478
|
+
);
|
|
479
|
+
let closed = false;
|
|
480
|
+
return {
|
|
481
|
+
close: () => {
|
|
482
|
+
if (!closed) zipFile.close();
|
|
483
|
+
closed = true;
|
|
484
|
+
return Promise.resolve();
|
|
485
|
+
},
|
|
486
|
+
copyEntry: async (name, target) => {
|
|
487
|
+
if (closed) throw new Error("Cannot read a closed ZIP archive");
|
|
488
|
+
const entry = entries.get(name);
|
|
489
|
+
if (entry === void 0) return false;
|
|
490
|
+
await copyNodeZipEntry(zipFile, entry, target);
|
|
491
|
+
return true;
|
|
492
|
+
},
|
|
493
|
+
getEntrySize: (name) => {
|
|
494
|
+
if (closed) throw new Error("Cannot read a closed ZIP archive");
|
|
495
|
+
return Promise.resolve(entries.get(name)?.uncompressedSize);
|
|
496
|
+
},
|
|
497
|
+
listEntries: () => Promise.resolve([...entries.keys()]),
|
|
498
|
+
readEntryRange: async (name, offset, length) => {
|
|
499
|
+
if (closed) throw new Error("Cannot read a closed ZIP archive");
|
|
500
|
+
const entry = entries.get(name);
|
|
501
|
+
if (entry === void 0) return void 0;
|
|
502
|
+
return await readNodeZipEntryRange(zipFile, entry, offset, length);
|
|
503
|
+
},
|
|
504
|
+
readEntry: async (name) => {
|
|
505
|
+
if (closed) throw new Error("Cannot read a closed ZIP archive");
|
|
506
|
+
const entry = entries.get(name);
|
|
507
|
+
if (entry === void 0) return void 0;
|
|
508
|
+
return await readNodeZipEntry(zipFile, entry);
|
|
509
|
+
}
|
|
510
|
+
};
|
|
511
|
+
}
|
|
512
|
+
async function copyNodeZipEntry(zipFile, entry, target) {
|
|
513
|
+
const writer = await target.openWriter();
|
|
514
|
+
try {
|
|
515
|
+
const stream = await openNodeZipEntryStream(zipFile, entry);
|
|
516
|
+
await writeNodeStream(stream, writer);
|
|
517
|
+
await writer.commit();
|
|
518
|
+
} catch (error) {
|
|
519
|
+
await writer.abort().catch(() => void 0);
|
|
520
|
+
throw error;
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
async function readNodeZipEntry(zipFile, entry) {
|
|
524
|
+
return await collectNodeStream(await openNodeZipEntryStream(zipFile, entry));
|
|
525
|
+
}
|
|
526
|
+
async function readNodeZipEntryRange(zipFile, entry, offset, length) {
|
|
527
|
+
assertFileRange(offset, length, entry.uncompressedSize);
|
|
528
|
+
if (length === 0) return new Uint8Array();
|
|
529
|
+
if (entry.compressionMethod !== 0) {
|
|
530
|
+
const content2 = await collectDecodedNodeStreamRange(
|
|
531
|
+
await openNodeZipEntryStream(zipFile, entry),
|
|
532
|
+
offset,
|
|
533
|
+
length
|
|
534
|
+
);
|
|
535
|
+
return new Uint8Array(
|
|
536
|
+
content2.buffer,
|
|
537
|
+
content2.byteOffset,
|
|
538
|
+
content2.byteLength
|
|
539
|
+
);
|
|
540
|
+
}
|
|
541
|
+
const content = await collectNodeStream(
|
|
542
|
+
await openNodeZipEntryStream(zipFile, entry, {
|
|
543
|
+
decompress: null,
|
|
544
|
+
decrypt: null,
|
|
545
|
+
end: offset + length,
|
|
546
|
+
start: offset
|
|
547
|
+
})
|
|
548
|
+
);
|
|
549
|
+
return new Uint8Array(content.buffer, content.byteOffset, content.byteLength);
|
|
550
|
+
}
|
|
551
|
+
async function collectDecodedNodeStreamRange(input, offset, length) {
|
|
552
|
+
const chunks = [];
|
|
553
|
+
const end = offset + length;
|
|
554
|
+
let position = 0;
|
|
555
|
+
let collected = 0;
|
|
556
|
+
for await (const value of input) {
|
|
557
|
+
const chunk = Buffer.isBuffer(value) ? value : Buffer.from(value);
|
|
558
|
+
const chunkEnd = position + chunk.byteLength;
|
|
559
|
+
if (chunkEnd > offset && position < end) {
|
|
560
|
+
const selected = chunk.subarray(
|
|
561
|
+
Math.max(0, offset - position),
|
|
562
|
+
Math.min(chunk.byteLength, end - position)
|
|
563
|
+
);
|
|
564
|
+
chunks.push(selected);
|
|
565
|
+
collected += selected.byteLength;
|
|
566
|
+
}
|
|
567
|
+
position = chunkEnd;
|
|
568
|
+
if (position >= end) break;
|
|
569
|
+
}
|
|
570
|
+
if (collected !== length) {
|
|
571
|
+
throw new Error("ZIP entry changed while reading its byte range");
|
|
572
|
+
}
|
|
573
|
+
return Buffer.concat(chunks, collected);
|
|
574
|
+
}
|
|
575
|
+
async function openNodeZipEntryStream(zipFile, entry, options) {
|
|
576
|
+
return await new Promise((resolve3, reject) => {
|
|
577
|
+
const callback = (error, opened) => {
|
|
578
|
+
if (error || opened === void 0) {
|
|
579
|
+
reject(error ?? new Error(`Cannot read ZIP entry: ${entry.fileName}`));
|
|
580
|
+
} else {
|
|
581
|
+
resolve3(opened);
|
|
582
|
+
}
|
|
583
|
+
};
|
|
584
|
+
if (options === void 0) zipFile.openReadStream(entry, callback);
|
|
585
|
+
else zipFile.openReadStream(entry, options, callback);
|
|
586
|
+
});
|
|
587
|
+
}
|
|
588
|
+
async function writeNodeZip(file, entries) {
|
|
589
|
+
const zipFile = new yazl.ZipFile();
|
|
590
|
+
const writer = await file.openWriter();
|
|
591
|
+
const outputStream = zipFile.outputStream;
|
|
592
|
+
const outputDone = writeNodeStream(outputStream, writer);
|
|
593
|
+
const entryState = { cancelled: false, complete: false };
|
|
594
|
+
let iterator;
|
|
595
|
+
try {
|
|
596
|
+
iterator = getNodeZipEntryIterator(entries);
|
|
597
|
+
const entriesDone = addNodeZipEntries(zipFile, iterator, entryState);
|
|
598
|
+
await Promise.race([entriesDone, outputDone]);
|
|
599
|
+
if (!entryState.complete) {
|
|
600
|
+
throw new Error("ZIP output ended before entry production completed");
|
|
601
|
+
}
|
|
602
|
+
zipFile.end();
|
|
603
|
+
await outputDone;
|
|
604
|
+
await writer.commit();
|
|
605
|
+
} catch (error) {
|
|
606
|
+
entryState.cancelled = true;
|
|
607
|
+
if (!entryState.complete && iterator !== void 0) {
|
|
608
|
+
closeNodeZipEntryIterator(iterator);
|
|
609
|
+
}
|
|
610
|
+
if (!outputStream.destroyed) {
|
|
611
|
+
outputStream.destroy(
|
|
612
|
+
error instanceof Error ? error : new Error("Cannot write ZIP archive")
|
|
613
|
+
);
|
|
614
|
+
}
|
|
615
|
+
await outputDone.catch(() => void 0);
|
|
616
|
+
await writer.abort().catch(() => void 0);
|
|
617
|
+
throw error;
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
async function addNodeZipEntries(zipFile, iterator, state) {
|
|
621
|
+
while (!state.cancelled) {
|
|
622
|
+
const result = await iterator.next();
|
|
623
|
+
if (state.cancelled) return;
|
|
624
|
+
if (result.done) {
|
|
625
|
+
state.complete = true;
|
|
626
|
+
return;
|
|
627
|
+
}
|
|
628
|
+
if ("file" in result.value) {
|
|
629
|
+
zipFile.addReadStream(
|
|
630
|
+
createNodeFileReadStream(result.value.file),
|
|
631
|
+
result.value.name,
|
|
632
|
+
{ compress: false }
|
|
633
|
+
);
|
|
634
|
+
} else if ("data" in result.value) {
|
|
635
|
+
zipFile.addBuffer(Buffer.from(result.value.data), result.value.name, {
|
|
636
|
+
compress: false
|
|
637
|
+
});
|
|
638
|
+
} else {
|
|
639
|
+
zipFile.addReadStream(
|
|
640
|
+
createNodeRangeReadStream(result.value),
|
|
641
|
+
result.value.name,
|
|
642
|
+
{ compress: false, size: result.value.size }
|
|
643
|
+
);
|
|
644
|
+
}
|
|
645
|
+
}
|
|
646
|
+
}
|
|
647
|
+
function createNodeFileReadStream(file) {
|
|
648
|
+
return Readable.from(
|
|
649
|
+
(async function* () {
|
|
650
|
+
const reader = await file.openReader();
|
|
651
|
+
try {
|
|
652
|
+
for (let offset = 0; offset < reader.size; ) {
|
|
653
|
+
const chunk = await reader.read(
|
|
654
|
+
offset,
|
|
655
|
+
Math.min(64 * 1024, reader.size - offset)
|
|
656
|
+
);
|
|
657
|
+
offset += chunk.byteLength;
|
|
658
|
+
yield chunk;
|
|
659
|
+
}
|
|
660
|
+
} finally {
|
|
661
|
+
await reader.close();
|
|
662
|
+
}
|
|
663
|
+
})()
|
|
664
|
+
);
|
|
665
|
+
}
|
|
666
|
+
function getBrandedNodeResourcePath(resource, kind) {
|
|
667
|
+
if (typeof resource !== "object" || resource === null) return void 0;
|
|
668
|
+
const candidate = resource;
|
|
669
|
+
return candidate[NODE_RESOURCE_KIND] === kind && typeof candidate.path === "string" ? candidate.path : void 0;
|
|
670
|
+
}
|
|
671
|
+
function createNodeRangeReadStream(entry) {
|
|
672
|
+
return Readable.from(
|
|
673
|
+
(async function* () {
|
|
674
|
+
for (let offset = 0; offset < entry.size; ) {
|
|
675
|
+
const chunk = await entry.read(
|
|
676
|
+
offset,
|
|
677
|
+
Math.min(64 * 1024, entry.size - offset)
|
|
678
|
+
);
|
|
679
|
+
offset += chunk.byteLength;
|
|
680
|
+
yield chunk;
|
|
681
|
+
}
|
|
682
|
+
})()
|
|
683
|
+
);
|
|
684
|
+
}
|
|
685
|
+
function getNodeZipEntryIterator(entries) {
|
|
686
|
+
const asyncIterator = entries[Symbol.asyncIterator];
|
|
687
|
+
return asyncIterator === void 0 ? entries[Symbol.iterator]() : asyncIterator.call(entries);
|
|
688
|
+
}
|
|
689
|
+
function closeNodeZipEntryIterator(iterator) {
|
|
690
|
+
if (iterator.return === void 0) return;
|
|
691
|
+
try {
|
|
692
|
+
void Promise.resolve(iterator.return()).catch(() => void 0);
|
|
693
|
+
} catch {
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
async function writeNodeStream(input, writer) {
|
|
697
|
+
await pipeline(
|
|
698
|
+
input,
|
|
699
|
+
new Writable({
|
|
700
|
+
write: (chunk, _encoding, callback) => {
|
|
701
|
+
void writer.write(chunk).then(
|
|
702
|
+
() => callback(),
|
|
703
|
+
(error) => callback(
|
|
704
|
+
error instanceof Error ? error : new Error("Cannot stream ZIP archive")
|
|
705
|
+
)
|
|
706
|
+
);
|
|
707
|
+
}
|
|
708
|
+
})
|
|
709
|
+
);
|
|
710
|
+
}
|
|
711
|
+
async function collectNodeStream(input) {
|
|
712
|
+
return await new Promise((resolve3, reject) => {
|
|
713
|
+
const chunks = [];
|
|
714
|
+
input.on("data", (chunk) => chunks.push(chunk));
|
|
715
|
+
input.once("error", reject);
|
|
716
|
+
input.once("end", () => resolve3(Buffer.concat(chunks)));
|
|
717
|
+
});
|
|
718
|
+
}
|
|
719
|
+
var nodeWikiGraphPlatform = {
|
|
720
|
+
asyncContext: {
|
|
721
|
+
create: () => new asyncHooks.AsyncLocalStorage()
|
|
722
|
+
},
|
|
723
|
+
database: { open: openNodeDatabase },
|
|
724
|
+
lifecycle: {
|
|
725
|
+
instanceId: `node-process:${nodeProcess.pid}`,
|
|
726
|
+
isInstanceAlive: (instanceId) => {
|
|
727
|
+
const match = /^node-process:(\d+)$/u.exec(instanceId);
|
|
728
|
+
if (match === null) return Promise.resolve(void 0);
|
|
729
|
+
try {
|
|
730
|
+
nodeProcess.kill(Number(match[1]), 0);
|
|
731
|
+
return Promise.resolve(true);
|
|
732
|
+
} catch (error) {
|
|
733
|
+
if (typeof error === "object" && error !== null && "code" in error && error.code === "ESRCH") {
|
|
734
|
+
return Promise.resolve(false);
|
|
735
|
+
}
|
|
736
|
+
return Promise.resolve(true);
|
|
737
|
+
}
|
|
738
|
+
}
|
|
739
|
+
},
|
|
740
|
+
resources: {
|
|
741
|
+
getDirectory: (identity) => {
|
|
742
|
+
const path = decodeNodeResourceIdentity(identity, "directory");
|
|
743
|
+
return Promise.resolve(
|
|
744
|
+
path === void 0 ? void 0 : new NodeDirectory(path)
|
|
745
|
+
);
|
|
746
|
+
},
|
|
747
|
+
getFile: (identity) => {
|
|
748
|
+
const path = decodeNodeResourceIdentity(identity, "file");
|
|
749
|
+
return Promise.resolve(
|
|
750
|
+
path === void 0 ? void 0 : new NodeFile(path)
|
|
751
|
+
);
|
|
752
|
+
},
|
|
753
|
+
resolveLegacyDirectory: (reference) => {
|
|
754
|
+
const path = decodeNodeResourceIdentity(reference, "directory");
|
|
755
|
+
return Promise.resolve(
|
|
756
|
+
path === void 0 ? void 0 : new NodeDirectory(path)
|
|
757
|
+
);
|
|
758
|
+
}
|
|
759
|
+
},
|
|
760
|
+
templates: {
|
|
761
|
+
createEnvironment: (options) => new Environment(new NodeTemplateLoader(), options)
|
|
762
|
+
},
|
|
763
|
+
zip: {
|
|
764
|
+
open: openNodeZip,
|
|
765
|
+
write: writeNodeZip
|
|
766
|
+
}
|
|
767
|
+
};
|
|
768
|
+
var NodeTemplateLoader = class extends Loader {
|
|
769
|
+
#dataDirectory;
|
|
770
|
+
constructor(dataDirectory) {
|
|
771
|
+
super();
|
|
772
|
+
this.#dataDirectory = dataDirectory;
|
|
773
|
+
}
|
|
774
|
+
getSource(templateName) {
|
|
775
|
+
const name = normalizeTemplateName(templateName);
|
|
776
|
+
const filePath = pathModule.join(
|
|
777
|
+
this.#dataDirectory ?? resolveNodeDataDirectory(),
|
|
778
|
+
name
|
|
779
|
+
);
|
|
780
|
+
return {
|
|
781
|
+
noCache: false,
|
|
782
|
+
path: name,
|
|
783
|
+
src: fs.readFileSync(filePath, "utf8")
|
|
784
|
+
};
|
|
785
|
+
}
|
|
786
|
+
};
|
|
787
|
+
function createNodeTemplateEnvironment(dataDirectory, options = {}) {
|
|
788
|
+
return new Environment(new NodeTemplateLoader(dataDirectory), {
|
|
789
|
+
autoescape: options.autoescape ?? true,
|
|
790
|
+
trimBlocks: options.trimBlocks ?? true
|
|
791
|
+
});
|
|
792
|
+
}
|
|
793
|
+
function resolveNodeDataDirectory() {
|
|
794
|
+
const injected = globalThis.__WIKIGRAPH_DATA_DIR__;
|
|
795
|
+
if (typeof injected === "string" && injected !== "") return injected;
|
|
796
|
+
let directory = nodeProcess.cwd();
|
|
797
|
+
while (true) {
|
|
798
|
+
for (const candidate of [
|
|
799
|
+
pathModule.join(directory, "data"),
|
|
800
|
+
pathModule.join(directory, "packages", "core", "data")
|
|
801
|
+
]) {
|
|
802
|
+
try {
|
|
803
|
+
if (fs.statSync(candidate).isDirectory()) return candidate;
|
|
804
|
+
} catch {
|
|
805
|
+
}
|
|
806
|
+
}
|
|
807
|
+
const parent = pathModule.dirname(directory);
|
|
808
|
+
if (parent === directory)
|
|
809
|
+
throw new Error("Could not locate data directory");
|
|
810
|
+
directory = parent;
|
|
811
|
+
}
|
|
812
|
+
}
|
|
813
|
+
function createNodeWikiGraphStorage(stateRoot = pathModule.join(os.homedir(), ".wikigraph")) {
|
|
814
|
+
return {
|
|
815
|
+
library: new NodeDirectory(stateRoot),
|
|
816
|
+
documentStore: new NodeDirectory(pathModule.join(stateRoot, "documents"))
|
|
817
|
+
};
|
|
818
|
+
}
|
|
819
|
+
function installNodeWikiGraphPlatform(stateRoot) {
|
|
820
|
+
installWikiGraphPlatform(nodeWikiGraphPlatform);
|
|
821
|
+
installWikiGraphStorage(createNodeWikiGraphStorage(stateRoot));
|
|
822
|
+
}
|
|
823
|
+
function ensureNodeWikiGraphPlatform() {
|
|
824
|
+
installWikiGraphPlatform(nodeWikiGraphPlatform);
|
|
825
|
+
try {
|
|
826
|
+
getWikiGraphStorage();
|
|
827
|
+
} catch {
|
|
828
|
+
installWikiGraphStorage(createNodeWikiGraphStorage());
|
|
829
|
+
}
|
|
830
|
+
}
|
|
831
|
+
async function withNodeWikiGraphStorage(stateRoot, operation) {
|
|
832
|
+
return await withWikiGraphStorage(
|
|
833
|
+
stateRoot === void 0 ? void 0 : createNodeWikiGraphStorage(stateRoot),
|
|
834
|
+
operation
|
|
835
|
+
);
|
|
836
|
+
}
|
|
837
|
+
|
|
838
|
+
// src/runtime-config.ts
|
|
839
|
+
import { z } from "zod";
|
|
840
|
+
|
|
841
|
+
// src/local-config.ts
|
|
842
|
+
import { openSharedStateDatabase } from "wiki-graph-core";
|
|
843
|
+
|
|
844
|
+
// src/runtime-context.ts
|
|
845
|
+
import { AsyncLocalStorage as AsyncLocalStorage2 } from "async_hooks";
|
|
846
|
+
var RUNTIME_CONTEXT_KEY = /* @__PURE__ */ Symbol.for("wiki-graph-sdk.runtime-context.v1");
|
|
847
|
+
var runtimeContext = getRuntimeContextStorage();
|
|
848
|
+
function getRuntimeContextStorage() {
|
|
849
|
+
const runtimeGlobal = globalThis;
|
|
850
|
+
const existing = runtimeGlobal[RUNTIME_CONTEXT_KEY];
|
|
851
|
+
if (existing !== void 0) {
|
|
852
|
+
return existing;
|
|
853
|
+
}
|
|
854
|
+
const storage = new AsyncLocalStorage2();
|
|
855
|
+
Object.defineProperty(runtimeGlobal, RUNTIME_CONTEXT_KEY, {
|
|
856
|
+
configurable: false,
|
|
857
|
+
enumerable: false,
|
|
858
|
+
value: storage,
|
|
859
|
+
writable: false
|
|
860
|
+
});
|
|
861
|
+
return storage;
|
|
862
|
+
}
|
|
863
|
+
async function withWikiGraphSDKRuntimeContext(context, operation) {
|
|
864
|
+
return await runtimeContext.run(context, operation);
|
|
865
|
+
}
|
|
866
|
+
function getWikiGraphSDKRuntimeContext() {
|
|
867
|
+
return runtimeContext.getStore() ?? {
|
|
868
|
+
cwd: process.cwd(),
|
|
869
|
+
env: process.env,
|
|
870
|
+
envPolicy: "production"
|
|
871
|
+
};
|
|
872
|
+
}
|
|
873
|
+
function getWikiGraphSDKStateDir() {
|
|
874
|
+
return getWikiGraphSDKRuntimeContext().stateDir;
|
|
875
|
+
}
|
|
876
|
+
|
|
877
|
+
// src/local-config.ts
|
|
878
|
+
import { join as join2 } from "path";
|
|
879
|
+
import { homedir as homedir2 } from "os";
|
|
880
|
+
var LOCAL_CONFIG_SECTIONS = [
|
|
881
|
+
"concurrent",
|
|
882
|
+
"embeddings",
|
|
883
|
+
"llm",
|
|
884
|
+
"wikimedia",
|
|
885
|
+
"wikispine"
|
|
886
|
+
];
|
|
887
|
+
var LOCAL_CONFIG_SCHEMA_SQL = `
|
|
888
|
+
CREATE TABLE IF NOT EXISTS config_sections (
|
|
889
|
+
section TEXT PRIMARY KEY,
|
|
890
|
+
value_json TEXT NOT NULL,
|
|
891
|
+
updated_at INTEGER NOT NULL
|
|
892
|
+
);
|
|
893
|
+
`;
|
|
894
|
+
async function readLocalConfigSection(section) {
|
|
895
|
+
return await withLocalConfigDatabase(async (database) => {
|
|
896
|
+
return await readLocalConfigSectionInDatabase(database, section);
|
|
897
|
+
});
|
|
898
|
+
}
|
|
899
|
+
async function replaceLocalConfigSection(section, value) {
|
|
900
|
+
const normalized = validateLocalConfigSection(section, value);
|
|
901
|
+
return await withLocalConfigDatabase(async (database) => {
|
|
902
|
+
await writeLocalConfigSectionInDatabase(database, section, normalized);
|
|
903
|
+
return normalized;
|
|
904
|
+
});
|
|
905
|
+
}
|
|
906
|
+
async function putLocalConfigValue(section, key, value) {
|
|
907
|
+
return await withLocalConfigDatabase(async (database) => {
|
|
908
|
+
const current = await readLocalConfigSectionInDatabase(database, section);
|
|
909
|
+
const next = validateLocalConfigSection(section, {
|
|
910
|
+
...current,
|
|
911
|
+
[normalizeLocalConfigKey(section, key)]: value
|
|
912
|
+
});
|
|
913
|
+
await writeLocalConfigSectionInDatabase(database, section, next);
|
|
914
|
+
return next;
|
|
915
|
+
});
|
|
916
|
+
}
|
|
917
|
+
async function deleteLocalConfigValue(section, key) {
|
|
918
|
+
return await withLocalConfigDatabase(async (database) => {
|
|
919
|
+
const current = await readLocalConfigSectionInDatabase(database, section);
|
|
920
|
+
const normalizedKey = normalizeLocalConfigKey(section, key);
|
|
921
|
+
const { [normalizedKey]: _removed, ...nextInput } = current;
|
|
922
|
+
const next = validateLocalConfigSection(section, nextInput);
|
|
923
|
+
await writeLocalConfigSectionInDatabase(database, section, next);
|
|
924
|
+
return next;
|
|
925
|
+
});
|
|
926
|
+
}
|
|
927
|
+
async function clearLocalConfigSection(section) {
|
|
928
|
+
return await replaceLocalConfigSection(section, {});
|
|
929
|
+
}
|
|
930
|
+
function parseLocalConfigSection(value) {
|
|
931
|
+
if (value === "embedding") {
|
|
932
|
+
return "embeddings";
|
|
933
|
+
}
|
|
934
|
+
return LOCAL_CONFIG_SECTIONS.find((section) => section === value);
|
|
935
|
+
}
|
|
936
|
+
function maskLocalConfigSection(section, value) {
|
|
937
|
+
if (section !== "llm" && section !== "embeddings" && section !== "wikimedia" && section !== "wikispine" || value.apiKey === void 0 && value.token === void 0) {
|
|
938
|
+
return value;
|
|
939
|
+
}
|
|
940
|
+
return {
|
|
941
|
+
...value,
|
|
942
|
+
...value.apiKey === void 0 ? {} : { apiKey: "****" },
|
|
943
|
+
...value.token === void 0 ? {} : { token: "****" }
|
|
944
|
+
};
|
|
945
|
+
}
|
|
946
|
+
function normalizeLocalConfigKey(section, key) {
|
|
947
|
+
const normalized = key.trim();
|
|
948
|
+
if (normalized === "") {
|
|
949
|
+
throw new Error("Config key cannot be empty.");
|
|
950
|
+
}
|
|
951
|
+
if (section === "llm" || section === "embeddings") {
|
|
952
|
+
switch (normalized) {
|
|
953
|
+
case "api-key":
|
|
954
|
+
return "apiKey";
|
|
955
|
+
case "base-url":
|
|
956
|
+
return "baseURL";
|
|
957
|
+
default:
|
|
958
|
+
return normalized;
|
|
959
|
+
}
|
|
960
|
+
}
|
|
961
|
+
return normalized;
|
|
962
|
+
}
|
|
963
|
+
function validateLocalConfigSection(section, value) {
|
|
964
|
+
switch (section) {
|
|
965
|
+
case "embeddings":
|
|
966
|
+
return validateEmbeddingsConfig(value);
|
|
967
|
+
case "llm":
|
|
968
|
+
return validateLLMConfig(value);
|
|
969
|
+
case "concurrent":
|
|
970
|
+
return validateConcurrentConfig(value);
|
|
971
|
+
case "wikimedia":
|
|
972
|
+
return validateWikimediaConfig(value);
|
|
973
|
+
case "wikispine":
|
|
974
|
+
return validateWikispineConfig(value);
|
|
975
|
+
}
|
|
976
|
+
}
|
|
977
|
+
async function withLocalConfigDatabase(operation) {
|
|
978
|
+
const database = await openSharedStateDatabase(
|
|
979
|
+
new NodeFile(
|
|
980
|
+
join2(
|
|
981
|
+
getWikiGraphSDKStateDir() ?? join2(homedir2(), ".wikigraph"),
|
|
982
|
+
"core.sqlite"
|
|
983
|
+
)
|
|
984
|
+
),
|
|
985
|
+
LOCAL_CONFIG_SCHEMA_SQL
|
|
986
|
+
);
|
|
987
|
+
try {
|
|
988
|
+
return await operation(database);
|
|
989
|
+
} finally {
|
|
990
|
+
await database.close();
|
|
991
|
+
}
|
|
992
|
+
}
|
|
993
|
+
async function readLocalConfigSectionInDatabase(database, section) {
|
|
994
|
+
const value = await database.queryOne(
|
|
995
|
+
"SELECT value_json FROM config_sections WHERE section = ?",
|
|
996
|
+
[section],
|
|
997
|
+
(row) => String(row.value_json)
|
|
998
|
+
);
|
|
999
|
+
return value === void 0 ? {} : parseSectionJSON(section, value);
|
|
1000
|
+
}
|
|
1001
|
+
async function writeLocalConfigSectionInDatabase(database, section, value) {
|
|
1002
|
+
await database.run(
|
|
1003
|
+
`
|
|
1004
|
+
INSERT INTO config_sections (section, value_json, updated_at)
|
|
1005
|
+
VALUES (?, ?, ?)
|
|
1006
|
+
ON CONFLICT(section) DO UPDATE SET
|
|
1007
|
+
value_json = excluded.value_json,
|
|
1008
|
+
updated_at = excluded.updated_at
|
|
1009
|
+
`,
|
|
1010
|
+
[section, JSON.stringify(value), Date.now()]
|
|
1011
|
+
);
|
|
1012
|
+
}
|
|
1013
|
+
function parseSectionJSON(section, value) {
|
|
1014
|
+
let parsed;
|
|
1015
|
+
try {
|
|
1016
|
+
parsed = JSON.parse(value);
|
|
1017
|
+
} catch (error) {
|
|
1018
|
+
throw new Error(
|
|
1019
|
+
`Invalid local config JSON for ${section}: ${formatError(error)}`
|
|
1020
|
+
);
|
|
1021
|
+
}
|
|
1022
|
+
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
1023
|
+
throw new Error(
|
|
1024
|
+
`Invalid local config section ${section}: expected object.`
|
|
1025
|
+
);
|
|
1026
|
+
}
|
|
1027
|
+
return validateLocalConfigSection(section, parsed);
|
|
1028
|
+
}
|
|
1029
|
+
function validateEmbeddingsConfig(value) {
|
|
1030
|
+
const allowedKeys = /* @__PURE__ */ new Set([
|
|
1031
|
+
"apiKey",
|
|
1032
|
+
"baseURL",
|
|
1033
|
+
"dimensions",
|
|
1034
|
+
"model",
|
|
1035
|
+
"name",
|
|
1036
|
+
"provider"
|
|
1037
|
+
]);
|
|
1038
|
+
const allowedProviders = /* @__PURE__ */ new Set(["openai", "openai-compatible"]);
|
|
1039
|
+
const next = {};
|
|
1040
|
+
for (const [key, entry] of Object.entries(value)) {
|
|
1041
|
+
if (!allowedKeys.has(key)) {
|
|
1042
|
+
throw new Error(`Unknown embeddings config key: ${key}`);
|
|
1043
|
+
}
|
|
1044
|
+
if (key === "dimensions") {
|
|
1045
|
+
const parsed = typeof entry === "number" ? entry : typeof entry === "string" ? Number(entry) : NaN;
|
|
1046
|
+
if (!Number.isInteger(parsed) || parsed <= 0) {
|
|
1047
|
+
throw new Error("embeddings.dimensions must be a positive integer.");
|
|
1048
|
+
}
|
|
1049
|
+
next[key] = parsed;
|
|
1050
|
+
continue;
|
|
1051
|
+
}
|
|
1052
|
+
if (typeof entry !== "string" || entry.trim() === "") {
|
|
1053
|
+
throw new Error(`embeddings.${key} must be a non-empty string.`);
|
|
1054
|
+
}
|
|
1055
|
+
const normalized = entry.trim();
|
|
1056
|
+
if (key === "provider" && !allowedProviders.has(normalized)) {
|
|
1057
|
+
throw new Error(`Unknown embeddings.provider: ${normalized}`);
|
|
1058
|
+
}
|
|
1059
|
+
next[key] = normalized;
|
|
1060
|
+
}
|
|
1061
|
+
return next;
|
|
1062
|
+
}
|
|
1063
|
+
function validateLLMConfig(value) {
|
|
1064
|
+
const allowedKeys = /* @__PURE__ */ new Set([
|
|
1065
|
+
"apiKey",
|
|
1066
|
+
"baseURL",
|
|
1067
|
+
"model",
|
|
1068
|
+
"name",
|
|
1069
|
+
"provider"
|
|
1070
|
+
]);
|
|
1071
|
+
const allowedProviders = /* @__PURE__ */ new Set([
|
|
1072
|
+
"anthropic",
|
|
1073
|
+
"google",
|
|
1074
|
+
"openai",
|
|
1075
|
+
"openai-compatible"
|
|
1076
|
+
]);
|
|
1077
|
+
const next = {};
|
|
1078
|
+
for (const [key, entry] of Object.entries(value)) {
|
|
1079
|
+
if (!allowedKeys.has(key)) {
|
|
1080
|
+
throw new Error(`Unknown llm config key: ${key}`);
|
|
1081
|
+
}
|
|
1082
|
+
if (typeof entry !== "string" || entry.trim() === "") {
|
|
1083
|
+
throw new Error(`llm.${key} must be a non-empty string.`);
|
|
1084
|
+
}
|
|
1085
|
+
const normalized = entry.trim();
|
|
1086
|
+
if (key === "provider" && !allowedProviders.has(normalized)) {
|
|
1087
|
+
throw new Error(`Unknown llm.provider: ${normalized}`);
|
|
1088
|
+
}
|
|
1089
|
+
next[key] = normalized;
|
|
1090
|
+
}
|
|
1091
|
+
return next;
|
|
1092
|
+
}
|
|
1093
|
+
function validateConcurrentConfig(value) {
|
|
1094
|
+
const allowedKeys = /* @__PURE__ */ new Set(["job", "request"]);
|
|
1095
|
+
const next = {};
|
|
1096
|
+
for (const [key, entry] of Object.entries(value)) {
|
|
1097
|
+
if (!allowedKeys.has(key)) {
|
|
1098
|
+
throw new Error(`Unknown concurrent config key: ${key}`);
|
|
1099
|
+
}
|
|
1100
|
+
const parsed = typeof entry === "number" ? entry : typeof entry === "string" ? Number(entry) : NaN;
|
|
1101
|
+
if (!Number.isInteger(parsed) || parsed <= 0) {
|
|
1102
|
+
throw new Error(`concurrent.${key} must be a positive integer.`);
|
|
1103
|
+
}
|
|
1104
|
+
next[key] = parsed;
|
|
1105
|
+
}
|
|
1106
|
+
return next;
|
|
1107
|
+
}
|
|
1108
|
+
function validateWikispineConfig(value) {
|
|
1109
|
+
const allowedKeys = /* @__PURE__ */ new Set(["endpoint", "provider", "token"]);
|
|
1110
|
+
const allowedProviders = /* @__PURE__ */ new Set(["cli", "fetch"]);
|
|
1111
|
+
const next = {};
|
|
1112
|
+
for (const [key, entry] of Object.entries(value)) {
|
|
1113
|
+
if (!allowedKeys.has(key)) {
|
|
1114
|
+
throw new Error(`Unknown wikispine config key: ${key}`);
|
|
1115
|
+
}
|
|
1116
|
+
if (typeof entry !== "string" || entry.trim() === "") {
|
|
1117
|
+
throw new Error(`wikispine.${key} must be a non-empty string.`);
|
|
1118
|
+
}
|
|
1119
|
+
const normalized = entry.trim();
|
|
1120
|
+
if (key === "provider" && !allowedProviders.has(normalized)) {
|
|
1121
|
+
throw new Error(`Unknown wikispine.provider: ${normalized}`);
|
|
1122
|
+
}
|
|
1123
|
+
next[key] = normalized;
|
|
1124
|
+
}
|
|
1125
|
+
return next;
|
|
1126
|
+
}
|
|
1127
|
+
function validateWikimediaConfig(value) {
|
|
1128
|
+
return validateEndpointConfig("wikimedia", value);
|
|
1129
|
+
}
|
|
1130
|
+
function validateEndpointConfig(section, value) {
|
|
1131
|
+
const allowedKeys = /* @__PURE__ */ new Set(["endpoint", "token"]);
|
|
1132
|
+
const next = {};
|
|
1133
|
+
for (const [key, entry] of Object.entries(value)) {
|
|
1134
|
+
if (!allowedKeys.has(key)) {
|
|
1135
|
+
throw new Error(`Unknown ${section} config key: ${key}`);
|
|
1136
|
+
}
|
|
1137
|
+
if (typeof entry !== "string" || entry.trim() === "") {
|
|
1138
|
+
throw new Error(`${section}.${key} must be a non-empty string.`);
|
|
1139
|
+
}
|
|
1140
|
+
next[key] = entry.trim();
|
|
1141
|
+
}
|
|
1142
|
+
return next;
|
|
1143
|
+
}
|
|
1144
|
+
function formatError(error) {
|
|
1145
|
+
return error instanceof Error ? error.message : String(error);
|
|
1146
|
+
}
|
|
1147
|
+
|
|
1148
|
+
// src/runtime-config.ts
|
|
1149
|
+
var WIKI_GRAPH_PROVIDER_VALUES = [
|
|
1150
|
+
"anthropic",
|
|
1151
|
+
"google",
|
|
1152
|
+
"openai",
|
|
1153
|
+
"openai-compatible"
|
|
1154
|
+
];
|
|
1155
|
+
var wikiGraphProviderSchema = z.enum(WIKI_GRAPH_PROVIDER_VALUES);
|
|
1156
|
+
var inlineLLMConfigSchema = z.object({
|
|
1157
|
+
apiKey: z.string().min(1).optional(),
|
|
1158
|
+
baseURL: z.string().min(1).optional(),
|
|
1159
|
+
baseUrl: z.string().min(1).optional(),
|
|
1160
|
+
chatCompletionsUrl: z.string().min(1).optional(),
|
|
1161
|
+
llm: z.object({
|
|
1162
|
+
apiKey: z.string().min(1).optional(),
|
|
1163
|
+
baseURL: z.string().min(1).optional(),
|
|
1164
|
+
baseUrl: z.string().min(1).optional(),
|
|
1165
|
+
chatCompletionsUrl: z.string().min(1).optional(),
|
|
1166
|
+
model: z.string().min(1).optional(),
|
|
1167
|
+
name: z.string().min(1).optional(),
|
|
1168
|
+
provider: wikiGraphProviderSchema.optional()
|
|
1169
|
+
}).optional(),
|
|
1170
|
+
model: z.string().min(1).optional(),
|
|
1171
|
+
name: z.string().min(1).optional(),
|
|
1172
|
+
provider: wikiGraphProviderSchema.optional()
|
|
1173
|
+
});
|
|
1174
|
+
async function loadWikiGraphRuntimeConfig(options) {
|
|
1175
|
+
const [embedding, localLLM, concurrent, wikimedia, wikispine] = await Promise.all([
|
|
1176
|
+
readLocalConfigSection("embeddings"),
|
|
1177
|
+
readLocalConfigSection("llm"),
|
|
1178
|
+
readLocalConfigSection("concurrent"),
|
|
1179
|
+
readLocalConfigSection("wikimedia"),
|
|
1180
|
+
readLocalConfigSection("wikispine")
|
|
1181
|
+
]);
|
|
1182
|
+
const inlineLLMConfig = options?.llmJSON === void 0 ? void 0 : parseInlineLLMConfig(options.llmJSON);
|
|
1183
|
+
const llm = createLLMConfig({
|
|
1184
|
+
apiKey: firstDefined(inlineLLMConfig?.apiKey, readString(localLLM.apiKey)),
|
|
1185
|
+
baseURL: firstDefined(
|
|
1186
|
+
inlineLLMConfig?.baseURL,
|
|
1187
|
+
readString(localLLM.baseURL)
|
|
1188
|
+
),
|
|
1189
|
+
model: firstDefined(inlineLLMConfig?.model, readString(localLLM.model)),
|
|
1190
|
+
name: firstDefined(inlineLLMConfig?.name, readString(localLLM.name)),
|
|
1191
|
+
provider: firstDefined(
|
|
1192
|
+
inlineLLMConfig?.provider,
|
|
1193
|
+
readProvider(localLLM.provider)
|
|
1194
|
+
)
|
|
1195
|
+
});
|
|
1196
|
+
const requestConcurrent = readPositiveInteger(concurrent.request);
|
|
1197
|
+
const jobConcurrent = readPositiveInteger(concurrent.job);
|
|
1198
|
+
const envPolicy = getWikiGraphSDKRuntimeContext().envPolicy;
|
|
1199
|
+
const wikispineConfig = resolveWikispineConfig(wikispine, envPolicy);
|
|
1200
|
+
const wikimediaConfig = createWikimediaConfig(wikimedia, envPolicy);
|
|
1201
|
+
const embeddingConfig = createEmbeddingConfig(embedding);
|
|
1202
|
+
return {
|
|
1203
|
+
...jobConcurrent === void 0 && requestConcurrent === void 0 ? {} : {
|
|
1204
|
+
concurrent: {
|
|
1205
|
+
...jobConcurrent === void 0 ? {} : { job: jobConcurrent },
|
|
1206
|
+
...requestConcurrent === void 0 ? {} : { request: requestConcurrent }
|
|
1207
|
+
}
|
|
1208
|
+
},
|
|
1209
|
+
...embeddingConfig === void 0 ? {} : { embedding: embeddingConfig },
|
|
1210
|
+
...llm === void 0 ? {} : { llm },
|
|
1211
|
+
...wikimediaConfig === void 0 ? {} : { wikimedia: wikimediaConfig },
|
|
1212
|
+
...wikispineConfig === void 0 ? {} : { wikispine: wikispineConfig }
|
|
1213
|
+
};
|
|
1214
|
+
}
|
|
1215
|
+
function createWikimediaConfig(input, envPolicy) {
|
|
1216
|
+
const endpoint = readString(input?.endpoint);
|
|
1217
|
+
const token = readString(input?.token);
|
|
1218
|
+
if (endpoint === void 0 && token === void 0) return void 0;
|
|
1219
|
+
if (token === void 0) {
|
|
1220
|
+
throw new Error(
|
|
1221
|
+
"Remote Wikimedia access requires an API key. Configure `wikg://local/config/wikimedia` token with `put token --secret`."
|
|
1222
|
+
);
|
|
1223
|
+
}
|
|
1224
|
+
return {
|
|
1225
|
+
endpoint: endpoint ?? resolveHostedProviderEndpoint("wg-wikimedia", envPolicy),
|
|
1226
|
+
token
|
|
1227
|
+
};
|
|
1228
|
+
}
|
|
1229
|
+
function createEmbeddingConfig(input) {
|
|
1230
|
+
const value = input ?? {};
|
|
1231
|
+
const provider = readEmbeddingProvider(value.provider);
|
|
1232
|
+
const apiKey = readString(value.apiKey);
|
|
1233
|
+
const baseURL = readString(value.baseURL);
|
|
1234
|
+
const dimensions = typeof value.dimensions === "number" && Number.isInteger(value.dimensions) && value.dimensions > 0 ? value.dimensions : void 0;
|
|
1235
|
+
const model = readString(value.model);
|
|
1236
|
+
const name = readString(value.name);
|
|
1237
|
+
if (provider === void 0 || model === void 0 || provider === "openai-compatible" && baseURL === void 0 || provider === "openai" && baseURL !== void 0) {
|
|
1238
|
+
return void 0;
|
|
1239
|
+
}
|
|
1240
|
+
return {
|
|
1241
|
+
...apiKey === void 0 ? {} : { apiKey },
|
|
1242
|
+
...baseURL === void 0 ? {} : { baseURL },
|
|
1243
|
+
...dimensions === void 0 ? {} : { dimensions },
|
|
1244
|
+
...model === void 0 ? {} : { model },
|
|
1245
|
+
...name === void 0 ? {} : { name },
|
|
1246
|
+
...provider === void 0 ? {} : { provider }
|
|
1247
|
+
};
|
|
1248
|
+
}
|
|
1249
|
+
function parseInlineLLMConfig(value) {
|
|
1250
|
+
const normalized = normalizeString(value);
|
|
1251
|
+
if (normalized === void 0) {
|
|
1252
|
+
throw new Error("--llm must be a non-empty JSON object.");
|
|
1253
|
+
}
|
|
1254
|
+
let parsedJson;
|
|
1255
|
+
try {
|
|
1256
|
+
parsedJson = JSON.parse(normalized);
|
|
1257
|
+
} catch (error) {
|
|
1258
|
+
throw new Error(`Invalid --llm JSON: ${formatError2(error)}`);
|
|
1259
|
+
}
|
|
1260
|
+
const parsed = inlineLLMConfigSchema.safeParse(parsedJson);
|
|
1261
|
+
if (!parsed.success) {
|
|
1262
|
+
throw new Error(
|
|
1263
|
+
`Invalid --llm config: ${parsed.error.issues.map((issue) => `${issue.path.join(".") || "<root>"}: ${issue.message}`).join("; ")}`
|
|
1264
|
+
);
|
|
1265
|
+
}
|
|
1266
|
+
const input = parsed.data.llm ?? parsed.data;
|
|
1267
|
+
const provider = input.provider ?? inferInlineProvider(input);
|
|
1268
|
+
const baseURL = resolveInlineBaseURL(input);
|
|
1269
|
+
const config = createLLMConfig({
|
|
1270
|
+
apiKey: input.apiKey,
|
|
1271
|
+
baseURL,
|
|
1272
|
+
model: input.model,
|
|
1273
|
+
name: input.name,
|
|
1274
|
+
provider
|
|
1275
|
+
});
|
|
1276
|
+
if (config === void 0) {
|
|
1277
|
+
throw new Error("--llm must contain at least one supported LLM field.");
|
|
1278
|
+
}
|
|
1279
|
+
return config;
|
|
1280
|
+
}
|
|
1281
|
+
function inferInlineProvider(input) {
|
|
1282
|
+
if (input.provider === void 0 && (input.baseURL !== void 0 || input.baseUrl !== void 0 || input.chatCompletionsUrl !== void 0)) {
|
|
1283
|
+
return "openai-compatible";
|
|
1284
|
+
}
|
|
1285
|
+
return input.provider;
|
|
1286
|
+
}
|
|
1287
|
+
function inferBaseURLFromChatCompletionsURL(input) {
|
|
1288
|
+
if (input.chatCompletionsUrl === void 0) {
|
|
1289
|
+
return void 0;
|
|
1290
|
+
}
|
|
1291
|
+
const suffix = "/chat/completions";
|
|
1292
|
+
if (!input.chatCompletionsUrl.endsWith(suffix)) {
|
|
1293
|
+
throw new Error(
|
|
1294
|
+
"--llm chatCompletionsUrl must end with /chat/completions when baseURL is not provided."
|
|
1295
|
+
);
|
|
1296
|
+
}
|
|
1297
|
+
return input.chatCompletionsUrl.slice(0, -suffix.length);
|
|
1298
|
+
}
|
|
1299
|
+
function resolveInlineBaseURL(input) {
|
|
1300
|
+
if (input.baseURL !== void 0) {
|
|
1301
|
+
return input.baseURL;
|
|
1302
|
+
}
|
|
1303
|
+
if (input.baseUrl !== void 0) {
|
|
1304
|
+
return input.baseUrl;
|
|
1305
|
+
}
|
|
1306
|
+
return inferBaseURLFromChatCompletionsURL(input);
|
|
1307
|
+
}
|
|
1308
|
+
function createLLMConfig(input) {
|
|
1309
|
+
if (input.apiKey === void 0 && input.baseURL === void 0 && input.model === void 0 && input.name === void 0 && input.provider === void 0) {
|
|
1310
|
+
return void 0;
|
|
1311
|
+
}
|
|
1312
|
+
return {
|
|
1313
|
+
...input.apiKey === void 0 ? {} : { apiKey: input.apiKey },
|
|
1314
|
+
...input.baseURL === void 0 ? {} : { baseURL: input.baseURL },
|
|
1315
|
+
...input.model === void 0 ? {} : { model: input.model },
|
|
1316
|
+
...input.name === void 0 ? {} : { name: input.name },
|
|
1317
|
+
...input.provider === void 0 ? {} : { provider: input.provider }
|
|
1318
|
+
};
|
|
1319
|
+
}
|
|
1320
|
+
function readProvider(value) {
|
|
1321
|
+
if (typeof value !== "string") {
|
|
1322
|
+
return void 0;
|
|
1323
|
+
}
|
|
1324
|
+
const parsed = wikiGraphProviderSchema.safeParse(value);
|
|
1325
|
+
return parsed.success ? parsed.data : void 0;
|
|
1326
|
+
}
|
|
1327
|
+
function resolveWikispineConfig(value, envPolicy = getWikiGraphSDKRuntimeContext().envPolicy) {
|
|
1328
|
+
const provider = readWikispineProvider(value.provider);
|
|
1329
|
+
const endpoint = readString(value.endpoint);
|
|
1330
|
+
const token = readString(value.token);
|
|
1331
|
+
if (provider === void 0) {
|
|
1332
|
+
return void 0;
|
|
1333
|
+
}
|
|
1334
|
+
if (provider === "cli") return { provider };
|
|
1335
|
+
if (token === void 0) {
|
|
1336
|
+
throw new Error(
|
|
1337
|
+
"Remote WikiSpine access requires an API key. Configure `wikg://local/config/wikispine` token with `put token --secret`."
|
|
1338
|
+
);
|
|
1339
|
+
}
|
|
1340
|
+
return {
|
|
1341
|
+
endpoint: endpoint ?? resolveHostedProviderEndpoint("wikispine", envPolicy),
|
|
1342
|
+
provider,
|
|
1343
|
+
token
|
|
1344
|
+
};
|
|
1345
|
+
}
|
|
1346
|
+
function resolveHostedProviderEndpoint(scope, envPolicy = getWikiGraphSDKRuntimeContext().envPolicy) {
|
|
1347
|
+
const origin = envPolicy === "development" ? "https://pdf-craft-api.oomol.dev" : "https://api.pdfcraft.ai";
|
|
1348
|
+
return `${origin}/v1/${scope}`;
|
|
1349
|
+
}
|
|
1350
|
+
function readWikispineProvider(value) {
|
|
1351
|
+
return value === "cli" || value === "fetch" ? value : void 0;
|
|
1352
|
+
}
|
|
1353
|
+
function readEmbeddingProvider(value) {
|
|
1354
|
+
return value === "openai" || value === "openai-compatible" ? value : void 0;
|
|
1355
|
+
}
|
|
1356
|
+
function readString(value) {
|
|
1357
|
+
return typeof value === "string" ? value : void 0;
|
|
1358
|
+
}
|
|
1359
|
+
function readPositiveInteger(value) {
|
|
1360
|
+
return typeof value === "number" && Number.isInteger(value) && value > 0 ? value : void 0;
|
|
1361
|
+
}
|
|
1362
|
+
function firstDefined(first, second) {
|
|
1363
|
+
return first ?? second;
|
|
1364
|
+
}
|
|
1365
|
+
function normalizeString(value) {
|
|
1366
|
+
const normalized = value?.trim();
|
|
1367
|
+
return normalized === void 0 || normalized === "" ? void 0 : normalized;
|
|
1368
|
+
}
|
|
1369
|
+
function formatError2(error) {
|
|
1370
|
+
return error instanceof Error ? error.message : String(error);
|
|
1371
|
+
}
|
|
1372
|
+
|
|
1373
|
+
// src/runtime-path.ts
|
|
1374
|
+
import { homedir as homedir3 } from "os";
|
|
1375
|
+
import { isAbsolute as isAbsolute2, resolve as resolve2 } from "path";
|
|
1376
|
+
function resolveWikiGraphRuntimePath(path) {
|
|
1377
|
+
const context = getWikiGraphSDKRuntimeContext();
|
|
1378
|
+
const environmentHome = context.env.HOME?.trim();
|
|
1379
|
+
const home = environmentHome === void 0 || environmentHome === "" ? homedir3() : environmentHome;
|
|
1380
|
+
if (path === "~") return home;
|
|
1381
|
+
if (path.startsWith("~/")) return resolve2(home, path.slice(2));
|
|
1382
|
+
return isAbsolute2(path) ? path : resolve2(context.cwd, path);
|
|
1383
|
+
}
|
|
1384
|
+
|
|
1385
|
+
// src/conversions.ts
|
|
1386
|
+
var WikiGraphConversionManager = class {
|
|
1387
|
+
#runtime;
|
|
1388
|
+
constructor(runtime) {
|
|
1389
|
+
this.#runtime = runtime;
|
|
1390
|
+
}
|
|
1391
|
+
async convert(options) {
|
|
1392
|
+
return await this.#runtime.run(async () => await this.#convert(options));
|
|
1393
|
+
}
|
|
1394
|
+
async #convert(options) {
|
|
1395
|
+
const targetStage = options.targetStage ?? "summarized";
|
|
1396
|
+
if (options.targetStage !== void 0 && options.output.format !== "wikg") {
|
|
1397
|
+
throw new Error("targetStage is only supported for wikg output.");
|
|
1398
|
+
}
|
|
1399
|
+
if (options.targetStage !== void 0 && options.input.format === "wikg") {
|
|
1400
|
+
throw new Error(
|
|
1401
|
+
"targetStage is only supported when creating a wikg archive."
|
|
1402
|
+
);
|
|
1403
|
+
}
|
|
1404
|
+
const requiresDigest = options.input.format !== "wikg";
|
|
1405
|
+
const requiresLLM = requiresDigest && targetStage !== "planned" && targetStage !== "sourced";
|
|
1406
|
+
const config = await loadWikiGraphRuntimeConfig({
|
|
1407
|
+
...options.llmJSON === void 0 ? {} : { llmJSON: options.llmJSON }
|
|
1408
|
+
});
|
|
1409
|
+
const app = new WikiGraph({
|
|
1410
|
+
...options.verbose === true ? { verbose: true } : {},
|
|
1411
|
+
...requiresLLM ? { llm: buildWikiGraphLLMOptions(config) } : {}
|
|
1412
|
+
});
|
|
1413
|
+
const documentDirectory = await prepareDigestDirectory(
|
|
1414
|
+
options.digestDirectory,
|
|
1415
|
+
requiresDigest
|
|
1416
|
+
);
|
|
1417
|
+
const write = async (archive) => {
|
|
1418
|
+
await writeArchive(archive, options.output.path, options.output.format);
|
|
1419
|
+
};
|
|
1420
|
+
if (options.input.format === "wikg") {
|
|
1421
|
+
if (!("path" in options.input)) {
|
|
1422
|
+
throw new Error("wikg input requires a file path.");
|
|
1423
|
+
}
|
|
1424
|
+
await app.openSession(
|
|
1425
|
+
new NodeFile(resolveWikiGraphRuntimePath(options.input.path)),
|
|
1426
|
+
write
|
|
1427
|
+
);
|
|
1428
|
+
} else if ("stream" in options.input) {
|
|
1429
|
+
await app.digestTextStreamSession(
|
|
1430
|
+
{
|
|
1431
|
+
...documentDirectory === void 0 ? {} : { documentDirectory },
|
|
1432
|
+
...options.onProgress === void 0 ? {} : { onProgress: options.onProgress },
|
|
1433
|
+
sourceFormat: options.input.format,
|
|
1434
|
+
stream: options.input.stream,
|
|
1435
|
+
...(options.prompt ?? config.prompt) === void 0 ? {} : { extractionPrompt: options.prompt ?? config.prompt },
|
|
1436
|
+
targetStage
|
|
1437
|
+
},
|
|
1438
|
+
write
|
|
1439
|
+
);
|
|
1440
|
+
} else {
|
|
1441
|
+
const digestOptions = {
|
|
1442
|
+
...documentDirectory === void 0 ? {} : { documentDirectory },
|
|
1443
|
+
file: new NodeFile(resolveWikiGraphRuntimePath(options.input.path)),
|
|
1444
|
+
...options.onProgress === void 0 ? {} : { onProgress: options.onProgress },
|
|
1445
|
+
...(options.prompt ?? config.prompt) === void 0 ? {} : { extractionPrompt: options.prompt ?? config.prompt },
|
|
1446
|
+
targetStage
|
|
1447
|
+
};
|
|
1448
|
+
if (options.input.format === "epub") {
|
|
1449
|
+
await app.digestEpubSession(digestOptions, write);
|
|
1450
|
+
} else if (options.input.format === "markdown") {
|
|
1451
|
+
await app.digestMarkdownSession(digestOptions, write);
|
|
1452
|
+
} else {
|
|
1453
|
+
await app.digestTxtSession(digestOptions, write);
|
|
1454
|
+
}
|
|
1455
|
+
}
|
|
1456
|
+
return {
|
|
1457
|
+
inputFormat: options.input.format,
|
|
1458
|
+
outputFormat: options.output.format,
|
|
1459
|
+
outputPath: options.output.path
|
|
1460
|
+
};
|
|
1461
|
+
}
|
|
1462
|
+
};
|
|
1463
|
+
async function prepareDigestDirectory(path, required) {
|
|
1464
|
+
const normalized = path?.trim();
|
|
1465
|
+
if (!required || normalized === void 0 || normalized === "")
|
|
1466
|
+
return void 0;
|
|
1467
|
+
const resolved = resolveWikiGraphRuntimePath(normalized);
|
|
1468
|
+
await rm2(resolved, { force: true, recursive: true });
|
|
1469
|
+
return new NodeDirectory(resolved);
|
|
1470
|
+
}
|
|
1471
|
+
async function writeArchive(archive, path, format) {
|
|
1472
|
+
const file = new NodeFile(resolveWikiGraphRuntimePath(path));
|
|
1473
|
+
if (format === "epub") await archive.exportEpub(file);
|
|
1474
|
+
else if (format === "wikg") await archive.saveAs(file);
|
|
1475
|
+
else await archive.exportText(file);
|
|
1476
|
+
}
|
|
1477
|
+
|
|
1478
|
+
// src/query-runtime.ts
|
|
1479
|
+
import {
|
|
1480
|
+
openWikimediaResolver
|
|
1481
|
+
} from "wiki-graph-core";
|
|
1482
|
+
|
|
1483
|
+
// src/embedding.ts
|
|
1484
|
+
import { embed, embedMany } from "ai";
|
|
1485
|
+
import { createOpenAI as createOpenAI2 } from "@ai-sdk/openai";
|
|
1486
|
+
import { createOpenAICompatible as createOpenAICompatible2 } from "@ai-sdk/openai-compatible";
|
|
1487
|
+
var EMBEDDING_BATCH_SIZE = 10;
|
|
1488
|
+
async function readWikiGraphEmbeddingConfig() {
|
|
1489
|
+
const config = await readLocalConfigSection("embeddings");
|
|
1490
|
+
return {
|
|
1491
|
+
...typeof config.apiKey === "string" ? { apiKey: config.apiKey } : {},
|
|
1492
|
+
...typeof config.baseURL === "string" ? { baseURL: config.baseURL } : {},
|
|
1493
|
+
...typeof config.dimensions === "number" ? { dimensions: config.dimensions } : {},
|
|
1494
|
+
...typeof config.model === "string" ? { model: config.model } : {},
|
|
1495
|
+
...typeof config.name === "string" ? { name: config.name } : {},
|
|
1496
|
+
...typeof config.provider === "string" ? { provider: parseEmbeddingProvider(config.provider) } : {}
|
|
1497
|
+
};
|
|
1498
|
+
}
|
|
1499
|
+
async function embedQueryText(value, config = {}) {
|
|
1500
|
+
const normalized = value.trim();
|
|
1501
|
+
if (normalized === "") {
|
|
1502
|
+
throw new Error("Query text cannot be empty.");
|
|
1503
|
+
}
|
|
1504
|
+
const provider = requireEmbeddingProvider(config.provider);
|
|
1505
|
+
const model = requireEmbeddingModel(config.model);
|
|
1506
|
+
const embeddingModel = createEmbeddingModel(provider, model, config);
|
|
1507
|
+
const providerOptions = createEmbeddingProviderOptions(provider, config);
|
|
1508
|
+
const result = await embed({
|
|
1509
|
+
model: embeddingModel,
|
|
1510
|
+
...providerOptions === void 0 ? {} : { providerOptions },
|
|
1511
|
+
value: normalized
|
|
1512
|
+
});
|
|
1513
|
+
return {
|
|
1514
|
+
dimensions: result.embedding.length,
|
|
1515
|
+
embedding: result.embedding,
|
|
1516
|
+
model,
|
|
1517
|
+
provider,
|
|
1518
|
+
...result.usage.tokens === void 0 ? {} : { usage: { tokens: result.usage.tokens } }
|
|
1519
|
+
};
|
|
1520
|
+
}
|
|
1521
|
+
function buildSearchIndexEmbeddingProvider(config) {
|
|
1522
|
+
const provider = requireEmbeddingProvider(config.provider);
|
|
1523
|
+
const model = requireEmbeddingModel(config.model);
|
|
1524
|
+
const embeddingModel = createEmbeddingModel(provider, model, config);
|
|
1525
|
+
const providerOptions = createEmbeddingProviderOptions(provider, config);
|
|
1526
|
+
return {
|
|
1527
|
+
...config.dimensions === void 0 ? {} : { dimensions: config.dimensions },
|
|
1528
|
+
identity: createEmbeddingIdentity(provider, model, config),
|
|
1529
|
+
model,
|
|
1530
|
+
embedTexts: async (texts, options) => {
|
|
1531
|
+
const embeddings = [];
|
|
1532
|
+
let tokens = 0;
|
|
1533
|
+
for (const batch of chunkTexts(texts, EMBEDDING_BATCH_SIZE)) {
|
|
1534
|
+
options?.signal?.throwIfAborted();
|
|
1535
|
+
const result = await embedMany({
|
|
1536
|
+
...options?.signal === void 0 ? {} : { abortSignal: options.signal },
|
|
1537
|
+
model: embeddingModel,
|
|
1538
|
+
...providerOptions === void 0 ? {} : { providerOptions },
|
|
1539
|
+
values: [...batch]
|
|
1540
|
+
});
|
|
1541
|
+
embeddings.push(...result.embeddings);
|
|
1542
|
+
tokens += result.usage.tokens ?? 0;
|
|
1543
|
+
}
|
|
1544
|
+
return {
|
|
1545
|
+
embeddings,
|
|
1546
|
+
...tokens === 0 ? {} : { tokens }
|
|
1547
|
+
};
|
|
1548
|
+
}
|
|
1549
|
+
};
|
|
1550
|
+
}
|
|
1551
|
+
function createEmbeddingIdentity(provider, model, config) {
|
|
1552
|
+
return JSON.stringify({
|
|
1553
|
+
...config.baseURL === void 0 ? {} : { baseURL: config.baseURL },
|
|
1554
|
+
...config.dimensions === void 0 ? {} : { dimensions: config.dimensions },
|
|
1555
|
+
model,
|
|
1556
|
+
provider
|
|
1557
|
+
});
|
|
1558
|
+
}
|
|
1559
|
+
function* chunkTexts(texts, size) {
|
|
1560
|
+
for (let index = 0; index < texts.length; index += size) {
|
|
1561
|
+
yield texts.slice(index, index + size);
|
|
1562
|
+
}
|
|
1563
|
+
}
|
|
1564
|
+
async function embedQueryTextWithLocalConfig(value) {
|
|
1565
|
+
return await embedQueryText(value, await readWikiGraphEmbeddingConfig());
|
|
1566
|
+
}
|
|
1567
|
+
function createEmbeddingModel(provider, model, config) {
|
|
1568
|
+
switch (provider) {
|
|
1569
|
+
case "openai": {
|
|
1570
|
+
if (config.baseURL !== void 0) {
|
|
1571
|
+
throw new Error(
|
|
1572
|
+
"openai does not accept embeddings.baseURL. Use openai-compatible for third-party OpenAI-style embeddings APIs."
|
|
1573
|
+
);
|
|
1574
|
+
}
|
|
1575
|
+
return createOpenAI2({
|
|
1576
|
+
...config.apiKey === void 0 ? {} : { apiKey: config.apiKey },
|
|
1577
|
+
...config.name === void 0 ? {} : { name: config.name }
|
|
1578
|
+
}).embeddingModel(model);
|
|
1579
|
+
}
|
|
1580
|
+
case "openai-compatible": {
|
|
1581
|
+
if (config.baseURL === void 0) {
|
|
1582
|
+
throw new Error("openai-compatible requires embeddings.baseURL.");
|
|
1583
|
+
}
|
|
1584
|
+
return createOpenAICompatible2({
|
|
1585
|
+
baseURL: config.baseURL,
|
|
1586
|
+
name: config.name ?? createOpenAICompatibleName2(config.baseURL),
|
|
1587
|
+
...config.apiKey === void 0 ? {} : { apiKey: config.apiKey }
|
|
1588
|
+
}).embeddingModel(model);
|
|
1589
|
+
}
|
|
1590
|
+
}
|
|
1591
|
+
}
|
|
1592
|
+
function createEmbeddingProviderOptions(provider, config) {
|
|
1593
|
+
if (config.dimensions === void 0) {
|
|
1594
|
+
return void 0;
|
|
1595
|
+
}
|
|
1596
|
+
return {
|
|
1597
|
+
[getEmbeddingProviderOptionsName(provider, config)]: {
|
|
1598
|
+
dimensions: config.dimensions
|
|
1599
|
+
}
|
|
1600
|
+
};
|
|
1601
|
+
}
|
|
1602
|
+
function getEmbeddingProviderOptionsName(provider, _config) {
|
|
1603
|
+
if (provider === "openai") {
|
|
1604
|
+
return "openai";
|
|
1605
|
+
}
|
|
1606
|
+
return "openaiCompatible";
|
|
1607
|
+
}
|
|
1608
|
+
function requireEmbeddingProvider(provider) {
|
|
1609
|
+
if (provider !== void 0) {
|
|
1610
|
+
return provider;
|
|
1611
|
+
}
|
|
1612
|
+
throw new Error(
|
|
1613
|
+
"Missing embeddings configuration. Configure `wikg://local/config/embeddings` with provider and model."
|
|
1614
|
+
);
|
|
1615
|
+
}
|
|
1616
|
+
function requireEmbeddingModel(model) {
|
|
1617
|
+
if (model !== void 0) {
|
|
1618
|
+
return model;
|
|
1619
|
+
}
|
|
1620
|
+
throw new Error(
|
|
1621
|
+
"Missing embeddings.model. Configure `wikg://local/config/embeddings` before using Dense search."
|
|
1622
|
+
);
|
|
1623
|
+
}
|
|
1624
|
+
function parseEmbeddingProvider(value) {
|
|
1625
|
+
switch (value) {
|
|
1626
|
+
case "openai":
|
|
1627
|
+
case "openai-compatible":
|
|
1628
|
+
return value;
|
|
1629
|
+
default:
|
|
1630
|
+
throw new Error(
|
|
1631
|
+
`Invalid embeddings.provider: ${value}. Expected openai or openai-compatible.`
|
|
1632
|
+
);
|
|
1633
|
+
}
|
|
1634
|
+
}
|
|
1635
|
+
function createOpenAICompatibleName2(baseURL) {
|
|
1636
|
+
try {
|
|
1637
|
+
return new URL(baseURL).hostname;
|
|
1638
|
+
} catch {
|
|
1639
|
+
return "openai-compatible";
|
|
1640
|
+
}
|
|
1641
|
+
}
|
|
1642
|
+
|
|
1643
|
+
// src/query-runtime.ts
|
|
1644
|
+
async function createConfiguredEmbeddingProvider() {
|
|
1645
|
+
const config = await readWikiGraphEmbeddingConfig();
|
|
1646
|
+
if (config.provider === void 0 || config.model === void 0 || config.provider === "openai-compatible" && config.baseURL === void 0 || config.provider === "openai" && config.baseURL !== void 0) {
|
|
1647
|
+
return void 0;
|
|
1648
|
+
}
|
|
1649
|
+
return buildSearchIndexEmbeddingProvider(config);
|
|
1650
|
+
}
|
|
1651
|
+
async function withConfiguredWikimediaResolver(objectUri, operation) {
|
|
1652
|
+
if (!objectUri.endsWith("/wikipage")) return await operation({});
|
|
1653
|
+
const config = await loadWikiGraphRuntimeConfig();
|
|
1654
|
+
const resolver = await openWikimediaResolver(
|
|
1655
|
+
config.wikimedia === void 0 ? { kind: "local" } : {
|
|
1656
|
+
endpoint: config.wikimedia.endpoint,
|
|
1657
|
+
kind: "remote",
|
|
1658
|
+
token: config.wikimedia.token
|
|
1659
|
+
}
|
|
1660
|
+
);
|
|
1661
|
+
try {
|
|
1662
|
+
return await operation({ wikimediaResolver: resolver });
|
|
1663
|
+
} finally {
|
|
1664
|
+
await resolver.close();
|
|
1665
|
+
}
|
|
1666
|
+
}
|
|
1667
|
+
|
|
1668
|
+
// src/archive/inspect.ts
|
|
1669
|
+
import {
|
|
1670
|
+
isArchiveSearchIndexCurrent,
|
|
1671
|
+
listChapters
|
|
1672
|
+
} from "wiki-graph-core";
|
|
1673
|
+
|
|
1674
|
+
// src/planning.ts
|
|
1675
|
+
var DEFAULT_GENERATION_JOB_CONCURRENCY = 3;
|
|
1676
|
+
var DEFAULT_GENERATION_REQUEST_CONCURRENCY = 6;
|
|
1677
|
+
function planGenerationTask(task, words, chapters, concurrent, model) {
|
|
1678
|
+
const profile = getGenerationPlanningProfile(task);
|
|
1679
|
+
const calls = Math.max(chapters, Math.ceil(words / profile.wordsPerCall));
|
|
1680
|
+
const effectiveConcurrency = Math.max(
|
|
1681
|
+
1,
|
|
1682
|
+
task === "reading-graph" ? 1 : concurrent.request
|
|
1683
|
+
);
|
|
1684
|
+
const callBatches = Math.ceil(calls / effectiveConcurrency);
|
|
1685
|
+
const wordSeconds = words * profile.secondsPerWord;
|
|
1686
|
+
return {
|
|
1687
|
+
model,
|
|
1688
|
+
timeSeconds: {
|
|
1689
|
+
max: Math.ceil(callBatches * profile.maxSecondsPerCall + wordSeconds),
|
|
1690
|
+
min: Math.ceil(callBatches * profile.minSecondsPerCall + wordSeconds)
|
|
1691
|
+
},
|
|
1692
|
+
tokens: {
|
|
1693
|
+
cacheableInput: Math.ceil(words * profile.cacheableInputTokenPerWord),
|
|
1694
|
+
input: Math.ceil(words * profile.inputTokenPerWord),
|
|
1695
|
+
output: Math.ceil(words * profile.outputTokenPerWord)
|
|
1696
|
+
}
|
|
1697
|
+
};
|
|
1698
|
+
}
|
|
1699
|
+
function createGenerationPerformanceHints(input) {
|
|
1700
|
+
if (!input.hasJobWork) {
|
|
1701
|
+
return [];
|
|
1702
|
+
}
|
|
1703
|
+
const hints = [];
|
|
1704
|
+
const recommendedRequest = input.concurrent.request < 4 ? DEFAULT_GENERATION_REQUEST_CONCURRENCY : input.concurrent.request < 8 ? 8 : void 0;
|
|
1705
|
+
if (input.hasRequestWork && recommendedRequest !== void 0) {
|
|
1706
|
+
hints.push({
|
|
1707
|
+
configKey: "request",
|
|
1708
|
+
configValue: recommendedRequest,
|
|
1709
|
+
current: input.concurrent.request,
|
|
1710
|
+
kind: "request",
|
|
1711
|
+
message: "LLM request concurrency can often be higher. Use at least 4; 6-8 is usually faster when the provider allows it.",
|
|
1712
|
+
recommended: recommendedRequest
|
|
1713
|
+
});
|
|
1714
|
+
}
|
|
1715
|
+
if (input.concurrent.job < 4 && input.chapters > 1) {
|
|
1716
|
+
hints.push({
|
|
1717
|
+
configKey: "job",
|
|
1718
|
+
configValue: 4,
|
|
1719
|
+
current: input.concurrent.job,
|
|
1720
|
+
kind: "job",
|
|
1721
|
+
message: "Multiple chapters need work and job concurrency is below 4. Raising it lets chapter jobs run in parallel.",
|
|
1722
|
+
recommended: 4
|
|
1723
|
+
});
|
|
1724
|
+
}
|
|
1725
|
+
return hints;
|
|
1726
|
+
}
|
|
1727
|
+
function formatGenerationPlanningModel(llm) {
|
|
1728
|
+
if (llm?.model === void 0) {
|
|
1729
|
+
return "not configured";
|
|
1730
|
+
}
|
|
1731
|
+
return llm.provider === void 0 ? llm.model : `${llm.provider}/${llm.model}`;
|
|
1732
|
+
}
|
|
1733
|
+
function formatGenerationPlanningDuration(seconds) {
|
|
1734
|
+
if (seconds < 60) {
|
|
1735
|
+
return `${seconds}s`;
|
|
1736
|
+
}
|
|
1737
|
+
const minutes = Math.round(seconds / 60);
|
|
1738
|
+
if (minutes < 60) {
|
|
1739
|
+
return `${minutes}m`;
|
|
1740
|
+
}
|
|
1741
|
+
return `${Math.round(minutes / 60)}h`;
|
|
1742
|
+
}
|
|
1743
|
+
function getGenerationPlanningProfile(task) {
|
|
1744
|
+
switch (task) {
|
|
1745
|
+
case "index-fts":
|
|
1746
|
+
return {
|
|
1747
|
+
cacheableInputTokenPerWord: 0,
|
|
1748
|
+
inputTokenPerWord: 0,
|
|
1749
|
+
maxSecondsPerCall: 1,
|
|
1750
|
+
minSecondsPerCall: 0,
|
|
1751
|
+
outputTokenPerWord: 0,
|
|
1752
|
+
secondsPerWord: 5e-4,
|
|
1753
|
+
wordsPerCall: 1e5
|
|
1754
|
+
};
|
|
1755
|
+
case "index-embedding-source":
|
|
1756
|
+
case "index-embedding-summary":
|
|
1757
|
+
return {
|
|
1758
|
+
cacheableInputTokenPerWord: 0,
|
|
1759
|
+
inputTokenPerWord: 0,
|
|
1760
|
+
maxSecondsPerCall: 10,
|
|
1761
|
+
minSecondsPerCall: 1,
|
|
1762
|
+
outputTokenPerWord: 0,
|
|
1763
|
+
secondsPerWord: 2e-3,
|
|
1764
|
+
wordsPerCall: 1e4
|
|
1765
|
+
};
|
|
1766
|
+
case "knowledge-graph":
|
|
1767
|
+
return {
|
|
1768
|
+
cacheableInputTokenPerWord: 60,
|
|
1769
|
+
inputTokenPerWord: 80,
|
|
1770
|
+
maxSecondsPerCall: 35,
|
|
1771
|
+
minSecondsPerCall: 15,
|
|
1772
|
+
outputTokenPerWord: 25,
|
|
1773
|
+
secondsPerWord: 0.02,
|
|
1774
|
+
wordsPerCall: 35
|
|
1775
|
+
};
|
|
1776
|
+
case "reading-graph":
|
|
1777
|
+
return {
|
|
1778
|
+
cacheableInputTokenPerWord: 20,
|
|
1779
|
+
inputTokenPerWord: 25,
|
|
1780
|
+
maxSecondsPerCall: 45,
|
|
1781
|
+
minSecondsPerCall: 15,
|
|
1782
|
+
outputTokenPerWord: 4,
|
|
1783
|
+
secondsPerWord: 0.01,
|
|
1784
|
+
wordsPerCall: 160
|
|
1785
|
+
};
|
|
1786
|
+
case "reading-summary":
|
|
1787
|
+
return {
|
|
1788
|
+
cacheableInputTokenPerWord: 45,
|
|
1789
|
+
inputTokenPerWord: 55,
|
|
1790
|
+
maxSecondsPerCall: 25,
|
|
1791
|
+
minSecondsPerCall: 10,
|
|
1792
|
+
outputTokenPerWord: 4,
|
|
1793
|
+
secondsPerWord: 0.01,
|
|
1794
|
+
wordsPerCall: 110
|
|
1795
|
+
};
|
|
1796
|
+
}
|
|
1797
|
+
}
|
|
1798
|
+
|
|
1799
|
+
// src/archive/inspect.ts
|
|
1800
|
+
async function createWikiGraphArchiveInspectReport(document, options) {
|
|
1801
|
+
const { archiveUri } = options;
|
|
1802
|
+
const [
|
|
1803
|
+
chapters,
|
|
1804
|
+
summaryWords,
|
|
1805
|
+
indexCacheCurrent,
|
|
1806
|
+
config,
|
|
1807
|
+
ftsArtifactCoverage,
|
|
1808
|
+
sourceEmbeddingArtifactCoverage,
|
|
1809
|
+
summaryEmbeddingArtifactCoverage
|
|
1810
|
+
] = await Promise.all([
|
|
1811
|
+
readInspectChapters(document, options.chapterId),
|
|
1812
|
+
readSummaryWords(document, options.chapterId),
|
|
1813
|
+
isArchiveSearchIndexCurrent(document),
|
|
1814
|
+
loadWikiGraphRuntimeConfig(),
|
|
1815
|
+
readIndexArtifactCoverage(document, "fts", options.chapterId),
|
|
1816
|
+
readIndexArtifactCoverage(document, "embedding-source", options.chapterId),
|
|
1817
|
+
readIndexArtifactCoverage(document, "embedding-summary", options.chapterId)
|
|
1818
|
+
]);
|
|
1819
|
+
const selectedChapter = options.chapterId === void 0 ? void 0 : chapters[0];
|
|
1820
|
+
const scopeUri = selectedChapter === void 0 ? archiveUri : formatInspectLocatedChapterUri(archiveUri, selectedChapter);
|
|
1821
|
+
const concurrent = {
|
|
1822
|
+
job: config.concurrent?.job ?? DEFAULT_GENERATION_JOB_CONCURRENCY,
|
|
1823
|
+
request: config.concurrent?.request ?? DEFAULT_GENERATION_REQUEST_CONCURRENCY
|
|
1824
|
+
};
|
|
1825
|
+
const planningModel = formatGenerationPlanningModel(config.llm);
|
|
1826
|
+
const contentChapters = chapters.filter(
|
|
1827
|
+
(chapter) => chapter.stage !== "planned"
|
|
1828
|
+
);
|
|
1829
|
+
const sourceWords = sumWords(contentChapters);
|
|
1830
|
+
const readingGraphCovered = contentChapters.filter(
|
|
1831
|
+
(chapter) => chapter.readingGraphReady
|
|
1832
|
+
);
|
|
1833
|
+
const knowledgeGraphCovered = contentChapters.filter(
|
|
1834
|
+
(chapter) => chapter.knowledgeGraphReady
|
|
1835
|
+
);
|
|
1836
|
+
const summaryCovered = contentChapters.filter(
|
|
1837
|
+
(chapter) => chapter.summaryReady
|
|
1838
|
+
);
|
|
1839
|
+
const ftsArtifactCovered = filterChaptersByCurrentIndexArtifact(
|
|
1840
|
+
contentChapters,
|
|
1841
|
+
ftsArtifactCoverage
|
|
1842
|
+
);
|
|
1843
|
+
const sourceEmbeddingArtifactCovered = filterChaptersByCurrentIndexArtifact(
|
|
1844
|
+
contentChapters,
|
|
1845
|
+
sourceEmbeddingArtifactCoverage
|
|
1846
|
+
);
|
|
1847
|
+
const summaryEmbeddingArtifactCovered = filterChaptersByCurrentIndexArtifact(
|
|
1848
|
+
contentChapters,
|
|
1849
|
+
summaryEmbeddingArtifactCoverage
|
|
1850
|
+
);
|
|
1851
|
+
const queryBlockedContentChapters = contentChapters.filter(
|
|
1852
|
+
(chapter) => !ftsArtifactCovered.includes(chapter) && !sourceEmbeddingArtifactCovered.includes(chapter)
|
|
1853
|
+
);
|
|
1854
|
+
const queryBlockedChapters = queryBlockedContentChapters.map(
|
|
1855
|
+
(chapter) => createInspectChapterReference(archiveUri, chapter)
|
|
1856
|
+
);
|
|
1857
|
+
const queryReady = queryBlockedChapters.length === 0;
|
|
1858
|
+
const improvements = createInspectImprovements({
|
|
1859
|
+
concurrent,
|
|
1860
|
+
contentChapters,
|
|
1861
|
+
indexCacheCurrent,
|
|
1862
|
+
planningModel,
|
|
1863
|
+
queryBlockedChapters: queryBlockedContentChapters,
|
|
1864
|
+
summaryCovered,
|
|
1865
|
+
knowledgeGraphCovered,
|
|
1866
|
+
readingGraphCovered
|
|
1867
|
+
});
|
|
1868
|
+
const performanceHints = createGenerationPerformanceHints({
|
|
1869
|
+
chapters: Math.max(
|
|
1870
|
+
0,
|
|
1871
|
+
...improvements.map((improvement) => improvement.missingChapters ?? 0)
|
|
1872
|
+
),
|
|
1873
|
+
concurrent,
|
|
1874
|
+
hasJobWork: improvements.some(
|
|
1875
|
+
(improvement) => improvement.planning !== void 0
|
|
1876
|
+
),
|
|
1877
|
+
hasRequestWork: improvements.some(
|
|
1878
|
+
(improvement) => improvement.planning !== void 0
|
|
1879
|
+
)
|
|
1880
|
+
});
|
|
1881
|
+
return {
|
|
1882
|
+
archiveUri,
|
|
1883
|
+
uri: scopeUri,
|
|
1884
|
+
scope: options.chapterId === void 0 ? { type: "archive" } : { chapterId: options.chapterId, type: "chapter" },
|
|
1885
|
+
content: {
|
|
1886
|
+
chapters: {
|
|
1887
|
+
content: contentChapters.length,
|
|
1888
|
+
planned: chapters.length - contentChapters.length,
|
|
1889
|
+
total: chapters.length
|
|
1890
|
+
},
|
|
1891
|
+
sourceWords,
|
|
1892
|
+
summaryWords
|
|
1893
|
+
},
|
|
1894
|
+
localIndexCache: {
|
|
1895
|
+
current: indexCacheCurrent,
|
|
1896
|
+
...!queryReady ? {
|
|
1897
|
+
blockedBy: "missing-search-artifacts",
|
|
1898
|
+
impact: "The cache cannot be built until every content chapter has a current FTS or source embedding artifact."
|
|
1899
|
+
} : indexCacheCurrent ? {} : {
|
|
1900
|
+
impact: "The next archive query may need to sync index cache first.",
|
|
1901
|
+
resource: "local CPU/disk time only; no provider calls."
|
|
1902
|
+
},
|
|
1903
|
+
status: !queryReady ? "blocked" : indexCacheCurrent ? "current" : "missing-or-outdated",
|
|
1904
|
+
syncAvailable: queryReady && !indexCacheCurrent
|
|
1905
|
+
},
|
|
1906
|
+
coverage: {
|
|
1907
|
+
ftsIndexArtifact: createInspectCoverage(
|
|
1908
|
+
ftsArtifactCovered,
|
|
1909
|
+
contentChapters
|
|
1910
|
+
),
|
|
1911
|
+
knowledgeGraph: createInspectCoverage(
|
|
1912
|
+
knowledgeGraphCovered,
|
|
1913
|
+
contentChapters
|
|
1914
|
+
),
|
|
1915
|
+
readingGraph: createInspectCoverage(readingGraphCovered, contentChapters),
|
|
1916
|
+
summary: createInspectCoverage(summaryCovered, contentChapters),
|
|
1917
|
+
sourceEmbeddingIndexArtifact: createInspectCoverage(
|
|
1918
|
+
sourceEmbeddingArtifactCovered,
|
|
1919
|
+
contentChapters
|
|
1920
|
+
),
|
|
1921
|
+
summaryEmbeddingIndexArtifact: createInspectCoverage(
|
|
1922
|
+
summaryEmbeddingArtifactCovered,
|
|
1923
|
+
contentChapters
|
|
1924
|
+
)
|
|
1925
|
+
},
|
|
1926
|
+
query: {
|
|
1927
|
+
ready: queryReady
|
|
1928
|
+
},
|
|
1929
|
+
searchArtifacts: {
|
|
1930
|
+
blockedChapters: queryBlockedChapters,
|
|
1931
|
+
...!queryReady ? {
|
|
1932
|
+
resource: "local CPU/disk time only; no provider calls."
|
|
1933
|
+
} : {},
|
|
1934
|
+
ready: queryReady,
|
|
1935
|
+
status: queryReady ? "ready" : "incomplete"
|
|
1936
|
+
},
|
|
1937
|
+
retrievalGuidance: formatRetrievalGuidance({
|
|
1938
|
+
indexCacheCurrent,
|
|
1939
|
+
knowledgeGraphCovered,
|
|
1940
|
+
readingGraphCovered,
|
|
1941
|
+
contentChapters,
|
|
1942
|
+
queryReady,
|
|
1943
|
+
sourceWords
|
|
1944
|
+
}),
|
|
1945
|
+
improvements,
|
|
1946
|
+
performanceHints,
|
|
1947
|
+
summaryCoverageNote: "Summary coverage means a current summary artifact exists for the chapter; it does not guarantee that every source topic is represented in the summary text."
|
|
1948
|
+
};
|
|
1949
|
+
}
|
|
1950
|
+
function createInspectChapterReference(archiveUri, chapter) {
|
|
1951
|
+
return {
|
|
1952
|
+
chapterId: chapter.chapterId,
|
|
1953
|
+
locatedUri: formatInspectLocatedChapterUri(archiveUri, chapter),
|
|
1954
|
+
title: chapter.title,
|
|
1955
|
+
uri: chapter.uri
|
|
1956
|
+
};
|
|
1957
|
+
}
|
|
1958
|
+
function formatInspectLocatedChapterUri(archiveUri, chapter) {
|
|
1959
|
+
return `${archiveUri.replace(/\/+$/u, "")}/${chapter.uri.replace(/^wikg:\/\//u, "")}`;
|
|
1960
|
+
}
|
|
1961
|
+
async function readInspectChapters(document, chapterId) {
|
|
1962
|
+
const chapters = chapterId === void 0 ? await listChapters(document) : (await listChapters(document)).filter(
|
|
1963
|
+
(chapter) => chapter.chapterId === chapterId
|
|
1964
|
+
);
|
|
1965
|
+
if (chapterId !== void 0 && chapters.length === 0) {
|
|
1966
|
+
throw new Error(`Chapter ${chapterId} does not exist.`);
|
|
1967
|
+
}
|
|
1968
|
+
return await Promise.all(
|
|
1969
|
+
chapters.map(async (chapter) => {
|
|
1970
|
+
const serial = await document.serials.getById(chapter.chapterId);
|
|
1971
|
+
return {
|
|
1972
|
+
...chapter,
|
|
1973
|
+
knowledgeGraphReady: serial?.knowledgeGraphReady === true,
|
|
1974
|
+
readingGraphReady: serial?.topologyReady === true,
|
|
1975
|
+
summaryReady: chapter.stage === "summarized"
|
|
1976
|
+
};
|
|
1977
|
+
})
|
|
1978
|
+
);
|
|
1979
|
+
}
|
|
1980
|
+
async function readSummaryWords(document, chapterId) {
|
|
1981
|
+
return await document.readDatabase(
|
|
1982
|
+
async (database) => await database.queryOne(
|
|
1983
|
+
`
|
|
1984
|
+
SELECT COALESCE(SUM(words_count), 0) AS words
|
|
1985
|
+
FROM text_sentence_records
|
|
1986
|
+
WHERE kind = 2
|
|
1987
|
+
${chapterId === void 0 ? "" : "AND chapter_id = ?"}
|
|
1988
|
+
`,
|
|
1989
|
+
chapterId === void 0 ? void 0 : [chapterId],
|
|
1990
|
+
(row) => Number(row.words)
|
|
1991
|
+
) ?? 0
|
|
1992
|
+
);
|
|
1993
|
+
}
|
|
1994
|
+
async function readIndexArtifactCoverage(document, kind, chapterId) {
|
|
1995
|
+
const coverage = await document.indexArtifacts.listCoverage(kind);
|
|
1996
|
+
return chapterId === void 0 ? coverage : coverage.filter((item) => item.serialId === chapterId);
|
|
1997
|
+
}
|
|
1998
|
+
function filterChaptersByCurrentIndexArtifact(chapters, coverage) {
|
|
1999
|
+
const currentChapterIds = new Set(
|
|
2000
|
+
coverage.filter((item) => item.current).map((item) => item.serialId)
|
|
2001
|
+
);
|
|
2002
|
+
return chapters.filter((chapter) => currentChapterIds.has(chapter.chapterId));
|
|
2003
|
+
}
|
|
2004
|
+
function createInspectCoverage(covered, total) {
|
|
2005
|
+
const coveredWords = sumWords(covered);
|
|
2006
|
+
const totalWords = sumWords(total);
|
|
2007
|
+
return {
|
|
2008
|
+
coveredChapters: covered.length,
|
|
2009
|
+
coveredWords,
|
|
2010
|
+
percent: formatPercent(coveredWords, totalWords),
|
|
2011
|
+
totalChapters: total.length,
|
|
2012
|
+
totalWords
|
|
2013
|
+
};
|
|
2014
|
+
}
|
|
2015
|
+
function formatRetrievalGuidance(input) {
|
|
2016
|
+
if (input.sourceWords === 0 || input.contentChapters.length === 0) {
|
|
2017
|
+
return [
|
|
2018
|
+
"Source content: empty.",
|
|
2019
|
+
"Add source text before using query coverage or graph-based retrieval."
|
|
2020
|
+
];
|
|
2021
|
+
}
|
|
2022
|
+
const lines = [
|
|
2023
|
+
input.queryReady ? `Query: available; local index cache is ${input.indexCacheCurrent ? "current" : "missing or outdated and can be synced from artifacts"}.` : "Query: unavailable until every content chapter has a current FTS or source embedding artifact."
|
|
2024
|
+
];
|
|
2025
|
+
lines.push(
|
|
2026
|
+
formatObjectSearchGuidance(
|
|
2027
|
+
"Reading Graph object retrieval",
|
|
2028
|
+
input.readingGraphCovered,
|
|
2029
|
+
input.contentChapters
|
|
2030
|
+
)
|
|
2031
|
+
);
|
|
2032
|
+
lines.push(
|
|
2033
|
+
formatObjectSearchGuidance(
|
|
2034
|
+
"Entity/triple retrieval",
|
|
2035
|
+
input.knowledgeGraphCovered,
|
|
2036
|
+
input.contentChapters
|
|
2037
|
+
)
|
|
2038
|
+
);
|
|
2039
|
+
return lines;
|
|
2040
|
+
}
|
|
2041
|
+
function formatObjectSearchGuidance(label, covered, total) {
|
|
2042
|
+
const coveredWords = sumWords(covered);
|
|
2043
|
+
const totalWords = sumWords(total);
|
|
2044
|
+
const ratio = totalWords === 0 ? 0 : coveredWords / totalWords;
|
|
2045
|
+
const coverage = formatPercent(coveredWords, totalWords);
|
|
2046
|
+
if (ratio >= 1) {
|
|
2047
|
+
return `${label}: covers all source content; it can represent the full scope for object retrieval.`;
|
|
2048
|
+
}
|
|
2049
|
+
if (ratio >= 0.9) {
|
|
2050
|
+
return `${label}: covers ${coverage}; use it as the main path, but source --query is needed for uncovered content.`;
|
|
2051
|
+
}
|
|
2052
|
+
if (ratio >= 0.5) {
|
|
2053
|
+
return `${label}: covers ${coverage}; use it as leads, not as a full-scope substitute for source --query.`;
|
|
2054
|
+
}
|
|
2055
|
+
return `${label}: covers ${coverage}; build missing graph coverage before relying on object retrieval.`;
|
|
2056
|
+
}
|
|
2057
|
+
function createInspectImprovements(input) {
|
|
2058
|
+
const improvements = [];
|
|
2059
|
+
if (input.contentChapters.length === 0) {
|
|
2060
|
+
improvements.push({
|
|
2061
|
+
kind: "add-source",
|
|
2062
|
+
recommendation: "No source content is available yet; add or import source text before graph or summary generation.",
|
|
2063
|
+
title: "Add source content"
|
|
2064
|
+
});
|
|
2065
|
+
return improvements;
|
|
2066
|
+
}
|
|
2067
|
+
if (input.queryBlockedChapters.length > 0) {
|
|
2068
|
+
improvements.push({
|
|
2069
|
+
kind: "build",
|
|
2070
|
+
missingChapters: input.queryBlockedChapters.length,
|
|
2071
|
+
missingWords: sumWords(input.queryBlockedChapters),
|
|
2072
|
+
recommendation: "Build local FTS artifacts to make this scope queryable. This does not call a provider.",
|
|
2073
|
+
task: "index-fts",
|
|
2074
|
+
title: "Enable query with local FTS"
|
|
2075
|
+
});
|
|
2076
|
+
} else if (!input.indexCacheCurrent) {
|
|
2077
|
+
improvements.push({
|
|
2078
|
+
kind: "sync-index",
|
|
2079
|
+
recommendation: "Sync the index cache from chapter index artifacts so query starts without a lazy cache rebuild.",
|
|
2080
|
+
title: "Sync index cache"
|
|
2081
|
+
});
|
|
2082
|
+
}
|
|
2083
|
+
improvements.push(
|
|
2084
|
+
...createGraphImprovement({
|
|
2085
|
+
concurrent: input.concurrent,
|
|
2086
|
+
covered: input.readingGraphCovered,
|
|
2087
|
+
planningModel: input.planningModel,
|
|
2088
|
+
task: "reading-graph",
|
|
2089
|
+
title: "Complete Reading Graph coverage",
|
|
2090
|
+
total: input.contentChapters
|
|
2091
|
+
})
|
|
2092
|
+
);
|
|
2093
|
+
improvements.push(
|
|
2094
|
+
...createGraphImprovement({
|
|
2095
|
+
concurrent: input.concurrent,
|
|
2096
|
+
covered: input.knowledgeGraphCovered,
|
|
2097
|
+
planningModel: input.planningModel,
|
|
2098
|
+
task: "knowledge-graph",
|
|
2099
|
+
title: "Complete Knowledge Graph coverage",
|
|
2100
|
+
total: input.contentChapters
|
|
2101
|
+
})
|
|
2102
|
+
);
|
|
2103
|
+
improvements.push(
|
|
2104
|
+
...createGraphImprovement({
|
|
2105
|
+
concurrent: input.concurrent,
|
|
2106
|
+
covered: input.summaryCovered,
|
|
2107
|
+
planningModel: input.planningModel,
|
|
2108
|
+
task: "reading-summary",
|
|
2109
|
+
title: "Complete Summary coverage",
|
|
2110
|
+
total: input.contentChapters
|
|
2111
|
+
})
|
|
2112
|
+
);
|
|
2113
|
+
return improvements;
|
|
2114
|
+
}
|
|
2115
|
+
function createGraphImprovement(input) {
|
|
2116
|
+
const coveredIds = new Set(input.covered.map((chapter) => chapter.chapterId));
|
|
2117
|
+
const missing = input.total.filter(
|
|
2118
|
+
(chapter) => !coveredIds.has(chapter.chapterId)
|
|
2119
|
+
);
|
|
2120
|
+
if (missing.length === 0) {
|
|
2121
|
+
return [];
|
|
2122
|
+
}
|
|
2123
|
+
const missingWords = sumWords(missing);
|
|
2124
|
+
return [
|
|
2125
|
+
{
|
|
2126
|
+
kind: "build",
|
|
2127
|
+
missingChapters: missing.length,
|
|
2128
|
+
missingWords,
|
|
2129
|
+
planning: planGenerationTask(
|
|
2130
|
+
input.task,
|
|
2131
|
+
missingWords,
|
|
2132
|
+
missing.length,
|
|
2133
|
+
input.concurrent,
|
|
2134
|
+
input.planningModel
|
|
2135
|
+
),
|
|
2136
|
+
recommendation: formatImprovementRecommendation(
|
|
2137
|
+
input.covered,
|
|
2138
|
+
input.total,
|
|
2139
|
+
missingWords
|
|
2140
|
+
),
|
|
2141
|
+
task: input.task,
|
|
2142
|
+
title: input.title
|
|
2143
|
+
}
|
|
2144
|
+
];
|
|
2145
|
+
}
|
|
2146
|
+
function formatImprovementRecommendation(covered, total, missingWords) {
|
|
2147
|
+
const totalWords = sumWords(total);
|
|
2148
|
+
const coveredWords = sumWords(covered);
|
|
2149
|
+
if (totalWords === 0) {
|
|
2150
|
+
return "No source content is available.";
|
|
2151
|
+
}
|
|
2152
|
+
if (total.length === 1 || coveredWords / totalWords >= 0.9 || missingWords <= 1e3) {
|
|
2153
|
+
return "Queue the full scope to finish the remaining gap.";
|
|
2154
|
+
}
|
|
2155
|
+
return "Queue selected chapters first if only part of the scope matters.";
|
|
2156
|
+
}
|
|
2157
|
+
function sumWords(chapters) {
|
|
2158
|
+
return chapters.reduce((total, chapter) => total + chapter.words, 0);
|
|
2159
|
+
}
|
|
2160
|
+
function formatPercent(numerator, denominator) {
|
|
2161
|
+
if (denominator === 0) {
|
|
2162
|
+
return "n/a";
|
|
2163
|
+
}
|
|
2164
|
+
const percent = numerator / denominator * 100;
|
|
2165
|
+
return `${Number.isInteger(percent) ? percent.toFixed(0) : percent.toFixed(1)}%`;
|
|
2166
|
+
}
|
|
2167
|
+
|
|
2168
|
+
// src/archive/archive.ts
|
|
2169
|
+
var WikiGraphArchiveExistsError = class extends Error {
|
|
2170
|
+
code = "WIKI_GRAPH_ARCHIVE_EXISTS";
|
|
2171
|
+
path;
|
|
2172
|
+
constructor(path) {
|
|
2173
|
+
super(`Archive already exists: ${path}`);
|
|
2174
|
+
this.name = "WikiGraphArchiveExistsError";
|
|
2175
|
+
this.path = path;
|
|
2176
|
+
}
|
|
2177
|
+
};
|
|
2178
|
+
var WikiGraphArchiveManager = class {
|
|
2179
|
+
#runtime;
|
|
2180
|
+
constructor(runtime) {
|
|
2181
|
+
this.#runtime = runtime;
|
|
2182
|
+
}
|
|
2183
|
+
async create(options) {
|
|
2184
|
+
return await this.#runtime.run(async () => {
|
|
2185
|
+
const path = resolveWikiGraphRuntimePath(options.path);
|
|
2186
|
+
if (options.replace !== true && await nodePathExists(path)) {
|
|
2187
|
+
throw new WikiGraphArchiveExistsError(path);
|
|
2188
|
+
}
|
|
2189
|
+
const outputPath = join3(
|
|
2190
|
+
dirname2(path),
|
|
2191
|
+
`.${basename2(path)}.${randomUUID2()}.tmp.wikg`
|
|
2192
|
+
);
|
|
2193
|
+
try {
|
|
2194
|
+
if (options.importPath === void 0) {
|
|
2195
|
+
await createEmptyArchiveFile(outputPath);
|
|
2196
|
+
} else {
|
|
2197
|
+
await new WikiGraphConversionManager(this.#runtime).convert({
|
|
2198
|
+
input: { format: "epub", path: options.importPath },
|
|
2199
|
+
...options.onProgress === void 0 ? {} : { onProgress: options.onProgress },
|
|
2200
|
+
output: { format: "wikg", path: outputPath },
|
|
2201
|
+
targetStage: "sourced"
|
|
2202
|
+
});
|
|
2203
|
+
}
|
|
2204
|
+
if (options.replace === true) {
|
|
2205
|
+
await rename2(outputPath, path);
|
|
2206
|
+
} else {
|
|
2207
|
+
try {
|
|
2208
|
+
await link(outputPath, path);
|
|
2209
|
+
} catch (error) {
|
|
2210
|
+
if (isNodeEEXISTError(error)) {
|
|
2211
|
+
throw new WikiGraphArchiveExistsError(path);
|
|
2212
|
+
}
|
|
2213
|
+
throw error;
|
|
2214
|
+
}
|
|
2215
|
+
}
|
|
2216
|
+
return { locatedUri: formatLocatedWikiGraphUri(path), path };
|
|
2217
|
+
} finally {
|
|
2218
|
+
await rm3(outputPath, { force: true, recursive: true });
|
|
2219
|
+
}
|
|
2220
|
+
}, options.signal);
|
|
2221
|
+
}
|
|
2222
|
+
async open(uriOrPath, options = {}) {
|
|
2223
|
+
const location = await this.#runtime.run(
|
|
2224
|
+
async () => await resolveWikiGraphArchiveLocation(uriOrPath),
|
|
2225
|
+
options.signal
|
|
2226
|
+
);
|
|
2227
|
+
return new WikiGraphArchiveHandle(this.#runtime, location);
|
|
2228
|
+
}
|
|
2229
|
+
};
|
|
2230
|
+
var WikiGraphArchiveHandle = class {
|
|
2231
|
+
#runtime;
|
|
2232
|
+
#location;
|
|
2233
|
+
constructor(runtime, location) {
|
|
2234
|
+
this.#runtime = runtime;
|
|
2235
|
+
this.#location = location;
|
|
2236
|
+
}
|
|
2237
|
+
get archiveKey() {
|
|
2238
|
+
return this.#location.archiveKey;
|
|
2239
|
+
}
|
|
2240
|
+
get indexScope() {
|
|
2241
|
+
return this.#location.indexScope;
|
|
2242
|
+
}
|
|
2243
|
+
get locatedUri() {
|
|
2244
|
+
return this.#location.locatedUri;
|
|
2245
|
+
}
|
|
2246
|
+
get objectUri() {
|
|
2247
|
+
return parseLocatedWikiGraphUri(this.#location.locatedUri).objectUri ?? "wikg://";
|
|
2248
|
+
}
|
|
2249
|
+
get path() {
|
|
2250
|
+
return this.#location.archivePath;
|
|
2251
|
+
}
|
|
2252
|
+
async inspect(options = {}) {
|
|
2253
|
+
return await this.#readDocument(
|
|
2254
|
+
async (document) => await createWikiGraphArchiveInspectReport(document, {
|
|
2255
|
+
archiveUri: this.#location.publicArchiveUri ?? this.locatedUri,
|
|
2256
|
+
...options.chapterId === void 0 ? {} : { chapterId: options.chapterId }
|
|
2257
|
+
}),
|
|
2258
|
+
options
|
|
2259
|
+
);
|
|
2260
|
+
}
|
|
2261
|
+
async search(query, options = {}) {
|
|
2262
|
+
const embeddingProvider = await this.#runtime.run(
|
|
2263
|
+
createConfiguredEmbeddingProvider,
|
|
2264
|
+
options.signal
|
|
2265
|
+
);
|
|
2266
|
+
return await this.#writeDocument(
|
|
2267
|
+
async (document) => {
|
|
2268
|
+
const chapters = await this.#resolveQueryChapters(document, options);
|
|
2269
|
+
await ensureArchiveSearchIndex(document, {
|
|
2270
|
+
...chapters === void 0 ? {} : { chapters },
|
|
2271
|
+
...embeddingProvider === void 0 ? {} : { embeddingProvider }
|
|
2272
|
+
});
|
|
2273
|
+
return await findArchiveObjects(document, query, {
|
|
2274
|
+
...withoutOperationAndScope(options),
|
|
2275
|
+
archiveKey: this.archiveKey,
|
|
2276
|
+
...chapters === void 0 ? {} : { chapters },
|
|
2277
|
+
...embeddingProvider === void 0 ? {} : { embeddingProvider }
|
|
2278
|
+
});
|
|
2279
|
+
},
|
|
2280
|
+
{
|
|
2281
|
+
searchIndexWritebackPolicy: "cache",
|
|
2282
|
+
...options.signal === void 0 ? {} : { signal: options.signal }
|
|
2283
|
+
}
|
|
2284
|
+
);
|
|
2285
|
+
}
|
|
2286
|
+
async list(options = {}) {
|
|
2287
|
+
return await this.#readDocument(async (document) => {
|
|
2288
|
+
if (isSourceLocatorScopeUri(this.objectUri)) {
|
|
2289
|
+
return await listArchiveSourceLocators(document, this.objectUri, {
|
|
2290
|
+
...options.cursor === void 0 ? {} : { cursor: options.cursor },
|
|
2291
|
+
...options.limit === void 0 ? {} : { limit: options.limit }
|
|
2292
|
+
});
|
|
2293
|
+
}
|
|
2294
|
+
const chapters = await this.#resolveScope(document, options);
|
|
2295
|
+
return await listArchiveCollection(document, {
|
|
2296
|
+
...withoutOperationAndScope(options),
|
|
2297
|
+
...chapters === void 0 ? {} : { chapters }
|
|
2298
|
+
});
|
|
2299
|
+
}, options);
|
|
2300
|
+
}
|
|
2301
|
+
async ensureSearchIndex(options = {}) {
|
|
2302
|
+
const embeddingProvider = await this.#runtime.run(
|
|
2303
|
+
createConfiguredEmbeddingProvider,
|
|
2304
|
+
options.signal
|
|
2305
|
+
);
|
|
2306
|
+
await this.#writeDocument(
|
|
2307
|
+
async (document) => {
|
|
2308
|
+
const chapters = await this.#resolveScope(document, options);
|
|
2309
|
+
await ensureArchiveSearchIndex(document, {
|
|
2310
|
+
...chapters === void 0 ? {} : { chapters },
|
|
2311
|
+
...embeddingProvider === void 0 ? {} : { embeddingProvider }
|
|
2312
|
+
});
|
|
2313
|
+
},
|
|
2314
|
+
{
|
|
2315
|
+
searchIndexWritebackPolicy: "cache",
|
|
2316
|
+
...options.signal === void 0 ? {} : { signal: options.signal }
|
|
2317
|
+
}
|
|
2318
|
+
);
|
|
2319
|
+
}
|
|
2320
|
+
async getSearchIndexStatus(options = {}) {
|
|
2321
|
+
return await this.#readDocument(
|
|
2322
|
+
async (document) => ({
|
|
2323
|
+
capabilities: await readSearchIndexCapabilityStatus(document),
|
|
2324
|
+
current: await isArchiveSearchIndexCurrent2(document)
|
|
2325
|
+
}),
|
|
2326
|
+
options
|
|
2327
|
+
);
|
|
2328
|
+
}
|
|
2329
|
+
async syncSearchIndex(options = {}) {
|
|
2330
|
+
const rebuilt = await this.#writeDocument(
|
|
2331
|
+
async (document) => {
|
|
2332
|
+
const scope = options.skipUnindexed === true ? { chapters: await listArchiveQueryableChapterIds(document) } : {};
|
|
2333
|
+
if (scope.chapters?.length === 0) {
|
|
2334
|
+
throw new Error(
|
|
2335
|
+
"Wiki Graph index cache is not ready. No chapters have a current FTS artifact or source embedding artifact."
|
|
2336
|
+
);
|
|
2337
|
+
}
|
|
2338
|
+
if (await isArchiveSearchIndexCurrent2(document, scope)) return false;
|
|
2339
|
+
await rebuildArchiveSearchIndex(document, options.onProgress, scope);
|
|
2340
|
+
return true;
|
|
2341
|
+
},
|
|
2342
|
+
{
|
|
2343
|
+
searchIndexWritebackPolicy: "cache",
|
|
2344
|
+
...options.signal === void 0 ? {} : { signal: options.signal }
|
|
2345
|
+
}
|
|
2346
|
+
);
|
|
2347
|
+
await this.#runtime.run(
|
|
2348
|
+
async () => await deleteArchiveSearchSessions(this.path),
|
|
2349
|
+
options.signal
|
|
2350
|
+
);
|
|
2351
|
+
return { rebuilt };
|
|
2352
|
+
}
|
|
2353
|
+
async cleanSearchIndex(options = {}) {
|
|
2354
|
+
const status = await this.#writeDocument(
|
|
2355
|
+
async (document) => {
|
|
2356
|
+
await document.deleteSearchIndexDatabase();
|
|
2357
|
+
return {
|
|
2358
|
+
capabilities: {
|
|
2359
|
+
dense: { current: false },
|
|
2360
|
+
indexes: "missing"
|
|
2361
|
+
},
|
|
2362
|
+
current: false
|
|
2363
|
+
};
|
|
2364
|
+
},
|
|
2365
|
+
{
|
|
2366
|
+
searchIndexWritebackPolicy: "cache",
|
|
2367
|
+
...options.signal === void 0 ? {} : { signal: options.signal }
|
|
2368
|
+
}
|
|
2369
|
+
);
|
|
2370
|
+
await this.#runtime.run(
|
|
2371
|
+
async () => await deleteArchiveSearchSessions(this.path),
|
|
2372
|
+
options.signal
|
|
2373
|
+
);
|
|
2374
|
+
return status;
|
|
2375
|
+
}
|
|
2376
|
+
async listChapters(options = {}) {
|
|
2377
|
+
return await this.#readDocument(
|
|
2378
|
+
async (document) => await listChapters2(document),
|
|
2379
|
+
options
|
|
2380
|
+
);
|
|
2381
|
+
}
|
|
2382
|
+
async getChapter(path, options = {}) {
|
|
2383
|
+
return await this.#readDocument(async (document) => {
|
|
2384
|
+
const chapterId = await resolveChapterPathReadonly(document, path);
|
|
2385
|
+
return requireChapter(await listChapters2(document), chapterId);
|
|
2386
|
+
}, options);
|
|
2387
|
+
}
|
|
2388
|
+
async addChapter(options) {
|
|
2389
|
+
return await this.#writeDocument(async (document) => {
|
|
2390
|
+
const parentChapterId = await resolveOptionalChapterPath(
|
|
2391
|
+
document,
|
|
2392
|
+
options.parentPath
|
|
2393
|
+
);
|
|
2394
|
+
await assertNoActiveBuildJobConflicts({
|
|
2395
|
+
archive: this.#location.archiveFile,
|
|
2396
|
+
operation: "Adding chapter",
|
|
2397
|
+
scope: { kind: "archive" }
|
|
2398
|
+
});
|
|
2399
|
+
let details = await addChapter(document, {
|
|
2400
|
+
...parentChapterId === void 0 ? {} : { parentChapterId },
|
|
2401
|
+
...options.title === void 0 ? {} : { title: options.title }
|
|
2402
|
+
});
|
|
2403
|
+
if (options.source !== void 0) {
|
|
2404
|
+
details = await setChapterSource(
|
|
2405
|
+
document,
|
|
2406
|
+
details.chapterId,
|
|
2407
|
+
asTextStream(options.source)
|
|
2408
|
+
);
|
|
2409
|
+
}
|
|
2410
|
+
return details;
|
|
2411
|
+
});
|
|
2412
|
+
}
|
|
2413
|
+
async getChapterArtifact(path, kind) {
|
|
2414
|
+
return await this.#readDocument(async (document) => {
|
|
2415
|
+
const chapterId = await resolveChapterPathReadonly(document, path);
|
|
2416
|
+
const [artifact, revision] = await Promise.all([
|
|
2417
|
+
document.indexArtifacts.get(chapterId, kind),
|
|
2418
|
+
document.serials.getRevision(chapterId)
|
|
2419
|
+
]);
|
|
2420
|
+
return {
|
|
2421
|
+
...artifact === void 0 ? {} : {
|
|
2422
|
+
artifact: {
|
|
2423
|
+
createdAt: artifact.createdAt,
|
|
2424
|
+
metadata: artifact.metadata,
|
|
2425
|
+
sourceRevision: artifact.sourceRevision
|
|
2426
|
+
}
|
|
2427
|
+
},
|
|
2428
|
+
chapterId,
|
|
2429
|
+
current: artifact?.sourceRevision === revision,
|
|
2430
|
+
kind,
|
|
2431
|
+
missing: artifact === void 0,
|
|
2432
|
+
revision
|
|
2433
|
+
};
|
|
2434
|
+
});
|
|
2435
|
+
}
|
|
2436
|
+
async deleteChapterArtifact(path, kind) {
|
|
2437
|
+
return await this.#writeDocument(async (document) => {
|
|
2438
|
+
const chapterId = await resolveChapterPathReadonly(document, path);
|
|
2439
|
+
await document.indexArtifacts.delete(chapterId, kind);
|
|
2440
|
+
return { chapterId, deleted: true, kind };
|
|
2441
|
+
});
|
|
2442
|
+
}
|
|
2443
|
+
async moveChapter(path, options) {
|
|
2444
|
+
return await this.#writeDocument(async (document) => {
|
|
2445
|
+
const chapterId = await resolveChapterPathReadonly(document, path);
|
|
2446
|
+
const [afterChapterId, beforeChapterId, parentChapterId] = await Promise.all([
|
|
2447
|
+
resolveOptionalChapterPath(document, options.afterPath),
|
|
2448
|
+
resolveOptionalChapterPath(document, options.beforePath),
|
|
2449
|
+
resolveOptionalChapterPath(document, options.parentPath)
|
|
2450
|
+
]);
|
|
2451
|
+
await assertNoActiveBuildJobConflicts({
|
|
2452
|
+
archive: this.#location.archiveFile,
|
|
2453
|
+
operation: "Moving chapter",
|
|
2454
|
+
scope: { kind: "archive" }
|
|
2455
|
+
});
|
|
2456
|
+
return await moveChapter(document, chapterId, {
|
|
2457
|
+
...afterChapterId === void 0 ? {} : { afterChapterId },
|
|
2458
|
+
...beforeChapterId === void 0 ? {} : { beforeChapterId },
|
|
2459
|
+
...parentChapterId === void 0 ? {} : { parentChapterId },
|
|
2460
|
+
...options.first === void 0 ? {} : { first: options.first },
|
|
2461
|
+
...options.last === void 0 ? {} : { last: options.last },
|
|
2462
|
+
...options.root === void 0 ? {} : { root: options.root }
|
|
2463
|
+
});
|
|
2464
|
+
});
|
|
2465
|
+
}
|
|
2466
|
+
async removeChapter(path, recursive = false) {
|
|
2467
|
+
await this.#writeDocument(async (document) => {
|
|
2468
|
+
const chapterId = await resolveChapterPathReadonly(document, path);
|
|
2469
|
+
await assertNoActiveBuildJobConflicts({
|
|
2470
|
+
archive: this.#location.archiveFile,
|
|
2471
|
+
operation: "Removing chapter",
|
|
2472
|
+
scope: { kind: "archive" }
|
|
2473
|
+
});
|
|
2474
|
+
await removeChapter(document, chapterId, { recursive });
|
|
2475
|
+
});
|
|
2476
|
+
}
|
|
2477
|
+
async resetChapter(path, stage) {
|
|
2478
|
+
return await this.#writeDocument(async (document) => {
|
|
2479
|
+
const chapterId = await resolveChapterPathReadonly(document, path);
|
|
2480
|
+
await assertChapterResetAllowed(
|
|
2481
|
+
this.#location.archiveFile,
|
|
2482
|
+
chapterId,
|
|
2483
|
+
stage
|
|
2484
|
+
);
|
|
2485
|
+
return await resetChapter(document, chapterId, stage);
|
|
2486
|
+
});
|
|
2487
|
+
}
|
|
2488
|
+
async setChapterSource(path, source, options = {}) {
|
|
2489
|
+
return await this.#writeDocument(async (document) => {
|
|
2490
|
+
const chapterId = await resolveChapterPathReadonly(document, path);
|
|
2491
|
+
await assertNoActiveBuildJobs({
|
|
2492
|
+
archive: this.#location.archiveFile,
|
|
2493
|
+
chapterIds: [chapterId],
|
|
2494
|
+
operation: "Setting chapter source"
|
|
2495
|
+
});
|
|
2496
|
+
return await setChapterSource(
|
|
2497
|
+
document,
|
|
2498
|
+
chapterId,
|
|
2499
|
+
asTextStream(source),
|
|
2500
|
+
options
|
|
2501
|
+
);
|
|
2502
|
+
});
|
|
2503
|
+
}
|
|
2504
|
+
async setChapterSummary(path, summary) {
|
|
2505
|
+
return await this.#writeDocument(async (document) => {
|
|
2506
|
+
const chapterId = await resolveChapterPathReadonly(document, path);
|
|
2507
|
+
await assertNoActiveBuildJobs({
|
|
2508
|
+
archive: this.#location.archiveFile,
|
|
2509
|
+
chapterIds: [chapterId],
|
|
2510
|
+
operation: "Setting chapter summary",
|
|
2511
|
+
requiresTarget: "reading-summary"
|
|
2512
|
+
});
|
|
2513
|
+
return await setChapterSummary(document, chapterId, summary);
|
|
2514
|
+
});
|
|
2515
|
+
}
|
|
2516
|
+
async setChapterTitle(path, title) {
|
|
2517
|
+
return await this.#writeDocument(async (document) => {
|
|
2518
|
+
const chapterId = await resolveChapterPathReadonly(document, path);
|
|
2519
|
+
await assertNoActiveBuildJobs({
|
|
2520
|
+
archive: this.#location.archiveFile,
|
|
2521
|
+
chapterIds: [chapterId],
|
|
2522
|
+
operation: "Setting chapter title"
|
|
2523
|
+
});
|
|
2524
|
+
return await setChapterTitle(document, chapterId, title);
|
|
2525
|
+
});
|
|
2526
|
+
}
|
|
2527
|
+
async getChapterTree() {
|
|
2528
|
+
return await this.#readDocument(
|
|
2529
|
+
async (document) => await getChapterTree(document)
|
|
2530
|
+
);
|
|
2531
|
+
}
|
|
2532
|
+
async applyChapterTree(tree, options = {}) {
|
|
2533
|
+
return await this.#writeDocument(
|
|
2534
|
+
async (document) => {
|
|
2535
|
+
if (options.dryRun !== true) {
|
|
2536
|
+
await assertNoActiveBuildJobConflicts({
|
|
2537
|
+
archive: this.#location.archiveFile,
|
|
2538
|
+
operation: "Changing chapter tree",
|
|
2539
|
+
scope: { kind: "archive" }
|
|
2540
|
+
});
|
|
2541
|
+
}
|
|
2542
|
+
return await applyChapterTree(document, tree, {
|
|
2543
|
+
dryRun: options.dryRun ?? false
|
|
2544
|
+
});
|
|
2545
|
+
},
|
|
2546
|
+
{ refreshLibraryIndex: options.dryRun !== true }
|
|
2547
|
+
);
|
|
2548
|
+
}
|
|
2549
|
+
async page(objectUri = this.objectUri, options = {}) {
|
|
2550
|
+
return await this.#runtime.run(
|
|
2551
|
+
async () => await withConfiguredWikimediaResolver(
|
|
2552
|
+
objectUri,
|
|
2553
|
+
async (wikimediaOptions) => await this.#readDocument(
|
|
2554
|
+
async (document) => await readArchivePage(document, objectUri, {
|
|
2555
|
+
...withoutOperation(options),
|
|
2556
|
+
...wikimediaOptions
|
|
2557
|
+
}),
|
|
2558
|
+
options
|
|
2559
|
+
)
|
|
2560
|
+
),
|
|
2561
|
+
options.signal
|
|
2562
|
+
);
|
|
2563
|
+
}
|
|
2564
|
+
async related(objectUri = this.objectUri, options = {}) {
|
|
2565
|
+
if (options.query === void 0) {
|
|
2566
|
+
return await this.#readDocument(async (document) => {
|
|
2567
|
+
const chapters = await this.#resolveScope(document, {});
|
|
2568
|
+
return await listRelatedArchiveObjects(document, objectUri, {
|
|
2569
|
+
...withoutOperation(options),
|
|
2570
|
+
...chapters === void 0 ? {} : { chapters }
|
|
2571
|
+
});
|
|
2572
|
+
}, options);
|
|
2573
|
+
}
|
|
2574
|
+
const embeddingProvider = await this.#runtime.run(
|
|
2575
|
+
createConfiguredEmbeddingProvider,
|
|
2576
|
+
options.signal
|
|
2577
|
+
);
|
|
2578
|
+
return await this.#writeDocument(
|
|
2579
|
+
async (document) => {
|
|
2580
|
+
const chapters = await this.#resolveQueryChapters(document, options);
|
|
2581
|
+
await ensureArchiveSearchIndex(document, {
|
|
2582
|
+
...chapters === void 0 ? {} : { chapters },
|
|
2583
|
+
...embeddingProvider === void 0 ? {} : { embeddingProvider }
|
|
2584
|
+
});
|
|
2585
|
+
return await listRelatedArchiveObjects(document, objectUri, {
|
|
2586
|
+
...withoutOperation(options),
|
|
2587
|
+
...chapters === void 0 ? {} : { chapters }
|
|
2588
|
+
});
|
|
2589
|
+
},
|
|
2590
|
+
{
|
|
2591
|
+
searchIndexWritebackPolicy: "cache",
|
|
2592
|
+
...options.signal === void 0 ? {} : { signal: options.signal }
|
|
2593
|
+
}
|
|
2594
|
+
);
|
|
2595
|
+
}
|
|
2596
|
+
async evidence(objectUri = this.objectUri, options = {}) {
|
|
2597
|
+
if (options.query === void 0) {
|
|
2598
|
+
return await this.#readDocument(async (document) => {
|
|
2599
|
+
const chapters = await this.#resolveScope(document, {});
|
|
2600
|
+
return await listArchiveEvidence(document, objectUri, {
|
|
2601
|
+
...withoutOperation(options),
|
|
2602
|
+
...chapters === void 0 ? {} : { chapters }
|
|
2603
|
+
});
|
|
2604
|
+
}, options);
|
|
2605
|
+
}
|
|
2606
|
+
const embeddingProvider = await this.#runtime.run(
|
|
2607
|
+
createConfiguredEmbeddingProvider,
|
|
2608
|
+
options.signal
|
|
2609
|
+
);
|
|
2610
|
+
return await this.#writeDocument(
|
|
2611
|
+
async (document) => {
|
|
2612
|
+
const chapters = await this.#resolveQueryChapters(document, options);
|
|
2613
|
+
await ensureArchiveSearchIndex(document, {
|
|
2614
|
+
...chapters === void 0 ? {} : { chapters },
|
|
2615
|
+
...embeddingProvider === void 0 ? {} : { embeddingProvider }
|
|
2616
|
+
});
|
|
2617
|
+
return await listArchiveEvidence(document, objectUri, {
|
|
2618
|
+
...withoutOperation(options),
|
|
2619
|
+
...chapters === void 0 ? {} : { chapters }
|
|
2620
|
+
});
|
|
2621
|
+
},
|
|
2622
|
+
{
|
|
2623
|
+
searchIndexWritebackPolicy: "cache",
|
|
2624
|
+
...options.signal === void 0 ? {} : { signal: options.signal }
|
|
2625
|
+
}
|
|
2626
|
+
);
|
|
2627
|
+
}
|
|
2628
|
+
async pack(objectUri = this.objectUri, budget = 5e3, options = {}) {
|
|
2629
|
+
return await this.#readDocument(
|
|
2630
|
+
async (document) => await packArchiveContext(document, objectUri, budget),
|
|
2631
|
+
options
|
|
2632
|
+
);
|
|
2633
|
+
}
|
|
2634
|
+
async getMetadata(objectPath, options = {}) {
|
|
2635
|
+
return await this.#readDocument(
|
|
2636
|
+
async (document) => await document.metadata.getMap(objectPath),
|
|
2637
|
+
options
|
|
2638
|
+
);
|
|
2639
|
+
}
|
|
2640
|
+
async readBookMeta(options = {}) {
|
|
2641
|
+
return await this.#readDocument(
|
|
2642
|
+
async (document) => await document.readBookMeta(),
|
|
2643
|
+
options
|
|
2644
|
+
);
|
|
2645
|
+
}
|
|
2646
|
+
async replaceBookMeta(meta, options = {}) {
|
|
2647
|
+
return await this.#writeDocument(async (document) => {
|
|
2648
|
+
await document.replaceBookMeta(meta);
|
|
2649
|
+
return meta;
|
|
2650
|
+
}, options);
|
|
2651
|
+
}
|
|
2652
|
+
async readCover(options = {}) {
|
|
2653
|
+
const app = new WikiGraph2({});
|
|
2654
|
+
return await this.#runtime.run(
|
|
2655
|
+
async () => await app.openSession(
|
|
2656
|
+
this.#location.archiveFile,
|
|
2657
|
+
async (archive) => await archive.readCover()
|
|
2658
|
+
),
|
|
2659
|
+
options.signal
|
|
2660
|
+
);
|
|
2661
|
+
}
|
|
2662
|
+
async replaceMetadata(objectPath, value, options = {}) {
|
|
2663
|
+
return await this.#writeDocument(async (document) => {
|
|
2664
|
+
await document.metadata.replaceMap(
|
|
2665
|
+
parseObjectMetadataTarget(objectPath),
|
|
2666
|
+
value
|
|
2667
|
+
);
|
|
2668
|
+
return await document.metadata.getMap(objectPath);
|
|
2669
|
+
}, options);
|
|
2670
|
+
}
|
|
2671
|
+
async putMetadata(objectPath, key, value, options = {}) {
|
|
2672
|
+
return await this.#writeDocument(async (document) => {
|
|
2673
|
+
await document.metadata.put(
|
|
2674
|
+
parseObjectMetadataTarget(objectPath),
|
|
2675
|
+
key,
|
|
2676
|
+
value
|
|
2677
|
+
);
|
|
2678
|
+
return await document.metadata.getMap(objectPath);
|
|
2679
|
+
}, options);
|
|
2680
|
+
}
|
|
2681
|
+
async deleteMetadata(objectPath, key, options = {}) {
|
|
2682
|
+
return await this.#writeDocument(async (document) => {
|
|
2683
|
+
await document.metadata.deleteKey(objectPath, key);
|
|
2684
|
+
return await document.metadata.getMap(objectPath);
|
|
2685
|
+
}, options);
|
|
2686
|
+
}
|
|
2687
|
+
async clearMetadata(objectPath, options = {}) {
|
|
2688
|
+
return await this.#writeDocument(async (document) => {
|
|
2689
|
+
await document.metadata.clear(objectPath);
|
|
2690
|
+
return await document.metadata.getMap(objectPath);
|
|
2691
|
+
}, options);
|
|
2692
|
+
}
|
|
2693
|
+
async resolveScope(options = {}) {
|
|
2694
|
+
return await this.#readDocument(
|
|
2695
|
+
async (document) => await resolveArchiveScope(document, this.objectUri, options.depth),
|
|
2696
|
+
options
|
|
2697
|
+
);
|
|
2698
|
+
}
|
|
2699
|
+
async #readDocument(operation, options = {}) {
|
|
2700
|
+
return await this.#runtime.run(
|
|
2701
|
+
async () => await new WikiGraphArchiveFile(this.#location.archiveFile).readDocument(
|
|
2702
|
+
operation
|
|
2703
|
+
),
|
|
2704
|
+
options.signal
|
|
2705
|
+
);
|
|
2706
|
+
}
|
|
2707
|
+
async #writeDocument(operation, options = {}) {
|
|
2708
|
+
return await this.#runtime.run(async () => {
|
|
2709
|
+
const result = await new WikiGraphArchiveFile(
|
|
2710
|
+
this.#location.archiveFile
|
|
2711
|
+
).write(operation, {
|
|
2712
|
+
...options.searchIndexWritebackPolicy === void 0 ? {} : {
|
|
2713
|
+
searchIndexWritebackPolicy: options.searchIndexWritebackPolicy
|
|
2714
|
+
}
|
|
2715
|
+
});
|
|
2716
|
+
if (options.refreshLibraryIndex !== false) {
|
|
2717
|
+
await this.#refreshLibraryIndex(options.onIndexSyncError);
|
|
2718
|
+
}
|
|
2719
|
+
return result;
|
|
2720
|
+
}, options.signal);
|
|
2721
|
+
}
|
|
2722
|
+
async #resolveScope(document, options) {
|
|
2723
|
+
if (options.chapters !== void 0) return options.chapters;
|
|
2724
|
+
return (await resolveArchiveScope(document, this.objectUri, options.depth))?.chapterIds;
|
|
2725
|
+
}
|
|
2726
|
+
async #resolveQueryChapters(document, options) {
|
|
2727
|
+
const chapters = await this.#resolveScope(document, options);
|
|
2728
|
+
if (options.skipUnindexed !== true) return chapters;
|
|
2729
|
+
const queryable = await listArchiveQueryableChapterIds(document, {
|
|
2730
|
+
...chapters === void 0 ? {} : { chapters }
|
|
2731
|
+
});
|
|
2732
|
+
if (queryable.length === 0) {
|
|
2733
|
+
throw new Error(
|
|
2734
|
+
"Wiki Graph query is not ready. No chapters in this scope have a current FTS artifact or source embedding artifact."
|
|
2735
|
+
);
|
|
2736
|
+
}
|
|
2737
|
+
return queryable;
|
|
2738
|
+
}
|
|
2739
|
+
async #refreshLibraryIndex(onError) {
|
|
2740
|
+
if (this.#location.libraryDirtyTarget === void 0) return;
|
|
2741
|
+
if (this.#location.libraryArchiveTarget !== void 0) {
|
|
2742
|
+
await finalizeWikiGraphLibraryArchiveWrite({
|
|
2743
|
+
target: this.#location.libraryArchiveTarget
|
|
2744
|
+
});
|
|
2745
|
+
}
|
|
2746
|
+
try {
|
|
2747
|
+
const state = await readWikiGraphLibraryIndexState(
|
|
2748
|
+
this.#location.libraryDirtyTarget
|
|
2749
|
+
);
|
|
2750
|
+
if (state.status !== "missing") {
|
|
2751
|
+
await rebuildWikiGraphLibraryIndex(this.#location.libraryDirtyTarget);
|
|
2752
|
+
}
|
|
2753
|
+
} catch (error) {
|
|
2754
|
+
onError?.(error);
|
|
2755
|
+
}
|
|
2756
|
+
}
|
|
2757
|
+
};
|
|
2758
|
+
async function resolveWikiGraphArchiveLocation(uriOrPath) {
|
|
2759
|
+
if (!uriOrPath.startsWith("wikg://")) {
|
|
2760
|
+
return createPathLocation(resolveWikiGraphRuntimePath(uriOrPath));
|
|
2761
|
+
}
|
|
2762
|
+
const parsed = parseLocatedWikiGraphUri(uriOrPath);
|
|
2763
|
+
const portableArchiveLocator = parsed.archivePath ?? uriOrPath;
|
|
2764
|
+
const archiveLocator = portableArchiveLocator.startsWith("wikg://lib/") ? portableArchiveLocator : resolveWikiGraphRuntimePath(portableArchiveLocator);
|
|
2765
|
+
const libraryArchiveTarget = archiveLocator.startsWith("wikg://lib/") ? parseWikiGraphLibraryUri(archiveLocator) : void 0;
|
|
2766
|
+
const archiveFile = libraryArchiveTarget?.kind === "archive" ? await resolveWikiGraphLibraryArchiveFile(archiveLocator) : new NodeFile(archiveLocator);
|
|
2767
|
+
const archivePath = getNodeResourcePath(archiveFile);
|
|
2768
|
+
return {
|
|
2769
|
+
archiveFile,
|
|
2770
|
+
archiveKey: archivePath,
|
|
2771
|
+
archivePath,
|
|
2772
|
+
indexScope: { archiveKey: archivePath, archivePath, kind: "archive-index" },
|
|
2773
|
+
...libraryArchiveTarget?.kind === "archive" ? {
|
|
2774
|
+
libraryArchiveTarget,
|
|
2775
|
+
libraryDirtyTarget: {
|
|
2776
|
+
isDefault: libraryArchiveTarget.isDefault,
|
|
2777
|
+
kind: "scope",
|
|
2778
|
+
...libraryArchiveTarget.publicId === void 0 ? {} : { publicId: libraryArchiveTarget.publicId }
|
|
2779
|
+
},
|
|
2780
|
+
publicArchiveUri: portableArchiveLocator
|
|
2781
|
+
} : {},
|
|
2782
|
+
locatedUri: formatLocatedWikiGraphUri(archivePath, parsed.objectUri)
|
|
2783
|
+
};
|
|
2784
|
+
}
|
|
2785
|
+
async function ensureArchiveSearchIndex(document, options = {}) {
|
|
2786
|
+
const scope = options.chapters === void 0 ? {} : { chapters: options.chapters };
|
|
2787
|
+
if (await isArchiveSearchIndexCurrent2(document, scope)) return;
|
|
2788
|
+
await rebuildArchiveSearchIndex(document, void 0, {
|
|
2789
|
+
...scope,
|
|
2790
|
+
...options.embeddingProvider === void 0 ? {} : { embeddingProvider: options.embeddingProvider }
|
|
2791
|
+
});
|
|
2792
|
+
}
|
|
2793
|
+
async function resolveArchiveScope(document, objectUri, depth) {
|
|
2794
|
+
const parsed = parseChapterScopePath(objectUri);
|
|
2795
|
+
if (parsed === void 0 || parsed.kind === "collection" && depth === void 0) {
|
|
2796
|
+
return void 0;
|
|
2797
|
+
}
|
|
2798
|
+
const chapters = await listChapters2(document);
|
|
2799
|
+
const entries = parsed.kind === "collection" ? chapters.filter((chapter) => chapter.depth <= depth) : selectChapterSubtree(
|
|
2800
|
+
chapters,
|
|
2801
|
+
await resolveChapterPathReadonly(document, parsed.chapterPath),
|
|
2802
|
+
depth
|
|
2803
|
+
);
|
|
2804
|
+
return { chapterIds: entries.map((entry) => entry.chapterId), entries };
|
|
2805
|
+
}
|
|
2806
|
+
function parseChapterScopePath(objectUri) {
|
|
2807
|
+
if (objectUri === "wikg://chapter" || objectUri === "wikg://chapter/") {
|
|
2808
|
+
return { kind: "collection" };
|
|
2809
|
+
}
|
|
2810
|
+
const match = /^wikg:\/\/chapter\/([^/].*?)(?:\/)?$/u.exec(objectUri);
|
|
2811
|
+
return match?.[1] === void 0 ? void 0 : { chapterPath: decodeURIComponent(match[1]), kind: "chapter" };
|
|
2812
|
+
}
|
|
2813
|
+
function selectChapterSubtree(chapters, rootChapterId, depth) {
|
|
2814
|
+
const root = chapters.find((chapter) => chapter.chapterId === rootChapterId);
|
|
2815
|
+
if (root === void 0) {
|
|
2816
|
+
throw new Error(`Chapter ${rootChapterId} does not exist.`);
|
|
2817
|
+
}
|
|
2818
|
+
const prefix = `${root.path}/`;
|
|
2819
|
+
return chapters.filter(
|
|
2820
|
+
(chapter) => chapter.chapterId === root.chapterId || chapter.path.startsWith(prefix) && (depth === void 0 || chapter.depth - root.depth <= depth)
|
|
2821
|
+
);
|
|
2822
|
+
}
|
|
2823
|
+
function createPathLocation(path) {
|
|
2824
|
+
return {
|
|
2825
|
+
archiveFile: new NodeFile(path),
|
|
2826
|
+
archiveKey: path,
|
|
2827
|
+
archivePath: path,
|
|
2828
|
+
indexScope: { archiveKey: path, archivePath: path, kind: "archive-index" },
|
|
2829
|
+
locatedUri: formatLocatedWikiGraphUri(path)
|
|
2830
|
+
};
|
|
2831
|
+
}
|
|
2832
|
+
async function createEmptyArchiveFile(path) {
|
|
2833
|
+
const directoryPath = await mkdtemp(
|
|
2834
|
+
join3(tmpdir(), "wikigraph-archive-write-")
|
|
2835
|
+
);
|
|
2836
|
+
try {
|
|
2837
|
+
const directory = new NodeDirectory(directoryPath);
|
|
2838
|
+
const document = await DirectoryDocument.open(directory);
|
|
2839
|
+
try {
|
|
2840
|
+
await document.openSession(async (openedDocument) => {
|
|
2841
|
+
await openedDocument.writeToc({ items: [], version: TOC_FILE_VERSION });
|
|
2842
|
+
});
|
|
2843
|
+
} finally {
|
|
2844
|
+
await document.release();
|
|
2845
|
+
}
|
|
2846
|
+
await writeWikgArchive(directory, new NodeFile(path));
|
|
2847
|
+
} finally {
|
|
2848
|
+
await rm3(directoryPath, { force: true, recursive: true });
|
|
2849
|
+
}
|
|
2850
|
+
}
|
|
2851
|
+
function isNodeEEXISTError(error) {
|
|
2852
|
+
return error instanceof Error && "code" in error && error.code === "EEXIST";
|
|
2853
|
+
}
|
|
2854
|
+
async function nodePathExists(path) {
|
|
2855
|
+
try {
|
|
2856
|
+
await stat2(path);
|
|
2857
|
+
return true;
|
|
2858
|
+
} catch (error) {
|
|
2859
|
+
if (isNodeENOENTError(error)) return false;
|
|
2860
|
+
throw error;
|
|
2861
|
+
}
|
|
2862
|
+
}
|
|
2863
|
+
function isNodeENOENTError(error) {
|
|
2864
|
+
return error instanceof Error && "code" in error && error.code === "ENOENT";
|
|
2865
|
+
}
|
|
2866
|
+
function parseObjectMetadataTarget(objectPath) {
|
|
2867
|
+
if (objectPath === "") {
|
|
2868
|
+
return { kind: ObjectMetadataKind.Archive, objectPath };
|
|
2869
|
+
}
|
|
2870
|
+
const chapter = /^chapter\/([1-9][0-9]*)(?:\/.*)?$/u.exec(objectPath);
|
|
2871
|
+
if (chapter?.[1] !== void 0) {
|
|
2872
|
+
return {
|
|
2873
|
+
chapterId: Number(chapter[1]),
|
|
2874
|
+
kind: ObjectMetadataKind.Chapter,
|
|
2875
|
+
objectPath
|
|
2876
|
+
};
|
|
2877
|
+
}
|
|
2878
|
+
const chunk = /^chunk\/([1-9][0-9]*)$/u.exec(objectPath);
|
|
2879
|
+
if (chunk?.[1] !== void 0) {
|
|
2880
|
+
return {
|
|
2881
|
+
chunkId: Number(chunk[1]),
|
|
2882
|
+
kind: ObjectMetadataKind.Chunk,
|
|
2883
|
+
objectPath
|
|
2884
|
+
};
|
|
2885
|
+
}
|
|
2886
|
+
const entity = /^entity\/(Q[1-9][0-9]*)$/u.exec(objectPath);
|
|
2887
|
+
if (entity?.[1] !== void 0) {
|
|
2888
|
+
return {
|
|
2889
|
+
entityQid: entity[1],
|
|
2890
|
+
kind: ObjectMetadataKind.Entity,
|
|
2891
|
+
objectPath
|
|
2892
|
+
};
|
|
2893
|
+
}
|
|
2894
|
+
const triple = /^triple\/(Q[1-9][0-9]*)\/([^/]+)\/(Q[1-9][0-9]*)$/u.exec(
|
|
2895
|
+
objectPath
|
|
2896
|
+
);
|
|
2897
|
+
if (triple?.[1] !== void 0) {
|
|
2898
|
+
return {
|
|
2899
|
+
kind: ObjectMetadataKind.Triple,
|
|
2900
|
+
objectPath,
|
|
2901
|
+
tripleObjectQid: triple[3],
|
|
2902
|
+
triplePredicate: decodeURIComponent(triple[2]),
|
|
2903
|
+
tripleSubjectQid: triple[1]
|
|
2904
|
+
};
|
|
2905
|
+
}
|
|
2906
|
+
return { kind: ObjectMetadataKind.Object, objectPath };
|
|
2907
|
+
}
|
|
2908
|
+
function withoutOperation(options) {
|
|
2909
|
+
const { signal: _signal, ...rest } = options;
|
|
2910
|
+
return rest;
|
|
2911
|
+
}
|
|
2912
|
+
function withoutOperationAndScope(options) {
|
|
2913
|
+
const {
|
|
2914
|
+
chapters: _chapters,
|
|
2915
|
+
depth: _depth,
|
|
2916
|
+
signal: _signal,
|
|
2917
|
+
...rest
|
|
2918
|
+
} = options;
|
|
2919
|
+
return rest;
|
|
2920
|
+
}
|
|
2921
|
+
async function resolveOptionalChapterPath(document, path) {
|
|
2922
|
+
return path === void 0 ? void 0 : await resolveChapterPathReadonly(document, path);
|
|
2923
|
+
}
|
|
2924
|
+
function requireChapter(chapters, chapterId) {
|
|
2925
|
+
const chapter = chapters.find((entry) => entry.chapterId === chapterId);
|
|
2926
|
+
if (chapter === void 0) {
|
|
2927
|
+
throw new Error(`Chapter internal id ${chapterId} does not exist.`);
|
|
2928
|
+
}
|
|
2929
|
+
return chapter;
|
|
2930
|
+
}
|
|
2931
|
+
function asTextStream(text) {
|
|
2932
|
+
return Readable2.from([text]);
|
|
2933
|
+
}
|
|
2934
|
+
async function assertChapterResetAllowed(archive, chapterId, stage) {
|
|
2935
|
+
if (stage === "planned") {
|
|
2936
|
+
await assertNoActiveBuildJobs({
|
|
2937
|
+
archive,
|
|
2938
|
+
chapterIds: [chapterId],
|
|
2939
|
+
operation: "Resetting chapter to planned"
|
|
2940
|
+
});
|
|
2941
|
+
return;
|
|
2942
|
+
}
|
|
2943
|
+
if (stage === "sourced") {
|
|
2944
|
+
await assertNoActiveBuildJobs({
|
|
2945
|
+
archive,
|
|
2946
|
+
chapterIds: [chapterId],
|
|
2947
|
+
operation: "Resetting chapter graph",
|
|
2948
|
+
requiresTarget: "reading-graph"
|
|
2949
|
+
});
|
|
2950
|
+
}
|
|
2951
|
+
await assertNoActiveBuildJobs({
|
|
2952
|
+
archive,
|
|
2953
|
+
chapterIds: [chapterId],
|
|
2954
|
+
operation: "Resetting chapter summary",
|
|
2955
|
+
requiresTarget: "reading-summary"
|
|
2956
|
+
});
|
|
2957
|
+
}
|
|
2958
|
+
|
|
2959
|
+
// src/wikispine.ts
|
|
2960
|
+
import { spawn } from "child_process";
|
|
2961
|
+
var nodeWikispineCommandRunner = {
|
|
2962
|
+
run: async ({ args, command, input, onStdout, signal }) => await new Promise((resolve3, reject) => {
|
|
2963
|
+
signal?.throwIfAborted();
|
|
2964
|
+
const child = spawn(command, [...args], {
|
|
2965
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
2966
|
+
});
|
|
2967
|
+
const stderr = [];
|
|
2968
|
+
let settled = false;
|
|
2969
|
+
const settle = (operation) => {
|
|
2970
|
+
if (settled) return;
|
|
2971
|
+
settled = true;
|
|
2972
|
+
signal?.removeEventListener("abort", abort);
|
|
2973
|
+
operation();
|
|
2974
|
+
};
|
|
2975
|
+
const abort = () => {
|
|
2976
|
+
child.kill();
|
|
2977
|
+
settle(() => reject(abortError(signal)));
|
|
2978
|
+
};
|
|
2979
|
+
signal?.addEventListener("abort", abort, { once: true });
|
|
2980
|
+
child.stdout.setEncoding("utf8");
|
|
2981
|
+
child.stdout.on("data", (chunk) => {
|
|
2982
|
+
if (settled) return;
|
|
2983
|
+
try {
|
|
2984
|
+
onStdout(chunk);
|
|
2985
|
+
} catch (error) {
|
|
2986
|
+
child.kill();
|
|
2987
|
+
settle(
|
|
2988
|
+
() => reject(error instanceof Error ? error : new Error(String(error)))
|
|
2989
|
+
);
|
|
2990
|
+
}
|
|
2991
|
+
});
|
|
2992
|
+
child.stderr.on("data", (chunk) => stderr.push(chunk));
|
|
2993
|
+
child.on("error", (error) => {
|
|
2994
|
+
settle(
|
|
2995
|
+
() => reject(
|
|
2996
|
+
new Error(`Failed to start wikispine command: ${error.message}`)
|
|
2997
|
+
)
|
|
2998
|
+
);
|
|
2999
|
+
});
|
|
3000
|
+
child.on("close", (exitCode) => {
|
|
3001
|
+
settle(
|
|
3002
|
+
() => resolve3({
|
|
3003
|
+
exitCode,
|
|
3004
|
+
stderr: Buffer.concat(stderr).toString("utf8")
|
|
3005
|
+
})
|
|
3006
|
+
);
|
|
3007
|
+
});
|
|
3008
|
+
child.stdin.end(input);
|
|
3009
|
+
})
|
|
3010
|
+
};
|
|
3011
|
+
function abortError(signal) {
|
|
3012
|
+
return signal?.reason instanceof Error ? signal.reason : new Error("WikiSpine command aborted.");
|
|
3013
|
+
}
|
|
3014
|
+
|
|
3015
|
+
// src/config.ts
|
|
3016
|
+
import { streamText } from "ai";
|
|
3017
|
+
import { testWikispineRuntime } from "wiki-graph-core";
|
|
3018
|
+
var WikiGraphConfigManager = class {
|
|
3019
|
+
#runtime;
|
|
3020
|
+
constructor(runtime) {
|
|
3021
|
+
this.#runtime = runtime;
|
|
3022
|
+
}
|
|
3023
|
+
async get(section, options = {}) {
|
|
3024
|
+
const value = await this.#runtime.run(
|
|
3025
|
+
async () => await readLocalConfigSection(section)
|
|
3026
|
+
);
|
|
3027
|
+
return options.maskSecrets === true ? maskLocalConfigSection(section, value) : value;
|
|
3028
|
+
}
|
|
3029
|
+
async replace(section, value) {
|
|
3030
|
+
return await this.#runtime.run(
|
|
3031
|
+
async () => await replaceLocalConfigSection(section, value)
|
|
3032
|
+
);
|
|
3033
|
+
}
|
|
3034
|
+
async put(section, key, value) {
|
|
3035
|
+
return await this.#runtime.run(
|
|
3036
|
+
async () => await putLocalConfigValue(section, key, value)
|
|
3037
|
+
);
|
|
3038
|
+
}
|
|
3039
|
+
async delete(section, key) {
|
|
3040
|
+
return await this.#runtime.run(
|
|
3041
|
+
async () => await deleteLocalConfigValue(section, key)
|
|
3042
|
+
);
|
|
3043
|
+
}
|
|
3044
|
+
async clear(section) {
|
|
3045
|
+
return await this.#runtime.run(
|
|
3046
|
+
async () => await clearLocalConfigSection(section)
|
|
3047
|
+
);
|
|
3048
|
+
}
|
|
3049
|
+
async testLLM() {
|
|
3050
|
+
return await this.#runtime.run(async () => {
|
|
3051
|
+
const startedAt = Date.now();
|
|
3052
|
+
const llm = await readLocalConfigSection("llm");
|
|
3053
|
+
const config = {
|
|
3054
|
+
...typeof llm.apiKey === "string" ? { apiKey: llm.apiKey } : {},
|
|
3055
|
+
...typeof llm.baseURL === "string" ? { baseURL: llm.baseURL } : {},
|
|
3056
|
+
...typeof llm.model === "string" ? { model: llm.model } : {},
|
|
3057
|
+
...typeof llm.name === "string" ? { name: llm.name } : {},
|
|
3058
|
+
...typeof llm.provider === "string" ? { provider: parseProvider(llm.provider) } : {}
|
|
3059
|
+
};
|
|
3060
|
+
const options = buildWikiGraphLLMOptions({ llm: config });
|
|
3061
|
+
const result = streamText({
|
|
3062
|
+
maxRetries: 0,
|
|
3063
|
+
messages: [{ content: "Reply with exactly: ok", role: "user" }],
|
|
3064
|
+
model: options.model,
|
|
3065
|
+
temperature: 0
|
|
3066
|
+
});
|
|
3067
|
+
const chunks = [];
|
|
3068
|
+
for await (const chunk of result.textStream) chunks.push(chunk);
|
|
3069
|
+
return {
|
|
3070
|
+
durationMs: Date.now() - startedAt,
|
|
3071
|
+
...config.model === void 0 ? {} : { model: config.model },
|
|
3072
|
+
...config.provider === void 0 ? {} : { provider: config.provider },
|
|
3073
|
+
response: chunks.join("").trim()
|
|
3074
|
+
};
|
|
3075
|
+
});
|
|
3076
|
+
}
|
|
3077
|
+
async testEmbedding() {
|
|
3078
|
+
return await this.#runtime.run(async () => {
|
|
3079
|
+
const startedAt = Date.now();
|
|
3080
|
+
const result = await embedQueryText(
|
|
3081
|
+
"Wiki Graph embedding connectivity test.",
|
|
3082
|
+
await readWikiGraphEmbeddingConfig()
|
|
3083
|
+
);
|
|
3084
|
+
return {
|
|
3085
|
+
dimensions: result.dimensions,
|
|
3086
|
+
durationMs: Date.now() - startedAt,
|
|
3087
|
+
model: result.model,
|
|
3088
|
+
provider: result.provider,
|
|
3089
|
+
...result.usage?.tokens === void 0 ? {} : { tokens: result.usage.tokens }
|
|
3090
|
+
};
|
|
3091
|
+
});
|
|
3092
|
+
}
|
|
3093
|
+
async testWikispine() {
|
|
3094
|
+
return await this.#runtime.run(async () => {
|
|
3095
|
+
const value = await readLocalConfigSection("wikispine");
|
|
3096
|
+
const resolved = resolveWikispineConfig(value);
|
|
3097
|
+
if (resolved === void 0) {
|
|
3098
|
+
throw new Error("WikiSpine provider is not configured.");
|
|
3099
|
+
}
|
|
3100
|
+
const result = await testWikispineRuntime({
|
|
3101
|
+
...resolved,
|
|
3102
|
+
...resolved.provider === "cli" ? { commandRunner: nodeWikispineCommandRunner } : {}
|
|
3103
|
+
});
|
|
3104
|
+
return {
|
|
3105
|
+
durationMs: result.durationMs,
|
|
3106
|
+
...resolved.provider === "fetch" ? { endpoint: resolved.endpoint } : {},
|
|
3107
|
+
...result.metadata === void 0 ? {} : { metadata: result.metadata },
|
|
3108
|
+
provider: resolved.provider
|
|
3109
|
+
};
|
|
3110
|
+
});
|
|
3111
|
+
}
|
|
3112
|
+
};
|
|
3113
|
+
function parseProvider(value) {
|
|
3114
|
+
if (value === "anthropic" || value === "google" || value === "openai" || value === "openai-compatible") {
|
|
3115
|
+
return value;
|
|
3116
|
+
}
|
|
3117
|
+
throw new Error(
|
|
3118
|
+
`Invalid llm.provider: ${value}. Expected anthropic, google, openai, or openai-compatible.`
|
|
3119
|
+
);
|
|
3120
|
+
}
|
|
3121
|
+
|
|
3122
|
+
// src/continuations.ts
|
|
3123
|
+
import {
|
|
3124
|
+
createContinuationCursor,
|
|
3125
|
+
findArchiveObjects as findArchiveObjects2,
|
|
3126
|
+
findWikiGraphLibraryArchiveMembers,
|
|
3127
|
+
findWikiGraphLibraryObjects,
|
|
3128
|
+
listArchiveCollection as listArchiveCollection2,
|
|
3129
|
+
listArchiveEvidence as listArchiveEvidence2,
|
|
3130
|
+
listArchiveSourceLocators as listArchiveSourceLocators2,
|
|
3131
|
+
listRelatedArchiveObjects as listRelatedArchiveObjects2,
|
|
3132
|
+
listRelatedWikiGraphLibraryObjects,
|
|
3133
|
+
listWikiGraphLibraryArchiveMembers,
|
|
3134
|
+
listWikiGraphLibraryEvidence,
|
|
3135
|
+
listWikiGraphLibraryObjects,
|
|
3136
|
+
readContinuationCursor,
|
|
3137
|
+
resolveWikiGraphLibraryQueryTargetById,
|
|
3138
|
+
WikiGraphArchiveFile as WikiGraphArchiveFile2
|
|
3139
|
+
} from "wiki-graph-core";
|
|
3140
|
+
var WikiGraphContinuationManager = class {
|
|
3141
|
+
#runtime;
|
|
3142
|
+
constructor(runtime) {
|
|
3143
|
+
this.#runtime = runtime;
|
|
3144
|
+
}
|
|
3145
|
+
async next(options) {
|
|
3146
|
+
return await this.#runtime.run(
|
|
3147
|
+
async () => await this.#next(options),
|
|
3148
|
+
options.signal
|
|
3149
|
+
);
|
|
3150
|
+
}
|
|
3151
|
+
async create(context, cursor) {
|
|
3152
|
+
if (cursor === null || cursor === void 0) return null;
|
|
3153
|
+
return await this.#runtime.run(
|
|
3154
|
+
async () => await createContinuationCursor(
|
|
3155
|
+
createContinuationPayload(context, cursor)
|
|
3156
|
+
)
|
|
3157
|
+
);
|
|
3158
|
+
}
|
|
3159
|
+
async #next(options) {
|
|
3160
|
+
const cursor = await readContinuationCursor(options.cursor);
|
|
3161
|
+
const continuationCursor = options.format === void 0 ? cursor : { ...cursor, format: options.format };
|
|
3162
|
+
const limit = options.limit ?? 20;
|
|
3163
|
+
if (options.archive !== void 0) {
|
|
3164
|
+
const explicit = await resolveWikiGraphArchiveLocation(options.archive);
|
|
3165
|
+
const cursorArchivePath = getCursorArchivePath(cursor);
|
|
3166
|
+
if (explicit.archivePath !== cursorArchivePath) {
|
|
3167
|
+
throw new Error(
|
|
3168
|
+
`Continuation cursor ${options.cursor} belongs to ${cursorArchivePath}, not ${explicit.archivePath}.`
|
|
3169
|
+
);
|
|
3170
|
+
}
|
|
3171
|
+
}
|
|
3172
|
+
const result = cursor.indexScope.kind === "library-index" ? await continueLibraryCursor(cursor, limit) : await continueArchiveCursor(cursor, limit);
|
|
3173
|
+
const durableResult = await persistResultCursor(continuationCursor, result);
|
|
3174
|
+
return {
|
|
3175
|
+
cursor: continuationCursor,
|
|
3176
|
+
format: continuationCursor.format,
|
|
3177
|
+
kind: cursor.kind,
|
|
3178
|
+
limit,
|
|
3179
|
+
result: durableResult
|
|
3180
|
+
};
|
|
3181
|
+
}
|
|
3182
|
+
};
|
|
3183
|
+
async function continueArchiveCursor(cursor, limit) {
|
|
3184
|
+
const location = await resolveWikiGraphArchiveLocation(
|
|
3185
|
+
getCursorArchivePath(cursor)
|
|
3186
|
+
);
|
|
3187
|
+
return await new WikiGraphArchiveFile2(location.archiveFile).readDocument(
|
|
3188
|
+
async (document) => {
|
|
3189
|
+
switch (cursor.kind) {
|
|
3190
|
+
case "source-locators":
|
|
3191
|
+
return await listArchiveSourceLocators2(document, cursor.targetUri, {
|
|
3192
|
+
cursor: cursor.cursor,
|
|
3193
|
+
limit
|
|
3194
|
+
});
|
|
3195
|
+
case "collection":
|
|
3196
|
+
return await listArchiveCollection2(
|
|
3197
|
+
document,
|
|
3198
|
+
createCollectionOptions(cursor, limit)
|
|
3199
|
+
);
|
|
3200
|
+
case "search":
|
|
3201
|
+
return await findArchiveObjects2(
|
|
3202
|
+
document,
|
|
3203
|
+
cursor.query ?? "",
|
|
3204
|
+
await createFindOptions(cursor, limit)
|
|
3205
|
+
);
|
|
3206
|
+
case "evidence":
|
|
3207
|
+
return await listArchiveEvidence2(document, cursor.targetUri, {
|
|
3208
|
+
...cursor.chapters === null ? {} : { chapters: cursor.chapters },
|
|
3209
|
+
cursor: cursor.cursor,
|
|
3210
|
+
limit,
|
|
3211
|
+
order: cursor.order,
|
|
3212
|
+
...cursor.query === void 0 ? {} : { query: cursor.query },
|
|
3213
|
+
...cursor.skipUnindexed === void 0 ? {} : { skipUnindexed: cursor.skipUnindexed },
|
|
3214
|
+
...cursor.sourceContext === void 0 ? {} : { sourceContext: cursor.sourceContext }
|
|
3215
|
+
});
|
|
3216
|
+
case "related":
|
|
3217
|
+
return await listRelatedArchiveObjects2(document, cursor.targetUri, {
|
|
3218
|
+
...cursor.chapters === null ? {} : { chapters: cursor.chapters },
|
|
3219
|
+
cursor: cursor.cursor,
|
|
3220
|
+
...cursor.evidenceLimit === void 0 ? {} : { evidenceLimit: cursor.evidenceLimit },
|
|
3221
|
+
limit,
|
|
3222
|
+
order: cursor.order,
|
|
3223
|
+
...cursor.query === void 0 ? {} : { query: cursor.query },
|
|
3224
|
+
...cursor.role === void 0 ? {} : { role: cursor.role },
|
|
3225
|
+
...cursor.skipUnindexed === void 0 ? {} : { skipUnindexed: cursor.skipUnindexed },
|
|
3226
|
+
...cursor.sourceContext === void 0 ? {} : { sourceContext: cursor.sourceContext }
|
|
3227
|
+
});
|
|
3228
|
+
}
|
|
3229
|
+
}
|
|
3230
|
+
);
|
|
3231
|
+
}
|
|
3232
|
+
async function continueLibraryCursor(cursor, limit) {
|
|
3233
|
+
if (cursor.kind === "source-locators") {
|
|
3234
|
+
throw new Error(
|
|
3235
|
+
"Source locator cursors do not use the aggregate library index."
|
|
3236
|
+
);
|
|
3237
|
+
}
|
|
3238
|
+
if (cursor.indexScope.kind !== "library-index") {
|
|
3239
|
+
throw new Error("Internal error: expected a library index cursor.");
|
|
3240
|
+
}
|
|
3241
|
+
const target = await resolveWikiGraphLibraryQueryTargetById(
|
|
3242
|
+
cursor.indexScope.libraryId
|
|
3243
|
+
);
|
|
3244
|
+
switch (cursor.kind) {
|
|
3245
|
+
case "collection":
|
|
3246
|
+
return await (requireLibraryQuery(cursor) === "archive-members" ? listWikiGraphLibraryArchiveMembers(
|
|
3247
|
+
target,
|
|
3248
|
+
createCollectionOptions(cursor, limit)
|
|
3249
|
+
) : listWikiGraphLibraryObjects(
|
|
3250
|
+
target,
|
|
3251
|
+
createCollectionOptions(cursor, limit)
|
|
3252
|
+
));
|
|
3253
|
+
case "search":
|
|
3254
|
+
return await (requireLibraryQuery(cursor) === "archive-members" ? findWikiGraphLibraryArchiveMembers(
|
|
3255
|
+
target,
|
|
3256
|
+
cursor.query ?? "",
|
|
3257
|
+
await createFindOptions(cursor, limit)
|
|
3258
|
+
) : findWikiGraphLibraryObjects(
|
|
3259
|
+
target,
|
|
3260
|
+
cursor.query ?? "",
|
|
3261
|
+
await createFindOptions(cursor, limit)
|
|
3262
|
+
));
|
|
3263
|
+
case "evidence":
|
|
3264
|
+
return await listWikiGraphLibraryEvidence(target, cursor.targetUri, {
|
|
3265
|
+
cursor: cursor.cursor,
|
|
3266
|
+
limit,
|
|
3267
|
+
order: cursor.order,
|
|
3268
|
+
...cursor.query === void 0 ? {} : { query: cursor.query },
|
|
3269
|
+
...cursor.skipUnindexed === void 0 ? {} : { skipUnindexed: cursor.skipUnindexed },
|
|
3270
|
+
...cursor.sourceContext === void 0 ? {} : { sourceContext: cursor.sourceContext }
|
|
3271
|
+
});
|
|
3272
|
+
case "related":
|
|
3273
|
+
return await listRelatedWikiGraphLibraryObjects(
|
|
3274
|
+
target,
|
|
3275
|
+
cursor.targetUri,
|
|
3276
|
+
{
|
|
3277
|
+
cursor: cursor.cursor,
|
|
3278
|
+
...cursor.evidenceLimit === void 0 ? {} : { evidenceLimit: cursor.evidenceLimit },
|
|
3279
|
+
limit,
|
|
3280
|
+
order: cursor.order,
|
|
3281
|
+
...cursor.query === void 0 ? {} : { query: cursor.query },
|
|
3282
|
+
...cursor.role === void 0 ? {} : { role: cursor.role },
|
|
3283
|
+
...cursor.skipUnindexed === void 0 ? {} : { skipUnindexed: cursor.skipUnindexed },
|
|
3284
|
+
...cursor.sourceContext === void 0 ? {} : { sourceContext: cursor.sourceContext }
|
|
3285
|
+
}
|
|
3286
|
+
);
|
|
3287
|
+
}
|
|
3288
|
+
}
|
|
3289
|
+
function createCollectionOptions(cursor, limit) {
|
|
3290
|
+
return {
|
|
3291
|
+
...cursor.backlinks === void 0 ? {} : { backlinks: cursor.backlinks },
|
|
3292
|
+
...cursor.chapters === null ? {} : { chapters: cursor.chapters },
|
|
3293
|
+
cursor: cursor.cursor,
|
|
3294
|
+
...cursor.evidenceLimit === void 0 ? {} : { evidenceLimit: cursor.evidenceLimit },
|
|
3295
|
+
...cursor.ids === null ? {} : { ids: cursor.ids },
|
|
3296
|
+
limit,
|
|
3297
|
+
order: cursor.order,
|
|
3298
|
+
...cursor.sourceContext === void 0 ? {} : { sourceContext: cursor.sourceContext },
|
|
3299
|
+
...cursor.triplePattern === void 0 ? {} : { triplePattern: cursor.triplePattern },
|
|
3300
|
+
...cursor.types === null ? {} : {
|
|
3301
|
+
types: cursor.types
|
|
3302
|
+
}
|
|
3303
|
+
};
|
|
3304
|
+
}
|
|
3305
|
+
async function createFindOptions(cursor, limit) {
|
|
3306
|
+
const embeddingProvider = await createConfiguredEmbeddingProvider();
|
|
3307
|
+
return {
|
|
3308
|
+
archiveKey: cursor.archiveKey,
|
|
3309
|
+
...cursor.backlinks === void 0 ? {} : { backlinks: cursor.backlinks },
|
|
3310
|
+
...cursor.chapters == null ? {} : { chapters: cursor.chapters },
|
|
3311
|
+
cursor: cursor.cursor,
|
|
3312
|
+
...cursor.evidenceLimit === void 0 ? {} : { evidenceLimit: cursor.evidenceLimit },
|
|
3313
|
+
limit,
|
|
3314
|
+
...cursor.skipUnindexed === void 0 ? {} : { skipUnindexed: cursor.skipUnindexed },
|
|
3315
|
+
...cursor.sourceContext === void 0 ? {} : { sourceContext: cursor.sourceContext },
|
|
3316
|
+
...cursor.triplePattern === void 0 ? {} : { triplePattern: cursor.triplePattern },
|
|
3317
|
+
...cursor.types === null ? {} : { types: cursor.types },
|
|
3318
|
+
...embeddingProvider === void 0 ? {} : { embeddingProvider }
|
|
3319
|
+
};
|
|
3320
|
+
}
|
|
3321
|
+
function requireLibraryQuery(cursor) {
|
|
3322
|
+
if ((cursor.kind === "collection" || cursor.kind === "search") && cursor.libraryQuery !== void 0) {
|
|
3323
|
+
return cursor.libraryQuery;
|
|
3324
|
+
}
|
|
3325
|
+
throw new Error(
|
|
3326
|
+
"Library continuation cursor is missing its typed query variant."
|
|
3327
|
+
);
|
|
3328
|
+
}
|
|
3329
|
+
function getCursorArchivePath(cursor) {
|
|
3330
|
+
return cursor.indexScope.kind === "archive-index" ? cursor.indexScope.archivePath : cursor.archivePath;
|
|
3331
|
+
}
|
|
3332
|
+
async function persistResultCursor(cursor, result) {
|
|
3333
|
+
if (result.nextCursor === null) return result;
|
|
3334
|
+
const nextCursor = await createContinuationCursor({
|
|
3335
|
+
...cursor,
|
|
3336
|
+
cursor: result.nextCursor
|
|
3337
|
+
});
|
|
3338
|
+
return { ...result, nextCursor };
|
|
3339
|
+
}
|
|
3340
|
+
function createContinuationPayload(context, cursor) {
|
|
3341
|
+
switch (context.continuationKind ?? "search") {
|
|
3342
|
+
case "source-locators":
|
|
3343
|
+
return {
|
|
3344
|
+
archiveKey: context.archiveKey,
|
|
3345
|
+
archivePath: context.archivePath,
|
|
3346
|
+
cursor,
|
|
3347
|
+
format: context.format,
|
|
3348
|
+
indexScope: context.indexScope,
|
|
3349
|
+
kind: "source-locators",
|
|
3350
|
+
targetUri: requireTargetUri(context, "Source locator")
|
|
3351
|
+
};
|
|
3352
|
+
case "evidence":
|
|
3353
|
+
return {
|
|
3354
|
+
archiveKey: context.archiveKey,
|
|
3355
|
+
archivePath: context.archivePath,
|
|
3356
|
+
chapters: context.chapters ?? null,
|
|
3357
|
+
cursor,
|
|
3358
|
+
format: context.format,
|
|
3359
|
+
indexScope: context.indexScope,
|
|
3360
|
+
kind: "evidence",
|
|
3361
|
+
order: context.order ?? "doc-asc",
|
|
3362
|
+
...context.query === void 0 ? {} : { query: context.query },
|
|
3363
|
+
...context.skipUnindexed === void 0 ? {} : { skipUnindexed: context.skipUnindexed },
|
|
3364
|
+
...context.sourceContext === void 0 ? {} : { sourceContext: context.sourceContext },
|
|
3365
|
+
targetUri: requireTargetUri(context, "Evidence")
|
|
3366
|
+
};
|
|
3367
|
+
case "related":
|
|
3368
|
+
return {
|
|
3369
|
+
archiveKey: context.archiveKey,
|
|
3370
|
+
archivePath: context.archivePath,
|
|
3371
|
+
chapters: context.chapters ?? null,
|
|
3372
|
+
cursor,
|
|
3373
|
+
...context.evidenceLimit === void 0 ? {} : { evidenceLimit: context.evidenceLimit },
|
|
3374
|
+
format: context.format,
|
|
3375
|
+
indexScope: context.indexScope,
|
|
3376
|
+
kind: "related",
|
|
3377
|
+
order: context.order ?? "doc-asc",
|
|
3378
|
+
...context.query === void 0 ? {} : { query: context.query },
|
|
3379
|
+
...context.role === void 0 ? {} : { role: context.role },
|
|
3380
|
+
...context.skipUnindexed === void 0 ? {} : { skipUnindexed: context.skipUnindexed },
|
|
3381
|
+
...context.sourceContext === void 0 ? {} : { sourceContext: context.sourceContext },
|
|
3382
|
+
targetUri: requireTargetUri(context, "Related")
|
|
3383
|
+
};
|
|
3384
|
+
case "collection":
|
|
3385
|
+
return {
|
|
3386
|
+
archiveKey: context.archiveKey,
|
|
3387
|
+
archivePath: context.archivePath,
|
|
3388
|
+
...context.backlinks === void 0 ? {} : { backlinks: context.backlinks },
|
|
3389
|
+
chapters: context.chapters ?? null,
|
|
3390
|
+
cursor,
|
|
3391
|
+
...context.evidenceLimit === void 0 ? {} : { evidenceLimit: context.evidenceLimit },
|
|
3392
|
+
format: context.format,
|
|
3393
|
+
ids: context.ids ?? null,
|
|
3394
|
+
indexScope: context.indexScope,
|
|
3395
|
+
kind: "collection",
|
|
3396
|
+
...createLibraryQueryPayload(context),
|
|
3397
|
+
order: context.order ?? "doc-asc",
|
|
3398
|
+
...context.sourceContext === void 0 ? {} : { sourceContext: context.sourceContext },
|
|
3399
|
+
...context.triplePattern === void 0 ? {} : { triplePattern: context.triplePattern },
|
|
3400
|
+
types: context.types
|
|
3401
|
+
};
|
|
3402
|
+
case "search":
|
|
3403
|
+
return {
|
|
3404
|
+
archiveKey: context.archiveKey,
|
|
3405
|
+
archivePath: context.archivePath,
|
|
3406
|
+
...context.backlinks === void 0 ? {} : { backlinks: context.backlinks },
|
|
3407
|
+
...context.chapters === void 0 ? {} : { chapters: context.chapters },
|
|
3408
|
+
cursor,
|
|
3409
|
+
...context.evidenceLimit === void 0 ? {} : { evidenceLimit: context.evidenceLimit },
|
|
3410
|
+
format: context.format,
|
|
3411
|
+
indexScope: context.indexScope,
|
|
3412
|
+
kind: "search",
|
|
3413
|
+
...createLibraryQueryPayload(context),
|
|
3414
|
+
...context.query === void 0 ? {} : { query: context.query },
|
|
3415
|
+
...context.skipUnindexed === void 0 ? {} : { skipUnindexed: context.skipUnindexed },
|
|
3416
|
+
...context.sourceContext === void 0 ? {} : { sourceContext: context.sourceContext },
|
|
3417
|
+
...context.triplePattern === void 0 ? {} : { triplePattern: context.triplePattern },
|
|
3418
|
+
types: context.types
|
|
3419
|
+
};
|
|
3420
|
+
}
|
|
3421
|
+
}
|
|
3422
|
+
function requireTargetUri(context, label) {
|
|
3423
|
+
if (context.targetUri !== void 0) return context.targetUri;
|
|
3424
|
+
throw new Error(`${label} continuation cursors require a target URI.`);
|
|
3425
|
+
}
|
|
3426
|
+
function createLibraryQueryPayload(context) {
|
|
3427
|
+
if (context.indexScope.kind !== "library-index") return {};
|
|
3428
|
+
if (context.libraryQuery !== void 0) {
|
|
3429
|
+
return { libraryQuery: context.libraryQuery };
|
|
3430
|
+
}
|
|
3431
|
+
throw new Error(
|
|
3432
|
+
"Library collection and search continuations require an explicit query variant."
|
|
3433
|
+
);
|
|
3434
|
+
}
|
|
3435
|
+
|
|
3436
|
+
// src/default-worker.ts
|
|
3437
|
+
import {
|
|
3438
|
+
getBuildJob,
|
|
3439
|
+
getChapterDetails,
|
|
3440
|
+
openWikimediaResolver as openWikimediaResolver2,
|
|
3441
|
+
recordBuildJobInputRevision,
|
|
3442
|
+
WikiGraphArchiveFile as WikiGraphArchiveFile3,
|
|
3443
|
+
WikiGraphScope,
|
|
3444
|
+
withLoggingContext
|
|
3445
|
+
} from "wiki-graph-core";
|
|
3446
|
+
import {
|
|
3447
|
+
applyChapterJobArtifactFile as applyChapterJobArtifactFile2,
|
|
3448
|
+
createLocalChapterJobFileExecutor as createLocalChapterJobFileExecutor2,
|
|
3449
|
+
writeChapterJobInputFile as writeChapterJobInputFile2
|
|
3450
|
+
} from "wiki-graph-core/worker";
|
|
3451
|
+
|
|
3452
|
+
// src/stage.ts
|
|
3453
|
+
import {
|
|
3454
|
+
DEFAULT_EXTRACTION_PROMPT,
|
|
3455
|
+
DEFAULT_KNOWLEDGE_GRAPH_RECALL_PROMPT,
|
|
3456
|
+
resolveExtractionPrompt,
|
|
3457
|
+
resolveKnowledgeGraphRecallPrompt
|
|
3458
|
+
} from "wiki-graph-core";
|
|
3459
|
+
import { createDefaultWikiGraphSampling } from "wiki-graph-core";
|
|
3460
|
+
import { LLM } from "wiki-graph-core";
|
|
3461
|
+
function createStageLLM(config, options) {
|
|
3462
|
+
const llmOptions = buildWikiGraphLLMOptions(config);
|
|
3463
|
+
return new LLM({
|
|
3464
|
+
sampling: createDefaultWikiGraphSampling({
|
|
3465
|
+
...llmOptions.temperature === void 0 ? {} : { temperature: llmOptions.temperature },
|
|
3466
|
+
...llmOptions.topP === void 0 ? {} : { topP: llmOptions.topP }
|
|
3467
|
+
}),
|
|
3468
|
+
...llmOptions,
|
|
3469
|
+
...options?.cacheDirectory === void 0 ? {} : { cacheDirectory: options.cacheDirectory },
|
|
3470
|
+
...options?.logDirectory === void 0 ? {} : { logDirectory: options.logDirectory },
|
|
3471
|
+
...options?.onStreamProgress === void 0 ? {} : { onStreamProgress: options.onStreamProgress },
|
|
3472
|
+
...options?.onTokenUsage === void 0 ? {} : { onTokenUsage: options.onTokenUsage }
|
|
3473
|
+
});
|
|
3474
|
+
}
|
|
3475
|
+
async function loadRequiredStageConfig(options) {
|
|
3476
|
+
const config = await loadWikiGraphRuntimeConfig({
|
|
3477
|
+
...options.llmJSON === void 0 ? {} : { llmJSON: options.llmJSON }
|
|
3478
|
+
});
|
|
3479
|
+
if (config.llm?.provider === void 0 || config.llm.model === void 0) {
|
|
3480
|
+
throw new Error(
|
|
3481
|
+
"Missing LLM configuration. Set --llm for one run, or configure `wikg://local/config/llm` with provider and model."
|
|
3482
|
+
);
|
|
3483
|
+
}
|
|
3484
|
+
return config;
|
|
3485
|
+
}
|
|
3486
|
+
|
|
3487
|
+
// src/worker.ts
|
|
3488
|
+
import {
|
|
3489
|
+
runBuildJobWorker as runCoreBuildJobWorker
|
|
3490
|
+
} from "wiki-graph-core/worker";
|
|
3491
|
+
import {
|
|
3492
|
+
applyChapterJobArtifactFile,
|
|
3493
|
+
createLocalChapterJobFileExecutor,
|
|
3494
|
+
writeChapterJobInputFile
|
|
3495
|
+
} from "wiki-graph-core/worker";
|
|
3496
|
+
async function runBuildJobWorker(options) {
|
|
3497
|
+
installNodeWikiGraphPlatform();
|
|
3498
|
+
const { stateDir, ...coreOptions } = options;
|
|
3499
|
+
await withNodeWikiGraphStorage(
|
|
3500
|
+
stateDir,
|
|
3501
|
+
async () => await runCoreBuildJobWorker(coreOptions)
|
|
3502
|
+
);
|
|
3503
|
+
}
|
|
3504
|
+
|
|
3505
|
+
// src/default-worker.ts
|
|
3506
|
+
async function runWikiGraphQueueWorker(options = {}) {
|
|
3507
|
+
const config = await loadWikiGraphRuntimeConfig();
|
|
3508
|
+
await runBuildJobWorker({
|
|
3509
|
+
concurrency: config.concurrent?.job ?? DEFAULT_GENERATION_JOB_CONCURRENCY,
|
|
3510
|
+
executeJob: async (job, reporter, context) => {
|
|
3511
|
+
await withLoggingContext(
|
|
3512
|
+
{ logDirectory: job.log, operation: "build-job" },
|
|
3513
|
+
async () => await executeBuildJob(job, reporter, context)
|
|
3514
|
+
);
|
|
3515
|
+
},
|
|
3516
|
+
...options.signal === void 0 ? {} : { signal: options.signal }
|
|
3517
|
+
});
|
|
3518
|
+
}
|
|
3519
|
+
async function executeBuildJob(job, reporter, context) {
|
|
3520
|
+
const config = await loadWikiGraphRuntimeConfig({
|
|
3521
|
+
...job.llmJSON === void 0 ? {} : { llmJSON: job.llmJSON }
|
|
3522
|
+
});
|
|
3523
|
+
const execution = await openJobExecutor(
|
|
3524
|
+
job,
|
|
3525
|
+
config,
|
|
3526
|
+
context.signal,
|
|
3527
|
+
reporter
|
|
3528
|
+
);
|
|
3529
|
+
try {
|
|
3530
|
+
if (job.target === "reading-summary") {
|
|
3531
|
+
let stage = await readChapterStage(job);
|
|
3532
|
+
if (stage === "sourced") {
|
|
3533
|
+
await executeStep(job, "reading-graph", execution, reporter, context, {
|
|
3534
|
+
...execution.extractionPrompt === void 0 ? {} : { extractionPrompt: execution.extractionPrompt }
|
|
3535
|
+
});
|
|
3536
|
+
stage = await readChapterStage(job);
|
|
3537
|
+
}
|
|
3538
|
+
if (stage === "summarized") return;
|
|
3539
|
+
if (stage !== "graphed") {
|
|
3540
|
+
throw new Error(
|
|
3541
|
+
`Chapter ${job.chapterId} is ${stage}. Cannot generate summary.`
|
|
3542
|
+
);
|
|
3543
|
+
}
|
|
3544
|
+
await executeStep(job, "reading-summary", execution, reporter, context, {
|
|
3545
|
+
...job.prompt === void 0 ? {} : { prompt: job.prompt }
|
|
3546
|
+
});
|
|
3547
|
+
return;
|
|
3548
|
+
}
|
|
3549
|
+
if (job.target === "reading-graph") {
|
|
3550
|
+
const stage = await readChapterStage(job);
|
|
3551
|
+
if (stage === "graphed" || stage === "summarized") return;
|
|
3552
|
+
if (stage !== "sourced") {
|
|
3553
|
+
throw new Error(
|
|
3554
|
+
`Chapter ${job.chapterId} is ${stage}. Cannot generate Reading Graph.`
|
|
3555
|
+
);
|
|
3556
|
+
}
|
|
3557
|
+
}
|
|
3558
|
+
if (job.target === "knowledge-graph") {
|
|
3559
|
+
const stage = await readChapterStage(job);
|
|
3560
|
+
if (stage === "planned") {
|
|
3561
|
+
throw new Error(
|
|
3562
|
+
`Chapter ${job.chapterId} is planned. Set source before generating Knowledge Graph.`
|
|
3563
|
+
);
|
|
3564
|
+
}
|
|
3565
|
+
}
|
|
3566
|
+
await executeStep(job, job.target, execution, reporter, context, {
|
|
3567
|
+
...job.target === "reading-graph" ? { extractionPrompt: execution.extractionPrompt } : {},
|
|
3568
|
+
...job.target === "knowledge-graph" ? { policyPrompt: execution.knowledgeGraphPrompt } : {}
|
|
3569
|
+
});
|
|
3570
|
+
} finally {
|
|
3571
|
+
await execution.close();
|
|
3572
|
+
}
|
|
3573
|
+
}
|
|
3574
|
+
async function executeStep(job, kind, execution, reporter, context, inputOptions) {
|
|
3575
|
+
await reporter.throwIfStopped();
|
|
3576
|
+
await reporter.stepStarted(kind);
|
|
3577
|
+
if (kind === "reading-graph" || kind === "knowledge-graph") {
|
|
3578
|
+
await assertCurrentFtsArtifact(job);
|
|
3579
|
+
}
|
|
3580
|
+
const stepWorkspace = await resetStepWorkspace(job.workspace, kind);
|
|
3581
|
+
const inputFile = await stepWorkspace.createFile("input.jsonl");
|
|
3582
|
+
const outputWorkspace = await stepWorkspace.createDirectory("output");
|
|
3583
|
+
const revision = await new WikiGraphArchiveFile3(job.archive).readDocument(
|
|
3584
|
+
async (document) => {
|
|
3585
|
+
await getChapterDetails(document, job.chapterId);
|
|
3586
|
+
return await writeChapterJobInputFile2(
|
|
3587
|
+
document,
|
|
3588
|
+
job.chapterId,
|
|
3589
|
+
kind,
|
|
3590
|
+
inputFile,
|
|
3591
|
+
inputOptions
|
|
3592
|
+
);
|
|
3593
|
+
}
|
|
3594
|
+
);
|
|
3595
|
+
await recordBuildJobInputRevision({
|
|
3596
|
+
currentRevision: revision,
|
|
3597
|
+
jobId: job.jobId,
|
|
3598
|
+
ownerId: requireRunningJobOwnerId(job)
|
|
3599
|
+
});
|
|
3600
|
+
await reporter.updatePhase({
|
|
3601
|
+
done: 0,
|
|
3602
|
+
phase: kind.startsWith("index-") ? "indexing" : "grounding",
|
|
3603
|
+
total: 1,
|
|
3604
|
+
unit: "item"
|
|
3605
|
+
});
|
|
3606
|
+
const result = await execution.executor({
|
|
3607
|
+
inputFile,
|
|
3608
|
+
kind,
|
|
3609
|
+
progress: reporter,
|
|
3610
|
+
revision,
|
|
3611
|
+
signal: context.signal,
|
|
3612
|
+
workspace: outputWorkspace
|
|
3613
|
+
});
|
|
3614
|
+
if (result.revision !== revision) {
|
|
3615
|
+
throw new Error(
|
|
3616
|
+
`Chapter job returned revision ${result.revision}; expected ${revision}.`
|
|
3617
|
+
);
|
|
3618
|
+
}
|
|
3619
|
+
await reporter.updatePhase({
|
|
3620
|
+
done: 0,
|
|
3621
|
+
phase: "committing",
|
|
3622
|
+
total: 1,
|
|
3623
|
+
unit: "item"
|
|
3624
|
+
});
|
|
3625
|
+
await new WikiGraphArchiveFile3(job.archive).write(async (document) => {
|
|
3626
|
+
assertJobStillRunning(await getBuildJob(job.jobId));
|
|
3627
|
+
await applyChapterJobArtifactFile2(
|
|
3628
|
+
document,
|
|
3629
|
+
job.chapterId,
|
|
3630
|
+
kind,
|
|
3631
|
+
revision,
|
|
3632
|
+
result.artifactFile
|
|
3633
|
+
);
|
|
3634
|
+
});
|
|
3635
|
+
await reporter.updatePhase({
|
|
3636
|
+
done: 1,
|
|
3637
|
+
phase: "committing",
|
|
3638
|
+
total: 1,
|
|
3639
|
+
unit: "item"
|
|
3640
|
+
});
|
|
3641
|
+
await reporter.stepCompleted(kind);
|
|
3642
|
+
assertJobStillRunning(await getBuildJob(job.jobId));
|
|
3643
|
+
}
|
|
3644
|
+
async function openJobExecutor(job, config, signal, reporter) {
|
|
3645
|
+
const generation = !job.target.startsWith("index-");
|
|
3646
|
+
const stageConfig = generation ? await loadRequiredStageConfig({
|
|
3647
|
+
...job.llmJSON === void 0 ? {} : { llmJSON: job.llmJSON }
|
|
3648
|
+
}) : void 0;
|
|
3649
|
+
const llm = stageConfig === void 0 ? void 0 : createStageLLM(stageConfig, {
|
|
3650
|
+
cacheDirectory: job.cache,
|
|
3651
|
+
logDirectory: job.log,
|
|
3652
|
+
onTokenUsage: async (usage) => {
|
|
3653
|
+
await reporter.addTokenUsage(usage);
|
|
3654
|
+
}
|
|
3655
|
+
});
|
|
3656
|
+
const promptSource = job.prompt ?? stageConfig?.prompt;
|
|
3657
|
+
const wikispine = job.target === "knowledge-graph" ? requireKnowledgeGraphWikispineConfig(config) : void 0;
|
|
3658
|
+
const request = llm === void 0 ? void 0 : async (messages, index, maxRetries) => await llm.request(messages, {
|
|
3659
|
+
retryIndex: index,
|
|
3660
|
+
retryMax: maxRetries,
|
|
3661
|
+
scope: WikiGraphScope.ReaderExtraction,
|
|
3662
|
+
signal
|
|
3663
|
+
});
|
|
3664
|
+
if (request !== void 0 && llm !== void 0) {
|
|
3665
|
+
request.lazy = async (operation) => await llm.request(async () => await operation(request));
|
|
3666
|
+
}
|
|
3667
|
+
const wikimediaResolver = job.target !== "knowledge-graph" ? void 0 : await openWikimediaResolver2(
|
|
3668
|
+
config.wikimedia === void 0 ? {
|
|
3669
|
+
kind: "local",
|
|
3670
|
+
llmRequest: requireValue(
|
|
3671
|
+
request,
|
|
3672
|
+
"Local Wikimedia resolution requires an LLM."
|
|
3673
|
+
)
|
|
3674
|
+
} : {
|
|
3675
|
+
endpoint: config.wikimedia.endpoint,
|
|
3676
|
+
kind: "remote",
|
|
3677
|
+
token: config.wikimedia.token
|
|
3678
|
+
}
|
|
3679
|
+
);
|
|
3680
|
+
const embeddingProvider = job.target === "index-embedding-source" || job.target === "index-embedding-summary" ? buildSearchIndexEmbeddingProvider(requireEmbeddingConfig(config)) : void 0;
|
|
3681
|
+
return {
|
|
3682
|
+
async close() {
|
|
3683
|
+
await wikimediaResolver?.close();
|
|
3684
|
+
},
|
|
3685
|
+
executor: createLocalChapterJobFileExecutor2({
|
|
3686
|
+
...embeddingProvider === void 0 ? {} : { embeddingProvider },
|
|
3687
|
+
...llm === void 0 ? {} : { llm },
|
|
3688
|
+
...wikimediaResolver === void 0 ? {} : { wikimediaResolver },
|
|
3689
|
+
...wikispine === void 0 ? {} : {
|
|
3690
|
+
wikispine: {
|
|
3691
|
+
...wikispine,
|
|
3692
|
+
...wikispine.provider === "cli" ? { commandRunner: nodeWikispineCommandRunner } : {}
|
|
3693
|
+
}
|
|
3694
|
+
}
|
|
3695
|
+
}),
|
|
3696
|
+
...promptSource === void 0 ? {} : { extractionPrompt: resolveExtractionPrompt(promptSource) },
|
|
3697
|
+
...promptSource === void 0 ? {} : {
|
|
3698
|
+
knowledgeGraphPrompt: resolveKnowledgeGraphRecallPrompt(promptSource)
|
|
3699
|
+
}
|
|
3700
|
+
};
|
|
3701
|
+
}
|
|
3702
|
+
async function resetStepWorkspace(workspace, kind) {
|
|
3703
|
+
const name = `chapter-job-${kind}`;
|
|
3704
|
+
if (await workspace.getDirectory(name) !== void 0) {
|
|
3705
|
+
await workspace.remove(name, { recursive: true });
|
|
3706
|
+
}
|
|
3707
|
+
return await workspace.createDirectory(name);
|
|
3708
|
+
}
|
|
3709
|
+
async function readChapterStage(job) {
|
|
3710
|
+
return await new WikiGraphArchiveFile3(job.archive).readDocument(
|
|
3711
|
+
async (document) => (await getChapterDetails(document, job.chapterId)).stage
|
|
3712
|
+
);
|
|
3713
|
+
}
|
|
3714
|
+
async function assertCurrentFtsArtifact(job) {
|
|
3715
|
+
await new WikiGraphArchiveFile3(job.archive).readDocument(async (document) => {
|
|
3716
|
+
const revision = await document.serials.getRevision(job.chapterId);
|
|
3717
|
+
const artifact = await document.indexArtifacts.get(job.chapterId, "fts");
|
|
3718
|
+
if (artifact?.sourceRevision !== revision) {
|
|
3719
|
+
throw new Error(
|
|
3720
|
+
`Chapter ${job.chapterId} needs a current FTS index artifact before running ${job.target}.`
|
|
3721
|
+
);
|
|
3722
|
+
}
|
|
3723
|
+
});
|
|
3724
|
+
}
|
|
3725
|
+
function requireEmbeddingConfig(config) {
|
|
3726
|
+
if (config.embedding !== void 0) return config.embedding;
|
|
3727
|
+
throw new Error(
|
|
3728
|
+
"Missing embeddings configuration. Configure `wikg://local/config/embeddings` before building embedding index artifacts."
|
|
3729
|
+
);
|
|
3730
|
+
}
|
|
3731
|
+
function requireKnowledgeGraphWikispineConfig(config) {
|
|
3732
|
+
if (config.wikispine?.provider !== void 0) return config.wikispine;
|
|
3733
|
+
throw new Error(
|
|
3734
|
+
"Knowledge Graph requires WikiSpine. Configure `wikg://local/config/wikispine` with provider `cli` or `fetch` and test the provider before running the worker."
|
|
3735
|
+
);
|
|
3736
|
+
}
|
|
3737
|
+
function requireRunningJobOwnerId(job) {
|
|
3738
|
+
if (job.ownerId === void 0) {
|
|
3739
|
+
throw new Error(`Build job ${job.jobId} has no running owner.`);
|
|
3740
|
+
}
|
|
3741
|
+
return job.ownerId;
|
|
3742
|
+
}
|
|
3743
|
+
function assertJobStillRunning(job) {
|
|
3744
|
+
if (job.state !== "running") {
|
|
3745
|
+
throw new Error(`Job ${job.jobId} is ${job.state}. Stop before flushing.`);
|
|
3746
|
+
}
|
|
3747
|
+
}
|
|
3748
|
+
function requireValue(value, message) {
|
|
3749
|
+
if (value === void 0) throw new Error(message);
|
|
3750
|
+
return value;
|
|
3751
|
+
}
|
|
3752
|
+
|
|
3753
|
+
// src/entry-context.ts
|
|
3754
|
+
var DANGEROUS_RUNTIME_ENV_NAMES = [
|
|
3755
|
+
"WIKIGRAPH_DEV",
|
|
3756
|
+
"WIKIGRAPH_ENV_POLICY",
|
|
3757
|
+
"WIKIGRAPH_QUEUE_DISABLE_AUTOSTART",
|
|
3758
|
+
"WIKIGRAPH_STATE_DIR"
|
|
3759
|
+
];
|
|
3760
|
+
function createEntryRuntimeContext(options) {
|
|
3761
|
+
const environment = createMergedEnvironment(options.env);
|
|
3762
|
+
if (options.envPolicy === "production") {
|
|
3763
|
+
const blocked = DANGEROUS_RUNTIME_ENV_NAMES.filter(
|
|
3764
|
+
(name) => environment[name] !== void 0
|
|
3765
|
+
);
|
|
3766
|
+
if (blocked.length > 0) {
|
|
3767
|
+
throw new Error(
|
|
3768
|
+
`Wiki Graph production entries do not support runtime env overrides: ${blocked.join(", ")}. Use the development entry or explicit SDK options instead.`
|
|
3769
|
+
);
|
|
3770
|
+
}
|
|
3771
|
+
return {
|
|
3772
|
+
env: environment,
|
|
3773
|
+
envPolicy: options.envPolicy,
|
|
3774
|
+
stateDir: options.stateDir
|
|
3775
|
+
};
|
|
3776
|
+
}
|
|
3777
|
+
const envStateDir = environment.WIKIGRAPH_DEV ?? environment.WIKIGRAPH_STATE_DIR;
|
|
3778
|
+
const stateDir = normalizeOptionalPath(options.stateDir ?? envStateDir);
|
|
3779
|
+
if (stateDir === void 0) {
|
|
3780
|
+
throw new Error(
|
|
3781
|
+
"Wiki Graph development entries require an explicit runtime state directory."
|
|
3782
|
+
);
|
|
3783
|
+
}
|
|
3784
|
+
const devProjectRoot = normalizeOptionalPath(options.devProjectRoot);
|
|
3785
|
+
if (devProjectRoot === void 0) {
|
|
3786
|
+
throw new Error(
|
|
3787
|
+
"Wiki Graph development entries require an explicit project root."
|
|
3788
|
+
);
|
|
3789
|
+
}
|
|
3790
|
+
return {
|
|
3791
|
+
devProjectRoot,
|
|
3792
|
+
env: environment,
|
|
3793
|
+
envPolicy: options.envPolicy,
|
|
3794
|
+
stateDir
|
|
3795
|
+
};
|
|
3796
|
+
}
|
|
3797
|
+
function createMergedEnvironment(environment) {
|
|
3798
|
+
if (environment === void 0) {
|
|
3799
|
+
return { ...process.env };
|
|
3800
|
+
}
|
|
3801
|
+
return {
|
|
3802
|
+
...process.env,
|
|
3803
|
+
...environment
|
|
3804
|
+
};
|
|
3805
|
+
}
|
|
3806
|
+
function normalizeOptionalPath(path) {
|
|
3807
|
+
if (path === void 0 || path.trim() === "") {
|
|
3808
|
+
return void 0;
|
|
3809
|
+
}
|
|
3810
|
+
return path;
|
|
3811
|
+
}
|
|
3812
|
+
|
|
3813
|
+
// src/jobs.ts
|
|
3814
|
+
import {
|
|
3815
|
+
addBuildJob,
|
|
3816
|
+
boostBuildJob,
|
|
3817
|
+
cancelBuildJob,
|
|
3818
|
+
cleanBuildJobs,
|
|
3819
|
+
formatLocatedChapterUri,
|
|
3820
|
+
getBuildJob as getBuildJob2,
|
|
3821
|
+
listBuildJobs,
|
|
3822
|
+
listChapters as listChapters3,
|
|
3823
|
+
pauseBuildJob,
|
|
3824
|
+
readBuildJobEvents,
|
|
3825
|
+
resolveBuildJobId,
|
|
3826
|
+
resumeBuildJob,
|
|
3827
|
+
updateBuildJobTarget,
|
|
3828
|
+
resolveChapterPathReadonly as resolveChapterPathReadonly2,
|
|
3829
|
+
WikiGraphArchiveFile as WikiGraphArchiveFile4
|
|
3830
|
+
} from "wiki-graph-core";
|
|
3831
|
+
var TERMINAL_STATES = /* @__PURE__ */ new Set(["canceled", "failed", "succeeded"]);
|
|
3832
|
+
var TERMINAL_EVENT_GRACE_READS = 3;
|
|
3833
|
+
var CORE_BUILD_JOB_BACKEND = {
|
|
3834
|
+
add: async (options) => await addBuildJob(options),
|
|
3835
|
+
boost: async (jobId) => await boostBuildJob(jobId),
|
|
3836
|
+
cancel: async (jobId) => await cancelBuildJob(jobId),
|
|
3837
|
+
get: async (jobId) => await getBuildJob2(jobId),
|
|
3838
|
+
list: async (options) => await listBuildJobs(options),
|
|
3839
|
+
pause: async (jobId) => await pauseBuildJob(jobId),
|
|
3840
|
+
readEventChunk: async (job, cursor) => await readIncrementalBuildJobEvents(job, cursor),
|
|
3841
|
+
readEvents: async (job) => await readBuildJobEvents(job),
|
|
3842
|
+
resume: async (jobId) => await resumeBuildJob(jobId),
|
|
3843
|
+
setTarget: async (jobId, target) => await updateBuildJobTarget(jobId, target)
|
|
3844
|
+
};
|
|
3845
|
+
async function readIncrementalBuildJobEvents(job, cursor) {
|
|
3846
|
+
let reader;
|
|
3847
|
+
try {
|
|
3848
|
+
reader = await job.events.openReader();
|
|
3849
|
+
} catch {
|
|
3850
|
+
return { cursor, events: await readBuildJobEvents(job) };
|
|
3851
|
+
}
|
|
3852
|
+
try {
|
|
3853
|
+
const offset = cursor <= reader.size ? cursor : 0;
|
|
3854
|
+
const content = new TextDecoder().decode(
|
|
3855
|
+
await reader.read(offset, reader.size - offset)
|
|
3856
|
+
);
|
|
3857
|
+
return {
|
|
3858
|
+
cursor: reader.size,
|
|
3859
|
+
events: content.split("\n").filter((line) => line.trim() !== "").map((line) => JSON.parse(line))
|
|
3860
|
+
};
|
|
3861
|
+
} finally {
|
|
3862
|
+
await reader.close();
|
|
3863
|
+
}
|
|
3864
|
+
}
|
|
3865
|
+
var WikiGraphJobManager = class {
|
|
3866
|
+
#runtime;
|
|
3867
|
+
#backend;
|
|
3868
|
+
#subscriptions = /* @__PURE__ */ new Set();
|
|
3869
|
+
constructor(runtime, backend = CORE_BUILD_JOB_BACKEND) {
|
|
3870
|
+
this.#runtime = runtime;
|
|
3871
|
+
this.#backend = backend;
|
|
3872
|
+
}
|
|
3873
|
+
async create(options) {
|
|
3874
|
+
const job = await this.#runtime.run(
|
|
3875
|
+
async () => await this.#backend.add({
|
|
3876
|
+
...options,
|
|
3877
|
+
archive: typeof options.archive === "string" ? new NodeFile(resolveWikiGraphRuntimePath(options.archive)) : options.archive
|
|
3878
|
+
})
|
|
3879
|
+
);
|
|
3880
|
+
return this.#createHandle(job);
|
|
3881
|
+
}
|
|
3882
|
+
async clean() {
|
|
3883
|
+
return await this.#runtime.run(async () => await cleanBuildJobs());
|
|
3884
|
+
}
|
|
3885
|
+
async get(jobId) {
|
|
3886
|
+
return this.#createHandle(
|
|
3887
|
+
await this.#runtime.run(async () => await this.#backend.get(jobId))
|
|
3888
|
+
);
|
|
3889
|
+
}
|
|
3890
|
+
async resolveId(reference) {
|
|
3891
|
+
return await this.#runtime.run(
|
|
3892
|
+
async () => await resolveBuildJobId(reference)
|
|
3893
|
+
);
|
|
3894
|
+
}
|
|
3895
|
+
async list(options = {}) {
|
|
3896
|
+
const jobs = await this.#runtime.run(async () => {
|
|
3897
|
+
const normalized = {
|
|
3898
|
+
...options.activeOnly === void 0 ? {} : { activeOnly: options.activeOnly },
|
|
3899
|
+
...options.all === void 0 ? {} : { all: options.all },
|
|
3900
|
+
...options.archive === void 0 ? {} : {
|
|
3901
|
+
archive: typeof options.archive === "string" ? new NodeFile(resolveWikiGraphRuntimePath(options.archive)) : options.archive
|
|
3902
|
+
}
|
|
3903
|
+
};
|
|
3904
|
+
return await this.#backend.list(normalized);
|
|
3905
|
+
});
|
|
3906
|
+
return jobs.map((job) => this.#createHandle(job));
|
|
3907
|
+
}
|
|
3908
|
+
async resolveChapters(jobs) {
|
|
3909
|
+
return await this.#runtime.run(async () => {
|
|
3910
|
+
const jobsByArchive = /* @__PURE__ */ new Map();
|
|
3911
|
+
for (const job of jobs) {
|
|
3912
|
+
const archivePath = getBuildJobArchivePath(job);
|
|
3913
|
+
const grouped = jobsByArchive.get(archivePath) ?? [];
|
|
3914
|
+
grouped.push(job);
|
|
3915
|
+
jobsByArchive.set(archivePath, grouped);
|
|
3916
|
+
}
|
|
3917
|
+
const entries = (await Promise.all(
|
|
3918
|
+
[...jobsByArchive].map(async ([archivePath, archiveJobs]) => {
|
|
3919
|
+
try {
|
|
3920
|
+
const chapters = await new WikiGraphArchiveFile4(
|
|
3921
|
+
new NodeFile(archivePath)
|
|
3922
|
+
).readDocument(async (document) => await listChapters3(document));
|
|
3923
|
+
const chaptersById = new Map(
|
|
3924
|
+
chapters.map((chapter) => [chapter.chapterId, chapter])
|
|
3925
|
+
);
|
|
3926
|
+
return archiveJobs.flatMap((job) => {
|
|
3927
|
+
const chapter = chaptersById.get(job.chapterId);
|
|
3928
|
+
return chapter === void 0 ? [] : [
|
|
3929
|
+
[
|
|
3930
|
+
job.jobId,
|
|
3931
|
+
{
|
|
3932
|
+
locatedUri: formatLocatedChapterUri(
|
|
3933
|
+
archivePath,
|
|
3934
|
+
chapter.path
|
|
3935
|
+
),
|
|
3936
|
+
title: chapter.title,
|
|
3937
|
+
uri: chapter.uri
|
|
3938
|
+
}
|
|
3939
|
+
]
|
|
3940
|
+
];
|
|
3941
|
+
});
|
|
3942
|
+
} catch {
|
|
3943
|
+
return [];
|
|
3944
|
+
}
|
|
3945
|
+
})
|
|
3946
|
+
)).flat();
|
|
3947
|
+
return new Map(entries);
|
|
3948
|
+
});
|
|
3949
|
+
}
|
|
3950
|
+
async resolveChapter(job) {
|
|
3951
|
+
return (await this.resolveChapters([job])).get(job.jobId);
|
|
3952
|
+
}
|
|
3953
|
+
async enqueue(options) {
|
|
3954
|
+
return await this.#runtime.run(async () => {
|
|
3955
|
+
const location = await resolveWikiGraphArchiveLocation(options.archive);
|
|
3956
|
+
const target = options.target ?? "reading-summary";
|
|
3957
|
+
await validateQueueTargetConfig(target, options.llmJSON);
|
|
3958
|
+
const selectedExplicitly = options.chapterId !== void 0 || options.chapterIds !== void 0 || options.chapterPath !== void 0 || options.depth !== void 0;
|
|
3959
|
+
const candidates = await new WikiGraphArchiveFile4(
|
|
3960
|
+
location.archiveFile
|
|
3961
|
+
).readDocument(async (document) => {
|
|
3962
|
+
const chapters = await listChapters3(document);
|
|
3963
|
+
const selected = await selectQueueChapters(document, chapters, options);
|
|
3964
|
+
const checked = await Promise.all(
|
|
3965
|
+
selected.map(async (chapter) => ({
|
|
3966
|
+
chapter,
|
|
3967
|
+
reason: await readQueueReadinessReason(document, chapter, target)
|
|
3968
|
+
}))
|
|
3969
|
+
);
|
|
3970
|
+
return checked;
|
|
3971
|
+
});
|
|
3972
|
+
const created = [];
|
|
3973
|
+
const skipped = [];
|
|
3974
|
+
for (const candidate of candidates) {
|
|
3975
|
+
if (candidate.reason !== void 0) {
|
|
3976
|
+
if (selectedExplicitly && candidates.length === 1) {
|
|
3977
|
+
throw new Error(
|
|
3978
|
+
formatQueueReadinessError(
|
|
3979
|
+
candidate.chapter,
|
|
3980
|
+
target,
|
|
3981
|
+
candidate.reason
|
|
3982
|
+
)
|
|
3983
|
+
);
|
|
3984
|
+
}
|
|
3985
|
+
skipped.push({
|
|
3986
|
+
chapter: candidate.chapter,
|
|
3987
|
+
reason: candidate.reason
|
|
3988
|
+
});
|
|
3989
|
+
continue;
|
|
3990
|
+
}
|
|
3991
|
+
try {
|
|
3992
|
+
created.push({
|
|
3993
|
+
chapter: candidate.chapter,
|
|
3994
|
+
job: await this.create({
|
|
3995
|
+
archive: location.archiveFile,
|
|
3996
|
+
boost: options.boost ?? false,
|
|
3997
|
+
chapterId: candidate.chapter.chapterId,
|
|
3998
|
+
...options.llmJSON === void 0 ? {} : { llmJSON: options.llmJSON },
|
|
3999
|
+
...options.prompt === void 0 ? {} : { prompt: options.prompt },
|
|
4000
|
+
target
|
|
4001
|
+
})
|
|
4002
|
+
});
|
|
4003
|
+
} catch (error) {
|
|
4004
|
+
if (selectedExplicitly && candidates.length === 1) throw error;
|
|
4005
|
+
skipped.push({
|
|
4006
|
+
chapter: candidate.chapter,
|
|
4007
|
+
reason: error instanceof Error ? error.message : String(error)
|
|
4008
|
+
});
|
|
4009
|
+
}
|
|
4010
|
+
}
|
|
4011
|
+
return { created, skipped };
|
|
4012
|
+
});
|
|
4013
|
+
}
|
|
4014
|
+
async planEnqueue(options) {
|
|
4015
|
+
return await this.#runtime.run(async () => {
|
|
4016
|
+
const location = await resolveWikiGraphArchiveLocation(options.archive);
|
|
4017
|
+
const target = options.target ?? "reading-summary";
|
|
4018
|
+
await validateQueueTargetConfig(target, options.llmJSON);
|
|
4019
|
+
const selectedExplicitly = options.chapterId !== void 0 || options.chapterIds !== void 0 || options.chapterPath !== void 0 || options.depth !== void 0;
|
|
4020
|
+
const candidates = await new WikiGraphArchiveFile4(
|
|
4021
|
+
location.archiveFile
|
|
4022
|
+
).readDocument(async (document) => {
|
|
4023
|
+
const chapters = await listChapters3(document);
|
|
4024
|
+
const selected = await selectQueueChapters(document, chapters, options);
|
|
4025
|
+
return await Promise.all(
|
|
4026
|
+
selected.map(async (chapter) => ({
|
|
4027
|
+
chapter,
|
|
4028
|
+
reason: await readQueueReadinessReason(document, chapter, target)
|
|
4029
|
+
}))
|
|
4030
|
+
);
|
|
4031
|
+
});
|
|
4032
|
+
if (selectedExplicitly && candidates.length === 1 && candidates[0]?.reason !== void 0) {
|
|
4033
|
+
throw new Error(
|
|
4034
|
+
formatQueueReadinessError(
|
|
4035
|
+
candidates[0].chapter,
|
|
4036
|
+
target,
|
|
4037
|
+
candidates[0].reason
|
|
4038
|
+
)
|
|
4039
|
+
);
|
|
4040
|
+
}
|
|
4041
|
+
return {
|
|
4042
|
+
ready: candidates.filter((item) => item.reason === void 0).map((item) => item.chapter),
|
|
4043
|
+
skipped: candidates.flatMap(
|
|
4044
|
+
(item) => item.reason === void 0 ? [] : [{ chapter: item.chapter, reason: item.reason }]
|
|
4045
|
+
)
|
|
4046
|
+
};
|
|
4047
|
+
});
|
|
4048
|
+
}
|
|
4049
|
+
close() {
|
|
4050
|
+
for (const controller of this.#subscriptions) controller.abort();
|
|
4051
|
+
this.#subscriptions.clear();
|
|
4052
|
+
}
|
|
4053
|
+
trackSubscription(controller) {
|
|
4054
|
+
this.#subscriptions.add(controller);
|
|
4055
|
+
return () => this.#subscriptions.delete(controller);
|
|
4056
|
+
}
|
|
4057
|
+
#createHandle(snapshot) {
|
|
4058
|
+
return new WikiGraphJob(this, this.#runtime, this.#backend, snapshot);
|
|
4059
|
+
}
|
|
4060
|
+
};
|
|
4061
|
+
function getBuildJobArchivePath(job) {
|
|
4062
|
+
const record = job;
|
|
4063
|
+
if (record.archive !== void 0) {
|
|
4064
|
+
return getNodeResourcePath(record.archive);
|
|
4065
|
+
}
|
|
4066
|
+
if (typeof record.archivePath === "string") return record.archivePath;
|
|
4067
|
+
throw new TypeError("Build job is missing archive");
|
|
4068
|
+
}
|
|
4069
|
+
var WikiGraphJob = class {
|
|
4070
|
+
#manager;
|
|
4071
|
+
#runtime;
|
|
4072
|
+
#backend;
|
|
4073
|
+
#snapshot;
|
|
4074
|
+
constructor(manager, runtime, backend, snapshot) {
|
|
4075
|
+
this.#manager = manager;
|
|
4076
|
+
this.#runtime = runtime;
|
|
4077
|
+
this.#backend = backend;
|
|
4078
|
+
this.#snapshot = snapshot;
|
|
4079
|
+
}
|
|
4080
|
+
get id() {
|
|
4081
|
+
return this.#snapshot.jobId;
|
|
4082
|
+
}
|
|
4083
|
+
get snapshot() {
|
|
4084
|
+
return this.#snapshot;
|
|
4085
|
+
}
|
|
4086
|
+
async status() {
|
|
4087
|
+
return await this.#update(async () => await this.#backend.get(this.id));
|
|
4088
|
+
}
|
|
4089
|
+
async pause() {
|
|
4090
|
+
return await this.#update(async () => await this.#backend.pause(this.id));
|
|
4091
|
+
}
|
|
4092
|
+
async resume() {
|
|
4093
|
+
return await this.#update(async () => await this.#backend.resume(this.id));
|
|
4094
|
+
}
|
|
4095
|
+
async cancel() {
|
|
4096
|
+
return await this.#update(async () => await this.#backend.cancel(this.id));
|
|
4097
|
+
}
|
|
4098
|
+
async boost() {
|
|
4099
|
+
return await this.#update(async () => await this.#backend.boost(this.id));
|
|
4100
|
+
}
|
|
4101
|
+
async setTarget(target) {
|
|
4102
|
+
return await this.#update(
|
|
4103
|
+
async () => await this.#backend.setTarget(this.id, target)
|
|
4104
|
+
);
|
|
4105
|
+
}
|
|
4106
|
+
async *events(options = {}) {
|
|
4107
|
+
let seenSeq = 0;
|
|
4108
|
+
let cursor = 0;
|
|
4109
|
+
let job = this.#snapshot;
|
|
4110
|
+
if (options.from === "now") {
|
|
4111
|
+
job = await this.status();
|
|
4112
|
+
const chunk = await this.#readEventChunkSafely(job, cursor);
|
|
4113
|
+
const events = chunk.events;
|
|
4114
|
+
cursor = chunk.cursor;
|
|
4115
|
+
seenSeq = events.at(-1)?.seq ?? 0;
|
|
4116
|
+
if (isTerminalState(job.state)) return;
|
|
4117
|
+
}
|
|
4118
|
+
let terminalReadsWithoutEvent = 0;
|
|
4119
|
+
while (options.signal?.aborted !== true) {
|
|
4120
|
+
const chunk = await this.#readEventChunkSafely(job, cursor);
|
|
4121
|
+
const events = chunk.events;
|
|
4122
|
+
cursor = chunk.cursor;
|
|
4123
|
+
let sawTerminalEvent = false;
|
|
4124
|
+
for (const event of events) {
|
|
4125
|
+
if (event.seq <= seenSeq) continue;
|
|
4126
|
+
seenSeq = event.seq;
|
|
4127
|
+
sawTerminalEvent ||= isTerminalEvent(event);
|
|
4128
|
+
yield event;
|
|
4129
|
+
}
|
|
4130
|
+
if (sawTerminalEvent) return;
|
|
4131
|
+
job = await this.status();
|
|
4132
|
+
if (isTerminalState(job.state)) {
|
|
4133
|
+
terminalReadsWithoutEvent += 1;
|
|
4134
|
+
if (terminalReadsWithoutEvent >= TERMINAL_EVENT_GRACE_READS) return;
|
|
4135
|
+
} else {
|
|
4136
|
+
terminalReadsWithoutEvent = 0;
|
|
4137
|
+
}
|
|
4138
|
+
await wait(options.pollIntervalMs ?? 1e3, options.signal);
|
|
4139
|
+
}
|
|
4140
|
+
}
|
|
4141
|
+
subscribe(listener, options = {}) {
|
|
4142
|
+
const controller = new AbortController();
|
|
4143
|
+
const untrack = this.#manager.trackSubscription(controller);
|
|
4144
|
+
const stopForwarding = forwardAbort(options.signal, controller);
|
|
4145
|
+
void (async () => {
|
|
4146
|
+
try {
|
|
4147
|
+
for await (const event of this.events({
|
|
4148
|
+
...options,
|
|
4149
|
+
signal: controller.signal
|
|
4150
|
+
})) {
|
|
4151
|
+
try {
|
|
4152
|
+
await listener(event);
|
|
4153
|
+
} catch (error) {
|
|
4154
|
+
options.onError?.(error);
|
|
4155
|
+
}
|
|
4156
|
+
}
|
|
4157
|
+
} catch (error) {
|
|
4158
|
+
if (!controller.signal.aborted) options.onError?.(error);
|
|
4159
|
+
} finally {
|
|
4160
|
+
stopForwarding();
|
|
4161
|
+
untrack();
|
|
4162
|
+
}
|
|
4163
|
+
})();
|
|
4164
|
+
return () => controller.abort();
|
|
4165
|
+
}
|
|
4166
|
+
async #update(operation) {
|
|
4167
|
+
this.#snapshot = await this.#runtime.run(operation);
|
|
4168
|
+
return this.#snapshot;
|
|
4169
|
+
}
|
|
4170
|
+
async #readEventChunk(job, cursor) {
|
|
4171
|
+
return await this.#runtime.run(async () => {
|
|
4172
|
+
if (this.#backend.readEventChunk !== void 0) {
|
|
4173
|
+
return await this.#backend.readEventChunk(job, cursor);
|
|
4174
|
+
}
|
|
4175
|
+
const events = await this.#backend.readEvents?.(job);
|
|
4176
|
+
return { cursor: 0, events: events ?? [] };
|
|
4177
|
+
});
|
|
4178
|
+
}
|
|
4179
|
+
async #readEventChunkSafely(job, cursor) {
|
|
4180
|
+
try {
|
|
4181
|
+
return await this.#readEventChunk(job, cursor);
|
|
4182
|
+
} catch {
|
|
4183
|
+
return { cursor, events: [] };
|
|
4184
|
+
}
|
|
4185
|
+
}
|
|
4186
|
+
};
|
|
4187
|
+
async function selectQueueChapters(document, chapters, options) {
|
|
4188
|
+
const ids = options.chapterIds ?? (options.chapterId === void 0 ? void 0 : [options.chapterId]);
|
|
4189
|
+
if (ids !== void 0) {
|
|
4190
|
+
const selected = new Set(ids);
|
|
4191
|
+
for (const id of selected) requireQueueChapter(chapters, id);
|
|
4192
|
+
return chapters.filter((chapter) => selected.has(chapter.chapterId));
|
|
4193
|
+
}
|
|
4194
|
+
if (options.chapterPath === void 0) {
|
|
4195
|
+
return options.depth === void 0 ? chapters : chapters.filter((chapter) => chapter.depth <= options.depth);
|
|
4196
|
+
}
|
|
4197
|
+
const rootId = await resolveChapterPathReadonly2(
|
|
4198
|
+
document,
|
|
4199
|
+
options.chapterPath
|
|
4200
|
+
);
|
|
4201
|
+
const root = requireQueueChapter(chapters, rootId);
|
|
4202
|
+
const prefix = `${root.path}/`;
|
|
4203
|
+
return chapters.filter(
|
|
4204
|
+
(chapter) => chapter.chapterId === rootId || chapter.path.startsWith(prefix) && (options.depth === void 0 || chapter.depth - root.depth <= options.depth)
|
|
4205
|
+
);
|
|
4206
|
+
}
|
|
4207
|
+
async function validateQueueTargetConfig(target, llmJSON) {
|
|
4208
|
+
const config = target === "knowledge-graph" || target === "reading-graph" || target === "reading-summary" ? await loadRequiredStageConfig({
|
|
4209
|
+
...llmJSON === void 0 ? {} : { llmJSON }
|
|
4210
|
+
}) : await loadWikiGraphRuntimeConfig({
|
|
4211
|
+
...llmJSON === void 0 ? {} : { llmJSON }
|
|
4212
|
+
});
|
|
4213
|
+
if ((target === "index-embedding-source" || target === "index-embedding-summary") && config.embedding === void 0) {
|
|
4214
|
+
throw new Error(
|
|
4215
|
+
"Missing embeddings configuration. Configure `wikg://local/config/embeddings` before queueing embedding index artifact jobs."
|
|
4216
|
+
);
|
|
4217
|
+
}
|
|
4218
|
+
if (target === "knowledge-graph")
|
|
4219
|
+
requireKnowledgeGraphWikispineConfig(config);
|
|
4220
|
+
}
|
|
4221
|
+
function requireQueueChapter(chapters, chapterId) {
|
|
4222
|
+
const chapter = chapters.find((entry) => entry.chapterId === chapterId);
|
|
4223
|
+
if (chapter === void 0)
|
|
4224
|
+
throw new Error(`Chapter ${chapterId} does not exist.`);
|
|
4225
|
+
return chapter;
|
|
4226
|
+
}
|
|
4227
|
+
async function readQueueReadinessReason(document, chapter, target) {
|
|
4228
|
+
if (chapter.stage === "planned") {
|
|
4229
|
+
return "planned";
|
|
4230
|
+
}
|
|
4231
|
+
if (target === "index-embedding-summary") {
|
|
4232
|
+
const summary = await document.readSummary(chapter.chapterId);
|
|
4233
|
+
if (summary === void 0 || summary.trim() === "") {
|
|
4234
|
+
return "missing summary";
|
|
4235
|
+
}
|
|
4236
|
+
}
|
|
4237
|
+
if (target === "knowledge-graph" || target === "reading-graph") {
|
|
4238
|
+
const [artifact, revision] = await Promise.all([
|
|
4239
|
+
document.indexArtifacts.get(chapter.chapterId, "fts"),
|
|
4240
|
+
document.serials.getRevision(chapter.chapterId)
|
|
4241
|
+
]);
|
|
4242
|
+
if (artifact?.sourceRevision !== revision) {
|
|
4243
|
+
return "missing current FTS index artifact";
|
|
4244
|
+
}
|
|
4245
|
+
}
|
|
4246
|
+
return void 0;
|
|
4247
|
+
}
|
|
4248
|
+
function formatQueueReadinessError(chapter, target, reason) {
|
|
4249
|
+
if (reason === "planned") {
|
|
4250
|
+
return `Chapter ${chapter.uri} is planned. Set source before queueing a build job.`;
|
|
4251
|
+
}
|
|
4252
|
+
if (reason === "missing summary") {
|
|
4253
|
+
return `Chapter ${chapter.uri} has no summary. Build a reading summary before queueing a summary embedding index artifact job.`;
|
|
4254
|
+
}
|
|
4255
|
+
if (reason === "missing current FTS index artifact") {
|
|
4256
|
+
return `Chapter ${chapter.uri} needs a current FTS index artifact before queueing ${target}.`;
|
|
4257
|
+
}
|
|
4258
|
+
return reason;
|
|
4259
|
+
}
|
|
4260
|
+
async function wait(milliseconds, signal) {
|
|
4261
|
+
if (signal?.aborted === true) return;
|
|
4262
|
+
await new Promise((resolve3) => {
|
|
4263
|
+
let settled = false;
|
|
4264
|
+
const complete = () => {
|
|
4265
|
+
if (settled) return;
|
|
4266
|
+
settled = true;
|
|
4267
|
+
clearTimeout(timeout);
|
|
4268
|
+
signal?.removeEventListener("abort", complete);
|
|
4269
|
+
resolve3();
|
|
4270
|
+
};
|
|
4271
|
+
const timeout = setTimeout(complete, milliseconds);
|
|
4272
|
+
signal?.addEventListener("abort", complete, { once: true });
|
|
4273
|
+
if (signal?.aborted === true) complete();
|
|
4274
|
+
});
|
|
4275
|
+
}
|
|
4276
|
+
function isTerminalState(state) {
|
|
4277
|
+
return TERMINAL_STATES.has(state);
|
|
4278
|
+
}
|
|
4279
|
+
function isTerminalEvent(event) {
|
|
4280
|
+
return event.type === "canceled" || event.type === "failed" || event.type === "succeeded";
|
|
4281
|
+
}
|
|
4282
|
+
function forwardAbort(source, target) {
|
|
4283
|
+
if (source === void 0) return () => void 0;
|
|
4284
|
+
const abort = () => target.abort(source.reason);
|
|
4285
|
+
if (source.aborted) abort();
|
|
4286
|
+
else source.addEventListener("abort", abort, { once: true });
|
|
4287
|
+
return () => source.removeEventListener("abort", abort);
|
|
4288
|
+
}
|
|
4289
|
+
|
|
4290
|
+
// src/libraries.ts
|
|
4291
|
+
import {
|
|
4292
|
+
addWikiGraphLibraryArchive,
|
|
4293
|
+
assertWikiGraphLibrarySchemaCurrent,
|
|
4294
|
+
cleanWikiGraphLibraryIndex,
|
|
4295
|
+
clearWikiGraphLibraryMetadata,
|
|
4296
|
+
createWikiGraphLibrary,
|
|
4297
|
+
deleteWikiGraphLibraryMetadataKey,
|
|
4298
|
+
findWikiGraphLibraryObjects as findWikiGraphLibraryObjects2,
|
|
4299
|
+
findWikiGraphLibraryArchiveMembers as findWikiGraphLibraryArchiveMembers2,
|
|
4300
|
+
formatWikiGraphLibraryUri,
|
|
4301
|
+
getWikiGraphLibraryArchive,
|
|
4302
|
+
getWikiGraphLibraryMetadata,
|
|
4303
|
+
listRelatedWikiGraphLibraryObjects as listRelatedWikiGraphLibraryObjects2,
|
|
4304
|
+
listWikiGraphLibraries,
|
|
4305
|
+
listWikiGraphLibraryArchives,
|
|
4306
|
+
listWikiGraphLibraryArchiveMembers as listWikiGraphLibraryArchiveMembers2,
|
|
4307
|
+
listWikiGraphLibraryEvidence as listWikiGraphLibraryEvidence2,
|
|
4308
|
+
listWikiGraphLibraryObjects as listWikiGraphLibraryObjects2,
|
|
4309
|
+
moveWikiGraphLibraryArchive,
|
|
4310
|
+
packWikiGraphLibraryContext,
|
|
4311
|
+
parseWikiGraphLibraryUri as parseWikiGraphLibraryUri2,
|
|
4312
|
+
putWikiGraphLibraryMetadata,
|
|
4313
|
+
readWikiGraphLibraryIndexState as readWikiGraphLibraryIndexState2,
|
|
4314
|
+
readWikiGraphLibraryPage,
|
|
4315
|
+
rebuildWikiGraphLibraryIndex as rebuildWikiGraphLibraryIndex2,
|
|
4316
|
+
rebindWikiGraphLibrary,
|
|
4317
|
+
removeWikiGraphLibrary,
|
|
4318
|
+
removeWikiGraphLibraryArchive,
|
|
4319
|
+
replaceWikiGraphLibraryMetadata,
|
|
4320
|
+
resolveWikiGraphLibrary,
|
|
4321
|
+
scanWikiGraphLibrary
|
|
4322
|
+
} from "wiki-graph-core";
|
|
4323
|
+
import { mkdir as mkdir2 } from "fs/promises";
|
|
4324
|
+
var WikiGraphLibraryManager = class {
|
|
4325
|
+
#runtime;
|
|
4326
|
+
constructor(runtime) {
|
|
4327
|
+
this.#runtime = runtime;
|
|
4328
|
+
}
|
|
4329
|
+
async create(folder) {
|
|
4330
|
+
const record = await this.#runtime.run(async () => {
|
|
4331
|
+
const path = resolveWikiGraphRuntimePath(folder);
|
|
4332
|
+
await mkdir2(path);
|
|
4333
|
+
return await createWikiGraphLibrary({ folder: new NodeDirectory(path) });
|
|
4334
|
+
});
|
|
4335
|
+
return new WikiGraphLibrary(this.#runtime, record);
|
|
4336
|
+
}
|
|
4337
|
+
async get(uri = formatWikiGraphLibraryUri()) {
|
|
4338
|
+
const target = requireLibraryTarget(uri);
|
|
4339
|
+
const record = await this.#runtime.run(
|
|
4340
|
+
async () => await resolveWikiGraphLibrary(target)
|
|
4341
|
+
);
|
|
4342
|
+
return new WikiGraphLibrary(this.#runtime, record);
|
|
4343
|
+
}
|
|
4344
|
+
async addArchive(options) {
|
|
4345
|
+
return await this.#runtime.run(
|
|
4346
|
+
async () => await addWikiGraphLibraryArchive({
|
|
4347
|
+
inputFile: new NodeFile(
|
|
4348
|
+
resolveWikiGraphRuntimePath(options.inputPath)
|
|
4349
|
+
),
|
|
4350
|
+
target: requireLibraryTarget(options.target),
|
|
4351
|
+
...options.to === void 0 ? {} : { to: options.to }
|
|
4352
|
+
})
|
|
4353
|
+
);
|
|
4354
|
+
}
|
|
4355
|
+
async assertCurrent(target) {
|
|
4356
|
+
await this.#runtime.run(
|
|
4357
|
+
async () => await assertWikiGraphLibrarySchemaCurrent(requireLibraryTarget(target))
|
|
4358
|
+
);
|
|
4359
|
+
}
|
|
4360
|
+
async rebind(target, folder) {
|
|
4361
|
+
return await this.#runtime.run(
|
|
4362
|
+
async () => await rebindWikiGraphLibrary({
|
|
4363
|
+
folder: new NodeDirectory(resolveWikiGraphRuntimePath(folder)),
|
|
4364
|
+
target: requireLibraryTarget(target)
|
|
4365
|
+
})
|
|
4366
|
+
);
|
|
4367
|
+
}
|
|
4368
|
+
async remove(target) {
|
|
4369
|
+
return await this.#runtime.run(
|
|
4370
|
+
async () => await removeWikiGraphLibrary(requireLibraryTarget(target))
|
|
4371
|
+
);
|
|
4372
|
+
}
|
|
4373
|
+
async scan(target) {
|
|
4374
|
+
return await this.#runtime.run(
|
|
4375
|
+
async () => await scanWikiGraphLibrary(requireLibraryTarget(target))
|
|
4376
|
+
);
|
|
4377
|
+
}
|
|
4378
|
+
async getArchive(target) {
|
|
4379
|
+
return await this.#runtime.run(
|
|
4380
|
+
async () => await getWikiGraphLibraryArchive(requireLibraryTarget(target))
|
|
4381
|
+
);
|
|
4382
|
+
}
|
|
4383
|
+
async moveArchive(options) {
|
|
4384
|
+
return await this.#runtime.run(
|
|
4385
|
+
async () => await moveWikiGraphLibraryArchive({
|
|
4386
|
+
target: requireLibraryTarget(options.target),
|
|
4387
|
+
to: options.to
|
|
4388
|
+
})
|
|
4389
|
+
);
|
|
4390
|
+
}
|
|
4391
|
+
async removeArchive(target) {
|
|
4392
|
+
return await this.#runtime.run(
|
|
4393
|
+
async () => await removeWikiGraphLibraryArchive({
|
|
4394
|
+
target: requireLibraryTarget(target)
|
|
4395
|
+
})
|
|
4396
|
+
);
|
|
4397
|
+
}
|
|
4398
|
+
async indexState(target) {
|
|
4399
|
+
return await this.#runtime.run(
|
|
4400
|
+
async () => await readWikiGraphLibraryIndexState2(requireLibraryTarget(target))
|
|
4401
|
+
);
|
|
4402
|
+
}
|
|
4403
|
+
async rebuildIndex(target, onProgress) {
|
|
4404
|
+
return await this.#runtime.run(
|
|
4405
|
+
async () => await rebuildWikiGraphLibraryIndex2(
|
|
4406
|
+
requireLibraryTarget(target),
|
|
4407
|
+
onProgress
|
|
4408
|
+
)
|
|
4409
|
+
);
|
|
4410
|
+
}
|
|
4411
|
+
async cleanIndex(target) {
|
|
4412
|
+
return await this.#runtime.run(
|
|
4413
|
+
async () => await cleanWikiGraphLibraryIndex(requireLibraryTarget(target))
|
|
4414
|
+
);
|
|
4415
|
+
}
|
|
4416
|
+
async getMetadata(target) {
|
|
4417
|
+
return await this.#runtime.run(
|
|
4418
|
+
async () => await getWikiGraphLibraryMetadata(requireLibraryTarget(target))
|
|
4419
|
+
);
|
|
4420
|
+
}
|
|
4421
|
+
async replaceMetadata(target, value) {
|
|
4422
|
+
return await this.#runtime.run(
|
|
4423
|
+
async () => await replaceWikiGraphLibraryMetadata(
|
|
4424
|
+
requireLibraryTarget(target),
|
|
4425
|
+
value
|
|
4426
|
+
)
|
|
4427
|
+
);
|
|
4428
|
+
}
|
|
4429
|
+
async putMetadata(target, key, value) {
|
|
4430
|
+
return await this.#runtime.run(
|
|
4431
|
+
async () => await putWikiGraphLibraryMetadata(
|
|
4432
|
+
requireLibraryTarget(target),
|
|
4433
|
+
key,
|
|
4434
|
+
value
|
|
4435
|
+
)
|
|
4436
|
+
);
|
|
4437
|
+
}
|
|
4438
|
+
async deleteMetadata(target, key) {
|
|
4439
|
+
return await this.#runtime.run(
|
|
4440
|
+
async () => await deleteWikiGraphLibraryMetadataKey(
|
|
4441
|
+
requireLibraryTarget(target),
|
|
4442
|
+
key
|
|
4443
|
+
)
|
|
4444
|
+
);
|
|
4445
|
+
}
|
|
4446
|
+
async clearMetadata(target) {
|
|
4447
|
+
return await this.#runtime.run(
|
|
4448
|
+
async () => await clearWikiGraphLibraryMetadata(requireLibraryTarget(target))
|
|
4449
|
+
);
|
|
4450
|
+
}
|
|
4451
|
+
async search(target, query, options = {}) {
|
|
4452
|
+
return await this.#runtime.run(async () => {
|
|
4453
|
+
const embeddingProvider = await createConfiguredEmbeddingProvider();
|
|
4454
|
+
return await findWikiGraphLibraryObjects2(
|
|
4455
|
+
requireLibraryTarget(target),
|
|
4456
|
+
query,
|
|
4457
|
+
{
|
|
4458
|
+
...options,
|
|
4459
|
+
...embeddingProvider === void 0 ? {} : { embeddingProvider }
|
|
4460
|
+
}
|
|
4461
|
+
);
|
|
4462
|
+
});
|
|
4463
|
+
}
|
|
4464
|
+
async searchArchiveMembers(target, query, options = {}) {
|
|
4465
|
+
return await this.#runtime.run(async () => {
|
|
4466
|
+
const embeddingProvider = await createConfiguredEmbeddingProvider();
|
|
4467
|
+
return await findWikiGraphLibraryArchiveMembers2(
|
|
4468
|
+
requireLibraryTarget(target),
|
|
4469
|
+
query,
|
|
4470
|
+
{
|
|
4471
|
+
...options,
|
|
4472
|
+
...embeddingProvider === void 0 ? {} : { embeddingProvider }
|
|
4473
|
+
}
|
|
4474
|
+
);
|
|
4475
|
+
});
|
|
4476
|
+
}
|
|
4477
|
+
async objects(target, options = {}) {
|
|
4478
|
+
return await this.#runtime.run(
|
|
4479
|
+
async () => await listWikiGraphLibraryObjects2(
|
|
4480
|
+
requireLibraryTarget(target),
|
|
4481
|
+
options
|
|
4482
|
+
)
|
|
4483
|
+
);
|
|
4484
|
+
}
|
|
4485
|
+
async archiveMembers(target, options = {}) {
|
|
4486
|
+
return await this.#runtime.run(
|
|
4487
|
+
async () => await listWikiGraphLibraryArchiveMembers2(
|
|
4488
|
+
requireLibraryTarget(target),
|
|
4489
|
+
options
|
|
4490
|
+
)
|
|
4491
|
+
);
|
|
4492
|
+
}
|
|
4493
|
+
async page(target, objectUri, options = {}) {
|
|
4494
|
+
return await this.#runtime.run(
|
|
4495
|
+
async () => await withConfiguredWikimediaResolver(
|
|
4496
|
+
objectUri,
|
|
4497
|
+
async (wikimediaOptions) => await readWikiGraphLibraryPage(
|
|
4498
|
+
requireLibraryTarget(target),
|
|
4499
|
+
objectUri,
|
|
4500
|
+
{ ...options, ...wikimediaOptions }
|
|
4501
|
+
)
|
|
4502
|
+
)
|
|
4503
|
+
);
|
|
4504
|
+
}
|
|
4505
|
+
async related(target, objectUri, options = {}) {
|
|
4506
|
+
return await this.#runtime.run(
|
|
4507
|
+
async () => await listRelatedWikiGraphLibraryObjects2(
|
|
4508
|
+
requireLibraryTarget(target),
|
|
4509
|
+
objectUri,
|
|
4510
|
+
options
|
|
4511
|
+
)
|
|
4512
|
+
);
|
|
4513
|
+
}
|
|
4514
|
+
async evidence(target, objectUri, options = {}) {
|
|
4515
|
+
return await this.#runtime.run(
|
|
4516
|
+
async () => await listWikiGraphLibraryEvidence2(
|
|
4517
|
+
requireLibraryTarget(target),
|
|
4518
|
+
objectUri,
|
|
4519
|
+
options
|
|
4520
|
+
)
|
|
4521
|
+
);
|
|
4522
|
+
}
|
|
4523
|
+
async pack(target, objectUri, budget = 5e3) {
|
|
4524
|
+
return await this.#runtime.run(
|
|
4525
|
+
async () => await packWikiGraphLibraryContext(
|
|
4526
|
+
requireLibraryTarget(target),
|
|
4527
|
+
objectUri,
|
|
4528
|
+
budget
|
|
4529
|
+
)
|
|
4530
|
+
);
|
|
4531
|
+
}
|
|
4532
|
+
async list() {
|
|
4533
|
+
const records = await this.#runtime.run(
|
|
4534
|
+
async () => await listWikiGraphLibraries()
|
|
4535
|
+
);
|
|
4536
|
+
return records.map((record) => new WikiGraphLibrary(this.#runtime, record));
|
|
4537
|
+
}
|
|
4538
|
+
};
|
|
4539
|
+
var WikiGraphLibrary = class {
|
|
4540
|
+
#runtime;
|
|
4541
|
+
#record;
|
|
4542
|
+
constructor(runtime, record) {
|
|
4543
|
+
this.#runtime = runtime;
|
|
4544
|
+
this.#record = record;
|
|
4545
|
+
}
|
|
4546
|
+
get snapshot() {
|
|
4547
|
+
return this.#record;
|
|
4548
|
+
}
|
|
4549
|
+
get uri() {
|
|
4550
|
+
return formatWikiGraphLibraryUri(
|
|
4551
|
+
this.#record.isDefault ? void 0 : this.#record.publicId
|
|
4552
|
+
);
|
|
4553
|
+
}
|
|
4554
|
+
async archives() {
|
|
4555
|
+
const target = requireLibraryTarget(this.uri);
|
|
4556
|
+
return await this.#runtime.run(
|
|
4557
|
+
async () => await listWikiGraphLibraryArchives(target)
|
|
4558
|
+
);
|
|
4559
|
+
}
|
|
4560
|
+
async scan() {
|
|
4561
|
+
const target = requireLibraryTarget(this.uri);
|
|
4562
|
+
return await this.#runtime.run(
|
|
4563
|
+
async () => await scanWikiGraphLibrary(target)
|
|
4564
|
+
);
|
|
4565
|
+
}
|
|
4566
|
+
async remove() {
|
|
4567
|
+
const target = requireLibraryTarget(this.uri);
|
|
4568
|
+
return await this.#runtime.run(
|
|
4569
|
+
async () => await removeWikiGraphLibrary(target)
|
|
4570
|
+
);
|
|
4571
|
+
}
|
|
4572
|
+
};
|
|
4573
|
+
function requireLibraryTarget(value) {
|
|
4574
|
+
if (typeof value !== "string") return value;
|
|
4575
|
+
const target = parseWikiGraphLibraryUri2(value);
|
|
4576
|
+
if (target === void 0) {
|
|
4577
|
+
throw new TypeError(`Invalid Wiki Graph library URI: ${value}`);
|
|
4578
|
+
}
|
|
4579
|
+
return target;
|
|
4580
|
+
}
|
|
4581
|
+
|
|
4582
|
+
// src/maintenance.ts
|
|
4583
|
+
import {
|
|
4584
|
+
migrateLegacySdpubToWikg,
|
|
4585
|
+
parseWikiGraphLibraryUri as parseWikiGraphLibraryUri3,
|
|
4586
|
+
upgradeWikiGraphMaintenanceTarget
|
|
4587
|
+
} from "wiki-graph-core";
|
|
4588
|
+
import { basename as basename3 } from "path";
|
|
4589
|
+
var WikiGraphMaintenanceManager = class {
|
|
4590
|
+
#runtime;
|
|
4591
|
+
constructor(runtime) {
|
|
4592
|
+
this.#runtime = runtime;
|
|
4593
|
+
}
|
|
4594
|
+
async upgrade(target) {
|
|
4595
|
+
return await this.#runtime.run(
|
|
4596
|
+
async () => {
|
|
4597
|
+
if (target.kind === "home") {
|
|
4598
|
+
return await upgradeWikiGraphMaintenanceTarget({ kind: "home" });
|
|
4599
|
+
}
|
|
4600
|
+
if (target.kind === "library") {
|
|
4601
|
+
const parsed = parseWikiGraphLibraryUri3(target.uri);
|
|
4602
|
+
if (parsed === void 0 || parsed.kind === "archive") {
|
|
4603
|
+
throw new Error(
|
|
4604
|
+
`Invalid Wiki Graph library upgrade target: ${target.uri}`
|
|
4605
|
+
);
|
|
4606
|
+
}
|
|
4607
|
+
return await upgradeWikiGraphMaintenanceTarget({
|
|
4608
|
+
kind: "library",
|
|
4609
|
+
target: parsed
|
|
4610
|
+
});
|
|
4611
|
+
}
|
|
4612
|
+
const path = resolveWikiGraphRuntimePath(target.path);
|
|
4613
|
+
return await upgradeWikiGraphMaintenanceTarget({
|
|
4614
|
+
file: new NodeFile(path),
|
|
4615
|
+
kind: "archive"
|
|
4616
|
+
});
|
|
4617
|
+
},
|
|
4618
|
+
void 0,
|
|
4619
|
+
{ skipHomeBootstrap: target.kind === "home" }
|
|
4620
|
+
);
|
|
4621
|
+
}
|
|
4622
|
+
async migrateLegacy(inputPath, outputPath = defaultWikgOutputPath(inputPath)) {
|
|
4623
|
+
return await this.#runtime.run(async () => {
|
|
4624
|
+
const resolvedInputPath = resolveWikiGraphRuntimePath(inputPath);
|
|
4625
|
+
const resolvedOutputPath = resolveWikiGraphRuntimePath(outputPath);
|
|
4626
|
+
await migrateLegacySdpubToWikg(
|
|
4627
|
+
new NodeFile(resolvedInputPath),
|
|
4628
|
+
new NodeFile(resolvedOutputPath, basename3(resolvedOutputPath))
|
|
4629
|
+
);
|
|
4630
|
+
return {
|
|
4631
|
+
inputPath: resolvedInputPath,
|
|
4632
|
+
outputPath: resolvedOutputPath
|
|
4633
|
+
};
|
|
4634
|
+
});
|
|
4635
|
+
}
|
|
4636
|
+
};
|
|
4637
|
+
function defaultWikgOutputPath(inputPath) {
|
|
4638
|
+
return inputPath.toLowerCase().endsWith(".sdpub") ? `${inputPath.slice(0, -".sdpub".length)}.wikg` : `${inputPath}.wikg`;
|
|
4639
|
+
}
|
|
4640
|
+
|
|
4641
|
+
// src/sdk.ts
|
|
4642
|
+
import { ensureWikiGraphHomeSchemaCurrent } from "wiki-graph-core";
|
|
4643
|
+
var WikiGraphSDK = class {
|
|
4644
|
+
#context;
|
|
4645
|
+
#homeReady;
|
|
4646
|
+
archives;
|
|
4647
|
+
conversions;
|
|
4648
|
+
config;
|
|
4649
|
+
continuations;
|
|
4650
|
+
jobs;
|
|
4651
|
+
libraries;
|
|
4652
|
+
maintenance;
|
|
4653
|
+
constructor(options = {}) {
|
|
4654
|
+
ensureNodeWikiGraphPlatform();
|
|
4655
|
+
this.#context = {
|
|
4656
|
+
cwd: options.cwd ?? process.cwd(),
|
|
4657
|
+
env: { ...process.env, ...options.env },
|
|
4658
|
+
envPolicy: options.envPolicy ?? "production",
|
|
4659
|
+
...options.stateDir === void 0 ? {} : { stateDir: options.stateDir }
|
|
4660
|
+
};
|
|
4661
|
+
this.archives = new WikiGraphArchiveManager(this);
|
|
4662
|
+
this.conversions = new WikiGraphConversionManager(this);
|
|
4663
|
+
this.config = new WikiGraphConfigManager(this);
|
|
4664
|
+
this.continuations = new WikiGraphContinuationManager(this);
|
|
4665
|
+
this.jobs = new WikiGraphJobManager(this);
|
|
4666
|
+
this.libraries = new WikiGraphLibraryManager(this);
|
|
4667
|
+
this.maintenance = new WikiGraphMaintenanceManager(this);
|
|
4668
|
+
}
|
|
4669
|
+
async run(operation, signal, options = {}) {
|
|
4670
|
+
throwIfAborted(signal);
|
|
4671
|
+
const runWithContext = async () => await withWikiGraphSDKRuntimeContext(this.#context, async () => {
|
|
4672
|
+
if (options.skipHomeBootstrap !== true) {
|
|
4673
|
+
this.#homeReady ??= ensureWikiGraphHomeSchemaCurrent().catch(
|
|
4674
|
+
(error) => {
|
|
4675
|
+
this.#homeReady = void 0;
|
|
4676
|
+
throw error;
|
|
4677
|
+
}
|
|
4678
|
+
);
|
|
4679
|
+
await this.#homeReady;
|
|
4680
|
+
}
|
|
4681
|
+
return await operation();
|
|
4682
|
+
});
|
|
4683
|
+
const result = this.#context.stateDir === void 0 ? await runWithContext() : await withNodeWikiGraphStorage(
|
|
4684
|
+
this.#context.stateDir,
|
|
4685
|
+
runWithContext
|
|
4686
|
+
);
|
|
4687
|
+
throwIfAborted(signal);
|
|
4688
|
+
return result;
|
|
4689
|
+
}
|
|
4690
|
+
close() {
|
|
4691
|
+
this.jobs.close();
|
|
4692
|
+
}
|
|
4693
|
+
};
|
|
4694
|
+
function throwIfAborted(signal) {
|
|
4695
|
+
if (signal?.aborted !== true) return;
|
|
4696
|
+
throw signal.reason instanceof Error ? signal.reason : new Error("The Wiki Graph SDK operation was aborted.");
|
|
4697
|
+
}
|
|
4698
|
+
function createWikiGraphSDK(options = {}) {
|
|
4699
|
+
return new WikiGraphSDK(options);
|
|
4700
|
+
}
|
|
4701
|
+
|
|
4702
|
+
// src/index.ts
|
|
4703
|
+
import {
|
|
4704
|
+
LLMPaymentRequiredError,
|
|
4705
|
+
WIKI_GRAPH_URI_PREFIX,
|
|
4706
|
+
WikiGraphError,
|
|
4707
|
+
formatError as formatError3,
|
|
4708
|
+
formatLocatedChapterResourceUri,
|
|
4709
|
+
formatLocatedChapterSourceCollectionUri,
|
|
4710
|
+
formatLocatedChapterUri as formatLocatedChapterUri2,
|
|
4711
|
+
formatLocatedWikiGraphUri as formatLocatedWikiGraphUri2,
|
|
4712
|
+
formatWikiGraphCommandUri,
|
|
4713
|
+
formatWikiGraphLibraryUri as formatWikiGraphLibraryUri2,
|
|
4714
|
+
isSourceLocatorScopeUri as isSourceLocatorScopeUri2,
|
|
4715
|
+
isWikiGraphLibraryUri,
|
|
4716
|
+
parseChapterPath,
|
|
4717
|
+
parseChapterTreeInput,
|
|
4718
|
+
parseChapterUriPath,
|
|
4719
|
+
parseLocatedWikiGraphUri as parseLocatedWikiGraphUri2,
|
|
4720
|
+
parseSourceTextJsonl,
|
|
4721
|
+
parseWikiGraphLibraryUri as parseWikiGraphLibraryUri4,
|
|
4722
|
+
parseWikiGraphUriSyntax
|
|
4723
|
+
} from "wiki-graph-core";
|
|
4724
|
+
export {
|
|
4725
|
+
DANGEROUS_RUNTIME_ENV_NAMES,
|
|
4726
|
+
DEFAULT_EXTRACTION_PROMPT,
|
|
4727
|
+
DEFAULT_GENERATION_JOB_CONCURRENCY,
|
|
4728
|
+
DEFAULT_GENERATION_REQUEST_CONCURRENCY,
|
|
4729
|
+
DEFAULT_KNOWLEDGE_GRAPH_RECALL_PROMPT,
|
|
4730
|
+
LLMPaymentRequiredError,
|
|
4731
|
+
LOCAL_CONFIG_SECTIONS,
|
|
4732
|
+
NodeDirectory,
|
|
4733
|
+
NodeFile,
|
|
4734
|
+
WIKI_GRAPH_URI_PREFIX,
|
|
4735
|
+
WikiGraphArchiveExistsError,
|
|
4736
|
+
WikiGraphArchiveHandle,
|
|
4737
|
+
WikiGraphArchiveManager,
|
|
4738
|
+
WikiGraphConfigManager,
|
|
4739
|
+
WikiGraphContinuationManager,
|
|
4740
|
+
WikiGraphConversionManager,
|
|
4741
|
+
WikiGraphError,
|
|
4742
|
+
WikiGraphJob,
|
|
4743
|
+
WikiGraphJobManager,
|
|
4744
|
+
WikiGraphLibrary,
|
|
4745
|
+
WikiGraphLibraryManager,
|
|
4746
|
+
WikiGraphMaintenanceManager,
|
|
4747
|
+
WikiGraphSDK,
|
|
4748
|
+
buildSearchIndexEmbeddingProvider,
|
|
4749
|
+
buildWikiGraphLLMOptions,
|
|
4750
|
+
clearLocalConfigSection,
|
|
4751
|
+
createEntryRuntimeContext,
|
|
4752
|
+
createGenerationPerformanceHints,
|
|
4753
|
+
createNodeTemplateEnvironment,
|
|
4754
|
+
createNodeWikiGraphStorage,
|
|
4755
|
+
createStageLLM,
|
|
4756
|
+
createWikiGraphArchiveInspectReport,
|
|
4757
|
+
createWikiGraphSDK,
|
|
4758
|
+
deleteLocalConfigValue,
|
|
4759
|
+
embedQueryText,
|
|
4760
|
+
embedQueryTextWithLocalConfig,
|
|
4761
|
+
ensureNodeWikiGraphPlatform,
|
|
4762
|
+
formatError3 as formatError,
|
|
4763
|
+
formatGenerationPlanningDuration,
|
|
4764
|
+
formatGenerationPlanningModel,
|
|
4765
|
+
formatLocatedChapterResourceUri,
|
|
4766
|
+
formatLocatedChapterSourceCollectionUri,
|
|
4767
|
+
formatLocatedChapterUri2 as formatLocatedChapterUri,
|
|
4768
|
+
formatLocatedWikiGraphUri2 as formatLocatedWikiGraphUri,
|
|
4769
|
+
formatWikiGraphCommandUri,
|
|
4770
|
+
formatWikiGraphLibraryUri2 as formatWikiGraphLibraryUri,
|
|
4771
|
+
getNodeResourcePath,
|
|
4772
|
+
getWikiGraphSDKRuntimeContext,
|
|
4773
|
+
getWikiGraphSDKStateDir,
|
|
4774
|
+
installNodeWikiGraphPlatform,
|
|
4775
|
+
isSourceLocatorScopeUri2 as isSourceLocatorScopeUri,
|
|
4776
|
+
isWikiGraphLibraryUri,
|
|
4777
|
+
loadRequiredStageConfig,
|
|
4778
|
+
loadWikiGraphRuntimeConfig,
|
|
4779
|
+
maskLocalConfigSection,
|
|
4780
|
+
nodeWikiGraphPlatform,
|
|
4781
|
+
nodeWikispineCommandRunner,
|
|
4782
|
+
normalizeLocalConfigKey,
|
|
4783
|
+
parseChapterPath,
|
|
4784
|
+
parseChapterTreeInput,
|
|
4785
|
+
parseChapterUriPath,
|
|
4786
|
+
parseLocalConfigSection,
|
|
4787
|
+
parseLocatedWikiGraphUri2 as parseLocatedWikiGraphUri,
|
|
4788
|
+
parseSourceTextJsonl,
|
|
4789
|
+
parseWikiGraphLibraryUri4 as parseWikiGraphLibraryUri,
|
|
4790
|
+
parseWikiGraphUriSyntax,
|
|
4791
|
+
planGenerationTask,
|
|
4792
|
+
putLocalConfigValue,
|
|
4793
|
+
readLocalConfigSection,
|
|
4794
|
+
readWikiGraphEmbeddingConfig,
|
|
4795
|
+
replaceLocalConfigSection,
|
|
4796
|
+
requireKnowledgeGraphWikispineConfig,
|
|
4797
|
+
resolveExtractionPrompt,
|
|
4798
|
+
resolveHostedProviderEndpoint,
|
|
4799
|
+
resolveKnowledgeGraphRecallPrompt,
|
|
4800
|
+
resolveWikiGraphArchiveLocation,
|
|
4801
|
+
resolveWikispineConfig,
|
|
4802
|
+
runWikiGraphQueueWorker,
|
|
4803
|
+
validateLocalConfigSection,
|
|
4804
|
+
withNodeWikiGraphStorage,
|
|
4805
|
+
withWikiGraphSDKRuntimeContext,
|
|
4806
|
+
writeNodeFileHandle
|
|
4807
|
+
};
|
|
4808
|
+
//# sourceMappingURL=index.js.map
|