diff --git a/README.md b/README.md index 6ab204d..a41d7cf 100644 --- a/README.md +++ b/README.md @@ -29,7 +29,7 @@ Served via jsDelivr; fetched automatically during install — you never need to - `arcrun-wait/` — **arcrun-wait**(首裝) - `daemon/` — 桌面 App(Mac/Windows)安裝檔 -Built from `Arcrun@d3019a52cb18` by `installer/scripts/ship.mjs`(arcrun-rag repo,release 1.4.54,built 2026-08-26)。 +Built from `Arcrun@d60cd1258f43` by `installer/scripts/ship.mjs`(arcrun-rag repo,release 1.4.55,built 2026-08-26)。 ⚠️ 這份檔案由出貨管線每次自動重寫(`installer/scripts/render-bundles-readme.mjs`)—— 不要手動改這裡列的零件清單——它是算出來的:公庫=Arcrun 這一版編了什麼, diff --git a/arcrun-kbdb/worker.mjs b/arcrun-kbdb/worker.mjs index b1a8576..e7b5c1b 100644 --- a/arcrun-kbdb/worker.mjs +++ b/arcrun-kbdb/worker.mjs @@ -2207,6 +2207,25 @@ async function listEntries(db, f = {}) { ]); return { entries: rowsRes.results ?? [], total: countRow?.total ?? 0 }; } +async function blocksOfPages(db, pages, perPageLimit = 8) { + if (pages.length === 0) return []; + const params = []; + const pairs = pages.map((p) => { + params.push(p.page_name); + if (p.owner_id === null) return "(page_name = ? AND owner_id IS NULL)"; + params.push(p.owner_id); + return "(page_name = ? AND owner_id = ?)"; + }); + const rows = await db.prepare( + `SELECT * FROM entries + WHERE (${pairs.join(" OR ")}) + AND ${NOT_MACHINERY_PREDICATE} + AND ${NOT_DEPRECATED_PREDICATE} + ORDER BY created_at ASC, rowid ASC + LIMIT ?` + ).bind(...params, pages.length * perPageLimit).all(); + return rows.results ?? []; +} async function updateEntry(db, id, patch) { const cols = []; const params = []; @@ -2512,6 +2531,65 @@ async function searchEntries(db, q, owner_id, entry_type, limit = 50, library, s return applyRelativeCut(res.results ?? []); } +// kbdb/src/search-rank.ts +var MACHINE_SECTIONS = /* @__PURE__ */ new Set([ + "\u51FA\u8655", + "\u5361\u7247\u95DC\u4FC2", + "\u5167\u6587\u77E5\u8B58\u95DC\u4FC2", + "\u95DC\u806F", + "\u4F86\u6E90", + "\u53CD\u5411\u9023\u7D50", + "source", + "sources", + "relations", + "related", + "links", + "references", + "backlinks" +]); +var ATX_HEADING = /^\s{0,3}#{1,6}\s+(.*)$/; +var LIST_MARKER = /^\s*(?:[-*+]|\d{1,3}[.)])\s+/; +var BREADCRUMB_ARROW = /^\s*(?:←|→|⇐|⇒)\s*/; +var TRIPLE_SEP = ">".repeat(2); +var WORD_CHAR = /[A-Za-z0-9぀-ヿ㐀-䶿一-鿿豈-﫿]/; +function isMachineLine(raw2) { + const line = raw2.trim(); + if (!line) return true; + if (ATX_HEADING.test(line)) return true; + if (line.includes(TRIPLE_SEP)) return true; + if (/^-{3,}$|^\*{3,}$|^={3,}$/.test(line)) return true; + let rest = line.replace(LIST_MARKER, "").replace(BREADCRUMB_ARROW, ""); + rest = rest.replace(/\[\[[^\]]*\]\]/g, " ").replace(/!?\[[^\]]*\]\([^)]*\)/g, " ").replace(/`[^`]*`/g, " ").replace(/[*_~]/g, " "); + return !WORD_CHAR.test(rest); +} +function classifyBlock(content) { + const text = (content ?? "").trim(); + if (!text) return 1 /* Structural */; + const lines = text.split("\n"); + const head = lines.find((l) => ATX_HEADING.test(l.trim())); + if (head) { + const name = (head.trim().match(ATX_HEADING)?.[1] ?? "").trim().toLowerCase(); + if (MACHINE_SECTIONS.has(name)) return 1 /* Structural */; + } + return lines.every(isMachineLine) ? 1 /* Structural */ : 0 /* Fact */; +} +function rankByTier(entries) { + return entries.map((e, i) => ({ e, i, tier: classifyBlock(e.content) })).sort((a, b) => a.tier - b.tier || b.e.score - a.e.score || a.i - b.i).map((x) => x.e); +} +function topPages(entries, limit) { + const best = /* @__PURE__ */ new Map(); + for (const e of entries) { + const page = (e.page_name ?? "").trim(); + if (!page) continue; + const key = `${e.owner_id ?? ""}\0${page}`; + const cur = best.get(key); + if (!cur || e.score > cur.score) { + best.set(key, { page_name: page, owner_id: e.owner_id, score: e.score, matched_block_id: e.id }); + } + } + return [...best.values()].sort((a, b) => b.score - a.score).slice(0, limit); +} + // kbdb/src/actions/maintenance-quota.ts var DEFAULT_MAINTENANCE_DAILY_WRITE_LIMIT = 2e4; function maintenanceDailyLimit(env) { @@ -3045,6 +3123,8 @@ async function libraryBackfillStatus(db, opts = {}) { // kbdb/src/routes/entries.ts var entryRoutes = new Hono2(); +var PAGE_EXPANSION_MAX_PAGES = 5; +var PAGE_EXPANSION_PER_PAGE = 4; function fireAndForget(c, p) { let ctx; try { @@ -3210,6 +3290,31 @@ entryRoutes.get("/search", async (c) => { const cut = relativeMinScore(entries2[0].score); entries2 = entries2.filter((e) => e.score >= cut); } + if (!include_deprecated && entries2.length > 0) { + const pages = topPages(entries2, PAGE_EXPANSION_MAX_PAGES); + if (pages.length > 0) { + const seen = new Set(entries2.map((e) => e.id)); + const scoreOf = new Map(pages.map((p) => [`${p.owner_id ?? ""} ${p.page_name}`, p])); + const added = /* @__PURE__ */ new Map(); + for (const b of await blocksOfPages(c.env.DB, pages, PAGE_EXPANSION_PER_PAGE)) { + if (seen.has(b.id)) continue; + if (classifyBlock(b.content) !== 0 /* Fact */) continue; + const key = `${b.owner_id ?? ""} ${(b.page_name ?? "").trim()}`; + const hit = scoreOf.get(key); + if (!hit) continue; + const n = added.get(key) ?? 0; + if (n >= PAGE_EXPANSION_PER_PAGE) continue; + added.set(key, n + 1); + entries2.push({ + ...b, + score: hit.score, + retrieved_via: "page_expansion", + matched_block_id: hit.matched_block_id + }); + } + } + } + entries2 = rankByTier(entries2); entries2 = entries2.slice(0, requestedTopK); if (entries2.length === 0) { let empty_reason; diff --git a/daemon/Arcrun-0.18.37.dmg b/daemon/Arcrun-0.18.37.dmg new file mode 100644 index 0000000..495e633 Binary files /dev/null and b/daemon/Arcrun-0.18.37.dmg differ diff --git a/daemon/Arcrun-0.18.37.msix b/daemon/Arcrun-0.18.37.msix new file mode 100644 index 0000000..24421f6 Binary files /dev/null and b/daemon/Arcrun-0.18.37.msix differ diff --git a/daemon/Arcrun-win-0.18.37.exe b/daemon/Arcrun-win-0.18.37.exe new file mode 100755 index 0000000..9ea9021 Binary files /dev/null and b/daemon/Arcrun-win-0.18.37.exe differ diff --git a/manifest.json b/manifest.json index 4fade1d..208d08f 100644 --- a/manifest.json +++ b/manifest.json @@ -1,7 +1,7 @@ { "schema": 2, "built": "2026-08-26", - "source": "Arcrun@d3019a52cb18", + "source": "Arcrun@d60cd1258f43", "core": [ { "name": "arcrun-array-ops", @@ -396,7 +396,7 @@ "name": "arcrun-kbdb", "main_module": "worker.mjs", "main_file": "arcrun-kbdb/worker.mjs", - "js_bytes": 181728, + "js_bytes": 185704, "modules": [], "compat_date": "2025-02-19", "compat_flags": [ @@ -416,10 +416,10 @@ "ENVIRONMENT": "production" } }, - "source_commit": "72a18e30b06c2318fb71872ac9dcbbb6b47fab66", - "source_content_sha256": "4fbf25c066c382758234379b81a2065fa88051d76013e1ab60b4f132209f8c34", - "sha256": "4fbf25c066c382758234379b81a2065fa88051d76013e1ab60b4f132209f8c34", - "bytes": 181728 + "source_commit": "a1f45aef73de264236ff044e5b5fd5eb4d1cadc1", + "source_content_sha256": "68efc2b8958d1047c8d17f63325949af738379f8f1a2d509b9fa8bcf09440855", + "sha256": "68efc2b8958d1047c8d17f63325949af738379f8f1a2d509b9fa8bcf09440855", + "bytes": 185704 }, { "name": "arcrun-mcp", @@ -699,28 +699,28 @@ } ], "daemon": { - "version": "0.18.36", - "built": "20260824-1817", - "notes": "移除資料夾時,可以順便把我們放進去的隱藏檔案一起清掉・只刪我們自己建的,你的檔案一個都不會碰・這幾種情況我們刻意不刪・不勾這個框的話,行為跟以前一模一樣:硬碟上一個檔都不會少", + "version": "0.18.37", + "built": "20260826-1711", + "notes": "雲端被清空過的檔案,小幫手現在會自己發現並補送・重跑第二次不會又全部送一遍・補送的時候看得到", "mac": { - "file": "daemon/Arcrun-0.18.36.dmg", - "sha256": "49ca6f32afff2cff3adb23ad83b3528558655a39ad9e437cae7b52a979975cd7" + "file": "daemon/Arcrun-0.18.37.dmg", + "sha256": "6c4dd003e29f06e3a326b2c28ef295168391408ea2800378287ab9701b7c771d" }, "win": { - "file": "daemon/Arcrun-win-0.18.36.exe", - "sha256": "7b55648142338b6cdb99022c95c94e12e518846e04999acdc0a382cdd6471913" + "file": "daemon/Arcrun-win-0.18.37.exe", + "sha256": "99df0dcdbca54b0c0c4feb3af2e28ce982bb5ebd02f0f1fb0c61210b5aecae48" }, "msix": { - "file": "daemon/Arcrun-0.18.36.msix", - "sha256": "ee59349c1996d47fc5af65ca458bb3b4218ddc15c5739086c3bf50a6fac8e307" + "file": "daemon/Arcrun-0.18.37.msix", + "sha256": "91b2c217891d6a06fb0269c090e5c2dd7aa2779a3a8ac72ec677b9f46ed0516d" } }, - "release": "1.4.54", + "release": "1.4.55", "built_for": "oauth-installer-lazy-load", "notes": [ "rag_takedown_direct 用了 __CARDS_PREFIX__,但安裝器的代換表沒有它 ⇒ 這個佔位符會原封不動被推進使用者的工作流。" ], - "fingerprint": "0e3e502c6386ffd1acabae1c1af4fce42bdbbe2bcd86ab35730bad9bda22390c", + "fingerprint": "83e5e42de07dac5a13f504af9ffc0649493322ab1cd9e40e0fe8aa0af83b73c3", "library": [ { "name": "arcrun-array-ops", @@ -1092,7 +1092,7 @@ "name": "arcrun-kbdb", "main_module": "worker.mjs", "main_file": "arcrun-kbdb/worker.mjs", - "js_bytes": 181728, + "js_bytes": 185704, "modules": [], "compat_date": "2025-02-19", "compat_flags": [ @@ -1112,8 +1112,8 @@ "ENVIRONMENT": "production" } }, - "source_commit": "72a18e30b06c2318fb71872ac9dcbbb6b47fab66", - "source_content_sha256": "4fbf25c066c382758234379b81a2065fa88051d76013e1ab60b4f132209f8c34", + "source_commit": "a1f45aef73de264236ff044e5b5fd5eb4d1cadc1", + "source_content_sha256": "68efc2b8958d1047c8d17f63325949af738379f8f1a2d509b9fa8bcf09440855", "first_install": true }, {