diff --git a/console-ui/public/portal/index.html b/console-ui/public/portal/index.html
index 2ed0b05..b484d37 100644
--- a/console-ui/public/portal/index.html
+++ b/console-ui/public/portal/index.html
@@ -676,7 +676,7 @@ function taipeiMonthDay(ms) { var d = new Date(ms + TAIPEI_OFFSET_MS); return {
var p = S.profile || {};
if (p.role !== 'admin') return;
var s = setupSteps();
- if (s.daemon && s.config && s.key) return; // 三件齊=安裝真的完成,不再打擾
+ if (s.daemon && s.key) return; // t54 起只剩兩件:設定改由小幫手輸入帳密自取,不再下載檔案
var cfg = window.ARCRUN_CONFIG || {};
var daemonUrl = cfg.daemonDownload || 'https://raw.githubusercontent.com/youlinhsieh/arcrun-rag-bundles/main/daemon/ArcrunRAG-mac-unsigned.zip';
var row = function (done, id, html) {
@@ -686,7 +686,7 @@ function taipeiMonthDay(ms) { var d = new Date(ms + TAIPEI_OFFSET_MS); return {
var el = document.createElement('div');
el.id = 'setup-checklist';
el.style.cssText = 'position:fixed;right:20px;bottom:20px;z-index:60;max-width:400px;width:calc(100% - 40px);padding:18px 20px;border-radius:14px;background:rgba(var(--amber-rgb),.10);border:1px solid rgba(var(--amber-rgb),.45);backdrop-filter:blur(8px);font-size:14px;line-height:1.65';
- el.innerHTML = '還差 ' + (3 - (s.daemon?1:0) - (s.config?1:0) - (s.key?1:0)) + ' 步,安裝就真的完成了'
+ el.innerHTML = '還差 ' + (2 - (s.daemon?1:0) - (s.key?1:0)) + ' 步,安裝就真的完成了'
+ row(s.daemon, 'daemon',
'下載同步小幫手(把資料夾變成知識庫)
'
+ (daemonUrl
@@ -695,10 +695,10 @@ function taipeiMonthDay(ms) { var d = new Date(ms + TAIPEI_OFFSET_MS); return {
// 誠實降級(leo 判準:按了連到錯的機制=不友善):沒有可用下載點時
// 不放死連結,說明現況並讓這步可手動打勾,不卡住清單。
: '封測期由我們直接寄給你安裝檔(Mac 版);'
- + '收到後回來按 我已經裝好了。'))
- + row(s.config, 'config',
- '下載設定檔 放到 ~/.arcrun-rag/config.json
'
- + '下載 config.json')
+ + '收到後回來按 我已經裝好了。')
+ // t54(leo:「最好的就是把它的帳密直接輸入」):設定不再是一個要下載的檔案——
+ // 小幫手第一次開啟會問網址+帳密,自己去換設定。
+ + '
裝好第一次開啟時,貼上這個網址+你的帳號密碼就連上了,不用下載設定檔。
')
+ row(s.key, 'key',
'啟用 AI 問答(aistudio.google.com 免費申請)
'
+ ' '
@@ -708,24 +708,7 @@ function taipeiMonthDay(ms) { var d = new Date(ms + TAIPEI_OFFSET_MS); return {
document.body.appendChild(el);
var dl = document.getElementById('sc-daemon');
if (dl) dl.addEventListener('click', function () { markStep('daemon'); });
- var cb = document.getElementById('sc-config');
- if (cb) cb.addEventListener('click', function (ev) {
- ev.preventDefault();
- var conf = {
- watch_folders: [],
- manifest: '~/.arcrun-rag/manifest.json',
- cypher_url: cfg.apiBase || '',
- namespace: cfg.tenant || '',
- library: 'kb',
- extractor: 'claude',
- email: (S.profile && S.profile.email) || ''
- };
- var a = document.createElement('a');
- a.href = 'data:application/json;charset=utf-8,' + encodeURIComponent(JSON.stringify(conf, null, 2));
- a.download = 'config.json';
- document.body.appendChild(a); a.click(); a.remove();
- markStep('config');
- });
+ // t54:config.json 下載鈕已移除(設定由小幫手憑帳密自取)
var kb = document.getElementById('sc-key-save');
if (kb) kb.addEventListener('click', function () {
var k = (document.getElementById('sc-key').value || '').trim();
diff --git a/cypher-executor/src/routes/portal.ts b/cypher-executor/src/routes/portal.ts
index 15e63fa..8c1d872 100644
--- a/cypher-executor/src/routes/portal.ts
+++ b/cypher-executor/src/routes/portal.ts
@@ -699,6 +699,46 @@ function toPublicLibrary(rec: PortalRecord) {
};
}
+// POST /portal/daemon/config — body {email, password}。同步小幫手憑「用戶剛設的帳密」
+// 直接換到自己的設定(t54,leo 07-25:「最好的就是把它的帳密直接輸入」)——
+// 用戶不必再下載 config.json 丟隱藏資料夾,托盤第一次開啟輸入網址+帳密就上工。
+// 認證=與 /portal/login 同一把(同樣吃節流與停用檢查);回傳只含連線設定,不含任何知識內容。
+portalRouter.post('/portal/daemon/config', (c) =>
+ run(c, async () => {
+ const body = (await c.req.json().catch(() => null)) as { email?: string; password?: string } | null;
+ const email = String(body?.email ?? '').trim().toLowerCase();
+ const password = String(body?.password ?? '');
+ if (!email || !password) return c.json({ error: 'email 與 password 必填' }, 400);
+ if (await isLocked(c.env, email)) {
+ return c.json({ error: '登入失敗次數過多,已暫時鎖定,請 15 分鐘後再試' }, 429);
+ }
+ const recordId = await findUserRecordId(c.env, email);
+ const rec = recordId ? await getRecordById(c.env, recordId) : null;
+ if (!rec) {
+ await recordLoginFail(c.env, email);
+ return c.json({ error: 'email 或密碼錯誤' }, 401);
+ }
+ if ((rec.values.status ?? '') !== 'active') return c.json({ error: '帳號已停用' }, 403);
+ if (!(await verifyPassword(password, rec.values.password_hash ?? ''))) {
+ await recordLoginFail(c.env, email);
+ return c.json({ error: 'email 或密碼錯誤' }, 401);
+ }
+ await clearLoginFail(c.env, email);
+ const tenant = portalTenant(c.env);
+ return c.json({
+ success: true,
+ config: {
+ cypher_url: new URL(c.req.url).origin,
+ namespace: tenant,
+ library: 'kb',
+ extractor: 'claude',
+ email,
+ instance_name: String(rec.values.display_name ?? ''),
+ },
+ });
+ }),
+);
+
// POST /portal/admin/chat-key — body {key}。站內啟用 AI 問答(t53,leo 07-25:
// 「他進到網站要去給 gemini API Key」——不再回安裝器)。手法=G10 直嵌同款:
// 改寫 tenant 的 rag_chat workflow 記錄,把 x-goog-api-key 換成實值(KV 覆寫=重推)。