diff --git a/README.md b/README.md index bff621f..974e7b7 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@f8590c7252cc` by `installer/scripts/ship.mjs`(arcrun-rag repo,release 1.4.65,built 2026-09-13)。 +Built from `Arcrun@63e3c6da1b38` by `installer/scripts/ship.mjs`(arcrun-rag repo,release 1.4.66,built 2026-09-13)。 ⚠️ 這份檔案由出貨管線每次自動重寫(`installer/scripts/render-bundles-readme.mjs`)—— 不要手動改這裡列的零件清單——它是算出來的:公庫=Arcrun 這一版編了什麼, diff --git a/arcrun-cypher-executor/worker.mjs b/arcrun-cypher-executor/worker.mjs index c439e33..85bbe45 100644 --- a/arcrun-cypher-executor/worker.mjs +++ b/arcrun-cypher-executor/worker.mjs @@ -3525,11 +3525,11 @@ function invalidateCredentialCache(apiKey) { async function getCredentialDirectory(env, apiKey) { const now2 = Date.now(); const cached = dirCache[apiKey]; - if (cached && now2 - cached.fetchedAt < DIR_CACHE_TTL_MS) return { rows: cached.rows, error: null }; + if (cached && now2 - cached.fetchedAt < DIR_CACHE_TTL_MS) return cached.rows; const qs = new URLSearchParams({ owner_id: apiKey, entry_type: CREDENTIAL_ENTRY_TYPE, limit: "200" }); const res = await kbdbCredFetch(env, `/entries?${qs.toString()}`); if (!res.ok) { - return { rows: [], error: `KBDB \u56DE HTTP ${res.status}` }; + return []; } const body = await res.json().catch(() => null); const rows = (body?.entries ?? []).filter((e) => !!e.page_name).map((e) => { @@ -3544,15 +3544,15 @@ async function getCredentialDirectory(env, apiKey) { }; }); dirCache[apiKey] = { rows, fetchedAt: now2 }; - return { rows, error: null }; + return rows; } -async function getCredentialSecretRefsDetailed(env, apiKey) { - const { rows, error } = await getCredentialDirectory(env, apiKey); - const refs = {}; +async function getCredentialSecretRefs(env, apiKey) { + const rows = await getCredentialDirectory(env, apiKey); + const out = {}; for (const r of rows) { - if (r.secret_ref) refs[r.name] = r.secret_ref; + if (r.secret_ref) out[r.name] = r.secret_ref; } - return { refs, directoryError: error }; + return out; } function touchLastUsed(env, apiKey, names) { const cached = dirCache[apiKey]; @@ -3560,7 +3560,6 @@ function touchLastUsed(env, apiKey, names) { const now2 = Math.floor(Date.now() / 1e3); for (const r of cached.rows) { if (!names.includes(r.name)) continue; - if (typeof r.last_used_at === "number" && now2 - r.last_used_at < LAST_USED_MIN_INTERVAL_S) continue; const meta = { service: r.service, sensitivity: r.sensitivity, @@ -3640,7 +3639,7 @@ async function writeCredential(env, apiKey, name, value, service, sensitivityRaw await upsertCredentialEntry(env, apiKey, name, service ?? null, sensitivity, secretRef); return { secretRef, sensitivity }; } -var credentialsRouter, CYPHER_SCRIPT_NAME, CREDENTIAL_ENTRY_TYPE, DIR_CACHE_TTL_MS, dirCache, LAST_USED_MIN_INTERVAL_S, VALUE_LIKE_FIELDS; +var credentialsRouter, CYPHER_SCRIPT_NAME, CREDENTIAL_ENTRY_TYPE, DIR_CACHE_TTL_MS, dirCache, VALUE_LIKE_FIELDS; var init_credentials = __esm({ "cypher-executor/src/routes/credentials.ts"() { "use strict"; @@ -3652,7 +3651,6 @@ var init_credentials = __esm({ CREDENTIAL_ENTRY_TYPE = "credential"; DIR_CACHE_TTL_MS = 6e4; dirCache = {}; - LAST_USED_MIN_INTERVAL_S = 300; VALUE_LIKE_FIELDS = ["value", "secret", "token", "text", "plaintext"]; credentialsRouter.post("/credentials/directory", async (c) => { const apiKey = c.req.header("X-Arcrun-API-Key"); @@ -3792,13 +3790,13 @@ var init_credentials = __esm({ }); // cypher-executor/src/actions/auth-dispatcher.ts -async function resolveSecretsFromNewHomeDetailed(env, apiKey, names) { +async function resolveSecretsFromNewHome(env, apiKey, names) { const resolved = {}; - if (names.length === 0) return { resolved, directoryError: null }; - const { refs, directoryError } = await getCredentialSecretRefsDetailed(env, apiKey); - if (Object.keys(refs).length === 0) return { resolved, directoryError }; + if (names.length === 0) return resolved; + const refs = await getCredentialSecretRefs(env, apiKey); + if (Object.keys(refs).length === 0) return resolved; const secretGet2 = createArcrunHostFunctions(env, apiKey).secret_get; - if (!secretGet2) return { resolved, directoryError }; + if (!secretGet2) return resolved; const resolvedNames = []; for (const name of names) { const ref = refs[name]; @@ -3809,11 +3807,7 @@ async function resolveSecretsFromNewHomeDetailed(env, apiKey, names) { resolvedNames.push(name); } if (resolvedNames.length > 0) touchLastUsed(env, apiKey, resolvedNames); - return { resolved, directoryError }; -} -function explainCredentialFailure(message, directoryError, names) { - if (!directoryError) return message; - return `credential \u76EE\u9304\u8B80\u4E0D\u5230\uFF08${directoryError}\uFF09\uFF0C${names.join("\u3001")} \u7121\u6CD5\u5F9E\u4FDD\u7BA1\u8655\u53D6\u7528\u2014\u2014\u9019\u4E0D\u4EE3\u8868 credential \u4E0D\u5B58\u5728\uFF0C\u662F\u77E5\u8B58\u5EAB\uFF08KBDB\uFF09\u9019\u4E00\u523B\u56DE\u932F\uFF0C\u8ACB\u5148\u78BA\u8A8D KBDB \u662F\u5426\u6B63\u5E38\u3002\u9000\u56DE\u820A\u8DEF\u5F91\u7684\u7D50\u679C\uFF1A${message}`; + return resolved; } async function tryAuthDispatch(componentId, input, env, apiKey, redactor) { if (AUTH_PRIMITIVE_IDS.has(componentId)) { @@ -3828,7 +3822,7 @@ async function tryAuthDispatch(componentId, input, env, apiKey, redactor) { if (!recipe) return null; if (!SUPPORTED_PRIMITIVES.has(recipe.primitive)) return null; const secretNames = recipe.required_secrets.filter((s) => !s.optional).map((s) => s.key); - const { resolved: resolvedSecrets, directoryError } = await resolveSecretsFromNewHomeDetailed(env, apiKey, secretNames); + const resolvedSecrets = await resolveSecretsFromNewHome(env, apiKey, secretNames); redactor?.addRecord(resolvedSecrets, (name) => `credential:${name}`); const primitiveUrl = wasmWorkerUrl(`auth_${recipe.primitive}`, env.WORKER_SUBDOMAIN); const res = await fetch(primitiveUrl, { @@ -3845,17 +3839,13 @@ async function tryAuthDispatch(componentId, input, env, apiKey, redactor) { if (!res.ok) { const text = await res.text().catch(() => ""); throw new Error( - explainCredentialFailure( - `auth primitive "${recipe.primitive}" \u56DE\u50B3 ${res.status}: ${text.slice(0, 200)}`, - directoryError, - secretNames - ) + `auth primitive "${recipe.primitive}" \u56DE\u50B3 ${res.status}: ${text.slice(0, 200)}` ); } const result = await res.json().catch(() => null); if (!result || result.success === false) { throw new Error( - explainCredentialFailure(`auth primitive \u5931\u6557: ${result?.error ?? "\u672A\u77E5\u932F\u8AA4"}`, directoryError, secretNames) + `auth primitive \u5931\u6557: ${result?.error ?? "\u672A\u77E5\u932F\u8AA4"}` ); } redactor?.addRecord(result.auth_headers, (k) => `auth_header:${k}`); @@ -3901,7 +3891,7 @@ async function resolveCredentialRefs(data, env, apiKey, redactor) { collectCredentialNames(data, names); if (names.size === 0) return data; const nameList = [...names]; - const { resolved: resolvedSecrets, directoryError } = await resolveSecretsFromNewHomeDetailed(env, apiKey, nameList); + const resolvedSecrets = await resolveSecretsFromNewHome(env, apiKey, nameList); redactor?.addRecord(resolvedSecrets, (name) => `credential:${name}`); if (nameList.every((n) => Object.prototype.hasOwnProperty.call(resolvedSecrets, n))) { return replaceCredentialRefs(data, resolvedSecrets); @@ -3919,15 +3909,11 @@ async function resolveCredentialRefs(data, env, apiKey, redactor) { }); if (!res.ok) { const text = await res.text().catch(() => ""); - throw new Error( - explainCredentialFailure(`credential resolve \u56DE\u50B3 ${res.status}: ${text.slice(0, 200)}`, directoryError, nameList) - ); + throw new Error(`credential resolve \u56DE\u50B3 ${res.status}: ${text.slice(0, 200)}`); } const result = await res.json().catch(() => null); if (!result || result.success === false) { - throw new Error( - explainCredentialFailure(`credential resolve \u5931\u6557: ${result?.error ?? "\u672A\u77E5\u932F\u8AA4"}`, directoryError, nameList) - ); + throw new Error(`credential resolve \u5931\u6557: ${result?.error ?? "\u672A\u77E5\u932F\u8AA4"}`); } redactor?.addRecord(result.credentials, (name) => `credential:${name}`); return replaceCredentialRefs(data, result.credentials ?? {}); @@ -8261,17 +8247,6 @@ var init_magic_vars = __esm({ }); // cypher-executor/src/lib/telemetry.ts -function recordNodeSteps(env, apiKey, workflowName, steps, ctx) { - if (steps.length === 0) return; - const failed = steps.filter((s) => !s.ok).length; - recordTelemetry(env, apiKey, { - event_type: "node_steps", - workflow_name: workflowName, - duration_ms: steps.reduce((sum, s) => sum + s.duration_ms, 0), - ...failed > 0 ? { error_code: "node_error" } : {}, - steps - }, ctx); -} async function hashApiKey(apiKey) { if (!apiKey) return "anon"; const encoder = new TextEncoder(); @@ -8562,9 +8537,6 @@ var init_graph_executor = __esm({ // GET /executions/:task_id、POST /workflows/resume)+ 一個持久化端(paused KV)。 // 遮在產地=六個出口一次補齊;遮在出口=下一個新出口又會漏。 redactor = new TraceRedactor(); - // inkstone/arcrun-rag#196:本次執行的 step-level 遙測先收在這裡,執行結束寫成一筆。 - // 原本每個 Component 節點各打一次 fetch,佔掉免費層每次呼叫 50 子請求的額度。 - nodeSteps = []; constructor(loader, workflowLoader, env, apiKey) { this.loader = loader; this.workflowLoader = workflowLoader; @@ -8593,17 +8565,11 @@ var init_graph_executor = __esm({ fanIn.set(node.id, { ctx: { ...ctxWithMagic }, remaining: inDeg }); } } - this.nodeSteps = []; - let results; - try { - results = await Promise.all( - startNodes.map( - (node) => this.executeNode(node, graph, ctxWithMagic, /* @__PURE__ */ new Set(), trace, fanIn, kvStore) - ) - ); - } finally { - this.flushNodeSteps(graph); - } + const results = await Promise.all( + startNodes.map( + (node) => this.executeNode(node, graph, ctxWithMagic, /* @__PURE__ */ new Set(), trace, fanIn, kvStore) + ) + ); let mergedResult; if (results.length === 1) { mergedResult = results[0]; @@ -8665,17 +8631,11 @@ var init_graph_executor = __esm({ } const visited = /* @__PURE__ */ new Set([`${paused_node_id}:${JSON.stringify(paused_context).slice(0, 50)}`]); const downstreamNodes = downstreamEdges.map((e) => graph.nodes.find((n) => n.id === e.to)).filter((n) => !!n); - this.nodeSteps = []; - let results; - try { - results = await Promise.all( - downstreamNodes.map( - (node) => this.executeNode(node, graph, mergedContext, visited, trace, fanIn, kvStore) - ) - ); - } finally { - this.flushNodeSteps(graph); - } + const results = await Promise.all( + downstreamNodes.map( + (node) => this.executeNode(node, graph, mergedContext, visited, trace, fanIn, kvStore) + ) + ); let mergedResult; if (results.length === 1) { mergedResult = results[0]; @@ -8690,12 +8650,6 @@ var init_graph_executor = __esm({ } return { data: this.redactor.redact(mergedResult), trace }; } - /** 把本次收集的 step-level 遙測寫成一筆(成功、失敗、暫停都寫)。 */ - flushNodeSteps(graph) { - const steps = this.nodeSteps; - this.nodeSteps = []; - if (this.env && steps.length > 0) recordNodeSteps(this.env, this.apiKey, graph.name, steps); - } async executeNode(node, graph, context, visited, trace, fanIn, kvStore) { const nodeKey = `${node.id}:${JSON.stringify(context).slice(0, 50)}`; if (visited.has(nodeKey)) return context; @@ -8809,8 +8763,14 @@ var init_graph_executor = __esm({ error: errMsg, duration_ms: duration_ms2 }); - if (node.type === "Component") { - this.nodeSteps.push({ component_id: node.componentId, duration_ms: duration_ms2, ok: false, error_code: "node_error" }); + if (this.env && node.type === "Component") { + recordTelemetry(this.env, this.apiKey, { + event_type: "node_failure", + workflow_name: graph.name, + component_id: node.componentId, + error_code: "node_error", + duration_ms: duration_ms2 + }); } if (e instanceof ExecutionError) throw e; throw new ExecutionError( @@ -8829,8 +8789,13 @@ var init_graph_executor = __esm({ output: this.redactor.redact(result), duration_ms }); - if (node.type === "Component") { - this.nodeSteps.push({ component_id: node.componentId, duration_ms, ok: true }); + if (this.env && node.type === "Component") { + recordTelemetry(this.env, this.apiKey, { + event_type: "node_success", + workflow_name: graph.name, + component_id: node.componentId, + duration_ms + }); } const outEdges = graph.edges.filter((e) => e.from === node.id); for (const edge of outEdges) { @@ -9037,17 +9002,6 @@ function componentVerdictsFromTrace(nodes, trace) { } return verdicts; } -function aggregateVerdicts(verdicts) { - const byId = /* @__PURE__ */ new Map(); - for (const v of verdicts) { - const a = byId.get(v.component_id) ?? { component_id: v.component_id, runs: 0, success_runs: 0, duration_ms: 0 }; - a.runs += 1; - a.success_runs += v.success ? 1 : 0; - a.duration_ms += v.duration_ms; - byId.set(v.component_id, a); - } - return [...byId.values()]; -} async function recordComponentStats(env, nodes, trace) { try { const base = (env.REGISTRY_BASE_URL ?? (env.WORKER_SUBDOMAIN ? wasmWorkerUrl("registry", env.WORKER_SUBDOMAIN) : void 0))?.replace(/\/$/, ""); @@ -9055,17 +9009,14 @@ async function recordComponentStats(env, nodes, trace) { const verdicts = componentVerdictsFromTrace(nodes, trace); if (verdicts.length === 0) return; await Promise.all( - aggregateVerdicts(verdicts).map( - (a) => fetch(`${base}/analytics/record`, { + verdicts.map( + (v) => fetch(`${base}/analytics/record`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ - canonical_id: a.component_id, - // 舊版 registry 不認 runs 時只會記 1 筆:給它「全部成功才算成功」的保守值 - success: a.success_runs === a.runs, - duration_ms: a.duration_ms, - runs: a.runs, - success_runs: a.success_runs + canonical_id: v.component_id, + success: v.success, + duration_ms: v.duration_ms }) }).catch(() => void 0) // 統計失敗不影響執行 diff --git a/arcrun-kbdb/worker.mjs b/arcrun-kbdb/worker.mjs index 83ffe22..de01a55 100644 --- a/arcrun-kbdb/worker.mjs +++ b/arcrun-kbdb/worker.mjs @@ -46,7 +46,7 @@ var init_relation_orphans = __esm({ } }); -// kbdb/node_modules/hono/dist/compose.js +// kbdb/node_modules/.pnpm/hono@4.12.23/node_modules/hono/dist/compose.js var compose = (middleware, onError, onNotFound) => { return (context, next) => { let index = -1; @@ -90,10 +90,10 @@ var compose = (middleware, onError, onNotFound) => { }; }; -// kbdb/node_modules/hono/dist/request/constants.js +// kbdb/node_modules/.pnpm/hono@4.12.23/node_modules/hono/dist/request/constants.js var GET_MATCH_RESULT = /* @__PURE__ */ Symbol(); -// kbdb/node_modules/hono/dist/utils/body.js +// kbdb/node_modules/.pnpm/hono@4.12.23/node_modules/hono/dist/utils/body.js var parseBody = async (request, options = /* @__PURE__ */ Object.create(null)) => { const { all = false, dot = false } = options; const headers = request instanceof HonoRequest ? request.raw.headers : request.headers; @@ -165,7 +165,7 @@ var handleParsingNestedValues = (form, key, value) => { }); }; -// kbdb/node_modules/hono/dist/utils/url.js +// kbdb/node_modules/.pnpm/hono@4.12.23/node_modules/hono/dist/utils/url.js var splitPath = (path) => { const paths = path.split("/"); if (paths[0] === "") { @@ -369,7 +369,7 @@ var getQueryParams = (url, key) => { }; var decodeURIComponent_ = decodeURIComponent; -// kbdb/node_modules/hono/dist/request.js +// kbdb/node_modules/.pnpm/hono@4.12.23/node_modules/hono/dist/request.js var tryDecodeURIComponent = (str) => tryDecode(str, decodeURIComponent_); var HonoRequest = class { /** @@ -652,7 +652,7 @@ var HonoRequest = class { } }; -// kbdb/node_modules/hono/dist/utils/html.js +// kbdb/node_modules/.pnpm/hono@4.12.23/node_modules/hono/dist/utils/html.js var HtmlEscapedCallbackPhase = { Stringify: 1, BeforeStream: 2, @@ -694,7 +694,7 @@ var resolveCallback = async (str, phase, preserveCallbacks, context, buffer) => } }; -// kbdb/node_modules/hono/dist/context.js +// kbdb/node_modules/.pnpm/hono@4.12.23/node_modules/hono/dist/context.js var TEXT_PLAIN = "text/plain; charset=UTF-8"; var setDefaultContentType = (contentType, headers) => { return { @@ -1101,7 +1101,7 @@ var Context = class { }; }; -// kbdb/node_modules/hono/dist/router.js +// kbdb/node_modules/.pnpm/hono@4.12.23/node_modules/hono/dist/router.js var METHOD_NAME_ALL = "ALL"; var METHOD_NAME_ALL_LOWERCASE = "all"; var METHODS = ["get", "post", "put", "delete", "options", "patch"]; @@ -1109,10 +1109,10 @@ var MESSAGE_MATCHER_IS_ALREADY_BUILT = "Can not add a route since the matcher is var UnsupportedPathError = class extends Error { }; -// kbdb/node_modules/hono/dist/utils/constants.js +// kbdb/node_modules/.pnpm/hono@4.12.23/node_modules/hono/dist/utils/constants.js var COMPOSED_HANDLER = "__COMPOSED_HANDLER"; -// kbdb/node_modules/hono/dist/hono-base.js +// kbdb/node_modules/.pnpm/hono@4.12.23/node_modules/hono/dist/hono-base.js var notFoundHandler = (c) => { return c.text("404 Not Found", 404); }; @@ -1488,7 +1488,7 @@ var Hono = class _Hono { }; }; -// kbdb/node_modules/hono/dist/router/reg-exp-router/matcher.js +// kbdb/node_modules/.pnpm/hono@4.12.23/node_modules/hono/dist/router/reg-exp-router/matcher.js var emptyParam = []; function match(method, path) { const matchers = this.buildAllMatchers(); @@ -1509,7 +1509,7 @@ function match(method, path) { return match2(method, path); } -// kbdb/node_modules/hono/dist/router/reg-exp-router/node.js +// kbdb/node_modules/.pnpm/hono@4.12.23/node_modules/hono/dist/router/reg-exp-router/node.js var LABEL_REG_EXP_STR = "[^/]+"; var ONLY_WILDCARD_REG_EXP_STR = ".*"; var TAIL_WILDCARD_REG_EXP_STR = "(?:|/.*)"; @@ -1617,7 +1617,7 @@ var Node = class _Node { } }; -// kbdb/node_modules/hono/dist/router/reg-exp-router/trie.js +// kbdb/node_modules/.pnpm/hono@4.12.23/node_modules/hono/dist/router/reg-exp-router/trie.js var Trie = class { #context = { varIndex: 0 }; #root = new Node(); @@ -1673,7 +1673,7 @@ var Trie = class { } }; -// kbdb/node_modules/hono/dist/router/reg-exp-router/router.js +// kbdb/node_modules/.pnpm/hono@4.12.23/node_modules/hono/dist/router/reg-exp-router/router.js var nullMatcher = [/^$/, [], /* @__PURE__ */ Object.create(null)]; var wildcardRegExpCache = /* @__PURE__ */ Object.create(null); function buildWildcardRegExp(path) { @@ -1852,7 +1852,7 @@ var RegExpRouter = class { } }; -// kbdb/node_modules/hono/dist/router/smart-router/router.js +// kbdb/node_modules/.pnpm/hono@4.12.23/node_modules/hono/dist/router/smart-router/router.js var SmartRouter = class { name = "SmartRouter"; #routers = []; @@ -1907,7 +1907,7 @@ var SmartRouter = class { } }; -// kbdb/node_modules/hono/dist/router/trie-router/node.js +// kbdb/node_modules/.pnpm/hono@4.12.23/node_modules/hono/dist/router/trie-router/node.js var emptyParams = /* @__PURE__ */ Object.create(null); var hasChildren = (children) => { for (const _ in children) { @@ -2082,7 +2082,7 @@ var Node2 = class _Node2 { } }; -// kbdb/node_modules/hono/dist/router/trie-router/router.js +// kbdb/node_modules/.pnpm/hono@4.12.23/node_modules/hono/dist/router/trie-router/router.js var TrieRouter = class { name = "TrieRouter"; #node; @@ -2104,7 +2104,7 @@ var TrieRouter = class { } }; -// kbdb/node_modules/hono/dist/hono.js +// kbdb/node_modules/.pnpm/hono@4.12.23/node_modules/hono/dist/hono.js var Hono2 = class extends Hono { /** * Creates an instance of the Hono class. @@ -3772,22 +3772,19 @@ async function getRecord(db, recordId) { const identity = await db.prepare("SELECT owner_id FROM entries WHERE id = ?").bind(recordId).first(); return { record_id: recordId, template_id: belongs.dst_id, values, owner_id: identity?.owner_id ?? null }; } -async function countSheetMembers(db, sheetId, owner_id, offset, limit, got) { - if (offset === 0 && got < limit) return got; - const row = owner_id ? await db.prepare( - // kbdb-sql-ok:牆內本體(kbdb/src/actions/) - `SELECT COUNT(*) AS total FROM entries WHERE rel_id = '${SYS_BELONGS}' AND dst_id = ? AND +owner_id = ?` - ).bind(sheetId, owner_id).first() : await db.prepare( - // kbdb-sql-ok:同上 - `SELECT COUNT(*) AS total FROM entries WHERE rel_id = '${SYS_BELONGS}' AND dst_id = ?` - ).bind(sheetId).first(); - return row?.total ?? 0; -} async function searchByTemplatePage(db, template, owner_id, limit = 100, offset = 0) { const tpl = await getTemplate(db, template); if (!tpl) return { records: [], total: 0 }; const cap = Math.min(Math.max(limit, 1), 500); const skip = Math.max(offset, 0); + const totalRow = owner_id ? await db.prepare( + // kbdb-sql-ok:牆內本體(kbdb/src/actions/) + `SELECT COUNT(*) AS total FROM entries WHERE rel_id = '${SYS_BELONGS}' AND dst_id = ? AND owner_id = ?` + ).bind(tpl.id, owner_id).first() : await db.prepare( + // kbdb-sql-ok:同上 + `SELECT COUNT(*) AS total FROM entries WHERE rel_id = '${SYS_BELONGS}' AND dst_id = ?` + ).bind(tpl.id).first(); + const total = totalRow?.total ?? 0; const res = owner_id ? await db.prepare( // kbdb-sql-ok:牆內本體(kbdb/src/actions/);本次 checkout 開在 worktree /private/tmp/wt-graph-first-44/,hook 逐字比對 matrix/arcrun/kbdb/src/ 吃不到,與 962d863/5919c6b 記載的是同一個假警報 `SELECT src_id AS record_id FROM entries @@ -3800,7 +3797,6 @@ async function searchByTemplatePage(db, template, owner_id, limit = 100, offset ORDER BY created_at DESC, rowid DESC LIMIT ? OFFSET ?` ).bind(tpl.id, cap, skip).all(); const ids = (res.results ?? []).map((r) => r.record_id); - const total = await countSheetMembers(db, tpl.id, owner_id, skip, cap, ids.length); if (ids.length === 0) return { records: [], total }; const byId = /* @__PURE__ */ new Map(); for (const id of ids) byId.set(id, { record_id: id, template_id: tpl.id, values: {}, owner_id: null }); @@ -3840,11 +3836,11 @@ async function deleteRecord(db, recordId) { ).bind(recordId).run(); for (const dst of dsts) { await db.prepare( - // kbdb-sql-ok:牆內本體(kbdb/src/actions/);worktree 開在 matrix/arcrun-wt-218/,hook 逐字比對 matrix/arcrun/kbdb/src/ 吃不到——與本檔既有註解記載的同一個假警報 `DELETE FROM entries WHERE id = ?1 AND entry_type NOT IN ('sheet', 'field', 'system') AND NOT EXISTS (SELECT 1 FROM entries WHERE dst_id = ?1) - AND NOT EXISTS (SELECT 1 FROM entries WHERE src_id = ?1)` + AND NOT EXISTS (SELECT 1 FROM entries WHERE src_id = ?1) + AND NOT EXISTS (SELECT 1 FROM entries WHERE rel_id = ?1)` ).bind(dst).run(); } return true; @@ -3947,7 +3943,7 @@ function tripletPivotSql(ownerFiltered) { 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.dst_id = ?${ownerFiltered ? " AND +b.owner_id = ?" : ""} + WHERE b.rel_id = 'sys_belongs' AND b.dst_id = ?${ownerFiltered ? " AND b.owner_id = ?" : ""} GROUP BY b.src_id`; } function mapPivotSql(ownerFiltered) { @@ -3964,7 +3960,7 @@ function mapPivotSql(ownerFiltered) { 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.dst_id = ?${ownerFiltered ? " AND +b.owner_id = ?" : ""} + WHERE b.rel_id = 'sys_belongs' AND b.dst_id = ?${ownerFiltered ? " AND b.owner_id = ?" : ""} GROUP BY b.src_id`; } function parseJsonArray(raw2) { @@ -4124,12 +4120,9 @@ async function liveTripletCountsByLibrary(db, tripletTemplateId, owner_id) { MAX(CASE WHEN r.rel_id = 'fld_' || b.dst_id || '_status' THEN v.content END) AS status, MAX(CASE WHEN r.rel_id = 'fld_' || b.dst_id || '_library' THEN v.content END) AS library FROM entries b - -- Arcrun#218\uFF1A\u9019\u652F\u53EA\u8B80 status\uFF0Flibrary \u5169\u683C \u21D2 join \u53EA\u63A5\u9019\u5169\u500B\u8B02\u8A5E\uFF0C\u5176\u9918\u683C\u5B50\u4E0D\u53BB v \u6488\u503C - -- \uFF08\u672C\u6A5F 35.7 \u842C\u5217 leo21c \u5F62\u72C0\u91CF\u6E2C\uFF1A500,400 \u2192 284,400 \u5217\uFF1B\u7B54\u6848\u4E0D\u8B8A\uFF0Ctests/ \u9010\u5EAB\u6BD4\u5C0D\uFF09 - LEFT JOIN entries r ON r.src_id = b.src_id - AND r.rel_id IN ('fld_' || b.dst_id || '_status', 'fld_' || b.dst_id || '_library') + 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.dst_id = ?${owner_id ? " AND +b.owner_id = ?" : ""} + WHERE b.rel_id = 'sys_belongs' AND b.dst_id = ?${owner_id ? " AND b.owner_id = ?" : ""} GROUP BY b.src_id ) AS tr WHERE COALESCE(tr.status, 'active') = 'active' @@ -4186,7 +4179,7 @@ async function knownLibraryNames(db, owner_id) { 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.dst_id = ?${owner_id ? " AND +b.owner_id = ?" : ""} + WHERE b.rel_id = 'sys_belongs' AND b.dst_id = ?${owner_id ? " AND b.owner_id = ?" : ""} GROUP BY b.src_id` ).bind(...libParams).all(); for (const r of libRows.results ?? []) if (r.name) names.add(r.name); diff --git a/daemon/Arcrun-0.18.53.dmg b/daemon/Arcrun-0.18.53.dmg new file mode 100644 index 0000000..ad486dc Binary files /dev/null and b/daemon/Arcrun-0.18.53.dmg differ diff --git a/daemon/Arcrun-0.18.53.msix b/daemon/Arcrun-0.18.53.msix new file mode 100644 index 0000000..444a94e Binary files /dev/null and b/daemon/Arcrun-0.18.53.msix differ diff --git a/daemon/Arcrun-win-0.18.53.exe b/daemon/Arcrun-win-0.18.53.exe new file mode 100755 index 0000000..6bde4bd Binary files /dev/null and b/daemon/Arcrun-win-0.18.53.exe differ diff --git a/manifest.json b/manifest.json index 95b547d..4b1960e 100644 --- a/manifest.json +++ b/manifest.json @@ -1,7 +1,7 @@ { "schema": 2, "built": "2026-09-13", - "source": "Arcrun@f8590c7252cc", + "source": "Arcrun@63e3c6da1b38", "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": 721657, + "js_bytes": 718803, "modules": [], "compat_date": "2025-02-19", "compat_flags": [ @@ -239,10 +239,10 @@ "stripped": { "services": 13 }, - "source_commit": "1eb26c98a3af2067303e7544ae9bca1a77867c32", - "source_content_sha256": "2e61c3ebc75761463e79e1740da65971a9111aa1675442d8742a372816551591", - "sha256": "2e61c3ebc75761463e79e1740da65971a9111aa1675442d8742a372816551591", - "bytes": 721657 + "source_commit": "5f96438d1211f4166f9cda9341b62d69af76c38e", + "source_content_sha256": "e9176c297d93a9d621662e6f037999967751c0542a471b7302f93f06cceee7f1", + "sha256": "e9176c297d93a9d621662e6f037999967751c0542a471b7302f93f06cceee7f1", + "bytes": 718803 }, { "name": "arcrun-date-ops", @@ -396,7 +396,7 @@ "name": "arcrun-kbdb", "main_module": "worker.mjs", "main_file": "arcrun-kbdb/worker.mjs", - "js_bytes": 204046, + "js_bytes": 203823, "modules": [], "compat_date": "2025-02-19", "compat_flags": [ @@ -416,10 +416,10 @@ "ENVIRONMENT": "production" } }, - "source_commit": "f8590c7252cc51761ca17980cf57206612d51b65", - "source_content_sha256": "a9be3bdd9678f54cae355b94427525ddeb369fb53ed281a5700df0db6765096f", - "sha256": "a9be3bdd9678f54cae355b94427525ddeb369fb53ed281a5700df0db6765096f", - "bytes": 204046 + "source_commit": "bed24fbbd5c3befed02c5690133d640f1d036e56", + "source_content_sha256": "8df57bc74d8f9c828db40207fd0079ae060374be27c0dc9cab835959aee091b0", + "sha256": "8df57bc74d8f9c828db40207fd0079ae060374be27c0dc9cab835959aee091b0", + "bytes": 203823 }, { "name": "arcrun-mcp", @@ -698,12 +698,12 @@ "bytes": 67697 } ], - "release": "1.4.65", + "release": "1.4.66", "built_for": "oauth-installer-lazy-load", "notes": [ "rag_takedown_direct 用了 __CARDS_PREFIX__,但安裝器的代換表沒有它 ⇒ 這個佔位符會原封不動被推進使用者的工作流。" ], - "fingerprint": "9cd6779e1c1576b0c70fb90859066334c12c6ce67c9b29584fb892c2afd62500", + "fingerprint": "65d88570080fc3018abdd5cb8f56b39e1af420ac8186c744924c1fbab9a4dbf0", "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": 721657, + "js_bytes": 718803, "modules": [], "compat_date": "2025-02-19", "compat_flags": [ @@ -929,8 +929,8 @@ "stripped": { "services": 13 }, - "source_commit": "1eb26c98a3af2067303e7544ae9bca1a77867c32", - "source_content_sha256": "2e61c3ebc75761463e79e1740da65971a9111aa1675442d8742a372816551591", + "source_commit": "5f96438d1211f4166f9cda9341b62d69af76c38e", + "source_content_sha256": "e9176c297d93a9d621662e6f037999967751c0542a471b7302f93f06cceee7f1", "first_install": true }, { @@ -1075,7 +1075,7 @@ "name": "arcrun-kbdb", "main_module": "worker.mjs", "main_file": "arcrun-kbdb/worker.mjs", - "js_bytes": 204046, + "js_bytes": 203823, "modules": [], "compat_date": "2025-02-19", "compat_flags": [ @@ -1095,8 +1095,8 @@ "ENVIRONMENT": "production" } }, - "source_commit": "f8590c7252cc51761ca17980cf57206612d51b65", - "source_content_sha256": "a9be3bdd9678f54cae355b94427525ddeb369fb53ed281a5700df0db6765096f", + "source_commit": "bed24fbbd5c3befed02c5690133d640f1d036e56", + "source_content_sha256": "8df57bc74d8f9c828db40207fd0079ae060374be27c0dc9cab835959aee091b0", "first_install": true }, { @@ -1489,21 +1489,21 @@ } ], "daemon": { - "version": "0.18.52", + "version": "0.18.53", "mac": { - "file": "daemon/Arcrun-0.18.52.dmg", - "sha256": "a3bc29ef6dc00da982a2d7990fc1731d88fc1436df38d75499aafae0245a09ce" + "file": "daemon/Arcrun-0.18.53.dmg", + "sha256": "f7f696d70350d5af74932505767348278f21c38a4048bcc0dc0e3823fa767c9e" }, "win": { - "file": "daemon/Arcrun-win-0.18.52.exe", - "sha256": "9e074685eb44e6a4729c819411221cc24018f7cdfcb732df15911741a5356f80" + "file": "daemon/Arcrun-win-0.18.53.exe", + "sha256": "6eb4d280e5c8632b33a43cb8f71bfc5ca495fabe0a6dbfec8dd32e86122b6fb7" }, "msix": { - "file": "daemon/Arcrun-0.18.52.msix", - "sha256": "6e1f022ce812fb7c92ca7a4fbc32c428ab483a666971d96bfb5acdef4b0fdca3" + "file": "daemon/Arcrun-0.18.53.msix", + "sha256": "149550f2ba2f8755b11ed883740591041633ddf7fb918e3892b699ff2ffc4625" }, - "built": "20260913-2009", - "notes": "雲端資料庫的免費額度用完時,小幫手會直接告訴你・額度用完期間不再重打雲端・急著用的話,卡片上也會告訴你:升級 Cloudflare Workers 付費方案(每月 5 美元起)就沒有每日上限" + "built": "20260914-0655", + "notes": "資料夾上的數字會跟著真的送上雲端的份數走・「同步中」底下會說正在做什麼、卡在哪(細節見說明文件)" }, "installer": { "version": "1.0.10"