eb9f2db513
真兇(總管查證):daemon/config 寫死 extractor='claude' 且不下發金鑰 ⇒ 封測者 100% 萃取失敗 (leo:「地端沒有 AI 根本不能萃,那它就不能玩」)。 - POST/GET /portal/admin/extractor(admin 閘;GET 只回 has_key 不回明文) - daemon/config 改讀設定:未設定→gemma 無金鑰;設定後→含 gemini_api_key - portal 設定頁加「萃取引擎」區(Gemini 推薦+aistudio 連結,存後提示「小幫手點連上知識庫重連即可」) 測試 3 條新綠(vitest 17 passed;1 紅=console HTML 搬遷陳舊測試,非本案)。 (實作=子 CC;驗證+commit=總管)
250 lines
22 KiB
Markdown
250 lines
22 KiB
Markdown
# portal-auth — Tasks(實作切分,每階段可獨立 PR)
|
||
|
||
> 狀態:**全部未動工——本 SDD 待總管/leo 審後才開工**(B 類流程:SDD 審 → 實作 PR → component-pr-review-standard → 總管 review → gated 部署 leo21c)
|
||
> 對應:Gitea #24/#25;design.md 各 §
|
||
|
||
依賴鏈:P1 → P2 → P3 → P4(P2 不依賴 P1 可並行起,但 P3 要兩者都齊)。
|
||
|
||
---
|
||
|
||
## P1 — KBDB「庫」filter 地基(design §3.2/§3.3)|觸碰:`kbdb/`
|
||
|
||
- [x] 開工第一件事:實測 Vectorize metadata filter `$in` 支援與否(決定主路徑 vs fan-out fallback)
|
||
- **核實結論(2026-07-14)**:**支援,走主路徑 `$in`,不需 fan-out fallback**。證據:① 官方文件
|
||
developers.cloudflare.com/vectorize/reference/metadata-filtering/ 明列 8 運算子
|
||
`$eq/$ne/$in/$nin/$lt/$lte/$gt/$gte`,「For $in and $nin, filter object values can be arrays of
|
||
string, number, boolean, or null values」;② 本 repo `@cloudflare/workers-types@4.20260702.1`
|
||
原生 typing `VectorizeVectorMetadataFilterCollectionOp = "$in" | "$nin"`(index.d.ts:15782),
|
||
wrangler 4.98.0。限制:metadata filtering 只對 2023-12-06 之後建的 index 有效(本專案 index 皆是)。
|
||
本機無 Vectorize runtime,`$in` 的**線上實跑**併入部署排練驗(見部署清單)。
|
||
- [x] `/entries/search`+`/entries` 加 `library` 多值參數(D1 json_extract IN+NULL→general fallback)
|
||
- 實作註:SQL 用 `COALESCE(json_extract(metadata_json,'$.library'),'general') IN (…)`——與 design §3.3
|
||
的 OR 形狀語意完全等價(單組佔位符較簡)。本機 miniflare+local D1 十案例實跑全過(PR 附證據)。
|
||
- [x] embed upsert metadata 加 `library` 欄;semanticSearch filter 支援 library(**$in 主路徑**,fan-out 不需)
|
||
- 實作註:Vectorize 端在**寫入時正規化**(未標記→`'general'`)——Vectorize filter 做不了 COALESCE,
|
||
寫入端蓋章後查詢端單純 `$in`;與 D1 查詢端 fallback 語意對齊。
|
||
- [x] Vectorize `library` metadata index 建立步驟+reindex backfill 寫進部署清單
|
||
- **部署清單(待雲端排練驗——本機無 Vectorize runtime,semantic 路徑 code 完成、線上實跑未驗)**:
|
||
1. `wrangler vectorize create-metadata-index arcrun-kbdb-embed --property-name library --type string`
|
||
2. `POST /embed/backfill {"reindex":true}` 分批到 remaining=0(index 只收建立後 upsert 的向量)
|
||
3. 驗收抽查:semantic 帶 `library=` 過濾命中/不帶行為不變
|
||
4. 掛號:`cli/src/lib/deploy.ts` `ensureVectorizeMetadataIndexes()` 待補 `library`(cli/ 屬 P1
|
||
派工範圍外「只動 kbdb」,隨 P3 或部署 PR 補——kbdb/wrangler.toml 註解已標)
|
||
- [x] cypher `kbdb-proxy` 透傳 `library` 參數(供 owner/admin 面用;portal 面走 P3 的注入,不經這)
|
||
- ⚠️ 範圍註(2026-07-14):P1 派工紅線「只動 kbdb、不碰 cypher-executor」與本項矛盾——照窄範圍
|
||
執行,本項順延(一行 query 透傳,隨 P3 動 cypher 時一併)。
|
||
- ✅ 已隨 P3 補上(2026-07-14 P3 PR):`/kbdb/search` 與 `/kbdb/entries` 透傳白名單各加 `library`。
|
||
- [x] 測試:D1 filter 單元測、NULL fallback、多值、semantic filter(mock VECTORIZE)
|
||
- `kbdb/tests/library-filter.test.ts` 11 項(SQL 形狀/route 解析/向後相容/降級仍 enforce/$in 構造/
|
||
寫入端正規化)+既有 6 項全綠(17/17);tsc exit 0。
|
||
- **驗收**:curl `/entries/search?q=&library=finance` 只回 finance+未標記條目歸 general 可驗;semantic 同
|
||
- **工程量**:小-中(0.5–1 個 CC 工作天)
|
||
|
||
## P2 — portal_user 模型+認證 API(design §2/§4)|觸碰:`cypher-executor/`(新 route 檔)
|
||
|
||
- [x] seed template `portal_user`+`portal_library`(init-seed 慣例)
|
||
- 實作註(2026-07-14):種子資料 `cypher-executor/src/lib/portal-seeds.ts`(*-seeds.ts 慣例類別);
|
||
冪等 ensure 在 `routes/portal.ts` `ensurePortalTemplates()`,掛進 `POST /init/seed`(回應加
|
||
`portal_templates` 區塊)+ bootstrap 前自動 ensure(沒跑過 seed 也能 bootstrap)。
|
||
- [x] PBKDF2-SHA256 雜湊模組(600k iters、常數時間比對、`pbkdf2-sha256$…` 格式)
|
||
- `cypher-executor/src/lib/portal-auth.ts`(WebCrypto deriveBits;verify 從儲存格式解析 iterations
|
||
→ 換 KDF/參數可共存漸進遷移;constantTimeEqual 同 mcp PR#15 實作)。
|
||
- [x] `POST /portal/admin/bootstrap`(console owner session 閘)
|
||
- [x] login/logout/session/改密碼(KV `portal_sess:`、TTL var、每請求回讀 record 驗 status)
|
||
- `PORTAL_SESSION_TTL` 進 wrangler.toml [vars]+types.ts(預設 604800)。
|
||
- [x] admin 用戶 CRUD+libraries 授權+庫目錄 CRUD(role=admin 閘)
|
||
- 安全註:admin 操作目標 record 需過「email head entry 指回同 record_id」成員驗證(防拿任意
|
||
record_id 改到不相干 KBDB record);列表/回應一律剝除 password_hash;未帶密碼建帳號 →
|
||
server 產一次性密碼隨回應回傳一次(不落地明碼)。
|
||
- [x] 登入失敗節流(5 次/15 分鐘,KV TTL)
|
||
- [x] 順手修 `record-crud.ts` updateRecord grow 路徑漏 owner_id(design §2.2 附帶)
|
||
- 修法:首查 JOIN entries 帶回 record 既有 owner_id,grow 的新 entry 沿用。回歸測試
|
||
`kbdb/tests/record-grow-owner.test.ts`(3 項:帶 owner/原無 owner 維持 null/非 grow 不 INSERT)。
|
||
- [x] 測試:bootstrap 閘、登入對錯、停用即拒(session 立即失效)、role 閘、雜湊格式、`{tenant}::portal` 子 namespace 隔離(**搜 email 搜不到**)
|
||
- 單元:`cypher-executor/tests/portal-auth.test.ts` 20 項(fetchMock 假 KBDB host,絕不外連);
|
||
cypher 全套 133/134(唯一失敗=executor「不存在的零件」pre-existing,stash 複驗);kbdb 20/20;
|
||
兩包 tsc exit 0。端到端:本機雙 worker(隔離 persist)33/33 全綠,含**搜尋隔離實測**
|
||
(租戶 ns 搜 email/雜湊 0 筆+對照組 leo::portal 搜得到=非假綠)與明碼落地抽查 0 筆。
|
||
- **驗收**(=#25 驗收):新增用戶能登入;停用登入被拒+既有 session 失效;KBDB 無新表;密碼抽查非明碼
|
||
- **工程量**:中(約 1 個 CC 工作天)
|
||
|
||
## P3 — `/portal` UI:登入殼+搜尋頁+設定頁+scope enforce(design §1/§3.3/§5/§6)|觸碰:`cypher-executor/`
|
||
|
||
- [x] `/portal` HTML 殼(重用 console 樣式/搜尋 view 抽共用 helper;`CONSOLE_BRAND` 品牌;零 Mira 字樣)
|
||
- 實作註(2026-07-14):`routes/portal-ui.ts` 單檔 HTML+原生 JS(console 同形態)。**UI 重用取捨=
|
||
複製 console 樣式子集而非抽共用模組**——抽共用要動 console.ts(違「console 現狀不動」鐵律),
|
||
代價=樣式雙份、改版要兩邊同步(PR 說明誠實記)。零 Mira 字樣由測試機械斷言。
|
||
- [x] 未登入只見登入殼;登入後兩頁:搜尋(keyword/semantic/graph 三模式+source 溯源+卡片詳頁)+設定(改密碼/看自己權限/主題)
|
||
- graph 模式按 session `graph_allowed` 顯示(D-4);工作流頁 admin 額外可見(D-8,
|
||
`PORTAL_SHOW_WORKFLOWS` 預設 admin,唯讀不開 trigger)——前端藏只是 UX,真閘在路由層。
|
||
- [x] `/portal/data/*` server-side enforce:session→record→注入 `owner_id`+`library`;**前端絕不下發租戶字串**
|
||
- `routes/portal-data.ts`(安全核心):caller 自帶 owner_id/library 一律被 server 值靜默覆蓋
|
||
(e2e 攻擊實測);`["*"]`=不注 library;HTML 殼經測試機械斷言零租戶字串/零
|
||
X-Arcrun-API-Key/不打 /kbdb/*。
|
||
- [x] 卡片詳頁逐筆驗 library(越庫 id 直讀 → 404)
|
||
- 雙重驗證:owner_id=CONSOLE_TENANT(防拿跨租戶 id 直讀)+library∈用戶集合(NULL→general);
|
||
越庫/跨租戶/不存在**同一句 404**(不洩存在性,e2e 逐字比對)。
|
||
- [x] graph 粗閘(D-4:無 graph 來源庫權限 → 模式不顯示+API 403)
|
||
- 來源庫=portal_library `graph_source='true'` 集合(seed 補 slot+ensurePortalTemplates 冪等
|
||
grow 舊 template 缺的 slots;admin libraries PATCH 可標記/取消),**沒有任何標記→預設
|
||
['general']**(D-4 定案);無權 403、前端模式鈕不顯示;停用庫不算來源。
|
||
- [x] 測試:curl 帶 user session 直打 data API 驗 filter 繞不過(=#24 驗收 3 的 server-side 證明)
|
||
- 單元:`cypher-executor/tests/portal-data.test.ts` 21 項(fetchMock 假 host,絕不外連);
|
||
cypher 全套 154/155(唯一失敗=executor「不存在的零件」pre-existing,stash 複驗);
|
||
kbdb 20/20;兩包 tsc exit 0。端到端:本機隔離雙 worker(9790/9791,demo 8787/8788 不碰)
|
||
**47/47 全綠**——含越庫 id 直讀 404、攻擊參數被覆蓋、graph 403→授 general 後放行、
|
||
admin 全庫+跨租戶不可見、權限 PATCH 即時生效、HTML 殼斷言+inline JS node --check、
|
||
改密碼全鏈。`wrangler deploy --dry-run` 打包過。
|
||
- **驗收**(=#24 驗收):leo21c `/portal` 可開;未登入只見登入殼;三模式+溯源可用;A 用戶(僅 general)搜不到 finance 內容——UI 與 curl 雙驗
|
||
- 本機端到端已全數驗過(上項證據);**leo21c 線上面=部署後驗**(gated wrangler 直推,B 類流程;
|
||
semantic 線上路徑仍依 P1 部署清單的 Vectorize index+reindex)。
|
||
- **工程量**:大(1–1.5 個 CC 工作天,UI 是最大件)
|
||
|
||
## P4 — admin 頁+工作流顯示(design §6)|觸碰:`cypher-executor/`
|
||
|
||
- [x] 帳號管理頁(admin-only nav):列表/新增/停用/重設密碼+每帳號庫權限勾選
|
||
- 實作註(2026-07-14):`portal-ui.ts` 加「管理」頁(v-admin,nav 只對 role=admin 顯示,
|
||
真閘=/portal/admin/* role 閘 403)。新增未帶密碼 → server `generatePassword` 一次性
|
||
密碼**只在畫面顯示一次**(otpbox,關閉即逝;server 只存 hash);每帳號庫勾選含
|
||
`["*"]` 全庫選項(勾選連動停用個別庫框);可勾清單=庫目錄啟用中 ∪ general ∪ 該帳號
|
||
既有授權(目錄外舊授權不憑空消失)。admin API P2 已交付不重做;本波補
|
||
**last-admin 鎖死保護**(總管派工明定,design 未明寫——PATCH 停用/降級「最後一個
|
||
active admin」→ 409;只在目標是 active admin 且 patch 會使其失格時多打一次 list)。
|
||
- [x] 庫目錄管理(登記/停用庫)
|
||
- 同頁第二區:登記(name/display_name/description)+停用/啟用+ `graph_source`
|
||
圖譜來源標記(D-4,P3 已有 API,本波補 UI)。
|
||
- [x] 工作流頁(唯讀 list+最近執行;`PORTAL_SHOW_WORKFLOWS` 預設 admin;不開 trigger)
|
||
- **P3 已交付**(`/portal/data/workflows` route+v-workflows 頁+D-8 閘),P4 沿用零改動。
|
||
- **驗收**:admin 全流程「發帳號→授庫→同仁登入查詢→停用」在 UI 走通;一般用戶看不到 admin 頁與(預設下)工作流頁
|
||
- 單元:`tests/portal-admin.test.ts` 14 項(鎖死保護含「另一 admin 是 disabled 不算數」
|
||
「停用一般 user 不多打 list」;一次性密碼/reset 明碼不進 KBDB body;HTML 殼加 admin 頁後
|
||
仍零租戶字串/零 /kbdb//零 API key/零 Mira)。cypher 全套 168/169(唯一失敗=executor
|
||
「不存在的零件」pre-existing);kbdb 20/20;兩包 tsc 0;dry-run 打包過;inline JS
|
||
node --check 過(2 段)。
|
||
- 端到端:本機隔離雙 worker(9790/9791,demo 8787/8788 不碰)**41/41 全綠**——bootstrap→
|
||
admin 建帳號拿一次性密碼→新 user 登入→勾庫後**同一 session 搜尋範圍即時變化**(1 筆→2 筆、
|
||
越庫 404→200)→停用 session 立即失效→重設密碼舊密 401 新密 200→last-admin 409(含降級
|
||
即時生效:原 admin session 打 admin API 變 403)→一般 user 打 admin API 403。
|
||
- leo21c 線上面=部署後驗(gated wrangler 直推,B 類流程)。
|
||
- **工程量**:中(約 1 個 CC 工作天)
|
||
|
||
---
|
||
|
||
## Bugfix(P1-P4 交付後)
|
||
|
||
- [x] **PBKDF2 600k → 100k(T6-cloud 部署抓到的框架蟲,2026-07-14,分支 `fix-pbkdf2-cf-limit`)**:
|
||
CF Workers 正式 runtime PBKDF2 上限 100,000 iterations,600k 時 `crypto.subtle.deriveBits`
|
||
直接拒絕 → `/portal/admin/bootstrap` 真雲 500(uncle6 實撞,證據 arcrun-rag
|
||
`docs/manual/uncle6-deploy-record.md`);miniflare 無此限制=本機測試全綠假象。
|
||
修:`portal-auth.ts` `PBKDF2_ITERATIONS` 100_000+註明平台封頂;design D-5 已加修訂註。
|
||
verify 相容性:迭代數從儲存值解析,補測試「600k 舊 hash 仍通」(miniflare 造);誠實邊界
|
||
=真雲 deriveBits 同樣封頂、600k hash 真雲仍拒,但真雲 bootstrap 從未成功過 → 無存量 hash、
|
||
無遷移面。同 PR 順手:`.component-builds/http_request/wrangler.toml` 補
|
||
`global_fetch_strictly_public` flag(self-hosted 同帳號 fetch 1042 正解,Arcrun#33 同族,
|
||
官方行為不變原理同 cypher 前例 rules/03 1042 段)。
|
||
|
||
- [x] **Portal 來源回溯超連結(2026-07-18,任務層小改,分支 `feat/portal-source-link`;
|
||
來源=leo RAG demo 客戶測試回饋「來源回溯 gitea:// 沒辦法點」)**:
|
||
新增可選 var `PORTAL_SOURCE_WEB_BASE`(types.ts 註解含語意);portal-ui 卡片詳頁「來源回溯」
|
||
與 AI 問答出處列在「var 有設+source 為 gitea://」時渲染 `<a target=_blank rel=noopener>`
|
||
(gitea://<path>#n → {base}/<path>,chunk 錨點捨棄、路徑逐段 encodeURIComponent、href 過
|
||
esc());未設 var 行為一字不變(Mira 零影響)。驗證:tsc 0、portal-auth/portal-data 測試
|
||
48/48 綠、渲染後 client JS new Function 語法檢查過。部署:uncle6 重部+設 var(leo 閘)。
|
||
|
||
- [x] **搜尋濾內部型別+總圖頁(2026-07-18 晚,任務層小改,分支 `feat/portal-library-overview`;
|
||
來源=leo 客戶測試回饋「無標題雜項列」+「書庫總圖」指示(原案=Arcrun#39 藏書地圖的 GUI 切片))**:
|
||
① `filterDeprecatedEntries` 加內部型別過濾(value=slot 值外漏雜項列、workflow=工作流定義),
|
||
#46 上游修好後隨整段拔掉;測試補一案(49/49 綠)。
|
||
② 新增 `GET /portal/data/graph/overview`(全租戶 active 三元組 → nodes/edges,D-4 graph
|
||
粗閘同 neighbors,kbdbFetch 直讀不經 plugin,邊數上限 500 誠實截斷)+portal「總圖」頁
|
||
(nav 隨 graph_allowed 顯示;手刻力導向佈局、固定種子確定性、紙感樣式、點節點跳圖譜搜尋;
|
||
頁尾連 00-MAP.md=#39「人機共用同一份地圖」的文字版)。
|
||
③ #39 本體(library_map Template/ingest 重算/MCP instructions+get_map)不在本次範圍,仍歸 #39 SDD。
|
||
|
||
- [x] **t95 搜尋 CJK 正規化(2026-07-28,任務層小改)**:
|
||
來源=leo 實測 geek6688 實例「搜『AI協作』(無空白)0 結果,搜『AI 協作』有結果,中文習慣不是每個人都會加空白」。
|
||
修:`portal-data.ts` 新增 `normalizeCjkQuery()`(CJK/ASCII 邊界自動插空白,純函式可 export 單測);
|
||
`/portal/data/search` 路由的 `q` 參數先過正規化再查 KBDB;只動查詢端不動索引端。
|
||
驗證:pure function 6 案(純 CJK/ASCII 不動、邊界插空白、已有空白不重複、全形符號不觸發)+
|
||
integration 2 案(`AI%E5%8D%94%E4%BD%9C` → KBDB 收到 `AI 協作`)。
|
||
|
||
- [x] **t96 圖譜節點模糊比對 fuzzy fallback(2026-07-28,任務層小改)**:
|
||
來源=leo 實測「graph 模式搜『AI 協作』回鄰居 0 關聯 0,但總圖上明明有節點『AI 協作規範書』—精確比對太嚴」。
|
||
修:`portal-data.ts` 新增 `findBestNodeMatch()`(contains 比對+最短名優先,純函式)+
|
||
`fuzzyFindNode()`(查 KBDB triplet records 找最佳節點名);
|
||
graph neighbors 路由 plugin fallback 路徑(②):精確命中 0 鄰居+0 邊 → 以 fuzzyFindNode 找最佳節點名重查;
|
||
工作流路徑(①)套 CJK 正規化但不加 fuzzy fallback(workflow 自管節點解析)。
|
||
B5 分支(work/b5-graph-library-filter-0726)只動同一行的 `libraries` 欄位,衝突面最小。
|
||
驗證:findBestNodeMatch 6 案(空清單/無命中/精確子字串/多命中取最短/CJK 未正規化/大小寫)+
|
||
integration 4 案(有鄰居直接回、0 鄰居 fuzzy 命中、0 鄰居 fuzzy 無命中誠實回 0、t95+t96 連動)。
|
||
|
||
- [x] **t97 庫目錄只顯示用戶同步進來的(2026-07-28,任務層小改)**:
|
||
來源=leo 裁定「用戶沒加上的庫,不要自作主張給它加上」。
|
||
t97a:bootstrap 後不再預埋 `kb` 庫(index.html firstsetup 移除 POST /portal/admin/libraries 種子 call)。
|
||
t97b:GET /portal/admin/libraries auto 段新增 `n === 'general'` 過濾——general 是系統「未標庫」
|
||
fallback 桶,不是用戶加的,不在目錄露臉;資料照舊、B5 權限語意不動。
|
||
驗證:portal-admin.test.ts 新增「auto 過濾 general」1 案(mock KBDB /entries/libraries → ['kb','general','notes'],
|
||
回應 names 含 kb/notes,不含 general)。
|
||
|
||
- [x] **t114 拿掉「登記到目錄」兩段式(2026-07-28,任務層小改)**:
|
||
來源=leo 裁定「掃進來的就是要進目錄…加入目錄這件小事還要分兩段做?是在攻打用戶嗎」。
|
||
修:`renderAdminLibs()` 拿掉 auto/已登記視覺分岔,auto 庫直接以完整卡片顯示(保留「同步自動出現」tag,
|
||
去掉「登記到目錄後可以改顯示名」描述文字);移除「登記到目錄」按鈕與 lib-adopt event handler;
|
||
空狀態文字改「同步小幫手還沒送來任何庫…庫會自動出現在這裡」。
|
||
顯示名:此輪 auto 庫唯讀(與已登記庫現行行為一致,實作成本最低;後續若需可補 auto-adopt on PATCH)。
|
||
搜尋不依賴登記(搜尋走 /portal/data/search → server 注入 library filter,與庫目錄登記簿無關)。
|
||
驗證:HTML 殼測試補「無 lib-adopt、無登記到目錄」斷言;既有 HTML 斷言(零租戶字串等)不回退。
|
||
|
||
- [x] **t115 kbdb 全域認證中介層(2026-07-28,安全洞熱修;二修 2026-07-28)**:
|
||
實證:不帶任何憑證直打 `arcrun-kbdb.<sub>.workers.dev/entries` → 200 回真實知識;POST 直寫
|
||
成功。B5 權限做在 cypher/portal 層,繞過 portal 直打 kbdb 全破,隱私賣點(原文不出機)形同虛設。
|
||
一修(已在樹上)兩個缺陷:① `if (env.KBDB_INTERNAL_TOKEN)` 才擋 = 沒設 secret 洞照開(fail-open);
|
||
② workflows/rag-ingest-card.local.yaml(arcrun-rag repo)有 3 處 `__KBDB_BASE__`
|
||
(post_block/post_triplet 走 http_request 直打 kbdb),rag-chat/graph-neighbors/takedown 同樣;
|
||
token 生效後收卡與查詢全 401。
|
||
二修(本次):
|
||
① `kbdb/src/index.ts` middleware 改 fail-closed:
|
||
- 未設 token → POST/PATCH/DELETE/PUT 直接 401(fail-closed for writes);
|
||
GET 記 console.warn 後放行(讀取升級窗口,老實例不整個炸)。
|
||
- 已設 token → 非 health 路由全部要求 Bearer(行為同一修)。
|
||
② `kbdb/tests/auth.test.ts` 改 12 項(含 PATCH/DELETE 無 token→401;原「無 token POST 過」改為 401)。
|
||
③ `kbdb/src/types.ts`+`kbdb/wrangler.toml` 說明改為 fail-closed 語意。
|
||
④ workflow http_request 節點(arcrun-rag 側):規格見本項末「給安裝器的規格」段,由總管派 arcrun-rag。
|
||
cypher 不需改(五處已有 `if (KBDB_INTERNAL_TOKEN) headers[Authorization]=Bearer`)。
|
||
老實例升級路徑:安裝器部署時自動生成同一把 token → `wrangler secret put KBDB_INTERNAL_TOKEN` 注入
|
||
kbdb 與 cypher 兩個 worker;workflow yaml 同批替換 `__KBDB_TOKEN__`(見規格)即封口。
|
||
kbdb_upsert_block WASM 指向死路由 `/blocks`(設 token 前後都壞,不新增破壞)。
|
||
|
||
- [x] **t116 圖搜尋靜默失敗——portal 補傳 kbdb_base(2026-07-29,任務層小改)**:
|
||
診斷:graph_neighbors workflow 的 fetch_triplets url 含 `{{input.kbdb_base}}/records/...`,
|
||
但 portal 呼叫 executeWebhookGraph 時未傳 kbdb_base → URL 解析失敗 → `HTTP request failed`。
|
||
修法(b 案 雙保險):portal-data.ts 補傳 `kbdb_base: env.KBDB_BASE_URL ?? ''`;
|
||
yaml 端(`__KBDB_BASE__` 安裝期替換)由總管派 arcrun-rag,詳見本項末規格段。
|
||
執行範圍:`cypher-executor/src/routes/portal-data.ts`(補傳 kbdb_base)。
|
||
|
||
- [x] **t117 三元組寫入靜默失敗——FOREACH 全失敗浮出水面(2026-07-29,任務層小改)**:
|
||
診斷:youlin 實例 triplet=0,懷疑 post_triplet http_request 401 被靜默吞掉;
|
||
FOREACH 收集到全 `success:false` 的 iterResults 後回 `{success:true, data:{results:[...]}}` —— 呼叫端不知失敗。
|
||
兩層修法:
|
||
① wasi-shim.ts http_request catch:改寫錯誤 envelope(含 fetch 原始 message)到 WASM 輸出,
|
||
取代只 return 1(WASM 寫 "HTTP request failed");
|
||
② graph-executor.ts FOREACH:若全部 iterResults 均 `success===false`,拋 Error(含首項 status code)
|
||
→ catch 轉 ExecutionError → executeWebhookGraph 回 `{success:false, error:"..."}`。
|
||
執行範圍:`cypher-executor/src/lib/wasi-shim.ts`、`cypher-executor/src/graph-executor.ts`。
|
||
測試:vitest 新增兩條(FOREACH 全失敗→error 含 status;wasi-shim catch 路徑)。
|
||
|
||
- [x] **t122 萃取引擎金鑰雲端下發(2026-07-29)**:
|
||
來源=總管確認:daemon/config 回傳 extractor:'claude' 寫死且不含金鑰,封測者萃取全滅。
|
||
修:① `POST /portal/admin/extractor`(admin 閘)存 `{engine, gemini_api_key?, llm_model?}` 到 KV
|
||
key=`{tenant}:portal:extractor_config`,金鑰不落 log;
|
||
② `GET /portal/admin/extractor`(回 engine + has_key:bool,不回明文);
|
||
③ `POST /portal/daemon/config` 回傳改讀上述設定(未設→預設 gemma;gemma+有 key → 含金鑰下發);
|
||
④ `console-ui/public/portal/index.html` 設定頁新增「萃取引擎」區塊(引擎選擇+金鑰輸入+提示重連)。
|
||
測試:vitest 新增 3 案(未設定→下發 gemma 無金鑰;設定後→下發含金鑰;GET 不回明文)。
|
||
|
||
## 第二波(不在本 SDD 動工範圍,掛號)
|
||
|
||
- MCP token 綁庫集合(design §9;PR#15 擴充,只動 `mcp/`)
|
||
- graph 逐節點/逐邊細粒度過濾
|
||
- Google OAuth2 fast-follow、審計 log、忘記密碼 email 自助、舊資料 library 回填清潔工
|