feat: KBDB self-hosted 查詢 + embed 模組 + thin-shell 收窄 + search_workflow(code done 待端到端)
按 issue 分段標明(檔 #5/#8 改動交疊處無法乾淨拆檔,故併一個 commit): #4 thin-shell §3.1 自力救濟階梯 + code-node 規則(純文檔/規則,code-node 零件未實作) #5 KBDB source filter(json_extract metadata_json 零建表)+ 能力對照;documents 聚合與 DELETE proxy 部分擱置等頂層 T8 #7 base embed 模組(kbdb/src/embed.ts)+ vectorize 開關(deploy/config/wrangler.toml 註解範本) + 語義查詢降級閉環(mode=semantic 未開→LIKE+capability_hint) #8 部分(workflow-discovery): - KBDB /entries/search 加 base 通用 entry_type filter(entry-crud/embed/route/kbdb-proxy 透傳) - /webhooks/named 強制 description(空→400,訊息要求操盤 AI 據實寫一句) - 部署雙寫 entry_type=workflow embeddable entry(waitUntil 非阻塞,供 search) - cypher GET /workflows/search + MCP u6u_search_workflows(優先語意、降級 hint) - cypher POST /workflows/backfill-search-entries(無 desc 列出不編造) - GET /webhooks/named 補回 description/created_at 欄位(為 list 來源收斂備) ⚠️ tsc 綠 = code done,非完成(mindset §7 禁假綠): - #7/#8 端到端待 leo21c 部署驗(Vectorize 需官方憑證、CC 跑不了) - #8 ①-a(MCP deploy 改打 /webhooks/named)未做、MCP deploy 那半仍 404 - #8 端到端(強制填擋空/語義命中/租戶隔離/降級 hint)未驗 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -28,6 +28,12 @@ export interface ArcrunConfig {
|
||||
// SDD: sdk-and-website/mcp-account-source.md
|
||||
mcp_url?: string;
|
||||
multi_tenant?: boolean;
|
||||
// 語義查詢開關(issue #7 / SDD T2.4,self-hosted 從零做)。
|
||||
// true → deploy 時建 CF Vectorize index 並注入 kbdb worker 的 [[vectorize]]+[ai] binding;
|
||||
// kbdb embed 模組啟用(寫入時對標記 embed 的 entry embed、search 支援 mode=semantic)。
|
||||
// 未設/false → base 維持 LIKE keyword(free-tier 友善,不建 index、不花費)。
|
||||
// 開法:設 kbdb_embed:true → redeploy(acr update)。「CC 幫開」=CC 寫此欄 true + 跑 acr update。
|
||||
kbdb_embed?: boolean;
|
||||
// 資料外流警示:本機記住「已同意暴露 / 選擇不再警示」的資源,避免每次 push 重問(§3 首次問記住)。
|
||||
// key 格式:`{kind}:{resourceName}`(如 "webhook:contacts_lookup" / "recipe:kbdb_get")。
|
||||
// 注意:這只是 CLI 端 UX(不重問);server 端獨立存法律憑證並強制(防 CLI 被繞過)。
|
||||
@@ -160,6 +166,11 @@ function readEnvOverrides(): Partial<ArcrunConfig> {
|
||||
(out as Record<string, unknown>)[field] = v;
|
||||
}
|
||||
}
|
||||
// bool 開關(issue #7):env 可選覆蓋,'true'/'1' → true。
|
||||
const embedEnv = process.env.ARCRUN_KBDB_EMBED;
|
||||
if (embedEnv !== undefined && embedEnv !== '') {
|
||||
out.kbdb_embed = embedEnv === 'true' || embedEnv === '1';
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
|
||||
+62
-1
@@ -102,8 +102,15 @@ export interface DeployContext {
|
||||
// 讓 MCP partner-auth 走 namespace 明碼分支(mcp-account-source §5.5)。
|
||||
// 未設 / false → 不注入(官方 SaaS 多租戶,行為不變)。
|
||||
selfHosted?: boolean;
|
||||
// 語義查詢開關(issue #7 / SDD T2.4)。true → 部署前建 CF Vectorize index 並注入 kbdb worker 的
|
||||
// [[vectorize]]+[ai] binding(取消 wrangler.toml 註解段)→ embed 模組啟用。未設/false → 不建、不注入,
|
||||
// base 維持 LIKE keyword(free-tier 友善)。
|
||||
kbdbEmbed?: boolean;
|
||||
}
|
||||
|
||||
/** Vectorize index 名(kbdb embed 模組用)。bge-base-en-v1.5 = 768 維、cosine。 */
|
||||
export const KBDB_VECTORIZE_INDEX = 'arcrun-kbdb-embed';
|
||||
|
||||
export interface DeployResult {
|
||||
implemented: boolean;
|
||||
cypherExecutorUrl?: string;
|
||||
@@ -186,11 +193,26 @@ export async function downloadAndDeploy(
|
||||
console.log(chalk.yellow(` ⚠ 共享安裝失敗,退回各 worker 自裝${tail ? `:${tail}` : ''}`));
|
||||
}
|
||||
|
||||
const failures: string[] = [];
|
||||
|
||||
// 2.6 語義查詢(issue #7 / T2.4):開 kbdb_embed → 先確保 Vectorize index 存在(REST,冪等),
|
||||
// 再由 injectWranglerConfig 取消 kbdb toml 的 [[vectorize]]+[ai] 註解 → embed 模組上線。
|
||||
// 失敗不致命(收進 failures,base 仍可部署、維持 keyword)。
|
||||
if (ctx.kbdbEmbed) {
|
||||
try {
|
||||
process.stdout.write(chalk.gray(' → 開語義查詢:確保 Vectorize index 存在...'));
|
||||
await ensureVectorizeIndex(ctx);
|
||||
console.log(chalk.green(' ✓'));
|
||||
} catch (e) {
|
||||
console.log(chalk.yellow(' ⚠'));
|
||||
failures.push(`Vectorize index (${KBDB_VECTORIZE_INDEX}): ${e instanceof Error ? e.message : String(e)}`);
|
||||
}
|
||||
}
|
||||
|
||||
// 3. 對每個 worker:注入 KV id(+ cypher WORKER_SUBDOMAIN)→ wrangler deploy。tier1 先 tier2 後。
|
||||
// 逐 worker 串流進度(每個含 pnpm install + wrangler deploy,沉默會讓人以為卡住——
|
||||
// 壓測 2026-06-11 richblack 觀察:「D1 ✓」後停很久其實在這個迴圈靜默部署 20+ worker)。
|
||||
const allDirs = [...tier1, ...tier2];
|
||||
const failures: string[] = [];
|
||||
let deployed = 0;
|
||||
let skipped = 0;
|
||||
// 內容指紋 manifest:未變動且上次成功的 worker 跳過(key 用 worker 名,不用 temp 絕對路徑)。
|
||||
@@ -296,6 +318,33 @@ async function applyD1Migration(ctx: DeployContext, sql: string): Promise<void>
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 確保 KBDB embed 用的 Vectorize index 存在(issue #7 / T2.4)。
|
||||
* REST `POST /accounts/{id}/vectorize/v2/indexes`(dimensions=768/metric=cosine,對齊 bge-base-en-v1.5)。
|
||||
* 冪等:已存在(CF 回「already exists」類錯)視為成功,不報錯。用 init 已驗的 apiToken+accountId。
|
||||
*/
|
||||
async function ensureVectorizeIndex(ctx: DeployContext): Promise<void> {
|
||||
const url = `https://api.cloudflare.com/client/v4/accounts/${ctx.accountId}/vectorize/v2/indexes`;
|
||||
const res = await fetch(url, {
|
||||
method: 'POST',
|
||||
headers: { Authorization: `Bearer ${ctx.apiToken}`, 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
name: KBDB_VECTORIZE_INDEX,
|
||||
config: { dimensions: 768, metric: 'cosine' },
|
||||
description: 'arcrun KBDB optional embed module (issue #7)',
|
||||
}),
|
||||
signal: AbortSignal.timeout(60_000),
|
||||
});
|
||||
if (res.ok) return;
|
||||
// 冪等:已存在 → 視為成功(CF 回 409 或 errors 含 already exists / duplicate)。
|
||||
const json = (await res.json().catch(() => null)) as
|
||||
| { success?: boolean; errors?: Array<{ message?: string; code?: number }> }
|
||||
| null;
|
||||
const msg = (json?.errors?.map(e => e.message).filter(Boolean).join('; ') || `HTTP ${res.status}`).toLowerCase();
|
||||
if (res.status === 409 || /already exists|duplicate|conflict/.test(msg)) return;
|
||||
throw new Error(msg);
|
||||
}
|
||||
|
||||
/** 下載 codeload tarball 解壓到暫存目錄,回傳解壓出的 repo root 路徑。*/
|
||||
async function downloadRepoTarball(ref: string): Promise<string> {
|
||||
const url = `https://codeload.github.com/${ARCRUN_REPO}/tar.gz/${ref}`;
|
||||
@@ -411,6 +460,18 @@ function injectWranglerConfig(tomlPath: string, ctx: DeployContext): void {
|
||||
|
||||
toml = stripOfficialOnlyBindings(toml);
|
||||
|
||||
// 語義查詢(issue #7 / T2.4):開 kbdb_embed → 取消 kbdb toml 的 [[vectorize]]+[ai] 註解段(注入 active binding)。
|
||||
// **必須在 stripOfficialOnlyBindings 之後**:strip 會移除 [ai] 區塊(官方專屬),若先注入會被它清掉。
|
||||
// 只對含該註解段的 toml(= kbdb)生效;其餘 worker toml 無此段,replace 不命中、不動。
|
||||
// 未開 → 維持註解 → worker env 無 VECTORIZE/AI → embedEnabled()=false → base keyword(不花費)。
|
||||
if (ctx.kbdbEmbed) {
|
||||
toml = toml.replace(
|
||||
/# (\[\[vectorize\]\])\n# (binding = "VECTORIZE")\n# (index_name = "[^"]*")/,
|
||||
'$1\n$2\n$3',
|
||||
);
|
||||
toml = toml.replace(/# (\[ai\])\n# (binding = "AI")/, '$1\n$2');
|
||||
}
|
||||
|
||||
writeFileSync(tomlPath, toml, 'utf8');
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user