aicodeman 1.11.1 → 1.11.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +10 -10
- package/README.zh-CN.md +8 -8
- package/dist/docker-hosts.d.ts.map +1 -1
- package/dist/docker-hosts.js +3 -0
- package/dist/docker-hosts.js.map +1 -1
- package/dist/tmux-manager.js +1 -1
- package/dist/utils/claude-cli-resolver.d.ts +1 -1
- package/dist/utils/claude-cli-resolver.js +1 -1
- package/dist/utils/cloudflared-resolver.d.ts +1 -1
- package/dist/utils/cloudflared-resolver.js +1 -1
- package/dist/web/public/admin-ui.js.gz +0 -0
- package/dist/web/public/api-client.c9b1cddc.js.gz +0 -0
- package/dist/web/public/{app.166af5f1.js → app.ce412e69.js} +5 -5
- package/dist/web/public/app.ce412e69.js.br +0 -0
- package/dist/web/public/app.ce412e69.js.gz +0 -0
- package/dist/web/public/constants.e7175b95.js.gz +0 -0
- package/dist/web/public/cron-ui.js.gz +0 -0
- package/dist/web/public/entrance-animations.js.gz +0 -0
- package/dist/web/public/i18n.0fbea500.js.gz +0 -0
- package/dist/web/public/image-input.ee16ad88.js.gz +0 -0
- package/dist/web/public/index.html +9 -5
- package/dist/web/public/index.html.br +0 -0
- package/dist/web/public/index.html.gz +0 -0
- package/dist/web/public/input-cjk.63794d0b.js.gz +0 -0
- package/dist/web/public/keyboard-accessory.9936227d.js.gz +0 -0
- package/dist/web/public/mobile-handlers.bc2b8c1d.js.gz +0 -0
- package/dist/web/public/mobile-overview.js.gz +0 -0
- package/dist/web/public/mobile.32cacde5.css.gz +0 -0
- package/dist/web/public/notification-manager.5d229063.js.gz +0 -0
- package/dist/web/public/orchestrator-panel.js.gz +0 -0
- package/dist/web/public/panels-ui.1d4203e0.js.gz +0 -0
- package/dist/web/public/ralph-panel.6de2d0f8.js.gz +0 -0
- package/dist/web/public/ralph-wizard.13a1831e.js.gz +0 -0
- package/dist/web/public/respawn-ui.ff0dae4c.js.gz +0 -0
- package/dist/web/public/sanitize-html.bc7078d6.js.gz +0 -0
- package/dist/web/public/session-ui.1356a4f4.js.gz +0 -0
- package/dist/web/public/{settings-ui.ed9cdb3f.js → settings-ui.01a1ae7d.js} +1 -1
- package/dist/web/public/settings-ui.01a1ae7d.js.br +0 -0
- package/dist/web/public/settings-ui.01a1ae7d.js.gz +0 -0
- package/dist/web/public/styles.50cd52bd.css +1 -0
- package/dist/web/public/styles.50cd52bd.css.br +0 -0
- package/dist/web/public/styles.50cd52bd.css.gz +0 -0
- package/dist/web/public/subagent-windows.4cc8b005.js.gz +0 -0
- package/dist/web/public/sw.js.gz +0 -0
- package/dist/web/public/terminal-ui.3ac6f8c2.js.gz +0 -0
- package/dist/web/public/ultracode-panel.js.gz +0 -0
- package/dist/web/public/ultracode-windows.js.gz +0 -0
- package/dist/web/public/upload.html.gz +0 -0
- package/dist/web/public/vendor/dompurify.min.js.gz +0 -0
- package/dist/web/public/vendor/marked.min.js.gz +0 -0
- package/dist/web/public/vendor/xterm-addon-fit.min.js.gz +0 -0
- package/dist/web/public/vendor/xterm-addon-serialize.min.js.gz +0 -0
- package/dist/web/public/vendor/xterm-addon-unicode11.min.js.gz +0 -0
- package/dist/web/public/vendor/xterm-addon-webgl.min.js.gz +0 -0
- package/dist/web/public/vendor/xterm-zerolag-input.137ad9f0.js.gz +0 -0
- package/dist/web/public/vendor/xterm.css.gz +0 -0
- package/dist/web/public/vendor/xterm.min.js.gz +0 -0
- package/dist/web/public/voice-input.085e9e73.js.gz +0 -0
- package/dist/web/public/webview-tabs.js.gz +0 -0
- package/dist/web/routes/session-routes.js +3 -3
- package/dist/web/routes/session-routes.js.map +1 -1
- package/dist/web/schemas.js +1 -1
- package/dist/web/schemas.js.map +1 -1
- package/package.json +2 -1
- package/dist/web/public/app.166af5f1.js.br +0 -0
- package/dist/web/public/app.166af5f1.js.gz +0 -0
- package/dist/web/public/settings-ui.ed9cdb3f.js.br +0 -0
- package/dist/web/public/settings-ui.ed9cdb3f.js.gz +0 -0
- package/dist/web/public/styles.2e8dc8b7.css +0 -1
- package/dist/web/public/styles.2e8dc8b7.css.br +0 -0
- package/dist/web/public/styles.2e8dc8b7.css.gz +0 -0
|
@@ -11,9 +11,9 @@
|
|
|
11
11
|
`).replace(/[ \t]+$/gm,"").replace(/\n{4,}/g,`
|
|
12
12
|
|
|
13
13
|
|
|
14
|
-
`).trim()}_preprocessAsciiArt(e){const t=/[─-╿▀-▟]/,s=/```[\s\S]*?```/g,n=[];return e.replace(s,a=>(n.push(a),`__CODEMAN_FENCE_${n.length-1}__`)).split(/(\n{2,})/).map(a=>/^\n{2,}$/.test(a)||!a.trim()||a.includes("__CODEMAN_FENCE_")?a:t.test(a)?"\n```\n"+a+"\n```\n":a).join("").replace(/__CODEMAN_FENCE_(\d+)__/g,(a,l)=>n[Number(l)])}_renderMarkdown(e){const t=e||"";if(typeof marked<"u"&&marked.parse)try{const n=this._preprocessAsciiArt(t);let i=this._sanitizeHtml(marked.parse(n,{breaks:!0,gfm:!0}));i=i.replace(/<table>/g,'<div class="rv-table-wrap"><table>').replace(/<\/table>/g,"</table></div>");const o=/[─-╿▀-▟]/,a=document.createElement("template");return a.innerHTML=i,a.content.querySelectorAll("pre > code").forEach(l=>{const r=l.parentElement,d=o.test(l.textContent||""),c=document.createElement("div");c.className=d?"rv-code-wrap rv-diagram-wrap":"rv-code-wrap";const u=document.createElement("div");u.className="rv-code-actions";const S=document.createElement("button");if(S.className="rv-copy-btn",S.type="button",S.setAttribute("aria-label","Copy code"),S.setAttribute("title","Copy code"),u.appendChild(S),d){r.classList.add("rv-diagram");const m=document.createElement("button");m.className="rv-wrap-toggle",m.type="button",m.setAttribute("aria-label","Toggle line wrapping"),m.setAttribute("title","Toggle line wrapping"),u.appendChild(m)}r.parentNode.insertBefore(c,r),c.appendChild(u),c.appendChild(r)}),a.innerHTML}catch{}return`<pre style="white-space:pre-wrap;word-break:break-word">${t.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">")}</pre>`}_bindResponseViewerInteractions(e){!e||e.dataset.rvBound==="1"||(e.dataset.rvBound="1",e.addEventListener("click",async t=>{const s=t.target.closest(".rv-copy-btn");if(s){t.preventDefault(),t.stopPropagation();const l=s.closest(".rv-code-wrap")?.querySelector("pre code"),r=l?await this._copyText(l.textContent||""):!1;s.classList.remove("rv-copied","rv-copy-failed"),s.classList.add(r?"rv-copied":"rv-copy-failed"),clearTimeout(s._resetTimer),s._resetTimer=setTimeout(()=>{s.classList.remove("rv-copied","rv-copy-failed")},1500);return}const n=t.target.closest(".rv-wrap-toggle");if(!n)return;t.preventDefault(),t.stopPropagation();const i=n.closest(".rv-diagram-wrap"),o=i?.querySelector("pre.rv-diagram");if(!o||!i)return;const a=o.classList.toggle("rv-nowrap");i.classList.toggle("rv-wrap-nowrap",a)}))}async _copyText(e){if(!e)return!1;try{if(navigator.clipboard?.writeText)return await navigator.clipboard.writeText(e),!0}catch{}try{const t=document.createElement("textarea");t.value=e,t.setAttribute("readonly",""),t.style.cssText="position:fixed;top:0;left:0;opacity:0;pointer-events:none",document.body.appendChild(t),t.select();const s=document.execCommand("copy");return document.body.removeChild(t),s}catch{return!1}}async toggleResponseViewer(){const e=document.getElementById("responseViewer"),t=document.getElementById("responseViewerBackdrop");if(!e)return;if(e.classList.contains("visible")){e.classList.remove("visible"),t.classList.remove("visible");return}if(this.activeSessionId)try{let o=((await(await fetch(`/api/sessions/${this.activeSessionId}/last-response`)).json())?.data??{}).text||"";const a=this.sessions.get(this.activeSessionId)?.mode||"claude";if(!o&&(a==="claude"||a==="shell")){const u=(await(await fetch(`/api/sessions/${this.activeSessionId}/terminal`)).json())?.data??{};u.terminalBuffer&&(o=this._cleanTerminalBuffer(u.terminalBuffer))}const l=document.getElementById("responseViewerBody");o?(l.innerHTML=this._renderMarkdown(o),this._bindResponseViewerInteractions(l)):l.textContent=window.codemanT?.("No response yet \u2014 send a message in this session first.")||"No response yet \u2014 send a message in this session first.";const r=document.getElementById("responseViewerTitle"),d=document.getElementById("responseViewerMore");r&&(r.textContent="Last Response"),d&&(d.style.display="",d.textContent="More"),e.classList.add("visible"),t.classList.add("visible"),l.scrollTop=0}catch(n){console.error("Failed to load response:",n)}}async loadFullContext(){if(!this.activeSessionId)return;const e=document.getElementById("responseViewerMore");e&&(e.textContent="...");try{const n=((await(await fetch(`/api/sessions/${this.activeSessionId}/last-response?context=full`)).json())?.data??{}).messages||[],i=document.getElementById("responseViewerBody"),o=document.getElementById("responseViewerTitle");if(!i)return;if(n.length===0){i.textContent="No conversation history available";return}const a=this.sessions.get(this.activeSessionId)?.mode,l=a==="codex"?"Codex":a==="gemini"?"Gemini":a==="antigravity"?"Antigravity":a==="opencode"?"OpenCode":"Claude";i.innerHTML="";for(const r of n){const d=document.createElement("div"),c=r.role==="user";d.className="rv-message "+(c?"rv-msg-user":"rv-msg-assistant");const u=document.createElement("div");u.className="rv-role "+(c?"rv-role-user":"rv-role-assistant"),u.textContent=c?"You":l,d.appendChild(u);const S=document.createElement("div");S.className="rv-text",S.innerHTML=this._renderMarkdown(r.text),d.appendChild(S),i.appendChild(d)}this._bindResponseViewerInteractions(i),o&&(o.textContent=`Conversation (${n.length} messages)`),e&&(e.style.display="none"),i.scrollTop=i.scrollHeight}catch(t){console.error("Failed to load context:",t)}finally{e&&(e.textContent="More")}}async _onSessionNeedsRefresh(){if(!(!this.activeSessionId||!this.terminal)&&!this._isLoadingBuffer)try{const t=(await(await fetch(`/api/sessions/${this.activeSessionId}/terminal?tail=${TERMINAL_TAIL_SIZE}`)).json())?.data??{};t.terminalBuffer&&(this.terminal.clear(),this.terminal.reset(),await this.chunkedTerminalWrite(t.terminalBuffer),this.terminal.scrollToBottom(),this._localEchoOverlay?.rerender(),this.activeSessionId&&this.sendResize(this.activeSessionId))}catch(e){console.error("needsRefresh reload failed:",e)}}async _onSessionClearTerminal(e){if(e.id===this.activeSessionId){if(this._isLoadingBuffer)return;try{const s=(await(await fetch(`/api/sessions/${e.id}/terminal`)).json())?.data??{};if(this.terminal.clear(),this.terminal.reset(),s.terminalBuffer){const n=s.terminalBuffer.replace(DEC_SYNC_STRIP_RE,"");await this.chunkedTerminalWrite(n)}this.sendResize(e.id),this._localEchoOverlay?.rerender()}catch(t){console.error("clearTerminal refresh failed:",t)}}}_onSessionCompletion(e){this.totalCost+=e.cost||0,this.updateCost(),e.id===this.activeSessionId&&(this.terminal.writeln(""),this.terminal.writeln(`\x1B[1;32m Done (Cost: $${(e.cost||0).toFixed(4)})\x1B[0m`))}_onSessionError(e){e.id===this.activeSessionId&&this.terminal.writeln(`\x1B[1;31m Error: ${e.error}\x1B[0m`),this._notifySession(e.id,"critical","session-error","Session Error",e.error||"Unknown error")}_onSessionExit(e){this._wsSessionId===e.id&&this._disconnectWs();const t=this.sessions.get(e.id);t&&(t.status="stopped",this.renderSessionTabs(),e.id===this.activeSessionId&&this._updateLocalEchoState()),e.code&&e.code!==0&&this._notifySession(e.id,"critical","session-crash","Session Crashed",`Exited with code ${e.code}`)}_onSessionIdle(e){const t=this.sessions.get(e.id);if(t&&(t.status="idle",this.renderSessionTabs(),this.sendPendingCtrlL(e.id),e.id===this.activeSessionId&&this._updateLocalEchoState()),!this.respawnStatus[e.id]?.enabled){const s=this.notificationManager?.preferences?.stuckThresholdMs||6e5;clearTimeout(this.idleTimers.get(e.id)),this.idleTimers.set(e.id,setTimeout(()=>{this._notifySession(e.id,"warning","session-stuck","Session Idle",`Idle for ${Math.round(s/6e4)}+ minutes`),this.idleTimers.delete(e.id)},s))}}_onSessionWorking(e){const t=this.sessions.get(e.id);t&&(t.status="busy",this.pendingHooks.has(e.id)||this.tabAlerts.delete(e.id),this.renderSessionTabs(),this.sendPendingCtrlL(e.id),e.id===this.activeSessionId&&this._updateLocalEchoState());const s=this.idleTimers.get(e.id);s&&(clearTimeout(s),this.idleTimers.delete(e.id))}_onSessionAutoClear(e){e.sessionId===this.activeSessionId&&(this.showToast(`Auto-cleared at ${e.tokens.toLocaleString()} tokens`,"info"),this.updateRespawnTokens(0)),this._notifySession(e.sessionId,"info","auto-clear","Auto-Cleared",`Context reset at ${(e.tokens||0).toLocaleString()} tokens`)}_onSessionLimitPauseScheduled(e){const t=this.sessions.get(e.sessionId);t&&(t.autoResumeAt=e.resumeAt);const s=new Date(e.resumeAt).toLocaleTimeString([],{hour:"2-digit",minute:"2-digit"});e.sessionId===this.activeSessionId&&this.showToast(`Usage limit reached \u2014 auto-resume at ${s}`,"warning"),this._notifySession(e.sessionId,"warning","limit-pause","Usage Limit Reached",`Auto-resume scheduled for ${s}`),this.updateAutoResumeStatus(e.sessionId)}_onSessionLimitResume(e){const t=this.sessions.get(e.sessionId);t&&(t.autoResumeAt=void 0),e.sessionId===this.activeSessionId&&this.showToast("Usage limit reset \u2014 work resumed automatically","success"),this._notifySession(e.sessionId,"info","limit-resume","Auto-Resumed","Usage limit reset \u2014 continuing work"),this.updateAutoResumeStatus(e.sessionId)}_onSessionLimitResumeCancelled(e){const t=this.sessions.get(e.sessionId);t&&(t.autoResumeAt=void 0),this.updateAutoResumeStatus(e.sessionId)}_onSessionRespawnBreakerTripped(e){const s=this.sessions.get(e.sessionId)?.name||"Session";this.showToast?.(`${s} stopped: repeated crashes detected. Restart to retry.`,"error")}_onSessionCliInfo(e){const t=this.sessions.get(e.sessionId);t&&(e.version&&(t.cliVersion=e.version),e.model&&(t.cliModel=e.model),e.accountType&&(t.cliAccountType=e.accountType),e.latestVersion&&(t.cliLatestVersion=e.latestVersion)),e.sessionId===this.activeSessionId&&this.updateCliInfoDisplay()}_onSessionStatusTelemetry(e){this.updatePlanUsageChip(e);try{localStorage.setItem("codeman:planUsage",JSON.stringify({t:Date.now(),data:e}))}catch{}}restorePlanUsageChip(){try{const e=localStorage.getItem("codeman:planUsage");if(!e)return;const t=JSON.parse(e);t?.data&&Date.now()-(t.t||0)<12*3600*1e3&&this.updatePlanUsageChip(t.data)}catch{}}updatePlanUsageChip(e){const t=document.getElementById("planUsageChip");if(!t||!e)return;const s=r=>r&&typeof r.usedPercentage=="number"?Math.round(r.usedPercentage):null,n=s(e.fiveHour),i=s(e.sevenDay);if(n===null&&i===null)return;const o=r=>r>=85?"pu-red":r>=60?"pu-yellow":"pu-green",a=(r,d)=>{if(d===null)return"";const c=Math.round(Number(d));return Number.isFinite(c)?`<span class="pu-win"><span class="pu-label">${r}</span><span class="pu-val ${o(c)}">${c}%</span></span>`:""};t.innerHTML=[a("5h",n),a("7d",i)].filter(Boolean).join('<span class="pu-sep">\xB7</span>');const l=r=>r&&r.resetAt?new Date(r.resetAt).toLocaleString():"\u2014";t.title=`Claude plan usage
|
|
14
|
+
`).trim()}_preprocessAsciiArt(e){const t=/[─-╿▀-▟]/,s=/```[\s\S]*?```/g,n=[];return e.replace(s,a=>(n.push(a),`__CODEMAN_FENCE_${n.length-1}__`)).split(/(\n{2,})/).map(a=>/^\n{2,}$/.test(a)||!a.trim()||a.includes("__CODEMAN_FENCE_")?a:t.test(a)?"\n```\n"+a+"\n```\n":a).join("").replace(/__CODEMAN_FENCE_(\d+)__/g,(a,l)=>n[Number(l)])}_renderMarkdown(e){const t=e||"";if(typeof marked<"u"&&marked.parse)try{const n=this._preprocessAsciiArt(t);let i=this._sanitizeHtml(marked.parse(n,{breaks:!0,gfm:!0}));i=i.replace(/<table>/g,'<div class="rv-table-wrap"><table>').replace(/<\/table>/g,"</table></div>");const o=/[─-╿▀-▟]/,a=document.createElement("template");return a.innerHTML=i,a.content.querySelectorAll("pre > code").forEach(l=>{const r=l.parentElement,d=o.test(l.textContent||""),c=document.createElement("div");c.className=d?"rv-code-wrap rv-diagram-wrap":"rv-code-wrap";const u=document.createElement("div");u.className="rv-code-actions";const p=document.createElement("button");if(p.className="rv-copy-btn",p.type="button",p.setAttribute("aria-label","Copy code"),p.setAttribute("title","Copy code"),u.appendChild(p),d){r.classList.add("rv-diagram");const _=document.createElement("button");_.className="rv-wrap-toggle",_.type="button",_.setAttribute("aria-label","Toggle line wrapping"),_.setAttribute("title","Toggle line wrapping"),u.appendChild(_)}r.parentNode.insertBefore(c,r),c.appendChild(u),c.appendChild(r)}),a.innerHTML}catch{}return`<pre style="white-space:pre-wrap;word-break:break-word">${t.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">")}</pre>`}_bindResponseViewerInteractions(e){!e||e.dataset.rvBound==="1"||(e.dataset.rvBound="1",e.addEventListener("click",async t=>{const s=t.target.closest(".rv-copy-btn");if(s){t.preventDefault(),t.stopPropagation();const l=s.closest(".rv-code-wrap")?.querySelector("pre code"),r=l?await this._copyText(l.textContent||""):!1;s.classList.remove("rv-copied","rv-copy-failed"),s.classList.add(r?"rv-copied":"rv-copy-failed"),clearTimeout(s._resetTimer),s._resetTimer=setTimeout(()=>{s.classList.remove("rv-copied","rv-copy-failed")},1500);return}const n=t.target.closest(".rv-wrap-toggle");if(!n)return;t.preventDefault(),t.stopPropagation();const i=n.closest(".rv-diagram-wrap"),o=i?.querySelector("pre.rv-diagram");if(!o||!i)return;const a=o.classList.toggle("rv-nowrap");i.classList.toggle("rv-wrap-nowrap",a)}))}async _copyText(e){if(!e)return!1;try{if(navigator.clipboard?.writeText)return await navigator.clipboard.writeText(e),!0}catch{}try{const t=document.createElement("textarea");t.value=e,t.setAttribute("readonly",""),t.style.cssText="position:fixed;top:0;left:0;opacity:0;pointer-events:none",document.body.appendChild(t),t.select();const s=document.execCommand("copy");return document.body.removeChild(t),s}catch{return!1}}_buildResponseViewerMessage(e,t,s){const n=document.createElement("div"),i=t==="user";n.className="rv-message "+(i?"rv-msg-user":"rv-msg-assistant");const o=document.createElement("div");o.className="rv-role "+(i?"rv-role-user":"rv-role-assistant"),o.textContent=i?"You":s,n.appendChild(o);const a=document.createElement("div");return a.className="rv-text",a.innerHTML=this._renderMarkdown(e),n.appendChild(a),n}_getResponseViewerAgentLabel(){const e=this.sessions.get(this.activeSessionId)?.mode;return e==="codex"?"Codex":e==="gemini"?"Gemini":e==="antigravity"?"Antigravity":e==="opencode"?"OpenCode":"Claude"}async toggleResponseViewer(){const e=document.getElementById("responseViewer"),t=document.getElementById("responseViewerBackdrop");if(!e)return;if(e.classList.contains("visible")){e.classList.remove("visible"),t.classList.remove("visible");return}if(this.activeSessionId)try{let o=((await(await fetch(`/api/sessions/${this.activeSessionId}/last-response`)).json())?.data??{}).text||"";const a=this.sessions.get(this.activeSessionId)?.mode||"claude";if(!o&&(a==="claude"||a==="shell")){const u=(await(await fetch(`/api/sessions/${this.activeSessionId}/terminal`)).json())?.data??{};u.terminalBuffer&&(o=this._cleanTerminalBuffer(u.terminalBuffer))}const l=document.getElementById("responseViewerBody");o?(l.innerHTML="",l.appendChild(this._buildResponseViewerMessage(o,"assistant",this._getResponseViewerAgentLabel())),this._bindResponseViewerInteractions(l)):l.textContent=window.codemanT?.("No response yet \u2014 send a message in this session first.")||"No response yet \u2014 send a message in this session first.";const r=document.getElementById("responseViewerTitle"),d=document.getElementById("responseViewerMore");r&&(r.textContent="Last Response"),d&&(d.style.display="",d.textContent="More"),e.classList.add("visible"),t.classList.add("visible"),l.scrollTop=0}catch(n){console.error("Failed to load response:",n)}}async loadFullContext(){if(!this.activeSessionId)return;const e=document.getElementById("responseViewerMore");e&&(e.textContent="...");try{const n=((await(await fetch(`/api/sessions/${this.activeSessionId}/last-response?context=full`)).json())?.data??{}).messages||[],i=document.getElementById("responseViewerBody"),o=document.getElementById("responseViewerTitle");if(!i)return;if(n.length===0){i.textContent="No conversation history available";return}const a=this._getResponseViewerAgentLabel();i.innerHTML="";for(const l of n)i.appendChild(this._buildResponseViewerMessage(l.text,l.role,a));this._bindResponseViewerInteractions(i),o&&(o.textContent=`Conversation (${n.length} messages)`),e&&(e.style.display="none"),i.scrollTop=i.scrollHeight}catch(t){console.error("Failed to load context:",t)}finally{e&&(e.textContent="More")}}async _onSessionNeedsRefresh(){if(!(!this.activeSessionId||!this.terminal)&&!this._isLoadingBuffer)try{const t=(await(await fetch(`/api/sessions/${this.activeSessionId}/terminal?tail=${TERMINAL_TAIL_SIZE}`)).json())?.data??{};t.terminalBuffer&&(this.terminal.clear(),this.terminal.reset(),await this.chunkedTerminalWrite(t.terminalBuffer),this.terminal.scrollToBottom(),this._localEchoOverlay?.rerender(),this.activeSessionId&&this.sendResize(this.activeSessionId))}catch(e){console.error("needsRefresh reload failed:",e)}}async _onSessionClearTerminal(e){if(e.id===this.activeSessionId){if(this._isLoadingBuffer)return;try{const s=(await(await fetch(`/api/sessions/${e.id}/terminal`)).json())?.data??{};if(this.terminal.clear(),this.terminal.reset(),s.terminalBuffer){const n=s.terminalBuffer.replace(DEC_SYNC_STRIP_RE,"");await this.chunkedTerminalWrite(n)}this.sendResize(e.id),this._localEchoOverlay?.rerender()}catch(t){console.error("clearTerminal refresh failed:",t)}}}_onSessionCompletion(e){this.totalCost+=e.cost||0,this.updateCost(),e.id===this.activeSessionId&&(this.terminal.writeln(""),this.terminal.writeln(`\x1B[1;32m Done (Cost: $${(e.cost||0).toFixed(4)})\x1B[0m`))}_onSessionError(e){e.id===this.activeSessionId&&this.terminal.writeln(`\x1B[1;31m Error: ${e.error}\x1B[0m`),this._notifySession(e.id,"critical","session-error","Session Error",e.error||"Unknown error")}_onSessionExit(e){this._wsSessionId===e.id&&this._disconnectWs();const t=this.sessions.get(e.id);t&&(t.status="stopped",this.renderSessionTabs(),e.id===this.activeSessionId&&this._updateLocalEchoState()),e.code&&e.code!==0&&this._notifySession(e.id,"critical","session-crash","Session Crashed",`Exited with code ${e.code}`)}_onSessionIdle(e){const t=this.sessions.get(e.id);if(t&&(t.status="idle",this.renderSessionTabs(),this.sendPendingCtrlL(e.id),e.id===this.activeSessionId&&this._updateLocalEchoState()),!this.respawnStatus[e.id]?.enabled){const s=this.notificationManager?.preferences?.stuckThresholdMs||6e5;clearTimeout(this.idleTimers.get(e.id)),this.idleTimers.set(e.id,setTimeout(()=>{this._notifySession(e.id,"warning","session-stuck","Session Idle",`Idle for ${Math.round(s/6e4)}+ minutes`),this.idleTimers.delete(e.id)},s))}}_onSessionWorking(e){const t=this.sessions.get(e.id);t&&(t.status="busy",this.pendingHooks.has(e.id)||this.tabAlerts.delete(e.id),this.renderSessionTabs(),this.sendPendingCtrlL(e.id),e.id===this.activeSessionId&&this._updateLocalEchoState());const s=this.idleTimers.get(e.id);s&&(clearTimeout(s),this.idleTimers.delete(e.id))}_onSessionAutoClear(e){e.sessionId===this.activeSessionId&&(this.showToast(`Auto-cleared at ${e.tokens.toLocaleString()} tokens`,"info"),this.updateRespawnTokens(0)),this._notifySession(e.sessionId,"info","auto-clear","Auto-Cleared",`Context reset at ${(e.tokens||0).toLocaleString()} tokens`)}_onSessionLimitPauseScheduled(e){const t=this.sessions.get(e.sessionId);t&&(t.autoResumeAt=e.resumeAt);const s=new Date(e.resumeAt).toLocaleTimeString([],{hour:"2-digit",minute:"2-digit"});e.sessionId===this.activeSessionId&&this.showToast(`Usage limit reached \u2014 auto-resume at ${s}`,"warning"),this._notifySession(e.sessionId,"warning","limit-pause","Usage Limit Reached",`Auto-resume scheduled for ${s}`),this.updateAutoResumeStatus(e.sessionId)}_onSessionLimitResume(e){const t=this.sessions.get(e.sessionId);t&&(t.autoResumeAt=void 0),e.sessionId===this.activeSessionId&&this.showToast("Usage limit reset \u2014 work resumed automatically","success"),this._notifySession(e.sessionId,"info","limit-resume","Auto-Resumed","Usage limit reset \u2014 continuing work"),this.updateAutoResumeStatus(e.sessionId)}_onSessionLimitResumeCancelled(e){const t=this.sessions.get(e.sessionId);t&&(t.autoResumeAt=void 0),this.updateAutoResumeStatus(e.sessionId)}_onSessionRespawnBreakerTripped(e){const s=this.sessions.get(e.sessionId)?.name||"Session";this.showToast?.(`${s} stopped: repeated crashes detected. Restart to retry.`,"error")}_onSessionCliInfo(e){const t=this.sessions.get(e.sessionId);t&&(e.version&&(t.cliVersion=e.version),e.model&&(t.cliModel=e.model),e.accountType&&(t.cliAccountType=e.accountType),e.latestVersion&&(t.cliLatestVersion=e.latestVersion)),e.sessionId===this.activeSessionId&&this.updateCliInfoDisplay()}_onSessionStatusTelemetry(e){this.updatePlanUsageChip(e);try{localStorage.setItem("codeman:planUsage",JSON.stringify({t:Date.now(),data:e}))}catch{}}restorePlanUsageChip(){try{const e=localStorage.getItem("codeman:planUsage");if(!e)return;const t=JSON.parse(e);t?.data&&Date.now()-(t.t||0)<12*3600*1e3&&this.updatePlanUsageChip(t.data)}catch{}}updatePlanUsageChip(e){const t=document.getElementById("planUsageChip");if(!t||!e)return;const s=r=>r&&typeof r.usedPercentage=="number"?Math.round(r.usedPercentage):null,n=s(e.fiveHour),i=s(e.sevenDay);if(n===null&&i===null)return;const o=r=>r>=85?"pu-red":r>=60?"pu-yellow":"pu-green",a=(r,d)=>{if(d===null)return"";const c=Math.round(Number(d));return Number.isFinite(c)?`<span class="pu-win"><span class="pu-label">${r}</span><span class="pu-val ${o(c)}">${c}%</span></span>`:""};t.innerHTML=[a("5h",n),a("7d",i)].filter(Boolean).join('<span class="pu-sep">\xB7</span>');const l=r=>r&&r.resetAt?new Date(r.resetAt).toLocaleString():"\u2014";t.title=`Claude plan usage
|
|
15
15
|
5-hour limit: ${n??"\u2014"}% used (resets ${l(e.fiveHour)})
|
|
16
|
-
Weekly limit: ${i??"\u2014"}% used (resets ${l(e.sevenDay)})`}_onScheduledCreated(e){this.currentRun=e,this.showTimer()}_onScheduledUpdated(e){this.currentRun=e,this.updateTimer()}_onScheduledCompleted(e){this.currentRun=e,this.hideTimer(),this.showToast("Scheduled run completed!","success")}_onScheduledStopped(){this.currentRun=null,this.hideTimer()}setConnectionStatus(e){this._connectionStatus=e,this._updateConnectionIndicator(),e==="connected"&&this._redeliverSweep()}_connectWs(e){this._disconnectWs(),this._wsState="connecting",this._updateConnectionIndicator();const t=location.protocol==="https:"?"wss:":"ws:",s=this._clientId?`${this._clientId}:${this._wsTabNonce}`:"",n=s?`?cid=${encodeURIComponent(s)}`:"",i=`${t}//${location.host}/ws/sessions/${e}/terminal${n}`,o=new WebSocket(i);this._ws=o,this._wsSessionId=e,o.onopen=()=>{this._ws===o&&(this._wsReady=!0,this._wsState="connected",this._wsReconnectAttempts=0,this._updateConnectionIndicator(),this.sendResize(e)?.catch?.(()=>{}),this._startMobileResizeRetry(e),this._onWsReady(e))},o.onmessage=a=>{if(this._ws===o){this._wsLastRecvAt=Date.now();try{const l=JSON.parse(a.data);l.t==="o"?this._onSessionTerminal({id:e,data:l.d}):l.t==="c"?this._onSessionClearTerminal({id:e}):l.t==="r"?this._onSessionNeedsRefresh({id:e}):l.t==="ia"&&this._onWsInputAck(l.seq)}catch{}}},o.onclose=a=>{if(this._ws!==o)return;this._ws=null,this._wsSessionId=null,this._wsReady=!1,this._stopMobileResizeRetry();const l=window.CodemanWsReconnect.plan(a.code,this._wsReconnectAttempts||0);_crashDiag.log(`WS CLOSE code=${a.code} reason=${a.reason||""} action=${l.action} attempts=${this._wsReconnectAttempts||0}`);const r=this.activeSessionId===e;if(l.action==="give-up")this._wsState=r?"fallback":"disconnected",this._updateConnectionIndicator();else if(l.action==="reconnect")if(r){this._wsState="reconnecting",this._updateConnectionIndicator();const d=l.delayMs+Math.floor(Math.random()*250);this._wsReconnectAttempts=(this._wsReconnectAttempts||0)+1,this._wsReconnectTimer=setTimeout(()=>{this._wsReconnectTimer=null,this.activeSessionId===e&&this._connectWs(e)},d)}else this._wsState="disconnected",this._updateConnectionIndicator();else this._wsState=r?"fallback":"disconnected",this._updateConnectionIndicator(),r&&(this._wsReconnectAttempts=(this._wsReconnectAttempts||0)+1,this._wsReconnectTimer=setTimeout(()=>{this._wsReconnectTimer=null,this.activeSessionId===e&&this._connectWs(e)},l.delayMs))},o.onerror=()=>{}}_disconnectWs(){this._clearTimer("_wsReconnectTimer"),this._wsState="disconnected",this._stopMobileResizeRetry(),this._ws&&(this._ws.onclose=null,this._ws.close(),this._ws=null,this._wsSessionId=null,this._wsReady=!1)}_startMobileResizeRetry(e){this._stopMobileResizeRetry(),(typeof MobileDetection<"u"&&MobileDetection.getDeviceType?MobileDetection.getDeviceType():"desktop")!=="desktop"&&(this._mobileResizeRetryTimer=setInterval(()=>{document.visibilityState==="visible"&&(!this._wsReady||this._wsSessionId!==e||typeof KeyboardHandler<"u"&&KeyboardHandler.keyboardVisible||this.sendResize(e)?.catch?.(()=>{}))},MOBILE_RESIZE_RETRY_MS))}_stopMobileResizeRetry(){this._mobileResizeRetryTimer&&(clearInterval(this._mobileResizeRetryTimer),this._mobileResizeRetryTimer=null)}_sendInputAsync(e,t,s){!e||!t||this._reliableSend(e,t,s?.useMux===!0)}_sendInputEphemeral(e,t){if(!(!e||!t)){if(this._ws&&this._ws.readyState===WebSocket.OPEN&&this._wsSessionId===e)try{this._ws.send(JSON.stringify({t:"i",d:t}));return}catch{}try{fetch(`/api/sessions/${e}/input`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({input:t}),keepalive:!0}).catch(()=>{})}catch{}}}_reliableSend(e,t,s){const i={seq:this._nextSeq(e),data:t,useMux:!!s,ts:Date.now(),tries:0,sentAt:0};let o=this._pendingDeliveries.get(e);o||(o=[],this._pendingDeliveries.set(e,o)),o.push(i),this._persistReliableState(),this._updateConnectionIndicator(),this._drainSession(e)}_nextSeq(e){const t=(this._seqCounters.get(e)||0)+1;return this._seqCounters.set(e,t),t}_drainSession(e){const t=this._pendingDeliveries.get(e);if(!(!t||t.length===0)){if(this._ws&&this._ws.readyState===WebSocket.OPEN&&this._wsSessionId===e){for(const s of t)if(s.sentAt===0)try{this._ws.send(JSON.stringify({t:"i",d:s.data,seq:s.seq,cid:this._clientId})),s.sentAt=Date.now(),s.tries++}catch{break}return}this._postDraining.has(e)||(this._postDraining.add(e),(async()=>{try{for(;;){const s=this._pendingDeliveries.get(e);if(!s||s.length===0||this._ws&&this._ws.readyState===WebSocket.OPEN&&this._wsSessionId===e)break;const n=s[0];n.tries++,n.sentAt=Date.now();let i=null;try{const o={input:n.data,seq:n.seq,clientId:this._clientId};n.useMux&&(o.useMux=!0),i=await fetch(`/api/sessions/${e}/input`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(o),keepalive:n.data.length<65536})}catch{i=null}if(i&&i.ok)this._ackDelivery(e,n.seq);else if(i&&(i.status===404||i.status===410))this._ackDelivery(e,n.seq);else break}}finally{this._postDraining.delete(e)}})())}}_ackDelivery(e,t){const s=this._pendingDeliveries.get(e);if(s){const n=s.findIndex(i=>i.seq===t);n!==-1&&(s.splice(n,1),s.length===0&&this._pendingDeliveries.delete(e),this._pendingDeliveries.size===0?this._persistReliableNow():this._persistReliableState(),this._updateConnectionIndicator())}this.clearPendingHooks?.(e)}_onWsInputAck(e){this._wsSessionId&&Number.isInteger(e)&&this._ackDelivery(this._wsSessionId,e)}_onWsReady(e){const t=this._pendingDeliveries.get(e);if(t)for(const s of t)s.sentAt=0;this._drainSession(e)}_redeliverSweep(){if(this._pendingDeliveries.size!==0)for(const e of[...this._pendingDeliveries.keys()]){const t=this._pendingDeliveries.get(e);if(!t||t.length===0)continue;if(this._ws&&this._ws.readyState===WebSocket.OPEN&&this._wsSessionId===e){const n=t[0],i=n&&n.sentAt&&Date.now()-n.sentAt>this._reliableAckTimeoutMs,o=Date.now()-this._wsLastRecvAt>this._reliableAckTimeoutMs;if(i&&o){try{this._ws.close()}catch{}continue}if(i)for(const a of t)a.sentAt&&Date.now()-a.sentAt>this._reliableAckTimeoutMs&&(a.sentAt=0)}this._drainSession(e)}}_pendingBytes(){let e=0,t=0;for(const s of this._pendingDeliveries.values())for(const n of s)e+=n.data.length,t++;return{bytes:e,count:t}}_loadReliableState(){try{this._clientId=localStorage.getItem("codeman:clientId")||""}catch{this._clientId=""}if(!this._clientId){this._clientId="c-"+Math.random().toString(36).slice(2)+"-"+Date.now().toString(36);try{localStorage.setItem("codeman:clientId",this._clientId)}catch{}}try{const e=localStorage.getItem("codeman:pendingInput");if(!e)return;const t=JSON.parse(e);if(t&&t.seqs)for(const[s,n]of Object.entries(t.seqs))Number.isFinite(n)&&this._seqCounters.set(s,n);if(t&&t.pending)for(const[s,n]of Object.entries(t.pending))Array.isArray(n)&&n.length&&this._pendingDeliveries.set(s,n.filter(i=>i&&typeof i.data=="string"&&Number.isInteger(i.seq)).map(i=>({seq:i.seq,data:i.data,useMux:!!i.useMux,ts:i.ts||Date.now(),tries:0,sentAt:0})))}catch{}}_persistReliableState(){this._persistReliableTimer||(this._persistReliableTimer=setTimeout(()=>{this._persistReliableTimer=null,this._persistReliableNow()},250))}_persistReliableNow(){this._persistReliableTimer&&(clearTimeout(this._persistReliableTimer),this._persistReliableTimer=null);try{const e={};for(const[i,o]of this._seqCounters)e[i]=o;const t={};let s=0;for(const[i,o]of this._pendingDeliveries)if(o.length){t[i]=o.map(a=>({seq:a.seq,data:a.data,useMux:a.useMux,ts:a.ts,tries:a.tries}));for(const a of o)s+=a.data.length}const n=s>this._reliableMaxBytes?{seqs:e}:{seqs:e,pending:t};localStorage.setItem("codeman:pendingInput",JSON.stringify(n))}catch{}}_computeConnectionDescriptor(){const{bytes:e,count:t}=this._pendingBytes(),s=t>0,i=e>4,o=l=>l<1024?`${l}B`:`${(l/1024).toFixed(1)}KB`,a=i?` \xB7 ${o(e)} queued`:"";if(!this.isOnline||this._connectionStatus==="offline")return{display:"flex",dotClass:"connection-dot offline",text:i?`Offline (${o(e)} queued)`:"Offline",title:"No network connection"};if(this.activeSessionId){let l,r,d;switch(this._wsState){case"connected":l="connected",r="WS",d="Terminal connected over WebSocket";break;case"fallback":l="fallback",r="HTTP",d="WebSocket unavailable \u2014 input sent over HTTP";break;case"reconnecting":l="reconnecting",r="WS\u2026",d="Reconnecting WebSocket";break;default:l="reconnecting",r="WS\u2026",d="Connecting WebSocket";break}return{display:"flex",dotClass:`connection-dot ${l}`,text:`${r}${a}`,title:d}}return this._connectionStatus==="reconnecting"||this._connectionStatus==="disconnected"?{display:"flex",dotClass:"connection-dot reconnecting",text:i?`Reconnecting (${o(e)} queued)`:"Reconnecting...",title:"Reconnecting to server"}:s?{display:"flex",dotClass:"connection-dot draining",text:i?`Sending ${o(e)}...`:"Sending...",title:"Delivering queued input"}:{display:"none",dotClass:"",text:"",title:""}}_updateConnectionIndicator(){const e=this.$("connectionIndicator"),t=this.$("connectionDot"),s=this.$("connectionText");if(!e||!t||!s)return;const n=this._computeConnectionDescriptor(),i=this._lastIndicatorDescriptor;i&&i.display===n.display&&i.dotClass===n.dotClass&&i.text===n.text&&i.title===n.title||(this._lastIndicatorDescriptor=n,e.style.display=n.display,n.display!=="none"&&(t.className=n.dotClass,s.textContent=n.text,e.title=n.title))}setupOnlineDetection(){window.addEventListener("online",()=>{this.isOnline=!0,this.reconnectAttempts=0,this.connectSSE(),this._redeliverSweep()}),window.addEventListener("offline",()=>{this.isOnline=!1,this.setConnectionStatus("offline")})}_updateCjkInputState(){const e=document.getElementById("cjkInput");if(!e)return;const t=this.loadAppSettingsFromStorage(),s=this.getDefaultSettings?.()||{},i=(this._serverCjkOverride||(t.cjkInputEnabled??s.cjkInputEnabled??!1))&&!!this.activeSessionId;e.classList.toggle("cjk-input-visible",!!i),document.body.classList.toggle("cjk-input-visible",!!i),e.style.display=i?"block":"none",e.setAttribute("aria-hidden",i?"false":"true"),i||(window.cjkActive=!1),typeof KeyboardHandler<"u"&&KeyboardHandler.updateLayoutForKeyboard()}_resetAllAppState(){this.sessions.clear(),this.ralphStates.clear(),this.terminalBuffers.clear(),this.terminalBufferCache.clear(),this._xtermSnapshots?.clear(),this.projectInsights.clear(),this.teams.clear(),this.teamTasks.clear();for(const e of this.idleTimers.values())clearTimeout(e);if(this.idleTimers.clear(),this._clearTimer("flickerFilterTimeout"),this.flickerFilterBuffer="",this.flickerFilterActive=!1,this._clearTimer("syncWaitTimeout"),this.pendingWrites=[],this.writeFrameScheduled=!1,this._isLoadingBuffer=!1,this._loadBufferQueue=null,this._bufferLoadOwner=null,this._chunkedWriteGen=(this._chunkedWriteGen||0)+1,this._localEchoOverlay?.rerender(),this.pendingHooks.clear(),this._parentNameCache&&this._parentNameCache.clear(),this.subagentActivity.clear(),this.subagentToolResults.clear(),this.workflowRuns&&this.workflowRuns.clear(),this.workflowRunDetails&&this.workflowRunDetails.clear(),this.activeWorkflowRunId=null,this.activeWorkflowPhaseIndex=null,MobileDetection.cleanup(),KeyboardHandler.cleanup(),MobileDetection.init(),KeyboardHandler.init(),this.tabAlerts.clear(),this.attachmentHistoryCounts.clear(),this._shownCompletions&&this._shownCompletions.clear(),this.notificationManager?.titleFlashInterval&&(clearInterval(this.notificationManager.titleFlashInterval),this.notificationManager.titleFlashInterval=null),this.notificationManager?.groupingMap){for(const{timeout:e}of this.notificationManager.groupingMap.values())clearTimeout(e);this.notificationManager.groupingMap.clear()}this.terminalResizeObserver&&(this.terminalResizeObserver.disconnect(),this.terminalResizeObserver=null),this.planLoadingTimer&&(clearInterval(this.planLoadingTimer),this.planLoadingTimer=null),this.timerCountdownInterval&&(clearInterval(this.timerCountdownInterval),this.timerCountdownInterval=null),this.runSummaryAutoRefreshTimer&&(clearInterval(this.runSummaryAutoRefreshTimer),this.runSummaryAutoRefreshTimer=null)}handleInit(e){this._clearTimer("_initFallbackTimer");const t=++this._initGeneration;if(this._serverCjkOverride=e.inputCjkForm||!1,this._updateCjkInputState(),e.planUsage&&this.updatePlanUsageChip(e.planUsage),e.version){const i=this.$("versionDisplay"),o=this.$("headerVersion");i&&(i.textContent=`v${e.version}`,i.title=`Codeman v${e.version}`),o&&(o.textContent=`v${e.version}`,o.title=`Codeman v${e.version}`)}VoiceInput.cleanup(),this._resetAllAppState(),e.sessions.forEach(i=>{this.sessions.set(i.id,i),(i.ralphLoop||i.ralphTodos)&&!this.ralphClosedSessions.has(i.id)&&this.ralphStates.set(i.id,{loop:i.ralphLoop||null,todos:i.ralphTodos||[]})});try{localStorage.removeItem("codeman-tab-meta")}catch{}if(Array.isArray(e.sessionOrder)&&e.sessionOrder.length)try{localStorage.setItem("codeman-session-order",JSON.stringify(e.sessionOrder))}catch{}this.syncSessionOrder(),e.respawnStatus?this.respawnStatus=e.respawnStatus:this.respawnStatus={},this.respawnTimers={},this.respawnCountdownTimers={},this.respawnActionLogs={},e.globalStats&&(this.globalStats=e.globalStats),this.totalCost=e.sessions.reduce((i,o)=>i+(o.totalCost||0),0),this.totalCost+=e.scheduledRuns.reduce((i,o)=>i+(o.totalCost||0),0);const s=e.scheduledRuns.find(i=>i.status==="running");if(s&&(this.currentRun=s,this.showTimer()),this.updateCost(),this.renderSessionTabs(),this.sessions.size>0?this.startSystemStatsPolling():this.stopSystemStatsPolling(),this.cleanupAllFloatingWindows(),e.subagents&&(this.subagents.clear(),this.subagentActivity.clear(),this.subagentToolResults.clear(),e.subagents.forEach(i=>{this.subagents.set(i.agentId,i)}),this.renderSubagentPanel(),this.subagentParentMap.clear(),this.loadSubagentParentMap().then(()=>{for(const[i,o]of this.subagentParentMap){const a=this.subagents.get(i);if(a&&this.sessions.has(o)){a.parentSessionId=o;const l=this.sessions.get(o);l&&(a.parentSessionName=this.getSessionName(l)),this.subagents.set(i,a)}}for(const[i]of this.subagents)this.subagentParentMap.has(i)||this.findParentSessionForSubagent(i);this.restoreSubagentWindowStates()})),e.workflowRuns&&this.seedWorkflowRuns(e.workflowRuns),t!==this._initGeneration)return;if(this.isSoloWindow){this._applySoloMode();return}const n=this.activeSessionId;if(this.activeSessionId=null,this.sessionOrder.length>0){let i=n;if(!i||!this.sessions.has(i))try{i=localStorage.getItem("codeman-active-session")}catch{}i&&this.sessions.has(i)?this.selectSession(i):this.selectSession(this.sessionOrder[0])}}async loadState(){try{const t=await(await fetch("/api/status")).json();this.handleInit(t?.data??{})}catch(e){console.error("Failed to load state:",e)}}_debouncedCall(e,t,s=100){this._debounceTimers[e]&&clearTimeout(this._debounceTimers[e]),this._debounceTimers[e]=setTimeout(()=>{this._debounceTimers[e]=null,t.call(this)},s)}renderSessionTabs(){this._inlineRenameActive||this._debouncedCall("sessionTabs",this._renderSessionTabsImmediate)}_updateActiveTabImmediate(e){const t=this.$("sessionTabs");if(!t)return;const s=t.querySelectorAll(".session-tab[data-id]");for(const n of s)n.dataset.id===e?n.classList.add("active"):n.classList.remove("active")}_setTerminalLoadState(e,t,s){this.terminalLoadStates.set(e,{generation:t,phase:s}),this._updateTerminalLoadTab(e)}_clearTerminalLoadState(e,t){const s=this.terminalLoadStates.get(e);s&&s.generation!==t||(this.terminalLoadStates.delete(e),this._updateTerminalLoadTab(e))}_updateTerminalLoadTab(e){const t=this.$("sessionTabs")?.querySelector(`.session-tab[data-id="${e}"]`);if(!t)return;const s=this.terminalLoadStates.get(e);if(t.classList.toggle("tab-loading",!!s),s){if(t.setAttribute("aria-busy","true"),t.dataset.loadPhase=s.phase,!t.querySelector(".tab-load-spinner")){const n=document.createElement("span");n.className="tab-load-spinner",n.setAttribute("aria-hidden","true");const i=t.querySelector(".tab-number");i?i.insertAdjacentElement("afterend",n):t.insertBefore(n,t.firstChild)}}else t.setAttribute("aria-busy","false"),delete t.dataset.loadPhase,t.querySelector(".tab-load-spinner")?.remove()}_renderSessionTabsImmediate(){if(this._inlineRenameActive)return;const e=this.$("sessionTabs"),t=e.querySelectorAll(".session-tab[data-id]"),s=new Set([...t].map(r=>r.dataset.id)),n=new Set(this.sessions.keys()),i=[...e.querySelectorAll(".session-tab[data-webview-id]")].map(r=>r.dataset.webviewId),o=(this.webviewOrder||[]).filter(r=>this.webviews?.has(r)),a=i.length===o.length&&i.every((r,d)=>r===o[d]);if(s.size===n.size&&[...s].every(r=>n.has(r))&&a)for(const[r,d]of this.sessions){const c=e.querySelector(`.session-tab[data-id="${r}"]`);if(!c)continue;const u=r===this.activeSessionId&&!this.activeWebviewId,S=d.status||"idle",m=this.getSessionName(d),v=d.taskStats||{running:0,total:0},g=v.running>0,C=this.terminalLoadStates.get(r);if(u&&!c.classList.contains("active")?c.classList.add("active"):!u&&c.classList.contains("active")&&c.classList.remove("active"),c.classList.toggle("tab-loading",!!C),C){if(c.setAttribute("aria-busy","true"),c.dataset.loadPhase=C.phase,!c.querySelector(".tab-load-spinner")){const f=document.createElement("span");f.className="tab-load-spinner",f.setAttribute("aria-hidden","true");const b=c.querySelector(".tab-number");b?b.insertAdjacentElement("afterend",f):c.insertBefore(f,c.firstChild)}}else c.setAttribute("aria-busy","false"),delete c.dataset.loadPhase,c.querySelector(".tab-load-spinner")?.remove();const k=this.tabAlerts.get(r),L=k==="action",E=k==="idle",A=c.classList.contains("tab-alert-action"),p=c.classList.contains("tab-alert-idle");if(L&&!A?(c.classList.add("tab-alert-action"),c.classList.remove("tab-alert-idle")):E&&!p?(c.classList.add("tab-alert-idle"),c.classList.remove("tab-alert-action")):!k&&(A||p)&&c.classList.remove("tab-alert-action","tab-alert-idle"),!c.querySelector(".tab-number")){const f=this.sessionOrder.indexOf(r);if(f>=0&&f<9){const b=document.createElement("span");b.className="tab-number",b.textContent=String(f+1),c.insertBefore(b,c.firstChild)}}const T=c.querySelector(".tab-status");T&&!T.classList.contains(S)&&(T.className=`tab-status ${S}`);const y=c.querySelector(".tab-name");if(y&&y.textContent!==m){const f=parseSessionPrefix(m);f&&f.suffix?y.innerHTML='<span class="tab-prefix">'+escapeHtml(f.prefix)+'</span><span class="tab-suffix">: '+escapeHtml(f.suffix)+"</span>":y.textContent=m}const _=c.querySelector(".tab-badge");if(g)if(_)_.textContent!==String(v.running)&&(_.textContent=v.running);else{this._fullRenderSessionTabs();return}else if(_){this._fullRenderSessionTabs();return}const w=c.querySelector(".tab-subagent-badge"),O=this.minimizedSubagents.get(r),R=O?.size||0;if(R>0&&w){const f=w.querySelector(".subagent-label"),b=R===1?"AGENT":`AGENTS (${R})`;f&&f.textContent!==b&&(f.textContent=b);const I=w.querySelector(".subagent-dropdown");if(I){const x=this.renderSubagentTabBadge(r,O),N=document.createElement("div");N.innerHTML=x;const D=N.querySelector(".subagent-dropdown");D&&(I.innerHTML=D.innerHTML)}}else if(R>0&&!w){const f=this.renderSubagentTabBadge(r,O),b=c.querySelector(".tab-gear");b&&b.insertAdjacentHTML("beforebegin",f)}else R===0&&w&&w.remove()}else this._fullRenderSessionTabs();this.updateTabOverflowMode(),this._applyTabEntrances?.(),this._refreshMobileOverviewIfVisible?.()}updateTabOverflowMode(){const e=this.$("sessionTabs");if(!e)return;const t=MobileDetection.getDeviceType(),s=this.loadAppSettingsFromStorage(),n=this.getDefaultSettings(),i=t==="desktop"?s.tabTwoRows??n.tabTwoRows??!1:!1;if(i||t!=="desktop"){e.classList.remove("tabs-auto-wrap");return}e.classList.remove("tabs-auto-wrap");const o=window.CodemanTabOverflow?.shouldAutoWrapTabs?window.CodemanTabOverflow.shouldAutoWrapTabs({deviceType:t,manualTwoRows:i,tabCount:this.sessions.size,scrollWidth:e.scrollWidth,clientWidth:e.clientWidth}):e.scrollWidth>e.clientWidth+1;e.classList.toggle("tabs-auto-wrap",o)}_fullRenderSessionTabs(){if(this._inlineRenameActive)return;const e=this.$("sessionTabs");document.querySelectorAll("body > .subagent-dropdown").forEach(i=>i.remove()),this.cancelHideSubagentDropdown();const t=[];let s=this.sessionOrder;MobileDetection.getDeviceType()==="mobile"&&this.activeSessionId&&(s=[this.activeSessionId,...this.sessionOrder.filter(i=>i!==this.activeSessionId)]);let n=0;for(const i of s){const o=this.sessions.get(i);if(!o)continue;const a=i===this.activeSessionId&&!this.activeWebviewId,l=o.status||"idle",r=this.getSessionName(o),d=o.mode||"claude",c=o.color||"default",u=o.taskStats||{running:0,total:0},S=u.running>0,m=this.tabAlerts.get(i),v=m==="action"?" tab-alert-action":m==="idle"?" tab-alert-idle":"",g=this.terminalLoadStates.get(i),C=this.minimizedSubagents.get(i),L=(C?.size||0)>0?this.renderSubagentTabBadge(i,C):"",E=this.renderUltracodeTabBadge?this.renderUltracodeTabBadge(i):"",A=o.workingDir&&o.workingDir.split("/").pop()||"",T=(this._tallTabsEnabled??!1)&&o.name&&A&&A!==r;t.push(`<div class="session-tab ${a?"active":""}${v}${g?" tab-loading":""}" data-id="${i}" data-color="${c}" ${g?`data-load-phase="${escapeHtml(g.phase)}"`:""} onclick="app.handleSessionTabClick(event, ${escapeHtml(JSON.stringify(i))})" oncontextmenu="event.preventDefault(); app.startInlineRename(${escapeHtml(JSON.stringify(i))})" tabindex="0" role="tab" aria-selected="${a?"true":"false"}" aria-busy="${g?"true":"false"}" aria-label="${escapeHtml(r)} session" ${o.workingDir?`title="${escapeHtml(o.workingDir)}"`:""}>
|
|
16
|
+
Weekly limit: ${i??"\u2014"}% used (resets ${l(e.sevenDay)})`}_onScheduledCreated(e){this.currentRun=e,this.showTimer()}_onScheduledUpdated(e){this.currentRun=e,this.updateTimer()}_onScheduledCompleted(e){this.currentRun=e,this.hideTimer(),this.showToast("Scheduled run completed!","success")}_onScheduledStopped(){this.currentRun=null,this.hideTimer()}setConnectionStatus(e){this._connectionStatus=e,this._updateConnectionIndicator(),e==="connected"&&this._redeliverSweep()}_connectWs(e){this._disconnectWs(),this._wsState="connecting",this._updateConnectionIndicator();const t=location.protocol==="https:"?"wss:":"ws:",s=this._clientId?`${this._clientId}:${this._wsTabNonce}`:"",n=s?`?cid=${encodeURIComponent(s)}`:"",i=`${t}//${location.host}/ws/sessions/${e}/terminal${n}`,o=new WebSocket(i);this._ws=o,this._wsSessionId=e,o.onopen=()=>{this._ws===o&&(this._wsReady=!0,this._wsState="connected",this._wsReconnectAttempts=0,this._updateConnectionIndicator(),this.sendResize(e)?.catch?.(()=>{}),this._startMobileResizeRetry(e),this._onWsReady(e))},o.onmessage=a=>{if(this._ws===o){this._wsLastRecvAt=Date.now();try{const l=JSON.parse(a.data);l.t==="o"?this._onSessionTerminal({id:e,data:l.d}):l.t==="c"?this._onSessionClearTerminal({id:e}):l.t==="r"?this._onSessionNeedsRefresh({id:e}):l.t==="ia"&&this._onWsInputAck(l.seq)}catch{}}},o.onclose=a=>{if(this._ws!==o)return;this._ws=null,this._wsSessionId=null,this._wsReady=!1,this._stopMobileResizeRetry();const l=window.CodemanWsReconnect.plan(a.code,this._wsReconnectAttempts||0);_crashDiag.log(`WS CLOSE code=${a.code} reason=${a.reason||""} action=${l.action} attempts=${this._wsReconnectAttempts||0}`);const r=this.activeSessionId===e;if(l.action==="give-up")this._wsState=r?"fallback":"disconnected",this._updateConnectionIndicator();else if(l.action==="reconnect")if(r){this._wsState="reconnecting",this._updateConnectionIndicator();const d=l.delayMs+Math.floor(Math.random()*250);this._wsReconnectAttempts=(this._wsReconnectAttempts||0)+1,this._wsReconnectTimer=setTimeout(()=>{this._wsReconnectTimer=null,this.activeSessionId===e&&this._connectWs(e)},d)}else this._wsState="disconnected",this._updateConnectionIndicator();else this._wsState=r?"fallback":"disconnected",this._updateConnectionIndicator(),r&&(this._wsReconnectAttempts=(this._wsReconnectAttempts||0)+1,this._wsReconnectTimer=setTimeout(()=>{this._wsReconnectTimer=null,this.activeSessionId===e&&this._connectWs(e)},l.delayMs))},o.onerror=()=>{}}_disconnectWs(){this._clearTimer("_wsReconnectTimer"),this._wsState="disconnected",this._stopMobileResizeRetry(),this._ws&&(this._ws.onclose=null,this._ws.close(),this._ws=null,this._wsSessionId=null,this._wsReady=!1)}_startMobileResizeRetry(e){this._stopMobileResizeRetry(),(typeof MobileDetection<"u"&&MobileDetection.getDeviceType?MobileDetection.getDeviceType():"desktop")!=="desktop"&&(this._mobileResizeRetryTimer=setInterval(()=>{document.visibilityState==="visible"&&(!this._wsReady||this._wsSessionId!==e||typeof KeyboardHandler<"u"&&KeyboardHandler.keyboardVisible||this.sendResize(e)?.catch?.(()=>{}))},MOBILE_RESIZE_RETRY_MS))}_stopMobileResizeRetry(){this._mobileResizeRetryTimer&&(clearInterval(this._mobileResizeRetryTimer),this._mobileResizeRetryTimer=null)}_sendInputAsync(e,t,s){!e||!t||this._reliableSend(e,t,s?.useMux===!0)}_sendInputEphemeral(e,t){if(!(!e||!t)){if(this._ws&&this._ws.readyState===WebSocket.OPEN&&this._wsSessionId===e)try{this._ws.send(JSON.stringify({t:"i",d:t}));return}catch{}try{fetch(`/api/sessions/${e}/input`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({input:t}),keepalive:!0}).catch(()=>{})}catch{}}}_reliableSend(e,t,s){const i={seq:this._nextSeq(e),data:t,useMux:!!s,ts:Date.now(),tries:0,sentAt:0};let o=this._pendingDeliveries.get(e);o||(o=[],this._pendingDeliveries.set(e,o)),o.push(i),this._persistReliableState(),this._updateConnectionIndicator(),this._drainSession(e)}_nextSeq(e){const t=(this._seqCounters.get(e)||0)+1;return this._seqCounters.set(e,t),t}_drainSession(e){const t=this._pendingDeliveries.get(e);if(!(!t||t.length===0)){if(this._ws&&this._ws.readyState===WebSocket.OPEN&&this._wsSessionId===e){for(const s of t)if(s.sentAt===0)try{this._ws.send(JSON.stringify({t:"i",d:s.data,seq:s.seq,cid:this._clientId})),s.sentAt=Date.now(),s.tries++}catch{break}return}this._postDraining.has(e)||(this._postDraining.add(e),(async()=>{try{for(;;){const s=this._pendingDeliveries.get(e);if(!s||s.length===0||this._ws&&this._ws.readyState===WebSocket.OPEN&&this._wsSessionId===e)break;const n=s[0];n.tries++,n.sentAt=Date.now();let i=null;try{const o={input:n.data,seq:n.seq,clientId:this._clientId};n.useMux&&(o.useMux=!0),i=await fetch(`/api/sessions/${e}/input`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(o),keepalive:n.data.length<65536})}catch{i=null}if(i&&i.ok)this._ackDelivery(e,n.seq);else if(i&&(i.status===404||i.status===410))this._ackDelivery(e,n.seq);else break}}finally{this._postDraining.delete(e)}})())}}_ackDelivery(e,t){const s=this._pendingDeliveries.get(e);if(s){const n=s.findIndex(i=>i.seq===t);n!==-1&&(s.splice(n,1),s.length===0&&this._pendingDeliveries.delete(e),this._pendingDeliveries.size===0?this._persistReliableNow():this._persistReliableState(),this._updateConnectionIndicator())}this.clearPendingHooks?.(e)}_onWsInputAck(e){this._wsSessionId&&Number.isInteger(e)&&this._ackDelivery(this._wsSessionId,e)}_onWsReady(e){const t=this._pendingDeliveries.get(e);if(t)for(const s of t)s.sentAt=0;this._drainSession(e)}_redeliverSweep(){if(this._pendingDeliveries.size!==0)for(const e of[...this._pendingDeliveries.keys()]){const t=this._pendingDeliveries.get(e);if(!t||t.length===0)continue;if(this._ws&&this._ws.readyState===WebSocket.OPEN&&this._wsSessionId===e){const n=t[0],i=n&&n.sentAt&&Date.now()-n.sentAt>this._reliableAckTimeoutMs,o=Date.now()-this._wsLastRecvAt>this._reliableAckTimeoutMs;if(i&&o){try{this._ws.close()}catch{}continue}if(i)for(const a of t)a.sentAt&&Date.now()-a.sentAt>this._reliableAckTimeoutMs&&(a.sentAt=0)}this._drainSession(e)}}_pendingBytes(){let e=0,t=0;for(const s of this._pendingDeliveries.values())for(const n of s)e+=n.data.length,t++;return{bytes:e,count:t}}_loadReliableState(){try{this._clientId=localStorage.getItem("codeman:clientId")||""}catch{this._clientId=""}if(!this._clientId){this._clientId="c-"+Math.random().toString(36).slice(2)+"-"+Date.now().toString(36);try{localStorage.setItem("codeman:clientId",this._clientId)}catch{}}try{const e=localStorage.getItem("codeman:pendingInput");if(!e)return;const t=JSON.parse(e);if(t&&t.seqs)for(const[s,n]of Object.entries(t.seqs))Number.isFinite(n)&&this._seqCounters.set(s,n);if(t&&t.pending)for(const[s,n]of Object.entries(t.pending))Array.isArray(n)&&n.length&&this._pendingDeliveries.set(s,n.filter(i=>i&&typeof i.data=="string"&&Number.isInteger(i.seq)).map(i=>({seq:i.seq,data:i.data,useMux:!!i.useMux,ts:i.ts||Date.now(),tries:0,sentAt:0})))}catch{}}_persistReliableState(){this._persistReliableTimer||(this._persistReliableTimer=setTimeout(()=>{this._persistReliableTimer=null,this._persistReliableNow()},250))}_persistReliableNow(){this._persistReliableTimer&&(clearTimeout(this._persistReliableTimer),this._persistReliableTimer=null);try{const e={};for(const[i,o]of this._seqCounters)e[i]=o;const t={};let s=0;for(const[i,o]of this._pendingDeliveries)if(o.length){t[i]=o.map(a=>({seq:a.seq,data:a.data,useMux:a.useMux,ts:a.ts,tries:a.tries}));for(const a of o)s+=a.data.length}const n=s>this._reliableMaxBytes?{seqs:e}:{seqs:e,pending:t};localStorage.setItem("codeman:pendingInput",JSON.stringify(n))}catch{}}_computeConnectionDescriptor(){const{bytes:e,count:t}=this._pendingBytes(),s=t>0,i=e>4,o=l=>l<1024?`${l}B`:`${(l/1024).toFixed(1)}KB`,a=i?` \xB7 ${o(e)} queued`:"";if(!this.isOnline||this._connectionStatus==="offline")return{display:"flex",dotClass:"connection-dot offline",text:i?`Offline (${o(e)} queued)`:"Offline",title:"No network connection"};if(this.activeSessionId){let l,r,d;switch(this._wsState){case"connected":l="connected",r="WS",d="Terminal connected over WebSocket";break;case"fallback":l="fallback",r="HTTP",d="WebSocket unavailable \u2014 input sent over HTTP";break;case"reconnecting":l="reconnecting",r="WS\u2026",d="Reconnecting WebSocket";break;default:l="reconnecting",r="WS\u2026",d="Connecting WebSocket";break}return{display:"flex",dotClass:`connection-dot ${l}`,text:`${r}${a}`,title:d}}return this._connectionStatus==="reconnecting"||this._connectionStatus==="disconnected"?{display:"flex",dotClass:"connection-dot reconnecting",text:i?`Reconnecting (${o(e)} queued)`:"Reconnecting...",title:"Reconnecting to server"}:s?{display:"flex",dotClass:"connection-dot draining",text:i?`Sending ${o(e)}...`:"Sending...",title:"Delivering queued input"}:{display:"none",dotClass:"",text:"",title:""}}_updateConnectionIndicator(){const e=this.$("connectionIndicator"),t=this.$("connectionDot"),s=this.$("connectionText");if(!e||!t||!s)return;const n=this._computeConnectionDescriptor(),i=this._lastIndicatorDescriptor;i&&i.display===n.display&&i.dotClass===n.dotClass&&i.text===n.text&&i.title===n.title||(this._lastIndicatorDescriptor=n,e.style.display=n.display,n.display!=="none"&&(t.className=n.dotClass,s.textContent=n.text,e.title=n.title))}setupOnlineDetection(){window.addEventListener("online",()=>{this.isOnline=!0,this.reconnectAttempts=0,this.connectSSE(),this._redeliverSweep()}),window.addEventListener("offline",()=>{this.isOnline=!1,this.setConnectionStatus("offline")})}_updateCjkInputState(){const e=document.getElementById("cjkInput");if(!e)return;const t=this.loadAppSettingsFromStorage(),s=this.getDefaultSettings?.()||{},i=(this._serverCjkOverride||(t.cjkInputEnabled??s.cjkInputEnabled??!1))&&!!this.activeSessionId;e.classList.toggle("cjk-input-visible",!!i),document.body.classList.toggle("cjk-input-visible",!!i),e.style.display=i?"block":"none",e.setAttribute("aria-hidden",i?"false":"true"),i||(window.cjkActive=!1),typeof KeyboardHandler<"u"&&KeyboardHandler.updateLayoutForKeyboard()}_resetAllAppState(){this.sessions.clear(),this.ralphStates.clear(),this.terminalBuffers.clear(),this.terminalBufferCache.clear(),this._xtermSnapshots?.clear(),this.projectInsights.clear(),this.teams.clear(),this.teamTasks.clear();for(const e of this.idleTimers.values())clearTimeout(e);if(this.idleTimers.clear(),this._clearTimer("flickerFilterTimeout"),this.flickerFilterBuffer="",this.flickerFilterActive=!1,this._clearTimer("syncWaitTimeout"),this.pendingWrites=[],this.writeFrameScheduled=!1,this._isLoadingBuffer=!1,this._loadBufferQueue=null,this._bufferLoadOwner=null,this._chunkedWriteGen=(this._chunkedWriteGen||0)+1,this._localEchoOverlay?.rerender(),this.pendingHooks.clear(),this._parentNameCache&&this._parentNameCache.clear(),this.subagentActivity.clear(),this.subagentToolResults.clear(),this.workflowRuns&&this.workflowRuns.clear(),this.workflowRunDetails&&this.workflowRunDetails.clear(),this.activeWorkflowRunId=null,this.activeWorkflowPhaseIndex=null,MobileDetection.cleanup(),KeyboardHandler.cleanup(),MobileDetection.init(),KeyboardHandler.init(),this.tabAlerts.clear(),this.attachmentHistoryCounts.clear(),this._shownCompletions&&this._shownCompletions.clear(),this.notificationManager?.titleFlashInterval&&(clearInterval(this.notificationManager.titleFlashInterval),this.notificationManager.titleFlashInterval=null),this.notificationManager?.groupingMap){for(const{timeout:e}of this.notificationManager.groupingMap.values())clearTimeout(e);this.notificationManager.groupingMap.clear()}this.terminalResizeObserver&&(this.terminalResizeObserver.disconnect(),this.terminalResizeObserver=null),this.planLoadingTimer&&(clearInterval(this.planLoadingTimer),this.planLoadingTimer=null),this.timerCountdownInterval&&(clearInterval(this.timerCountdownInterval),this.timerCountdownInterval=null),this.runSummaryAutoRefreshTimer&&(clearInterval(this.runSummaryAutoRefreshTimer),this.runSummaryAutoRefreshTimer=null)}handleInit(e){this._clearTimer("_initFallbackTimer");const t=++this._initGeneration;if(this._serverCjkOverride=e.inputCjkForm||!1,this._updateCjkInputState(),e.planUsage&&this.updatePlanUsageChip(e.planUsage),e.version){const i=this.$("versionDisplay"),o=this.$("headerVersion");i&&(i.textContent=`v${e.version}`,i.title=`Codeman v${e.version}`),o&&(o.textContent=`v${e.version}`,o.title=`Codeman v${e.version}`)}VoiceInput.cleanup(),this._resetAllAppState(),e.sessions.forEach(i=>{this.sessions.set(i.id,i),(i.ralphLoop||i.ralphTodos)&&!this.ralphClosedSessions.has(i.id)&&this.ralphStates.set(i.id,{loop:i.ralphLoop||null,todos:i.ralphTodos||[]})});try{localStorage.removeItem("codeman-tab-meta")}catch{}if(Array.isArray(e.sessionOrder)&&e.sessionOrder.length)try{localStorage.setItem("codeman-session-order",JSON.stringify(e.sessionOrder))}catch{}this.syncSessionOrder(),e.respawnStatus?this.respawnStatus=e.respawnStatus:this.respawnStatus={},this.respawnTimers={},this.respawnCountdownTimers={},this.respawnActionLogs={},e.globalStats&&(this.globalStats=e.globalStats),this.totalCost=e.sessions.reduce((i,o)=>i+(o.totalCost||0),0),this.totalCost+=e.scheduledRuns.reduce((i,o)=>i+(o.totalCost||0),0);const s=e.scheduledRuns.find(i=>i.status==="running");if(s&&(this.currentRun=s,this.showTimer()),this.updateCost(),this.renderSessionTabs(),this.sessions.size>0?this.startSystemStatsPolling():this.stopSystemStatsPolling(),this.cleanupAllFloatingWindows(),e.subagents&&(this.subagents.clear(),this.subagentActivity.clear(),this.subagentToolResults.clear(),e.subagents.forEach(i=>{this.subagents.set(i.agentId,i)}),this.renderSubagentPanel(),this.subagentParentMap.clear(),this.loadSubagentParentMap().then(()=>{for(const[i,o]of this.subagentParentMap){const a=this.subagents.get(i);if(a&&this.sessions.has(o)){a.parentSessionId=o;const l=this.sessions.get(o);l&&(a.parentSessionName=this.getSessionName(l)),this.subagents.set(i,a)}}for(const[i]of this.subagents)this.subagentParentMap.has(i)||this.findParentSessionForSubagent(i);this.restoreSubagentWindowStates()})),e.workflowRuns&&this.seedWorkflowRuns(e.workflowRuns),t!==this._initGeneration)return;if(this.isSoloWindow){this._applySoloMode();return}const n=this.activeSessionId;if(this.activeSessionId=null,this.sessionOrder.length>0){let i=n;if(!i||!this.sessions.has(i))try{i=localStorage.getItem("codeman-active-session")}catch{}i&&this.sessions.has(i)?this.selectSession(i):this.selectSession(this.sessionOrder[0])}}async loadState(){try{const t=await(await fetch("/api/status")).json();this.handleInit(t?.data??{})}catch(e){console.error("Failed to load state:",e)}}_debouncedCall(e,t,s=100){this._debounceTimers[e]&&clearTimeout(this._debounceTimers[e]),this._debounceTimers[e]=setTimeout(()=>{this._debounceTimers[e]=null,t.call(this)},s)}renderSessionTabs(){this._inlineRenameActive||this._debouncedCall("sessionTabs",this._renderSessionTabsImmediate)}_updateActiveTabImmediate(e){const t=this.$("sessionTabs");if(!t)return;const s=t.querySelectorAll(".session-tab[data-id]");for(const n of s)n.dataset.id===e?n.classList.add("active"):n.classList.remove("active")}_setTerminalLoadState(e,t,s){this.terminalLoadStates.set(e,{generation:t,phase:s}),this._updateTerminalLoadTab(e)}_clearTerminalLoadState(e,t){const s=this.terminalLoadStates.get(e);s&&s.generation!==t||(this.terminalLoadStates.delete(e),this._updateTerminalLoadTab(e))}_updateTerminalLoadTab(e){const t=this.$("sessionTabs")?.querySelector(`.session-tab[data-id="${e}"]`);if(!t)return;const s=this.terminalLoadStates.get(e);if(t.classList.toggle("tab-loading",!!s),s){if(t.setAttribute("aria-busy","true"),t.dataset.loadPhase=s.phase,!t.querySelector(".tab-load-spinner")){const n=document.createElement("span");n.className="tab-load-spinner",n.setAttribute("aria-hidden","true");const i=t.querySelector(".tab-number");i?i.insertAdjacentElement("afterend",n):t.insertBefore(n,t.firstChild)}}else t.setAttribute("aria-busy","false"),delete t.dataset.loadPhase,t.querySelector(".tab-load-spinner")?.remove()}_renderSessionTabsImmediate(){if(this._inlineRenameActive)return;const e=this.$("sessionTabs"),t=e.querySelectorAll(".session-tab[data-id]"),s=new Set([...t].map(r=>r.dataset.id)),n=new Set(this.sessions.keys()),i=[...e.querySelectorAll(".session-tab[data-webview-id]")].map(r=>r.dataset.webviewId),o=(this.webviewOrder||[]).filter(r=>this.webviews?.has(r)),a=i.length===o.length&&i.every((r,d)=>r===o[d]);if(s.size===n.size&&[...s].every(r=>n.has(r))&&a)for(const[r,d]of this.sessions){const c=e.querySelector(`.session-tab[data-id="${r}"]`);if(!c)continue;const u=r===this.activeSessionId&&!this.activeWebviewId,p=d.status||"idle",_=this.getSessionName(d),v=d.taskStats||{running:0,total:0},g=v.running>0,C=this.terminalLoadStates.get(r);if(u&&!c.classList.contains("active")?c.classList.add("active"):!u&&c.classList.contains("active")&&c.classList.remove("active"),c.classList.toggle("tab-loading",!!C),C){if(c.setAttribute("aria-busy","true"),c.dataset.loadPhase=C.phase,!c.querySelector(".tab-load-spinner")){const f=document.createElement("span");f.className="tab-load-spinner",f.setAttribute("aria-hidden","true");const b=c.querySelector(".tab-number");b?b.insertAdjacentElement("afterend",f):c.insertBefore(f,c.firstChild)}}else c.setAttribute("aria-busy","false"),delete c.dataset.loadPhase,c.querySelector(".tab-load-spinner")?.remove();const k=this.tabAlerts.get(r),L=k==="action",E=k==="idle",A=c.classList.contains("tab-alert-action"),S=c.classList.contains("tab-alert-idle");if(L&&!A?(c.classList.add("tab-alert-action"),c.classList.remove("tab-alert-idle")):E&&!S?(c.classList.add("tab-alert-idle"),c.classList.remove("tab-alert-action")):!k&&(A||S)&&c.classList.remove("tab-alert-action","tab-alert-idle"),!c.querySelector(".tab-number")){const f=this.sessionOrder.indexOf(r);if(f>=0&&f<9){const b=document.createElement("span");b.className="tab-number",b.textContent=String(f+1),c.insertBefore(b,c.firstChild)}}const T=c.querySelector(".tab-status");T&&!T.classList.contains(p)&&(T.className=`tab-status ${p}`);const y=c.querySelector(".tab-name");if(y&&y.textContent!==_){const f=parseSessionPrefix(_);f&&f.suffix?y.innerHTML='<span class="tab-prefix">'+escapeHtml(f.prefix)+'</span><span class="tab-suffix">: '+escapeHtml(f.suffix)+"</span>":y.textContent=_}const m=c.querySelector(".tab-badge");if(g)if(m)m.textContent!==String(v.running)&&(m.textContent=v.running);else{this._fullRenderSessionTabs();return}else if(m){this._fullRenderSessionTabs();return}const w=c.querySelector(".tab-subagent-badge"),O=this.minimizedSubagents.get(r),R=O?.size||0;if(R>0&&w){const f=w.querySelector(".subagent-label"),b=R===1?"AGENT":`AGENTS (${R})`;f&&f.textContent!==b&&(f.textContent=b);const I=w.querySelector(".subagent-dropdown");if(I){const x=this.renderSubagentTabBadge(r,O),N=document.createElement("div");N.innerHTML=x;const D=N.querySelector(".subagent-dropdown");D&&(I.innerHTML=D.innerHTML)}}else if(R>0&&!w){const f=this.renderSubagentTabBadge(r,O),b=c.querySelector(".tab-gear");b&&b.insertAdjacentHTML("beforebegin",f)}else R===0&&w&&w.remove()}else this._fullRenderSessionTabs();this.updateTabOverflowMode(),this._applyTabEntrances?.(),this._refreshMobileOverviewIfVisible?.()}updateTabOverflowMode(){const e=this.$("sessionTabs");if(!e)return;const t=MobileDetection.getDeviceType(),s=this.loadAppSettingsFromStorage(),n=this.getDefaultSettings(),i=t==="desktop"?s.tabTwoRows??n.tabTwoRows??!1:!1;if(i||t!=="desktop"){e.classList.remove("tabs-auto-wrap");return}e.classList.remove("tabs-auto-wrap");const o=window.CodemanTabOverflow?.shouldAutoWrapTabs?window.CodemanTabOverflow.shouldAutoWrapTabs({deviceType:t,manualTwoRows:i,tabCount:this.sessions.size,scrollWidth:e.scrollWidth,clientWidth:e.clientWidth}):e.scrollWidth>e.clientWidth+1;e.classList.toggle("tabs-auto-wrap",o)}_fullRenderSessionTabs(){if(this._inlineRenameActive)return;const e=this.$("sessionTabs");document.querySelectorAll("body > .subagent-dropdown").forEach(i=>i.remove()),this.cancelHideSubagentDropdown();const t=[];let s=this.sessionOrder;MobileDetection.getDeviceType()==="mobile"&&this.activeSessionId&&(s=[this.activeSessionId,...this.sessionOrder.filter(i=>i!==this.activeSessionId)]);let n=0;for(const i of s){const o=this.sessions.get(i);if(!o)continue;const a=i===this.activeSessionId&&!this.activeWebviewId,l=o.status||"idle",r=this.getSessionName(o),d=o.mode||"claude",c=o.color||"default",u=o.taskStats||{running:0,total:0},p=u.running>0,_=this.tabAlerts.get(i),v=_==="action"?" tab-alert-action":_==="idle"?" tab-alert-idle":"",g=this.terminalLoadStates.get(i),C=this.minimizedSubagents.get(i),L=(C?.size||0)>0?this.renderSubagentTabBadge(i,C):"",E=this.renderUltracodeTabBadge?this.renderUltracodeTabBadge(i):"",A=o.workingDir&&o.workingDir.split("/").pop()||"",T=(this._tallTabsEnabled??!1)&&o.name&&A&&A!==r;t.push(`<div class="session-tab ${a?"active":""}${v}${g?" tab-loading":""}" data-id="${i}" data-color="${c}" ${g?`data-load-phase="${escapeHtml(g.phase)}"`:""} onclick="app.handleSessionTabClick(event, ${escapeHtml(JSON.stringify(i))})" oncontextmenu="event.preventDefault(); app.startInlineRename(${escapeHtml(JSON.stringify(i))})" tabindex="0" role="tab" aria-selected="${a?"true":"false"}" aria-busy="${g?"true":"false"}" aria-label="${escapeHtml(r)} session" ${o.workingDir?`title="${escapeHtml(o.workingDir)}"`:""}>
|
|
17
17
|
${n<9?'<span class="tab-number">'+(n+1)+"</span>":""}
|
|
18
18
|
${g?'<span class="tab-load-spinner" aria-hidden="true"></span>':""}
|
|
19
19
|
<span class="tab-status ${l}" aria-hidden="true"></span>
|
|
@@ -25,14 +25,14 @@ Weekly limit: ${i??"\u2014"}% used (resets ${l(e.sevenDay)})`}_onScheduledCreate
|
|
|
25
25
|
</span>
|
|
26
26
|
${T?`<span class="tab-folder">\u{1F4C1} ${escapeHtml(A)}</span>`:""}
|
|
27
27
|
</span>
|
|
28
|
-
${
|
|
28
|
+
${p?`<span class="tab-badge" onclick="event.stopPropagation(); app.toggleTaskPanel()" aria-label="${u.running} running tasks">${u.running}</span>`:""}
|
|
29
29
|
${L}
|
|
30
30
|
${E}
|
|
31
31
|
<span class="tab-gear" onclick="event.stopPropagation(); app.openSessionOptions(${escapeHtml(JSON.stringify(i))})" title="Session options" aria-label="Session options" tabindex="0">⚙</span>
|
|
32
32
|
<span class="tab-detach" onclick="event.stopPropagation(); app.detachSession(${escapeHtml(JSON.stringify(i))})" title="Open in a new window" aria-label="Open session in a new window" tabindex="0">⧉</span>
|
|
33
33
|
<span class="tab-close" onclick="event.stopPropagation(); app.requestCloseSession(${escapeHtml(JSON.stringify(i))})" title="Close session" aria-label="Close session" tabindex="0">×</span>
|
|
34
|
-
</div>`),n++}t.push(this.renderWebviewTabs?this.renderWebviewTabs(n):""),e.innerHTML=t.join(""),this.setupTabDragHandlers(),this.setupTabKeyboardNavigation(e),this.updateConnectionLines(),this.updateTabOverflowMode(),this._applyTabEntrances?.()}setupTabKeyboardNavigation(e){this._tabKeydownHandler&&e.removeEventListener("keydown",this._tabKeydownHandler),this._tabKeydownHandler=t=>{if(!["ArrowLeft","ArrowRight","Home","End","Enter"," "].includes(t.key))return;const s=[...e.querySelectorAll(".session-tab")],n=s.indexOf(document.activeElement);if((t.key==="Enter"||t.key===" ")&&n>=0){t.preventDefault();const o=s[n].dataset.id;this.selectSession(o,{forceReload:!0});return}if(n<0)return;let i;switch(t.key){case"ArrowLeft":i=n>0?n-1:s.length-1;break;case"ArrowRight":i=n<s.length-1?n+1:0;break;case"Home":i=0;break;case"End":i=s.length-1;break;default:return}t.preventDefault(),s[i]?.focus()},e.addEventListener("keydown",this._tabKeydownHandler)}handleSessionTabClick(e,t){return e?.preventDefault?.(),!(typeof KeyboardHandler<"u"&&KeyboardHandler.keyboardVisible===!0)&&MobileDetection.isTouchDevice()&&document.activeElement?.blur?.(),this.selectSession(t,{forceReload:!0})}syncSessionOrder(){const e=new Set(this.sessions.keys()),s=this.loadSessionOrder().filter(o=>e.has(o)),n=new Set(s),i=[...e].filter(o=>!n.has(o));this.sessionOrder=[...s,...i]}loadSessionOrder(){try{const e=localStorage.getItem("codeman-session-order");return e?JSON.parse(e):[]}catch{return[]}}saveSessionOrder(){try{localStorage.setItem("codeman-session-order",JSON.stringify(this.sessionOrder))}catch{}const e=[...this.sessionOrder];this._debouncedCall("saveSessionOrderServer",()=>{fetch("/api/session-order",{method:"PUT",headers:{"Content-Type":"application/json"},body:JSON.stringify({order:e})}).catch(()=>{})},400)}_onSessionOrderChanged(e){if(!e||!Array.isArray(e.order))return;try{localStorage.setItem("codeman-session-order",JSON.stringify(e.order))}catch{}const t=JSON.stringify(this.sessionOrder);this.syncSessionOrder(),JSON.stringify(this.sessionOrder)!==t&&this._fullRenderSessionTabs()}setupTabDragHandlers(){const e=this.$("sessionTabs");e.querySelectorAll(".session-tab[data-id]").forEach(s=>{s.setAttribute("draggable","true"),s.addEventListener("dragstart",n=>{this.draggedTabId=s.dataset.id,s.classList.add("dragging"),n.dataTransfer.effectAllowed="move",n.dataTransfer.setData("text/plain",s.dataset.id)}),s.addEventListener("dragend",()=>{s.classList.remove("dragging"),this.draggedTabId=null,e.querySelectorAll(".session-tab").forEach(n=>{n.classList.remove("drag-over-left","drag-over-right")})}),s.addEventListener("dragover",n=>{if(n.preventDefault(),!this.draggedTabId||this.draggedTabId===s.dataset.id)return;n.dataTransfer.dropEffect="move";const i=s.getBoundingClientRect(),o=i.left+i.width/2,a=n.clientX<o;s.classList.toggle("drag-over-left",a),s.classList.toggle("drag-over-right",!a)}),s.addEventListener("dragleave",()=>{s.classList.remove("drag-over-left","drag-over-right")}),s.addEventListener("drop",n=>{if(n.preventDefault(),s.classList.remove("drag-over-left","drag-over-right"),!this.draggedTabId||this.draggedTabId===s.dataset.id)return;const i=s.dataset.id,o=this.draggedTabId,a=s.getBoundingClientRect(),l=a.left+a.width/2,r=n.clientX<l,d=this.sessionOrder.indexOf(o);let c=this.sessionOrder.indexOf(i);d===-1||c===-1||(this.sessionOrder.splice(d,1),c=this.sessionOrder.indexOf(i),c!==-1&&(r?this.sessionOrder.splice(c,0,o):this.sessionOrder.splice(c+1,0,o),this.saveSessionOrder(),this._fullRenderSessionTabs()))})})}moveActiveTabLeft(){if(!this.activeSessionId)return;const e=this.sessionOrder.indexOf(this.activeSessionId);e<=0||([this.sessionOrder[e-1],this.sessionOrder[e]]=[this.sessionOrder[e],this.sessionOrder[e-1]],this.saveSessionOrder(),this._fullRenderSessionTabs())}moveActiveTabRight(){if(!this.activeSessionId)return;const e=this.sessionOrder.indexOf(this.activeSessionId);e===-1||e>=this.sessionOrder.length-1||([this.sessionOrder[e],this.sessionOrder[e+1]]=[this.sessionOrder[e+1],this.sessionOrder[e]],this.saveSessionOrder(),this._fullRenderSessionTabs())}getShortId(e){if(!e)return"";let t=this._shortIdCache.get(e);return t||(t=e.slice(0,8),this._shortIdCache.set(e,t)),t}getSessionName(e){return e.name?e.name:e.workingDir?e.workingDir.split("/").pop()||e.workingDir:this.getShortId(e.id)}_notifySession(e,t,s,n,i){const o=this.sessions.get(e);this.notificationManager?.notify({urgency:t,category:s,sessionId:e,sessionName:o?.name||this.getShortId(e),title:n,message:i})}_isUsableXtermSnapshot(e){return!e||typeof e!="string"||e.length<8?!1:e.replace(/\x1b\][^\x07\x1b]*(?:\x07|\x1b\\)/g,"").replace(/\x1b\[[0-9;?]*[ -/]*[@-~]/g,"").replace(/\x1b[()][0-2A-Z]/g,"").replace(/[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]/g,"").trim().length>=3}_persistXtermSnapshot(e,t){const s="codeman-xs-",i=()=>Object.keys(localStorage).filter(o=>o.startsWith(s)&&o!==e);try{if(localStorage.getItem(e)===null){const o=new Set(Array.from(this.sessions?.keys?.()||[])),a=i().sort((l,r)=>Number(o.has(l.slice(s.length)))-Number(o.has(r.slice(s.length))));for(;a.length>=10;)localStorage.removeItem(a.shift())}try{localStorage.setItem(e,t)}catch{for(const a of i()){localStorage.removeItem(a);try{localStorage.setItem(e,t);return}catch{}}try{localStorage.removeItem(e)}catch{}}}catch{}}_cleanupPreviousSession(e){const t=this.activeSessionId?this.sessions?.get?.(this.activeSessionId):null;if(this.activeSessionId&&t?.mode!=="shell"&&this._serializeAddon&&this._xtermSnapshots)try{const s=this._serializeAddon.serialize({scrollback:1e3});if(this._isUsableXtermSnapshot(s)){if(this._xtermSnapshots.delete(this.activeSessionId),this._xtermSnapshots.set(this.activeSessionId,s),this._xtermSnapshots.size>20){const n=this._xtermSnapshots.keys().next().value;this._xtermSnapshots.delete(n)}s.length<256*1024&&this._persistXtermSnapshot(`codeman-xs-${this.activeSessionId}`,s)}else{this._xtermSnapshots.delete(this.activeSessionId);try{localStorage.removeItem(`codeman-xs-${this.activeSessionId}`)}catch{}}}catch{}if(this._disconnectWs(),typeof CjkInput<"u")CjkInput.clear();else{const s=document.getElementById("cjkInput");s&&(s.value="")}this._clearTimer("flickerFilterTimeout"),this.flickerFilterBuffer="",this.flickerFilterActive=!1,this._tabCompletionSessionId=null,this._tabCompletionRetries=0,this._tabCompletionBaseText=null,this._clearTimer("_tabCompletionFallback"),this._clearTimer("_clientDropRecoveryTimer"),this._clearTimer("syncWaitTimeout"),this.pendingWrites=[],this.writeFrameScheduled=!1,this._isLoadingBuffer=!1,this._loadBufferQueue=null,this._bufferLoadOwner=null,this._chunkedWriteGen=(this._chunkedWriteGen||0)+1;try{const s=this.terminal?._core?._compositionHelper;if(s?._isComposing){s._isComposing=!1;const n=this.terminal?.element?.querySelector(".xterm-helper-textarea");n&&n.dispatchEvent(new CompositionEvent("compositionend",{data:""}))}}catch{}if(this.activeSessionId){const s=this._localEchoOverlay?.pendingText||"",n=this._localEchoOverlay?.getFlushed()?.count||0,i=this._localEchoOverlay?.getFlushed()?.text||"";s&&this._sendInputAsync(this.activeSessionId,s);const o=n+s.length;o>0&&(this._flushedOffsets||(this._flushedOffsets=new Map),this._flushedTexts||(this._flushedTexts=new Map),this._flushedOffsets.set(this.activeSessionId,o),this._flushedTexts.set(this.activeSessionId,i+s))}this._localEchoOverlay?.clear(),this._localEchoOverlay&&!this._flushedOffsets?.has(e)&&this._localEchoOverlay.suppressBufferDetection()}_resetTerminalForReplay(){this.terminal.reset(),this.terminal.write("\x1B[3J\x1B[H\x1B[2J")}_shouldFocusTerminalForTabSwitch(){return typeof MobileDetection>"u"||!MobileDetection.isTouchDevice()?!0:typeof KeyboardHandler<"u"&&KeyboardHandler.keyboardVisible}async selectSession(e,t={}){if(!this.isSoloWindow&&this.detachedSessions.has(e)&&this._raiseDetached(e))return;const s=t?.forceReload===!0;if(this.activeSessionId===e&&!s)return;if(this.activeSessionId===e&&s){this.terminalBufferCache?.delete(e),this._xtermSnapshots?.delete(e);try{localStorage.removeItem(`codeman-xs-${e}`)}catch{}this._clearTimer("syncWaitTimeout"),this.pendingWrites=[],this.writeFrameScheduled=!1,this._isLoadingBuffer=!1,this._loadBufferQueue=null,this._chunkedWriteGen=(this._chunkedWriteGen||0)+1,this.activeSessionId=null}const n=this._shouldFocusTerminalForTabSwitch();n&&this.terminal&&this.terminal.focus();const i=performance.now(),o=this.sessions.get(e)?.name||e.slice(0,8);_crashDiag.log(`SELECT: ${o}`),console.log(`[CRASH-DIAG] selectSession START: ${e.slice(0,8)}`);const a=++this._selectGeneration;if(this._setTerminalLoadState(e,a,"resizing"),a!==this._selectGeneration){this._clearTerminalLoadState(e,a);return}this._hideWebviewLayer?.(),this._cleanupPreviousSession(e),this.activeSessionId=e;try{localStorage.setItem("codeman-active-session",e)}catch{}this._updateSseSubscription(e),this.hideWelcome(),this.playTerminalEntrance?.(e),this.clearPendingHooks(e,"idle_prompt"),this._updateActiveTabImmediate(e),this.renderSessionTabs(),this.updateAttachmentHistoryBadge?.(),this.attachmentHistoryDrawerOpen&&this.loadAttachmentHistory?.(e),this._updateLocalEchoState(),this._flushedOffsets?.has(e)&&this._localEchoOverlay&&this._localEchoOverlay.setFlushed(this._flushedOffsets.get(e),this._flushedTexts?.get(e)||"",!1);const l=document.querySelector(`.session-tab.active[data-id="${e}"]`);l&&(l.classList.add("tab-glow"),l.addEventListener("animationend",()=>l.classList.remove("tab-glow"),{once:!0}));const r=this.sessions.get(e);if(this.currentSessionWorkingDir=r?.workingDir||null,r&&r.pid===null)if(r.respawnBlocked){const u=r.name||"Session";if(window.confirm(`${u} was stopped after crashing repeatedly. Restart it?`))try{await fetch(`/api/sessions/${e}/interactive`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({clearBreaker:!0})}),r.respawnBlocked=!1,r.status="busy"}catch(S){console.error("Failed to restart crash-looped session:",S)}}else try{const u=r.mode==="shell"?`/api/sessions/${e}/shell`:`/api/sessions/${e}/interactive`;await fetch(u,{method:"POST"}),r.status="busy"}catch(u){console.error("Failed to attach to restored session:",u)}this._restoringFlushedState=!0;const d=this._beginBufferLoad(a);let c=!1;try{this.fitAddon&&this.fitAddon.fit();const u=await this.sendResize(e,{forceHttp:!0}).catch(()=>!1);if(this._isStaleSelect(a)){this._clearTerminalLoadState(e,a);return}let S=this._xtermSnapshots?.get(e);if(S&&!this._isUsableXtermSnapshot(S)&&(this._xtermSnapshots?.delete(e),S=null),!S)try{const p=localStorage.getItem(`codeman-xs-${e}`);p&&this._isUsableXtermSnapshot(p)?(S=p,this._xtermSnapshots?.delete(e),this._xtermSnapshots?.set(e,p)):p&&localStorage.removeItem(`codeman-xs-${e}`)}catch{}const m=r&&(r.status==="busy"||r.status==="working");let v=!1;if(S&&!m&&r?.mode!=="shell"){if(_crashDiag.log(`SNAPSHOT_RESTORE: ${(S.length/1024).toFixed(0)}KB`),this._setTerminalLoadState(e,a,"replaying"),this._resetTerminalForReplay(),await new Promise(p=>this.terminal.write(S,p)),this._isStaleSelect(a)){this._clearTerminalLoadState(e,a);return}this.scrollToLastNonEmptyLine(),_crashDiag.log("SNAPSHOT_RESTORE_DONE"),v=!0}const g=this.terminalBufferCache.get(e);let C=!1;if(g&&!m&&!v){if(_crashDiag.log(`CACHE_WRITE: ${(g.length/1024).toFixed(0)}KB`),this._setTerminalLoadState(e,a,"replaying"),this._resetTerminalForReplay(),await this.chunkedTerminalWrite(g,TERMINAL_CHUNK_SIZE,d),this._isStaleSelect(a)){this._clearTerminalLoadState(e,a);return}this.terminal.scrollToBottom(),_crashDiag.log("CACHE_DONE")}else m&&(this._resetTerminalForReplay(),C=!0,_crashDiag.log("CACHE_SKIP_BUSY"));if(r?.mode!=="shell"&&u&&(await new Promise(p=>setTimeout(p,TUI_REDRAW_SETTLE_MS)),this._isStaleSelect(a))){this._clearTerminalLoadState(e,a);return}this._setTerminalLoadState(e,a,"fetching"),_crashDiag.log("FETCH_START");const k=this._initialFullBufferLoad===!0;this._initialFullBufferLoad=!1;const L=await fetch(k?`/api/sessions/${e}/terminal?full=1`:`/api/sessions/${e}/terminal?tail=${TERMINAL_TAIL_SIZE}`);if(this._isStaleSelect(a)){this._clearTerminalLoadState(e,a);return}const E=(await L.json())?.data??{};if(_crashDiag.log(`FETCH_DONE: ${E.terminalBuffer?(E.terminalBuffer.length/1024).toFixed(0)+"KB":"empty"} truncated=${E.truncated}`),E.terminalBuffer){if(v||C||E.terminalBuffer!==g){if(_crashDiag.log(`REWRITE: ${(E.terminalBuffer.length/1024).toFixed(0)}KB`),this._setTerminalLoadState(e,a,"replaying"),this._resetTerminalForReplay(),E.truncated&&this.terminal.write(`\x1B[90m... (earlier output truncated for performance) ...\x1B[0m\r
|
|
34
|
+
</div>`),n++}t.push(this.renderWebviewTabs?this.renderWebviewTabs(n):""),e.innerHTML=t.join(""),this.setupTabDragHandlers(),this.setupTabKeyboardNavigation(e),this.updateConnectionLines(),this.updateTabOverflowMode(),this._applyTabEntrances?.()}setupTabKeyboardNavigation(e){this._tabKeydownHandler&&e.removeEventListener("keydown",this._tabKeydownHandler),this._tabKeydownHandler=t=>{if(!["ArrowLeft","ArrowRight","Home","End","Enter"," "].includes(t.key))return;const s=[...e.querySelectorAll(".session-tab")],n=s.indexOf(document.activeElement);if((t.key==="Enter"||t.key===" ")&&n>=0){t.preventDefault();const o=s[n].dataset.id;this.selectSession(o,{forceReload:!0});return}if(n<0)return;let i;switch(t.key){case"ArrowLeft":i=n>0?n-1:s.length-1;break;case"ArrowRight":i=n<s.length-1?n+1:0;break;case"Home":i=0;break;case"End":i=s.length-1;break;default:return}t.preventDefault(),s[i]?.focus()},e.addEventListener("keydown",this._tabKeydownHandler)}handleSessionTabClick(e,t){return e?.preventDefault?.(),!(typeof KeyboardHandler<"u"&&KeyboardHandler.keyboardVisible===!0)&&MobileDetection.isTouchDevice()&&document.activeElement?.blur?.(),this.selectSession(t,{forceReload:!0})}syncSessionOrder(){const e=new Set(this.sessions.keys()),s=this.loadSessionOrder().filter(o=>e.has(o)),n=new Set(s),i=[...e].filter(o=>!n.has(o));this.sessionOrder=[...s,...i]}loadSessionOrder(){try{const e=localStorage.getItem("codeman-session-order");return e?JSON.parse(e):[]}catch{return[]}}saveSessionOrder(){try{localStorage.setItem("codeman-session-order",JSON.stringify(this.sessionOrder))}catch{}const e=[...this.sessionOrder];this._debouncedCall("saveSessionOrderServer",()=>{fetch("/api/session-order",{method:"PUT",headers:{"Content-Type":"application/json"},body:JSON.stringify({order:e})}).catch(()=>{})},400)}_onSessionOrderChanged(e){if(!e||!Array.isArray(e.order))return;try{localStorage.setItem("codeman-session-order",JSON.stringify(e.order))}catch{}const t=JSON.stringify(this.sessionOrder);this.syncSessionOrder(),JSON.stringify(this.sessionOrder)!==t&&this._fullRenderSessionTabs()}setupTabDragHandlers(){const e=this.$("sessionTabs");e.querySelectorAll(".session-tab[data-id]").forEach(s=>{s.setAttribute("draggable","true"),s.addEventListener("dragstart",n=>{this.draggedTabId=s.dataset.id,s.classList.add("dragging"),n.dataTransfer.effectAllowed="move",n.dataTransfer.setData("text/plain",s.dataset.id)}),s.addEventListener("dragend",()=>{s.classList.remove("dragging"),this.draggedTabId=null,e.querySelectorAll(".session-tab").forEach(n=>{n.classList.remove("drag-over-left","drag-over-right")})}),s.addEventListener("dragover",n=>{if(n.preventDefault(),!this.draggedTabId||this.draggedTabId===s.dataset.id)return;n.dataTransfer.dropEffect="move";const i=s.getBoundingClientRect(),o=i.left+i.width/2,a=n.clientX<o;s.classList.toggle("drag-over-left",a),s.classList.toggle("drag-over-right",!a)}),s.addEventListener("dragleave",()=>{s.classList.remove("drag-over-left","drag-over-right")}),s.addEventListener("drop",n=>{if(n.preventDefault(),s.classList.remove("drag-over-left","drag-over-right"),!this.draggedTabId||this.draggedTabId===s.dataset.id)return;const i=s.dataset.id,o=this.draggedTabId,a=s.getBoundingClientRect(),l=a.left+a.width/2,r=n.clientX<l,d=this.sessionOrder.indexOf(o);let c=this.sessionOrder.indexOf(i);d===-1||c===-1||(this.sessionOrder.splice(d,1),c=this.sessionOrder.indexOf(i),c!==-1&&(r?this.sessionOrder.splice(c,0,o):this.sessionOrder.splice(c+1,0,o),this.saveSessionOrder(),this._fullRenderSessionTabs()))})})}moveActiveTabLeft(){if(!this.activeSessionId)return;const e=this.sessionOrder.indexOf(this.activeSessionId);e<=0||([this.sessionOrder[e-1],this.sessionOrder[e]]=[this.sessionOrder[e],this.sessionOrder[e-1]],this.saveSessionOrder(),this._fullRenderSessionTabs())}moveActiveTabRight(){if(!this.activeSessionId)return;const e=this.sessionOrder.indexOf(this.activeSessionId);e===-1||e>=this.sessionOrder.length-1||([this.sessionOrder[e],this.sessionOrder[e+1]]=[this.sessionOrder[e+1],this.sessionOrder[e]],this.saveSessionOrder(),this._fullRenderSessionTabs())}getShortId(e){if(!e)return"";let t=this._shortIdCache.get(e);return t||(t=e.slice(0,8),this._shortIdCache.set(e,t)),t}getSessionName(e){return e.name?e.name:e.workingDir?e.workingDir.split("/").pop()||e.workingDir:this.getShortId(e.id)}_notifySession(e,t,s,n,i){const o=this.sessions.get(e);this.notificationManager?.notify({urgency:t,category:s,sessionId:e,sessionName:o?.name||this.getShortId(e),title:n,message:i})}_isUsableXtermSnapshot(e){return!e||typeof e!="string"||e.length<8?!1:e.replace(/\x1b\][^\x07\x1b]*(?:\x07|\x1b\\)/g,"").replace(/\x1b\[[0-9;?]*[ -/]*[@-~]/g,"").replace(/\x1b[()][0-2A-Z]/g,"").replace(/[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]/g,"").trim().length>=3}_persistXtermSnapshot(e,t){const s="codeman-xs-",i=()=>Object.keys(localStorage).filter(o=>o.startsWith(s)&&o!==e);try{if(localStorage.getItem(e)===null){const o=new Set(Array.from(this.sessions?.keys?.()||[])),a=i().sort((l,r)=>Number(o.has(l.slice(s.length)))-Number(o.has(r.slice(s.length))));for(;a.length>=10;)localStorage.removeItem(a.shift())}try{localStorage.setItem(e,t)}catch{for(const a of i()){localStorage.removeItem(a);try{localStorage.setItem(e,t);return}catch{}}try{localStorage.removeItem(e)}catch{}}}catch{}}_cleanupPreviousSession(e){const t=this.activeSessionId?this.sessions?.get?.(this.activeSessionId):null;if(this.activeSessionId&&t?.mode!=="shell"&&this._serializeAddon&&this._xtermSnapshots)try{const s=this._serializeAddon.serialize({scrollback:1e3});if(this._isUsableXtermSnapshot(s)){if(this._xtermSnapshots.delete(this.activeSessionId),this._xtermSnapshots.set(this.activeSessionId,s),this._xtermSnapshots.size>20){const n=this._xtermSnapshots.keys().next().value;this._xtermSnapshots.delete(n)}s.length<256*1024&&this._persistXtermSnapshot(`codeman-xs-${this.activeSessionId}`,s)}else{this._xtermSnapshots.delete(this.activeSessionId);try{localStorage.removeItem(`codeman-xs-${this.activeSessionId}`)}catch{}}}catch{}if(this._disconnectWs(),typeof CjkInput<"u")CjkInput.clear();else{const s=document.getElementById("cjkInput");s&&(s.value="")}this._clearTimer("flickerFilterTimeout"),this.flickerFilterBuffer="",this.flickerFilterActive=!1,this._tabCompletionSessionId=null,this._tabCompletionRetries=0,this._tabCompletionBaseText=null,this._clearTimer("_tabCompletionFallback"),this._clearTimer("_clientDropRecoveryTimer"),this._clearTimer("syncWaitTimeout"),this.pendingWrites=[],this.writeFrameScheduled=!1,this._isLoadingBuffer=!1,this._loadBufferQueue=null,this._bufferLoadOwner=null,this._chunkedWriteGen=(this._chunkedWriteGen||0)+1;try{const s=this.terminal?._core?._compositionHelper;if(s?._isComposing){s._isComposing=!1;const n=this.terminal?.element?.querySelector(".xterm-helper-textarea");n&&n.dispatchEvent(new CompositionEvent("compositionend",{data:""}))}}catch{}if(this.activeSessionId){const s=this._localEchoOverlay?.pendingText||"",n=this._localEchoOverlay?.getFlushed()?.count||0,i=this._localEchoOverlay?.getFlushed()?.text||"";s&&this._sendInputAsync(this.activeSessionId,s);const o=n+s.length;o>0&&(this._flushedOffsets||(this._flushedOffsets=new Map),this._flushedTexts||(this._flushedTexts=new Map),this._flushedOffsets.set(this.activeSessionId,o),this._flushedTexts.set(this.activeSessionId,i+s))}this._localEchoOverlay?.clear(),this._localEchoOverlay&&!this._flushedOffsets?.has(e)&&this._localEchoOverlay.suppressBufferDetection()}_resetTerminalForReplay(){this.terminal.reset(),this.terminal.write("\x1B[3J\x1B[H\x1B[2J")}_shouldFocusTerminalForTabSwitch(){return typeof MobileDetection>"u"||!MobileDetection.isTouchDevice()?!0:typeof KeyboardHandler<"u"&&KeyboardHandler.keyboardVisible}async selectSession(e,t={}){if(!this.isSoloWindow&&this.detachedSessions.has(e)&&this._raiseDetached(e))return;const s=t?.forceReload===!0;if(this.activeSessionId===e&&!s)return;if(this.activeSessionId===e&&s){this.terminalBufferCache?.delete(e),this._xtermSnapshots?.delete(e);try{localStorage.removeItem(`codeman-xs-${e}`)}catch{}this._clearTimer("syncWaitTimeout"),this.pendingWrites=[],this.writeFrameScheduled=!1,this._isLoadingBuffer=!1,this._loadBufferQueue=null,this._chunkedWriteGen=(this._chunkedWriteGen||0)+1,this.activeSessionId=null}const n=this._shouldFocusTerminalForTabSwitch();n&&this.terminal&&this.terminal.focus();const i=performance.now(),o=this.sessions.get(e)?.name||e.slice(0,8);_crashDiag.log(`SELECT: ${o}`),console.log(`[CRASH-DIAG] selectSession START: ${e.slice(0,8)}`);const a=++this._selectGeneration;if(this._setTerminalLoadState(e,a,"resizing"),a!==this._selectGeneration){this._clearTerminalLoadState(e,a);return}this._hideWebviewLayer?.(),this._cleanupPreviousSession(e),this.activeSessionId=e;try{localStorage.setItem("codeman-active-session",e)}catch{}this._updateSseSubscription(e),this.hideWelcome(),this.playTerminalEntrance?.(e),this.clearPendingHooks(e,"idle_prompt"),this._updateActiveTabImmediate(e),this.renderSessionTabs(),this.updateAttachmentHistoryBadge?.(),this.attachmentHistoryDrawerOpen&&this.loadAttachmentHistory?.(e),this._updateLocalEchoState(),this._flushedOffsets?.has(e)&&this._localEchoOverlay&&this._localEchoOverlay.setFlushed(this._flushedOffsets.get(e),this._flushedTexts?.get(e)||"",!1);const l=document.querySelector(`.session-tab.active[data-id="${e}"]`);l&&(l.classList.add("tab-glow"),l.addEventListener("animationend",()=>l.classList.remove("tab-glow"),{once:!0}));const r=this.sessions.get(e);if(this.currentSessionWorkingDir=r?.workingDir||null,r&&r.pid===null)if(r.respawnBlocked){const u=r.name||"Session";if(window.confirm(`${u} was stopped after crashing repeatedly. Restart it?`))try{await fetch(`/api/sessions/${e}/interactive`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({clearBreaker:!0})}),r.respawnBlocked=!1,r.status="busy"}catch(p){console.error("Failed to restart crash-looped session:",p)}}else try{const u=r.mode==="shell"?`/api/sessions/${e}/shell`:`/api/sessions/${e}/interactive`;await fetch(u,{method:"POST"}),r.status="busy"}catch(u){console.error("Failed to attach to restored session:",u)}this._restoringFlushedState=!0;const d=this._beginBufferLoad(a);let c=!1;try{this.fitAddon&&this.fitAddon.fit();const u=await this.sendResize(e,{forceHttp:!0}).catch(()=>!1);if(this._isStaleSelect(a)){this._clearTerminalLoadState(e,a);return}let p=this._xtermSnapshots?.get(e);if(p&&!this._isUsableXtermSnapshot(p)&&(this._xtermSnapshots?.delete(e),p=null),!p)try{const S=localStorage.getItem(`codeman-xs-${e}`);S&&this._isUsableXtermSnapshot(S)?(p=S,this._xtermSnapshots?.delete(e),this._xtermSnapshots?.set(e,S)):S&&localStorage.removeItem(`codeman-xs-${e}`)}catch{}const _=r&&(r.status==="busy"||r.status==="working");let v=!1;if(p&&!_&&r?.mode!=="shell"){if(_crashDiag.log(`SNAPSHOT_RESTORE: ${(p.length/1024).toFixed(0)}KB`),this._setTerminalLoadState(e,a,"replaying"),this._resetTerminalForReplay(),await new Promise(S=>this.terminal.write(p,S)),this._isStaleSelect(a)){this._clearTerminalLoadState(e,a);return}this.scrollToLastNonEmptyLine(),_crashDiag.log("SNAPSHOT_RESTORE_DONE"),v=!0}const g=this.terminalBufferCache.get(e);let C=!1;if(g&&!_&&!v){if(_crashDiag.log(`CACHE_WRITE: ${(g.length/1024).toFixed(0)}KB`),this._setTerminalLoadState(e,a,"replaying"),this._resetTerminalForReplay(),await this.chunkedTerminalWrite(g,TERMINAL_CHUNK_SIZE,d),this._isStaleSelect(a)){this._clearTerminalLoadState(e,a);return}this.terminal.scrollToBottom(),_crashDiag.log("CACHE_DONE")}else _&&(this._resetTerminalForReplay(),C=!0,_crashDiag.log("CACHE_SKIP_BUSY"));if(r?.mode!=="shell"&&u&&(await new Promise(S=>setTimeout(S,TUI_REDRAW_SETTLE_MS)),this._isStaleSelect(a))){this._clearTerminalLoadState(e,a);return}this._setTerminalLoadState(e,a,"fetching"),_crashDiag.log("FETCH_START");const k=this._initialFullBufferLoad===!0;this._initialFullBufferLoad=!1;const L=await fetch(k?`/api/sessions/${e}/terminal?full=1`:`/api/sessions/${e}/terminal?tail=${TERMINAL_TAIL_SIZE}`);if(this._isStaleSelect(a)){this._clearTerminalLoadState(e,a);return}const E=(await L.json())?.data??{};if(_crashDiag.log(`FETCH_DONE: ${E.terminalBuffer?(E.terminalBuffer.length/1024).toFixed(0)+"KB":"empty"} truncated=${E.truncated}`),E.terminalBuffer){if(v||C||E.terminalBuffer!==g){if(_crashDiag.log(`REWRITE: ${(E.terminalBuffer.length/1024).toFixed(0)}KB`),this._setTerminalLoadState(e,a,"replaying"),this._resetTerminalForReplay(),E.truncated&&this.terminal.write(`\x1B[90m... (earlier output truncated for performance) ...\x1B[0m\r
|
|
35
35
|
\r
|
|
36
|
-
`),await this.chunkedTerminalWrite(E.terminalBuffer,TERMINAL_CHUNK_SIZE,d),this._isStaleSelect(a)){this._clearTerminalLoadState(e,a);return}this.terminal.scrollToBottom()}if(this.terminalBufferCache.set(e,E.terminalBuffer),this.terminalBufferCache.size>20){const T=this.terminalBufferCache.keys().next().value;this.terminalBufferCache.delete(T)}}else g||(this._resetTerminalForReplay(),c=!0);if(this._isLoadingBuffer&&this._finishBufferLoad(d,{flushQueued:c}),this._restoringFlushedState=!1,this._flushedOffsets?.has(e)&&this._localEchoOverlay){this._localEchoOverlay.setFlushed(this._flushedOffsets.get(e),this._flushedTexts?.get(e)||"",!1);const
|
|
36
|
+
`),await this.chunkedTerminalWrite(E.terminalBuffer,TERMINAL_CHUNK_SIZE,d),this._isStaleSelect(a)){this._clearTerminalLoadState(e,a);return}this.terminal.scrollToBottom()}if(this.terminalBufferCache.set(e,E.terminalBuffer),this.terminalBufferCache.size>20){const T=this.terminalBufferCache.keys().next().value;this.terminalBufferCache.delete(T)}}else g||(this._resetTerminalForReplay(),c=!0);if(this._isLoadingBuffer&&this._finishBufferLoad(d,{flushQueued:c}),this._restoringFlushedState=!1,this._flushedOffsets?.has(e)&&this._localEchoOverlay){this._localEchoOverlay.setFlushed(this._flushedOffsets.get(e),this._flushedTexts?.get(e)||"",!1);const S=this._localEchoOverlay;this.terminal.write("",()=>{S.hasPending&&S.rerender()})}this.sendResize(e),(typeof requestIdleCallback=="function"?requestIdleCallback:S=>setTimeout(S,16))(()=>{if(a!==this._selectGeneration)return;this.respawnStatus[e]?(this.showRespawnBanner(),this.updateRespawnBanner(this.respawnStatus[e].state),document.getElementById("respawnCycleCount").textContent=this.respawnStatus[e].cycleCount||0,this.updateCountdownTimerDisplay(),this.updateActionLogDisplay(),Object.keys(this.respawnCountdownTimers[e]||{}).length>0&&this.startCountdownInterval()):(this.hideRespawnBanner(),this.stopCountdownInterval());const S=document.getElementById("taskPanel");S&&S.classList.contains("open")&&this.renderTaskPanel();const T=this.sessions.get(e);if(T&&(T.ralphLoop||T.ralphTodos)&&this.updateRalphState(e,{loop:T.ralphLoop,todos:T.ralphTodos}),this.renderRalphStatePanel(),this.updateCliInfoDisplay(),this.renderProjectInsightsPanel(),this.updateSubagentWindowVisibility(),this.loadAppSettingsFromStorage().showFileBrowser){const m=this.$("fileBrowserPanel");if(m&&(m.classList.add("visible"),this.loadFileBrowser(e),!this.fileBrowserDragListeners)){const w=m.querySelector(".file-browser-header");if(w){const O=()=>{if(!m.style.left){const R=m.getBoundingClientRect();m.style.left=`${R.left}px`,m.style.top=`${R.top}px`,m.style.right="auto"}};w.addEventListener("mousedown",O),w.addEventListener("touchstart",O,{passive:!0}),this.fileBrowserDragListeners=this.makeWindowDraggable(m,w),this.fileBrowserDragListeners._onFirstDrag=O}}}}),this._connectWs(e),_crashDiag.log("FOCUS"),n&&this.terminal&&this.terminal.focus(),this.scrollToLastNonEmptyLine(),typeof KeyboardHandler<"u"&&KeyboardHandler.keyboardVisible&&KeyboardHandler.onKeyboardShow(),this._clearTerminalLoadState(e,a),_crashDiag.log(`SELECT_DONE: ${(performance.now()-i).toFixed(0)}ms`),console.log(`[CRASH-DIAG] selectSession DONE: ${e.slice(0,8)} in ${(performance.now()-i).toFixed(0)}ms`)}catch(u){this._isLoadingBuffer&&this._finishBufferLoad(d),this._restoringFlushedState=!1,this._setTerminalLoadState(e,a,"failed"),console.error("Failed to load session terminal:",u)}}_cleanupSessionData(e){this._activeRename?.sessionId===e&&this._activeRename.cancel(),this.sessions.delete(e);const t=this.sessionOrder.indexOf(e);t!==-1&&(this.sessionOrder.splice(t,1),this.saveSessionOrder()),this.terminalBuffers.delete(e),this.terminalBufferCache.delete(e),this._xtermSnapshots?.delete(e);try{localStorage.removeItem(`codeman-xs-${e}`)}catch{}this._flushedOffsets?.delete(e),this._flushedTexts?.delete(e),this._pendingDeliveries?.delete(e),this._seqCounters?.delete(e),this._postDraining?.delete(e),this._persistReliableState(),this.ralphStates.delete(e),this.ralphClosedSessions.delete(e),this.projectInsights.delete(e),this.pendingHooks.delete(e),this.tabAlerts.delete(e),this.attachmentHistoryCounts.delete(e),this.attachmentHistoryDrawerOpen&&this.activeSessionId===e&&this.closeAttachmentHistory?.(),this.terminalLoadStates.delete(e),this.clearCountdownTimers(e),this.closeSessionLogViewerWindows(e),this.closeSessionImagePopups(e),this.closeSessionAttachmentCards(e),this.closeSessionSubagentWindows(e,!0);const s=this.idleTimers.get(e);s&&(clearTimeout(s),this.idleTimers.delete(e)),delete this.respawnStatus[e],delete this.respawnTimers[e],delete this.respawnCountdownTimers[e],delete this.respawnActionLogs[e]}async closeSession(e,t=!0){try{if(await this._apiDelete(`/api/sessions/${e}?killMux=${t}`),this._cleanupSessionData(e),this.activeSessionId===e){this.activeSessionId=null;try{localStorage.removeItem("codeman-active-session")}catch{}if(this.sessionOrder.length>0&&this.sessions.size>0){const s=this.sessionOrder[0];this.selectSession(s)}else this.terminal.clear(),this.showWelcome(),this.renderRalphStatePanel()}this.renderSessionTabs(),t?this.showToast("Session closed and tmux killed","success"):this.showToast("Tab hidden, tmux still running","info")}catch{this.showToast("Failed to close session","error")}}requestCloseSession(e){const t=this.sessions.get(e);if(!t)return;this.pendingCloseSessionId=e;const s=this.getSessionName(t),n=document.getElementById("closeConfirmSessionName");n.textContent=s;const i=document.getElementById("closeConfirmKillTitle");i&&(i.textContent=t.mode==="opencode"?"Kill Tmux & OpenCode":t.mode==="codex"?"Kill Tmux & Codex":t.mode==="gemini"?"Kill Tmux & Gemini":t.mode==="antigravity"?"Kill Tmux & Antigravity":"Kill Tmux & Claude Code"),document.getElementById("closeConfirmModal").classList.add("active")}cancelCloseSession(){this.pendingCloseSessionId=null,document.getElementById("closeConfirmModal").classList.remove("active")}async confirmCloseSession(e=!0){const t=this.pendingCloseSessionId;this.cancelCloseSession(),t&&await this.closeSession(t,e)}nextSession(){if(this.sessionOrder.length<=1)return;const t=(this.sessionOrder.indexOf(this.activeSessionId)+1)%this.sessionOrder.length;this.selectSession(this.sessionOrder[t])}prevSession(){if(this.sessionOrder.length<=1)return;const t=(this.sessionOrder.indexOf(this.activeSessionId)-1+this.sessionOrder.length)%this.sessionOrder.length;this.selectSession(this.sessionOrder[t])}goHome(){this.activeSessionId=null;try{localStorage.removeItem("codeman-active-session")}catch{}this.terminal.clear(),this.showWelcome(),this.renderSessionTabs(),this.renderRalphStatePanel()}ralphWizardStep=1;ralphWizardConfig={taskDescription:"",completionPhrase:"COMPLETE",maxIterations:10,caseName:"testcase",enableRespawn:!1,generatedPlan:null,planGenerated:!1,skipPlanGeneration:!1,planDetailLevel:"detailed",existingPlan:null,useExistingPlan:!1};planLoadingTimer=null;planLoadingStartTime=null;async killActiveSession(){if(!this.activeSessionId){this.showToast("No active session","warning");return}await this.closeSession(this.activeSessionId)}async killAllSessions(){if(this.sessions.size!==0&&confirm(`Kill all ${this.sessions.size} session(s)?`))try{await this._apiDelete("/api/sessions"),this.sessions.clear(),this.terminalBuffers.clear(),this.terminalBufferCache.clear(),this.terminalLoadStates.clear(),this._xtermSnapshots?.clear();try{for(const e of Object.keys(localStorage))e.startsWith("codeman-xs-")&&localStorage.removeItem(e)}catch{}this.activeSessionId=null;try{localStorage.removeItem("codeman-active-session")}catch{}this.respawnStatus={},this.respawnCountdownTimers={},this.respawnActionLogs={},this.stopCountdownInterval(),this.hideRespawnBanner(),this.renderSessionTabs(),this.terminal.clear(),this.showWelcome(),this.showToast("All sessions killed","success")}catch{this.showToast("Failed to kill sessions","error")}}showTimer(){document.getElementById("timerBanner").style.display="flex",this.updateTimer(),this.timerInterval=setInterval(()=>this.updateTimer(),1e3)}hideTimer(){document.getElementById("timerBanner").style.display="none",this.timerInterval&&(clearInterval(this.timerInterval),this.timerInterval=null)}updateTimer(){if(!this.currentRun||this.currentRun.status!=="running")return;const e=Date.now(),t=Math.max(0,this.currentRun.endAt-e),s=this.currentRun.endAt-this.currentRun.startedAt,n=e-this.currentRun.startedAt,i=Math.min(100,n/s*100);document.getElementById("timerValue").textContent=this.formatTime(t),document.getElementById("timerProgress").style.width=`${i}%`,document.getElementById("timerMeta").textContent=`${this.currentRun.completedTasks} tasks | $${this.currentRun.totalCost.toFixed(2)}`}async stopCurrentRun(){if(this.currentRun)try{await fetch(`/api/scheduled/${this.currentRun.id}`,{method:"DELETE"})}catch{this.showToast("Failed to stop run","error")}}formatTime(e){const t=Math.floor(e/1e3),s=Math.floor(t/3600),n=Math.floor(t%3600/60),i=t%60;return`${s.toString().padStart(2,"0")}:${n.toString().padStart(2,"0")}:${i.toString().padStart(2,"0")}`}updateCost(){this.updateTokens()}updateTokens(){this._clearTimer("_updateTokensTimeout"),this._updateTokensTimeout=setTimeout(()=>{this._updateTokensTimeout=null,this._updateTokensImmediate()},200)}_updateTokensImmediate(){let e=0,t=0;this.globalStats?(e=this.globalStats.totalInputTokens||0,t=this.globalStats.totalOutputTokens||0):this.sessions.forEach(a=>{a.tokens&&(e+=a.tokens.input||0,t+=a.tokens.output||0)});const s=e+t;this.totalTokens=s;const n=this.formatTokens(s),i=this.estimateCost(e,t),o=this.$("headerTokens");if(o){const l=this.loadAppSettingsFromStorage().showCost??!1;o.textContent=s>0?l?`${n} tokens \xB7 $${i.toFixed(2)}`:`${n} tokens`:"0 tokens",o.title=this.globalStats?`Lifetime: ${this.globalStats.totalSessionsCreated} sessions created${l?`
|
|
37
37
|
Estimated cost based on Claude Opus pricing`:""}`:`Token usage across active sessions${l?`
|
|
38
38
|
Estimated cost based on Claude Opus pricing`:""}`}}getShortcutRegistry(){const t=this.loadAppSettingsFromStorage().shortcutOverrides||{};return DEFAULT_SHORTCUTS.map(s=>{const n=t[s.id];if(!n)return s;const i={...s};return Array.isArray(n.bindings)&&(i.bindings=n.bindings,delete i.displayBindings),typeof n.disabled=="boolean"&&(i.disabled=n.disabled),i})}matchesShortcutEvent(e,t){return!t||!Array.isArray(t.bindings)?!1:t.bindings.some(s=>{const n=s.modifiers||[];return(n.includes("ctrl")||n.includes("meta"))!==!!(e.ctrlKey||e.metaKey)||n.includes("shift")!==!!e.shiftKey||n.includes("alt")!==!!e.altKey?!1:!!(s.code&&e.code===s.code||s.key&&typeof e.key=="string"&&e.key.toLowerCase()===s.key.toLowerCase())})}showShortcutOverlay(){const e=document.getElementById("shortcutOverlayModal");e&&(this.renderShortcutOverlay(),e.classList.add("active"),e.focus?.())}renderShortcutOverlay(){const e=document.getElementById("shortcutOverlayList");if(!e)return;const t=this.getShortcutRegistry(),s={};for(const i of t){const o=i.group||"General";s[o]||(s[o]=[]),s[o].push(i)}const n=i=>i.displayBindings?i.displayBindings.map(o=>`<kbd>${escapeHtml(o)}</kbd>`).join(" / "):i.bindings?i.bindings.map(o=>{const a=[...(o.modifiers||[]).map(l=>l.charAt(0).toUpperCase()+l.slice(1)),o.key||o.code||""];return`<kbd>${escapeHtml(a.join("+"))}</kbd>`}).join(" / "):"";e.innerHTML=Object.entries(s).map(([i,o])=>`<div class="shortcut-overlay-group"><div class="shortcut-overlay-group-label">${escapeHtml(i)}</div>`+o.map(a=>`<div class="shortcut-overlay-row"><span class="shortcut-overlay-label">${escapeHtml(a.label)}</span><span class="shortcut-overlay-keys">${n(a)}</span></div>`).join("")+"</div>").join("")}closeShortcutOverlay(){const e=document.getElementById("shortcutOverlayModal");e&&e.classList.remove("active")}}try{for(let h=0;h<localStorage.length;h++){const e=localStorage.key(h);if(e&&(e.startsWith("claudeman-")||e.startsWith("claudeman_"))){const t=e.replace(/^claudeman[-_]/,s=>"codeman"+s.charAt(s.length-1));localStorage.getItem(t)===null&&localStorage.setItem(t,localStorage.getItem(e))}}}catch{}let app;document.addEventListener("DOMContentLoaded",()=>{app=new CodemanApp,window.app=app}),window.MobileDetection=MobileDetection;
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
<link rel="manifest" href="manifest.json">
|
|
17
17
|
<title>Codeman</title>
|
|
18
18
|
<link rel="icon" type="image/svg+xml" href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'%3E%3Cdefs%3E%3ClinearGradient id='g' x1='0%25' y1='0%25' x2='100%25' y2='100%25'%3E%3Cstop offset='0%25' stop-color='%2360a5fa'/%3E%3Cstop offset='100%25' stop-color='%233b82f6'/%3E%3C/linearGradient%3E%3C/defs%3E%3Crect width='32' height='32' rx='6' fill='%230a0a0a'/%3E%3Cpath d='M18 4L8 18h6l-2 10 10-14h-6z' fill='url(%23g)'/%3E%3C/svg%3E">
|
|
19
|
-
<link rel="stylesheet" href="styles.
|
|
19
|
+
<link rel="stylesheet" href="styles.50cd52bd.css">
|
|
20
20
|
<link rel="stylesheet" href="mobile.32cacde5.css" media="(max-width: 1023px)">
|
|
21
21
|
<!-- xterm.css loaded async — terminal won't display until xterm.js runs anyway -->
|
|
22
22
|
<link rel="preload" href="vendor/xterm.css" as="style" onload="this.onload=null;this.rel='stylesheet'">
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
instead of waiting until <script> tags at bottom-of-body are reached. -->
|
|
26
26
|
<link rel="preload" href="vendor/xterm.min.js" as="script">
|
|
27
27
|
<link rel="preload" href="constants.e7175b95.js" as="script">
|
|
28
|
-
<link rel="preload" href="app.
|
|
28
|
+
<link rel="preload" href="app.ce412e69.js" as="script">
|
|
29
29
|
<!-- Self-hosted xterm.js — eliminates CDN DNS/TLS latency (~100ms).
|
|
30
30
|
'defer' preserves execution order (xterm loads before fit addon). -->
|
|
31
31
|
<script defer src="vendor/xterm.min.js"></script>
|
|
@@ -323,6 +323,10 @@
|
|
|
323
323
|
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polygon points="5 3 19 12 5 21 5 3"/></svg>
|
|
324
324
|
Run OpenCode
|
|
325
325
|
</button>
|
|
326
|
+
<button class="welcome-btn welcome-btn-antigravity" id="welcomeAntigravityBtn" style="display: none;" onclick="app.setRunMode('antigravity'); app.runAntigravity()">
|
|
327
|
+
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polygon points="5 3 19 12 5 21 5 3"/></svg>
|
|
328
|
+
Run Antigravity
|
|
329
|
+
</button>
|
|
326
330
|
<button class="welcome-btn welcome-btn-gemini" id="welcomeGeminiBtn" style="display: none;" onclick="app.setRunMode('gemini'); app.runGemini()">
|
|
327
331
|
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polygon points="5 3 19 12 5 21 5 3"/></svg>
|
|
328
332
|
Run Gemini
|
|
@@ -2192,7 +2196,7 @@
|
|
|
2192
2196
|
<div class="form-row">
|
|
2193
2197
|
<label>Image</label>
|
|
2194
2198
|
<input type="text" id="dockerImage" placeholder="codeman/agent:base" autocomplete="off" autocapitalize="off" spellcheck="false">
|
|
2195
|
-
<span class="form-hint">Build it once with <code>node scripts/build-agent-image.mjs</code>. Contains node + claude/codex/gemini + tmux.</span>
|
|
2199
|
+
<span class="form-hint">Build it once with <code>node scripts/build-agent-image.mjs</code>. Contains node + claude/codex/gemini/opencode/agy + tmux.</span>
|
|
2196
2200
|
</div>
|
|
2197
2201
|
<div class="form-row">
|
|
2198
2202
|
<label>Network</label>
|
|
@@ -2665,13 +2669,13 @@
|
|
|
2665
2669
|
<script defer src="input-cjk.63794d0b.js"></script>
|
|
2666
2670
|
<!-- Hardened markdown HTML sanitizer (wires DOMPurify). Must precede app.js. -->
|
|
2667
2671
|
<script defer src="sanitize-html.bc7078d6.js"></script>
|
|
2668
|
-
<script defer src="app.
|
|
2672
|
+
<script defer src="app.ce412e69.js"></script>
|
|
2669
2673
|
<script defer src="terminal-ui.3ac6f8c2.js"></script>
|
|
2670
2674
|
<script defer src="respawn-ui.ff0dae4c.js"></script>
|
|
2671
2675
|
<script defer src="ralph-panel.6de2d0f8.js"></script>
|
|
2672
2676
|
<script defer src="orchestrator-panel.js"></script>
|
|
2673
2677
|
<script defer src="cron-ui.js"></script>
|
|
2674
|
-
<script defer src="settings-ui.
|
|
2678
|
+
<script defer src="settings-ui.01a1ae7d.js"></script>
|
|
2675
2679
|
<script defer src="panels-ui.1d4203e0.js"></script>
|
|
2676
2680
|
<script defer src="ultracode-panel.js"></script>
|
|
2677
2681
|
<script defer src="admin-ui.js"></script>
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"use strict";Object.assign(CodemanApp.prototype,{_onHookIdlePrompt(e){e.sessionId&&this.setPendingHook(e.sessionId,"idle_prompt"),this._notifySession(e.sessionId,"warning","hook-idle","Waiting for Input",e.message||"Claude is idle and waiting for a prompt")},_onHookPermissionPrompt(e){e.sessionId&&this.setPendingHook(e.sessionId,"permission_prompt");const t=e.tool?`${e.tool}${e.command?": "+e.command:e.file?": "+e.file:""}`:"";this._notifySession(e.sessionId,"critical","hook-permission","Permission Required",t||"Claude needs tool approval to continue")},_onHookElicitationDialog(e){e.sessionId&&this.setPendingHook(e.sessionId,"elicitation_dialog"),this._notifySession(e.sessionId,"critical","hook-elicitation","Question Asked",e.question||"Claude is asking a question and waiting for your answer")},_onHookStop(e){e.sessionId&&this.clearPendingHooks(e.sessionId),this._notifySession(e.sessionId,"info","hook-stop","Response Complete",e.reason||"Claude has finished responding")},_onHookTeammateIdle(e){const t=this.sessions.get(e.sessionId);this._notifySession(e.sessionId,"warning","hook-teammate-idle","Teammate Idle",`A teammate is idle in ${t?.name||e.sessionId}`)},_onHookTaskCompleted(e){const t=this.sessions.get(e.sessionId);this._notifySession(e.sessionId,"info","hook-task-completed","Task Completed",`A team task completed in ${t?.name||e.sessionId}`)},_onTunnelStarted(e){console.log("[Tunnel] Started:",e.url),this._tunnelUrl=e.url,this._dismissTunnelConnecting(),this._updateTunnelUrlDisplay(e.url),this._updateTunnelIndicator(!0),document.getElementById("welcomeOverlay")?.classList.contains("visible")?(this._updateWelcomeTunnelBtn(!0,e.url,!0),this.showToast("Tunnel active","success")):(this._updateWelcomeTunnelBtn(!0,e.url),this.showToast(`Tunnel active: ${e.url}`,"success"),this.showTunnelQR())},_onTunnelStopped(){console.log("[Tunnel] Stopped"),this._tunnelUrl=null,this._dismissTunnelConnecting(),this._updateTunnelUrlDisplay(null),this._updateWelcomeTunnelBtn(!1),this._updateTunnelIndicator(!1),this.closeTunnelPanel(),this.closeTunnelQR()},_onTunnelProgress(e){console.log("[Tunnel] Progress:",e.message);const t=document.getElementById("tunnelConnectingToast");t&&(t.innerHTML=`<span class="tunnel-spinner"></span> ${e.message}`);const n=document.getElementById("welcomeTunnelBtn");n?.classList.contains("connecting")&&(n.innerHTML=`<span class="tunnel-spinner"></span> ${e.message}`)},_onTunnelError(e){console.warn("[Tunnel] Error:",e.message),this._dismissTunnelConnecting(),this.showToast(`Tunnel error: ${e.message}`,"error");const t=document.getElementById("welcomeTunnelBtn");t&&(t.disabled=!1,t.classList.remove("connecting"))},_onTunnelQrRotated(e){if(e.svg){const t=document.getElementById("tunnelQrContainer");t&&(t.innerHTML=e.svg);const n=document.getElementById("welcomeQrInner");n&&(n.innerHTML=e.svg)}else this._refreshTunnelQrFromApi();this._resetQrCountdown()},_onTunnelQrRegenerated(e){if(e.svg){const t=document.getElementById("tunnelQrContainer");t&&(t.innerHTML=e.svg);const n=document.getElementById("welcomeQrInner");n&&(n.innerHTML=e.svg)}else this._refreshTunnelQrFromApi();this._resetQrCountdown()},_onTunnelQrAuthUsed(e){const n=(e.ua||"Unknown device").match(/Chrome|Firefox|Safari|Edge|Mobile/)?.[0]||"Browser";this.showToast(`Device authenticated via QR (${n}, ${e.ip}). Not you?`,"warning",{duration:1e4,action:{label:"Revoke All",onClick:()=>{fetch("/api/auth/revoke",{method:"POST",headers:{"Content-Type":"application/json"},body:"{}"}).then(()=>this.showToast("All sessions revoked","success")).catch(()=>this.showToast("Failed to revoke sessions","error"))}}})},registerServiceWorker(){"serviceWorker"in navigator&&navigator.serviceWorker.register("/sw.js").then(e=>{this._swRegistration=e,navigator.serviceWorker.addEventListener("message",t=>{if(t.data?.type==="notification-click"){const{sessionId:n}=t.data;n&&this.sessions.has(n)&&this.selectSession(n),window.focus()}}),e.pushManager.getSubscription().then(t=>{t&&(this._pushSubscription=t,this._updatePushUI(!0))})}).catch(()=>{})},async subscribeToPush(){if(!this._swRegistration){this.showToast("Service worker not available. HTTPS or localhost required.","error");return}try{const e=await this._apiJson("/api/push/vapid-key");if(!e)throw new Error("Failed to get VAPID key");const t=urlBase64ToUint8Array(e.publicKey),n=await this._swRegistration.pushManager.subscribe({userVisibleOnly:!0,applicationServerKey:t}),s=n.toJSON(),o=await this._apiJson("/api/push/subscribe",{method:"POST",body:{endpoint:s.endpoint,keys:s.keys,userAgent:navigator.userAgent,pushPreferences:this._buildPushPreferences()}});if(!o)throw new Error("Failed to register subscription");this._pushSubscription=n,this._pushSubscriptionId=o.id,localStorage.setItem("codeman-push-subscription-id",o.id),this._updatePushUI(!0),this.showToast("Push notifications enabled","success")}catch(e){this.showToast("Push subscription failed: "+(e.message||e),"error")}},async unsubscribeFromPush(){try{this._pushSubscription&&await this._pushSubscription.unsubscribe();const e=this._pushSubscriptionId||localStorage.getItem("codeman-push-subscription-id");e&&await fetch(`/api/push/subscribe/${e}`,{method:"DELETE"}).catch(()=>{}),this._pushSubscription=null,this._pushSubscriptionId=null,localStorage.removeItem("codeman-push-subscription-id"),this._updatePushUI(!1),this.showToast("Push notifications disabled","success")}catch(e){this.showToast("Failed to unsubscribe: "+(e.message||e),"error")}},async togglePushSubscription(){this._pushSubscription?await this.unsubscribeFromPush():await this.subscribeToPush()},async _syncPushPreferences(){const e=this._pushSubscriptionId||localStorage.getItem("codeman-push-subscription-id");if(e)try{await fetch(`/api/push/subscribe/${e}`,{method:"PUT",headers:{"Content-Type":"application/json"},body:JSON.stringify({pushPreferences:this._buildPushPreferences()})})}catch{}},_buildPushPreferences(){const e={},t={"hook:permission_prompt":"eventPermissionPush","hook:elicitation_dialog":"eventQuestionPush","hook:idle_prompt":"eventIdlePush","hook:stop":"eventStopPush","respawn:blocked":"eventRespawnPush","session:ralphCompletionDetected":"eventRalphPush"};for(const[n,s]of Object.entries(t)){const o=document.getElementById(s);e[n]=o?o.checked:!0}return e["session:error"]=!0,e},_updatePushUI(e){const t=document.getElementById("pushSubscribeBtn"),n=document.getElementById("pushSubscriptionStatus");t&&(t.textContent=e?"Unsubscribe":"Subscribe"),n&&(n.textContent=e?"active":"off",n.classList.remove("granted","denied"),e&&n.classList.add("granted"))},openAppSettings(){const e=this.loadAppSettingsFromStorage();document.getElementById("appSettingsDisplayName").value=e.displayName||"Codeman",document.getElementById("appSettingsLanguage").value=e.language==="zh-CN"?"zh-CN":"en",document.getElementById("appSettingsClaudeMdPath").value=e.defaultClaudeMdPath||"",document.getElementById("appSettingsDefaultDir").value=e.defaultWorkingDir||"";const t=this.getDefaultSettings();document.getElementById("appSettingsRalphEnabled").checked=e.ralphTrackerEnabled??t.ralphTrackerEnabled??!1,document.getElementById("appSettingsShowFontControls").checked=e.showFontControls??t.showFontControls??!1,document.getElementById("appSettingsShowSystemStats").checked=e.showSystemStats??t.showSystemStats??!0,document.getElementById("appSettingsShowLifecycleLog").checked=e.showLifecycleLog??t.showLifecycleLog??!1,document.getElementById("appSettingsShowResponseViewer").checked=e.showResponseViewer??t.showResponseViewer??!1,document.getElementById("appSettingsShowFileViewerButton").checked=e.showFileViewerButton??t.showFileViewerButton??!0,document.getElementById("appSettingsShowAttachmentsButton").checked=e.showAttachmentsButton??t.showAttachmentsButton??!1,document.getElementById("appSettingsSkin").value=e.skin??t.skin??"daylight-blue",this._syncEntranceAnimSetting?.(),document.getElementById("appSettingsWebglRenderer").checked=e.webglRendererEnabled??t.webglRendererEnabled??!0;const n=document.getElementById("appSettingsWebglRendererItem");n&&(n.style.display=MobileDetection.getDeviceType()==="desktop"?"":"none"),document.getElementById("appSettingsShowMonitor").checked=e.showMonitor??t.showMonitor??!1,document.getElementById("appSettingsShowProjectInsights").checked=e.showProjectInsights??t.showProjectInsights??!1,document.getElementById("appSettingsShowFileBrowser").checked=e.showFileBrowser??t.showFileBrowser??!1,document.getElementById("appSettingsShowSubagents").checked=e.showSubagents??t.showSubagents??!1,document.getElementById("appSettingsShowUltracodeAgents").checked=e.showUltracodeAgents??t.showUltracodeAgents??!1,document.getElementById("appSettingsUltracodeFloatingWindows").checked=e.ultracodeFloatingWindows??t.ultracodeFloatingWindows??!1,document.getElementById("appSettingsShowMultiMonitorButton").checked=e.showMultiMonitorButton??t.showMultiMonitorButton??!1,document.getElementById("appSettingsShowPlanUsageLimits").checked=this.planUsageChipEnabled(e),document.getElementById("appSettingsShowRedrawButton").checked=e.showRedrawButton??t.showRedrawButton??!1,document.getElementById("appSettingsMobileOverview").checked=e.mobileOverviewEnabled??t.mobileOverviewEnabled??!1;const s=MobileDetection.getDeviceType()==="mobile"?"":"none",o=document.getElementById("appSettingsMobileOverviewItem");o&&(o.style.display=s);const i=document.getElementById("appSettingsPhoneSection");i&&(i.style.display=s),document.getElementById("appSettingsShowSessionButton").checked=e.showSessionButton??t.showSessionButton??!1,document.getElementById("appSettingsShowAwayDigestButton").checked=e.showAwayDigestButton??t.showAwayDigestButton??!1,document.getElementById("appSettingsShowCronButton").checked=e.showCronButton??t.showCronButton??!1;const a=document.getElementById("appSettingsGestureControlItem");a&&(a.style.display=window.__codemanGestureAvailable?"":"none"),document.getElementById("appSettingsGestureControl").checked=e.gestureControlEnabled??t.gestureControlEnabled??!1,document.getElementById("appSettingsSubagentTracking").checked=e.subagentTrackingEnabled??t.subagentTrackingEnabled??!0,document.getElementById("appSettingsSubagentActiveTabOnly").checked=e.subagentActiveTabOnly??t.subagentActiveTabOnly??!0,document.getElementById("appSettingsImageWatcherEnabled").checked=e.imageWatcherEnabled??t.imageWatcherEnabled??!1,document.getElementById("appSettingsTunnelEnabled").checked=e.tunnelEnabled??!1,this.loadTunnelStatus(),document.getElementById("appSettingsLocalEcho").checked=e.localEchoEnabled??MobileDetection.isTouchDevice(),document.getElementById("appSettingsTerminalWheelLocal").checked=e.terminalWheelLocalScrollback??t.terminalWheelLocalScrollback??!1,document.getElementById("appSettingsCjkInput").checked=e.cjkInputEnabled??t.cjkInputEnabled??!1,document.getElementById("appSettingsExtendedKeyboardBar").checked=e.extendedKeyboardBar??!1,document.getElementById("appSettingsTabTwoRows").checked=e.tabTwoRows??t.tabTwoRows??!1;const l=document.getElementById("appSettingsClaudeMode"),r=document.getElementById("allowedToolsRow");l.value=e.claudeMode||"dangerously-skip-permissions",document.getElementById("appSettingsAllowedTools").value=e.allowedTools||"",r.style.display=l.value==="allowedTools"?"":"none",l.onchange=()=>{r.style.display=l.value==="allowedTools"?"":"none"},document.getElementById("appSettingsCodexDangerouslyBypassApprovals").checked=e.codexDangerouslyBypassApprovals??!1,document.getElementById("appSettingsCodexAnimations").checked=e.codexAnimationsEnabled??!1,this._applyCodexSettingsVisibility(),document.getElementById("appSettingsAgentTeams").checked=e.agentTeamsEnabled??!1,document.getElementById("appSettingsClaudeModel").value=e.claudeModel??"",document.getElementById("appSettingsOpusContext1m").checked=e.opusContext1mEnabled??!1,document.getElementById("appSettingsRemoteAutoReconnect").checked=e.remoteAutoReconnect??!0,document.getElementById("appSettingsThinkingEffort").value=e.thinkingEffort??"";const d=e.nice||{};document.getElementById("appSettingsNiceEnabled").checked=d.enabled??!1,document.getElementById("appSettingsNiceValue").value=d.niceValue??10,this.loadModelConfigForSettings();const c=this.notificationManager?.preferences||{};document.getElementById("appSettingsNotifEnabled").checked=c.enabled??!0,document.getElementById("appSettingsNotifBrowser").checked=c.browserNotifications??!1,document.getElementById("appSettingsNotifAudio").checked=c.audioAlerts??!1,document.getElementById("appSettingsNotifStuckMins").value=Math.round((c.stuckThresholdMs||6e5)/6e4),document.getElementById("appSettingsNotifCritical").checked=!c.muteCritical,document.getElementById("appSettingsNotifWarning").checked=!c.muteWarning,document.getElementById("appSettingsNotifInfo").checked=!c.muteInfo,document.getElementById("appSettingsPushEnabled").checked=!!this._pushSubscription,this._updatePushUI(!!this._pushSubscription);const u=c.eventTypes||{},h=u.permission_prompt||{};document.getElementById("eventPermissionEnabled").checked=h.enabled??!0,document.getElementById("eventPermissionBrowser").checked=h.browser??!0,document.getElementById("eventPermissionPush").checked=h.push??!1,document.getElementById("eventPermissionAudio").checked=h.audio??!0;const p=u.elicitation_dialog||{};document.getElementById("eventQuestionEnabled").checked=p.enabled??!0,document.getElementById("eventQuestionBrowser").checked=p.browser??!0,document.getElementById("eventQuestionPush").checked=p.push??!1,document.getElementById("eventQuestionAudio").checked=p.audio??!0;const y=u.idle_prompt||{};document.getElementById("eventIdleEnabled").checked=y.enabled??!0,document.getElementById("eventIdleBrowser").checked=y.browser??!0,document.getElementById("eventIdlePush").checked=y.push??!1,document.getElementById("eventIdleAudio").checked=y.audio??!1;const v=u.stop||{};document.getElementById("eventStopEnabled").checked=v.enabled??!1,document.getElementById("eventStopBrowser").checked=v.browser??!1,document.getElementById("eventStopPush").checked=v.push??!1,document.getElementById("eventStopAudio").checked=v.audio??!1;const f=u.respawn_cycle||{};document.getElementById("eventRespawnEnabled").checked=f.enabled??!0,document.getElementById("eventRespawnBrowser").checked=f.browser??!1,document.getElementById("eventRespawnPush").checked=f.push??!1,document.getElementById("eventRespawnAudio").checked=f.audio??!1;const E=u.ralph_complete||{};document.getElementById("eventRalphEnabled").checked=E.enabled??!0,document.getElementById("eventRalphBrowser").checked=E.browser??!0,document.getElementById("eventRalphPush").checked=E.push??!1,document.getElementById("eventRalphAudio").checked=E.audio??!0;const w=u.subagent_spawn||{};document.getElementById("eventSubagentEnabled").checked=w.enabled??!1,document.getElementById("eventSubagentBrowser").checked=w.browser??!1,document.getElementById("eventSubagentPush").checked=w.push??!1,document.getElementById("eventSubagentAudio").checked=w.audio??!1;const b=document.getElementById("notifPermissionStatus");if(b&&typeof Notification<"u"){const m=Notification.permission;b.textContent=m==="granted"?"\u2713":m==="denied"?"\u2717":"?",b.classList.remove("granted","denied"),m==="granted"?b.classList.add("granted"):m==="denied"&&b.classList.add("denied")}const g=VoiceInput._getDeepgramConfig();document.getElementById("voiceDeepgramKey").value=g.apiKey||"",document.getElementById("voiceLanguage").value=g.language||"en-US",document.getElementById("voiceKeyterms").value=g.keyterms||"refactor, endpoint, middleware, callback, async, regex, TypeScript, npm, API, deploy, config, linter, env, webhook, schema, CLI, JSON, CSS, DOM, SSE, backend, frontend, localhost, dependencies, repository, merge, rebase, diff, commit, com",document.getElementById("voiceInsertMode").value=g.insertMode||"direct";const B=document.getElementById("voiceDeepgramKey");B.type="password",document.getElementById("voiceKeyToggleBtn").textContent="Show";const S=VoiceInput.getActiveProviderName(),T=document.getElementById("voiceProviderStatus");T.textContent=S,T.className="voice-provider-status"+(S.startsWith("Deepgram")?" active":""),this._initUpdatesSection(),this.switchSettingsTab("settings-display");const I=document.getElementById("appSettingsModal");I.querySelectorAll(".modal-tabs .modal-tab-btn").forEach(m=>{m.onclick=()=>this.switchSettingsTab(m.dataset.tab)}),I.classList.add("active"),this.activeFocusTrap=new FocusTrap(I),this.activeFocusTrap.activate()},_applyCodexSettingsVisibility(){const e=document.querySelector('#appSettingsModal .modal-tab-btn[data-tab="settings-codex"]');e&&(e.style.display=window.__codemanCliAvailable?.codex===!0?"":"none")},switchSettingsTab(e){const t=document.getElementById("appSettingsModal");t.querySelectorAll(".modal-tabs .modal-tab-btn").forEach(n=>{n.classList.toggle("active",n.dataset.tab===e)}),t.querySelectorAll(".modal-tab-content").forEach(n=>{n.classList.toggle("hidden",n.id!==e)}),e==="settings-shortcuts"&&this.renderShortcutSettingsList?.()},closeAppSettings(){document.getElementById("appSettingsModal").classList.remove("active"),this.activeFocusTrap&&(this.activeFocusTrap.deactivate(),this.activeFocusTrap=null)},_updatePhaseText(e){return{queued:"Queued\u2026",preparing:"Preparing\u2026",stashing:"Stashing local changes\u2026",fetching:"Fetching release\u2026",checkout:"Checking out release\u2026",installing:"Installing dependencies\u2026",building:"Building\u2026",restarting:"Restarting Codeman\u2026"}[e]||e},_initUpdatesSection(){const e=this.$("updateCurrentVersion");e&&(e.textContent=(this.$("versionDisplay")?.textContent||"").trim()||"\u2014");for(const t of["updateResult","updateActionRow","updateNotes","updateProgress"]){const n=this.$(t);n&&(n.style.display="none")}this._updateCheck=null},_setUpdateResult(e){const t=this.$("updateResult");t&&(t.style.display="block",t.innerHTML=e)},_setUpdateProgress(e){const t=this.$("updateProgress");t&&(t.style.display="block",t.innerHTML=e)},async checkForUpdate(){const e=this.$("updateCheckBtn");e&&(e.disabled=!0,e.textContent="Checking\u2026");const t=await this._apiJson("/api/system/update/check");e&&(e.disabled=!1,e.textContent="Check now");const n=this.$("updateActionRow"),s=this.$("updateNotes");if(n&&(n.style.display="none"),s&&(s.style.display="none"),!t){this._setUpdateResult("Could not check for updates. Try again later.");return}this._updateCheck=t;const o=this.$("updateCurrentVersion");if(o&&t.currentVersion&&(o.textContent=`v${t.currentVersion}`),t.installKind&&t.installKind!=="git"){this._setUpdateResult(`This install can't update itself (${escapeHtml(t.installKind)}). Update with <code>npm i -g aicodeman@latest</code>.`);return}if(t.selfUpdateEnabled===!1){this._setUpdateResult("In-app updates are disabled on this server (CODEMAN_DISABLE_SELF_UPDATE=1).");return}if(t.error&&!t.updateAvailable){this._setUpdateResult(escapeHtml(t.error));return}if(t.updateAvailable&&t.latestVersion){this._setUpdateResult(`Update available: <strong>v${escapeHtml(t.latestVersion)}</strong> (current v${escapeHtml(t.currentVersion||"")})`);const i=this.$("updateActionLabel");i&&(i.textContent=`Update to v${t.latestVersion}`),n&&(n.style.display="flex");const a=this.$("updateNowBtn");a&&(a.disabled=!1,a.textContent="Update now"),s&&t.notes&&(s.style.display="block",s.textContent=t.notes)}else this._setUpdateResult(`You're up to date (v${escapeHtml(t.currentVersion||"")}).`)},async startSelfUpdate(){const e=this._updateCheck?.latestVersion?`v${this._updateCheck.latestVersion}`:"the latest release";if(!confirm(`Update Codeman to ${e}? The server will restart and this page will reload.`))return;const t=this.$("updateNowBtn");t&&(t.disabled=!0,t.textContent="Starting\u2026");const n=await this._apiPost("/api/system/update",{});if(!n||!n.ok){let i="Failed to start the update.";try{const a=await n.json();typeof a?.error=="string"&&a.error&&(i=a.error)}catch{}this._setUpdateProgress(`<span style="color:var(--danger,#e5534b)">${escapeHtml(i)}</span>`),t&&(t.disabled=!1,t.textContent="Update now");return}const s=this.$("updateActionRow");s&&(s.style.display="none");const o=this.$("updateNotes");o&&(o.style.display="none"),this._setUpdateProgress("Starting update\u2026"),this._pollUpdateStatus()},_stopUpdatePolling(){this._updatePollTimer&&(clearInterval(this._updatePollTimer),this._updatePollTimer=null)},_pollUpdateStatus(){this._stopUpdatePolling();const e=new Set(["completed","completed-needs-manual-restart","failed","idle"]),t=async()=>{let n=null;try{const s=await fetch("/api/system/update/status");if(s.ok){const o=await s.json();n=o&&o.success===!0?o.data:o}}catch{}if(!n){this._setUpdateProgress("\u21BB Restarting Codeman\u2026");return}if(!e.has(n.phase)){const s=n.message&&n.message.trim()?n.message.trim():this._updatePhaseText(n.phase);let o="";n.startedAt&&(o=` <span style="color:var(--text-secondary)">\xB7 ${Math.max(0,Math.round((Date.now()-n.startedAt)/1e3))}s</span>`),this._setUpdateProgress(`<span class="tunnel-spinner"></span> ${escapeHtml(s)}${o}`);return}if(this._stopUpdatePolling(),n.phase==="completed"){let s=`<span style="color:var(--success,#3fb950)">\u2713 Updated to v${escapeHtml(n.toVersion||"")}. Reloading\u2026</span>`;n.stashRef&&(s+=`<br><span style="color:var(--text-secondary)">Local changes stashed as <code>${escapeHtml(n.stashRef)}</code> \u2014 run <code>git stash pop</code> to restore.</span>`),this._setUpdateProgress(s),setTimeout(()=>location.reload(),2500)}else if(n.phase==="completed-needs-manual-restart")this._setUpdateProgress(`Update staged. Restart Codeman to apply:<br><code>${escapeHtml(n.manualRestartCommand||"restart codeman web")}</code>`);else if(n.phase==="failed"){let s=`<span style="color:var(--danger,#e5534b)">\u2717 ${escapeHtml(n.message||"Update failed")}.</span>`;n.error&&(s+=`<br><span style="color:var(--text-secondary)">${escapeHtml(n.error)}</span>`),s+='<br><span style="color:var(--text-secondary)">The previous version is still running.</span>',n.stashRef&&(s+=`<br><span style="color:var(--text-secondary)">Local changes stashed as <code>${escapeHtml(n.stashRef)}</code>.</span>`),this._setUpdateProgress(s);const o=this.$("updateNowBtn"),i=this.$("updateActionRow");o&&(o.disabled=!1,o.textContent="Try again"),i&&(i.style.display="flex")}};t(),this._updatePollTimer=setInterval(t,1500)},isCliAvailable(e){const t=window.__codemanCliAvailable;return!t||typeof t!="object"?!0:t[e]!==!1},applyWelcomeCliVisibility(){const e=[["welcomeClaudeBtn","claude"],["welcomeOpencodeBtn","opencode"],["welcomeGeminiBtn","gemini"],["welcomeTunnelBtn","cloudflared"]];for(const[t,n]of e){const s=document.getElementById(t);s&&(s.style.display=this.isCliAvailable(n)?"flex":"none")}},async loadTunnelStatus(){try{const t=await(await fetch("/api/tunnel/status")).json(),n=t?.success===!0?t.data:t,s=n.running&&n.url;this._tunnelUrl=s?n.url:null,this._updateTunnelUrlDisplay(this._tunnelUrl),this._updateWelcomeTunnelBtn(!!s,this._tunnelUrl),this._updateTunnelIndicator(!!s)}catch{this._tunnelUrl=null,this._updateTunnelUrlDisplay(null),this._updateWelcomeTunnelBtn(!1),this._updateTunnelIndicator(!1)}},_updateTunnelUrlRow(e,t,n,s=""){const o=document.getElementById(e),i=document.getElementById(t);if(!(!o||!i))if(n){const a=n+s;o.style.display="",i.textContent=a,i.onclick=()=>{navigator.clipboard.writeText(a).then(()=>{this.showToast(`${s?"Upload":"Tunnel"} URL copied`,"success")})}}else o.style.display="none",i.textContent="",i.onclick=null},_updateTunnelUrlDisplay(e){this._updateTunnelUrlRow("tunnelUrlRow","tunnelUrlDisplay",e),this._updateTunnelUrlRow("tunnelUploadUrlRow","tunnelUploadUrlDisplay",e,"/upload.html")},showTunnelQR(){this.closeTunnelQR();const e=document.createElement("div");e.id="tunnelQrOverlay",e.style.cssText="position:fixed;inset:0;background:rgba(0,0,0,0.7);z-index:5000;display:flex;align-items:center;justify-content:center;cursor:pointer",e.onclick=n=>{n.target===e&&this.closeTunnelQR()};const t=document.createElement("div");t.style.cssText="background:var(--bg-card);border:1px solid var(--border);border-radius:12px;padding:24px;text-align:center;max-width:340px;width:90vw;box-shadow:var(--shadow-lg);cursor:default",t.innerHTML=`
|
|
1
|
+
"use strict";Object.assign(CodemanApp.prototype,{_onHookIdlePrompt(e){e.sessionId&&this.setPendingHook(e.sessionId,"idle_prompt"),this._notifySession(e.sessionId,"warning","hook-idle","Waiting for Input",e.message||"Claude is idle and waiting for a prompt")},_onHookPermissionPrompt(e){e.sessionId&&this.setPendingHook(e.sessionId,"permission_prompt");const t=e.tool?`${e.tool}${e.command?": "+e.command:e.file?": "+e.file:""}`:"";this._notifySession(e.sessionId,"critical","hook-permission","Permission Required",t||"Claude needs tool approval to continue")},_onHookElicitationDialog(e){e.sessionId&&this.setPendingHook(e.sessionId,"elicitation_dialog"),this._notifySession(e.sessionId,"critical","hook-elicitation","Question Asked",e.question||"Claude is asking a question and waiting for your answer")},_onHookStop(e){e.sessionId&&this.clearPendingHooks(e.sessionId),this._notifySession(e.sessionId,"info","hook-stop","Response Complete",e.reason||"Claude has finished responding")},_onHookTeammateIdle(e){const t=this.sessions.get(e.sessionId);this._notifySession(e.sessionId,"warning","hook-teammate-idle","Teammate Idle",`A teammate is idle in ${t?.name||e.sessionId}`)},_onHookTaskCompleted(e){const t=this.sessions.get(e.sessionId);this._notifySession(e.sessionId,"info","hook-task-completed","Task Completed",`A team task completed in ${t?.name||e.sessionId}`)},_onTunnelStarted(e){console.log("[Tunnel] Started:",e.url),this._tunnelUrl=e.url,this._dismissTunnelConnecting(),this._updateTunnelUrlDisplay(e.url),this._updateTunnelIndicator(!0),document.getElementById("welcomeOverlay")?.classList.contains("visible")?(this._updateWelcomeTunnelBtn(!0,e.url,!0),this.showToast("Tunnel active","success")):(this._updateWelcomeTunnelBtn(!0,e.url),this.showToast(`Tunnel active: ${e.url}`,"success"),this.showTunnelQR())},_onTunnelStopped(){console.log("[Tunnel] Stopped"),this._tunnelUrl=null,this._dismissTunnelConnecting(),this._updateTunnelUrlDisplay(null),this._updateWelcomeTunnelBtn(!1),this._updateTunnelIndicator(!1),this.closeTunnelPanel(),this.closeTunnelQR()},_onTunnelProgress(e){console.log("[Tunnel] Progress:",e.message);const t=document.getElementById("tunnelConnectingToast");t&&(t.innerHTML=`<span class="tunnel-spinner"></span> ${e.message}`);const n=document.getElementById("welcomeTunnelBtn");n?.classList.contains("connecting")&&(n.innerHTML=`<span class="tunnel-spinner"></span> ${e.message}`)},_onTunnelError(e){console.warn("[Tunnel] Error:",e.message),this._dismissTunnelConnecting(),this.showToast(`Tunnel error: ${e.message}`,"error");const t=document.getElementById("welcomeTunnelBtn");t&&(t.disabled=!1,t.classList.remove("connecting"))},_onTunnelQrRotated(e){if(e.svg){const t=document.getElementById("tunnelQrContainer");t&&(t.innerHTML=e.svg);const n=document.getElementById("welcomeQrInner");n&&(n.innerHTML=e.svg)}else this._refreshTunnelQrFromApi();this._resetQrCountdown()},_onTunnelQrRegenerated(e){if(e.svg){const t=document.getElementById("tunnelQrContainer");t&&(t.innerHTML=e.svg);const n=document.getElementById("welcomeQrInner");n&&(n.innerHTML=e.svg)}else this._refreshTunnelQrFromApi();this._resetQrCountdown()},_onTunnelQrAuthUsed(e){const n=(e.ua||"Unknown device").match(/Chrome|Firefox|Safari|Edge|Mobile/)?.[0]||"Browser";this.showToast(`Device authenticated via QR (${n}, ${e.ip}). Not you?`,"warning",{duration:1e4,action:{label:"Revoke All",onClick:()=>{fetch("/api/auth/revoke",{method:"POST",headers:{"Content-Type":"application/json"},body:"{}"}).then(()=>this.showToast("All sessions revoked","success")).catch(()=>this.showToast("Failed to revoke sessions","error"))}}})},registerServiceWorker(){"serviceWorker"in navigator&&navigator.serviceWorker.register("/sw.js").then(e=>{this._swRegistration=e,navigator.serviceWorker.addEventListener("message",t=>{if(t.data?.type==="notification-click"){const{sessionId:n}=t.data;n&&this.sessions.has(n)&&this.selectSession(n),window.focus()}}),e.pushManager.getSubscription().then(t=>{t&&(this._pushSubscription=t,this._updatePushUI(!0))})}).catch(()=>{})},async subscribeToPush(){if(!this._swRegistration){this.showToast("Service worker not available. HTTPS or localhost required.","error");return}try{const e=await this._apiJson("/api/push/vapid-key");if(!e)throw new Error("Failed to get VAPID key");const t=urlBase64ToUint8Array(e.publicKey),n=await this._swRegistration.pushManager.subscribe({userVisibleOnly:!0,applicationServerKey:t}),s=n.toJSON(),o=await this._apiJson("/api/push/subscribe",{method:"POST",body:{endpoint:s.endpoint,keys:s.keys,userAgent:navigator.userAgent,pushPreferences:this._buildPushPreferences()}});if(!o)throw new Error("Failed to register subscription");this._pushSubscription=n,this._pushSubscriptionId=o.id,localStorage.setItem("codeman-push-subscription-id",o.id),this._updatePushUI(!0),this.showToast("Push notifications enabled","success")}catch(e){this.showToast("Push subscription failed: "+(e.message||e),"error")}},async unsubscribeFromPush(){try{this._pushSubscription&&await this._pushSubscription.unsubscribe();const e=this._pushSubscriptionId||localStorage.getItem("codeman-push-subscription-id");e&&await fetch(`/api/push/subscribe/${e}`,{method:"DELETE"}).catch(()=>{}),this._pushSubscription=null,this._pushSubscriptionId=null,localStorage.removeItem("codeman-push-subscription-id"),this._updatePushUI(!1),this.showToast("Push notifications disabled","success")}catch(e){this.showToast("Failed to unsubscribe: "+(e.message||e),"error")}},async togglePushSubscription(){this._pushSubscription?await this.unsubscribeFromPush():await this.subscribeToPush()},async _syncPushPreferences(){const e=this._pushSubscriptionId||localStorage.getItem("codeman-push-subscription-id");if(e)try{await fetch(`/api/push/subscribe/${e}`,{method:"PUT",headers:{"Content-Type":"application/json"},body:JSON.stringify({pushPreferences:this._buildPushPreferences()})})}catch{}},_buildPushPreferences(){const e={},t={"hook:permission_prompt":"eventPermissionPush","hook:elicitation_dialog":"eventQuestionPush","hook:idle_prompt":"eventIdlePush","hook:stop":"eventStopPush","respawn:blocked":"eventRespawnPush","session:ralphCompletionDetected":"eventRalphPush"};for(const[n,s]of Object.entries(t)){const o=document.getElementById(s);e[n]=o?o.checked:!0}return e["session:error"]=!0,e},_updatePushUI(e){const t=document.getElementById("pushSubscribeBtn"),n=document.getElementById("pushSubscriptionStatus");t&&(t.textContent=e?"Unsubscribe":"Subscribe"),n&&(n.textContent=e?"active":"off",n.classList.remove("granted","denied"),e&&n.classList.add("granted"))},openAppSettings(){const e=this.loadAppSettingsFromStorage();document.getElementById("appSettingsDisplayName").value=e.displayName||"Codeman",document.getElementById("appSettingsLanguage").value=e.language==="zh-CN"?"zh-CN":"en",document.getElementById("appSettingsClaudeMdPath").value=e.defaultClaudeMdPath||"",document.getElementById("appSettingsDefaultDir").value=e.defaultWorkingDir||"";const t=this.getDefaultSettings();document.getElementById("appSettingsRalphEnabled").checked=e.ralphTrackerEnabled??t.ralphTrackerEnabled??!1,document.getElementById("appSettingsShowFontControls").checked=e.showFontControls??t.showFontControls??!1,document.getElementById("appSettingsShowSystemStats").checked=e.showSystemStats??t.showSystemStats??!0,document.getElementById("appSettingsShowLifecycleLog").checked=e.showLifecycleLog??t.showLifecycleLog??!1,document.getElementById("appSettingsShowResponseViewer").checked=e.showResponseViewer??t.showResponseViewer??!1,document.getElementById("appSettingsShowFileViewerButton").checked=e.showFileViewerButton??t.showFileViewerButton??!0,document.getElementById("appSettingsShowAttachmentsButton").checked=e.showAttachmentsButton??t.showAttachmentsButton??!1,document.getElementById("appSettingsSkin").value=e.skin??t.skin??"daylight-blue",this._syncEntranceAnimSetting?.(),document.getElementById("appSettingsWebglRenderer").checked=e.webglRendererEnabled??t.webglRendererEnabled??!0;const n=document.getElementById("appSettingsWebglRendererItem");n&&(n.style.display=MobileDetection.getDeviceType()==="desktop"?"":"none"),document.getElementById("appSettingsShowMonitor").checked=e.showMonitor??t.showMonitor??!1,document.getElementById("appSettingsShowProjectInsights").checked=e.showProjectInsights??t.showProjectInsights??!1,document.getElementById("appSettingsShowFileBrowser").checked=e.showFileBrowser??t.showFileBrowser??!1,document.getElementById("appSettingsShowSubagents").checked=e.showSubagents??t.showSubagents??!1,document.getElementById("appSettingsShowUltracodeAgents").checked=e.showUltracodeAgents??t.showUltracodeAgents??!1,document.getElementById("appSettingsUltracodeFloatingWindows").checked=e.ultracodeFloatingWindows??t.ultracodeFloatingWindows??!1,document.getElementById("appSettingsShowMultiMonitorButton").checked=e.showMultiMonitorButton??t.showMultiMonitorButton??!1,document.getElementById("appSettingsShowPlanUsageLimits").checked=this.planUsageChipEnabled(e),document.getElementById("appSettingsShowRedrawButton").checked=e.showRedrawButton??t.showRedrawButton??!1,document.getElementById("appSettingsMobileOverview").checked=e.mobileOverviewEnabled??t.mobileOverviewEnabled??!1;const s=MobileDetection.getDeviceType()==="mobile"?"":"none",o=document.getElementById("appSettingsMobileOverviewItem");o&&(o.style.display=s);const i=document.getElementById("appSettingsPhoneSection");i&&(i.style.display=s),document.getElementById("appSettingsShowSessionButton").checked=e.showSessionButton??t.showSessionButton??!1,document.getElementById("appSettingsShowAwayDigestButton").checked=e.showAwayDigestButton??t.showAwayDigestButton??!1,document.getElementById("appSettingsShowCronButton").checked=e.showCronButton??t.showCronButton??!1;const a=document.getElementById("appSettingsGestureControlItem");a&&(a.style.display=window.__codemanGestureAvailable?"":"none"),document.getElementById("appSettingsGestureControl").checked=e.gestureControlEnabled??t.gestureControlEnabled??!1,document.getElementById("appSettingsSubagentTracking").checked=e.subagentTrackingEnabled??t.subagentTrackingEnabled??!0,document.getElementById("appSettingsSubagentActiveTabOnly").checked=e.subagentActiveTabOnly??t.subagentActiveTabOnly??!0,document.getElementById("appSettingsImageWatcherEnabled").checked=e.imageWatcherEnabled??t.imageWatcherEnabled??!1,document.getElementById("appSettingsTunnelEnabled").checked=e.tunnelEnabled??!1,this.loadTunnelStatus(),document.getElementById("appSettingsLocalEcho").checked=e.localEchoEnabled??MobileDetection.isTouchDevice(),document.getElementById("appSettingsTerminalWheelLocal").checked=e.terminalWheelLocalScrollback??t.terminalWheelLocalScrollback??!1,document.getElementById("appSettingsCjkInput").checked=e.cjkInputEnabled??t.cjkInputEnabled??!1,document.getElementById("appSettingsExtendedKeyboardBar").checked=e.extendedKeyboardBar??!1,document.getElementById("appSettingsTabTwoRows").checked=e.tabTwoRows??t.tabTwoRows??!1;const l=document.getElementById("appSettingsClaudeMode"),r=document.getElementById("allowedToolsRow");l.value=e.claudeMode||"dangerously-skip-permissions",document.getElementById("appSettingsAllowedTools").value=e.allowedTools||"",r.style.display=l.value==="allowedTools"?"":"none",l.onchange=()=>{r.style.display=l.value==="allowedTools"?"":"none"},document.getElementById("appSettingsCodexDangerouslyBypassApprovals").checked=e.codexDangerouslyBypassApprovals??!1,document.getElementById("appSettingsCodexAnimations").checked=e.codexAnimationsEnabled??!1,this._applyCodexSettingsVisibility(),document.getElementById("appSettingsAgentTeams").checked=e.agentTeamsEnabled??!1,document.getElementById("appSettingsClaudeModel").value=e.claudeModel??"",document.getElementById("appSettingsOpusContext1m").checked=e.opusContext1mEnabled??!1,document.getElementById("appSettingsRemoteAutoReconnect").checked=e.remoteAutoReconnect??!0,document.getElementById("appSettingsThinkingEffort").value=e.thinkingEffort??"";const d=e.nice||{};document.getElementById("appSettingsNiceEnabled").checked=d.enabled??!1,document.getElementById("appSettingsNiceValue").value=d.niceValue??10,this.loadModelConfigForSettings();const c=this.notificationManager?.preferences||{};document.getElementById("appSettingsNotifEnabled").checked=c.enabled??!0,document.getElementById("appSettingsNotifBrowser").checked=c.browserNotifications??!1,document.getElementById("appSettingsNotifAudio").checked=c.audioAlerts??!1,document.getElementById("appSettingsNotifStuckMins").value=Math.round((c.stuckThresholdMs||6e5)/6e4),document.getElementById("appSettingsNotifCritical").checked=!c.muteCritical,document.getElementById("appSettingsNotifWarning").checked=!c.muteWarning,document.getElementById("appSettingsNotifInfo").checked=!c.muteInfo,document.getElementById("appSettingsPushEnabled").checked=!!this._pushSubscription,this._updatePushUI(!!this._pushSubscription);const u=c.eventTypes||{},h=u.permission_prompt||{};document.getElementById("eventPermissionEnabled").checked=h.enabled??!0,document.getElementById("eventPermissionBrowser").checked=h.browser??!0,document.getElementById("eventPermissionPush").checked=h.push??!1,document.getElementById("eventPermissionAudio").checked=h.audio??!0;const p=u.elicitation_dialog||{};document.getElementById("eventQuestionEnabled").checked=p.enabled??!0,document.getElementById("eventQuestionBrowser").checked=p.browser??!0,document.getElementById("eventQuestionPush").checked=p.push??!1,document.getElementById("eventQuestionAudio").checked=p.audio??!0;const y=u.idle_prompt||{};document.getElementById("eventIdleEnabled").checked=y.enabled??!0,document.getElementById("eventIdleBrowser").checked=y.browser??!0,document.getElementById("eventIdlePush").checked=y.push??!1,document.getElementById("eventIdleAudio").checked=y.audio??!1;const v=u.stop||{};document.getElementById("eventStopEnabled").checked=v.enabled??!1,document.getElementById("eventStopBrowser").checked=v.browser??!1,document.getElementById("eventStopPush").checked=v.push??!1,document.getElementById("eventStopAudio").checked=v.audio??!1;const f=u.respawn_cycle||{};document.getElementById("eventRespawnEnabled").checked=f.enabled??!0,document.getElementById("eventRespawnBrowser").checked=f.browser??!1,document.getElementById("eventRespawnPush").checked=f.push??!1,document.getElementById("eventRespawnAudio").checked=f.audio??!1;const E=u.ralph_complete||{};document.getElementById("eventRalphEnabled").checked=E.enabled??!0,document.getElementById("eventRalphBrowser").checked=E.browser??!0,document.getElementById("eventRalphPush").checked=E.push??!1,document.getElementById("eventRalphAudio").checked=E.audio??!0;const w=u.subagent_spawn||{};document.getElementById("eventSubagentEnabled").checked=w.enabled??!1,document.getElementById("eventSubagentBrowser").checked=w.browser??!1,document.getElementById("eventSubagentPush").checked=w.push??!1,document.getElementById("eventSubagentAudio").checked=w.audio??!1;const b=document.getElementById("notifPermissionStatus");if(b&&typeof Notification<"u"){const m=Notification.permission;b.textContent=m==="granted"?"\u2713":m==="denied"?"\u2717":"?",b.classList.remove("granted","denied"),m==="granted"?b.classList.add("granted"):m==="denied"&&b.classList.add("denied")}const g=VoiceInput._getDeepgramConfig();document.getElementById("voiceDeepgramKey").value=g.apiKey||"",document.getElementById("voiceLanguage").value=g.language||"en-US",document.getElementById("voiceKeyterms").value=g.keyterms||"refactor, endpoint, middleware, callback, async, regex, TypeScript, npm, API, deploy, config, linter, env, webhook, schema, CLI, JSON, CSS, DOM, SSE, backend, frontend, localhost, dependencies, repository, merge, rebase, diff, commit, com",document.getElementById("voiceInsertMode").value=g.insertMode||"direct";const B=document.getElementById("voiceDeepgramKey");B.type="password",document.getElementById("voiceKeyToggleBtn").textContent="Show";const S=VoiceInput.getActiveProviderName(),T=document.getElementById("voiceProviderStatus");T.textContent=S,T.className="voice-provider-status"+(S.startsWith("Deepgram")?" active":""),this._initUpdatesSection(),this.switchSettingsTab("settings-display");const I=document.getElementById("appSettingsModal");I.querySelectorAll(".modal-tabs .modal-tab-btn").forEach(m=>{m.onclick=()=>this.switchSettingsTab(m.dataset.tab)}),I.classList.add("active"),this.activeFocusTrap=new FocusTrap(I),this.activeFocusTrap.activate()},_applyCodexSettingsVisibility(){const e=document.querySelector('#appSettingsModal .modal-tab-btn[data-tab="settings-codex"]');e&&(e.style.display=window.__codemanCliAvailable?.codex===!0?"":"none")},switchSettingsTab(e){const t=document.getElementById("appSettingsModal");t.querySelectorAll(".modal-tabs .modal-tab-btn").forEach(n=>{n.classList.toggle("active",n.dataset.tab===e)}),t.querySelectorAll(".modal-tab-content").forEach(n=>{n.classList.toggle("hidden",n.id!==e)}),e==="settings-shortcuts"&&this.renderShortcutSettingsList?.()},closeAppSettings(){document.getElementById("appSettingsModal").classList.remove("active"),this.activeFocusTrap&&(this.activeFocusTrap.deactivate(),this.activeFocusTrap=null)},_updatePhaseText(e){return{queued:"Queued\u2026",preparing:"Preparing\u2026",stashing:"Stashing local changes\u2026",fetching:"Fetching release\u2026",checkout:"Checking out release\u2026",installing:"Installing dependencies\u2026",building:"Building\u2026",restarting:"Restarting Codeman\u2026"}[e]||e},_initUpdatesSection(){const e=this.$("updateCurrentVersion");e&&(e.textContent=(this.$("versionDisplay")?.textContent||"").trim()||"\u2014");for(const t of["updateResult","updateActionRow","updateNotes","updateProgress"]){const n=this.$(t);n&&(n.style.display="none")}this._updateCheck=null},_setUpdateResult(e){const t=this.$("updateResult");t&&(t.style.display="block",t.innerHTML=e)},_setUpdateProgress(e){const t=this.$("updateProgress");t&&(t.style.display="block",t.innerHTML=e)},async checkForUpdate(){const e=this.$("updateCheckBtn");e&&(e.disabled=!0,e.textContent="Checking\u2026");const t=await this._apiJson("/api/system/update/check");e&&(e.disabled=!1,e.textContent="Check now");const n=this.$("updateActionRow"),s=this.$("updateNotes");if(n&&(n.style.display="none"),s&&(s.style.display="none"),!t){this._setUpdateResult("Could not check for updates. Try again later.");return}this._updateCheck=t;const o=this.$("updateCurrentVersion");if(o&&t.currentVersion&&(o.textContent=`v${t.currentVersion}`),t.installKind&&t.installKind!=="git"){this._setUpdateResult(`This install can't update itself (${escapeHtml(t.installKind)}). Update with <code>npm i -g aicodeman@latest</code>.`);return}if(t.selfUpdateEnabled===!1){this._setUpdateResult("In-app updates are disabled on this server (CODEMAN_DISABLE_SELF_UPDATE=1).");return}if(t.error&&!t.updateAvailable){this._setUpdateResult(escapeHtml(t.error));return}if(t.updateAvailable&&t.latestVersion){this._setUpdateResult(`Update available: <strong>v${escapeHtml(t.latestVersion)}</strong> (current v${escapeHtml(t.currentVersion||"")})`);const i=this.$("updateActionLabel");i&&(i.textContent=`Update to v${t.latestVersion}`),n&&(n.style.display="flex");const a=this.$("updateNowBtn");a&&(a.disabled=!1,a.textContent="Update now"),s&&t.notes&&(s.style.display="block",s.textContent=t.notes)}else this._setUpdateResult(`You're up to date (v${escapeHtml(t.currentVersion||"")}).`)},async startSelfUpdate(){const e=this._updateCheck?.latestVersion?`v${this._updateCheck.latestVersion}`:"the latest release";if(!confirm(`Update Codeman to ${e}? The server will restart and this page will reload.`))return;const t=this.$("updateNowBtn");t&&(t.disabled=!0,t.textContent="Starting\u2026");const n=await this._apiPost("/api/system/update",{});if(!n||!n.ok){let i="Failed to start the update.";try{const a=await n.json();typeof a?.error=="string"&&a.error&&(i=a.error)}catch{}this._setUpdateProgress(`<span style="color:var(--danger,#e5534b)">${escapeHtml(i)}</span>`),t&&(t.disabled=!1,t.textContent="Update now");return}const s=this.$("updateActionRow");s&&(s.style.display="none");const o=this.$("updateNotes");o&&(o.style.display="none"),this._setUpdateProgress("Starting update\u2026"),this._pollUpdateStatus()},_stopUpdatePolling(){this._updatePollTimer&&(clearInterval(this._updatePollTimer),this._updatePollTimer=null)},_pollUpdateStatus(){this._stopUpdatePolling();const e=new Set(["completed","completed-needs-manual-restart","failed","idle"]),t=async()=>{let n=null;try{const s=await fetch("/api/system/update/status");if(s.ok){const o=await s.json();n=o&&o.success===!0?o.data:o}}catch{}if(!n){this._setUpdateProgress("\u21BB Restarting Codeman\u2026");return}if(!e.has(n.phase)){const s=n.message&&n.message.trim()?n.message.trim():this._updatePhaseText(n.phase);let o="";n.startedAt&&(o=` <span style="color:var(--text-secondary)">\xB7 ${Math.max(0,Math.round((Date.now()-n.startedAt)/1e3))}s</span>`),this._setUpdateProgress(`<span class="tunnel-spinner"></span> ${escapeHtml(s)}${o}`);return}if(this._stopUpdatePolling(),n.phase==="completed"){let s=`<span style="color:var(--success,#3fb950)">\u2713 Updated to v${escapeHtml(n.toVersion||"")}. Reloading\u2026</span>`;n.stashRef&&(s+=`<br><span style="color:var(--text-secondary)">Local changes stashed as <code>${escapeHtml(n.stashRef)}</code> \u2014 run <code>git stash pop</code> to restore.</span>`),this._setUpdateProgress(s),setTimeout(()=>location.reload(),2500)}else if(n.phase==="completed-needs-manual-restart")this._setUpdateProgress(`Update staged. Restart Codeman to apply:<br><code>${escapeHtml(n.manualRestartCommand||"restart codeman web")}</code>`);else if(n.phase==="failed"){let s=`<span style="color:var(--danger,#e5534b)">\u2717 ${escapeHtml(n.message||"Update failed")}.</span>`;n.error&&(s+=`<br><span style="color:var(--text-secondary)">${escapeHtml(n.error)}</span>`),s+='<br><span style="color:var(--text-secondary)">The previous version is still running.</span>',n.stashRef&&(s+=`<br><span style="color:var(--text-secondary)">Local changes stashed as <code>${escapeHtml(n.stashRef)}</code>.</span>`),this._setUpdateProgress(s);const o=this.$("updateNowBtn"),i=this.$("updateActionRow");o&&(o.disabled=!1,o.textContent="Try again"),i&&(i.style.display="flex")}};t(),this._updatePollTimer=setInterval(t,1500)},isCliAvailable(e){const t=window.__codemanCliAvailable;return!t||typeof t!="object"?!0:t[e]!==!1},applyWelcomeCliVisibility(){const e=[["welcomeClaudeBtn","claude"],["welcomeOpencodeBtn","opencode"],["welcomeAntigravityBtn","antigravity"],["welcomeGeminiBtn","gemini"],["welcomeTunnelBtn","cloudflared"]];for(const[t,n]of e){const s=document.getElementById(t);s&&(s.style.display=this.isCliAvailable(n)?"flex":"none")}},async loadTunnelStatus(){try{const t=await(await fetch("/api/tunnel/status")).json(),n=t?.success===!0?t.data:t,s=n.running&&n.url;this._tunnelUrl=s?n.url:null,this._updateTunnelUrlDisplay(this._tunnelUrl),this._updateWelcomeTunnelBtn(!!s,this._tunnelUrl),this._updateTunnelIndicator(!!s)}catch{this._tunnelUrl=null,this._updateTunnelUrlDisplay(null),this._updateWelcomeTunnelBtn(!1),this._updateTunnelIndicator(!1)}},_updateTunnelUrlRow(e,t,n,s=""){const o=document.getElementById(e),i=document.getElementById(t);if(!(!o||!i))if(n){const a=n+s;o.style.display="",i.textContent=a,i.onclick=()=>{navigator.clipboard.writeText(a).then(()=>{this.showToast(`${s?"Upload":"Tunnel"} URL copied`,"success")})}}else o.style.display="none",i.textContent="",i.onclick=null},_updateTunnelUrlDisplay(e){this._updateTunnelUrlRow("tunnelUrlRow","tunnelUrlDisplay",e),this._updateTunnelUrlRow("tunnelUploadUrlRow","tunnelUploadUrlDisplay",e,"/upload.html")},showTunnelQR(){this.closeTunnelQR();const e=document.createElement("div");e.id="tunnelQrOverlay",e.style.cssText="position:fixed;inset:0;background:rgba(0,0,0,0.7);z-index:5000;display:flex;align-items:center;justify-content:center;cursor:pointer",e.onclick=n=>{n.target===e&&this.closeTunnelQR()};const t=document.createElement("div");t.style.cssText="background:var(--bg-card);border:1px solid var(--border);border-radius:12px;padding:24px;text-align:center;max-width:340px;width:90vw;box-shadow:var(--shadow-lg);cursor:default",t.innerHTML=`
|
|
2
2
|
<div style="font-size:14px;font-weight:600;color:var(--text-primary);margin-bottom:16px">Scan to connect</div>
|
|
3
3
|
<div id="tunnelQrContainer" style="background:#fff;border-radius:8px;padding:16px;display:inline-block">
|
|
4
4
|
<div style="color:#666;font-size:12px">Loading...</div>
|
|
Binary file
|
|
Binary file
|