@bndynet/ragbox 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +765 -0
- package/README.zh-CN.md +774 -0
- package/dist/src/advanced.d.ts +13 -0
- package/dist/src/advanced.js +29 -0
- package/dist/src/cli.d.ts +2 -0
- package/dist/src/cli.js +1013 -0
- package/dist/src/config-file.d.ts +69 -0
- package/dist/src/config-file.js +246 -0
- package/dist/src/folder-index/config.d.ts +2 -0
- package/dist/src/folder-index/config.js +56 -0
- package/dist/src/folder-index/hash.d.ts +1 -0
- package/dist/src/folder-index/hash.js +14 -0
- package/dist/src/folder-index/indexer.d.ts +2 -0
- package/dist/src/folder-index/indexer.js +154 -0
- package/dist/src/folder-index/llm-client.d.ts +3 -0
- package/dist/src/folder-index/llm-client.js +45 -0
- package/dist/src/folder-index/manifest.d.ts +17 -0
- package/dist/src/folder-index/manifest.js +158 -0
- package/dist/src/folder-index/multi-query.d.ts +45 -0
- package/dist/src/folder-index/multi-query.js +109 -0
- package/dist/src/folder-index/pageindex-runner.d.ts +3 -0
- package/dist/src/folder-index/pageindex-runner.js +218 -0
- package/dist/src/folder-index/path-utils.d.ts +5 -0
- package/dist/src/folder-index/path-utils.js +33 -0
- package/dist/src/folder-index/query.d.ts +19 -0
- package/dist/src/folder-index/query.js +597 -0
- package/dist/src/folder-index/queue.d.ts +1 -0
- package/dist/src/folder-index/queue.js +18 -0
- package/dist/src/folder-index/root-tree.d.ts +3 -0
- package/dist/src/folder-index/root-tree.js +82 -0
- package/dist/src/folder-index/scan.d.ts +14 -0
- package/dist/src/folder-index/scan.js +152 -0
- package/dist/src/folder-index/types.d.ts +368 -0
- package/dist/src/folder-index/types.js +2 -0
- package/dist/src/folder-index/watch.d.ts +17 -0
- package/dist/src/folder-index/watch.js +550 -0
- package/dist/src/index.d.ts +6 -0
- package/dist/src/index.js +45 -0
- package/dist/src/sdk.d.ts +101 -0
- package/dist/src/sdk.js +352 -0
- package/dist/src/serve.d.ts +64 -0
- package/dist/src/serve.js +466 -0
- package/dist/src/setup-pageindex.d.ts +30 -0
- package/dist/src/setup-pageindex.js +184 -0
- package/package.json +43 -0
|
@@ -0,0 +1,550 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.startWatchFolder = startWatchFolder;
|
|
7
|
+
exports.watchFolder = watchFolder;
|
|
8
|
+
const chokidar_1 = __importDefault(require("chokidar"));
|
|
9
|
+
const promises_1 = __importDefault(require("node:fs/promises"));
|
|
10
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
11
|
+
const config_1 = require("./config");
|
|
12
|
+
const indexer_1 = require("./indexer");
|
|
13
|
+
const manifest_1 = require("./manifest");
|
|
14
|
+
const path_utils_1 = require("./path-utils");
|
|
15
|
+
const scan_1 = require("./scan");
|
|
16
|
+
const WATCH_IGNORED = /(^|[/\\])(node_modules|\.git|\.pageindex|dist|build)([/\\]|$)/;
|
|
17
|
+
class WatchIndexRunError extends Error {
|
|
18
|
+
attempt;
|
|
19
|
+
maxAttempts;
|
|
20
|
+
constructor(error, attempt, maxAttempts) {
|
|
21
|
+
super(errorMessage(error));
|
|
22
|
+
this.name = "WatchIndexRunError";
|
|
23
|
+
this.attempt = attempt;
|
|
24
|
+
this.maxAttempts = maxAttempts;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
function errorMessage(error) {
|
|
28
|
+
return error instanceof Error ? error.message : String(error);
|
|
29
|
+
}
|
|
30
|
+
async function pathExists(filePath) {
|
|
31
|
+
try {
|
|
32
|
+
await promises_1.default.access(filePath);
|
|
33
|
+
return true;
|
|
34
|
+
}
|
|
35
|
+
catch {
|
|
36
|
+
return false;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
function sleep(ms) {
|
|
40
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
41
|
+
}
|
|
42
|
+
function toIndexCounts(result) {
|
|
43
|
+
return {
|
|
44
|
+
total: result.manifest.documents.length,
|
|
45
|
+
ready: result.ready,
|
|
46
|
+
failed: result.failed,
|
|
47
|
+
added: result.added,
|
|
48
|
+
modified: result.modified,
|
|
49
|
+
retryFailed: result.retryFailed,
|
|
50
|
+
unchanged: result.unchanged,
|
|
51
|
+
deleted: result.deleted
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
function createWatchEvent(event) {
|
|
55
|
+
return {
|
|
56
|
+
version: 1,
|
|
57
|
+
timestamp: new Date().toISOString(),
|
|
58
|
+
...event
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
function reportWatchProgress(options, event, sendWebhook = true) {
|
|
62
|
+
try {
|
|
63
|
+
options.watchProgress?.(event);
|
|
64
|
+
}
|
|
65
|
+
catch {
|
|
66
|
+
// Watch progress reporting must never change watch behavior.
|
|
67
|
+
}
|
|
68
|
+
if (!sendWebhook || !options.watchWebhookUrl) {
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
void (async () => {
|
|
72
|
+
try {
|
|
73
|
+
const response = await fetch(options.watchWebhookUrl, {
|
|
74
|
+
method: "POST",
|
|
75
|
+
headers: {
|
|
76
|
+
"Content-Type": "application/json"
|
|
77
|
+
},
|
|
78
|
+
body: JSON.stringify(event)
|
|
79
|
+
});
|
|
80
|
+
if (!response.ok) {
|
|
81
|
+
throw new Error(`Webhook returned ${response.status}`);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
catch (error) {
|
|
85
|
+
reportWatchProgress(options, createWatchEvent({
|
|
86
|
+
type: "watch-webhook-failed",
|
|
87
|
+
rootDir: event.rootDir,
|
|
88
|
+
outputDir: event.outputDir,
|
|
89
|
+
url: options.watchWebhookUrl,
|
|
90
|
+
error: errorMessage(error)
|
|
91
|
+
}), false);
|
|
92
|
+
}
|
|
93
|
+
})();
|
|
94
|
+
}
|
|
95
|
+
async function acquireLock(lockFile, rootDir, outputDir) {
|
|
96
|
+
await promises_1.default.mkdir(node_path_1.default.dirname(lockFile), { recursive: true });
|
|
97
|
+
let handle;
|
|
98
|
+
try {
|
|
99
|
+
handle = await promises_1.default.open(lockFile, "wx");
|
|
100
|
+
await handle.writeFile(`${JSON.stringify({
|
|
101
|
+
version: 1,
|
|
102
|
+
pid: process.pid,
|
|
103
|
+
rootDir,
|
|
104
|
+
outputDir,
|
|
105
|
+
startedAt: new Date().toISOString()
|
|
106
|
+
}, null, 2)}\n`, "utf8");
|
|
107
|
+
}
|
|
108
|
+
catch (error) {
|
|
109
|
+
if (error.code === "EEXIST") {
|
|
110
|
+
throw new Error(`Watch lock already exists: ${lockFile}`);
|
|
111
|
+
}
|
|
112
|
+
throw error;
|
|
113
|
+
}
|
|
114
|
+
finally {
|
|
115
|
+
await handle?.close();
|
|
116
|
+
}
|
|
117
|
+
return async () => {
|
|
118
|
+
await promises_1.default.rm(lockFile, { force: true });
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
async function prepareStagingOutput(outputDir, stagingOutputDir) {
|
|
122
|
+
await promises_1.default.rm(stagingOutputDir, { force: true, recursive: true });
|
|
123
|
+
if (await pathExists(outputDir)) {
|
|
124
|
+
await promises_1.default.cp(outputDir, stagingOutputDir, { recursive: true });
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
await promises_1.default.mkdir(stagingOutputDir, { recursive: true });
|
|
128
|
+
}
|
|
129
|
+
async function promoteStagingOutput(outputDir, stagingOutputDir) {
|
|
130
|
+
const backupDir = `${outputDir}.previous-${process.pid}-${Date.now()}`;
|
|
131
|
+
if (!(await pathExists(outputDir))) {
|
|
132
|
+
await promises_1.default.rename(stagingOutputDir, outputDir);
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
await promises_1.default.rm(backupDir, { force: true, recursive: true });
|
|
136
|
+
await promises_1.default.rename(outputDir, backupDir);
|
|
137
|
+
try {
|
|
138
|
+
await promises_1.default.rename(stagingOutputDir, outputDir);
|
|
139
|
+
await promises_1.default.rm(backupDir, { force: true, recursive: true });
|
|
140
|
+
}
|
|
141
|
+
catch (error) {
|
|
142
|
+
try {
|
|
143
|
+
if (await pathExists(backupDir)) {
|
|
144
|
+
await promises_1.default.rm(outputDir, { force: true, recursive: true });
|
|
145
|
+
await promises_1.default.rename(backupDir, outputDir);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
catch {
|
|
149
|
+
// Preserve the original promotion error; rollback best-effort failed.
|
|
150
|
+
}
|
|
151
|
+
throw error;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
function withOutputDir(result, outputDir) {
|
|
155
|
+
return {
|
|
156
|
+
...result,
|
|
157
|
+
outputDir,
|
|
158
|
+
manifestPath: node_path_1.default.join(outputDir, manifest_1.MANIFEST_FILE),
|
|
159
|
+
rootTreePath: node_path_1.default.join(outputDir, manifest_1.ROOT_TREE_FILE)
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
async function startWatchFolder(folder, options = {}) {
|
|
163
|
+
const rootDir = node_path_1.default.resolve(folder);
|
|
164
|
+
const config = (0, config_1.loadPageIndexConfig)(options);
|
|
165
|
+
const outputDir = (0, manifest_1.resolvePageIndexDir)(rootDir, config.outputDir);
|
|
166
|
+
const lockFile = config.watchLockFile ? node_path_1.default.resolve(config.watchLockFile) : undefined;
|
|
167
|
+
const healthFile = config.watchHealthFile ? node_path_1.default.resolve(config.watchHealthFile) : undefined;
|
|
168
|
+
const retryAttempts = Math.max(0, config.watchRetryAttempts ?? 0);
|
|
169
|
+
const retryDelayMs = Math.max(0, config.watchRetryDelayMs ?? 1000);
|
|
170
|
+
const maxAttempts = retryAttempts + 1;
|
|
171
|
+
const debounceMs = Math.max(0, config.watchDebounceMs ?? 500);
|
|
172
|
+
const stagingOutputDir = config.watchStaging
|
|
173
|
+
? node_path_1.default.resolve(config.watchStagingOutputDir ?? `${outputDir}.staging`)
|
|
174
|
+
: undefined;
|
|
175
|
+
const ignoredOutputDirs = [outputDir, stagingOutputDir]
|
|
176
|
+
.filter((value) => Boolean(value))
|
|
177
|
+
.filter((candidate) => (0, path_utils_1.isStrictSubPath)(rootDir, candidate));
|
|
178
|
+
const hasStructuredProgress = Boolean(config.watchProgress);
|
|
179
|
+
const startedAt = new Date().toISOString();
|
|
180
|
+
let watcher;
|
|
181
|
+
let timer;
|
|
182
|
+
let running = false;
|
|
183
|
+
let pending = false;
|
|
184
|
+
let stopped = false;
|
|
185
|
+
let stopReported = false;
|
|
186
|
+
let readySettled = false;
|
|
187
|
+
let closeStarted;
|
|
188
|
+
let releaseLock;
|
|
189
|
+
let lastSuccessAt;
|
|
190
|
+
let lastFailureAt;
|
|
191
|
+
let lastHealthResult;
|
|
192
|
+
let lastHealthError;
|
|
193
|
+
let resolveReady;
|
|
194
|
+
let resolveClosed;
|
|
195
|
+
const ready = new Promise((resolve) => {
|
|
196
|
+
resolveReady = resolve;
|
|
197
|
+
});
|
|
198
|
+
const closed = new Promise((resolve) => {
|
|
199
|
+
resolveClosed = resolve;
|
|
200
|
+
});
|
|
201
|
+
async function writeHealth(status, reason, fields = {}) {
|
|
202
|
+
if (!healthFile) {
|
|
203
|
+
return;
|
|
204
|
+
}
|
|
205
|
+
const updatedAt = new Date().toISOString();
|
|
206
|
+
if (fields.result) {
|
|
207
|
+
lastHealthResult = fields.result;
|
|
208
|
+
}
|
|
209
|
+
if (fields.error) {
|
|
210
|
+
lastHealthError = fields.error;
|
|
211
|
+
}
|
|
212
|
+
const health = {
|
|
213
|
+
version: 1,
|
|
214
|
+
ok: status === "ready",
|
|
215
|
+
status,
|
|
216
|
+
rootDir,
|
|
217
|
+
outputDir,
|
|
218
|
+
pid: process.pid,
|
|
219
|
+
startedAt,
|
|
220
|
+
updatedAt,
|
|
221
|
+
lastSuccessAt,
|
|
222
|
+
lastFailureAt,
|
|
223
|
+
reason,
|
|
224
|
+
result: fields.result ?? lastHealthResult,
|
|
225
|
+
error: fields.error ?? lastHealthError
|
|
226
|
+
};
|
|
227
|
+
try {
|
|
228
|
+
await (0, manifest_1.atomicWriteJson)(healthFile, health);
|
|
229
|
+
reportWatchProgress(config, createWatchEvent({
|
|
230
|
+
type: "watch-health",
|
|
231
|
+
rootDir,
|
|
232
|
+
outputDir,
|
|
233
|
+
healthFile,
|
|
234
|
+
status,
|
|
235
|
+
ok: health.ok
|
|
236
|
+
}));
|
|
237
|
+
}
|
|
238
|
+
catch (error) {
|
|
239
|
+
reportWatchProgress(config, createWatchEvent({
|
|
240
|
+
type: "watch-health-failed",
|
|
241
|
+
rootDir,
|
|
242
|
+
outputDir,
|
|
243
|
+
healthFile,
|
|
244
|
+
error: errorMessage(error)
|
|
245
|
+
}));
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
async function finishClose() {
|
|
249
|
+
if (closeStarted) {
|
|
250
|
+
return await closeStarted;
|
|
251
|
+
}
|
|
252
|
+
closeStarted = (async () => {
|
|
253
|
+
if (timer) {
|
|
254
|
+
clearTimeout(timer);
|
|
255
|
+
timer = undefined;
|
|
256
|
+
}
|
|
257
|
+
if (watcher) {
|
|
258
|
+
await watcher.close();
|
|
259
|
+
watcher = undefined;
|
|
260
|
+
}
|
|
261
|
+
if (!stopReported) {
|
|
262
|
+
stopReported = true;
|
|
263
|
+
await writeHealth("stopped");
|
|
264
|
+
reportWatchProgress(config, createWatchEvent({
|
|
265
|
+
type: "watch-stop",
|
|
266
|
+
rootDir,
|
|
267
|
+
outputDir
|
|
268
|
+
}));
|
|
269
|
+
}
|
|
270
|
+
if (releaseLock && lockFile) {
|
|
271
|
+
await releaseLock();
|
|
272
|
+
releaseLock = undefined;
|
|
273
|
+
reportWatchProgress(config, createWatchEvent({
|
|
274
|
+
type: "watch-lock-released",
|
|
275
|
+
rootDir,
|
|
276
|
+
outputDir,
|
|
277
|
+
lockFile
|
|
278
|
+
}));
|
|
279
|
+
}
|
|
280
|
+
resolveClosed();
|
|
281
|
+
})();
|
|
282
|
+
return await closeStarted;
|
|
283
|
+
}
|
|
284
|
+
async function close() {
|
|
285
|
+
stopped = true;
|
|
286
|
+
finishReady({ ok: false, error: "Watch closed before initial index completed" });
|
|
287
|
+
if (!running) {
|
|
288
|
+
await finishClose();
|
|
289
|
+
}
|
|
290
|
+
return await closed;
|
|
291
|
+
}
|
|
292
|
+
function finishReady(value) {
|
|
293
|
+
if (!readySettled) {
|
|
294
|
+
readySettled = true;
|
|
295
|
+
resolveReady(value);
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
function isIgnored(candidatePath) {
|
|
299
|
+
if (WATCH_IGNORED.test(candidatePath)) {
|
|
300
|
+
return true;
|
|
301
|
+
}
|
|
302
|
+
if (ignoredOutputDirs.length === 0) {
|
|
303
|
+
return false;
|
|
304
|
+
}
|
|
305
|
+
const absoluteCandidatePath = node_path_1.default.isAbsolute(candidatePath) ? candidatePath : node_path_1.default.resolve(rootDir, candidatePath);
|
|
306
|
+
return ignoredOutputDirs.some((ignoredOutputDir) => (0, path_utils_1.isSubPath)(ignoredOutputDir, absoluteCandidatePath));
|
|
307
|
+
}
|
|
308
|
+
async function runIndexAttempt(reason, attempt) {
|
|
309
|
+
reportWatchProgress(config, createWatchEvent({
|
|
310
|
+
type: "watch-index-start",
|
|
311
|
+
rootDir,
|
|
312
|
+
outputDir,
|
|
313
|
+
reason,
|
|
314
|
+
attempt,
|
|
315
|
+
maxAttempts
|
|
316
|
+
}));
|
|
317
|
+
await writeHealth("indexing", reason);
|
|
318
|
+
if (!stagingOutputDir) {
|
|
319
|
+
return await (0, indexer_1.indexFolder)(rootDir, config);
|
|
320
|
+
}
|
|
321
|
+
await prepareStagingOutput(outputDir, stagingOutputDir);
|
|
322
|
+
const stagedResult = await (0, indexer_1.indexFolder)(rootDir, {
|
|
323
|
+
...config,
|
|
324
|
+
outputDir: stagingOutputDir
|
|
325
|
+
});
|
|
326
|
+
if (stagedResult.failed > 0) {
|
|
327
|
+
return stagedResult;
|
|
328
|
+
}
|
|
329
|
+
await promoteStagingOutput(outputDir, stagingOutputDir);
|
|
330
|
+
reportWatchProgress(config, createWatchEvent({
|
|
331
|
+
type: "watch-output-promoted",
|
|
332
|
+
rootDir,
|
|
333
|
+
outputDir,
|
|
334
|
+
stagingOutputDir
|
|
335
|
+
}));
|
|
336
|
+
return withOutputDir(stagedResult, outputDir);
|
|
337
|
+
}
|
|
338
|
+
async function reportRetry(reason, attempt, error) {
|
|
339
|
+
reportWatchProgress(config, createWatchEvent({
|
|
340
|
+
type: "watch-index-retry",
|
|
341
|
+
rootDir,
|
|
342
|
+
outputDir,
|
|
343
|
+
reason,
|
|
344
|
+
attempt,
|
|
345
|
+
maxAttempts,
|
|
346
|
+
delayMs: retryDelayMs,
|
|
347
|
+
error
|
|
348
|
+
}));
|
|
349
|
+
if (retryDelayMs > 0) {
|
|
350
|
+
await sleep(retryDelayMs);
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
async function runIndexWithRetry(reason) {
|
|
354
|
+
for (let attempt = 1; attempt <= maxAttempts; attempt += 1) {
|
|
355
|
+
try {
|
|
356
|
+
const result = await runIndexAttempt(reason, attempt);
|
|
357
|
+
if (result.failed > 0) {
|
|
358
|
+
const counts = toIndexCounts(result);
|
|
359
|
+
const message = `Index completed with ${result.failed} failed document(s)`;
|
|
360
|
+
reportWatchProgress(config, createWatchEvent({
|
|
361
|
+
type: "watch-index-partial-failure",
|
|
362
|
+
rootDir,
|
|
363
|
+
outputDir,
|
|
364
|
+
reason,
|
|
365
|
+
attempt,
|
|
366
|
+
maxAttempts,
|
|
367
|
+
failed: result.failed,
|
|
368
|
+
result: counts
|
|
369
|
+
}));
|
|
370
|
+
if (attempt < maxAttempts && !stopped) {
|
|
371
|
+
await reportRetry(reason, attempt, message);
|
|
372
|
+
continue;
|
|
373
|
+
}
|
|
374
|
+
if (stagingOutputDir) {
|
|
375
|
+
throw new Error(`${message}; staging output was not promoted`);
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
return {
|
|
379
|
+
attempt,
|
|
380
|
+
maxAttempts,
|
|
381
|
+
result
|
|
382
|
+
};
|
|
383
|
+
}
|
|
384
|
+
catch (error) {
|
|
385
|
+
if (attempt < maxAttempts && !stopped) {
|
|
386
|
+
await reportRetry(reason, attempt, errorMessage(error));
|
|
387
|
+
continue;
|
|
388
|
+
}
|
|
389
|
+
throw new WatchIndexRunError(error, attempt, maxAttempts);
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
throw new WatchIndexRunError("Index did not run", maxAttempts, maxAttempts);
|
|
393
|
+
}
|
|
394
|
+
async function runIndex(reason) {
|
|
395
|
+
if (running) {
|
|
396
|
+
pending = true;
|
|
397
|
+
return;
|
|
398
|
+
}
|
|
399
|
+
if (stopped) {
|
|
400
|
+
return;
|
|
401
|
+
}
|
|
402
|
+
running = true;
|
|
403
|
+
pending = false;
|
|
404
|
+
try {
|
|
405
|
+
const run = await runIndexWithRetry(reason);
|
|
406
|
+
const counts = toIndexCounts(run.result);
|
|
407
|
+
const healthStatus = run.result.failed > 0 ? "degraded" : "ready";
|
|
408
|
+
if (healthStatus === "ready") {
|
|
409
|
+
lastSuccessAt = new Date().toISOString();
|
|
410
|
+
lastHealthError = undefined;
|
|
411
|
+
}
|
|
412
|
+
else {
|
|
413
|
+
lastFailureAt = new Date().toISOString();
|
|
414
|
+
lastHealthError = `Index completed with ${run.result.failed} failed document(s)`;
|
|
415
|
+
}
|
|
416
|
+
await writeHealth(healthStatus, reason, { result: counts, error: lastHealthError });
|
|
417
|
+
reportWatchProgress(config, createWatchEvent({
|
|
418
|
+
type: "watch-index-done",
|
|
419
|
+
rootDir,
|
|
420
|
+
outputDir,
|
|
421
|
+
reason,
|
|
422
|
+
attempt: run.attempt,
|
|
423
|
+
maxAttempts: run.maxAttempts,
|
|
424
|
+
result: counts,
|
|
425
|
+
manifestPath: run.result.manifestPath,
|
|
426
|
+
rootTreePath: run.result.rootTreePath
|
|
427
|
+
}));
|
|
428
|
+
if (reason === "initial") {
|
|
429
|
+
finishReady({ ok: true, result: run.result });
|
|
430
|
+
}
|
|
431
|
+
if (!hasStructuredProgress) {
|
|
432
|
+
console.log(`Indexed ${rootDir}: ready=${run.result.ready}, failed=${run.result.failed}, added=${run.result.added}, modified=${run.result.modified}, deleted=${run.result.deleted}, unchanged=${run.result.unchanged}`);
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
catch (error) {
|
|
436
|
+
const message = errorMessage(error);
|
|
437
|
+
const attempt = error instanceof WatchIndexRunError ? error.attempt : maxAttempts;
|
|
438
|
+
const failedMaxAttempts = error instanceof WatchIndexRunError ? error.maxAttempts : maxAttempts;
|
|
439
|
+
lastFailureAt = new Date().toISOString();
|
|
440
|
+
lastHealthError = message;
|
|
441
|
+
await writeHealth("failed", reason, { error: message });
|
|
442
|
+
reportWatchProgress(config, createWatchEvent({
|
|
443
|
+
type: "watch-index-failed",
|
|
444
|
+
rootDir,
|
|
445
|
+
outputDir,
|
|
446
|
+
reason,
|
|
447
|
+
attempt,
|
|
448
|
+
maxAttempts: failedMaxAttempts,
|
|
449
|
+
error: message
|
|
450
|
+
}));
|
|
451
|
+
if (reason === "initial") {
|
|
452
|
+
finishReady({ ok: false, error: message });
|
|
453
|
+
}
|
|
454
|
+
if (!hasStructuredProgress) {
|
|
455
|
+
console.error(`Index failed: ${message}`);
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
finally {
|
|
459
|
+
running = false;
|
|
460
|
+
if (pending && !stopped) {
|
|
461
|
+
await runIndex("change");
|
|
462
|
+
}
|
|
463
|
+
else if (stopped) {
|
|
464
|
+
await finishClose();
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
function scheduleIndex(eventName, changedPath) {
|
|
469
|
+
if (stopped) {
|
|
470
|
+
return;
|
|
471
|
+
}
|
|
472
|
+
if (!(0, scan_1.isIncludedPath)(changedPath, { exclude: config.exclude, include: config.include })) {
|
|
473
|
+
return;
|
|
474
|
+
}
|
|
475
|
+
reportWatchProgress(config, createWatchEvent({
|
|
476
|
+
type: "watch-file-event",
|
|
477
|
+
rootDir,
|
|
478
|
+
outputDir,
|
|
479
|
+
eventName,
|
|
480
|
+
path: changedPath
|
|
481
|
+
}));
|
|
482
|
+
if (!hasStructuredProgress) {
|
|
483
|
+
console.log(`${eventName}: ${changedPath}`);
|
|
484
|
+
}
|
|
485
|
+
if (timer) {
|
|
486
|
+
clearTimeout(timer);
|
|
487
|
+
}
|
|
488
|
+
timer = setTimeout(() => {
|
|
489
|
+
void runIndex("change");
|
|
490
|
+
}, debounceMs);
|
|
491
|
+
}
|
|
492
|
+
if (lockFile) {
|
|
493
|
+
releaseLock = await acquireLock(lockFile, rootDir, outputDir);
|
|
494
|
+
reportWatchProgress(config, createWatchEvent({
|
|
495
|
+
type: "watch-lock-acquired",
|
|
496
|
+
rootDir,
|
|
497
|
+
outputDir,
|
|
498
|
+
lockFile
|
|
499
|
+
}));
|
|
500
|
+
}
|
|
501
|
+
await writeHealth("starting");
|
|
502
|
+
reportWatchProgress(config, createWatchEvent({
|
|
503
|
+
type: "watch-start",
|
|
504
|
+
rootDir,
|
|
505
|
+
outputDir,
|
|
506
|
+
pid: process.pid
|
|
507
|
+
}));
|
|
508
|
+
void (async () => {
|
|
509
|
+
await runIndex("initial");
|
|
510
|
+
if (stopped) {
|
|
511
|
+
await finishClose();
|
|
512
|
+
return;
|
|
513
|
+
}
|
|
514
|
+
watcher = chokidar_1.default.watch(["**/*.md", "**/*.mdx"], {
|
|
515
|
+
cwd: rootDir,
|
|
516
|
+
ignored: isIgnored,
|
|
517
|
+
ignoreInitial: true,
|
|
518
|
+
persistent: true
|
|
519
|
+
});
|
|
520
|
+
watcher
|
|
521
|
+
.on("add", (changedPath) => scheduleIndex("add", changedPath))
|
|
522
|
+
.on("change", (changedPath) => scheduleIndex("change", changedPath))
|
|
523
|
+
.on("unlink", (changedPath) => scheduleIndex("unlink", changedPath));
|
|
524
|
+
if (stopped) {
|
|
525
|
+
await finishClose();
|
|
526
|
+
}
|
|
527
|
+
})();
|
|
528
|
+
return {
|
|
529
|
+
rootDir,
|
|
530
|
+
outputDir,
|
|
531
|
+
ready,
|
|
532
|
+
closed,
|
|
533
|
+
close
|
|
534
|
+
};
|
|
535
|
+
}
|
|
536
|
+
async function watchFolder(folder, options = {}) {
|
|
537
|
+
const handle = await startWatchFolder(folder, options);
|
|
538
|
+
const stop = () => {
|
|
539
|
+
void handle.close();
|
|
540
|
+
};
|
|
541
|
+
process.once("SIGINT", stop);
|
|
542
|
+
process.once("SIGTERM", stop);
|
|
543
|
+
try {
|
|
544
|
+
await handle.closed;
|
|
545
|
+
}
|
|
546
|
+
finally {
|
|
547
|
+
process.off("SIGINT", stop);
|
|
548
|
+
process.off("SIGTERM", stop);
|
|
549
|
+
}
|
|
550
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { createIndex, inspectIndex, queryIndex, validateIndex, watchIndex } from "./sdk";
|
|
2
|
+
export { startServe } from "./serve";
|
|
3
|
+
export type { RagboxConfig, RagboxConfigSource, RagboxIndexConfig, RagboxLlmConfig, RagboxPageIndexConfig } from "./config-file";
|
|
4
|
+
export type { CreateIndexOptions, CreateIndexResult, IndexCounts, InspectIndexDocument, InspectIndexResult, LlmChatRequest, LlmClient, QueryIndexOptions, QueryResult, SdkOptions, ValidateIndexResult, ValidationIssue, WatchIndexHandle, WatchIndexOptions, WatchIndexReadyResult } from "./sdk";
|
|
5
|
+
export type { ServeHandle, ServeHealthResult, ServeIndexesResult, ServeIndexSummary, ServeOptions } from "./serve";
|
|
6
|
+
export * as advanced from "./advanced";
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.advanced = exports.startServe = exports.watchIndex = exports.validateIndex = exports.queryIndex = exports.inspectIndex = exports.createIndex = void 0;
|
|
37
|
+
var sdk_1 = require("./sdk");
|
|
38
|
+
Object.defineProperty(exports, "createIndex", { enumerable: true, get: function () { return sdk_1.createIndex; } });
|
|
39
|
+
Object.defineProperty(exports, "inspectIndex", { enumerable: true, get: function () { return sdk_1.inspectIndex; } });
|
|
40
|
+
Object.defineProperty(exports, "queryIndex", { enumerable: true, get: function () { return sdk_1.queryIndex; } });
|
|
41
|
+
Object.defineProperty(exports, "validateIndex", { enumerable: true, get: function () { return sdk_1.validateIndex; } });
|
|
42
|
+
Object.defineProperty(exports, "watchIndex", { enumerable: true, get: function () { return sdk_1.watchIndex; } });
|
|
43
|
+
var serve_1 = require("./serve");
|
|
44
|
+
Object.defineProperty(exports, "startServe", { enumerable: true, get: function () { return serve_1.startServe; } });
|
|
45
|
+
exports.advanced = __importStar(require("./advanced"));
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { DocumentRecord, IndexCounts, IndexProgressEvent, LlmClient, Manifest, QueryResult, RootTreeNode, WatchProgressEvent } from "./folder-index/types";
|
|
2
|
+
export type { IndexCounts, IndexProgressEvent, LlmChatRequest, LlmClient, QueryResult, WatchProgressEvent } from "./folder-index/types";
|
|
3
|
+
export type SdkOptions = {
|
|
4
|
+
apiKey?: string;
|
|
5
|
+
baseUrl?: string;
|
|
6
|
+
configPath?: string;
|
|
7
|
+
model?: string;
|
|
8
|
+
outputDir?: string;
|
|
9
|
+
source?: string;
|
|
10
|
+
env?: NodeJS.ProcessEnv;
|
|
11
|
+
llmClient?: LlmClient;
|
|
12
|
+
};
|
|
13
|
+
export type CreateIndexOptions = SdkOptions & {
|
|
14
|
+
exclude?: string[];
|
|
15
|
+
include?: string[];
|
|
16
|
+
pageIndexCli?: string;
|
|
17
|
+
pageIndexPython?: string;
|
|
18
|
+
pageIndexOutputArg?: string;
|
|
19
|
+
pageIndexExtraArgs?: string[];
|
|
20
|
+
concurrency?: number;
|
|
21
|
+
onProgress?: (event: IndexProgressEvent) => void;
|
|
22
|
+
};
|
|
23
|
+
export type QueryIndexOptions = SdkOptions & {
|
|
24
|
+
trace?: boolean;
|
|
25
|
+
};
|
|
26
|
+
export type WatchIndexOptions = CreateIndexOptions & {
|
|
27
|
+
debounceMs?: number;
|
|
28
|
+
healthFile?: string;
|
|
29
|
+
lockFile?: string;
|
|
30
|
+
onEvent?: (event: WatchProgressEvent) => void;
|
|
31
|
+
retryAttempts?: number;
|
|
32
|
+
retryDelayMs?: number;
|
|
33
|
+
staging?: boolean;
|
|
34
|
+
stagingOutputDir?: string;
|
|
35
|
+
webhookUrl?: string;
|
|
36
|
+
};
|
|
37
|
+
export type CreateIndexResult = {
|
|
38
|
+
version: 1;
|
|
39
|
+
rootDir: string;
|
|
40
|
+
outputDir: string;
|
|
41
|
+
manifestPath: string;
|
|
42
|
+
rootTreePath: string;
|
|
43
|
+
generatedAt: string;
|
|
44
|
+
counts: IndexCounts;
|
|
45
|
+
manifest: Manifest;
|
|
46
|
+
rootTree: RootTreeNode;
|
|
47
|
+
};
|
|
48
|
+
export type WatchIndexReadyResult = {
|
|
49
|
+
ok: true;
|
|
50
|
+
result: CreateIndexResult;
|
|
51
|
+
} | {
|
|
52
|
+
ok: false;
|
|
53
|
+
error: string;
|
|
54
|
+
};
|
|
55
|
+
export type WatchIndexHandle = {
|
|
56
|
+
rootDir: string;
|
|
57
|
+
outputDir: string;
|
|
58
|
+
ready: Promise<WatchIndexReadyResult>;
|
|
59
|
+
closed: Promise<void>;
|
|
60
|
+
close: () => Promise<void>;
|
|
61
|
+
};
|
|
62
|
+
export type InspectIndexDocument = {
|
|
63
|
+
docId: string;
|
|
64
|
+
path: string;
|
|
65
|
+
title: string;
|
|
66
|
+
status: DocumentRecord["status"];
|
|
67
|
+
indexPath: string;
|
|
68
|
+
summary?: string;
|
|
69
|
+
size: number;
|
|
70
|
+
mtimeMs: number;
|
|
71
|
+
};
|
|
72
|
+
export type InspectIndexResult = {
|
|
73
|
+
version: 1;
|
|
74
|
+
target: string;
|
|
75
|
+
rootDir: string;
|
|
76
|
+
outputDir: string;
|
|
77
|
+
manifestPath: string;
|
|
78
|
+
rootTreePath: string;
|
|
79
|
+
generatedAt: string;
|
|
80
|
+
counts: IndexCounts;
|
|
81
|
+
documents: InspectIndexDocument[];
|
|
82
|
+
};
|
|
83
|
+
export type ValidationIssue = {
|
|
84
|
+
code: string;
|
|
85
|
+
message: string;
|
|
86
|
+
path?: string;
|
|
87
|
+
docId?: string;
|
|
88
|
+
};
|
|
89
|
+
export type ValidateIndexResult = {
|
|
90
|
+
version: 1;
|
|
91
|
+
target: string;
|
|
92
|
+
ok: boolean;
|
|
93
|
+
errors: ValidationIssue[];
|
|
94
|
+
warnings: ValidationIssue[];
|
|
95
|
+
inspect?: InspectIndexResult;
|
|
96
|
+
};
|
|
97
|
+
export declare function createIndex(folder: string, options?: CreateIndexOptions): Promise<CreateIndexResult>;
|
|
98
|
+
export declare function queryIndex(target: string, question: string, options?: QueryIndexOptions): Promise<QueryResult>;
|
|
99
|
+
export declare function watchIndex(folder: string, options?: WatchIndexOptions): Promise<WatchIndexHandle>;
|
|
100
|
+
export declare function inspectIndex(target: string): Promise<InspectIndexResult>;
|
|
101
|
+
export declare function validateIndex(target: string): Promise<ValidateIndexResult>;
|