diff --git a/README.md b/README.md index 68dca8d..5e4486e 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@6521914df572` by `installer/scripts/ship.mjs`(arcrun-rag repo,release 1.4.56,built 2026-08-26)。 +Built from `Arcrun@9a9891a0dde7` by `installer/scripts/ship.mjs`(arcrun-rag repo,release 1.4.57,built 2026-08-27)。 ⚠️ 這份檔案由出貨管線每次自動重寫(`installer/scripts/render-bundles-readme.mjs`)—— 不要手動改這裡列的零件清單——它是算出來的:公庫=Arcrun 這一版編了什麼, diff --git a/arcrun-cypher-executor/worker.mjs b/arcrun-cypher-executor/worker.mjs index 5699eeb..560291b 100644 --- a/arcrun-cypher-executor/worker.mjs +++ b/arcrun-cypher-executor/worker.mjs @@ -3339,6 +3339,19 @@ var init_kbdb_proxy = __esm({ const res = await fetch(`${base}/entries?${params.toString()}`, { headers }); return new Response(res.body, { status: res.status, headers: { "Content-Type": "application/json" } }); }); + kbdbProxyRouter.get("/kbdb/entries/library-cards", async (c) => { + const owner = tenant(c); + if (!owner) return c.json(NEED_KEY, 401); + const { base, headers } = kbdbBase(c.env); + const params = new URLSearchParams(); + params.set("owner_id", owner); + for (const k of ["library", "limit"]) { + const v = c.req.query(k); + if (v) params.set(k, v); + } + const res = await fetch(`${base}/entries/library-cards?${params.toString()}`, { headers }); + return new Response(res.body, { status: res.status, headers: { "Content-Type": "application/json" } }); + }); kbdbProxyRouter.get("/kbdb/entries/:id", async (c) => { if (!tenant(c)) return c.json(NEED_KEY, 401); const { base, headers } = kbdbBase(c.env); @@ -17525,6 +17538,50 @@ function canReadRecord(rec, tenant2, libraries) { const lib = recordLibrary(rec.values); return lib === null || canReadLibrary(libraries, lib); } +portalDataRouter.get( + "/portal/data/library-cards", + (c) => run(c, async () => { + const auth = await requirePortalUser(c); + if (!auth.ok) return auth.res; + const libraries = parseLibraries(auth.user.values.libraries); + const library = c.req.query("library") || ""; + if (!library.trim()) return c.json({ error: "\u7F3A\u5C11 library \u53C3\u6578\uFF08\u76EE\u9304\u5929\u751F\u5C6C\u65BC\u67D0\u4E00\u500B\u5EAB\uFF09" }, 400); + if (!canReadLibrary(libraries, library)) return notFound(c); + const params = new URLSearchParams({ library }); + params.set("owner_id", String(knowledgeOwner(c.env))); + const limit = c.req.query("limit"); + if (limit && /^\d{1,3}$/.test(limit)) params.set("limit", limit); + const res = await kbdbFetch(c.env, `/entries/library-cards?${params.toString()}`); + if (!res.ok) return c.json({ error: `KBDB \u56DE\u932F\uFF08HTTP ${res.status}\uFF09` }, 502); + return new Response(res.body, { status: 200, headers: { "Content-Type": "application/json" } }); + }) +); +portalDataRouter.get( + "/portal/data/card", + (c) => run(c, async () => { + const auth = await requirePortalUser(c); + if (!auth.ok) return auth.res; + const libraries = parseLibraries(auth.user.values.libraries); + const library = c.req.query("library") || ""; + const pageName = c.req.query("page_name") || ""; + if (!library.trim() || !pageName.trim()) { + return c.json({ error: "\u7F3A\u5C11 library \u6216 page_name\uFF08\u4E00\u5F35\u5361\u7531\u300C\u54EA\u500B\u5EAB\u7684\u54EA\u500B\u5361\u540D\u300D\u5B9A\u4F4D\uFF09" }, 400); + } + if (!canReadLibrary(libraries, library)) return notFound(c); + const params = new URLSearchParams({ library, page_name: pageName }); + params.set("owner_id", String(knowledgeOwner(c.env))); + params.set("limit", "200"); + const res = await kbdbFetch(c.env, `/entries?${params.toString()}`); + if (!res.ok) return c.json({ error: `KBDB \u56DE\u932F\uFF08HTTP ${res.status}\uFF09` }, 502); + const body = await res.json().catch(() => null); + if (!body || !Array.isArray(body.entries)) { + return c.json({ error: "KBDB \u56DE\u61C9\u4E0D\u662F\u9810\u671F\u7684 entries \u6E05\u55AE" }, 502); + } + const entries = filterDeprecatedEntries(body.entries); + if (entries.length === 0) return notFound(c); + return c.json({ success: true, library, page_name: pageName, entries, count: entries.length }); + }) +); portalDataRouter.get( "/portal/data/map", (c) => run(c, async () => { diff --git a/arcrun-kbdb/worker.mjs b/arcrun-kbdb/worker.mjs index e7b5c1b..26a9748 100644 --- a/arcrun-kbdb/worker.mjs +++ b/arcrun-kbdb/worker.mjs @@ -2590,6 +2590,67 @@ function topPages(entries, limit) { return [...best.values()].sort((a, b) => b.score - a.score).slice(0, limit); } +// kbdb/src/actions/library-index.ts +var DEFAULT_CARD_LIMIT = 200; +var MAX_CARD_LIMIT = 500; +var GLOSS_HEADINGS = ["## \u6458\u8981", "## \u4E00\u53E5\u8A71\u5B9A\u7FA9"]; +var INDEX_CARD_NAME = "00-INDEX"; +function glossOf(block) { + if (!block) return null; + const lines = String(block).split("\n"); + const body = lines.filter((l) => !GLOSS_HEADINGS.some((h) => l.trim().startsWith(h))).map((l) => l.trim()).filter(Boolean); + if (body.length === 0) return null; + const text = body.join(" ").replace(/\s+/g, " ").trim(); + return text || null; +} +async function listLibraryCards(db, f) { + const library = f.library; + const owner = f.owner_id ?? ""; + const limit = Math.min(Math.max(f.limit ?? DEFAULT_CARD_LIMIT, 1), MAX_CARD_LIMIT); + const glossLike = GLOSS_HEADINGS.map(() => "content LIKE ?").join(" OR "); + const glossParams = GLOSS_HEADINGS.map((h) => `${h}%`); + const where = `WHERE entry_type = 'block' + AND page_name IS NOT NULL AND page_name != '' + AND (? = '' OR owner_id = ?) + AND COALESCE(json_extract(metadata_json, '$.status'), '') != 'deprecated' + AND ${ENTRY_LIBRARY} = ?`; + const whereParams = [owner, owner, library]; + const [rowsRes, countRow, indexRow] = await Promise.all([ + db.prepare( + `SELECT + page_name, + COUNT(*) AS block_count, + MAX(CASE WHEN ${glossLike} THEN content END) AS gloss_block, + MAX(json_extract(metadata_json, '$.source_path')) AS source_path, + MAX(updated_at) AS updated_at + FROM entries + ${where} + GROUP BY page_name + ORDER BY MAX(updated_at) DESC, page_name ASC + LIMIT ?` + ).bind(...glossParams, ...whereParams, limit).all(), + db.prepare(`SELECT COUNT(DISTINCT page_name) AS total FROM entries ${where}`).bind(...whereParams).first(), + // 🔴 目錄卡在不在,**不能靠掃上面那一頁**——它照 updated_at 排序, + // 目錄卡很可能不在前 limit 名內 ⇒ 那樣會回報「沒有目錄」而其實有, + // 而這個欄位正是呼叫端用來決定「要不要退回導出清單」的開關。 + // 它只有一張、名字固定,所以用卡名直接問一次(EXISTS,成本可忽略)。 + db.prepare(`SELECT 1 AS hit FROM entries ${where} AND page_name = ? LIMIT 1`).bind(...whereParams, INDEX_CARD_NAME).first() + ]); + const cards = (rowsRes.results ?? []).map((r) => ({ + page_name: r.page_name, + block_count: Number(r.block_count) || 0, + gloss: glossOf(r.gloss_block), + source_path: r.source_path ?? null, + updated_at: Number(r.updated_at) || 0 + })); + return { + library, + cards, + total: countRow?.total ?? cards.length, + has_index_card: !!indexRow + }; +} + // kbdb/src/actions/maintenance-quota.ts var DEFAULT_MAINTENANCE_DAILY_WRITE_LIMIT = 2e4; function maintenanceDailyLimit(env) { @@ -3162,6 +3223,19 @@ entryRoutes.get("/libraries", async (c) => { const libraries = (rows.results ?? []).map((r) => r.library).filter(Boolean); return c.json({ success: true, libraries, count: libraries.length }); }); +entryRoutes.get("/library-cards", async (c) => { + const library = c.req.query("library") || ""; + if (!library.trim()) return c.json({ success: false, error: "library required" }, 400); + const limitRaw = Number(c.req.query("limit")); + const result = await listLibraryCards(c.env.DB, { + library, + owner_id: c.req.query("owner_id") || void 0, + // 壞值當沒帶(回預設),不 clamp 成 1——clamp 會回一筆長得像「這個庫只有一張卡」 + // 的結果(#100 教訓:把「讀不到」演成「你沒有」)。 + limit: Number.isFinite(limitRaw) && limitRaw > 0 ? Math.floor(limitRaw) : void 0 + }); + return c.json({ success: true, ...result, count: result.cards.length }); +}); entryRoutes.get("/library-stats", async (c) => { const owner = c.req.query("owner_id") || ""; const rows = await c.env.DB.prepare( diff --git a/arcrun-mcp/worker.mjs b/arcrun-mcp/worker.mjs index f940330..3102b6c 100644 --- a/arcrun-mcp/worker.mjs +++ b/arcrun-mcp/worker.mjs @@ -32203,7 +32203,7 @@ async function buildLibraryMapInstructions(env, identity) { const data = await res.json(); const body = renderLibraryMapLines(Array.isArray(data.libraries) ? data.libraries : []); if (body) { - text = "\u3010\u85CF\u66F8\u5730\u5716\u3011KBDB \u5168\u9928\u73FE\u6CC1\uFF08\u6BCF\u5EAB\u4E00\u884C\uFF09\u3002\u67E5\u8CC7\u6599\u524D\u5148\u770B\u9019\u88E1\u5B9A\u4F4D\u8A72\u9032\u54EA\u500B\u5EAB\uFF1B\u9700\u8981\u67D0\u5EAB\u7D30\u7BC0\u547C\u53EB kbdb_get_map(library)\uFF0C\u4E0D\u78BA\u5B9A\u8A72\u67E5\u4EC0\u9EBC\u6642\u5148\u547C\u53EB kbdb_get_map\u3002\n" + body; + text = "\u3010\u85CF\u66F8\u5730\u5716\u3011KBDB \u5168\u9928\u73FE\u6CC1\uFF08\u6BCF\u5EAB\u4E00\u884C\uFF09\uFF1D\u6AA2\u7D22\u4E09\u6B65\u7684**\u7B2C 1 \u6B65**\uFF1A\u5148\u5728\u9019\u88E1\u5B9A\u4F4D\u8A72\u9032\u54EA\u500B\u5EAB\u3002\n\u5B9A\u4F4D\u597D\u4E4B\u5F8C\uFF1A`kbdb_get_index(library)` \u8B80\u90A3\u500B\u5EAB\u7684\u76EE\u9304\u6311\u51FA\u8A72\u8B80\u7684\u5361\uFF08\u7B2C 2 \u6B65\uFF09\u2192 `kbdb_get_card(library, page_name)` \u8B80\u6574\u5F35\u5361\uFF08\u7B2C 3 \u6B65\uFF09\u3002\n\u9700\u8981\u67D0\u5EAB\u7684\u7D30\u7BC0\u7D71\u8A08\uFF08\u6838\u5FC3 entities\uFF0F\u95DC\u4FC2\u5206\u5E03\uFF0F\u8DE8\u5EAB bridges\uFF09\u624D\u547C\u53EB `kbdb_get_map(library)`\u3002\n" + body; } } } catch { @@ -32329,6 +32329,155 @@ function registerGetMap(server, env, identity) { ); } +// mcp/src/tools/kbdb_index.ts +var INDEX_CARD_NAME = "00-INDEX"; +function anchorOf(block) { + try { + const meta = typeof block.metadata_json === "string" ? JSON.parse(block.metadata_json) : block.metadata_json ?? {}; + const src = typeof meta?.source === "string" ? meta.source : ""; + const m = src.match(/#(\d+)\s*$/); + if (!m) return Number.MAX_SAFE_INTEGER; + return Number(m[1]); + } catch { + return Number.MAX_SAFE_INTEGER; + } +} +function assembleCard(blocks) { + return blocks.slice().sort((a, b) => anchorOf(a) - anchorOf(b)).map((b) => String(b.content ?? "").trim()).filter(Boolean).join("\n\n"); +} +function registerAllKbdbIndexTools(server, env, identity) { + registerGetIndex(server, env, identity); + registerGetCard(server, env, identity); +} +function registerGetIndex(server, env, identity) { + server.tool( + "kbdb_get_index", + "\u5EAB\u76EE\u9304\uFF1A\u67D0\u4E00\u500B\u77E5\u8B58\u5EAB\u88E1**\u6709\u54EA\u4E9B\u5361**\u3001\u6BCF\u5F35\u5361\u4E00\u53E5\u8A71\u5728\u8B1B\u4EC0\u9EBC\u3002\u9019\u662F\u6AA2\u7D22\u4E09\u6B65\u7684\u7B2C 2 \u6B65\u2014\u2014\u5148 kbdb_get_map \u5B9A\u4F4D\u8A72\u9032\u54EA\u500B\u5EAB\uFF0C\u518D\u7528\u672C\u5DE5\u5177\u8B80\u90A3\u500B\u5EAB\u7684\u76EE\u9304\u6311\u51FA\u8A72\u8B80\u7684\u5361\uFF0C\u6700\u5F8C\u7528 kbdb_get_card \u8B80\u6574\u5F35\u5361\u3002**\u8981\u56DE\u7B54\u95DC\u65BC\u67D0\u500B\u5EAB\u7684\u554F\u984C\u6642\u8D70\u9019\u689D\uFF0C\u4E0D\u8981\u4E00\u958B\u59CB\u5C31 kbdb_search**\uFF08\u90A3\u662F\u5E73\u9762\u641C\u5C0B\uFF0C\u56DE\u7684\u662F\u6563\u843D\u7684\u6BB5\u843D\uFF0C\u4E0D\u662F\u5361\uFF09\u3002", + { + library: external_exports.string().min(1).describe("\u5EAB\u540D\uFF08\u5F9E kbdb_get_map \u62FF\uFF1B\u4F8B 'youlinhsieh-test1'\u3001'kb'\uFF09"), + limit: external_exports.number().int().positive().optional().describe("\u6700\u591A\u5217\u5E7E\u5F35\u5361\uFF08\u9810\u8A2D 200\uFF0C\u4E0A\u9650 500\uFF1B\u622A\u65B7\u6642 total \u6703\u544A\u8A34\u4F60\u9019\u500B\u5EAB\u5BE6\u969B\u6709\u5E7E\u5F35\uFF09") + }, + async ({ library, limit }) => { + if (identity.kind === "stale") return staleIdentityError(); + try { + const isPortal = identity.kind === "portal"; + const qs = new URLSearchParams({ library }); + if (limit) qs.set("limit", String(limit)); + const res = isPortal ? await portalFetch(env, identity.portal.session, `/portal/data/library-cards`, { + query: { library, ...limit ? { limit: String(limit) } : {} } + }) : await kbdbFetch(env, `/entries/library-cards?${qs.toString()}`); + if (res.status === 404) { + return errorResponse( + "library_not_found", + `\u67E5\u7121\u5EAB\u300C${library}\u300D\u2014\u2014\u9019\u500B\u540D\u5B57\u4E0D\u5B58\u5728\uFF0C\u6216\u4E0D\u5728\u4F60\u88AB\u6388\u6B0A\u7684\u77E5\u8B58\u5EAB\u7BC4\u570D\u5167`, + ["kbdb_get_map() \u770B\u5168\u9928\u6709\u54EA\u4E9B\u5EAB\uFF08\u78BA\u8A8D\u5EAB\u540D\uFF0F\u78BA\u8A8D\u4F60\u6709\u6B0A\u9650\u7684\u5EAB\uFF09"] + ); + } + if (!res.ok && isPortal) return portalError(res, `\u53D6\u5EAB\u300C${library}\u300D\u7684\u76EE\u9304`); + if (!res.ok) { + return errorResponse( + "index_fetch_failed", + `\u53D6\u5EAB\u300C${library}\u300D\u7684\u76EE\u9304\u5931\u6557 HTTP ${res.status}`, + ["\u7A0D\u5F8C\u91CD\u8A66"], + await res.text().catch(() => "") + ); + } + const body = await res.json(); + const cards = Array.isArray(body.cards) ? body.cards : []; + const total = Number(body.total ?? cards.length) || 0; + if (cards.length === 0) { + return successResponse({ library, source: "derived", cards: [], total: 0 }, [ + `\u5EAB\u300C${library}\u300D\u76EE\u524D\u4E00\u5F35\u5361\u90FD\u6C92\u6709\uFF08\u7AEF\u9EDE\u6B63\u5E38\uFF0C\u662F\u771F\u7684\u6C92\u6709\u5167\u5BB9\u2014\u2014\u4E0D\u662F\u76EE\u9304\u58DE\u4E86\uFF09`, + "kbdb_get_map() \u770B\u770B\u5225\u7684\u5EAB" + ]); + } + const hasIndexCard = !!body.has_index_card; + const truncated = total > cards.length; + const truncationHint = truncated ? [`\u9019\u500B\u5EAB\u5171 ${total} \u5F35\u5361\uFF0C\u76EE\u9304\u53EA\u5217\u4E86 ${cards.length} \u5F35\u2014\u2014\u8981\u770B\u5168\u90E8\u5C31\u5E36 limit\uFF08\u4E0A\u9650 500\uFF09`] : []; + if (hasIndexCard) { + return successResponse( + { library, source: "index_card", index_card: INDEX_CARD_NAME, cards, total }, + [ + `\u5EAB\u300C${library}\u300D\u6709\u76EE\u9304\u5361\u300C${INDEX_CARD_NAME}\u300D\u2014\u2014\u7528 kbdb_get_card(library='${library}', page_name='${INDEX_CARD_NAME}') \u8B80\u5B83\u7684\u539F\u6587\uFF08\u90A3\u662F ingest \u7AEF\u7522\u7684\u771F\u8EAB\uFF0C\u5206\u985E\u8207\u8AAA\u660E\u6BD4\u4E0B\u9762\u9019\u4EFD\u6A5F\u68B0\u6E05\u55AE\u7D30\uFF09`, + "\u6311\u597D\u8981\u8B80\u54EA\u5F35\u5361\u4E4B\u5F8C\uFF1Akbdb_get_card(library, page_name) \u8B80**\u6574\u5F35**\u5361", + ...truncationHint + ] + ); + } + return successResponse( + { library, source: "derived", index_card: null, cards, total }, + [ + // 🔴 這句話是本工具最重要的輸出,不是客套:它讓「索引缺了」變成看得見的事實。 + `\u26A0\uFE0F \u5EAB\u300C${library}\u300D**\u6C92\u6709\u76EE\u9304\u5361\uFF08${INDEX_CARD_NAME}\uFF09**\u2014\u2014\u4E0B\u9762\u9019\u4EFD\u662F\u7167\u5EAB\u88E1\u7684\u5361\u6A5F\u68B0\u5217\u51FA\u4F86\u7684\u66FF\u4EE3\u54C1\uFF0C\u4E0D\u662F ingest \u7AEF\u7522\u7684\u76EE\u9304\u3002\u5B83\u6C92\u6709\u5206\u985E\u3001\u6C92\u6709\u4EBA\u5BEB\u7684\u8AAA\u660E\u3002`, + "\u6311\u597D\u8981\u8B80\u54EA\u5F35\u5361\u4E4B\u5F8C\uFF1Akbdb_get_card(library, page_name) \u8B80**\u6574\u5F35**\u5361", + ...truncationHint + ] + ); + } catch (e) { + return errorResponse("internal_error", e instanceof Error ? e.message : String(e), ["\u7A0D\u5F8C\u91CD\u8A66"]); + } + } + ); +} +function registerGetCard(server, env, identity) { + server.tool( + "kbdb_get_card", + "\u8B80**\u6574\u5F35**\u77E5\u8B58\u5361\uFF08\u6458\u8981\uFF0B\u91CD\u9EDE\uFF0B\u5BE6\u9AD4\uFF0B\u95DC\u806F\u4E00\u6B21\u5230\u624B\uFF0C\u7167\u539F\u7A3F\u9806\u5E8F\u7D44\u597D\uFF09\u3002\u9019\u662F\u6AA2\u7D22\u4E09\u6B65\u7684\u7B2C 3 \u6B65\uFF1Akbdb_get_map \u5B9A\u4F4D\u5EAB \u2192 kbdb_get_index \u5F9E\u76EE\u9304\u6311\u5361 \u2192 \u672C\u5DE5\u5177\u8B80\u6574\u5F35\u5361\u3002**\u8981\u5F15\u7528\u67D0\u5F35\u5361\u7684\u5167\u5BB9\u6642\u7528\u9019\u500B\uFF0C\u4E0D\u8981\u7528 kbdb_search \u7684\u7247\u6BB5**\uFF08\u90A3\u53EA\u6703\u56DE\u547D\u4E2D\u7684\u6BB5\u843D\uFF0C\u4F60\u4E0D\u6703\u77E5\u9053\u81EA\u5DF1\u6F0F\u4E86\u54EA\u5E7E\u6BB5\uFF09\u3002", + { + library: external_exports.string().min(1).describe("\u5EAB\u540D\uFF08kbdb_get_map\uFF0Fkbdb_get_index \u7D66\u7684\u90A3\u500B\uFF09"), + page_name: external_exports.string().min(1).describe("\u5361\u540D\uFF0C\uFF1D kbdb_get_index \u5217\u51FA\u7684 page_name\uFF0C\u4E5F\u662F\u5361\u7247\u88E1 [[\u96D9\u62EC\u865F]] \u6307\u7684\u90A3\u500B\u540D\u5B57") + }, + async ({ library, page_name }) => { + if (identity.kind === "stale") return staleIdentityError(); + try { + const isPortal = identity.kind === "portal"; + const qs = new URLSearchParams({ library, page_name, limit: "200" }); + const res = isPortal ? await portalFetch(env, identity.portal.session, `/portal/data/card`, { + query: { library, page_name } + }) : await kbdbFetch(env, `/entries?${qs.toString()}`); + if (res.status === 404) { + return errorResponse( + "card_not_found", + `\u5EAB\u300C${library}\u300D\u88E1\u67E5\u7121\u5361\u7247\u300C${page_name}\u300D`, + [ + `kbdb_get_index(library='${library}') \u770B\u9019\u500B\u5EAB\u5BE6\u969B\u6709\u54EA\u4E9B\u5361\uFF08\u5361\u540D\u8981\u4E00\u5B57\u4E0D\u5DEE\uFF09`, + "\u78BA\u8A8D\u5EAB\u540D\u6C92\u6253\u932F\uFF1Akbdb_get_map()" + ] + ); + } + if (!res.ok && isPortal) return portalError(res, `\u8B80\u5361\u7247\u300C${page_name}\u300D`); + if (!res.ok) { + return errorResponse( + "card_fetch_failed", + `\u8B80\u5361\u7247\u300C${page_name}\u300D\u5931\u6557 HTTP ${res.status}`, + ["\u7A0D\u5F8C\u91CD\u8A66"], + await res.text().catch(() => "") + ); + } + const body = await res.json(); + const blocks = Array.isArray(body.entries) ? body.entries : []; + if (blocks.length === 0) { + return errorResponse( + "card_not_found", + `\u5EAB\u300C${library}\u300D\u88E1\u67E5\u7121\u5361\u7247\u300C${page_name}\u300D`, + [`kbdb_get_index(library='${library}') \u770B\u9019\u500B\u5EAB\u5BE6\u969B\u6709\u54EA\u4E9B\u5361\uFF08\u5361\u540D\u8981\u4E00\u5B57\u4E0D\u5DEE\uFF09`] + ); + } + const content = assembleCard(blocks); + return successResponse( + { library, page_name, content, block_count: blocks.length }, + [ + `\u9019\u662F\u300C${page_name}\u300D\u7684\u5B8C\u6574\u5167\u5BB9\uFF08${blocks.length} \u6BB5\uFF0C\u5DF2\u7167\u539F\u7A3F\u9806\u5E8F\u7D44\u597D\uFF09`, + "\u8981\u770B\u5B83\u9023\u5230\u54EA\u4E9B\u5361\uFF1Akbdb_graph_neighbors(name)" + ] + ); + } catch (e) { + return errorResponse("internal_error", e instanceof Error ? e.message : String(e), ["\u7A0D\u5F8C\u91CD\u8A66"]); + } + } + ); +} + // mcp/src/tools/arcrun_whoami.ts function registerWhoami(server, env, orgNamespace, identity) { server.tool( @@ -32393,6 +32542,7 @@ function registerAllTools(server, env, orgNamespace, partnerToken, identity) { registerAllKbdbDataTools(server, env, identity); registerAllKbdbGraphTools(server, env, orgNamespace, identity); registerAllKbdbMapTools(server, env, identity); + registerAllKbdbIndexTools(server, env, identity); registerWhoami(server, env, orgNamespace, identity); } @@ -32405,12 +32555,27 @@ var KNOWLEDGE_FIRST = [ "\u4F60\u5C0D\u9019\u4E9B\u5C08\u6848\u7684\u5167\u5EFA\u5370\u8C61\u591A\u534A\u662F\u932F\u7684\u6216\u904E\u6642\u7684\uFF0C\u5EAB\u88E1\u90A3\u4EFD\u624D\u662F\u4E3B\u4EBA\u8A8D\u7684\u7248\u672C\u3002", "", "\u{1F534} **\u6709\u4EBA\u554F\u4F60\u300CX \u662F\u4EC0\u9EBC\uFF0F\u70BA\u4EC0\u9EBC\u9019\u6A23\u505A\uFF0F\u4E4B\u524D\u600E\u9EBC\u6C7A\u5B9A\u7684\uFF0F\u73FE\u5728\u505A\u5230\u54EA\u300D\u2014\u2014", - "\u4F60\u7684\u7B2C\u4E00\u500B\u52D5\u4F5C\u662F `kbdb_search`\uFF0C\u4E0D\u662F grep \u539F\u59CB\u78BC\u3001\u4E0D\u662F\u4E0A\u7DB2\u641C\u3001\u4E0D\u662F\u56DE\u7B54\u300C\u6211\u4E0D\u77E5\u9053\u300D\u3002**", + "\u4F60\u7684\u7B2C\u4E00\u500B\u52D5\u4F5C\u662F\u67E5\u9019\u500B\u77E5\u8B58\u5EAB\uFF0C\u4E0D\u662F grep \u539F\u59CB\u78BC\u3001\u4E0D\u662F\u4E0A\u7DB2\u641C\u3001\u4E0D\u662F\u56DE\u7B54\u300C\u6211\u4E0D\u77E5\u9053\u300D\u3002**", "", - "- `kbdb_search({ q: \"Arcrun \u662F\u4EC0\u9EBC\" })` \u2014 \u95DC\u9375\u5B57\u67E5\uFF08\u9810\u8A2D `mode:'keyword'`\uFF0C\u57FA\u672C\u76E4\u6C38\u9060\u53EF\u7528\uFF09\u3002", - " \u63DB\u5E7E\u7D44\u8B1B\u6CD5\u518D\u653E\u68C4\uFF1B\u60F3\u8981\u8A9E\u7FA9\u76F8\u4F3C\u5EA6\u7528 `mode:'semantic'`\u3002**\u9019\u4E00\u652F\u662F\u4F60\u7684\u7B2C\u4E00\u7AD9\u3002**", - "- `kbdb_get_map()` \u2014 \u4E0D\u77E5\u9053\u8A72\u9032\u54EA\u500B\u5EAB\u6642\u5148\u770B\u85CF\u66F8\u5730\u5716\uFF08\u4E0B\u9762\u82E5\u6709\u3010\u85CF\u66F8\u5730\u5716\u3011\u5C31\u662F\u5B83\u7684\u5FEB\u7167\uFF09\u3002", - '- `kbdb_graph_neighbors({ subject: "Arcrun" })` \u2014 \u67E5\u67D0\u500B\u6771\u897F\u8DDF\u8AB0\u6709\u95DC\u4FC2\uFF08\u4E09\u5143\u7D44\u904D\u6B77\uFF09\u3002', + "\u{1F534} **\u600E\u9EBC\u67E5\uFF1A\u7167\u9019\u4E09\u6B65\u8D70\uFF0C\u4E00\u6B65\u90FD\u4E0D\u8981\u8DF3**\uFF08leo 2026-08-26 \u5B9A\u6848\uFF0C\uFF1DD81\uFF09\u3002", + "\u9019\u500B\u5EAB\u662F\u300C\u4E00\u5EA7**\u6709\u76EE\u9304**\u7684\u5716\u66F8\u9928\u300D\uFF0C\u4E0D\u662F\u4E00\u5806\u53EF\u4EE5\u5168\u6587\u641C\u7684\u6563\u9801\uFF1A", + "", + "1. **\u9019\u4EF6\u4E8B\u5728\u54EA\u5E7E\u500B\u5EAB** \u2192 `kbdb_get_map()`", + " \u56DE\u6BCF\u500B\u5EAB\u4E00\u884C\uFF08\u5EAB\u540D\uFF0B\u9019\u500B\u5EAB\u5728\u8AC7\u4EC0\u9EBC\uFF0B\u6838\u5FC3\u6982\u5FF5\uFF0B\u898F\u6A21\uFF09\u3002\u4E0B\u9762\u82E5\u6709\u3010\u85CF\u66F8\u5730\u5716\u3011\u5C31\u662F\u5B83\u7684\u5FEB\u7167\u3002", + "2. **\u90A3\u500B\u5EAB\u88E1\u8A72\u8B80\u54EA\u5F35\u5361** \u2192 `kbdb_get_index({ library })`", + " \u56DE\u90A3\u500B\u5EAB\u7684**\u76EE\u9304**\uFF1A\u6709\u54EA\u4E9B\u5361\u3001\u6BCF\u5F35\u5361\u4E00\u53E5\u8A71\u5728\u8B1B\u4EC0\u9EBC\u3002**\u5F9E\u76EE\u9304\u6311\uFF0C\u4E0D\u8981\u7528\u731C\u7684\u3002**", + "3. **\u628A\u90A3\u5F35\u5361\u6574\u5F35\u8B80\u5B8C** \u2192 `kbdb_get_card({ library, page_name })`", + " \u6458\u8981\uFF0B\u91CD\u9EDE\uFF0B\u5BE6\u9AD4\uFF0B\u95DC\u806F\u4E00\u6B21\u5230\u624B\uFF0C\u7167\u539F\u7A3F\u9806\u5E8F\u7D44\u597D\u3002**\u5F15\u7528\u5167\u5BB9\u6642\u4E00\u5F8B\u7528\u9019\u4E00\u652F\u3002**", + "", + "\u70BA\u4EC0\u9EBC\u662F\u9019\u4E09\u6B65\u3001\u800C\u4E0D\u662F\u76F4\u63A5\u5168\u6587\u641C\uFF1A\u4E00\u500B\u554F\u984C\u7684\u7B54\u6848\u901A\u5E38**\u6574\u5F35\u5361**\u624D\u8AAA\u5F97\u5B8C\u6574\uFF0C", + "\u800C\u641C\u5C0B\u53EA\u6703\u56DE**\u547D\u4E2D\u95DC\u9375\u5B57\u7684\u90A3\u5E7E\u6BB5**\u2014\u2014\u4F60\u53EF\u80FD\u62FF\u5230\u6458\u8981\u537B\u6C92\u62FF\u5230\u6578\u5B57\u3001\u62FF\u5230\u95DC\u806F\u537B\u6C92\u62FF\u5230\u5BE6\u9AD4\uFF0C", + "\u800C\u4E14**\u4F60\u4E0D\u6703\u77E5\u9053\u81EA\u5DF1\u6F0F\u4E86\u54EA\u5E7E\u6BB5**\u3002\u76EE\u9304\u7684\u5B58\u5728\u5C31\u662F\u70BA\u4E86\u8B93\u4F60\u5148\u770B\u5230\u300C\u6709\u4EC0\u9EBC\u300D\u518D\u6C7A\u5B9A\u8B80\u4EC0\u9EBC\u3002", + "", + "\u5176\u9918\u5DE5\u5177\uFF08\u8F14\u52A9\uFF0C\u4E0D\u662F\u4E3B\u8DEF\u5F91\uFF09\uFF1A", + "- `kbdb_search({ q: \"...\" })` \u2014 \u5E73\u9762\u641C\u5C0B\uFF08\u95DC\u9375\u5B57\uFF0C\u6216 `mode:'semantic'`\uFF09\u3002**\u4E09\u6B65\u8D70\u4E0D\u901A\u6642\u624D\u7528**\uFF1A", + " \u4F8B\u5982\u9023\u8A72\u9032\u54EA\u500B\u5EAB\u90FD\u554F\u4E0D\u51FA\u4F86\u3001\u6216\u8981\u5728\u5168\u9928\u6488\u4E00\u500B\u5F88\u7279\u5225\u7684\u5B57\u3002\u5B83\u56DE\u7684\u662F**\u584A**\u4E0D\u662F\u5361 \u21D2", + " \u6488\u5230\u4E4B\u5F8C\u8ACB\u62FF\u5B83\u7684 `page_name` \u56DE\u5230\u7B2C 3 \u6B65\u628A\u6574\u5F35\u5361\u8B80\u5B8C\uFF0C**\u4E0D\u8981\u53EA\u6191\u4E00\u500B\u7247\u6BB5\u56DE\u7B54**\u3002", + '- `kbdb_graph_neighbors({ name: "..." })` \u2014 \u67E5\u67D0\u500B\u6771\u897F\u8DDF\u8AB0\u6709\u95DC\u4FC2\uFF08\u4E09\u5143\u7D44\u904D\u6B77\uFF09\u3002', "- `kbdb_list_templates` / `kbdb_query` \u2014 \u6309 template \u53D6\u6574\u6279\u7D50\u69CB\u5316\u8CC7\u6599\u3002", "", "\u{1F534} **\u9019\u4E09\u4EF6\u4E8B\u4E0D\u53EF\u4EE5\u8B1B\u6210\u540C\u4E00\u53E5**\uFF08\u8B1B\u6210\u540C\u4E00\u53E5\u5C31\u662F\u5728\u9A19\u4EBA\uFF09\uFF1A", diff --git a/daemon/Arcrun-0.18.39.dmg b/daemon/Arcrun-0.18.39.dmg new file mode 100644 index 0000000..1eb0601 Binary files /dev/null and b/daemon/Arcrun-0.18.39.dmg differ diff --git a/daemon/Arcrun-0.18.39.msix b/daemon/Arcrun-0.18.39.msix new file mode 100644 index 0000000..fa19890 Binary files /dev/null and b/daemon/Arcrun-0.18.39.msix differ diff --git a/daemon/Arcrun-win-0.18.39.exe b/daemon/Arcrun-win-0.18.39.exe new file mode 100755 index 0000000..5b540ae Binary files /dev/null and b/daemon/Arcrun-win-0.18.39.exe differ diff --git a/manifest.json b/manifest.json index 2ea3744..885f617 100644 --- a/manifest.json +++ b/manifest.json @@ -1,7 +1,7 @@ { "schema": 2, - "built": "2026-08-26", - "source": "Arcrun@6521914df572", + "built": "2026-08-27", + "source": "Arcrun@9a9891a0dde7", "core": [ { "name": "arcrun-array-ops", @@ -198,7 +198,7 @@ "name": "arcrun-cypher-executor", "main_module": "worker.mjs", "main_file": "arcrun-cypher-executor/worker.mjs", - "js_bytes": 691420, + "js_bytes": 694462, "modules": [], "compat_date": "2025-02-19", "compat_flags": [ @@ -239,10 +239,10 @@ "stripped": { "services": 13 }, - "source_commit": "8257fd90a9a172a160a77980b0f10c1bc5a2eabb", - "source_content_sha256": "1750e8a938ae467a7091b9faa6c0549fac64fc7d35cd9067bf4c43f4b2db49d3", - "sha256": "1750e8a938ae467a7091b9faa6c0549fac64fc7d35cd9067bf4c43f4b2db49d3", - "bytes": 691420 + "source_commit": "af6f244b44b28a16a3578cf554d1e199a170a05e", + "source_content_sha256": "531937c2ebade75fe67676071ca5f658775c11aa855359330ae19117db1d1807", + "sha256": "531937c2ebade75fe67676071ca5f658775c11aa855359330ae19117db1d1807", + "bytes": 694462 }, { "name": "arcrun-date-ops", @@ -396,7 +396,7 @@ "name": "arcrun-kbdb", "main_module": "worker.mjs", "main_file": "arcrun-kbdb/worker.mjs", - "js_bytes": 185704, + "js_bytes": 189149, "modules": [], "compat_date": "2025-02-19", "compat_flags": [ @@ -416,16 +416,16 @@ "ENVIRONMENT": "production" } }, - "source_commit": "a1f45aef73de264236ff044e5b5fd5eb4d1cadc1", - "source_content_sha256": "68efc2b8958d1047c8d17f63325949af738379f8f1a2d509b9fa8bcf09440855", - "sha256": "68efc2b8958d1047c8d17f63325949af738379f8f1a2d509b9fa8bcf09440855", - "bytes": 185704 + "source_commit": "af6f244b44b28a16a3578cf554d1e199a170a05e", + "source_content_sha256": "80d647b70a89f3d42398be621001ebfcdd08371b68ba523b6fa1f2048c06b64b", + "sha256": "80d647b70a89f3d42398be621001ebfcdd08371b68ba523b6fa1f2048c06b64b", + "bytes": 189149 }, { "name": "arcrun-mcp", "main_module": "worker.mjs", "main_file": "arcrun-mcp/worker.mjs", - "js_bytes": 1193125, + "js_bytes": 1205568, "modules": [], "compat_date": "2024-11-27", "compat_flags": [ @@ -440,10 +440,10 @@ "ai": false, "vars": {} }, - "source_commit": "c3efed7d1a01fc171df6eefb67d0bd380781be73", - "source_content_sha256": "e14dedfd630fed6e30f5d5f24a9d9b885899b81f4131ffb463705b151028c579", - "sha256": "e14dedfd630fed6e30f5d5f24a9d9b885899b81f4131ffb463705b151028c579", - "bytes": 1193125 + "source_commit": "af6f244b44b28a16a3578cf554d1e199a170a05e", + "source_content_sha256": "03d2022d9d3134988fddfd52bd9aa6d1c7734f9d14870fbe1cbb39d9aa27a2f4", + "sha256": "03d2022d9d3134988fddfd52bd9aa6d1c7734f9d14870fbe1cbb39d9aa27a2f4", + "bytes": 1205568 }, { "name": "arcrun-merge", @@ -698,12 +698,12 @@ "bytes": 67697 } ], - "release": "1.4.56", + "release": "1.4.57", "built_for": "oauth-installer-lazy-load", "notes": [ "rag_takedown_direct 用了 __CARDS_PREFIX__,但安裝器的代換表沒有它 ⇒ 這個佔位符會原封不動被推進使用者的工作流。" ], - "fingerprint": "a4051e876cc06cad3523c47d263882da17855abfc660eec9d8ecd29e9455ff8a", + "fingerprint": "8b0d054d65c958590d350d8342fe72a0377bd3f35033d9e33fd4d07b25e75b9a", "library": [ { "name": "arcrun-array-ops", @@ -888,7 +888,7 @@ "name": "arcrun-cypher-executor", "main_module": "worker.mjs", "main_file": "arcrun-cypher-executor/worker.mjs", - "js_bytes": 691420, + "js_bytes": 694462, "modules": [], "compat_date": "2025-02-19", "compat_flags": [ @@ -929,8 +929,8 @@ "stripped": { "services": 13 }, - "source_commit": "8257fd90a9a172a160a77980b0f10c1bc5a2eabb", - "source_content_sha256": "1750e8a938ae467a7091b9faa6c0549fac64fc7d35cd9067bf4c43f4b2db49d3", + "source_commit": "af6f244b44b28a16a3578cf554d1e199a170a05e", + "source_content_sha256": "531937c2ebade75fe67676071ca5f658775c11aa855359330ae19117db1d1807", "first_install": true }, { @@ -1075,7 +1075,7 @@ "name": "arcrun-kbdb", "main_module": "worker.mjs", "main_file": "arcrun-kbdb/worker.mjs", - "js_bytes": 185704, + "js_bytes": 189149, "modules": [], "compat_date": "2025-02-19", "compat_flags": [ @@ -1095,15 +1095,15 @@ "ENVIRONMENT": "production" } }, - "source_commit": "a1f45aef73de264236ff044e5b5fd5eb4d1cadc1", - "source_content_sha256": "68efc2b8958d1047c8d17f63325949af738379f8f1a2d509b9fa8bcf09440855", + "source_commit": "af6f244b44b28a16a3578cf554d1e199a170a05e", + "source_content_sha256": "80d647b70a89f3d42398be621001ebfcdd08371b68ba523b6fa1f2048c06b64b", "first_install": true }, { "name": "arcrun-mcp", "main_module": "worker.mjs", "main_file": "arcrun-mcp/worker.mjs", - "js_bytes": 1193125, + "js_bytes": 1205568, "modules": [], "compat_date": "2024-11-27", "compat_flags": [ @@ -1118,8 +1118,8 @@ "ai": false, "vars": {} }, - "source_commit": "c3efed7d1a01fc171df6eefb67d0bd380781be73", - "source_content_sha256": "e14dedfd630fed6e30f5d5f24a9d9b885899b81f4131ffb463705b151028c579", + "source_commit": "af6f244b44b28a16a3578cf554d1e199a170a05e", + "source_content_sha256": "03d2022d9d3134988fddfd52bd9aa6d1c7734f9d14870fbe1cbb39d9aa27a2f4", "first_install": true }, { @@ -1485,20 +1485,20 @@ } ], "daemon": { - "version": "0.18.38", + "version": "0.18.39", "mac": { - "file": "daemon/Arcrun-0.18.38.dmg", - "sha256": "0b91b70295823f343166e31f53bd170ec315b5a4d84c4858424547bdafaf4aa5" + "file": "daemon/Arcrun-0.18.39.dmg", + "sha256": "d5680ff4d683db68a8e1c333ef2ee8591b74795427ab645beefa766a2fcfc1df" }, "win": { - "file": "daemon/Arcrun-win-0.18.38.exe", - "sha256": "92927b0f8b7f3063d569628deec72b2b95c124060612741ef1aa6d6734656f56" + "file": "daemon/Arcrun-win-0.18.39.exe", + "sha256": "ed8f20ac1e676482b784e828417bbe18c0900dee8903d0a6275cb4f2c41db7ac" }, "msix": { - "file": "daemon/Arcrun-0.18.38.msix", - "sha256": "9f0527b60fba0bd27316b86cf606178bc221a3ef3265880490467a76b2d4ed98" + "file": "daemon/Arcrun-0.18.39.msix", + "sha256": "87c3e2a5b058b984636028c3ee1d54a93be21f6fa1f212646ca1e465b887a1d7" }, - "built": "20260826-2051", - "notes": "每一層子資料夾都看得到,不是只有第一層・小幫手看得到,不必等雲端、也不必連線・每一層都告訴你「幾份、上去了幾份」・整個沒走進去的資料夾,不會假裝成 0 / 0" + "built": "20260827-1248", + "notes": "「已送上去」從今以後是真的送上去了・太大的檔案現在會好好跟你說" } }