t176:雲端不再管地端 LLM 設定(leo 08-03 緊急)
leo 回報四件事:①雲端 Gemini 無法用 ②搞不清楚設定 AI 是設雲端還是地端 ③地端不會萃、每個人都死掉因為都去抓 Claude ④雲端還要設置明明就有 Workers AI。 目標:「雲端裝好就能用,地端輸一個 Gemini API Key,然後就開始用。」 根因:extractor_config 的 KV key 由 portalTenant() 組出,而 portalTenant 是 **worker 層級**環境變數(portal.ts:43)⇒ **全租戶共用一把**。任一處設了 claude, 所有人的 daemon 都收到 claude;沒裝 Claude Code 的機器萃取全滅,而 portal 的 Claude 勾選框又恆 disabled(daemon 從未實作 report-capabilities ⇒ daemon_caps 永遠空) ⇒ 用戶自己解不開(awindhon 08-03 實證:雲端同步成功、金鑰有效、零張卡)。 本次(雲端側): - POST /portal/daemon/config **不再下發** extractor/gemini_api_key/llm_model, 只回連線欄位。⚠️ route 本身與 daemon/libraries 資料夾管理完全不動(leo 明確劃界: 「雲端拉地端檔案夾部分不要刪,刪掉從雲端設置地端 LLM 選項部分」)。 - 移除 POST|GET /portal/admin/extractor(t122)——這是「雲端指定地端引擎」的入口, 且無任何伺服器端驗證,打一下就能把全租戶設成 claude。 - 移除 POST /portal/daemon/report-capabilities(t131)——daemon 端從未實作該呼叫。 - 移除 t131 那組重複註冊的 /portal/admin/ai。**它是重複 route**:後段(arcrun-rag#10) 另有同路徑一組,Hono 先到先比 ⇒ 舊的一直贏,後段修好的「金鑰真的寫進 credentials」 形同死碼。保留後段那組(只管 Gemini 金鑰、走 storeCredential),並拿掉 Claude 偏好欄。 - portal 設定頁「AI 設定」整塊移除,改成一句話說明:雲端不需設定(Workers AI 免金鑰), 地端請在同步小幫手的「AI 設定…」填 Gemini Key。順手清掉 main 上既有的 conflict 標記。 - 清掉隨之孤兒化的 helper(ExtractorConfig/AiConfig/DaemonCapabilities/ syncExtractorFromAiConfig/readAiPref 等)。 測試:**相對 merge 前 main 基準線,新增失敗 = 0**(基準線本就 9 紅:console 藏書地圖 6/ portal HTML 殼 2/POST execute 1,皆與本次無關)。t122/t131 兩組測試改寫成 **回歸守衛**(斷言那些端點/欄位確實 404、確實不存在),不是刪掉充綠。 順手修 health bundle_version 測試與實作對齊(實作刻意省略該欄,測試卻期待空字串)。 未送達:本 commit 只到 code,尚未部署上線。 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -363,44 +363,18 @@ if (!window.ARCRUN_API_BASE) {
|
||||
<div style="margin-top:8px;font-size:13px;line-height:1.7;color:rgba(var(--ink-rgb),.5)">封測版未簽章,第一次請右鍵→打開。裝好第一次開啟時,貼上這個網址+你的帳號密碼就連上了。</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- t131 合併 AI 設定(Gemini API Key 必填;Claude 加強版視 daemon 回報 enable) -->
|
||||
<div class="panel" id="st-ai-panel">
|
||||
<!-- t176(leo 08-03):AI 設定整塊移除。
|
||||
雲端聊天問答走 Workers AI(用戶自己 CF 帳號內建,**免金鑰、裝好就能用**);
|
||||
地端萃取用哪把金鑰改由「同步小幫手」自己設定(托盤選單「AI 設定…」)。
|
||||
為什麼移除:雲端的 extractor 設定是全租戶共用一把 KV,任一處設了 claude
|
||||
會讓所有沒裝 Claude Code 的機器萃取全滅,而這裡的 Claude 勾選框又恆為
|
||||
disabled(daemon 從未回報 has_claude)⇒ 用戶自己解不開(08-03 封測實證)。
|
||||
leo:「搞不清楚設定 AI 是設雲端還是地端」——設定只留一處,就不會再搞混。 -->
|
||||
<div class="panel">
|
||||
<div style="font-size:17px;font-weight:600">AI 設定</div>
|
||||
<div style="margin-top:14px;display:flex;flex-direction:column;gap:16px">
|
||||
<!-- 第一格:Gemini API Key(必填,聊天+萃取共用) -->
|
||||
<div>
|
||||
<div style="font-size:14px;font-weight:500;margin-bottom:4px">Gemini API Key</div>
|
||||
<div style="font-size:13px;line-height:1.6;color:rgba(var(--ink-rgb),.55);margin-bottom:8px">聊天問答與文件萃取都用這一把。<a href="https://aistudio.google.com/apikey" target="_blank" rel="noopener">免費申請</a>,金鑰只存在你自己的知識庫裡。</div>
|
||||
<<<<<<< HEAD
|
||||
<!-- 已存狀態(leo 2026-08-01 規格):唯讀顯示「已輸入」+「修改」鈕,**不顯示 key 本身**。
|
||||
舊版只把 placeholder 改成藍色小字「已設定」⇒ 讀起來像臨時提示、不像已保存的記錄,
|
||||
這正是它看起來像 UI bug 的原因(arcrun-rag#10 附帶修正)。 -->
|
||||
<div id="st-ai-key-saved" style="display:none;align-items:center;gap:10px">
|
||||
<span style="flex:1;padding:9px 12px;border:1px solid rgba(var(--ink-rgb),.14);border-radius:8px;background:rgba(var(--ink-rgb),.03);font-size:14px">
|
||||
<b>已輸入</b><span style="color:rgba(var(--ink-rgb),.45)">(金鑰已安全保存,不顯示內容)</span>
|
||||
</span>
|
||||
<button type="button" class="btn" id="st-ai-key-edit" style="flex:none">修改</button>
|
||||
</div>
|
||||
=======
|
||||
>>>>>>> main
|
||||
<input type="password" id="st-ai-key" class="txt" placeholder="貼上 Gemini API Key" autocomplete="off">
|
||||
</div>
|
||||
<!-- 第二格:Claude 加強版(選填,依 daemon 回報 enable) -->
|
||||
<div style="padding-top:12px;border-top:1px solid rgba(var(--ink-rgb),.08)">
|
||||
<div style="font-size:14px;font-weight:500;margin-bottom:6px">讓知識卡整理得更好(選填)</div>
|
||||
<label style="display:flex;gap:8px;align-items:flex-start;cursor:pointer">
|
||||
<input type="checkbox" id="st-ai-use-claude" style="margin-top:3px;flex:none" disabled>
|
||||
<span style="font-size:14px">本地萃取改用 Claude Code</span>
|
||||
</label>
|
||||
<div id="st-ai-claude-desc" style="margin-top:8px;font-size:13px;line-height:1.65;color:rgba(var(--ink-rgb),.55)">
|
||||
讀你文件、整理成知識卡的那個 AI,換成更強的模型。<b style="color:var(--ink)">卡片會更抓得到重點、關聯也連得更準</b>,之後搜尋和問答的品質跟著提升。需要你的電腦已安裝 Claude Code。不填就用上面那把 Gemini,一樣能用。
|
||||
</div>
|
||||
<div id="st-ai-claude-hint" style="margin-top:6px;font-size:13px;color:rgba(var(--ink-rgb),.4);display:none"></div>
|
||||
</div>
|
||||
<div>
|
||||
<button class="btn" id="st-ai-save">儲存 AI 設定</button>
|
||||
<div id="st-ai-status" style="font-size:14px;min-height:1.2em;margin-top:8px"></div>
|
||||
</div>
|
||||
<div style="margin-top:12px;font-size:13.5px;line-height:1.7;color:rgba(var(--ink-rgb),.55);background:rgba(var(--ink-rgb),.03);border-radius:9px;padding:11px 13px">
|
||||
<b style="color:var(--ink)">這裡不需要任何設定。</b>聊天問答用的是你自己 Cloudflare 帳號內建的 AI,裝好就能直接問。<br>
|
||||
文件整理成知識卡的部分,請在<b style="color:var(--ink)">同步小幫手</b>(電腦上的托盤圖示)的「AI 設定…」填一把 Gemini API Key。
|
||||
</div>
|
||||
</div>
|
||||
<button class="btn3" id="st-logout" style="padding:14px;font-size:16px;border-radius:11px">登出</button>
|
||||
@@ -433,11 +407,7 @@ if (!window.ARCRUN_API_BASE) {
|
||||
<button class="btn3" id="ad-copy-url" style="padding:5px 12px;font-size:13px;white-space:nowrap;flex:none">複製</button>
|
||||
</div>
|
||||
<div style="margin-bottom:12px;padding:12px 15px;border-radius:11px;background:rgba(var(--amber-rgb),.06);border:1px dashed rgba(var(--amber-rgb),.35);font-size:13.5px;line-height:1.7;color:rgba(var(--ink-rgb),.65)">
|
||||
<<<<<<< HEAD
|
||||
<!-- 07-27 leo:拿掉人工建庫表單——庫由「同步小幫手」看守資料夾時自動蓋章產生,
|
||||
=======
|
||||
<!-- 07-27 leo:拿掉「登記新庫」——庫由「同步小幫手」看守資料夾時自動蓋章產生,
|
||||
>>>>>>> main
|
||||
人工登記只會製造對不上的空庫。這裡只做「已存在的庫」的管理。 -->
|
||||
裝好<b style="color:var(--ink)">同步小幫手</b>並選好要看守的資料夾之後,每個資料夾會自動成為一個「庫」出現在下面——<b style="color:var(--ink)">不需要人工新增</b>。下面還是空的,代表小幫手還沒裝好或還沒選資料夾。
|
||||
</div>
|
||||
@@ -826,114 +796,10 @@ function taipeiMonthDay(ms) { var d = new Date(ms + TAIPEI_OFFSET_MS); return {
|
||||
}
|
||||
})();
|
||||
|
||||
// t131 AI 設定(合併 Gemini API Key+Claude 加強版)
|
||||
(function () {
|
||||
<<<<<<< HEAD
|
||||
/**
|
||||
* 切換金鑰欄的「已存 / 可輸入」兩態(leo 2026-08-01 規格,arcrun-rag#10):
|
||||
* 已存 → 唯讀顯示「已輸入」+「修改」鈕(**不顯示 key 本身**,D36)
|
||||
* 未存 / 按了修改 → 一般輸入框
|
||||
* 為什麼要這樣:舊版只把 placeholder 改成「已設定」=看起來像臨時提示,
|
||||
* 用戶讀不出「這是已保存的記錄」⇒ 以為沒存到、反覆重填。
|
||||
*/
|
||||
function setAiKeySaved(saved) {
|
||||
var box = $('st-ai-key-saved');
|
||||
var input = $('st-ai-key');
|
||||
if (!box || !input) return;
|
||||
if (saved) {
|
||||
box.style.display = 'flex';
|
||||
input.style.display = 'none';
|
||||
input.value = '';
|
||||
} else {
|
||||
box.style.display = 'none';
|
||||
input.style.display = '';
|
||||
}
|
||||
}
|
||||
// 按「修改」→ 變回可輸入框並聚焦(留空送出=不變更,由後端 body 判斷)
|
||||
var editBtn = $('st-ai-key-edit');
|
||||
if (editBtn) editBtn.addEventListener('click', function () {
|
||||
setAiKeySaved(false);
|
||||
var input = $('st-ai-key');
|
||||
if (input) { input.placeholder = '貼上新的 Gemini API Key(留空取消變更)'; input.focus(); }
|
||||
});
|
||||
|
||||
=======
|
||||
>>>>>>> main
|
||||
// 進入設定頁時讀取現有設定(GET /portal/admin/ai)
|
||||
function loadAiConfig() {
|
||||
if (!(S.profile && S.profile.role === 'admin')) return;
|
||||
fetch(API_BASE + '/portal/admin/ai', { headers: authHeaders() })
|
||||
.then(function (r) { return r.ok ? safeJson(r) : null; })
|
||||
.then(function (d) {
|
||||
if (!d) return;
|
||||
<<<<<<< HEAD
|
||||
// leo 2026-08-01 規格:已存 → 唯讀「已輸入」+「修改」鈕;未存 → 一般輸入框
|
||||
setAiKeySaved(!!d.has_key);
|
||||
=======
|
||||
var ki = $('st-ai-key');
|
||||
if (ki && d.has_key) ki.placeholder = '已設定(留空=不變更)';
|
||||
>>>>>>> main
|
||||
var cb = $('st-ai-use-claude');
|
||||
if (cb) {
|
||||
// 有 claude 才能勾;沒有則停用並顯示提示
|
||||
var hint = $('st-ai-claude-hint');
|
||||
if (d.claude_available) {
|
||||
cb.disabled = false;
|
||||
cb.checked = !!d.use_claude_for_extract;
|
||||
if (hint) hint.style.display = 'none';
|
||||
} else {
|
||||
cb.disabled = true;
|
||||
cb.checked = false;
|
||||
if (hint) {
|
||||
hint.style.display = '';
|
||||
// 區分:從未連上小幫手 vs 有連上但沒裝 Claude
|
||||
hint.textContent = d.has_key
|
||||
? '你的電腦沒有偵測到 Claude Code;裝好並讓小幫手重連一次後,這個選項就會開啟。'
|
||||
: '連上小幫手後才知道你的電腦有沒有 Claude Code。';
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
.catch(function () { /* 讀不到不擋頁面 */ });
|
||||
}
|
||||
window._loadAiConfig = loadAiConfig;
|
||||
|
||||
// 儲存
|
||||
var sb = $('st-ai-save');
|
||||
if (sb) sb.addEventListener('click', function () {
|
||||
var m = $('st-ai-status');
|
||||
var k = (($('st-ai-key') && $('st-ai-key').value) || '').trim();
|
||||
var cb = $('st-ai-use-claude');
|
||||
var useClaud = cb && !cb.disabled ? cb.checked : undefined;
|
||||
var body = {};
|
||||
if (k) body.gemini_api_key = k;
|
||||
if (useClaud !== undefined) body.use_claude_for_extract = useClaud;
|
||||
sb.disabled = true; m.textContent = '儲存中…'; m.style.color = '';
|
||||
fetch(API_BASE + '/portal/admin/ai', {
|
||||
method: 'POST',
|
||||
headers: Object.assign({ 'Content-Type': 'application/json' }, authHeaders()),
|
||||
body: JSON.stringify(body)
|
||||
}).then(function (r) { return safeJson(r).then(function (d) { return { ok: r.ok, status: r.status, d: d }; }); })
|
||||
.then(function (x) {
|
||||
sb.disabled = false;
|
||||
if (guard401(x.status)) return;
|
||||
if (!x.ok) { m.textContent = (x.d && x.d.error) || '儲存失敗'; m.style.color = '#b4462f'; return; }
|
||||
<<<<<<< HEAD
|
||||
// 存好了才切回「已輸入」唯讀態。只有真的送了新 key 才切;
|
||||
// 單純改 Claude 勾選(k 為空)時維持現狀,不誤報「已輸入」。
|
||||
if (k) setAiKeySaved(true);
|
||||
=======
|
||||
if ($('st-ai-key')) { $('st-ai-key').value = ''; $('st-ai-key').placeholder = '已設定(留空=不變更)'; }
|
||||
>>>>>>> main
|
||||
var claudeOn = x.d && x.d.use_claude_for_extract;
|
||||
m.textContent = claudeOn
|
||||
? '已儲存,萃取改用 Claude Code。小幫手請重連一次生效。'
|
||||
: '已儲存,AI 問答與萃取皆可使用。';
|
||||
m.style.color = '#3f7a4f';
|
||||
})
|
||||
.catch(function (e) { sb.disabled = false; m.textContent = friendlyErr(e); m.style.color = '#b4462f'; });
|
||||
});
|
||||
})();
|
||||
// t176(leo 08-03):AI 設定的前端邏輯整段移除。
|
||||
// 雲端聊天走 Workers AI(免金鑰);地端萃取金鑰改由同步小幫手托盤「AI 設定…」自己設。
|
||||
// 原本這裡有 Claude 勾選框的 enable/disable 邏輯——它依賴 daemon 回報 has_claude,
|
||||
// 而 daemon 從未實作該回報 ⇒ 勾選框恆為 disabled(leo 實測「hover 變可點但點擊沒反應」)。
|
||||
|
||||
$('st-logout').addEventListener('click', function () {
|
||||
fetch(API_BASE + '/portal/logout', { method: 'POST', headers: authHeaders() }).catch(function () { /* 盡力而為 */ });
|
||||
|
||||
Reference in New Issue
Block a user