@autorag/librarian 2.13.5 → 2.13.6
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/dist/agent/agent.d.ts +1 -1
- package/dist/cli/index.js +247 -182
- package/dist/core.js +247 -182
- package/dist/index.js +247 -182
- package/dist/mcp/index.js +247 -182
- package/dist/mirror/sync.d.ts +46 -0
- package/dist/profiles.js +247 -182
- package/package.json +1 -1
package/dist/mcp/index.js
CHANGED
|
@@ -10374,195 +10374,225 @@ async function syncWithRegistry(options, registry, isolated) {
|
|
|
10374
10374
|
});
|
|
10375
10375
|
const pending = new Set(plan.decisions.filter((decision) => decision.action === "process" || decision.action === "retry").map((decision) => decision.key));
|
|
10376
10376
|
const deferred = new Map(plan.decisions.filter((decision) => decision.deferred === true).map((decision) => [decision.key, decision]));
|
|
10377
|
-
|
|
10378
|
-
|
|
10379
|
-
|
|
10380
|
-
const path = pathsByKey.get(decision.key);
|
|
10381
|
-
await session.delete(decision.key);
|
|
10382
|
-
if (path === undefined)
|
|
10383
|
-
continue;
|
|
10384
|
-
if (previous.entries[path])
|
|
10385
|
-
deleted += 1;
|
|
10386
|
-
if (!currentPaths.has(path))
|
|
10387
|
-
diagnostics.push({
|
|
10388
|
-
code: "deleted-mirror",
|
|
10389
|
-
severity: "info",
|
|
10390
|
-
message: "A previously indexed document is gone; its parsed mirror was removed.",
|
|
10391
|
-
source: path
|
|
10392
|
-
});
|
|
10393
|
-
}
|
|
10394
|
-
await gate.classify(current.filter((entry) => pending.has(mirrorStageKey(entry.virtualPath))).map((entry) => entry.sourcePath));
|
|
10395
|
-
for (const [position, entry] of current.entries()) {
|
|
10396
|
-
options.onProgress?.({
|
|
10397
|
-
processed: position,
|
|
10398
|
-
total: current.length,
|
|
10399
|
-
written,
|
|
10400
|
-
deleted,
|
|
10401
|
-
skipped,
|
|
10402
|
-
currentVirtualPath: entry.virtualPath
|
|
10403
|
-
});
|
|
10404
|
-
if (options.signal?.aborted)
|
|
10405
|
-
throw abortReason(options.signal);
|
|
10406
|
-
const omission = omitted.get(entry.virtualPath);
|
|
10407
|
-
if (omission !== undefined) {
|
|
10408
|
-
if (pending.has(mirrorStageKey(entry.virtualPath))) {
|
|
10409
|
-
if (previous.entries[entry.virtualPath])
|
|
10410
|
-
deleted += 1;
|
|
10411
|
-
await session.skip(mirrorStageKey(entry.virtualPath), versions.get(entry.virtualPath), omission.reason);
|
|
10412
|
-
}
|
|
10413
|
-
skipped += 1;
|
|
10414
|
-
if (omission.diagnostic)
|
|
10415
|
-
diagnostics.push({ ...omission.diagnostic, source: entry.virtualPath });
|
|
10416
|
-
continue;
|
|
10417
|
-
}
|
|
10418
|
-
const deferredFailure = deferred.get(mirrorStageKey(entry.virtualPath));
|
|
10419
|
-
if (deferredFailure !== undefined) {
|
|
10420
|
-
skipped += 1;
|
|
10421
|
-
const code = deferredFailure.error === "source-unreadable" ? "source-unreadable" : "parser-failed";
|
|
10422
|
-
diagnostics.push({
|
|
10423
|
-
code,
|
|
10424
|
-
severity: "warning",
|
|
10425
|
-
source: entry.virtualPath,
|
|
10426
|
-
message: code === "parser-failed" ? PARSER_FAILURE_MESSAGE : "The source could not be read; retry is deferred until the source or settings change, or an explicit retry is requested."
|
|
10427
|
-
});
|
|
10428
|
-
continue;
|
|
10429
|
-
}
|
|
10430
|
-
const parser = registry.getForVirtualPath(entry.virtualPath);
|
|
10431
|
-
const outputPath = parsedOutputPath(options.root, entry.virtualPath);
|
|
10432
|
-
const cloudDecision = pending.has(mirrorStageKey(entry.virtualPath)) ? await gate.decide(entry.sourcePath, entry.sizeBytes, dirname12(entry.sourcePath)) : { kind: "local" };
|
|
10433
|
-
if (cloudDecision.kind === "skip") {
|
|
10434
|
-
skipped += 1;
|
|
10435
|
-
diagnostics.push({
|
|
10436
|
-
code: "cloud-placeholder-skipped",
|
|
10437
|
-
severity: "info",
|
|
10438
|
-
message: `The file is not downloaded and its provider has no verified release path, so it was not read; its last indexed copy was kept. Make it available offline to index it.`,
|
|
10439
|
-
source: entry.virtualPath,
|
|
10440
|
-
...cloudDecision.provider === undefined ? {} : { reason: cloudDecision.provider }
|
|
10441
|
-
});
|
|
10442
|
-
continue;
|
|
10443
|
-
}
|
|
10444
|
-
if (cloudDecision.kind === "defer") {
|
|
10445
|
-
skipped += 1;
|
|
10446
|
-
diagnostics.push({
|
|
10447
|
-
code: "cloud-download-deferred",
|
|
10448
|
-
severity: "info",
|
|
10449
|
-
message: "The download budget for this refresh is used up; the file stays queued for the next refresh.",
|
|
10450
|
-
source: entry.virtualPath
|
|
10451
|
-
});
|
|
10452
|
-
continue;
|
|
10453
|
-
}
|
|
10454
|
-
if (pending.has(mirrorStageKey(entry.virtualPath))) {
|
|
10455
|
-
let bytes;
|
|
10456
|
-
try {
|
|
10457
|
-
bytes = await withTransientRetry(() => readFile(entry.sourcePath), transientRetries, transientBackoffMs);
|
|
10458
|
-
} catch (error) {
|
|
10459
|
-
const failure = sourceReadFailure(error);
|
|
10460
|
-
await skipFile(entry, failure, failure.code === "source-unreadable" ? "source-unreadable" : undefined);
|
|
10377
|
+
const applyDeleteDecisions = async () => {
|
|
10378
|
+
for (const decision of plan.decisions) {
|
|
10379
|
+
if (decision.action !== "delete")
|
|
10461
10380
|
continue;
|
|
10462
|
-
|
|
10463
|
-
|
|
10464
|
-
if (
|
|
10465
|
-
await skipFile(entry, { code: "parser-skipped", severity: "warning", message: tooSmallImage }, "parser-skipped");
|
|
10381
|
+
const path = pathsByKey.get(decision.key);
|
|
10382
|
+
await session.delete(decision.key);
|
|
10383
|
+
if (path === undefined)
|
|
10466
10384
|
continue;
|
|
10467
|
-
|
|
10468
|
-
|
|
10469
|
-
|
|
10470
|
-
|
|
10471
|
-
|
|
10472
|
-
|
|
10473
|
-
|
|
10474
|
-
|
|
10475
|
-
|
|
10476
|
-
|
|
10477
|
-
|
|
10478
|
-
|
|
10479
|
-
|
|
10480
|
-
|
|
10481
|
-
|
|
10482
|
-
|
|
10483
|
-
|
|
10484
|
-
|
|
10485
|
-
|
|
10486
|
-
|
|
10487
|
-
|
|
10385
|
+
if (previous.entries[path])
|
|
10386
|
+
deleted += 1;
|
|
10387
|
+
if (!currentPaths.has(path))
|
|
10388
|
+
diagnostics.push({
|
|
10389
|
+
code: "deleted-mirror",
|
|
10390
|
+
severity: "info",
|
|
10391
|
+
message: "A previously indexed document is gone; its parsed mirror was removed.",
|
|
10392
|
+
source: path
|
|
10393
|
+
});
|
|
10394
|
+
}
|
|
10395
|
+
};
|
|
10396
|
+
const deferDeletions = options.onBatch !== undefined;
|
|
10397
|
+
const batchSize = Math.max(1, Math.floor(options.batchSize ?? DEFAULT_MIRROR_BATCH_SIZE));
|
|
10398
|
+
const batchCount = Math.max(1, Math.ceil(current.length / batchSize));
|
|
10399
|
+
let batchIndex = 0;
|
|
10400
|
+
let batchStopped = false;
|
|
10401
|
+
const runBatch = async (virtualPaths, final) => {
|
|
10402
|
+
if (options.onBatch === undefined)
|
|
10403
|
+
return;
|
|
10404
|
+
batchIndex += 1;
|
|
10405
|
+
const outcome = await options.onBatch({ index: batchIndex, total: batchCount, virtualPaths, final });
|
|
10406
|
+
if (outcome?.stop === true)
|
|
10407
|
+
batchStopped = true;
|
|
10408
|
+
};
|
|
10409
|
+
if (!deferDeletions)
|
|
10410
|
+
await applyDeleteDecisions();
|
|
10411
|
+
await gate.classify(current.filter((entry) => pending.has(mirrorStageKey(entry.virtualPath))).map((entry) => entry.sourcePath));
|
|
10412
|
+
for (let batchStart = 0;batchStart < Math.max(current.length, 1); batchStart += batchSize) {
|
|
10413
|
+
const batchEnd = Math.min(batchStart + batchSize, current.length);
|
|
10414
|
+
const finalBatch = batchEnd >= current.length;
|
|
10415
|
+
const changedBefore = written + deleted;
|
|
10416
|
+
for (let position = batchStart;position < batchEnd; position += 1) {
|
|
10417
|
+
const entry = current[position];
|
|
10418
|
+
options.onProgress?.({
|
|
10419
|
+
processed: position,
|
|
10420
|
+
total: current.length,
|
|
10421
|
+
written,
|
|
10422
|
+
deleted,
|
|
10423
|
+
skipped,
|
|
10424
|
+
currentVirtualPath: entry.virtualPath
|
|
10425
|
+
});
|
|
10426
|
+
if (options.signal?.aborted)
|
|
10427
|
+
throw abortReason(options.signal);
|
|
10428
|
+
const omission = omitted.get(entry.virtualPath);
|
|
10429
|
+
if (omission !== undefined) {
|
|
10430
|
+
if (pending.has(mirrorStageKey(entry.virtualPath))) {
|
|
10431
|
+
if (previous.entries[entry.virtualPath])
|
|
10432
|
+
deleted += 1;
|
|
10433
|
+
await session.skip(mirrorStageKey(entry.virtualPath), versions.get(entry.virtualPath), omission.reason);
|
|
10434
|
+
}
|
|
10435
|
+
skipped += 1;
|
|
10436
|
+
if (omission.diagnostic)
|
|
10437
|
+
diagnostics.push({ ...omission.diagnostic, source: entry.virtualPath });
|
|
10488
10438
|
continue;
|
|
10489
10439
|
}
|
|
10490
|
-
|
|
10491
|
-
|
|
10492
|
-
|
|
10493
|
-
|
|
10494
|
-
|
|
10440
|
+
const deferredFailure = deferred.get(mirrorStageKey(entry.virtualPath));
|
|
10441
|
+
if (deferredFailure !== undefined) {
|
|
10442
|
+
skipped += 1;
|
|
10443
|
+
const code = deferredFailure.error === "source-unreadable" ? "source-unreadable" : "parser-failed";
|
|
10444
|
+
diagnostics.push({
|
|
10445
|
+
code,
|
|
10446
|
+
severity: "warning",
|
|
10447
|
+
source: entry.virtualPath,
|
|
10448
|
+
message: code === "parser-failed" ? PARSER_FAILURE_MESSAGE : "The source could not be read; retry is deferred until the source or settings change, or an explicit retry is requested."
|
|
10449
|
+
});
|
|
10495
10450
|
continue;
|
|
10496
10451
|
}
|
|
10497
|
-
const
|
|
10498
|
-
|
|
10452
|
+
const parser = registry.getForVirtualPath(entry.virtualPath);
|
|
10453
|
+
const outputPath = parsedOutputPath(options.root, entry.virtualPath);
|
|
10454
|
+
const cloudDecision = pending.has(mirrorStageKey(entry.virtualPath)) ? await gate.decide(entry.sourcePath, entry.sizeBytes, dirname12(entry.sourcePath)) : { kind: "local" };
|
|
10455
|
+
if (cloudDecision.kind === "skip") {
|
|
10499
10456
|
skipped += 1;
|
|
10500
10457
|
diagnostics.push({
|
|
10501
|
-
code: "
|
|
10502
|
-
severity: "
|
|
10503
|
-
message:
|
|
10504
|
-
source: entry.virtualPath
|
|
10458
|
+
code: "cloud-placeholder-skipped",
|
|
10459
|
+
severity: "info",
|
|
10460
|
+
message: `The file is not downloaded and its provider has no verified release path, so it was not read; its last indexed copy was kept. Make it available offline to index it.`,
|
|
10461
|
+
source: entry.virtualPath,
|
|
10462
|
+
...cloudDecision.provider === undefined ? {} : { reason: cloudDecision.provider }
|
|
10505
10463
|
});
|
|
10506
|
-
await session.fail(mirrorStageKey(entry.virtualPath), versions.get(entry.virtualPath), "source-changed");
|
|
10507
10464
|
continue;
|
|
10508
10465
|
}
|
|
10509
|
-
|
|
10510
|
-
|
|
10511
|
-
|
|
10512
|
-
|
|
10513
|
-
|
|
10514
|
-
|
|
10515
|
-
|
|
10516
|
-
await skipFile(entry, {
|
|
10517
|
-
code: "mirror-write-failed",
|
|
10518
|
-
severity: "warning",
|
|
10519
|
-
message: error instanceof Error ? error.message : String(error),
|
|
10520
|
-
...code !== undefined ? { reason: code } : {}
|
|
10466
|
+
if (cloudDecision.kind === "defer") {
|
|
10467
|
+
skipped += 1;
|
|
10468
|
+
diagnostics.push({
|
|
10469
|
+
code: "cloud-download-deferred",
|
|
10470
|
+
severity: "info",
|
|
10471
|
+
message: "The download budget for this refresh is used up; the file stays queued for the next refresh.",
|
|
10472
|
+
source: entry.virtualPath
|
|
10521
10473
|
});
|
|
10522
10474
|
continue;
|
|
10523
10475
|
}
|
|
10524
|
-
|
|
10525
|
-
|
|
10526
|
-
|
|
10527
|
-
|
|
10528
|
-
|
|
10529
|
-
|
|
10530
|
-
|
|
10531
|
-
|
|
10532
|
-
|
|
10533
|
-
|
|
10534
|
-
|
|
10535
|
-
|
|
10536
|
-
|
|
10537
|
-
|
|
10538
|
-
|
|
10539
|
-
|
|
10540
|
-
|
|
10541
|
-
|
|
10542
|
-
|
|
10543
|
-
|
|
10544
|
-
|
|
10545
|
-
|
|
10546
|
-
|
|
10476
|
+
if (pending.has(mirrorStageKey(entry.virtualPath))) {
|
|
10477
|
+
let bytes;
|
|
10478
|
+
try {
|
|
10479
|
+
bytes = await withTransientRetry(() => readFile(entry.sourcePath), transientRetries, transientBackoffMs);
|
|
10480
|
+
} catch (error) {
|
|
10481
|
+
const failure = sourceReadFailure(error);
|
|
10482
|
+
await skipFile(entry, failure, failure.code === "source-unreadable" ? "source-unreadable" : undefined);
|
|
10483
|
+
continue;
|
|
10484
|
+
}
|
|
10485
|
+
const tooSmallImage = standaloneImageTooSmallMessage(entry.virtualPath, bytes);
|
|
10486
|
+
if (tooSmallImage !== undefined) {
|
|
10487
|
+
await skipFile(entry, { code: "parser-skipped", severity: "warning", message: tooSmallImage }, "parser-skipped");
|
|
10488
|
+
continue;
|
|
10489
|
+
}
|
|
10490
|
+
let parsed;
|
|
10491
|
+
try {
|
|
10492
|
+
const input = { virtualPath: entry.virtualPath, sourcePath: entry.sourcePath, bytes };
|
|
10493
|
+
parsed = isolated !== undefined && ISOLATED_PARSER_NAMES.has(parser.name) ? await isolated.parse(parser.name, input, options.signal) : await parser.parse(input);
|
|
10494
|
+
} catch (error) {
|
|
10495
|
+
if (options.signal?.aborted)
|
|
10496
|
+
throw abortReason(options.signal);
|
|
10497
|
+
if (error instanceof ParseError)
|
|
10498
|
+
await skipFile(entry, {
|
|
10499
|
+
code: "parser-failed",
|
|
10547
10500
|
severity: "warning",
|
|
10548
|
-
message:
|
|
10549
|
-
|
|
10501
|
+
message: error.cause instanceof ParseIsolationError ? `${PARSER_FAILURE_MESSAGE} ${error.cause.message}.` : PARSER_FAILURE_MESSAGE
|
|
10502
|
+
}, "parser-failed");
|
|
10503
|
+
else
|
|
10504
|
+
await skipFile(entry, {
|
|
10505
|
+
code: "parser-crashed",
|
|
10506
|
+
severity: "warning",
|
|
10507
|
+
message: PARSER_CRASH_MESSAGE,
|
|
10508
|
+
...error instanceof Error ? { reason: error.name } : {}
|
|
10550
10509
|
});
|
|
10551
|
-
|
|
10510
|
+
continue;
|
|
10552
10511
|
}
|
|
10553
|
-
|
|
10554
|
-
|
|
10555
|
-
|
|
10556
|
-
|
|
10512
|
+
let info;
|
|
10513
|
+
try {
|
|
10514
|
+
info = await stat3(entry.sourcePath, { bigint: true });
|
|
10515
|
+
} catch (error) {
|
|
10516
|
+
await skipFile(entry, sourceReadFailure(error));
|
|
10517
|
+
continue;
|
|
10518
|
+
}
|
|
10519
|
+
const unchanged = cloudDecision.kind === "download" ? contentIdentity(info) === entry.contentIdentity : statIdentity(info) === entry.statIdentity;
|
|
10520
|
+
if (!unchanged) {
|
|
10521
|
+
skipped += 1;
|
|
10557
10522
|
diagnostics.push({
|
|
10558
|
-
code: "
|
|
10523
|
+
code: "source-changed",
|
|
10559
10524
|
severity: "warning",
|
|
10560
|
-
message:
|
|
10525
|
+
message: "The source changed while parsing; its last good mirror was kept and it will be retried on the next refresh.",
|
|
10561
10526
|
source: entry.virtualPath
|
|
10562
10527
|
});
|
|
10528
|
+
await session.fail(mirrorStageKey(entry.virtualPath), versions.get(entry.virtualPath), "source-changed");
|
|
10529
|
+
continue;
|
|
10530
|
+
}
|
|
10531
|
+
const markdown = normalizeMarkdown(parsed.markdown);
|
|
10532
|
+
try {
|
|
10533
|
+
await withTransientRetry(() => writeFileAtomic(outputPath, markdown), transientRetries, transientBackoffMs);
|
|
10534
|
+
} catch (error) {
|
|
10535
|
+
const code = errorCode(error);
|
|
10536
|
+
if (code !== undefined && Object.hasOwn(WORKSPACE_ERROR_CODES, code))
|
|
10537
|
+
throw new MirrorWorkspaceError(options.root, error);
|
|
10538
|
+
await skipFile(entry, {
|
|
10539
|
+
code: "mirror-write-failed",
|
|
10540
|
+
severity: "warning",
|
|
10541
|
+
message: error instanceof Error ? error.message : String(error),
|
|
10542
|
+
...code !== undefined ? { reason: code } : {}
|
|
10543
|
+
});
|
|
10544
|
+
continue;
|
|
10545
|
+
}
|
|
10546
|
+
await session.commit(mirrorStageKey(entry.virtualPath), versions.get(entry.virtualPath), {
|
|
10547
|
+
path: basename10(outputPath),
|
|
10548
|
+
size: Buffer.byteLength(markdown)
|
|
10549
|
+
});
|
|
10550
|
+
for (const diagnostic of parsed.diagnostics ?? [])
|
|
10551
|
+
diagnostics.push({ ...diagnostic, source: entry.virtualPath });
|
|
10552
|
+
written += 1;
|
|
10553
|
+
if (fingerprintCloud && cloudFilePaths.has(entry.sourcePath)) {
|
|
10554
|
+
const existing = cloudFingerprintEntries[entry.sourcePath];
|
|
10555
|
+
if (existing === undefined || existing.sourceVersion !== entry.sourceVersion) {
|
|
10556
|
+
try {
|
|
10557
|
+
const fingerprintResult = await fingerprintWithDupey(entry.sourcePath, options.cloudFingerprints?.dupey ?? {});
|
|
10558
|
+
cloudFingerprintEntries[entry.sourcePath] = {
|
|
10559
|
+
sourceVersion: entry.sourceVersion,
|
|
10560
|
+
exact: fingerprintResult.exact,
|
|
10561
|
+
chars: fingerprintResult.chars,
|
|
10562
|
+
modified: fingerprintResult.modified,
|
|
10563
|
+
revision: fingerprintResult.revision
|
|
10564
|
+
};
|
|
10565
|
+
cloudFingerprintsDirty = true;
|
|
10566
|
+
} catch (error) {
|
|
10567
|
+
diagnostics.push({
|
|
10568
|
+
code: "cloud-fingerprint-failed",
|
|
10569
|
+
severity: "warning",
|
|
10570
|
+
message: `The cloud or network file was indexed but its duplicate fingerprint could not be read, so it will not take part in exact-duplicate exclusion: ${error instanceof Error ? error.message : String(error)}`,
|
|
10571
|
+
source: entry.virtualPath
|
|
10572
|
+
});
|
|
10573
|
+
}
|
|
10574
|
+
}
|
|
10575
|
+
}
|
|
10576
|
+
if (cloudDecision.kind === "download" && gate.evictAfterParse) {
|
|
10577
|
+
const released = await gate.release(entry.sourcePath);
|
|
10578
|
+
if (!released.ok) {
|
|
10579
|
+
diagnostics.push({
|
|
10580
|
+
code: "cloud-evict-failed",
|
|
10581
|
+
severity: "warning",
|
|
10582
|
+
message: `The file was indexed but its local copy could not be released: ${released.message}`,
|
|
10583
|
+
source: entry.virtualPath
|
|
10584
|
+
});
|
|
10585
|
+
}
|
|
10563
10586
|
}
|
|
10564
10587
|
}
|
|
10565
10588
|
}
|
|
10589
|
+
if (finalBatch && deferDeletions)
|
|
10590
|
+
await applyDeleteDecisions();
|
|
10591
|
+
if (finalBatch || written + deleted !== changedBefore) {
|
|
10592
|
+
await runBatch(current.slice(batchStart, batchEnd).map((item) => item.virtualPath), finalBatch);
|
|
10593
|
+
}
|
|
10594
|
+
if (batchStopped)
|
|
10595
|
+
break;
|
|
10566
10596
|
}
|
|
10567
10597
|
await session.finish();
|
|
10568
10598
|
if (fingerprintCloud) {
|
|
@@ -10854,7 +10884,7 @@ async function collectFiles(state, directory, depth) {
|
|
|
10854
10884
|
warnDirectoryUnreadable(state, directory, code);
|
|
10855
10885
|
}
|
|
10856
10886
|
}
|
|
10857
|
-
var DEFAULT_MAX_SOURCE_BYTES, DEFAULT_MAX_DELETE_RATIO = 0.3, MASS_DELETE_FLOOR = 100, DEFAULT_MAX_SCANNED_ENTRIES = 2000000, DEFAULT_MAX_WALK_DEPTH = 64, PRODUCT_ARTIFACT_FILE_NAMES, SKIP_DIR_NAMES, PARSER_FAILURE_MESSAGE = "The registered parser failed on this file; it was skipped during indexing.", PARSER_CRASH_MESSAGE = "The registered parser crashed unexpectedly on this file; it was skipped during indexing.", DEFAULT_TRANSIENT_RETRIES = 3, DEFAULT_TRANSIENT_BACKOFF_MS = 50, TRANSIENT_ERROR_CODES, WORKSPACE_ERROR_CODES, MirrorWorkspaceError, STANDALONE_OCR_IMAGE_EXTENSIONS, SEARCH_ROOT_UNAVAILABLE_MESSAGES;
|
|
10887
|
+
var DEFAULT_MAX_SOURCE_BYTES, DEFAULT_MAX_DELETE_RATIO = 0.3, MASS_DELETE_FLOOR = 100, DEFAULT_MAX_SCANNED_ENTRIES = 2000000, DEFAULT_MAX_WALK_DEPTH = 64, DEFAULT_MIRROR_BATCH_SIZE = 25, PRODUCT_ARTIFACT_FILE_NAMES, SKIP_DIR_NAMES, PARSER_FAILURE_MESSAGE = "The registered parser failed on this file; it was skipped during indexing.", PARSER_CRASH_MESSAGE = "The registered parser crashed unexpectedly on this file; it was skipped during indexing.", DEFAULT_TRANSIENT_RETRIES = 3, DEFAULT_TRANSIENT_BACKOFF_MS = 50, TRANSIENT_ERROR_CODES, WORKSPACE_ERROR_CODES, MirrorWorkspaceError, STANDALONE_OCR_IMAGE_EXTENSIONS, SEARCH_ROOT_UNAVAILABLE_MESSAGES;
|
|
10858
10888
|
var init_sync = __esm(() => {
|
|
10859
10889
|
init_cli2();
|
|
10860
10890
|
init_cloud_placeholder();
|
|
@@ -38693,11 +38723,52 @@ ${this.discoveryHint((tools) => `When exploring local files and folders, activel
|
|
|
38693
38723
|
};
|
|
38694
38724
|
try {
|
|
38695
38725
|
writeRefreshProgress(this.workspaceProjectRoot, progress);
|
|
38726
|
+
const batchedMinSync = needsParsed && wants("minsync");
|
|
38727
|
+
const minSyncBatches = [];
|
|
38728
|
+
const minSyncBatchFailures = [];
|
|
38729
|
+
let minSyncSyncedBase = 0;
|
|
38730
|
+
let minSyncTotalBase = 0;
|
|
38731
|
+
const minSyncBatch = async (batch) => {
|
|
38732
|
+
signal?.throwIfAborted();
|
|
38733
|
+
progress = updateRefreshProgress(progress, { phase: "minsync" });
|
|
38734
|
+
writeRefreshProgress(this.workspaceProjectRoot, progress);
|
|
38735
|
+
let batchTotal = minSyncTotalBase;
|
|
38736
|
+
const result = await this.syncMinSync(force && batch.index === 1, signal, (minsyncProgress) => {
|
|
38737
|
+
batchTotal = minSyncTotalBase + minsyncProgress.total;
|
|
38738
|
+
progress = updateRefreshProgress(progress, {
|
|
38739
|
+
minsync: { synced: minSyncSyncedBase + minsyncProgress.processed, total: batchTotal }
|
|
38740
|
+
});
|
|
38741
|
+
writeRefreshProgress(this.workspaceProjectRoot, progress);
|
|
38742
|
+
emitProgress({
|
|
38743
|
+
phase: "minsync",
|
|
38744
|
+
processed: minSyncSyncedBase + minsyncProgress.processed,
|
|
38745
|
+
total: batchTotal
|
|
38746
|
+
});
|
|
38747
|
+
});
|
|
38748
|
+
minSyncBatches.push(result);
|
|
38749
|
+
if (result.ok)
|
|
38750
|
+
minSyncBatchFailures.length = 0;
|
|
38751
|
+
else if (result.reason !== "lock-held") {
|
|
38752
|
+
for (const virtualPath of batch.virtualPaths) {
|
|
38753
|
+
minSyncBatchFailures.push({
|
|
38754
|
+
code: "minsync-sync-failed",
|
|
38755
|
+
severity: "warning",
|
|
38756
|
+
message: `This file stayed at its previous index entry: its batch could not be indexed and the next sync retries it. ${sanitizeDiagnosticMessage(result.diagnostic?.message ?? result.reason ?? "MinSync sync failed.")}`,
|
|
38757
|
+
source: virtualPath
|
|
38758
|
+
});
|
|
38759
|
+
}
|
|
38760
|
+
}
|
|
38761
|
+
minSyncSyncedBase = batchTotal;
|
|
38762
|
+
minSyncTotalBase = batchTotal;
|
|
38763
|
+
return !result.ok && result.reason === "lock-held" ? { stop: true } : undefined;
|
|
38764
|
+
};
|
|
38696
38765
|
const summary = needsParsed ? await this.syncParsedMirrors(force, {
|
|
38697
38766
|
...opts?.retrySkipped === true ? { retrySkipped: true } : {},
|
|
38698
38767
|
...signal === undefined ? {} : { signal },
|
|
38768
|
+
...batchedMinSync ? { onBatch: minSyncBatch } : {},
|
|
38699
38769
|
onProgress: (parsedProgress) => {
|
|
38700
38770
|
progress = updateRefreshProgress(progress, {
|
|
38771
|
+
phase: "parsed",
|
|
38701
38772
|
sourceFiles: { total: parsedProgress.total },
|
|
38702
38773
|
parsedCounts: {
|
|
38703
38774
|
scanned: parsedProgress.processed,
|
|
@@ -38719,7 +38790,7 @@ ${this.discoveryHint((tools) => `When exploring local files and folders, activel
|
|
|
38719
38790
|
}) : await this.scanMirrorStaleness();
|
|
38720
38791
|
signal?.throwIfAborted();
|
|
38721
38792
|
progress = updateRefreshProgress(progress, {
|
|
38722
|
-
phase: wants("
|
|
38793
|
+
phase: wants("datasources") ? "datasources" : wants("jikji") ? "jikji" : "finalizing",
|
|
38723
38794
|
sourceFiles: { total: summary.scanned },
|
|
38724
38795
|
parsedCounts: {
|
|
38725
38796
|
scanned: summary.scanned,
|
|
@@ -38729,24 +38800,12 @@ ${this.discoveryHint((tools) => `When exploring local files and folders, activel
|
|
|
38729
38800
|
}
|
|
38730
38801
|
});
|
|
38731
38802
|
writeRefreshProgress(this.workspaceProjectRoot, progress);
|
|
38732
|
-
const minsync =
|
|
38733
|
-
progress = updateRefreshProgress(progress, {
|
|
38734
|
-
minsync: { synced: minsyncProgress.processed, total: minsyncProgress.total }
|
|
38735
|
-
});
|
|
38736
|
-
writeRefreshProgress(this.workspaceProjectRoot, progress);
|
|
38737
|
-
emitProgress({
|
|
38738
|
-
phase: "minsync",
|
|
38739
|
-
processed: minsyncProgress.processed,
|
|
38740
|
-
total: minsyncProgress.total
|
|
38741
|
-
});
|
|
38742
|
-
}) : undefined;
|
|
38743
|
-
signal?.throwIfAborted();
|
|
38803
|
+
const minsync = batchedMinSync ? aggregateMinSyncBatches(minSyncBatches) : undefined;
|
|
38744
38804
|
if (minsync !== undefined) {
|
|
38745
38805
|
progress = updateRefreshProgress(progress, {
|
|
38746
|
-
phase: wants("datasources") ? "datasources" : wants("jikji") ? "jikji" : "finalizing",
|
|
38747
38806
|
minsync: {
|
|
38748
|
-
...progress.minsync,
|
|
38749
38807
|
synced: minsync.synced,
|
|
38808
|
+
total: Math.max(minSyncTotalBase, minsync.synced),
|
|
38750
38809
|
...minsync.filesFailed !== undefined && minsync.filesFailed.length > 0 ? { filesSkipped: minsync.filesFailed.length } : {}
|
|
38751
38810
|
}
|
|
38752
38811
|
});
|
|
@@ -38808,7 +38867,7 @@ ${this.discoveryHint((tools) => `When exploring local files and folders, activel
|
|
|
38808
38867
|
finishedAt: new Date().toISOString()
|
|
38809
38868
|
});
|
|
38810
38869
|
writeRefreshProgress(this.workspaceProjectRoot, progress);
|
|
38811
|
-
const minsyncDiagnostics = minSyncRefreshDiagnostics(minsync);
|
|
38870
|
+
const minsyncDiagnostics = [...minSyncRefreshDiagnostics(minsync), ...minSyncBatchFailures];
|
|
38812
38871
|
const publicMinsync = minsync ? {
|
|
38813
38872
|
ok: minsync.ok,
|
|
38814
38873
|
synced: minsync.synced,
|
|
@@ -39613,6 +39672,12 @@ function fsearchRefreshDiagnostics(fsearch) {
|
|
|
39613
39672
|
}
|
|
39614
39673
|
];
|
|
39615
39674
|
}
|
|
39675
|
+
function aggregateMinSyncBatches(batches) {
|
|
39676
|
+
const last = batches.at(-1);
|
|
39677
|
+
if (last === undefined)
|
|
39678
|
+
return;
|
|
39679
|
+
return { ...last, synced: batches.reduce((total, batch) => total + batch.synced, 0) };
|
|
39680
|
+
}
|
|
39616
39681
|
function minSyncRefreshDiagnostics(minsync) {
|
|
39617
39682
|
if (!minsync)
|
|
39618
39683
|
return [];
|
package/dist/mirror/sync.d.ts
CHANGED
|
@@ -10,6 +10,12 @@ export declare const DEFAULT_MAX_DELETE_RATIO = 0.3;
|
|
|
10
10
|
export declare const DEFAULT_MAX_SCANNED_ENTRIES = 2000000;
|
|
11
11
|
/** Deepest directory level (search root = 0) the walk descends into. */
|
|
12
12
|
export declare const DEFAULT_MAX_WALK_DEPTH = 64;
|
|
13
|
+
/**
|
|
14
|
+
* Sources parsed and committed per batch before {@link ParsedMirrorSyncOptions.onBatch}
|
|
15
|
+
* runs. Small enough that a corpus becomes searchable after its first slice,
|
|
16
|
+
* large enough to amortize one index sync's startup.
|
|
17
|
+
*/
|
|
18
|
+
export declare const DEFAULT_MIRROR_BATCH_SIZE = 25;
|
|
13
19
|
export interface ParsedMirrorSyncOptions {
|
|
14
20
|
readonly root: string;
|
|
15
21
|
readonly searchPaths: readonly string[];
|
|
@@ -36,6 +42,20 @@ export interface ParsedMirrorSyncOptions {
|
|
|
36
42
|
readonly signal?: AbortSignal;
|
|
37
43
|
/** Called before each file and once more with `processed === total` when the parse phase ends. */
|
|
38
44
|
readonly onProgress?: (progress: ParsedMirrorProgress) => void;
|
|
45
|
+
/**
|
|
46
|
+
* Sources parsed and committed per batch before {@link ParsedMirrorSyncOptions.onBatch}
|
|
47
|
+
* runs. Default {@link DEFAULT_MIRROR_BATCH_SIZE}.
|
|
48
|
+
*/
|
|
49
|
+
readonly batchSize?: number;
|
|
50
|
+
/**
|
|
51
|
+
* Runs after each batch of sources is parsed and committed to the mirror, and
|
|
52
|
+
* before the next batch is read, so a caller can make the batch's results
|
|
53
|
+
* searchable (stage them and sync the index). A thrown error aborts the
|
|
54
|
+
* refresh; returning `stop` ends the loop after this batch. Deletions of
|
|
55
|
+
* genuinely-removed sources wait for the final batch, so a mid-refresh call
|
|
56
|
+
* never drops a not-yet-reached document from the index.
|
|
57
|
+
*/
|
|
58
|
+
readonly onBatch?: (batch: ParsedMirrorBatch) => Promise<ParsedMirrorBatchOutcome | undefined>;
|
|
39
59
|
/**
|
|
40
60
|
* Largest share of previously indexed documents one refresh may delete because their source
|
|
41
61
|
* disappeared. A larger deletion (above 100 documents) is suppressed with `mass-delete-suppressed`
|
|
@@ -115,6 +135,32 @@ export interface ParsedMirrorSyncResult {
|
|
|
115
135
|
readonly indexPath: string;
|
|
116
136
|
readonly diagnostics: readonly ParsedMirrorDiagnostic[];
|
|
117
137
|
}
|
|
138
|
+
/**
|
|
139
|
+
* One batch of the source listing, parsed and committed to the mirror before
|
|
140
|
+
* {@link ParsedMirrorSyncOptions.onBatch} runs. The last batch is the only one
|
|
141
|
+
* whose run reflects deletions of genuinely-removed sources.
|
|
142
|
+
*/
|
|
143
|
+
export interface ParsedMirrorBatch {
|
|
144
|
+
/** 1-based index of this batch among the batches handed to `onBatch`. */
|
|
145
|
+
readonly index: number;
|
|
146
|
+
/**
|
|
147
|
+
* Batches the listing was divided into. A batch that wrote and deleted no
|
|
148
|
+
* mirror output is not handed to `onBatch`; the final batch always is.
|
|
149
|
+
*/
|
|
150
|
+
readonly total: number;
|
|
151
|
+
/** Virtual paths the refresh attempted in this batch. */
|
|
152
|
+
readonly virtualPaths: readonly string[];
|
|
153
|
+
/** The last batch: genuine deletions have by now been applied to the mirror. */
|
|
154
|
+
readonly final: boolean;
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* What {@link ParsedMirrorSyncOptions.onBatch} asks the refresh to do next.
|
|
158
|
+
* A thrown error aborts the refresh; `stop` ends the loop after this batch, so a
|
|
159
|
+
* systemic index failure never reads sources it knows it cannot index.
|
|
160
|
+
*/
|
|
161
|
+
export interface ParsedMirrorBatchOutcome {
|
|
162
|
+
readonly stop?: boolean;
|
|
163
|
+
}
|
|
118
164
|
/** Default retries for a transient per-file I/O error. Mirrors the `refresh.transientRetries` config default. */
|
|
119
165
|
export declare const DEFAULT_TRANSIENT_RETRIES = 3;
|
|
120
166
|
/**
|