la-machina-engine 0.7.0 → 0.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -0
- package/dist/index.cjs +242 -228
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +242 -228
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1181,6 +1181,15 @@ await writeKnowledgeIndex({ adapter: k, base: 'hr-policies' })
|
|
|
1181
1181
|
await writeKnowledgeIndex({ adapter: k, base: 'sales-playbook' })
|
|
1182
1182
|
```
|
|
1183
1183
|
|
|
1184
|
+
**Forgot to build the index?** Both tools fall back to an in-memory
|
|
1185
|
+
build on first call when `_index.json` is missing or corrupted. The
|
|
1186
|
+
fallback caches for the rest of the run, so subsequent searches are
|
|
1187
|
+
free. This makes the index a performance optimisation (skip the walk
|
|
1188
|
+
on every fresh run), not a setup requirement — drop files into the
|
|
1189
|
+
folder and the agent can discover them immediately. Pre-build with
|
|
1190
|
+
`writeKnowledgeIndex()` for production-scale corpora where the
|
|
1191
|
+
first-call cost matters.
|
|
1192
|
+
|
|
1184
1193
|
**Configure the engine** to enable the tools (off by default):
|
|
1185
1194
|
|
|
1186
1195
|
```ts
|
package/dist/index.cjs
CHANGED
|
@@ -8201,75 +8201,8 @@ init_cjs_shims();
|
|
|
8201
8201
|
var import_zod26 = require("zod");
|
|
8202
8202
|
init_contract();
|
|
8203
8203
|
|
|
8204
|
-
// src/knowledge/
|
|
8204
|
+
// src/knowledge/indexer.ts
|
|
8205
8205
|
init_cjs_shims();
|
|
8206
|
-
var SAFE_PATH_RE = /^[a-zA-Z0-9_\-./]+$/;
|
|
8207
|
-
function parseFolderRef(raw) {
|
|
8208
|
-
if (typeof raw !== "string" || raw.length === 0) {
|
|
8209
|
-
throw new Error(`invalid knowledge folder ref: empty`);
|
|
8210
|
-
}
|
|
8211
|
-
if (raw.startsWith("/")) {
|
|
8212
|
-
throw new Error(`invalid knowledge folder ref: absolute paths not allowed ("${raw}")`);
|
|
8213
|
-
}
|
|
8214
|
-
const trimmed = raw.replace(/\/+$/g, "");
|
|
8215
|
-
if (trimmed.length === 0) {
|
|
8216
|
-
throw new Error(`invalid knowledge folder ref: "${raw}"`);
|
|
8217
|
-
}
|
|
8218
|
-
if (!SAFE_PATH_RE.test(trimmed)) {
|
|
8219
|
-
throw new Error(`invalid knowledge folder ref: unsafe characters in "${raw}"`);
|
|
8220
|
-
}
|
|
8221
|
-
if (trimmed.split("/").some((seg) => seg === ".." || seg === "." || seg === "")) {
|
|
8222
|
-
throw new Error(`invalid knowledge folder ref: traversal in "${raw}"`);
|
|
8223
|
-
}
|
|
8224
|
-
const segs = trimmed.split("/");
|
|
8225
|
-
const base = segs[0];
|
|
8226
|
-
const subPath = segs.slice(1).join("/");
|
|
8227
|
-
return { path: trimmed, base, subPath };
|
|
8228
|
-
}
|
|
8229
|
-
function relPathInScope(folder, relPath) {
|
|
8230
|
-
if (folder.subPath === "") return true;
|
|
8231
|
-
return relPath === folder.subPath || relPath.startsWith(`${folder.subPath}/`);
|
|
8232
|
-
}
|
|
8233
|
-
function parseKnowledgeRef(raw) {
|
|
8234
|
-
if (typeof raw !== "string" || raw.length === 0) {
|
|
8235
|
-
throw new Error("invalid knowledge ref: empty");
|
|
8236
|
-
}
|
|
8237
|
-
if (raw.startsWith("ext:")) {
|
|
8238
|
-
const name = raw.slice("ext:".length);
|
|
8239
|
-
if (!/^[a-zA-Z0-9_-]+$/.test(name) || name.length === 0) {
|
|
8240
|
-
throw new Error(`invalid knowledge ref: external name "${name}" has unsafe characters`);
|
|
8241
|
-
}
|
|
8242
|
-
return { kind: "ext", target: name };
|
|
8243
|
-
}
|
|
8244
|
-
if (raw.startsWith("/")) {
|
|
8245
|
-
throw new Error(`invalid knowledge ref: absolute paths not allowed ("${raw}")`);
|
|
8246
|
-
}
|
|
8247
|
-
const hashAt = raw.indexOf("#");
|
|
8248
|
-
const filePath = hashAt === -1 ? raw : raw.slice(0, hashAt);
|
|
8249
|
-
const section = hashAt === -1 ? void 0 : raw.slice(hashAt + 1);
|
|
8250
|
-
if (!SAFE_PATH_RE.test(filePath)) {
|
|
8251
|
-
throw new Error(`invalid knowledge ref: unsafe characters in "${filePath}"`);
|
|
8252
|
-
}
|
|
8253
|
-
if (filePath.split("/").some((seg) => seg === ".." || seg === "." || seg === "")) {
|
|
8254
|
-
throw new Error(`invalid knowledge ref: traversal in "${filePath}"`);
|
|
8255
|
-
}
|
|
8256
|
-
if (section !== void 0) {
|
|
8257
|
-
if (section.length > 0 && !/^[a-zA-Z0-9_-]+$/.test(section)) {
|
|
8258
|
-
throw new Error(`invalid knowledge ref: unsafe characters in section "${section}"`);
|
|
8259
|
-
}
|
|
8260
|
-
return { kind: "section", target: filePath, section };
|
|
8261
|
-
}
|
|
8262
|
-
return { kind: "file", target: filePath };
|
|
8263
|
-
}
|
|
8264
|
-
function refInScope(folders, filePath) {
|
|
8265
|
-
return folders.some((f) => {
|
|
8266
|
-
if (filePath === f.base || filePath.startsWith(`${f.base}/`)) {
|
|
8267
|
-
const relInBase = filePath === f.base ? "" : filePath.slice(f.base.length + 1);
|
|
8268
|
-
return relPathInScope(f, relInBase);
|
|
8269
|
-
}
|
|
8270
|
-
return false;
|
|
8271
|
-
});
|
|
8272
|
-
}
|
|
8273
8206
|
|
|
8274
8207
|
// src/knowledge/tokenize.ts
|
|
8275
8208
|
init_cjs_shims();
|
|
@@ -8346,6 +8279,220 @@ function scoreOverlap(sectionWords, queryTokens) {
|
|
|
8346
8279
|
return n;
|
|
8347
8280
|
}
|
|
8348
8281
|
|
|
8282
|
+
// src/knowledge/indexer.ts
|
|
8283
|
+
var HEADING_RE = /^(#{1,6})[ \t]+(.+?)\s*$/;
|
|
8284
|
+
var WIKI_LINK_RE = /\[\[([^\]|#]+)(?:[#|][^\]]*)?\]\]/g;
|
|
8285
|
+
var FORMAT_BY_EXT = {
|
|
8286
|
+
md: "md",
|
|
8287
|
+
markdown: "md",
|
|
8288
|
+
txt: "txt",
|
|
8289
|
+
json: "json",
|
|
8290
|
+
csv: "csv",
|
|
8291
|
+
html: "html",
|
|
8292
|
+
htm: "html",
|
|
8293
|
+
pdf: "pdf",
|
|
8294
|
+
docx: "docx"
|
|
8295
|
+
};
|
|
8296
|
+
var PREVIEW_CHARS = 200;
|
|
8297
|
+
async function buildKnowledgeIndex(options) {
|
|
8298
|
+
const { adapter, base } = options;
|
|
8299
|
+
const safeBase = base.replace(/^\/+|\/+$/g, "");
|
|
8300
|
+
if (safeBase.length === 0 || safeBase.includes("..")) {
|
|
8301
|
+
throw new Error(`buildKnowledgeIndex: invalid base "${base}"`);
|
|
8302
|
+
}
|
|
8303
|
+
const files = await listFilesRecursive(adapter, safeBase);
|
|
8304
|
+
const sections = [];
|
|
8305
|
+
const filesMeta = {};
|
|
8306
|
+
for (const fileRel of files) {
|
|
8307
|
+
if (fileRel === "_index.json") continue;
|
|
8308
|
+
const fullPath = `${safeBase}/${fileRel}`;
|
|
8309
|
+
const ext = (fileRel.split(".").pop() ?? "").toLowerCase();
|
|
8310
|
+
const format = FORMAT_BY_EXT[ext];
|
|
8311
|
+
if (format === void 0) continue;
|
|
8312
|
+
const raw = await adapter.readFile(fullPath);
|
|
8313
|
+
if (raw === null) continue;
|
|
8314
|
+
const sizeBytes = byteLength3(raw);
|
|
8315
|
+
const meta = { format, size: sizeBytes };
|
|
8316
|
+
if (format === "md" || format === "txt") {
|
|
8317
|
+
const fileSections = splitSections(raw, fileRel);
|
|
8318
|
+
sections.push(...fileSections);
|
|
8319
|
+
const wikiLinks = extractWikiLinks(raw);
|
|
8320
|
+
if (wikiLinks.length > 0) meta.wikiLinks = wikiLinks;
|
|
8321
|
+
}
|
|
8322
|
+
filesMeta[fileRel] = meta;
|
|
8323
|
+
}
|
|
8324
|
+
return {
|
|
8325
|
+
schema: "v1",
|
|
8326
|
+
base: safeBase,
|
|
8327
|
+
builtAt: options.nowIso ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
8328
|
+
fileCount: Object.keys(filesMeta).length,
|
|
8329
|
+
sections,
|
|
8330
|
+
files: filesMeta
|
|
8331
|
+
};
|
|
8332
|
+
}
|
|
8333
|
+
async function writeKnowledgeIndex(options) {
|
|
8334
|
+
const index = await buildKnowledgeIndex(options);
|
|
8335
|
+
await options.adapter.writeFile(`${index.base}/_index.json`, JSON.stringify(index, null, 2));
|
|
8336
|
+
return index;
|
|
8337
|
+
}
|
|
8338
|
+
async function listFilesRecursive(adapter, dir) {
|
|
8339
|
+
const out = [];
|
|
8340
|
+
const stack = [""];
|
|
8341
|
+
while (stack.length > 0) {
|
|
8342
|
+
const sub = stack.pop();
|
|
8343
|
+
const fullDir = sub === "" ? dir : `${dir}/${sub}`;
|
|
8344
|
+
let entries = [];
|
|
8345
|
+
try {
|
|
8346
|
+
entries = await adapter.listDir(fullDir);
|
|
8347
|
+
} catch {
|
|
8348
|
+
continue;
|
|
8349
|
+
}
|
|
8350
|
+
for (const name of entries) {
|
|
8351
|
+
const childRel = sub === "" ? name : `${sub}/${name}`;
|
|
8352
|
+
const childFull = `${dir}/${childRel}`;
|
|
8353
|
+
const isDir = await adapter.isDirectory(childFull).catch(() => false);
|
|
8354
|
+
if (isDir) {
|
|
8355
|
+
stack.push(childRel);
|
|
8356
|
+
} else {
|
|
8357
|
+
out.push(childRel);
|
|
8358
|
+
}
|
|
8359
|
+
}
|
|
8360
|
+
}
|
|
8361
|
+
return out.sort();
|
|
8362
|
+
}
|
|
8363
|
+
function splitSections(content, relPath) {
|
|
8364
|
+
const lines = content.split(/\r?\n/);
|
|
8365
|
+
const out = [];
|
|
8366
|
+
const heads = [];
|
|
8367
|
+
for (let i = 0; i < lines.length; i++) {
|
|
8368
|
+
const line = lines[i];
|
|
8369
|
+
const m = HEADING_RE.exec(line);
|
|
8370
|
+
if (m) heads.push({ line: i + 1, depth: m[1].length, heading: m[2].trim() });
|
|
8371
|
+
}
|
|
8372
|
+
const leadInEndLine = heads.length > 0 ? heads[0].line - 1 : lines.length;
|
|
8373
|
+
const leadInBody = lines.slice(0, leadInEndLine).join("\n").trim();
|
|
8374
|
+
if (leadInBody.length > 0) {
|
|
8375
|
+
out.push({
|
|
8376
|
+
relPath,
|
|
8377
|
+
heading: "",
|
|
8378
|
+
slug: `${relPath}#`,
|
|
8379
|
+
depth: 0,
|
|
8380
|
+
words: tokenize(leadInBody),
|
|
8381
|
+
preview: makePreview(leadInBody),
|
|
8382
|
+
startLine: 1,
|
|
8383
|
+
endLine: leadInEndLine
|
|
8384
|
+
});
|
|
8385
|
+
}
|
|
8386
|
+
for (let i = 0; i < heads.length; i++) {
|
|
8387
|
+
const h = heads[i];
|
|
8388
|
+
const startLine = h.line;
|
|
8389
|
+
const endLine = i + 1 < heads.length ? heads[i + 1].line - 1 : lines.length;
|
|
8390
|
+
const body = lines.slice(startLine - 1, endLine).join("\n");
|
|
8391
|
+
out.push({
|
|
8392
|
+
relPath,
|
|
8393
|
+
heading: h.heading,
|
|
8394
|
+
slug: `${relPath}#${slugify(h.heading)}`,
|
|
8395
|
+
depth: h.depth,
|
|
8396
|
+
words: tokenize(body),
|
|
8397
|
+
preview: makePreview(body),
|
|
8398
|
+
startLine,
|
|
8399
|
+
endLine
|
|
8400
|
+
});
|
|
8401
|
+
}
|
|
8402
|
+
return out;
|
|
8403
|
+
}
|
|
8404
|
+
function makePreview(body) {
|
|
8405
|
+
const trimmed = body.replace(/^#{1,6}\s+.+$\r?\n?/m, "").trim();
|
|
8406
|
+
if (trimmed.length <= PREVIEW_CHARS) return trimmed;
|
|
8407
|
+
return trimmed.slice(0, PREVIEW_CHARS) + "\u2026";
|
|
8408
|
+
}
|
|
8409
|
+
function slugify(text2) {
|
|
8410
|
+
return text2.toLowerCase().trim().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
8411
|
+
}
|
|
8412
|
+
function extractWikiLinks(text2) {
|
|
8413
|
+
const seen = /* @__PURE__ */ new Set();
|
|
8414
|
+
let m;
|
|
8415
|
+
WIKI_LINK_RE.lastIndex = 0;
|
|
8416
|
+
while ((m = WIKI_LINK_RE.exec(text2)) !== null) {
|
|
8417
|
+
const target = m[1].trim();
|
|
8418
|
+
if (target.length > 0) seen.add(target);
|
|
8419
|
+
}
|
|
8420
|
+
return [...seen].sort();
|
|
8421
|
+
}
|
|
8422
|
+
function byteLength3(s) {
|
|
8423
|
+
return new TextEncoder().encode(s).byteLength;
|
|
8424
|
+
}
|
|
8425
|
+
|
|
8426
|
+
// src/knowledge/scope.ts
|
|
8427
|
+
init_cjs_shims();
|
|
8428
|
+
var SAFE_PATH_RE = /^[a-zA-Z0-9_\-./]+$/;
|
|
8429
|
+
function parseFolderRef(raw) {
|
|
8430
|
+
if (typeof raw !== "string" || raw.length === 0) {
|
|
8431
|
+
throw new Error(`invalid knowledge folder ref: empty`);
|
|
8432
|
+
}
|
|
8433
|
+
if (raw.startsWith("/")) {
|
|
8434
|
+
throw new Error(`invalid knowledge folder ref: absolute paths not allowed ("${raw}")`);
|
|
8435
|
+
}
|
|
8436
|
+
const trimmed = raw.replace(/\/+$/g, "");
|
|
8437
|
+
if (trimmed.length === 0) {
|
|
8438
|
+
throw new Error(`invalid knowledge folder ref: "${raw}"`);
|
|
8439
|
+
}
|
|
8440
|
+
if (!SAFE_PATH_RE.test(trimmed)) {
|
|
8441
|
+
throw new Error(`invalid knowledge folder ref: unsafe characters in "${raw}"`);
|
|
8442
|
+
}
|
|
8443
|
+
if (trimmed.split("/").some((seg) => seg === ".." || seg === "." || seg === "")) {
|
|
8444
|
+
throw new Error(`invalid knowledge folder ref: traversal in "${raw}"`);
|
|
8445
|
+
}
|
|
8446
|
+
const segs = trimmed.split("/");
|
|
8447
|
+
const base = segs[0];
|
|
8448
|
+
const subPath = segs.slice(1).join("/");
|
|
8449
|
+
return { path: trimmed, base, subPath };
|
|
8450
|
+
}
|
|
8451
|
+
function relPathInScope(folder, relPath) {
|
|
8452
|
+
if (folder.subPath === "") return true;
|
|
8453
|
+
return relPath === folder.subPath || relPath.startsWith(`${folder.subPath}/`);
|
|
8454
|
+
}
|
|
8455
|
+
function parseKnowledgeRef(raw) {
|
|
8456
|
+
if (typeof raw !== "string" || raw.length === 0) {
|
|
8457
|
+
throw new Error("invalid knowledge ref: empty");
|
|
8458
|
+
}
|
|
8459
|
+
if (raw.startsWith("ext:")) {
|
|
8460
|
+
const name = raw.slice("ext:".length);
|
|
8461
|
+
if (!/^[a-zA-Z0-9_-]+$/.test(name) || name.length === 0) {
|
|
8462
|
+
throw new Error(`invalid knowledge ref: external name "${name}" has unsafe characters`);
|
|
8463
|
+
}
|
|
8464
|
+
return { kind: "ext", target: name };
|
|
8465
|
+
}
|
|
8466
|
+
if (raw.startsWith("/")) {
|
|
8467
|
+
throw new Error(`invalid knowledge ref: absolute paths not allowed ("${raw}")`);
|
|
8468
|
+
}
|
|
8469
|
+
const hashAt = raw.indexOf("#");
|
|
8470
|
+
const filePath = hashAt === -1 ? raw : raw.slice(0, hashAt);
|
|
8471
|
+
const section = hashAt === -1 ? void 0 : raw.slice(hashAt + 1);
|
|
8472
|
+
if (!SAFE_PATH_RE.test(filePath)) {
|
|
8473
|
+
throw new Error(`invalid knowledge ref: unsafe characters in "${filePath}"`);
|
|
8474
|
+
}
|
|
8475
|
+
if (filePath.split("/").some((seg) => seg === ".." || seg === "." || seg === "")) {
|
|
8476
|
+
throw new Error(`invalid knowledge ref: traversal in "${filePath}"`);
|
|
8477
|
+
}
|
|
8478
|
+
if (section !== void 0) {
|
|
8479
|
+
if (section.length > 0 && !/^[a-zA-Z0-9_-]+$/.test(section)) {
|
|
8480
|
+
throw new Error(`invalid knowledge ref: unsafe characters in section "${section}"`);
|
|
8481
|
+
}
|
|
8482
|
+
return { kind: "section", target: filePath, section };
|
|
8483
|
+
}
|
|
8484
|
+
return { kind: "file", target: filePath };
|
|
8485
|
+
}
|
|
8486
|
+
function refInScope(folders, filePath) {
|
|
8487
|
+
return folders.some((f) => {
|
|
8488
|
+
if (filePath === f.base || filePath.startsWith(`${f.base}/`)) {
|
|
8489
|
+
const relInBase = filePath === f.base ? "" : filePath.slice(f.base.length + 1);
|
|
8490
|
+
return relPathInScope(f, relInBase);
|
|
8491
|
+
}
|
|
8492
|
+
return false;
|
|
8493
|
+
});
|
|
8494
|
+
}
|
|
8495
|
+
|
|
8349
8496
|
// src/tools/searchKnowledge.ts
|
|
8350
8497
|
var DEFAULT_MAX_RESULTS = 5;
|
|
8351
8498
|
var inputSchema18 = import_zod26.z.object({
|
|
@@ -8430,17 +8577,24 @@ function truncatePreview(s) {
|
|
|
8430
8577
|
async function loadIndex(adapter, base, cache) {
|
|
8431
8578
|
const cached2 = cache.get(base);
|
|
8432
8579
|
if (cached2 !== void 0) return cached2;
|
|
8433
|
-
let raw;
|
|
8580
|
+
let raw = null;
|
|
8434
8581
|
try {
|
|
8435
8582
|
raw = await adapter.readFile(`${base}/_index.json`);
|
|
8436
8583
|
} catch {
|
|
8437
|
-
|
|
8584
|
+
raw = null;
|
|
8585
|
+
}
|
|
8586
|
+
if (raw !== null) {
|
|
8587
|
+
try {
|
|
8588
|
+
const parsed = JSON.parse(raw);
|
|
8589
|
+
cache.set(base, parsed);
|
|
8590
|
+
return parsed;
|
|
8591
|
+
} catch {
|
|
8592
|
+
}
|
|
8438
8593
|
}
|
|
8439
|
-
if (raw === null) return null;
|
|
8440
8594
|
try {
|
|
8441
|
-
const
|
|
8442
|
-
cache.set(base,
|
|
8443
|
-
return
|
|
8595
|
+
const built = await buildKnowledgeIndex({ adapter, base });
|
|
8596
|
+
cache.set(base, built);
|
|
8597
|
+
return built;
|
|
8444
8598
|
} catch {
|
|
8445
8599
|
return null;
|
|
8446
8600
|
}
|
|
@@ -8599,7 +8753,7 @@ function createReadKnowledgeTool(opts) {
|
|
|
8599
8753
|
const idx = await loadIndex2(opts.adapter, base, indexCache);
|
|
8600
8754
|
if (idx === null) {
|
|
8601
8755
|
return {
|
|
8602
|
-
content: `
|
|
8756
|
+
content: `ERR_KNOWLEDGE_REF_NOT_FOUND: base "${base}" has no readable files`,
|
|
8603
8757
|
isError: true
|
|
8604
8758
|
};
|
|
8605
8759
|
}
|
|
@@ -8705,17 +8859,24 @@ ${payload}`,
|
|
|
8705
8859
|
async function loadIndex2(adapter, base, cache) {
|
|
8706
8860
|
const cached2 = cache.get(base);
|
|
8707
8861
|
if (cached2 !== void 0) return cached2;
|
|
8708
|
-
let raw;
|
|
8862
|
+
let raw = null;
|
|
8709
8863
|
try {
|
|
8710
8864
|
raw = await adapter.readFile(`${base}/_index.json`);
|
|
8711
8865
|
} catch {
|
|
8712
|
-
|
|
8866
|
+
raw = null;
|
|
8867
|
+
}
|
|
8868
|
+
if (raw !== null) {
|
|
8869
|
+
try {
|
|
8870
|
+
const idx = JSON.parse(raw);
|
|
8871
|
+
cache.set(base, idx);
|
|
8872
|
+
return idx;
|
|
8873
|
+
} catch {
|
|
8874
|
+
}
|
|
8713
8875
|
}
|
|
8714
|
-
if (raw === null) return null;
|
|
8715
8876
|
try {
|
|
8716
|
-
const
|
|
8717
|
-
cache.set(base,
|
|
8718
|
-
return
|
|
8877
|
+
const built = await buildKnowledgeIndex({ adapter, base });
|
|
8878
|
+
cache.set(base, built);
|
|
8879
|
+
return built;
|
|
8719
8880
|
} catch {
|
|
8720
8881
|
return null;
|
|
8721
8882
|
}
|
|
@@ -11239,153 +11400,6 @@ function buildToolRegistry(options) {
|
|
|
11239
11400
|
// src/index.ts
|
|
11240
11401
|
init_contract();
|
|
11241
11402
|
init_fetchData();
|
|
11242
|
-
|
|
11243
|
-
// src/knowledge/indexer.ts
|
|
11244
|
-
init_cjs_shims();
|
|
11245
|
-
var HEADING_RE = /^(#{1,6})[ \t]+(.+?)\s*$/;
|
|
11246
|
-
var WIKI_LINK_RE = /\[\[([^\]|#]+)(?:[#|][^\]]*)?\]\]/g;
|
|
11247
|
-
var FORMAT_BY_EXT = {
|
|
11248
|
-
md: "md",
|
|
11249
|
-
markdown: "md",
|
|
11250
|
-
txt: "txt",
|
|
11251
|
-
json: "json",
|
|
11252
|
-
csv: "csv",
|
|
11253
|
-
html: "html",
|
|
11254
|
-
htm: "html",
|
|
11255
|
-
pdf: "pdf",
|
|
11256
|
-
docx: "docx"
|
|
11257
|
-
};
|
|
11258
|
-
var PREVIEW_CHARS = 200;
|
|
11259
|
-
async function buildKnowledgeIndex(options) {
|
|
11260
|
-
const { adapter, base } = options;
|
|
11261
|
-
const safeBase = base.replace(/^\/+|\/+$/g, "");
|
|
11262
|
-
if (safeBase.length === 0 || safeBase.includes("..")) {
|
|
11263
|
-
throw new Error(`buildKnowledgeIndex: invalid base "${base}"`);
|
|
11264
|
-
}
|
|
11265
|
-
const files = await listFilesRecursive(adapter, safeBase);
|
|
11266
|
-
const sections = [];
|
|
11267
|
-
const filesMeta = {};
|
|
11268
|
-
for (const fileRel of files) {
|
|
11269
|
-
if (fileRel === "_index.json") continue;
|
|
11270
|
-
const fullPath = `${safeBase}/${fileRel}`;
|
|
11271
|
-
const ext = (fileRel.split(".").pop() ?? "").toLowerCase();
|
|
11272
|
-
const format = FORMAT_BY_EXT[ext];
|
|
11273
|
-
if (format === void 0) continue;
|
|
11274
|
-
const raw = await adapter.readFile(fullPath);
|
|
11275
|
-
if (raw === null) continue;
|
|
11276
|
-
const sizeBytes = byteLength3(raw);
|
|
11277
|
-
const meta = { format, size: sizeBytes };
|
|
11278
|
-
if (format === "md" || format === "txt") {
|
|
11279
|
-
const fileSections = splitSections(raw, fileRel);
|
|
11280
|
-
sections.push(...fileSections);
|
|
11281
|
-
const wikiLinks = extractWikiLinks(raw);
|
|
11282
|
-
if (wikiLinks.length > 0) meta.wikiLinks = wikiLinks;
|
|
11283
|
-
}
|
|
11284
|
-
filesMeta[fileRel] = meta;
|
|
11285
|
-
}
|
|
11286
|
-
return {
|
|
11287
|
-
schema: "v1",
|
|
11288
|
-
base: safeBase,
|
|
11289
|
-
builtAt: options.nowIso ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
11290
|
-
fileCount: Object.keys(filesMeta).length,
|
|
11291
|
-
sections,
|
|
11292
|
-
files: filesMeta
|
|
11293
|
-
};
|
|
11294
|
-
}
|
|
11295
|
-
async function writeKnowledgeIndex(options) {
|
|
11296
|
-
const index = await buildKnowledgeIndex(options);
|
|
11297
|
-
await options.adapter.writeFile(`${index.base}/_index.json`, JSON.stringify(index, null, 2));
|
|
11298
|
-
return index;
|
|
11299
|
-
}
|
|
11300
|
-
async function listFilesRecursive(adapter, dir) {
|
|
11301
|
-
const out = [];
|
|
11302
|
-
const stack = [""];
|
|
11303
|
-
while (stack.length > 0) {
|
|
11304
|
-
const sub = stack.pop();
|
|
11305
|
-
const fullDir = sub === "" ? dir : `${dir}/${sub}`;
|
|
11306
|
-
let entries = [];
|
|
11307
|
-
try {
|
|
11308
|
-
entries = await adapter.listDir(fullDir);
|
|
11309
|
-
} catch {
|
|
11310
|
-
continue;
|
|
11311
|
-
}
|
|
11312
|
-
for (const name of entries) {
|
|
11313
|
-
const childRel = sub === "" ? name : `${sub}/${name}`;
|
|
11314
|
-
const childFull = `${dir}/${childRel}`;
|
|
11315
|
-
const isDir = await adapter.isDirectory(childFull).catch(() => false);
|
|
11316
|
-
if (isDir) {
|
|
11317
|
-
stack.push(childRel);
|
|
11318
|
-
} else {
|
|
11319
|
-
out.push(childRel);
|
|
11320
|
-
}
|
|
11321
|
-
}
|
|
11322
|
-
}
|
|
11323
|
-
return out.sort();
|
|
11324
|
-
}
|
|
11325
|
-
function splitSections(content, relPath) {
|
|
11326
|
-
const lines = content.split(/\r?\n/);
|
|
11327
|
-
const out = [];
|
|
11328
|
-
const heads = [];
|
|
11329
|
-
for (let i = 0; i < lines.length; i++) {
|
|
11330
|
-
const line = lines[i];
|
|
11331
|
-
const m = HEADING_RE.exec(line);
|
|
11332
|
-
if (m) heads.push({ line: i + 1, depth: m[1].length, heading: m[2].trim() });
|
|
11333
|
-
}
|
|
11334
|
-
const leadInEndLine = heads.length > 0 ? heads[0].line - 1 : lines.length;
|
|
11335
|
-
const leadInBody = lines.slice(0, leadInEndLine).join("\n").trim();
|
|
11336
|
-
if (leadInBody.length > 0) {
|
|
11337
|
-
out.push({
|
|
11338
|
-
relPath,
|
|
11339
|
-
heading: "",
|
|
11340
|
-
slug: `${relPath}#`,
|
|
11341
|
-
depth: 0,
|
|
11342
|
-
words: tokenize(leadInBody),
|
|
11343
|
-
preview: makePreview(leadInBody),
|
|
11344
|
-
startLine: 1,
|
|
11345
|
-
endLine: leadInEndLine
|
|
11346
|
-
});
|
|
11347
|
-
}
|
|
11348
|
-
for (let i = 0; i < heads.length; i++) {
|
|
11349
|
-
const h = heads[i];
|
|
11350
|
-
const startLine = h.line;
|
|
11351
|
-
const endLine = i + 1 < heads.length ? heads[i + 1].line - 1 : lines.length;
|
|
11352
|
-
const body = lines.slice(startLine - 1, endLine).join("\n");
|
|
11353
|
-
out.push({
|
|
11354
|
-
relPath,
|
|
11355
|
-
heading: h.heading,
|
|
11356
|
-
slug: `${relPath}#${slugify(h.heading)}`,
|
|
11357
|
-
depth: h.depth,
|
|
11358
|
-
words: tokenize(body),
|
|
11359
|
-
preview: makePreview(body),
|
|
11360
|
-
startLine,
|
|
11361
|
-
endLine
|
|
11362
|
-
});
|
|
11363
|
-
}
|
|
11364
|
-
return out;
|
|
11365
|
-
}
|
|
11366
|
-
function makePreview(body) {
|
|
11367
|
-
const trimmed = body.replace(/^#{1,6}\s+.+$\r?\n?/m, "").trim();
|
|
11368
|
-
if (trimmed.length <= PREVIEW_CHARS) return trimmed;
|
|
11369
|
-
return trimmed.slice(0, PREVIEW_CHARS) + "\u2026";
|
|
11370
|
-
}
|
|
11371
|
-
function slugify(text2) {
|
|
11372
|
-
return text2.toLowerCase().trim().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
11373
|
-
}
|
|
11374
|
-
function extractWikiLinks(text2) {
|
|
11375
|
-
const seen = /* @__PURE__ */ new Set();
|
|
11376
|
-
let m;
|
|
11377
|
-
WIKI_LINK_RE.lastIndex = 0;
|
|
11378
|
-
while ((m = WIKI_LINK_RE.exec(text2)) !== null) {
|
|
11379
|
-
const target = m[1].trim();
|
|
11380
|
-
if (target.length > 0) seen.add(target);
|
|
11381
|
-
}
|
|
11382
|
-
return [...seen].sort();
|
|
11383
|
-
}
|
|
11384
|
-
function byteLength3(s) {
|
|
11385
|
-
return new TextEncoder().encode(s).byteLength;
|
|
11386
|
-
}
|
|
11387
|
-
|
|
11388
|
-
// src/index.ts
|
|
11389
11403
|
init_orchestrate();
|
|
11390
11404
|
init_planParser();
|
|
11391
11405
|
init_retry();
|