From 41b7757c19e8d6a78900088780476aba4e601bb3 Mon Sep 17 00:00:00 2001 From: cc-dan Date: Wed, 18 Mar 2026 01:46:09 +0000 Subject: [PATCH] =?UTF-8?q?feat:=20PNG=E5=A4=B4=E5=83=8F=E6=9B=BF=E6=8D=A2?= =?UTF-8?q?SVG=20+=20=E6=94=AF=E6=8C=81/init=E5=91=BD=E4=BB=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/agent-runtime.js | 44 ++++++++-------- public/app.js | 31 +++++------ public/claude.png | Bin 0 -> 2186 bytes public/codex.png | Bin 0 -> 22809 bytes public/style.css | 6 +-- server.js | 123 +++++++++++++++++++++++++------------------ 6 files changed, 112 insertions(+), 92 deletions(-) create mode 100644 public/claude.png create mode 100644 public/codex.png diff --git a/lib/agent-runtime.js b/lib/agent-runtime.js index ee9551d..ca759b6 100644 --- a/lib/agent-runtime.js +++ b/lib/agent-runtime.js @@ -70,29 +70,29 @@ function createAgentRuntime(deps) { const runtimeConfig = prepareCodexCustomRuntime(codexConfig); if (runtimeConfig?.error) { return { error: runtimeConfig.error }; - } - const runtimeId = getRuntimeSessionId(session); - const args = ['exec']; - args.push('--json', '--skip-git-repo-check'); + } + const runtimeId = getRuntimeSessionId(session); + const args = ['exec']; + args.push('--json', '--skip-git-repo-check'); - const permMode = session.permissionMode || 'yolo'; - // `-s/--sandbox` is an option for `codex exec`, but not for `codex exec resume`. - // When resuming, it must appear before the `resume` subcommand, otherwise Codex CLI errors - // with: "unexpected argument '-s' found". - if (runtimeId && permMode === 'plan') { - args.push('-s', 'read-only'); - } - if (runtimeId) args.push('resume'); - switch (permMode) { - case 'yolo': - args.push('--dangerously-bypass-approvals-and-sandbox'); - break; - case 'plan': - if (!runtimeId) args.push('-s', 'read-only'); - break; - case 'default': - default: - args.push('--full-auto'); + const permMode = session.permissionMode || 'yolo'; + // `-s/--sandbox` is an option for `codex exec`, but not for `codex exec resume`. + // When resuming, it must appear before the `resume` subcommand, otherwise Codex CLI errors + // with: "unexpected argument '-s' found". + if (runtimeId && permMode === 'plan') { + args.push('-s', 'read-only'); + } + if (runtimeId) args.push('resume'); + switch (permMode) { + case 'yolo': + args.push('--dangerously-bypass-approvals-and-sandbox'); + break; + case 'plan': + if (!runtimeId) args.push('-s', 'read-only'); + break; + case 'default': + default: + args.push('--full-auto'); break; } diff --git a/public/app.js b/public/app.js index 3d29f91..1a62566 100644 --- a/public/app.js +++ b/public/app.js @@ -11,6 +11,7 @@ { cmd: '/mode', desc: '查看/切换权限模式' }, { cmd: '/cost', desc: '查看会话费用' }, { cmd: '/compact', desc: '压缩上下文' }, + { cmd: '/init', desc: '生成/更新 CLAUDE.md' }, { cmd: '/help', desc: '显示帮助' }, ]; @@ -3260,13 +3261,13 @@
-
系统
-
- - -
-
- `; +
系统
+
+ + +
+
+ `; overlay.appendChild(panel); document.body.appendChild(overlay); @@ -3281,9 +3282,9 @@ const codexStatus = panel.querySelector('#codex-status'); const codexSaveBtn = panel.querySelector('#codex-save-btn'); - const pwOpenModalBtn = panel.querySelector('#pw-open-modal-btn'); - const checkUpdateBtn = panel.querySelector('#check-update-btn'); - const updateStatusEl = panel.querySelector('#update-status'); + const pwOpenModalBtn = panel.querySelector('#pw-open-modal-btn'); + const checkUpdateBtn = panel.querySelector('#check-update-btn'); + const updateStatusEl = panel.querySelector('#update-status'); let currentCodexConfig = null; let codexEditingProfiles = []; @@ -3822,12 +3823,12 @@ renderModelCustomArea(); }; - // === Notify Config UI (moved to subpage) === - // notify config is handled by openNotifySubpage() + // === Notify Config UI (moved to subpage) === + // notify config is handled by openNotifySubpage() - const closeBtn = panel.querySelector('.settings-close'); - const pwOpenModalBtn = panel.querySelector('#pw-open-modal-btn'); - pwOpenModalBtn.addEventListener('click', openPasswordModal); + const closeBtn = panel.querySelector('.settings-close'); + const pwOpenModalBtn = panel.querySelector('#pw-open-modal-btn'); + pwOpenModalBtn.addEventListener('click', openPasswordModal); // Check update button const checkUpdateBtn = panel.querySelector('#check-update-btn'); diff --git a/public/claude.png b/public/claude.png new file mode 100644 index 0000000000000000000000000000000000000000..ae857716d726e77f198d34fca6a64dccdc0e1ce0 GIT binary patch literal 2186 zcmV;52zB>~P) zNsk*v6o5aE?aX8*VGElf4ij+6&m=;RN$?pKSdc(adz%M!SapF_R=THKMxX?m*y1p{vnc!v&9Q(+~ zrzUrBcTv&+=E>^Lr_rl2J~i1@AQA8)S(CV;lRPUvg={L6w}PHxTq_VS_!hDdAU=hh z9%Dr4)krRZL(qK$Hh>V;$*!eWyUBu7#3rabLbegyI=L)KN47K}l+Q#FyZL6#WyvIj z?pnK~n(*TOvSjk~MzzkgL(ml0dN7pbQWin4^L_hxRhfJh`8_mT-F0Q>_^O1U$*}sj zL+faaK{^f8!Kw=%s;v-$6Y@G+pOggly@wMnlM%UD7c z@iAT%A-fuQ*@>6y{y`y{o?W}Pv92<%EiI38q_4G+*I{Zj{qDlmI6|KoCb3<^!vg7R znP~c~#AD_!G_QtEvG3j+r@n`(@ons7yUpVD<++XxJl*j#X>tpA4z=6;KC!U$31=u{ z_~YoeHfk^J^OS2eTfj@eb=3GZi}LFLFP6w}8EgZu0^g(RWJ2tse2H=zx&^$3s^1f0 zM})LvMVENuw*~A2BN7eCbn=%+gZ`JOia%mr$olgHcoe;Nqs(_u4I>X}FOPjxqZvoP zZAKTcOT4i?WM(Lh{TEdyW8#K-5qK&(F3Te%T`vz&^>PyBt0!an|4#JM4XO)#-5c(` z3)~}dP?`(CD^Y@?6mko&(gL22UMBK}%}CbE3|0R{Ww6kr+PdrXSWrhr zXF2h$f^d_VCQfCpAw91t+SMPCngiHo*Xa}0$*Kv{MV-oEBi#tbsIL8gOcSN_toStC zKVCQH8iq;8RZNCL7|bcAO_N>Ug|Nu1}q5KbX(IF#V54=!Rthko^?TSO+xN6zkerBkZ+AN zfby7(8)b&gZIlfEcRMl(`4R9Z@C@)E`Yma&24&yW27Vo4f15ZefKUx>KNz8em9!`5 z`qs_@S}twiEO3jITum*djq+wVPi%PB25W8SoUkb5w?>2hXT&b3qSeJn%VN|U?!Arj zm`MwH10~3I;`;_l(GGxrk{Ovad4dXyP?R5t-GKx@8~}H!X5w?5yy-bV!IL+wPoe1X zLGXhf2EAk?gvm;8lM(VEu{BR2MdFYYLNqyCf)uh$y8PlzX(V#}8c^pZbP;$pf{Ynr zS|^L3%M;`hs_Y|Z1v5d$yUvPxk=*-~wL_z5<1n44Z2Gcg@x7-jNFitS3Pr~2Di`N_ zh^U%h+4Mn<$!9#~B`Djlm*A^DHTf=T({wxfoD9)_5!DE6TRVScK7h&=aajMZCEXO| z+@PZ&=w0_6Vy_CltSh1wQ|F3l@imDR2|zWHF1-isbHVCp$;^oDzH05?*3?EoK+2K`Sg=iBcM z_ueG7eQT?Yw%lmJy4LTexq%k4-rcW_@gI!_{f{mATu&JIiPCLFX|Ie1{Vy;p)X%-) z-uoyc|88_n9cAG*$}^;L+z_9dd<=YN`R^O-6Ob{=WhEIU=OL;$u;mA;LT-{jx=t02 zfFB+IPf-H7m-tP)PW_HuP!CYf7AMR%a<4RHYIH@%aoJ5>_EfE9O_8M#wz8gc{I`MX zwQ8lQN^%VguJO`UzNYtVxrE^w5_Gq1LPzzfl@Oc;PQwN}m!MqFpqS9W4Wp)n>@u6V zgq+pNo4ljuI#)8zS(>Fc;HHE;p(t`IMC{i;i$xE1_tzZM+LflgplKZ65xW zwcIH)X=!>1i1VIo4K@bE%5ClOG1VIo4K@bE%5Z0Xk0KcO6t0K-iYXATM M07*qoM6N<$f+~J4r~m)} literal 0 HcmV?d00001 diff --git a/public/codex.png b/public/codex.png new file mode 100644 index 0000000000000000000000000000000000000000..7f5f24db1bdabc9a5faf52539f07485fa4617016 GIT binary patch literal 22809 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 literal 0 HcmV?d00001 diff --git a/public/style.css b/public/style.css index 5aed2b0..ef4a478 100644 --- a/public/style.css +++ b/public/style.css @@ -1021,10 +1021,10 @@ body.session-loading-active { background: transparent; border: none; } -/* Codex avatar: GPT logo on green bg */ +/* Codex avatar: transparent bg to match the supplied asset */ .msg.assistant.agent-codex .msg-avatar { - background: #10a37f; - color: #fff; + background: transparent; + border: none; } .msg-bubble { diff --git a/server.js b/server.js index 6a0bb19..dde6dac 100644 --- a/server.js +++ b/server.js @@ -437,27 +437,27 @@ const activeTokens = new Set(); const AUTH_FAIL_WINDOW = 5 * 60 * 1000; // 5 minutes const AUTH_FAIL_MAX = 3; const authFailures = new Map(); // ip -> [timestamp, ...] - let bannedIPs = new Set(); +let bannedIPs = new Set(); - // Tailscale / loopback whitelist — never ban these IPs. - // Extra whitelist can be provided via env var (comma/space separated): - // CC_WEB_IP_WHITELIST="," - const EXTRA_WHITELIST_IPS = new Set( - String(process.env.CC_WEB_IP_WHITELIST || '') - .split(/[\s,]+/) - .map(s => s.trim()) - .filter(Boolean) - .map(s => s.replace(/^::ffff:/, '')) - ); +// Tailscale / loopback whitelist — never ban these IPs. +// Extra whitelist can be provided via env var (comma/space separated): +// CC_WEB_IP_WHITELIST="," +const EXTRA_WHITELIST_IPS = new Set( + String(process.env.CC_WEB_IP_WHITELIST || '') + .split(/[\s,]+/) + .map(s => s.trim()) + .filter(Boolean) + .map(s => s.replace(/^::ffff:/, '')) +); - function isWhitelistedIP(ip) { - if (!ip) return false; - const cleaned = ip.replace(/^::ffff:/, ''); - return cleaned === '127.0.0.1' - || cleaned === '::1' - || cleaned.startsWith('100.') - || EXTRA_WHITELIST_IPS.has(cleaned); - } +function isWhitelistedIP(ip) { + if (!ip) return false; + const cleaned = ip.replace(/^::ffff:/, ''); + return cleaned === '127.0.0.1' + || cleaned === '::1' + || cleaned.startsWith('100.') + || EXTRA_WHITELIST_IPS.has(cleaned); +} function loadBannedIPs() { try { @@ -2055,24 +2055,43 @@ function handleSlashCommand(ws, text, sessionId, fallbackAgent) { break; } - case '/mode': { - const modeInput = parts[1]; - const VALID_MODES = ['default', 'plan', 'yolo']; - const MODE_DESC = { default: '默认(需权限审批,受限操作)', plan: 'Plan(需确认计划后执行)', yolo: 'YOLO(跳过所有权限检查)' }; - if (!modeInput) { - const cur = session?.permissionMode || 'yolo'; - wsSend(ws, { type: 'system_message', message: `当前模式: ${MODE_DESC[cur] || cur}\n可选: default, plan, yolo` }); - } else if (VALID_MODES.includes(modeInput.toLowerCase())) { - const mode = modeInput.toLowerCase(); - if (session) { - session.permissionMode = mode; - // Mode switching should not reset runtime context (Claude/Codex both resume). - session.updated = new Date().toISOString(); - saveSession(session); - } - wsSend(ws, { type: 'system_message', message: `权限模式已切换为: ${MODE_DESC[mode]}` }); - wsSend(ws, { type: 'mode_changed', mode }); - } else { + case '/init': { + if (agent !== 'claude') { + wsSend(ws, { type: 'system_message', message: '/init 仅支持 Claude,Codex 暂不支持该命令。' }); + break; + } + if (!sessionId || !session) { + wsSend(ws, { type: 'system_message', message: '请先进入一个会话后再执行 /init。' }); + break; + } + if (activeProcesses.has(sessionId)) { + wsSend(ws, { type: 'system_message', message: '当前会话正在处理中,请先等待完成或点击停止。' }); + break; + } + wsSend(ws, { type: 'system_message', message: '正在分析项目并生成 CLAUDE.md ...' }); + pendingSlashCommands.set(session.id, { kind: 'init' }); + handleMessage(ws, { text: '/init', sessionId: session.id, mode: session.permissionMode || 'yolo' }, { hideInHistory: true }); + break; + } + + case '/mode': { + const modeInput = parts[1]; + const VALID_MODES = ['default', 'plan', 'yolo']; + const MODE_DESC = { default: '默认(需权限审批,受限操作)', plan: 'Plan(需确认计划后执行)', yolo: 'YOLO(跳过所有权限检查)' }; + if (!modeInput) { + const cur = session?.permissionMode || 'yolo'; + wsSend(ws, { type: 'system_message', message: `当前模式: ${MODE_DESC[cur] || cur}\n可选: default, plan, yolo` }); + } else if (VALID_MODES.includes(modeInput.toLowerCase())) { + const mode = modeInput.toLowerCase(); + if (session) { + session.permissionMode = mode; + // Mode switching should not reset runtime context (Claude/Codex both resume). + session.updated = new Date().toISOString(); + saveSession(session); + } + wsSend(ws, { type: 'system_message', message: `权限模式已切换为: ${MODE_DESC[mode]}` }); + wsSend(ws, { type: 'mode_changed', mode }); + } else { wsSend(ws, { type: 'system_message', message: `无效模式: ${modeInput}\n可选: default, plan, yolo` }); } break; @@ -2088,7 +2107,7 @@ function handleSlashCommand(ws, text, sessionId, fallbackAgent) { type: 'system_message', message: agent === 'codex' ? base + '\n/model [名称] — 查看/切换 Codex 模型(自由输入)\n/compact — 执行 Codex /compact 压缩上下文' - : base + '\n/model [名称] — 查看/切换模型(opus, sonnet, haiku)\n/compact — 执行 Claude 原生上下文压缩(保留压缩计划并可自动续跑)', + : base + '\n/model [名称] — 查看/切换模型(opus, sonnet, haiku)\n/compact — 执行 Claude 原生上下文压缩(保留压缩计划并可自动续跑)\n/init — 分析项目并生成/更新 CLAUDE.md', }); break; } @@ -2330,20 +2349,20 @@ function handleRenameSession(ws, sessionId, title) { } } - function handleSetMode(ws, sessionId, mode) { - const VALID_MODES = ['default', 'plan', 'yolo']; - if (!mode || !VALID_MODES.includes(mode)) return; - if (sessionId) { - const session = loadSession(sessionId); - if (session) { - session.permissionMode = mode; - // Same rule as /mode: don't clear runtime context on mode changes. - session.updated = new Date().toISOString(); - saveSession(session); - } - } - wsSend(ws, { type: 'mode_changed', mode }); - } + function handleSetMode(ws, sessionId, mode) { + const VALID_MODES = ['default', 'plan', 'yolo']; + if (!mode || !VALID_MODES.includes(mode)) return; + if (sessionId) { + const session = loadSession(sessionId); + if (session) { + session.permissionMode = mode; + // Same rule as /mode: don't clear runtime context on mode changes. + session.updated = new Date().toISOString(); + saveSession(session); + } + } + wsSend(ws, { type: 'mode_changed', mode }); + } function handleDisconnect(ws, wsId) { const affectedSessions = [];