ship 1.4.59:Arcrun@f87260b1234f

This commit is contained in:
ship
2026-08-27 19:50:15 +08:00
parent 472407621d
commit f54c9d8df2
9 changed files with 304 additions and 116 deletions
+1 -1
View File
@@ -29,7 +29,7 @@ Served via jsDelivr; fetched automatically during install — you never need to
- `arcrun-wait/`**arcrun-wait**(首裝)
- `daemon/` — 桌面 AppMacWindows)安裝檔
Built from `Arcrun@ad60863a8017` by `installer/scripts/ship.mjs`arcrun-rag reporelease 1.4.58built 2026-08-27)。
Built from `Arcrun@f87260b1234f` by `installer/scripts/ship.mjs`arcrun-rag reporelease 1.4.59built 2026-08-27)。
⚠️ 這份檔案由出貨管線每次自動重寫(`installer/scripts/render-bundles-readme.mjs`)——
不要手動改這裡列的零件清單——它是算出來的:公庫=Arcrun 這一版編了什麼,
+111 -62
View File
@@ -3195,15 +3195,6 @@ function kbdbBase(env) {
function tenant(c) {
return c.req.header("X-Arcrun-API-Key") ?? null;
}
function graphBase(env) {
if (env.KBDB_GRAPH_URL) return env.KBDB_GRAPH_URL.replace(/\/$/, "");
return `https://kbdb-graph-plugin.${env.WORKER_SUBDOMAIN}.workers.dev`;
}
function graphHeaders(env) {
const headers = {};
if (env.KBDB_INTERNAL_TOKEN) headers["Authorization"] = `Bearer ${env.KBDB_INTERNAL_TOKEN}`;
return headers;
}
var kbdbProxyRouter, NEED_KEY;
var init_kbdb_proxy = __esm({
"cypher-executor/src/routes/kbdb-proxy.ts"() {
@@ -3359,14 +3350,23 @@ var init_kbdb_proxy = __esm({
return new Response(res.body, { status: res.status, headers: { "Content-Type": "application/json" } });
});
kbdbProxyRouter.get("/kbdb/graph/neighbors/:name", async (c) => {
if (!tenant(c)) return c.json(NEED_KEY, 401);
const base = graphBase(c.env);
const headers = graphHeaders(c.env);
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 ["depth", "template", "directed"]) {
const v = c.req.query(k);
if (v) params.set(k, v);
}
try {
const res = await fetch(`${base}/graph/neighbors/${encodeURIComponent(c.req.param("name"))}`, { headers });
const res = await fetch(
`${base}/graph/neighbors/${encodeURIComponent(c.req.param("name"))}?${params.toString()}`,
{ headers }
);
return new Response(res.body, { status: res.status, headers: { "Content-Type": "application/json" } });
} catch (e) {
return c.json({ error: `kbdb-graph-plugin \u4E0D\u53EF\u9054\uFF08${base}\uFF09\uFF1A${e instanceof Error ? e.message : String(e)}` }, 502);
return c.json({ error: `KBDB \u4E0D\u53EF\u9054\uFF08${base}\uFF09\uFF1A${e instanceof Error ? e.message : String(e)}` }, 502);
}
});
kbdbProxyRouter.get("/kbdb/map", async (c) => {
@@ -15047,7 +15047,6 @@ consoleDashboardRouter.get("/console/dashboard-data", async (c) => {
const tenant2 = knowledgeOwner(c.env);
const now2 = Date.now();
const { base: kbdbUrl, headers: kbdbHeaders2 } = kbdbBase(c.env);
const graphUrl = graphBase(c.env);
const [
beatEntries,
taskEntries,
@@ -15056,7 +15055,7 @@ consoleDashboardRouter.get("/console/dashboard-data", async (c) => {
giteaSprint,
kbdbHealth,
embedStatus,
graphStats,
graphProbe,
tripletTotal,
entriesTotal,
wikiCardTotal,
@@ -15069,11 +15068,15 @@ consoleDashboardRouter.get("/console/dashboard-data", async (c) => {
cachedGiteaSprint(c.env, now2, (p) => c.executionCtx.waitUntil(p)),
fetchJson(`${kbdbUrl}/health`, kbdbHeaders2),
fetchJson(`${kbdbUrl}/embed/backfill/status`, kbdbHeaders2),
// graph-plugin 只拿來判「圖服務活著沒(燈號)——數字不從這裡拿,見 fetchTripletTotal。
// headers 一定要帶:plugin 的 /triplets 前綴掛 Bearer 閘,漏帶=永遠 401=永遠假紅燈(#100)。
// 圖服務活著沒(燈號)——數字不從這裡拿,見 fetchTripletTotal。
// 🔴 inkstone/Arcrun#168 收斂:原本探的是 kbdb-graph-plugin 的 /triplets/stats
// 但圖能力已收斂回 KBDBGET /graph/neighbors/:node)⇒ 探那顆 plugin 等於在量一個
// 沒有人走的服務:它沒裝就永遠紅燈、裝了也只證明一個不再被使用的東西活著。
// 改探 KBDB 那支端點本身——用一個不存在的節點名,它會誠實回 count:0 的 200
//graph-query.ts:查不到就是空結果,不是錯誤),正好當「這條路通不通」的探針。
fetchJson(
`${graphUrl}/triplets/stats`,
graphHeaders(c.env)
`${kbdbUrl}/graph/neighbors/${encodeURIComponent("__arcrun_graph_probe__")}?depth=1`,
kbdbHeaders2
),
fetchTripletTotal(c.env, tenant2),
// owner_id 一律鎖本租戶:原本不帶 owner 會混到別租戶(實測 459,137 vs leo 的 458,732
@@ -15183,8 +15186,8 @@ consoleDashboardRouter.get("/console/dashboard-data", async (c) => {
system: {
kbdb_ok: kbdbHealth ? kbdbHealth.ok === true : false,
embed: embedStatus ? { enabled: embedStatus.enabled === true, embedded: embedStatus.embedded ?? null, pending: embedStatus.pending ?? null } : null,
// ok = plugin 通不通(graphStats 讀得到就是通);triplets = KBDB 真 COUNT與 plugin 分頁長度無關
graph: { ok: graphStats !== null, triplets: tripletTotal },
// ok = 圖查詢通不通(探針讀得到就是通);triplets = KBDB 真 COUNT兩件事,不互相吞
graph: { ok: graphProbe !== null, triplets: tripletTotal },
workflow_total: workflowTotal
},
kb: {
@@ -15270,7 +15273,6 @@ consoleDashboardRouter.post("/console/triage-check", async (c) => {
// cypher-executor/src/routes/portal-data.ts
init_dist();
init_kbdb_proxy();
init_webhook_handlers();
// cypher-executor/src/lib/app-system.ts
@@ -17001,7 +17003,7 @@ function unwrapWorkflowData(data, key) {
}
return outer;
}
function mapGraphWorkflowOutput(data) {
function mapGraphNeighborsResponse(data) {
const layer = unwrapWorkflowData(data, "neighbors");
const neighbors = Array.isArray(layer.neighbors) ? layer.neighbors : [];
const edges = Array.isArray(layer.edges) ? layer.edges : [];
@@ -17168,6 +17170,14 @@ portalDataRouter.get(
return c.json({ success: true, entry });
})
);
async function fetchNeighborsFromKbdb(env, tenant2, node, depth) {
const qs = new URLSearchParams();
qs.set("depth", String(depth));
qs.set("template", "triplet");
const res = await kbdbFetch(env, `/graph/neighbors/${encodeURIComponent(node)}?${qs.toString()}&${ownerQuery(tenant2)}`);
const body = await res.json().catch(() => null);
return { ok: res.ok, status: res.status, body };
}
portalDataRouter.get(
"/portal/data/graph/neighbors/:name",
(c) => run(c, async () => {
@@ -17177,50 +17187,41 @@ portalDataRouter.get(
if (!await hasGraphAccess(c.env, libraries)) {
return c.json({ error: "\u7121\u77E5\u8B58\u5716\u8B5C\u6AA2\u8996\u6B0A\u9650" }, 403);
}
const nodeName = normalizeCjkQuery(c.req.param("name"));
const tenant2 = knowledgeOwner(c.env);
const wfGraph = await getTenantWorkflowGraph(c.env, "graph_neighbors");
if (wfGraph) {
const depthRaw = c.req.query("depth") ?? "";
const depth = /^\d{1,2}$/.test(depthRaw) ? Number(depthRaw) : 2;
const result = await executeWebhookGraph(
c.env,
wfGraph,
// t116: 補傳 kbdb_baset128: 補傳 templateworkflow fetch_triplets.url 用 {{input.template}}
{ node: nodeName, depth, namespace: tenant2, owner: tenant2, kbdb_base: c.env.KBDB_BASE_URL ?? "", template: "triplet" },
"graph_neighbors",
tenant2,
c.executionCtx
);
if (!result.success) {
return c.json({ error: `graph_neighbors workflow \u57F7\u884C\u5931\u6557\uFF1A${result.error ?? "\u672A\u77E5\u932F\u8AA4"}` }, 502);
}
return c.json(mapGraphWorkflowOutput(result.data));
}
const base = graphBase(c.env);
const headers = graphHeaders(c.env);
const rawName = c.req.param("name");
const depthRaw = c.req.query("depth") ?? "";
const depth = /^\d{1,2}$/.test(depthRaw) ? Number(depthRaw) : 2;
const tryNames = [rawName];
const normalized = normalizeCjkQuery(rawName);
if (normalized !== rawName) tryNames.push(normalized);
let first = null;
try {
const res = await fetch(`${base}/graph/neighbors/${encodeURIComponent(nodeName)}`, { headers });
if (!res.ok) {
return new Response(res.body, { status: res.status, headers: { "Content-Type": "application/json" } });
for (const name of tryNames) {
const r = await fetchNeighborsFromKbdb(c.env, tenant2, name, depth);
if (!first) first = r;
if (!r.ok) break;
const mapped = mapGraphNeighborsResponse(r.body);
if (mapped.count > 0) return c.json(mapped);
}
const resText = await res.text().catch(() => "");
let data = null;
try {
data = JSON.parse(resText);
} catch {
}
if (data && Array.isArray(data.neighbors) && data.neighbors.length === 0 && Array.isArray(data.edges) && data.edges.length === 0) {
const fallbackName = await fuzzyFindNode(c.env, tenant2, nodeName);
if (fallbackName && fallbackName !== nodeName) {
const res2 = await fetch(`${base}/graph/neighbors/${encodeURIComponent(fallbackName)}`, { headers });
return new Response(res2.body, { status: res2.status, headers: { "Content-Type": "application/json" } });
if (first?.ok) {
const fallbackName = await fuzzyFindNode(c.env, tenant2, rawName);
if (fallbackName && !tryNames.includes(fallbackName)) {
const r = await fetchNeighborsFromKbdb(c.env, tenant2, fallbackName, depth);
if (r.ok) {
const mapped = mapGraphNeighborsResponse(r.body);
if (mapped.count > 0) return c.json(mapped);
}
}
}
return new Response(resText, { status: res.status, headers: { "Content-Type": "application/json" } });
} catch (e) {
return c.json({ error: `kbdb-graph-plugin \u4E0D\u53EF\u9054\uFF1A${e instanceof Error ? e.message : String(e)}` }, 502);
return c.json({ error: `KBDB \u5716\u8B5C\u67E5\u8A62\u4E0D\u53EF\u9054\uFF1A${e instanceof Error ? e.message : String(e)}` }, 502);
}
if (!first) return c.json({ error: "KBDB \u5716\u8B5C\u67E5\u8A62\u6C92\u6709\u56DE\u61C9" }, 502);
if (!first.ok) {
const err = first.body && typeof first.body === "object" ? first.body : { error: `KBDB \u5716\u8B5C\u67E5\u8A62\u5931\u6557\uFF08HTTP ${first.status}\uFF09` };
return c.json(err, first.status);
}
return c.json(mapGraphNeighborsResponse(first.body));
})
);
portalDataRouter.get(
@@ -17538,6 +17539,29 @@ function canReadRecord(rec, tenant2, libraries) {
const lib = recordLibrary(rec.values);
return lib === null || canReadLibrary(libraries, lib);
}
function cardOriginalLocation(entries, library, libraryRoot) {
for (const e of entries) {
if (typeof e.metadata_json !== "string" || !e.metadata_json) continue;
let meta;
try {
meta = JSON.parse(e.metadata_json);
} catch {
continue;
}
const sourcePath = typeof meta.source_path === "string" && meta.source_path.trim() ? meta.source_path.trim() : null;
const machine = typeof meta.machine_label === "string" && meta.machine_label.trim() ? meta.machine_label.trim() : typeof meta.machine === "string" && meta.machine.trim() ? meta.machine.trim() : null;
if (!sourcePath && !machine) continue;
const root = libraryRoot && libraryRoot.trim() ? libraryRoot.trim() : null;
return {
machine,
library,
library_root: root,
source_path: sourcePath,
full_path: root && sourcePath ? `${root.replace(/\/+$/, "")}/${sourcePath.replace(/^\/+/, "")}` : null
};
}
return null;
}
portalDataRouter.get(
"/portal/data/library-cards",
(c) => run(c, async () => {
@@ -17579,7 +17603,19 @@ portalDataRouter.get(
}
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 });
const libRecords = await listRecordsByTemplate(c.env, LIBRARY_TEMPLATE).catch(() => []);
const libRootRaw = libRecords.find((r) => String(r.values.name ?? "") === library)?.values.root;
const libraryRoot = typeof libRootRaw === "string" ? libRootRaw : null;
const location = cardOriginalLocation(entries, library, libraryRoot);
return c.json({
success: true,
library,
page_name: pageName,
entries,
count: entries.length,
location,
...location ? {} : { location_hint: "\u9019\u5F35\u5361\u7684\u6BB5\u843D\u6C92\u6709 machine/source_path \u4E2D\u7E7C\u8CC7\u6599\uFF0C\u7B54\u4E0D\u51FA\u539F\u6587\u7684\u5BE6\u9AD4\u4F4D\u7F6E" }
});
})
);
portalDataRouter.get(
@@ -17711,6 +17747,19 @@ portalDataRouter.get(
if (!auth.ok) return auth.res;
const libraries = parseLibraries(auth.user.values.libraries);
if (libraries.length === 0) return c.json({ success: true, records: [], count: 0, total: 0 });
const template = c.req.param("template");
if (template === LIBRARY_TEMPLATE) {
const all = await listRecordsByTemplate(c.env, LIBRARY_TEMPLATE);
const records2 = libraries.includes("*") ? all : all.filter((r) => libraries.includes(String(r.values.name ?? "")));
return c.json({
success: true,
records: records2,
count: records2.length,
page_size: all.length,
filtered_out: all.length - records2.length,
total: records2.length
});
}
const tenant2 = knowledgeOwner(c.env);
const params = new URLSearchParams(ownerQuery(tenant2));
for (const k of ["limit", "offset"]) {
+116 -2
View File
@@ -4241,11 +4241,15 @@ async function selectLibrariesForQuestion(db, question, opts = {}) {
SELECT object AS name, ${libraryOf("library")} AS library FROM act WHERE object IS NOT NULL)
SELECT name, library, COUNT(*) AS degree
FROM ent
WHERE LENGTH(name) >= 2 AND instr(?, lower(name)) > 0
WHERE LENGTH(name) >= 2
AND (
instr(?, lower(name)) > 0
OR (LENGTH(?) >= 2 AND instr(lower(name), ?) > 0)
)
GROUP BY name, library
ORDER BY degree DESC, name ASC
LIMIT 200`
).bind(...params, qNorm).all();
).bind(...params, qNorm, qNorm, qNorm).all();
for (const r of res.results ?? []) {
const cur = hits.get(r.library) ?? { entities: /* @__PURE__ */ new Set(), score: 0 };
cur.entities.add(r.name);
@@ -4545,6 +4549,109 @@ mapRoutes.get("/:library", async (c) => {
return c.json({ success: true, map });
});
// kbdb/src/actions/graph-query.ts
async function findTripletEdgesByNode(db, templateIdOrName, fields, nodeValue, owner_id) {
if (!nodeValue || fields.length === 0) return [];
const tpl = await getTemplate(db, templateIdOrName);
if (!tpl) return [];
const valueSql = owner_id ? `SELECT id FROM entries WHERE content = ? AND entry_type = 'value' AND (owner_id = ? OR owner_id IS NULL)` : `SELECT id FROM entries WHERE content = ? AND entry_type = 'value'`;
const valueParams = owner_id ? [nodeValue, owner_id] : [nodeValue];
const valueRows = await db.prepare(valueSql).bind(...valueParams).all();
const valueIds = (valueRows.results ?? []).map((r) => r.id);
if (valueIds.length === 0) return [];
const fieldIds = fields.map((f) => fieldEntryId(tpl.id, f));
const dstPh = valueIds.map(() => "?").join(",");
const relPh = fieldIds.map(() => "?").join(",");
const relSql = owner_id ? `SELECT DISTINCT src_id AS record_id FROM entries
WHERE dst_id IN (${dstPh}) AND rel_id IN (${relPh}) AND owner_id = ?` : `SELECT DISTINCT src_id AS record_id FROM entries
WHERE dst_id IN (${dstPh}) AND rel_id IN (${relPh})`;
const relParams = owner_id ? [...valueIds, ...fieldIds, owner_id] : [...valueIds, ...fieldIds];
const relRows = await db.prepare(relSql).bind(...relParams).all();
const recordIds = (relRows.results ?? []).map((r) => r.record_id);
if (recordIds.length === 0) return [];
const recPh = recordIds.map(() => "?").join(",");
const pivotSql = `
SELECT b.src_id AS record_id,
MAX(CASE WHEN r.rel_id = ? THEN v.content END) AS subject,
MAX(CASE WHEN r.rel_id = ? THEN v.content END) AS predicate,
MAX(CASE WHEN r.rel_id = ? THEN v.content END) AS object,
MAX(CASE WHEN r.rel_id = ? THEN v.content END) AS status
FROM entries b
LEFT JOIN entries r ON r.src_id = b.src_id AND r.rel_id != 'sys_belongs'
LEFT JOIN entries v ON v.id = r.dst_id
WHERE b.rel_id = 'sys_belongs' AND b.src_id IN (${recPh})
GROUP BY b.src_id`;
const pivotParams = [
fieldEntryId(tpl.id, "subject"),
fieldEntryId(tpl.id, "predicate"),
fieldEntryId(tpl.id, "object"),
fieldEntryId(tpl.id, "status"),
...recordIds
];
const pivotRows = await db.prepare(pivotSql).bind(...pivotParams).all();
return pivotRows.results ?? [];
}
async function graphNeighbors(db, start, opts = {}) {
const depth = Math.max(1, Math.min(Math.floor(opts.depth ?? 1) || 1, 10));
const template = opts.template ?? "triplet";
const directed = !!opts.directed;
const owner_id = opts.owner_id;
const visited = /* @__PURE__ */ new Set([start]);
let frontier = [start];
const neighbors = [];
for (let d = 1; d <= depth; d++) {
if (frontier.length === 0) break;
const next = [];
for (const cur of frontier) {
const outgoing = await findTripletEdgesByNode(db, template, ["subject"], cur, owner_id);
for (const e of outgoing) {
if (e.status === "deprecated") continue;
const nb = e.object;
if (!nb || visited.has(nb)) continue;
visited.add(nb);
neighbors.push({ node: nb, predicate: e.predicate ?? "", from: cur, depth: d });
next.push(nb);
}
if (!directed) {
const incoming = await findTripletEdgesByNode(db, template, ["object"], cur, owner_id);
for (const e of incoming) {
if (e.status === "deprecated") continue;
const nb = e.subject;
if (!nb || visited.has(nb)) continue;
visited.add(nb);
neighbors.push({ node: nb, predicate: e.predicate ?? "", from: cur, depth: d });
next.push(nb);
}
}
}
frontier = next;
}
return { success: true, start, depth, directed, neighbors, count: neighbors.length };
}
// kbdb/src/routes/graph.ts
var graphRoutes = new Hono2();
graphRoutes.get("/neighbors/:node", async (c) => {
const node = c.req.param("node");
if (!node) return c.json({ success: false, error: "node required" }, 400);
const depthRaw = Number(c.req.query("depth"));
const depth = Number.isFinite(depthRaw) && depthRaw > 0 ? Math.floor(depthRaw) : void 0;
const directed = c.req.query("directed") === "true";
const template = c.req.query("template") || void 0;
const owner_id = c.req.query("owner_id") || void 0;
try {
const result = await graphNeighbors(c.env.DB, node, { depth, template, directed, owner_id });
const edges = result.neighbors.map((n) => ({
subject: n.from,
predicate: n.predicate,
object: n.node
}));
return c.json({ ...result, edges });
} catch (e) {
return c.json({ success: false, error: e instanceof Error ? e.message : String(e) }, 500);
}
});
// kbdb/src/actions/execution-log.ts
var SUCCESS_MESSAGE_MAX = 200;
var FAILED_MESSAGE_MAX = 2e3;
@@ -4832,6 +4939,12 @@ var GENERATIONS = [
// 🪦 entry_values 2026-08-15 廢除;「看到它=那台沒跟上,不是它還合法」。
{ kind: "no_table", name: "entry_values" }
]
},
{
n: 8,
file: "0008_entries_content_index.sql",
what: "entries.content \u7D22\u5F15\u2014\u2014graph \u9130\u5C45\u67E5\u8A62\u5F9E\u7BC0\u9EDE\u540D\u76F4\u63A5\u67E5\uFF08\u4E0D\u6488\u5168\u8868\uFF09\uFF0CArcrun#168 \u6839\u56E0\u4FEE\u5FA9\u7684\u5730\u57FA",
checks: [{ kind: "index", name: "idx_entries_content" }]
}
];
var EXPECTED_GENERATION = GENERATIONS[GENERATIONS.length - 1].n;
@@ -4990,6 +5103,7 @@ app.route("/recipe-stats", recipeStatRoutes);
app.route("/execution-log", executionLogRoutes);
app.route("/embed", embedRoutes);
app.route("/map", mapRoutes);
app.route("/graph", graphRoutes);
var index_default = app;
export {
index_default as default
+24 -3
View File
@@ -32025,7 +32025,7 @@ function registerAllKbdbGraphTools(server, env, orgNamespace, identity) {
function registerGraphNeighbors(server, env, orgNamespace, identity) {
server.tool(
"kbdb_graph_neighbors",
"knowledge graph \u9130\u5C45\u67E5\u8A62\uFF081-hop/N-hop \u95DC\u4FC2\u904D\u6B77\uFF09\uFF1A\u7D66\u4E00\u500B\u7BC0\u9EDE\u540D\uFF0C\u6CBF KBDB triplet\uFF08subject-predicate-object\uFF09\u8A18\u9304\u505A BFS\uFF0C\u56DE\u50B3 depth \u8DF3\u5167\u7684\u9130\u5C45\u6E05\u55AE\uFF08[{node, predicate, from, depth}]\uFF09\u3002\u8207 kbdb_search\uFF08\u95DC\u9375\u5B57/\u8A9E\u7FA9\uFF09\u4E92\u88DC\uFF1A\u627E\u300C\u8DDF X \u6709\u95DC\u4FC2\u7684\u6771\u897F\u300D\u7528\u672C\u5DE5\u5177\uFF0C\u627E\u300C\u5167\u5BB9\u542B\u95DC\u9375\u5B57\u7684\u6771\u897F\u300D\u7528 kbdb_search\u3002\u9700\u8981 namespace \u88E1\u5DF2\u90E8\u7F72 graph_neighbors workflow\uFF08\u6C92\u88DD\u6703\u56DE\u5B89\u88DD\u6307\u5F15\uFF0C\u4E0D\u6703 crash\uFF09\u3002",
"knowledge graph \u9130\u5C45\u67E5\u8A62\uFF081-hop/N-hop \u95DC\u4FC2\u904D\u6B77\uFF09\uFF1A\u7D66\u4E00\u500B\u7BC0\u9EDE\u540D\uFF0C\u6CBF KBDB triplet\uFF08subject-predicate-object\uFF09\u8A18\u9304\u505A BFS\uFF0C\u56DE\u50B3 depth \u8DF3\u5167\u7684\u9130\u5C45\u6E05\u55AE\uFF08[{node, predicate, from, depth}]\uFF09\u3002\u8207 kbdb_search\uFF08\u95DC\u9375\u5B57/\u8A9E\u7FA9\uFF09\u4E92\u88DC\uFF1A\u627E\u300C\u8DDF X \u6709\u95DC\u4FC2\u7684\u6771\u897F\u300D\u7528\u672C\u5DE5\u5177\uFF0C\u627E\u300C\u5167\u5BB9\u542B\u95DC\u9375\u5B57\u7684\u6771\u897F\u300D\u7528 kbdb_search\u3002\u767B\u5165\u8EAB\u5206\uFF08\u5E33\u5BC6\uFF0FOAuth\uFF09\u76F4\u63A5\u8D70 KBDB \u7684\u5716 API\uFF0C\u4E0D\u9700\u8981\u88DD\u4EFB\u4F55 workflow\uFF1B\u670D\u52D9\u7D1A token \u9023\u7DDA\u624D\u9700\u8981 namespace \u88E1\u5DF2\u90E8\u7F72 graph_neighbors workflow\uFF08\u6C92\u88DD\u6703\u56DE\u5B89\u88DD\u6307\u5F15\uFF0C\u4E0D\u6703 crash\uFF09\u3002",
{
subject: external_exports.string().min(1).describe(
"\u8D77\u9EDE\u7BC0\u9EDE\u540D\uFF08graph triplet \u7684 subject/object \u503C\uFF09\uFF0C\u5982 'Arcrun'"
@@ -32331,6 +32331,20 @@ function registerGetMap(server, env, identity) {
// mcp/src/tools/kbdb_index.ts
var INDEX_CARD_NAME = "00-INDEX";
function locationHints(location, hint) {
if (location) {
const where = [
location.machine ? `\u8A2D\u5099\u300C${location.machine}\u300D` : null,
`\u5EAB\u300C${location.library}\u300D`,
location.full_path ? `\u8DEF\u5F91 ${location.full_path}` : location.source_path ? `\u5EAB\u5167\u8DEF\u5F91 ${location.source_path}\uFF08\u9019\u500B\u5EAB\u9084\u6C92\u767B\u8A18\u5BE6\u9AD4\u8CC7\u6599\u593E\uFF0C\u7B54\u4E0D\u51FA\u5B8C\u6574\u8DEF\u5F91\uFF09` : null
].filter(Boolean);
return [
`\u539F\u6587\u5728\u54EA\uFF1A${where.join("\u3001")}\u3002`,
"\u{1F534} \u56DE\u7B54\u300C\u539F\u6587\u5728\u54EA\u300D\u6642\u7528\u9019\u500B location \u6B04\u4F4D\uFF0C\u4E0D\u8981\u5F15\u7528\u5361\u7247\u5167\u6587\u88E1\u300C### \u51FA\u8655\u300D\u90A3\u4E00\u6BB5\u2014\u2014\u90A3\u5BEB\u7684\u662F\u5167\u90E8\u76F8\u5C0D\u8DEF\u5F91\uFF08`../\u6A94\u540D`\uFF09\uFF0C\u4F7F\u7528\u8005\u62FF\u8457\u5B83\u8D70\u4E0D\u5230\u4EFB\u4F55\u5730\u65B9\u3002"
];
}
return [hint ?? "\u9019\u5F35\u5361\u6C92\u6709 machine/source_path \u4E2D\u7E7C\u8CC7\u6599\uFF0C\u7B54\u4E0D\u51FA\u539F\u6587\u7684\u5BE6\u9AD4\u4F4D\u7F6E\u2014\u2014\u4E0D\u8981\u7528\u5167\u6587\u300C### \u51FA\u8655\u300D\u90A3\u6BB5\u4EE3\u7B54\u3002"];
}
function anchorOf(block) {
try {
const meta = typeof block.metadata_json === "string" ? JSON.parse(block.metadata_json) : block.metadata_json ?? {};
@@ -32464,11 +32478,13 @@ function registerGetCard(server, env, identity) {
);
}
const content = assembleCard(blocks);
const location = isPortal ? body.location ?? null : null;
return successResponse(
{ library, page_name, content, block_count: blocks.length },
{ library, page_name, content, block_count: blocks.length, location },
[
`\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)"
"\u8981\u770B\u5B83\u9023\u5230\u54EA\u4E9B\u5361\uFF1Akbdb_graph_neighbors(name)",
...locationHints(location, isPortal ? body.location_hint : void 0)
]
);
} catch (e) {
@@ -32567,6 +32583,11 @@ var KNOWLEDGE_FIRST = [
"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**",
"",
"\u{1F534} **\u6709\u4EBA\u554F\u300C\u539F\u6587\u5728\u54EA\uFF0F\u9019\u4EFD\u8CC7\u6599\u54EA\u4F86\u7684\u300D\u2014\u2014\u7B54\u6848\u8981\u8B80 `kbdb_get_card` \u56DE\u61C9\u7684 `location` \u6B04\u4F4D",
"\uFF08\u8A2D\u5099\uFF0F\u5EAB\uFF0F\u5EAB\u5167\u8DEF\u5F91\uFF09\uFF0C\u4E0D\u8981\u5F15\u7528\u5361\u7247\u5167\u6587\u88E1\u300C### \u51FA\u8655\u300D\u90A3\u4E00\u6BB5\u3002** \u90A3\u6BB5\u5BEB\u7684\u662F\u8403\u5361\u7576\u4E0B\u7684",
"\u5167\u90E8\u76F8\u5C0D\u8DEF\u5F91\uFF08\u5F62\u5982 `../\u6A94\u540D`\uFF09\uFF0C\u4F7F\u7528\u8005\u62FF\u8457\u5B83\u8D70\u4E0D\u5230\u4EFB\u4F55\u5730\u65B9\uFF08inkstone/Arcrun#167\uFF09\u3002",
"`location` \u662F null \u6642\uFF0C`kbdb_get_card` \u7684 hints \u6703\u8AA0\u5BE6\u8B1B\u7F3A\u4EC0\u9EBC\u2014\u2014\u7167\u8457\u8AAA\uFF0C\u4E0D\u8981\u7528\u5167\u6587\u90A3\u6BB5\u4EE3\u7B54\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",
Binary file not shown.
Binary file not shown.
BIN
View File
Binary file not shown.
+50 -46
View File
@@ -1,7 +1,7 @@
{
"schema": 2,
"built": "2026-08-27",
"source": "Arcrun@ad60863a8017",
"source": "Arcrun@f87260b1234f",
"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": 694462,
"js_bytes": 696639,
"modules": [],
"compat_date": "2025-02-19",
"compat_flags": [
@@ -239,10 +239,10 @@
"stripped": {
"services": 13
},
"source_commit": "af6f244b44b28a16a3578cf554d1e199a170a05e",
"source_content_sha256": "531937c2ebade75fe67676071ca5f658775c11aa855359330ae19117db1d1807",
"sha256": "531937c2ebade75fe67676071ca5f658775c11aa855359330ae19117db1d1807",
"bytes": 694462
"source_commit": "f87260b1234f0c108c378caf474d7af78cdd6cb9",
"source_content_sha256": "2390294711ed529125559e505ed8f575093798480606253b31021b6faca4c758",
"sha256": "2390294711ed529125559e505ed8f575093798480606253b31021b6faca4c758",
"bytes": 696639
},
{
"name": "arcrun-date-ops",
@@ -396,7 +396,7 @@
"name": "arcrun-kbdb",
"main_module": "worker.mjs",
"main_file": "arcrun-kbdb/worker.mjs",
"js_bytes": 189149,
"js_bytes": 194366,
"modules": [],
"compat_date": "2025-02-19",
"compat_flags": [
@@ -416,16 +416,16 @@
"ENVIRONMENT": "production"
}
},
"source_commit": "af6f244b44b28a16a3578cf554d1e199a170a05e",
"source_content_sha256": "80d647b70a89f3d42398be621001ebfcdd08371b68ba523b6fa1f2048c06b64b",
"sha256": "80d647b70a89f3d42398be621001ebfcdd08371b68ba523b6fa1f2048c06b64b",
"bytes": 189149
"source_commit": "f87260b1234f0c108c378caf474d7af78cdd6cb9",
"source_content_sha256": "97140b9d10337c0f995b5bd5a4a3403ff06403afb6a51394b3ede013293c5bf6",
"sha256": "97140b9d10337c0f995b5bd5a4a3403ff06403afb6a51394b3ede013293c5bf6",
"bytes": 194366
},
{
"name": "arcrun-mcp",
"main_module": "worker.mjs",
"main_file": "arcrun-mcp/worker.mjs",
"js_bytes": 1205568,
"js_bytes": 1208080,
"modules": [],
"compat_date": "2024-11-27",
"compat_flags": [
@@ -440,10 +440,10 @@
"ai": false,
"vars": {}
},
"source_commit": "af6f244b44b28a16a3578cf554d1e199a170a05e",
"source_content_sha256": "03d2022d9d3134988fddfd52bd9aa6d1c7734f9d14870fbe1cbb39d9aa27a2f4",
"sha256": "03d2022d9d3134988fddfd52bd9aa6d1c7734f9d14870fbe1cbb39d9aa27a2f4",
"bytes": 1205568
"source_commit": "f87260b1234f0c108c378caf474d7af78cdd6cb9",
"source_content_sha256": "fad73a5128b50f8f42317ea1c4844ea39eee3c32846ab061282e77b731d810d0",
"sha256": "fad73a5128b50f8f42317ea1c4844ea39eee3c32846ab061282e77b731d810d0",
"bytes": 1208080
},
{
"name": "arcrun-merge",
@@ -507,7 +507,7 @@
"name": "arcrun-rag-ui",
"main_module": "index.js",
"main_file": "tier2/ui/index.js",
"js_bytes": 673028,
"js_bytes": 677629,
"modules": [],
"compat_date": "2026-07-01",
"compat_flags": [],
@@ -518,10 +518,10 @@
"ai": false,
"vars": {}
},
"source_commit": "790b41d10d48f8d6d117797c4b81f9884f909209",
"source_content_sha256": "8a6048f8ffe7125d0077688043a787849e8332d9613d6b693494583aef9de927",
"sha256": "8a6048f8ffe7125d0077688043a787849e8332d9613d6b693494583aef9de927",
"bytes": 673028
"source_commit": "f87260b1234f0c108c378caf474d7af78cdd6cb9",
"source_content_sha256": "8f3bbc90f901a0f06f40f4fdaaf5ec4ed80a2012822596a78ae47a92014a9a42",
"sha256": "8f3bbc90f901a0f06f40f4fdaaf5ec4ed80a2012822596a78ae47a92014a9a42",
"bytes": 677629
},
{
"name": "arcrun-set",
@@ -698,12 +698,12 @@
"bytes": 67697
}
],
"release": "1.4.58",
"release": "1.4.59",
"built_for": "oauth-installer-lazy-load",
"notes": [
"rag_takedown_direct 用了 __CARDS_PREFIX__,但安裝器的代換表沒有它 ⇒ 這個佔位符會原封不動被推進使用者的工作流。"
],
"fingerprint": "0216b2185da93feb5878b09abb05c72eb25978a9eeeef0e2c06639b377d67be9",
"fingerprint": "6d5575253074f913fa548e13da8ce6c9cda4d9598a8bd5971642ab66ea43bf80",
"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": 694462,
"js_bytes": 696639,
"modules": [],
"compat_date": "2025-02-19",
"compat_flags": [
@@ -929,8 +929,8 @@
"stripped": {
"services": 13
},
"source_commit": "af6f244b44b28a16a3578cf554d1e199a170a05e",
"source_content_sha256": "531937c2ebade75fe67676071ca5f658775c11aa855359330ae19117db1d1807",
"source_commit": "f87260b1234f0c108c378caf474d7af78cdd6cb9",
"source_content_sha256": "2390294711ed529125559e505ed8f575093798480606253b31021b6faca4c758",
"first_install": true
},
{
@@ -1075,7 +1075,7 @@
"name": "arcrun-kbdb",
"main_module": "worker.mjs",
"main_file": "arcrun-kbdb/worker.mjs",
"js_bytes": 189149,
"js_bytes": 194366,
"modules": [],
"compat_date": "2025-02-19",
"compat_flags": [
@@ -1095,15 +1095,15 @@
"ENVIRONMENT": "production"
}
},
"source_commit": "af6f244b44b28a16a3578cf554d1e199a170a05e",
"source_content_sha256": "80d647b70a89f3d42398be621001ebfcdd08371b68ba523b6fa1f2048c06b64b",
"source_commit": "f87260b1234f0c108c378caf474d7af78cdd6cb9",
"source_content_sha256": "97140b9d10337c0f995b5bd5a4a3403ff06403afb6a51394b3ede013293c5bf6",
"first_install": true
},
{
"name": "arcrun-mcp",
"main_module": "worker.mjs",
"main_file": "arcrun-mcp/worker.mjs",
"js_bytes": 1205568,
"js_bytes": 1208080,
"modules": [],
"compat_date": "2024-11-27",
"compat_flags": [
@@ -1118,8 +1118,8 @@
"ai": false,
"vars": {}
},
"source_commit": "af6f244b44b28a16a3578cf554d1e199a170a05e",
"source_content_sha256": "03d2022d9d3134988fddfd52bd9aa6d1c7734f9d14870fbe1cbb39d9aa27a2f4",
"source_commit": "f87260b1234f0c108c378caf474d7af78cdd6cb9",
"source_content_sha256": "fad73a5128b50f8f42317ea1c4844ea39eee3c32846ab061282e77b731d810d0",
"first_install": true
},
{
@@ -1180,7 +1180,7 @@
"name": "arcrun-rag-ui",
"main_module": "index.js",
"main_file": "tier2/ui/index.js",
"js_bytes": 673028,
"js_bytes": 677629,
"modules": [],
"compat_date": "2026-07-01",
"compat_flags": [],
@@ -1191,8 +1191,8 @@
"ai": false,
"vars": {}
},
"source_commit": "790b41d10d48f8d6d117797c4b81f9884f909209",
"source_content_sha256": "8a6048f8ffe7125d0077688043a787849e8332d9613d6b693494583aef9de927",
"source_commit": "f87260b1234f0c108c378caf474d7af78cdd6cb9",
"source_content_sha256": "8f3bbc90f901a0f06f40f4fdaaf5ec4ed80a2012822596a78ae47a92014a9a42",
"first_install": true
},
{
@@ -1375,13 +1375,17 @@
"worker": "arcrun-mcp",
"why": "leo 2026-08-10:「今天只要清單含有 MCP 即可」。產品承諾是「把自己的 AI 接上自己的知識庫」,MCP 就是那條線;缺它的話那句承諾從第一步就不成立(prod 1.4.30 曾整包沒有它)。"
},
{
"worker": "arcrun-code",
"why": "graph_neighborsprep 用 __CODE_URL__"
},
{
"worker": "arcrun-http-request",
"why": "graph_neighborsfetch_triplets 用 __HTTP_REQ_URL__"
"why": "graph_neighborsfetch_neighbors 用 __HTTP_REQ_URL__"
},
{
"worker": "arcrun-code",
"why": "graph_neighborsbfs_neighbors 用 __CODE_URL__"
"why": "graph_neighborsparse_neighbors 用 __CODE_URL__"
},
{
"worker": "arcrun-auth-static-key",
@@ -1485,20 +1489,20 @@
}
],
"daemon": {
"version": "0.18.40",
"version": "0.18.41",
"mac": {
"file": "daemon/Arcrun-0.18.40.dmg",
"sha256": "5434b22742c9d1b449d22ab18fe48f2bb9f57c6ac4690e824e391135e7df0ab3"
"file": "daemon/Arcrun-0.18.41.dmg",
"sha256": "c37705824a139a2b0f2f8fb7d3d5aede686296e6c7820cb96b246d5e6638af61"
},
"win": {
"file": "daemon/Arcrun-win-0.18.40.exe",
"sha256": "d84c3159379fd3a415bcab5192a376ae3753f9e12dd8624c5ffced59406b5139"
"file": "daemon/Arcrun-win-0.18.41.exe",
"sha256": "873ffe08176cfcd381d1ef02a9c88f52be12a8f918eaa83d2004d59db257c19f"
},
"msix": {
"file": "daemon/Arcrun-0.18.40.msix",
"sha256": "809b0da36d43ce0b75780f91e34f9f4f0138ea075f3ac92c95155a4f0df8d804"
"file": "daemon/Arcrun-0.18.41.msix",
"sha256": "6806a58b6efd570e59502f7a5c4d7307fda2fcae39162f8089bab135110c4532"
},
"built": "20260827-1401",
"notes": "卡住的檔案不再擋住後面所有人"
"built": "20260827-1950",
"notes": "不叫「docs」的文件夾,現在也認得出來"
}
}
+2 -2
View File
File diff suppressed because one or more lines are too long