aicodeman 1.26.0 → 1.26.1
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/dist/codex-transcript.d.ts +84 -0
- package/dist/codex-transcript.d.ts.map +1 -0
- package/dist/codex-transcript.js +341 -0
- package/dist/codex-transcript.js.map +1 -0
- package/dist/config/cli-registry/schema.d.ts +4 -0
- package/dist/config/cli-registry/schema.d.ts.map +1 -1
- package/dist/config/cli-registry/schema.js +15 -1
- package/dist/config/cli-registry/schema.js.map +1 -1
- package/dist/config/cli-registry/stock.d.ts.map +1 -1
- package/dist/config/cli-registry/stock.js +12 -0
- package/dist/config/cli-registry/stock.js.map +1 -1
- package/dist/config/cli-registry/types.d.ts +23 -0
- package/dist/config/cli-registry/types.d.ts.map +1 -1
- package/dist/services/unified-session-service.d.ts +28 -10
- package/dist/services/unified-session-service.d.ts.map +1 -1
- package/dist/services/unified-session-service.js +11 -6
- package/dist/services/unified-session-service.js.map +1 -1
- package/dist/session.d.ts +11 -0
- package/dist/session.d.ts.map +1 -1
- package/dist/session.js +90 -41
- package/dist/session.js.map +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.3a89ac57.js.gz +0 -0
- package/dist/web/public/approvals-ui.js.gz +0 -0
- package/dist/web/public/constants.7a635e34.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/home-sessions.js.gz +0 -0
- package/dist/web/public/i18n.9db8c4e0.js.gz +0 -0
- package/dist/web/public/image-input.ee16ad88.js.gz +0 -0
- package/dist/web/public/index.html +3 -3
- package/dist/web/public/index.html.br +0 -0
- package/dist/web/public/index.html.gz +0 -0
- package/dist/web/public/input-cjk.87f14251.js.gz +0 -0
- package/dist/web/public/keyboard-accessory.daf18302.js.gz +0 -0
- package/dist/web/public/mobile-handlers.0f83a608.js.gz +0 -0
- package/dist/web/public/mobile-overview.js +12 -1
- package/dist/web/public/mobile-overview.js.br +0 -0
- package/dist/web/public/mobile-overview.js.gz +0 -0
- package/dist/web/public/mobile.7112f27d.css.gz +0 -0
- package/dist/web/public/notification-manager.36ea4624.js.gz +0 -0
- package/dist/web/public/orchestrator-panel.js.gz +0 -0
- package/dist/web/public/{panels-ui.db4785bf.js → panels-ui.9eb5b235.js} +1 -1
- package/dist/web/public/panels-ui.9eb5b235.js.br +0 -0
- package/dist/web/public/panels-ui.9eb5b235.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/readmymind-ui.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-lineage.js.gz +0 -0
- package/dist/web/public/{session-ui.a9c126c6.js → session-ui.cd222710.js} +1 -1
- package/dist/web/public/session-ui.cd222710.js.br +0 -0
- package/dist/web/public/session-ui.cd222710.js.gz +0 -0
- package/dist/web/public/settings-ui.554f9cdd.js.gz +0 -0
- package/dist/web/public/styles.298f81f2.css.gz +0 -0
- package/dist/web/public/subagent-windows.e6ca799f.js.gz +0 -0
- package/dist/web/public/sw.js.gz +0 -0
- package/dist/web/public/tab-rail-resize.42c24949.js.gz +0 -0
- package/dist/web/public/{terminal-ui.70445f81.js → terminal-ui.a39b5aa7.js} +2 -2
- package/dist/web/public/terminal-ui.a39b5aa7.js.br +0 -0
- package/dist/web/public/terminal-ui.a39b5aa7.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-predictive-echo.bd6882b8.js.gz +0 -0
- package/dist/web/public/vendor/xterm-zerolag-input.6fee72f2.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.c4b51eb6.js.gz +0 -0
- package/dist/web/public/voice-pcm-worklet.js.gz +0 -0
- package/dist/web/public/webview-tabs.js.gz +0 -0
- package/dist/web/routes/session-routes.d.ts.map +1 -1
- package/dist/web/routes/session-routes.js +53 -1
- package/dist/web/routes/session-routes.js.map +1 -1
- package/package.json +1 -1
- package/dist/web/public/panels-ui.db4785bf.js.br +0 -0
- package/dist/web/public/panels-ui.db4785bf.js.gz +0 -0
- package/dist/web/public/session-ui.a9c126c6.js.br +0 -0
- package/dist/web/public/session-ui.a9c126c6.js.gz +0 -0
- package/dist/web/public/terminal-ui.70445f81.js.br +0 -0
- package/dist/web/public/terminal-ui.70445f81.js.gz +0 -0
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -3462,18 +3462,18 @@
|
|
|
3462
3462
|
<script defer src="sanitize-html.bc7078d6.js"></script>
|
|
3463
3463
|
<script defer src="app.3a89ac57.js"></script>
|
|
3464
3464
|
<script defer src="tab-rail-resize.42c24949.js"></script>
|
|
3465
|
-
<script defer src="terminal-ui.
|
|
3465
|
+
<script defer src="terminal-ui.a39b5aa7.js"></script>
|
|
3466
3466
|
<script defer src="respawn-ui.ff0dae4c.js"></script>
|
|
3467
3467
|
<script defer src="ralph-panel.6de2d0f8.js"></script>
|
|
3468
3468
|
<script defer src="orchestrator-panel.js"></script>
|
|
3469
3469
|
<script defer src="cron-ui.js"></script>
|
|
3470
3470
|
<script defer src="settings-ui.554f9cdd.js"></script>
|
|
3471
|
-
<script defer src="panels-ui.
|
|
3471
|
+
<script defer src="panels-ui.9eb5b235.js"></script>
|
|
3472
3472
|
<script defer src="readmymind-ui.js"></script>
|
|
3473
3473
|
<script defer src="ultracode-panel.js"></script>
|
|
3474
3474
|
<script defer src="approvals-ui.js"></script>
|
|
3475
3475
|
<script defer src="admin-ui.js"></script>
|
|
3476
|
-
<script defer src="session-ui.
|
|
3476
|
+
<script defer src="session-ui.cd222710.js"></script>
|
|
3477
3477
|
<script defer src="webview-tabs.js"></script>
|
|
3478
3478
|
<script defer src="mobile-overview.js"></script>
|
|
3479
3479
|
<script defer src="home-sessions.js"></script>
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -228,6 +228,11 @@ Object.assign(CodemanApp.prototype, {
|
|
|
228
228
|
name: item.name || '',
|
|
229
229
|
title: title || item.name || dir.split('/').pop() || item.sessionId.slice(0, 8),
|
|
230
230
|
mode: item.mode || 'claude',
|
|
231
|
+
// Only the scanner sets this, and only for a codex rollout. Dropping it here
|
|
232
|
+
// is not cosmetic: resumeMobileOverviewSession() passes row.resumeId on to
|
|
233
|
+
// resumeHistorySession(), so without it a tapped Codex row starts a FRESH
|
|
234
|
+
// session on a thread that is already on disk.
|
|
235
|
+
resumeId: item.resumeId || undefined,
|
|
231
236
|
caseName: matched ? matched.name : '',
|
|
232
237
|
dir: this._shortenHomePath ? this._shortenHomePath(dir) : dir,
|
|
233
238
|
at: item.lastActivityAt || item.createdAt || 0,
|
|
@@ -389,7 +394,13 @@ Object.assign(CodemanApp.prototype, {
|
|
|
389
394
|
async resumeMobileOverviewSession(sessionId) {
|
|
390
395
|
const row = (this._mobileOverviewPastRows || []).find((r) => r.id === sessionId);
|
|
391
396
|
if (!row || !row.workingDir) return;
|
|
392
|
-
await this.resumeHistorySession(
|
|
397
|
+
await this.resumeHistorySession(
|
|
398
|
+
row.claudeSessionId || row.id,
|
|
399
|
+
row.workingDir,
|
|
400
|
+
row.name || undefined,
|
|
401
|
+
row.mode,
|
|
402
|
+
row.resumeId
|
|
403
|
+
);
|
|
393
404
|
},
|
|
394
405
|
|
|
395
406
|
// ═══════════════════════════════════════════════════════════════
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
<span class="command-palette-subtitle">${escapeHtml(i.subtitle||"")}</span>
|
|
7
7
|
</span>
|
|
8
8
|
</button>
|
|
9
|
-
`}).join("")},moveCommandPaletteSelection(e){const t=this.commandPaletteItems||this.buildCommandPaletteItems(document.getElementById("commandPaletteSearch")?.value||"");t.length&&(this.commandPaletteActiveIndex=(this.commandPaletteActiveIndex+e+t.length)%t.length,this.renderCommandPalette())},async activateCommandPaletteItem(e=this.commandPaletteActiveIndex||0){const t=(this.commandPaletteItems||[])[e];if(t){if(this.closeCommandPalette(),t.type==="session"&&t.sessionId){await this.selectSession(t.sessionId);return}if(t.type==="browse-sessions"){this.openSessionManager();return}if(t.type==="new-session"){const s=document.getElementById("quickStartCase");if(s&&t.caseName){if(s.tagName==="SELECT"&&typeof s.appendChild=="function"&&!Array.from(s.options||[]).some(n=>n.value===t.caseName)){const n=document.createElement("option");n.value=t.caseName,n.textContent=t.caseName,s.appendChild(n)}typeof this.selectQuickStartCase=="function"?this.selectQuickStartCase(t.caseName):s.value=t.caseName}await this.run()}}},async openSessionManager(){const e=document.getElementById("sessionManagerModal");if(e&&(e.classList.add("active"),this._sessionManagerEscWired||(this._sessionManagerEscWired=!0,e.addEventListener("keydown",s=>{s.key==="Escape"&&(s.preventDefault(),s.stopPropagation(),this.closeSessionManager())}))),!Array.isArray(this.cases)||this.cases.length===0)try{const s=await fetch("/api/cases"),n=s.ok?await s.json():null;this.cases=n?.data||[]}catch{this.cases=this.cases||[]}const t=document.getElementById("sessionManagerSearch");t&&(this._sessionManagerSearchWired||(this._sessionManagerSearchWired=!0,t.addEventListener("input",()=>{const s=t.value.trim();this._debouncedCall("sessionManagerSearch",()=>this._loadSessionManagerList(s),200)})),t.value="",t.focus()),await this._loadSessionManagerList("")},closeSessionManager(){const e=document.getElementById("sessionManagerModal");e&&e.classList.remove("active")},_setSessionManagerMessage(e,t){e.replaceChildren();const s=document.createElement("p");s.className="empty-message",s.textContent=t,e.appendChild(s)},async _loadSessionManagerList(e=""){this._sessionManagerQuery=e;const t=document.getElementById("sessionManagerList");if(t)try{const s="/api/sessions/unified?limit=200"+(e?"&q="+encodeURIComponent(e):""),n=await fetch(s),i=await n.json().catch(()=>null);if(!n.ok||!i||i.success===!1||!i.data){this._setSessionManagerMessage(t,i?.error||`Failed to load sessions (HTTP ${n.status})`);return}const a=i.data.sessions||[];if(t.replaceChildren(),a.length===0){this._setSessionManagerMessage(t,e?"No sessions match your search":"No sessions found");return}for(const r of a){const o={sessionId:r.sessionId,workingDir:r.workingDir||"",sizeBytes:r.sizeBytes??0,lastModified:new Date(r.lastActivityAt??r.createdAt??Date.now()).toISOString(),firstPrompt:r.firstPrompt||r.name||"",gitBranch:r.gitBranch,worktreeName:r.worktreeName,worktreeRepo:r.worktreeRepo},l=!!this.sessions?.has?.(r.sessionId),c=this._buildHistoryItem(o,this.cases,{showViewAll:!1,onActivate:()=>{this.closeSessionManager(),l?this.selectSession(r.sessionId):o.workingDir&&this.resumeHistorySession(r.claudeSessionId||r.sessionId,o.workingDir,void 0,r.mode)}});t.appendChild(c)}}catch(s){console.error("[_loadSessionManagerList]",s),this._setSessionManagerMessage(t,"Failed to load sessions")}},async openAwayDigest(e="since-last-visit"){this.awayDigestRange=e,this._awayDigestLoadedSuccessfully=!1,this._awayDigestSinceLastVisitGeneratedAt=void 0;const t=document.getElementById("awayDigestModal");t&&t.classList.add("active"),this.updateAwayDigestRangeControls(),await this.loadAwayDigest()},closeAwayDigest(){const e=document.getElementById("awayDigestModal"),t=this._awayDigestSinceLastVisitGeneratedAt;if(Number.isFinite(t))try{localStorage.setItem(AWAY_DIGEST_LAST_VIEWED_KEY,String(t))}catch(s){console.warn("Failed to save away digest last-viewed marker:",s)}e&&e.classList.remove("active")},_onSessionListMaybeChanged(){const e=document.getElementById("sessionManagerModal");e&&e.classList.contains("active")&&this._debouncedCall("sessionManagerRefresh",()=>this._loadSessionManagerList(this._sessionManagerQuery||""),400);const t=document.getElementById("welcomeOverlay");t&&t.classList.contains("visible")&&this._debouncedCall("welcomeHistoryRefresh",()=>this.loadHistorySessions(),600)},setAwayDigestRange(e){this.awayDigestRange=e,this._awayDigestLoadedSuccessfully=!1,this.updateAwayDigestRangeControls(),this.loadAwayDigest()},updateAwayDigestRangeControls(){const e=this.awayDigestRange||"since-last-visit";document.querySelectorAll("[data-away-range]").forEach(s=>{s.classList.toggle("active",s.dataset.awayRange===e)});const t=document.getElementById("awayDigestCustomRange");t&&t.classList.toggle("active",e==="custom"),e==="custom"&&this.ensureAwayDigestCustomDefaults()},ensureAwayDigestCustomDefaults(){const e=document.getElementById("awayDigestCustomSince"),t=document.getElementById("awayDigestCustomUntil");if(!e||!t)return;const s=new Date;if(t.value||(t.value=this.formatAwayDigestDateTimeLocal(s)),!e.value){const n=new Date(s.getTime()-36e5);e.value=this.formatAwayDigestDateTimeLocal(n)}},formatAwayDigestDateTimeLocal(e){const t=s=>String(s).padStart(2,"0");return`${e.getFullYear()}-${t(e.getMonth()+1)}-${t(e.getDate())}T${t(e.getHours())}:${t(e.getMinutes())}`},async loadAwayDigest(){const e=document.getElementById("awayDigestSummary"),t=document.getElementById("awayDigestFreshness"),s=document.getElementById("awayDigestSections");e&&(e.innerHTML='<div class="away-digest-loading">Loading digest...</div>'),t&&(t.textContent=""),s&&(s.innerHTML="");try{const n=this.awayDigestRange||"since-last-visit",i=new URLSearchParams({range:n});if(n==="since-last-visit"){const o=this.readAwayDigestLastViewed();Number.isFinite(o)&&i.set("lastViewed",String(o))}if(n==="custom"){this.ensureAwayDigestCustomDefaults();const o=this.readAwayDigestDateTimeInput("awayDigestCustomSince"),l=this.readAwayDigestDateTimeInput("awayDigestCustomUntil");if(!Number.isFinite(o))throw new Error("Choose a custom start time");i.set("since",String(o)),Number.isFinite(l)&&i.set("until",String(l))}const a=await fetch(`/api/away-digest?${i.toString()}`),r=await a.json();if(!a.ok||!r.success)throw new Error(r.error||"Failed to load away digest");this._awayDigestLoadedSuccessfully=!0,this._awayDigestGeneratedAt=r.digest.generatedAt,n==="since-last-visit"&&(this._awayDigestSinceLastVisitGeneratedAt=r.digest.generatedAt),this.renderAwayDigest(r.digest)}catch(n){const i=n instanceof Error?n.message:"Failed to load away digest";console.error("Failed to fetch away digest:",n),e&&(e.innerHTML='<div class="away-digest-load-error">Failed to load away digest</div>'),s&&(s.innerHTML=`<div class="empty-message">${escapeHtml(i)}</div>`),this.showToast(i,"error")}},readAwayDigestLastViewed(){try{const e=localStorage.getItem(AWAY_DIGEST_LAST_VIEWED_KEY),t=Number(e);return Number.isFinite(t)?t:void 0}catch{return}},readAwayDigestDateTimeInput(e){const t=document.getElementById(e);if(!t||!t.value)return;const s=Date.parse(t.value);return Number.isFinite(s)?s:void 0},renderAwayDigest(e){const t=document.getElementById("awayDigestSummary"),s=document.getElementById("awayDigestFreshness"),n=document.getElementById("awayDigestSections");if(!t||!s||!n)return;const i=e.totals.inputTokens||0,a=e.totals.outputTokens||0,r=e.totals.estimatedCost||0;t.innerHTML=`
|
|
9
|
+
`}).join("")},moveCommandPaletteSelection(e){const t=this.commandPaletteItems||this.buildCommandPaletteItems(document.getElementById("commandPaletteSearch")?.value||"");t.length&&(this.commandPaletteActiveIndex=(this.commandPaletteActiveIndex+e+t.length)%t.length,this.renderCommandPalette())},async activateCommandPaletteItem(e=this.commandPaletteActiveIndex||0){const t=(this.commandPaletteItems||[])[e];if(t){if(this.closeCommandPalette(),t.type==="session"&&t.sessionId){await this.selectSession(t.sessionId);return}if(t.type==="browse-sessions"){this.openSessionManager();return}if(t.type==="new-session"){const s=document.getElementById("quickStartCase");if(s&&t.caseName){if(s.tagName==="SELECT"&&typeof s.appendChild=="function"&&!Array.from(s.options||[]).some(n=>n.value===t.caseName)){const n=document.createElement("option");n.value=t.caseName,n.textContent=t.caseName,s.appendChild(n)}typeof this.selectQuickStartCase=="function"?this.selectQuickStartCase(t.caseName):s.value=t.caseName}await this.run()}}},async openSessionManager(){const e=document.getElementById("sessionManagerModal");if(e&&(e.classList.add("active"),this._sessionManagerEscWired||(this._sessionManagerEscWired=!0,e.addEventListener("keydown",s=>{s.key==="Escape"&&(s.preventDefault(),s.stopPropagation(),this.closeSessionManager())}))),!Array.isArray(this.cases)||this.cases.length===0)try{const s=await fetch("/api/cases"),n=s.ok?await s.json():null;this.cases=n?.data||[]}catch{this.cases=this.cases||[]}const t=document.getElementById("sessionManagerSearch");t&&(this._sessionManagerSearchWired||(this._sessionManagerSearchWired=!0,t.addEventListener("input",()=>{const s=t.value.trim();this._debouncedCall("sessionManagerSearch",()=>this._loadSessionManagerList(s),200)})),t.value="",t.focus()),await this._loadSessionManagerList("")},closeSessionManager(){const e=document.getElementById("sessionManagerModal");e&&e.classList.remove("active")},_setSessionManagerMessage(e,t){e.replaceChildren();const s=document.createElement("p");s.className="empty-message",s.textContent=t,e.appendChild(s)},async _loadSessionManagerList(e=""){this._sessionManagerQuery=e;const t=document.getElementById("sessionManagerList");if(t)try{const s="/api/sessions/unified?limit=200"+(e?"&q="+encodeURIComponent(e):""),n=await fetch(s),i=await n.json().catch(()=>null);if(!n.ok||!i||i.success===!1||!i.data){this._setSessionManagerMessage(t,i?.error||`Failed to load sessions (HTTP ${n.status})`);return}const a=i.data.sessions||[];if(t.replaceChildren(),a.length===0){this._setSessionManagerMessage(t,e?"No sessions match your search":"No sessions found");return}for(const r of a){const o={sessionId:r.sessionId,workingDir:r.workingDir||"",sizeBytes:r.sizeBytes??0,lastModified:new Date(r.lastActivityAt??r.createdAt??Date.now()).toISOString(),firstPrompt:r.firstPrompt||r.name||"",gitBranch:r.gitBranch,worktreeName:r.worktreeName,worktreeRepo:r.worktreeRepo},l=!!this.sessions?.has?.(r.sessionId),c=this._buildHistoryItem(o,this.cases,{showViewAll:!1,onActivate:()=>{this.closeSessionManager(),l?this.selectSession(r.sessionId):o.workingDir&&this.resumeHistorySession(r.claudeSessionId||r.sessionId,o.workingDir,void 0,r.mode,r.resumeId)}});t.appendChild(c)}}catch(s){console.error("[_loadSessionManagerList]",s),this._setSessionManagerMessage(t,"Failed to load sessions")}},async openAwayDigest(e="since-last-visit"){this.awayDigestRange=e,this._awayDigestLoadedSuccessfully=!1,this._awayDigestSinceLastVisitGeneratedAt=void 0;const t=document.getElementById("awayDigestModal");t&&t.classList.add("active"),this.updateAwayDigestRangeControls(),await this.loadAwayDigest()},closeAwayDigest(){const e=document.getElementById("awayDigestModal"),t=this._awayDigestSinceLastVisitGeneratedAt;if(Number.isFinite(t))try{localStorage.setItem(AWAY_DIGEST_LAST_VIEWED_KEY,String(t))}catch(s){console.warn("Failed to save away digest last-viewed marker:",s)}e&&e.classList.remove("active")},_onSessionListMaybeChanged(){const e=document.getElementById("sessionManagerModal");e&&e.classList.contains("active")&&this._debouncedCall("sessionManagerRefresh",()=>this._loadSessionManagerList(this._sessionManagerQuery||""),400);const t=document.getElementById("welcomeOverlay");t&&t.classList.contains("visible")&&this._debouncedCall("welcomeHistoryRefresh",()=>this.loadHistorySessions(),600)},setAwayDigestRange(e){this.awayDigestRange=e,this._awayDigestLoadedSuccessfully=!1,this.updateAwayDigestRangeControls(),this.loadAwayDigest()},updateAwayDigestRangeControls(){const e=this.awayDigestRange||"since-last-visit";document.querySelectorAll("[data-away-range]").forEach(s=>{s.classList.toggle("active",s.dataset.awayRange===e)});const t=document.getElementById("awayDigestCustomRange");t&&t.classList.toggle("active",e==="custom"),e==="custom"&&this.ensureAwayDigestCustomDefaults()},ensureAwayDigestCustomDefaults(){const e=document.getElementById("awayDigestCustomSince"),t=document.getElementById("awayDigestCustomUntil");if(!e||!t)return;const s=new Date;if(t.value||(t.value=this.formatAwayDigestDateTimeLocal(s)),!e.value){const n=new Date(s.getTime()-36e5);e.value=this.formatAwayDigestDateTimeLocal(n)}},formatAwayDigestDateTimeLocal(e){const t=s=>String(s).padStart(2,"0");return`${e.getFullYear()}-${t(e.getMonth()+1)}-${t(e.getDate())}T${t(e.getHours())}:${t(e.getMinutes())}`},async loadAwayDigest(){const e=document.getElementById("awayDigestSummary"),t=document.getElementById("awayDigestFreshness"),s=document.getElementById("awayDigestSections");e&&(e.innerHTML='<div class="away-digest-loading">Loading digest...</div>'),t&&(t.textContent=""),s&&(s.innerHTML="");try{const n=this.awayDigestRange||"since-last-visit",i=new URLSearchParams({range:n});if(n==="since-last-visit"){const o=this.readAwayDigestLastViewed();Number.isFinite(o)&&i.set("lastViewed",String(o))}if(n==="custom"){this.ensureAwayDigestCustomDefaults();const o=this.readAwayDigestDateTimeInput("awayDigestCustomSince"),l=this.readAwayDigestDateTimeInput("awayDigestCustomUntil");if(!Number.isFinite(o))throw new Error("Choose a custom start time");i.set("since",String(o)),Number.isFinite(l)&&i.set("until",String(l))}const a=await fetch(`/api/away-digest?${i.toString()}`),r=await a.json();if(!a.ok||!r.success)throw new Error(r.error||"Failed to load away digest");this._awayDigestLoadedSuccessfully=!0,this._awayDigestGeneratedAt=r.digest.generatedAt,n==="since-last-visit"&&(this._awayDigestSinceLastVisitGeneratedAt=r.digest.generatedAt),this.renderAwayDigest(r.digest)}catch(n){const i=n instanceof Error?n.message:"Failed to load away digest";console.error("Failed to fetch away digest:",n),e&&(e.innerHTML='<div class="away-digest-load-error">Failed to load away digest</div>'),s&&(s.innerHTML=`<div class="empty-message">${escapeHtml(i)}</div>`),this.showToast(i,"error")}},readAwayDigestLastViewed(){try{const e=localStorage.getItem(AWAY_DIGEST_LAST_VIEWED_KEY),t=Number(e);return Number.isFinite(t)?t:void 0}catch{return}},readAwayDigestDateTimeInput(e){const t=document.getElementById(e);if(!t||!t.value)return;const s=Date.parse(t.value);return Number.isFinite(s)?s:void 0},renderAwayDigest(e){const t=document.getElementById("awayDigestSummary"),s=document.getElementById("awayDigestFreshness"),n=document.getElementById("awayDigestSections");if(!t||!s||!n)return;const i=e.totals.inputTokens||0,a=e.totals.outputTokens||0,r=e.totals.estimatedCost||0;t.innerHTML=`
|
|
10
10
|
<div class="away-digest-card">
|
|
11
11
|
<span class="away-digest-card-label">Needs Attention</span>
|
|
12
12
|
<span class="away-digest-card-value">${e.totals.needsAttention}</span>
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
<span class="case-combobox-check">${d?"\u2713":""}</span>
|
|
11
11
|
<span class="case-combobox-option-label">${escapeHtml(o.label)}</span>
|
|
12
12
|
</button>
|
|
13
|
-
`}).join(""),t.classList.remove("hidden"),e.setAttribute("aria-activedescendant",`quickStartCaseOption-${this._casePickerActiveIndex}`)},selectQuickStartCase(e,{save:t=!0}={}){const s=document.getElementById("quickStartCase");if(!s)return;s.value=e||"testcase",this.updateCasePickerInput(s.value),this.closeCasePicker(),this.updateDirDisplayForCase(s.value),this.updateMobileCaseLabel(s.value);const n=(this.cases||[]).find(a=>a.name===s.value);n?.location==="docker"&&this._probeDockerCaseModes(n,null),t&&this.saveLastUsedCase(s.value)},setupQuickStartCasePicker(){const e=document.getElementById("quickStartCase"),t=document.getElementById("quickStartCaseSearch"),s=document.getElementById("quickStartCaseList"),n=document.getElementById("quickStartCasePicker");!e||!t||!s||!n||t.dataset.listenerAdded||(t.addEventListener("focus",()=>{t.select?.(),this.openCasePicker("")}),t.addEventListener("click",()=>{t.select?.(),this.openCasePicker("")}),t.addEventListener("input",()=>{this.openCasePicker(t.value)}),t.addEventListener("keydown",a=>{const i=this.filterCasePickerOptions(this.getCasePickerOptions(),this._casePickerFilter||t.value);if(a.key==="ArrowDown")a.preventDefault(),this._casePickerActiveIndex=Math.min((this._casePickerActiveIndex||0)+1,Math.max(0,i.length-1)),this._casePickerOpen?this.renderCasePickerList():this.openCasePicker(t.value);else if(a.key==="ArrowUp")a.preventDefault(),this._casePickerActiveIndex=Math.max((this._casePickerActiveIndex||0)-1,0),this._casePickerOpen?this.renderCasePickerList():this.openCasePicker(t.value);else if(a.key==="Enter"){const o=i[this._casePickerActiveIndex||0];o&&(a.preventDefault(),this.selectQuickStartCase(o.name),this.run?.())}else a.key==="Escape"?(a.preventDefault(),this.updateCasePickerInput(e.value),this.closeCasePicker()):a.key==="Tab"&&(this.updateCasePickerInput(e.value),this.closeCasePicker())}),s.addEventListener("mousedown",a=>a.preventDefault()),s.addEventListener("click",a=>{const i=a.target.closest?.(".case-combobox-option");i?.dataset?.case&&this.selectQuickStartCase(i.dataset.case)}),document.addEventListener&&!this._casePickerDocumentListenerAdded&&(document.addEventListener("pointerdown",a=>{n.contains(a.target)||(this.updateCasePickerInput(e.value),this.closeCasePicker())}),this._casePickerDocumentListenerAdded=!0),t.dataset.listenerAdded="true")},async loadQuickStartCases(e=null,t=null){try{let s=null;try{const r=t?await t:await fetch("/api/settings").then(c=>c.ok?c.json():null).then(c=>c?.data??null);r&&(s=r.lastUsedCase||null)}catch{}const a=(await(await fetch("/api/cases")).json()).data;this.cases=a,console.log("[loadQuickStartCases] Loaded cases:",a.map(r=>r.name),"lastUsedCase:",s);const i=document.getElementById("quickStartCase"),o=this.getCasePickerOptions();if(this.renderQuickStartCaseSelectOptions(i,o),console.log("[loadQuickStartCases] Set options:",i.innerHTML.substring(0,200)),e)i.value=e,this.updateDirDisplayForCase(e),this.updateMobileCaseLabel(e);else if(s&&a.some(r=>r.name===s))i.value=s,this.updateDirDisplayForCase(s),this.updateMobileCaseLabel(s);else if(a.length>0){const r=a.find(c=>c.name==="testcase")||a[0];i.value=r.name,this.updateDirDisplayForCase(r.name),this.updateMobileCaseLabel(r.name)}else i.value="testcase",document.getElementById("dirDisplay").textContent="~/codeman-cases/testcase",this.updateMobileCaseLabel("testcase");this.updateCasePickerInput(i.value),this.renderCasePickerList(),this.closeCasePicker(),i.dataset.listenerAdded||(i.addEventListener("change",()=>{this.updateDirDisplayForCase(i.value),this.saveLastUsedCase(i.value),this.updateMobileCaseLabel(i.value),this.updateCasePickerInput(i.value)}),i.dataset.listenerAdded="true"),this.setupQuickStartCasePicker(),this._refreshMobileOverviewIfVisible?.()}catch(s){console.error("Failed to load cases:",s)}},async updateDirDisplayForCase(e){try{const s=(await(await fetch(`/api/cases/${e}`)).json()).data;s.path&&(document.getElementById("dirDisplay").textContent=s.path,document.getElementById("dirInput").value=s.path)}catch{document.getElementById("dirDisplay").textContent=e}},async saveLastUsedCase(e){try{await fetch("/api/settings",{method:"PUT",headers:{"Content-Type":"application/json"},body:JSON.stringify({lastUsedCase:e})})}catch(t){console.error("Failed to save last used case:",t)}},async quickStart(){return this.run()},async _ensureCreatedSessionVisible(e,t){if(!e)return;let s=t;if(!s&&!this.sessions?.has(e)){const a=await(await fetch(`/api/sessions/${encodeURIComponent(e)}`)).json();if(!a.success)throw new Error(a.error||"Failed to load the new session");s=a.data?.session||a.data}s?.id&&this._onSessionCreated(s),this._renderSessionTabsImmediate?.()},async run(){if(this._runInFlight)return;const e=Date.now(),t=Number.isFinite(this._runMinLockMs)?this._runMinLockMs:500,s=document.getElementById("runBtn");this._runInFlight=!0,s&&(s.disabled=!0,s.setAttribute("aria-busy","true"));try{const n=this._runMode||"claude";return n==="opencode"?await this.runOpenCode():n==="codex"?await this.runCodex():n==="gemini"?await this.runGemini():n==="antigravity"?await this.runAntigravity():n==="omp"?await this.runOmp():n==="pi"?await this.runPi():n==="grok"?await this.runGrok():n==="deepseek"?await this.runDeepSeek():n==="shell"?await this.runShell():await this.runClaude()}finally{const n=t-(Date.now()-e);n>0&&await new Promise(a=>setTimeout(a,n)),this._runInFlight=!1,s&&(s.disabled=!1,s.removeAttribute("aria-busy"))}},setRunMode(e){this._runMode=e;try{localStorage.setItem("codeman_runMode",e)}catch{}this._applyRunMode(),this._apiPut("/api/settings",{runMode:e}).catch(()=>{}),document.getElementById("runModeMenu")?.classList.remove("active")},toggleRunModeMenu(e){e?.stopPropagation();const t=document.getElementById("runModeMenu");if(t&&(t.classList.toggle("active"),t.querySelectorAll(".run-mode-option").forEach(s=>{s.classList.toggle("selected",s.dataset.mode===this.runMode)}),t.classList.contains("active"))){this._loadRunModeHistory(),this._refreshRunModeAvailability(t);const s=n=>{t.contains(n.target)||(t.classList.remove("active"),document.removeEventListener("click",s))};setTimeout(()=>document.addEventListener("click",s),0)}},_refreshRunModeAvailability(e){const t=document.getElementById("quickStartCase")?.value,s=t?(this.cases||[]).find(d=>d.name===t):null,n=s?.location==="docker",a=n&&(this._dockerCaseModes?.[t]||s.docker?.availableModes)||null;n&&!this._dockerCaseModes?.[t]&&this._probeDockerCaseModes(s,e);const i=n?this._dockerCaseProbeError?.[t]:null;for(const d of["claude","opencode","codex","gemini","antigravity","pi","grok","deepseek","omp"]){const l=e.querySelector(`.run-mode-option[data-mode="${d}"]`);if(!l)continue;let m;n?m=i?!1:a?a.includes(d):!0:m=this.isCliAvailable(d),l.style.display=m?"flex":"none"}this._renderRunModeNotice(e,i);const o=window.__codemanCliAvailable||{},r=e.querySelector("#runModeDeepSeekInstall");r&&(r.style.display=!o.deepseek&&o.deepseekBinary?"flex":"none");const c=e.querySelector("#runModeDeepSeekWeb");c&&(c.style.display=o.deepseekBinary?"flex":"none")},async runDeepSeekWeb(){document.getElementById("runModeMenu")?.classList.remove("active");const e=this._beginSessionLaunchStatus("Starting the DeepSeek web UI...");try{const s=await(await fetch("/api/deepseek/web",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({authority:location.host})})).json();if(!s.success)throw new Error(s.error||"Failed to start the DeepSeek web UI");const n=s.data.url;let a=[...this.webviews?.values()||[]].find(i=>i.managed==="deepseek-web");if(a){const o=await(await fetch(`/api/webviews/${a.id}`,{method:"PATCH",headers:{"Content-Type":"application/json"},body:JSON.stringify({url:n,trusted:!0})})).json();if(!o.success)throw new Error(o.error||"Failed to update the web tab");a=o.data.webview||o.data}else{const o=await(await fetch("/api/webviews",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({name:"DeepSeek Harness",url:n,icon:"\u{1F433}",managed:"deepseek-web",trusted:!0})})).json();if(!o.success)throw new Error(o.error||"Failed to save the web tab");a=o.data.webview||o.data}await this.refreshWebviews?.(),this._appendSessionLaunchStatus(e,`Serving on ${n} - opening it as a tab.`),a?.id&&await this.openWebview(a.id)}catch(t){this._reportSessionLaunchError(e,t.message)}},async installDeepSeekProfile(){const t=this._beginSessionLaunchStatus("Installing a DeepSeek terminal profile (this can take a minute)...");try{const n=await(await fetch("/api/deepseek/install-profile",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({})})).json();if(!n.success)throw new Error(n.error||"Failed to install the profile");window.__codemanCliAvailable={...window.__codemanCliAvailable||{},deepseek:!!n.data.runnable},this._appendSessionLaunchStatus(t,`Installed ${n.data.package} into profile "${n.data.profile}".`),this.showToast?.(`DeepSeek profile "${n.data.profile}" installed`,"success");const a=document.getElementById("runModeMenu");a&&this._refreshRunModeAvailability(a)}catch(s){this._reportSessionLaunchError(t,s.message)}},_renderRunModeNotice(e,t){if(!e)return;let s=e.querySelector(".run-mode-notice");if(!t){s?.remove();return}s||(s=document.createElement("div"),s.className="run-mode-notice",e.prepend(s)),s.textContent=t},async _probeDockerCaseModes(e,t){const s=e?.name,n=e?.docker?.container,a=e?.docker?.hostId;if(!(!s||!n||!a)&&(this._dockerCaseModes=this._dockerCaseModes||{},!this._dockerModeProbeInFlight?.[s])){this._dockerModeProbeInFlight=this._dockerModeProbeInFlight||{},this._dockerModeProbeInFlight[s]=!0;try{const i=await this._apiJson("/api/docker-cases/adopt-preflight",{method:"POST",body:{hostId:a,container:n}});i?.ok&&Array.isArray(i.availableModes)?(this._dockerCaseModes[s]=i.availableModes,delete this._dockerCaseProbeError?.[s]):(e?.docker?.owned===!1&&(this._dockerCaseProbeError=this._dockerCaseProbeError||{},this._dockerCaseProbeError[s]=i?.error||`Could not read container "${n}".`),delete this._dockerCaseModes[s]),t?.classList.contains("active")&&this._refreshRunModeAvailability(t)}finally{delete this._dockerModeProbeInFlight[s]}}},async _loadRunModeHistory(){const e=document.getElementById("runModeHistory");if(e){e.innerHTML='<div class="run-mode-hist-empty">Loading...</div>';try{const t=await this._fetchHistorySessions(10);if(t.length===0){e.innerHTML='<div class="run-mode-hist-empty">No history</div>';return}e.replaceChildren();for(const s of t){const n=new Date(s.lastModified),a=n.toLocaleDateString("en",{month:"short",day:"numeric"})+" "+n.toLocaleTimeString("en",{hour:"2-digit",minute:"2-digit",hour12:!1}),i=this._shortenHomePath(s.workingDir),o=i.lastIndexOf("/"),r=o===-1?i:i.slice(o+1),c=(o===-1?"":i.slice(0,o)).replace(/\/\.claude\/worktrees$/,""),d=document.createElement("button");d.className="run-mode-option run-mode-hist-row",d.title=s.workingDir,d.dataset.sessionId=s.sessionId,d.dataset.workingDir=s.workingDir;const l=document.createElement("span");l.className="hist-name",l.textContent=r;const m=[l],h=this._worktreeLabel?this._worktreeLabel(s):"";if(h){const u=document.createElement("span");u.className="hist-wt",u.textContent=h,m.push(u)}if(c){const u=document.createElement("span");u.className="hist-dir",u.textContent=c,m.push(u)}const C=document.createElement("span");C.className="hist-meta",C.textContent=a,m.push(C),d.append(...m),d.addEventListener("click",u=>{u.stopPropagation(),this.resumeHistorySession(s.sessionId,s.workingDir,s.name,s.mode)}),e.appendChild(d)}}catch{e.innerHTML='<div class="run-mode-hist-empty">Failed to load</div>'}}},_applyRunMode(){const e=this.runMode,t=document.getElementById("runBtn"),s=t?.nextElementSibling,n=document.getElementById("runBtnLabel");t&&(t.className=`btn-toolbar btn-run mode-${e}`),s&&(s.className=`btn-toolbar btn-run-gear mode-${e}`),n&&(n.textContent=e==="opencode"?"Run OC":e==="codex"?"Run CX":e==="gemini"?"Run GM":e==="antigravity"?"Run AG":e==="pi"?"Run PI":e==="grok"?"Run GK":e==="deepseek"?"Run DS":e==="omp"?"Run OMP":e==="shell"?"Run SH":"Run")},sendEnterKey(){if(!this.activeSessionId)return;const e=this.terminal?._core?.coreService;if(e&&typeof e.triggerDataEvent=="function"){e.triggerDataEvent("\r",!0);return}this.sendInput("\r")},_initRunMode(){try{this._runMode=localStorage.getItem("codeman_runMode")||"claude"}catch{this._runMode="claude"}this._applyRunMode()},incrementTabCount(){const e=document.getElementById("tabCount"),t=parseInt(e.value)||1;e.value=Math.min(20,t+1)},decrementTabCount(){const e=document.getElementById("tabCount"),t=parseInt(e.value)||1;e.value=Math.max(1,t-1)},incrementShellCount(){const e=document.getElementById("shellCount"),t=parseInt(e.value)||1;e.value=Math.min(20,t+1)},decrementShellCount(){const e=document.getElementById("shellCount"),t=parseInt(e.value)||1;e.value=Math.max(1,t-1)},_nextCaseSessionStartNumber(e,t="w"){const s=new RegExp(`^${t}(\\d+)-([a-zA-Z0-9_-]+)`);let n=1;for(const[,a]of this.sessions||[]){const i=a.name&&a.name.match(s);if(i&&i[2]===e){const o=parseInt(i[1]);o>=n&&(n=o+1)}}return n},_beginSessionLaunchStatus(e,t="1;32"){const s=!this.activeSessionId;return s?(this.terminal.clear(),this.terminal.writeln(`\x1B[${t}m ${e}\x1B[0m`),this.terminal.writeln("")):this.showToast?.(e,"info"),s},_appendSessionLaunchStatus(e,t,s="90"){!e||this.activeSessionId||this.terminal.writeln(`\x1B[${s}m ${t}\x1B[0m`)},_reportSessionLaunchError(e,t){e&&!this.activeSessionId?this.terminal.writeln(`\x1B[1;31m Error: ${t}\x1B[0m`):this.showToast?.(t,"error")},async runClaude(){const e=document.getElementById("quickStartCase").value||"testcase",t=Math.min(20,Math.max(1,parseInt(document.getElementById("tabCount").value)||1)),s=this._beginSessionLaunchStatus(`Starting ${t} Claude session(s) in ${e}...`);this.terminal.focus();try{let a=(await(await fetch(`/api/cases/${e}`)).json())?.data??{};if(!a.path){const S=await(await fetch("/api/cases",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({name:e,description:""})})).json();if(!S.success)throw new Error(S.error||"Failed to create case");a=S.data.case}const i=a.path;if(!i)throw new Error("Case path not found");if(a.location==="remote"||a.location==="docker"){const g=this._nextCaseSessionStartNumber(e);let S;if(a.location==="docker"){const E=this.loadAppSettingsFromStorage(),I=this.getCaseSettings(e).opusContext1m||E.opusContext1mEnabled;S=E.claudeModel||(I?"opus[1m]":"")}const b=[];let T=!1;for(let E=0;E<t;E++){const B=JSON.stringify({caseName:e,mode:"claude",sessionName:`w${g+E}-${e}`,...S!==void 0?{modelOverride:S}:{}}),I=async()=>(await fetch("/api/quick-start",{method:"POST",headers:{"Content-Type":"application/json"},body:B})).json();let v=await I();if(!v.success&&v.errorCode==="CONFLICT"&&a.location==="docker"&&!T&&(T=!0,confirm(`Container config for "${e}" changed since its container was created.
|
|
13
|
+
`}).join(""),t.classList.remove("hidden"),e.setAttribute("aria-activedescendant",`quickStartCaseOption-${this._casePickerActiveIndex}`)},selectQuickStartCase(e,{save:t=!0}={}){const s=document.getElementById("quickStartCase");if(!s)return;s.value=e||"testcase",this.updateCasePickerInput(s.value),this.closeCasePicker(),this.updateDirDisplayForCase(s.value),this.updateMobileCaseLabel(s.value);const n=(this.cases||[]).find(a=>a.name===s.value);n?.location==="docker"&&this._probeDockerCaseModes(n,null),t&&this.saveLastUsedCase(s.value)},setupQuickStartCasePicker(){const e=document.getElementById("quickStartCase"),t=document.getElementById("quickStartCaseSearch"),s=document.getElementById("quickStartCaseList"),n=document.getElementById("quickStartCasePicker");!e||!t||!s||!n||t.dataset.listenerAdded||(t.addEventListener("focus",()=>{t.select?.(),this.openCasePicker("")}),t.addEventListener("click",()=>{t.select?.(),this.openCasePicker("")}),t.addEventListener("input",()=>{this.openCasePicker(t.value)}),t.addEventListener("keydown",a=>{const i=this.filterCasePickerOptions(this.getCasePickerOptions(),this._casePickerFilter||t.value);if(a.key==="ArrowDown")a.preventDefault(),this._casePickerActiveIndex=Math.min((this._casePickerActiveIndex||0)+1,Math.max(0,i.length-1)),this._casePickerOpen?this.renderCasePickerList():this.openCasePicker(t.value);else if(a.key==="ArrowUp")a.preventDefault(),this._casePickerActiveIndex=Math.max((this._casePickerActiveIndex||0)-1,0),this._casePickerOpen?this.renderCasePickerList():this.openCasePicker(t.value);else if(a.key==="Enter"){const o=i[this._casePickerActiveIndex||0];o&&(a.preventDefault(),this.selectQuickStartCase(o.name),this.run?.())}else a.key==="Escape"?(a.preventDefault(),this.updateCasePickerInput(e.value),this.closeCasePicker()):a.key==="Tab"&&(this.updateCasePickerInput(e.value),this.closeCasePicker())}),s.addEventListener("mousedown",a=>a.preventDefault()),s.addEventListener("click",a=>{const i=a.target.closest?.(".case-combobox-option");i?.dataset?.case&&this.selectQuickStartCase(i.dataset.case)}),document.addEventListener&&!this._casePickerDocumentListenerAdded&&(document.addEventListener("pointerdown",a=>{n.contains(a.target)||(this.updateCasePickerInput(e.value),this.closeCasePicker())}),this._casePickerDocumentListenerAdded=!0),t.dataset.listenerAdded="true")},async loadQuickStartCases(e=null,t=null){try{let s=null;try{const r=t?await t:await fetch("/api/settings").then(c=>c.ok?c.json():null).then(c=>c?.data??null);r&&(s=r.lastUsedCase||null)}catch{}const a=(await(await fetch("/api/cases")).json()).data;this.cases=a,console.log("[loadQuickStartCases] Loaded cases:",a.map(r=>r.name),"lastUsedCase:",s);const i=document.getElementById("quickStartCase"),o=this.getCasePickerOptions();if(this.renderQuickStartCaseSelectOptions(i,o),console.log("[loadQuickStartCases] Set options:",i.innerHTML.substring(0,200)),e)i.value=e,this.updateDirDisplayForCase(e),this.updateMobileCaseLabel(e);else if(s&&a.some(r=>r.name===s))i.value=s,this.updateDirDisplayForCase(s),this.updateMobileCaseLabel(s);else if(a.length>0){const r=a.find(c=>c.name==="testcase")||a[0];i.value=r.name,this.updateDirDisplayForCase(r.name),this.updateMobileCaseLabel(r.name)}else i.value="testcase",document.getElementById("dirDisplay").textContent="~/codeman-cases/testcase",this.updateMobileCaseLabel("testcase");this.updateCasePickerInput(i.value),this.renderCasePickerList(),this.closeCasePicker(),i.dataset.listenerAdded||(i.addEventListener("change",()=>{this.updateDirDisplayForCase(i.value),this.saveLastUsedCase(i.value),this.updateMobileCaseLabel(i.value),this.updateCasePickerInput(i.value)}),i.dataset.listenerAdded="true"),this.setupQuickStartCasePicker(),this._refreshMobileOverviewIfVisible?.()}catch(s){console.error("Failed to load cases:",s)}},async updateDirDisplayForCase(e){try{const s=(await(await fetch(`/api/cases/${e}`)).json()).data;s.path&&(document.getElementById("dirDisplay").textContent=s.path,document.getElementById("dirInput").value=s.path)}catch{document.getElementById("dirDisplay").textContent=e}},async saveLastUsedCase(e){try{await fetch("/api/settings",{method:"PUT",headers:{"Content-Type":"application/json"},body:JSON.stringify({lastUsedCase:e})})}catch(t){console.error("Failed to save last used case:",t)}},async quickStart(){return this.run()},async _ensureCreatedSessionVisible(e,t){if(!e)return;let s=t;if(!s&&!this.sessions?.has(e)){const a=await(await fetch(`/api/sessions/${encodeURIComponent(e)}`)).json();if(!a.success)throw new Error(a.error||"Failed to load the new session");s=a.data?.session||a.data}s?.id&&this._onSessionCreated(s),this._renderSessionTabsImmediate?.()},async run(){if(this._runInFlight)return;const e=Date.now(),t=Number.isFinite(this._runMinLockMs)?this._runMinLockMs:500,s=document.getElementById("runBtn");this._runInFlight=!0,s&&(s.disabled=!0,s.setAttribute("aria-busy","true"));try{const n=this._runMode||"claude";return n==="opencode"?await this.runOpenCode():n==="codex"?await this.runCodex():n==="gemini"?await this.runGemini():n==="antigravity"?await this.runAntigravity():n==="omp"?await this.runOmp():n==="pi"?await this.runPi():n==="grok"?await this.runGrok():n==="deepseek"?await this.runDeepSeek():n==="shell"?await this.runShell():await this.runClaude()}finally{const n=t-(Date.now()-e);n>0&&await new Promise(a=>setTimeout(a,n)),this._runInFlight=!1,s&&(s.disabled=!1,s.removeAttribute("aria-busy"))}},setRunMode(e){this._runMode=e;try{localStorage.setItem("codeman_runMode",e)}catch{}this._applyRunMode(),this._apiPut("/api/settings",{runMode:e}).catch(()=>{}),document.getElementById("runModeMenu")?.classList.remove("active")},toggleRunModeMenu(e){e?.stopPropagation();const t=document.getElementById("runModeMenu");if(t&&(t.classList.toggle("active"),t.querySelectorAll(".run-mode-option").forEach(s=>{s.classList.toggle("selected",s.dataset.mode===this.runMode)}),t.classList.contains("active"))){this._loadRunModeHistory(),this._refreshRunModeAvailability(t);const s=n=>{t.contains(n.target)||(t.classList.remove("active"),document.removeEventListener("click",s))};setTimeout(()=>document.addEventListener("click",s),0)}},_refreshRunModeAvailability(e){const t=document.getElementById("quickStartCase")?.value,s=t?(this.cases||[]).find(d=>d.name===t):null,n=s?.location==="docker",a=n&&(this._dockerCaseModes?.[t]||s.docker?.availableModes)||null;n&&!this._dockerCaseModes?.[t]&&this._probeDockerCaseModes(s,e);const i=n?this._dockerCaseProbeError?.[t]:null;for(const d of["claude","opencode","codex","gemini","antigravity","pi","grok","deepseek","omp"]){const l=e.querySelector(`.run-mode-option[data-mode="${d}"]`);if(!l)continue;let m;n?m=i?!1:a?a.includes(d):!0:m=this.isCliAvailable(d),l.style.display=m?"flex":"none"}this._renderRunModeNotice(e,i);const o=window.__codemanCliAvailable||{},r=e.querySelector("#runModeDeepSeekInstall");r&&(r.style.display=!o.deepseek&&o.deepseekBinary?"flex":"none");const c=e.querySelector("#runModeDeepSeekWeb");c&&(c.style.display=o.deepseekBinary?"flex":"none")},async runDeepSeekWeb(){document.getElementById("runModeMenu")?.classList.remove("active");const e=this._beginSessionLaunchStatus("Starting the DeepSeek web UI...");try{const s=await(await fetch("/api/deepseek/web",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({authority:location.host})})).json();if(!s.success)throw new Error(s.error||"Failed to start the DeepSeek web UI");const n=s.data.url;let a=[...this.webviews?.values()||[]].find(i=>i.managed==="deepseek-web");if(a){const o=await(await fetch(`/api/webviews/${a.id}`,{method:"PATCH",headers:{"Content-Type":"application/json"},body:JSON.stringify({url:n,trusted:!0})})).json();if(!o.success)throw new Error(o.error||"Failed to update the web tab");a=o.data.webview||o.data}else{const o=await(await fetch("/api/webviews",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({name:"DeepSeek Harness",url:n,icon:"\u{1F433}",managed:"deepseek-web",trusted:!0})})).json();if(!o.success)throw new Error(o.error||"Failed to save the web tab");a=o.data.webview||o.data}await this.refreshWebviews?.(),this._appendSessionLaunchStatus(e,`Serving on ${n} - opening it as a tab.`),a?.id&&await this.openWebview(a.id)}catch(t){this._reportSessionLaunchError(e,t.message)}},async installDeepSeekProfile(){const t=this._beginSessionLaunchStatus("Installing a DeepSeek terminal profile (this can take a minute)...");try{const n=await(await fetch("/api/deepseek/install-profile",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({})})).json();if(!n.success)throw new Error(n.error||"Failed to install the profile");window.__codemanCliAvailable={...window.__codemanCliAvailable||{},deepseek:!!n.data.runnable},this._appendSessionLaunchStatus(t,`Installed ${n.data.package} into profile "${n.data.profile}".`),this.showToast?.(`DeepSeek profile "${n.data.profile}" installed`,"success");const a=document.getElementById("runModeMenu");a&&this._refreshRunModeAvailability(a)}catch(s){this._reportSessionLaunchError(t,s.message)}},_renderRunModeNotice(e,t){if(!e)return;let s=e.querySelector(".run-mode-notice");if(!t){s?.remove();return}s||(s=document.createElement("div"),s.className="run-mode-notice",e.prepend(s)),s.textContent=t},async _probeDockerCaseModes(e,t){const s=e?.name,n=e?.docker?.container,a=e?.docker?.hostId;if(!(!s||!n||!a)&&(this._dockerCaseModes=this._dockerCaseModes||{},!this._dockerModeProbeInFlight?.[s])){this._dockerModeProbeInFlight=this._dockerModeProbeInFlight||{},this._dockerModeProbeInFlight[s]=!0;try{const i=await this._apiJson("/api/docker-cases/adopt-preflight",{method:"POST",body:{hostId:a,container:n}});i?.ok&&Array.isArray(i.availableModes)?(this._dockerCaseModes[s]=i.availableModes,delete this._dockerCaseProbeError?.[s]):(e?.docker?.owned===!1&&(this._dockerCaseProbeError=this._dockerCaseProbeError||{},this._dockerCaseProbeError[s]=i?.error||`Could not read container "${n}".`),delete this._dockerCaseModes[s]),t?.classList.contains("active")&&this._refreshRunModeAvailability(t)}finally{delete this._dockerModeProbeInFlight[s]}}},async _loadRunModeHistory(){const e=document.getElementById("runModeHistory");if(e){e.innerHTML='<div class="run-mode-hist-empty">Loading...</div>';try{const t=await this._fetchHistorySessions(10);if(t.length===0){e.innerHTML='<div class="run-mode-hist-empty">No history</div>';return}e.replaceChildren();for(const s of t){const n=new Date(s.lastModified),a=n.toLocaleDateString("en",{month:"short",day:"numeric"})+" "+n.toLocaleTimeString("en",{hour:"2-digit",minute:"2-digit",hour12:!1}),i=this._shortenHomePath(s.workingDir),o=i.lastIndexOf("/"),r=o===-1?i:i.slice(o+1),c=(o===-1?"":i.slice(0,o)).replace(/\/\.claude\/worktrees$/,""),d=document.createElement("button");d.className="run-mode-option run-mode-hist-row",d.title=s.workingDir,d.dataset.sessionId=s.sessionId,d.dataset.workingDir=s.workingDir;const l=document.createElement("span");l.className="hist-name",l.textContent=r;const m=[l],h=this._worktreeLabel?this._worktreeLabel(s):"";if(h){const u=document.createElement("span");u.className="hist-wt",u.textContent=h,m.push(u)}if(c){const u=document.createElement("span");u.className="hist-dir",u.textContent=c,m.push(u)}const C=document.createElement("span");C.className="hist-meta",C.textContent=a,m.push(C),d.append(...m),d.addEventListener("click",u=>{u.stopPropagation(),this.resumeHistorySession(s.sessionId,s.workingDir,s.name,s.mode,s.resumeId)}),e.appendChild(d)}}catch{e.innerHTML='<div class="run-mode-hist-empty">Failed to load</div>'}}},_applyRunMode(){const e=this.runMode,t=document.getElementById("runBtn"),s=t?.nextElementSibling,n=document.getElementById("runBtnLabel");t&&(t.className=`btn-toolbar btn-run mode-${e}`),s&&(s.className=`btn-toolbar btn-run-gear mode-${e}`),n&&(n.textContent=e==="opencode"?"Run OC":e==="codex"?"Run CX":e==="gemini"?"Run GM":e==="antigravity"?"Run AG":e==="pi"?"Run PI":e==="grok"?"Run GK":e==="deepseek"?"Run DS":e==="omp"?"Run OMP":e==="shell"?"Run SH":"Run")},sendEnterKey(){if(!this.activeSessionId)return;const e=this.terminal?._core?.coreService;if(e&&typeof e.triggerDataEvent=="function"){e.triggerDataEvent("\r",!0);return}this.sendInput("\r")},_initRunMode(){try{this._runMode=localStorage.getItem("codeman_runMode")||"claude"}catch{this._runMode="claude"}this._applyRunMode()},incrementTabCount(){const e=document.getElementById("tabCount"),t=parseInt(e.value)||1;e.value=Math.min(20,t+1)},decrementTabCount(){const e=document.getElementById("tabCount"),t=parseInt(e.value)||1;e.value=Math.max(1,t-1)},incrementShellCount(){const e=document.getElementById("shellCount"),t=parseInt(e.value)||1;e.value=Math.min(20,t+1)},decrementShellCount(){const e=document.getElementById("shellCount"),t=parseInt(e.value)||1;e.value=Math.max(1,t-1)},_nextCaseSessionStartNumber(e,t="w"){const s=new RegExp(`^${t}(\\d+)-([a-zA-Z0-9_-]+)`);let n=1;for(const[,a]of this.sessions||[]){const i=a.name&&a.name.match(s);if(i&&i[2]===e){const o=parseInt(i[1]);o>=n&&(n=o+1)}}return n},_beginSessionLaunchStatus(e,t="1;32"){const s=!this.activeSessionId;return s?(this.terminal.clear(),this.terminal.writeln(`\x1B[${t}m ${e}\x1B[0m`),this.terminal.writeln("")):this.showToast?.(e,"info"),s},_appendSessionLaunchStatus(e,t,s="90"){!e||this.activeSessionId||this.terminal.writeln(`\x1B[${s}m ${t}\x1B[0m`)},_reportSessionLaunchError(e,t){e&&!this.activeSessionId?this.terminal.writeln(`\x1B[1;31m Error: ${t}\x1B[0m`):this.showToast?.(t,"error")},async runClaude(){const e=document.getElementById("quickStartCase").value||"testcase",t=Math.min(20,Math.max(1,parseInt(document.getElementById("tabCount").value)||1)),s=this._beginSessionLaunchStatus(`Starting ${t} Claude session(s) in ${e}...`);this.terminal.focus();try{let a=(await(await fetch(`/api/cases/${e}`)).json())?.data??{};if(!a.path){const S=await(await fetch("/api/cases",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({name:e,description:""})})).json();if(!S.success)throw new Error(S.error||"Failed to create case");a=S.data.case}const i=a.path;if(!i)throw new Error("Case path not found");if(a.location==="remote"||a.location==="docker"){const g=this._nextCaseSessionStartNumber(e);let S;if(a.location==="docker"){const E=this.loadAppSettingsFromStorage(),I=this.getCaseSettings(e).opusContext1m||E.opusContext1mEnabled;S=E.claudeModel||(I?"opus[1m]":"")}const b=[];let T=!1;for(let E=0;E<t;E++){const B=JSON.stringify({caseName:e,mode:"claude",sessionName:`w${g+E}-${e}`,...S!==void 0?{modelOverride:S}:{}}),I=async()=>(await fetch("/api/quick-start",{method:"POST",headers:{"Content-Type":"application/json"},body:B})).json();let v=await I();if(!v.success&&v.errorCode==="CONFLICT"&&a.location==="docker"&&!T&&(T=!0,confirm(`Container config for "${e}" changed since its container was created.
|
|
14
14
|
|
|
15
15
|
Recreate the container to apply the new config? Workspace files and the conversation survive (the conversation resumes on launch).`))){const _=await(await fetch(`/api/docker-cases/${encodeURIComponent(e)}/recreate`,{method:"POST"})).json();if(!_.success)throw new Error(_.error||"Failed to recreate container");v=await I()}if(!v.success)throw new Error(v.error||"Failed to start remote Claude session");await this._ensureCreatedSessionVisible(v.data.sessionId,v.data.session),b.push(v.data.sessionId)}this._appendSessionLaunchStatus(s,`All ${t} remote session(s) ready`),b[0]&&(await this.selectSession(b[0]),this.loadQuickStartCases()),this.terminal.focus();return}let o=null;const r=this._nextCaseSessionStartNumber(e),c=this.isRalphTrackerEnabledByDefault(),d=[];for(let g=0;g<t;g++)d.push(`w${r+g}-${e}`);const l=this.getCaseSettings(e),m=this.loadAppSettingsFromStorage(),h=this.buildEnvOverrides(l,m),C=Object.keys(h).length>0,u=this.getEffortSetting(m),f=l.opusContext1m||m.opusContext1mEnabled,y=m.claudeModel||(f?"opus[1m]":"");this._appendSessionLaunchStatus(s,`Creating ${t} session(s)...`);const k=d.map(g=>fetch("/api/sessions",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({workingDir:i,name:g,...C?{envOverrides:h}:{},...u?{effort:u}:{},...y!==void 0?{modelOverride:y}:{},statusLineTelemetry:this.planUsageChipEnabled(m)})}).then(S=>S.json())),p=await Promise.all(k),w=[];for(const g of p){if(!g.success)throw new Error(g.error);await this._ensureCreatedSessionVisible(g.data.session.id,g.data.session),w.push(g.data.session.id)}o=w[0],await Promise.all(w.map(g=>fetch(`/api/sessions/${g}/ralph-config`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({enabled:c,disableAutoEnable:!c})}))),this._appendSessionLaunchStatus(s,`Starting ${t} session(s) in parallel...`),await Promise.all(w.map(g=>fetch(`/api/sessions/${g}/interactive`,{method:"POST"}))),this._appendSessionLaunchStatus(s,`All ${t} sessions ready`),o&&(await this.selectSession(o),this.loadQuickStartCases()),this.terminal.focus()}catch(n){this._reportSessionLaunchError(s,n.message)}},stopClaude(){if(!this.activeSessionId)return;const e=document.querySelector(".btn-toolbar.btn-stop");e&&(this._stopConfirmTimer?(clearTimeout(this._stopConfirmTimer),this._stopConfirmTimer=null,e.innerHTML=e.dataset.origHtml,delete e.dataset.origHtml,e.classList.remove("confirming"),fetch(`/api/sessions/${this.activeSessionId}/input`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({input:""})})):(e.dataset.origHtml=e.innerHTML,e.textContent="Tap again",e.classList.add("confirming"),this._stopConfirmTimer=setTimeout(()=>{this._stopConfirmTimer=null,e.dataset.origHtml&&(e.innerHTML=e.dataset.origHtml,delete e.dataset.origHtml),e.classList.remove("confirming")},2e3)))},async runShell(){const e=document.getElementById("quickStartCase").value||"testcase",t=Math.min(20,Math.max(1,parseInt(document.getElementById("shellCount").value)||1)),s=this._beginSessionLaunchStatus(`Starting ${t} Shell session(s) in ${e}...`,"1;33");try{let a=(await(await fetch(`/api/cases/${e}`)).json())?.data??{};if(!a.path){const f=await(await fetch("/api/cases",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({name:e,description:""})})).json();if(!f.success)throw new Error(f.error||"Failed to create case");a=f.data.case}const i=(this.cases||[]).find(u=>u.name===e),o=a.location==="remote"||a.location==="docker"||i?.location==="remote"||i?.location==="docker",r=a.path;if(!r)throw new Error("Case path not found");if(a.location==="remote"||a.location==="docker"){const u=this._nextCaseSessionStartNumber(e,"s"),f=[];for(let y=0;y<t;y++){const p=await(await fetch("/api/quick-start",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({caseName:e,mode:"shell",sessionName:`s${u+y}-${e}`})})).json();if(!p.success)throw new Error(p.error||"Failed to start remote shell session");await this._ensureCreatedSessionVisible(p.data.sessionId,p.data.session),f.push(p.data.sessionId)}f[0]&&await this.selectSession(f[0]),this.terminal.focus();return}const c=this._nextCaseSessionStartNumber(e,"s"),d=[];for(let u=0;u<t;u++)d.push(`s${c+u}-${e}`);const l=d.map(u=>fetch("/api/sessions",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({...o?{caseName:e}:{workingDir:r},mode:"shell",name:u})}).then(f=>f.json())),m=await Promise.all(l),h=[];for(const u of m){if(!u.success)throw new Error(u.error);await this._ensureCreatedSessionVisible(u.data.session.id,u.data.session),h.push(u.data.session.id)}await Promise.all(h.map(u=>fetch(`/api/sessions/${u}/shell`,{method:"POST"})));const C=this.getTerminalDimensions();C&&await Promise.all(h.map(u=>fetch(`/api/sessions/${u}/resize`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(C)}))),h.length>0&&await this.selectSession(h[0]),this.terminal.focus()}catch(n){this._reportSessionLaunchError(s,n.message)}},async runOpenCode(){const e=document.getElementById("quickStartCase").value||"testcase",t=(this.cases||[]).find(a=>a.name===e)?.location,s=t==="remote"||t==="docker",n=this._beginSessionLaunchStatus(`Starting OpenCode session in ${e}...`);this.terminal.focus();try{if(!s&&!(await(await fetch("/api/opencode/status")).json()).data.available){this._reportSessionLaunchError(n,"OpenCode CLI not found. Install with: curl -fsSL https://opencode.ai/install | bash");return}const a=this.buildEnvOverrides(this.getCaseSettings(e),this.loadAppSettingsFromStorage()),o=await(await fetch("/api/quick-start",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({caseName:e,mode:"opencode",sessionName:`w${this._nextCaseSessionStartNumber(e)}-${e}`,...s?{}:{openCodeConfig:{autoAllowTools:!0},...Object.keys(a).length>0?{envOverrides:a}:{}}})})).json();if(!o.success)throw new Error(o.error||"Failed to start OpenCode");await this._ensureCreatedSessionVisible(o.data.sessionId,o.data.session),o.data.sessionId&&await this.selectSession(o.data.sessionId),this.terminal.focus()}catch(a){this._reportSessionLaunchError(n,a.message)}},async runCodex(){const e=document.getElementById("quickStartCase").value||"testcase",t=(this.cases||[]).find(a=>a.name===e)?.location,s=t==="remote"||t==="docker",n=this._beginSessionLaunchStatus(`Starting Codex session in ${e}...`);this.terminal.focus();try{if(!s&&!(await(await fetch("/api/codex/status")).json()).data.available){this._reportSessionLaunchError(n,"Codex CLI not found. Install with: npm install -g @openai/codex");return}const a=this.loadAppSettingsFromStorage(),i=this.buildEnvOverrides(this.getCaseSettings(e),a),r=await(await fetch("/api/quick-start",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({caseName:e,mode:"codex",sessionName:`w${this._nextCaseSessionStartNumber(e)}-${e}`,...s?{}:{codexConfig:{dangerouslyBypassApprovals:a.codexDangerouslyBypassApprovals??!1,animations:a.codexAnimationsEnabled??!1,renderMode:"hybrid"},...Object.keys(i).length>0?{envOverrides:i}:{}}})})).json();if(!r.success)throw new Error(r.error||"Failed to start Codex");await this._ensureCreatedSessionVisible(r.data.sessionId,r.data.session),r.data.sessionId&&await this.selectSession(r.data.sessionId),this.terminal.focus()}catch(a){this._reportSessionLaunchError(n,a.message)}},async runGemini(){const e=document.getElementById("quickStartCase").value||"testcase",t=(this.cases||[]).find(a=>a.name===e)?.location,s=t==="remote"||t==="docker",n=this._beginSessionLaunchStatus(`Starting Gemini session in ${e}...`);this.terminal.focus();try{if(!s&&!(await(await fetch("/api/gemini/status")).json()).data.available){this._reportSessionLaunchError(n,"Gemini CLI not found. Install with: npm install -g @google/gemini-cli");return}const a=this.buildEnvOverrides(this.getCaseSettings(e),this.loadAppSettingsFromStorage()),o=await(await fetch("/api/quick-start",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({caseName:e,mode:"gemini",sessionName:`w${this._nextCaseSessionStartNumber(e)}-${e}`,...s?{}:{geminiConfig:{approvalMode:"yolo"},...Object.keys(a).length>0?{envOverrides:a}:{}}})})).json();if(!o.success)throw new Error(o.error||"Failed to start Gemini");await this._ensureCreatedSessionVisible(o.data.sessionId,o.data.session),o.data.sessionId&&await this.selectSession(o.data.sessionId),this.terminal.focus()}catch(a){this._reportSessionLaunchError(n,a.message)}},async runAntigravity(){const e=document.getElementById("quickStartCase").value||"testcase",t=(this.cases||[]).find(a=>a.name===e)?.location,s=t==="remote"||t==="docker",n=this._beginSessionLaunchStatus(`Starting Antigravity session in ${e}...`);this.terminal.focus();try{if(!s&&!(await(await fetch("/api/antigravity/status")).json()).data.available){this._reportSessionLaunchError(n,"Antigravity CLI not found. Install with: curl -fsSL https://antigravity.google/cli/install.sh | bash");return}const a=this.buildEnvOverrides(this.getCaseSettings(e),this.loadAppSettingsFromStorage()),o=await(await fetch("/api/quick-start",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({caseName:e,mode:"antigravity",sessionName:`w${this._nextCaseSessionStartNumber(e)}-${e}`,...s?{}:{antigravityConfig:{dangerouslySkipPermissions:!0},...Object.keys(a).length>0?{envOverrides:a}:{}}})})).json();if(!o.success)throw new Error(o.error||"Failed to start Antigravity");await this._ensureCreatedSessionVisible(o.data.sessionId,o.data.session),o.data.sessionId&&await this.selectSession(o.data.sessionId),this.terminal.focus()}catch(a){this._reportSessionLaunchError(n,a.message)}},async runPi(){const e=document.getElementById("quickStartCase").value||"testcase",t=(this.cases||[]).find(a=>a.name===e)?.location,s=t==="remote"||t==="docker",n=this._beginSessionLaunchStatus(`Starting Pi session in ${e}...`);this.terminal.focus();try{if(!s&&!(await(await fetch("/api/pi/status")).json()).data.available){this._reportSessionLaunchError(n,"Pi CLI not found. Install with: npm install -g --ignore-scripts @earendil-works/pi-coding-agent");return}const a=this.buildEnvOverrides(this.getCaseSettings(e),this.loadAppSettingsFromStorage()),o=await(await fetch("/api/quick-start",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({caseName:e,mode:"pi",sessionName:`w${this._nextCaseSessionStartNumber(e)}-${e}`,...s||Object.keys(a).length===0?{}:{envOverrides:a}})})).json();if(!o.success)throw new Error(o.error||"Failed to start Pi");await this._ensureCreatedSessionVisible(o.data.sessionId,o.data.session),o.data.sessionId&&await this.selectSession(o.data.sessionId),this.terminal.focus()}catch(a){this._reportSessionLaunchError(n,a.message)}},async runOmp(){const e=document.getElementById("quickStartCase").value||"testcase",t=(this.cases||[]).find(a=>a.name===e)?.location,s=t==="remote"||t==="docker",n=this._beginSessionLaunchStatus(`Starting OMP session in ${e}...`);this.terminal.focus();try{if(!s&&!(await(await fetch("/api/omp/status")).json()).data.available){this._reportSessionLaunchError(n,"OMP CLI not found. Install with: curl -fsSL https://omp.sh/install | sh");return}const a=this.buildEnvOverrides(this.getCaseSettings(e),this.loadAppSettingsFromStorage()),o=await(await fetch("/api/quick-start",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({caseName:e,mode:"omp",sessionName:`w${this._nextCaseSessionStartNumber(e)}-${e}`,...s?{}:{...Object.keys(a).length>0?{envOverrides:a}:{}}})})).json();if(!o.success)throw new Error(o.error||"Failed to start OMP");await this._ensureCreatedSessionVisible(o.data.sessionId,o.data.session),o.data.sessionId&&await this.selectSession(o.data.sessionId),this.terminal.focus()}catch(a){this._reportSessionLaunchError(n,a.message)}},async runGrok(){const e=document.getElementById("quickStartCase").value||"testcase",t=(this.cases||[]).find(a=>a.name===e)?.location,s=t==="remote"||t==="docker",n=this._beginSessionLaunchStatus(`Starting Grok session in ${e}...`);this.terminal.focus();try{if(!s&&!(await(await fetch("/api/grok/status")).json()).data.available){this._reportSessionLaunchError(n,"Grok CLI not found. Install with: curl -fsSL https://x.ai/cli/install.sh | bash");return}const a=this.buildEnvOverrides(this.getCaseSettings(e),this.loadAppSettingsFromStorage()),o=await(await fetch("/api/quick-start",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({caseName:e,mode:"grok",sessionName:`w${this._nextCaseSessionStartNumber(e)}-${e}`,...s?{}:{grokConfig:{alwaysApprove:!0},...Object.keys(a).length>0?{envOverrides:a}:{}}})})).json();if(!o.success)throw new Error(o.error||"Failed to start Grok");await this._ensureCreatedSessionVisible(o.data.sessionId,o.data.session),o.data.sessionId&&await this.selectSession(o.data.sessionId),this.terminal.focus()}catch(a){this._reportSessionLaunchError(n,a.message)}},async runDeepSeek(){const e=document.getElementById("quickStartCase").value||"testcase",t=(this.cases||[]).find(a=>a.name===e)?.location,s=t==="remote"||t==="docker",n=this._beginSessionLaunchStatus(`Starting DeepSeek session in ${e}...`);this.terminal.focus();try{if(!s){const c=(await(await fetch("/api/deepseek/status")).json()).data;if(!c.available){this._reportSessionLaunchError(n,"DeepSeek Harness CLI (dsh) not found. Install with: npm install -g @deepseek-ai/dsh");return}if(!c.runnable){this._reportSessionLaunchError(n,"No interactive DeepSeek Harness profile is installed. DeepSeek ships only web and headless profiles, so the terminal agent comes from a plugin. Install one from the Run menu, or run: dsh plugin --profile dsh-tui add @deepseek-harness-tui/dsh-tui");return}}const a=this.buildEnvOverrides(this.getCaseSettings(e),this.loadAppSettingsFromStorage()),o=await(await fetch("/api/quick-start",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({caseName:e,mode:"deepseek",sessionName:`w${this._nextCaseSessionStartNumber(e)}-${e}`,...s?{}:{deepSeekConfig:{permissionMode:"danger-full-access"},...Object.keys(a).length>0?{envOverrides:a}:{}}})})).json();if(!o.success)throw new Error(o.error||"Failed to start DeepSeek");await this._ensureCreatedSessionVisible(o.data.sessionId,o.data.session),o.data.sessionId&&await this.selectSession(o.data.sessionId),this.terminal.focus()}catch(a){this._reportSessionLaunchError(n,a.message)}},_tabDetachOverrides(){if(this._tabDetachOverrideMap===void 0)try{this._tabDetachOverrideMap=JSON.parse(localStorage.getItem("codeman:tab-detach-overrides")||"{}")||{}}catch{this._tabDetachOverrideMap={}}return this._tabDetachOverrideMap},hasTabDetachOverride(e){return!!this._tabDetachOverrides()[e]},onSessionTabDetachToggle(e){const t=this.editingSessionId;if(!t)return;const s=this._tabDetachOverrides();e?s[t]=1:delete s[t];for(const a of Object.keys(s))a!==t&&this.sessions&&!this.sessions.has(a)&&delete s[a];try{localStorage.setItem("codeman:tab-detach-overrides",JSON.stringify(s))}catch{}const n=document.querySelector(`.session-tab[data-id="${CSS.escape(t)}"]`);n&&n.classList.toggle("tab-show-detach",!!e)},openSessionOptions(e){const t=this.sessions.get(e);if(!t)return;this.editingSessionId=e;const s=document.getElementById("sessionOptShowTabDetach");s&&(s.checked=this.hasTabDetachOverride(e));const n=t.mode==="opencode"||t.mode==="codex"||t.mode==="gemini"||t.mode==="antigravity"||t.mode==="pi"||t.mode==="grok"||t.mode==="deepseek"||t.mode==="omp";this.switchOptionsTab(n?"summary":"respawn");const a=document.getElementById("sessionRespawnStatus"),i=document.getElementById("modalEnableRespawnBtn"),o=document.getElementById("modalStopRespawnBtn");this.respawnStatus[e]?(a.classList.add("active"),a.querySelector(".respawn-status-text").textContent=this.respawnStatus[e].state||"Active",i.style.display="none",o.style.display=""):(a.classList.remove("active"),a.querySelector(".respawn-status-text").textContent="Not active",i.style.display="",o.style.display="none");const r=document.getElementById("sessionRespawnSection");t.mode==="claude"&&t.pid?r.style.display="":r.style.display="none";const c=t.mode==="opencode"||t.mode==="codex"||t.mode==="gemini"||t.mode==="antigravity"||t.mode==="pi"||t.mode==="grok"||t.mode==="deepseek"||t.mode==="omp";document.querySelectorAll("[data-claude-only]").forEach(k=>{k.style.display=c?"none":""}),this.selectDurationPreset(""),this.loadSavedRespawnConfig(e),document.getElementById("modalAutoCompactEnabled").checked=t.autoCompactEnabled??!1,document.getElementById("modalAutoCompactThreshold").value=t.autoCompactThreshold??11e4,document.getElementById("modalAutoCompactPrompt").value=t.autoCompactPrompt??"",document.getElementById("modalAutoClearEnabled").checked=t.autoClearEnabled??!1,document.getElementById("modalAutoClearThreshold").value=t.autoClearThreshold??14e4,document.getElementById("modalAutoResumeEnabled").checked=t.autoResumeEnabled??!1,this.updateAutoResumeStatus(e),document.getElementById("modalImageWatcherEnabled").checked=t.imageWatcherEnabled??!0,document.getElementById("modalFlickerFilterEnabled").checked=t.flickerFilterEnabled??!1;const l=parseSessionPrefix(t.name),m=document.getElementById("modalSessionPrefix");l?(m.textContent=l.prefix+": ",m.style.display="",document.getElementById("modalSessionName").value=l.suffix,document.getElementById("modalSessionName").placeholder="Add description..."):(m.style.display="none",m.textContent="",document.getElementById("modalSessionName").value=t.name||"",document.getElementById("modalSessionName").placeholder="Auto (directory name)");const h=t.color||"default";document.getElementById("sessionColorPicker")?.querySelectorAll(".color-swatch").forEach(k=>{k.classList.toggle("selected",k.dataset.color===h)}),this.renderPresetDropdown(),document.getElementById("respawnPresetSelect").value="",document.getElementById("presetDescriptionHint").textContent="";const u=document.querySelector('#sessionOptionsModal .set-rail-item[data-tab="ralph"]'),f=document.querySelector('#sessionOptionsModal .set-rail-item[data-tab="respawn"]');if(c?(u&&(u.style.display="none"),f&&(f.style.display="none"),this.switchOptionsTab("context")):(u&&(u.style.display=""),f&&(f.style.display="")),!c){const k=this.ralphStates.get(e);this.populateRalphForm({enabled:k?.loop?.enabled??t.ralphLoop?.enabled??!1,completionPhrase:k?.loop?.completionPhrase||t.ralphLoop?.completionPhrase||"",maxIterations:k?.loop?.maxIterations||t.ralphLoop?.maxIterations||0,maxTodos:k?.loop?.maxTodos||t.ralphLoop?.maxTodos,todoExpirationMinutes:k?.loop?.todoExpirationMinutes||t.ralphLoop?.todoExpirationMinutes})}const y=document.getElementById("sessionOptionsModal");y.dataset.chipsReady!=="1"&&(y.dataset.chipsReady="1",y.addEventListener("change",k=>{k.target?.closest?.(".set-chip")&&this._syncSettingsChips()})),this._syncSettingsChips(),y.classList.add("active"),this.activeFocusTrap=new FocusTrap(y),this.activeFocusTrap.activate()},_applyLocalSessionName(e,t){if(typeof t!="string")return;const s=this.sessions.get(e);s&&(s.name=t,this.sessions.set(e,s),this.updateSubagentParentNames?.(e))},async _putSessionName(e,t){const s=await this._apiPut(`/api/sessions/${e}/name`,{name:t});if(!s||!s.ok)return null;let n=null;try{n=await s.json()}catch{return null}if(n&&n.success===!1)return null;const a=n?.data?.name;return typeof a=="string"?a:t},async saveSessionName(){if(!this.editingSessionId)return;const e=this.editingSessionId,t=this.sessions.get(e),s=t?parseSessionPrefix(t.name):null,n=document.getElementById("modalSessionName").value.trim();let a;s?a=s.prefix+(n?": "+n:""):a=n;const i=await this._putSessionName(e,a);if(i===null){this.showToast("Failed to save session name","error");return}this._applyLocalSessionName(e,i),this.renderSessionTabs()},async autoSaveAutoCompact(){if(this.editingSessionId)try{await this._apiPost(`/api/sessions/${this.editingSessionId}/auto-compact`,{enabled:document.getElementById("modalAutoCompactEnabled").checked,threshold:parseInt(document.getElementById("modalAutoCompactThreshold").value)||11e4,prompt:document.getElementById("modalAutoCompactPrompt").value.trim()||void 0})}catch{}},async autoSaveAutoClear(){if(this.editingSessionId)try{await this._apiPost(`/api/sessions/${this.editingSessionId}/auto-clear`,{enabled:document.getElementById("modalAutoClearEnabled").checked,threshold:parseInt(document.getElementById("modalAutoClearThreshold").value)||14e4})}catch{}},async autoSaveAutoResume(){if(!this.editingSessionId)return;const e=document.getElementById("modalAutoResumeEnabled").checked;try{await this._apiPost(`/api/sessions/${this.editingSessionId}/auto-resume`,{enabled:e});const t=this.sessions.get(this.editingSessionId);t&&(t.autoResumeEnabled=e,e||(t.autoResumeAt=void 0)),this.updateAutoResumeStatus(this.editingSessionId),this.showToast(`Auto-resume on usage limit ${e?"enabled":"disabled"}`,"success")}catch(t){this.showToast("Failed to toggle auto-resume: "+t.message,"error")}},updateAutoResumeStatus(e){const t=document.getElementById("autoResumeStatus");if(!t||this.editingSessionId!==e)return;const s=this.sessions.get(e);if(s?.autoResumeAt&&s.autoResumeAt>Date.now()){const n=new Date(s.autoResumeAt).toLocaleTimeString([],{hour:"2-digit",minute:"2-digit"});t.textContent=`Usage limit pause active \u2014 resumes at ${n}`,t.classList.add("active")}else t.textContent="",t.classList.remove("active")},async toggleSessionImageWatcher(){if(!this.editingSessionId)return;const e=document.getElementById("modalImageWatcherEnabled").checked;try{await this._apiPost(`/api/sessions/${this.editingSessionId}/image-watcher`,{enabled:e});const t=this.sessions.get(this.editingSessionId);t&&(t.imageWatcherEnabled=e),this.showToast(`Image watcher ${e?"enabled":"disabled"}`,"success")}catch{this.showToast("Failed to toggle image watcher","error")}},async toggleFlickerFilter(){if(!this.editingSessionId)return;const e=document.getElementById("modalFlickerFilterEnabled").checked;try{await this._apiPost(`/api/sessions/${this.editingSessionId}/flicker-filter`,{enabled:e});const t=this.sessions.get(this.editingSessionId);t&&(t.flickerFilterEnabled=e),this.showToast(`Flicker filter ${e?"enabled":"disabled"}`,"success")}catch{this.showToast("Failed to toggle flicker filter","error")}},async autoSaveRespawnConfig(){if(!this.editingSessionId)return;const e={updatePrompt:document.getElementById("modalRespawnPrompt").value,sendClear:document.getElementById("modalRespawnSendClear").checked,sendInit:document.getElementById("modalRespawnSendInit").checked,kickstartPrompt:document.getElementById("modalRespawnKickstart").value.trim()||void 0,autoAcceptPrompts:document.getElementById("modalRespawnAutoAccept").checked};try{await this._apiPut(`/api/sessions/${this.editingSessionId}/respawn/config`,e)}catch{}},async loadSavedRespawnConfig(e){try{const s=await(await fetch(`/api/sessions/${e}/respawn/config`)).json();if(s.success&&s.data&&s.data.config){const n=s.data.config;document.getElementById("modalRespawnPrompt").value=n.updatePrompt||"update all the docs and CLAUDE.md",document.getElementById("modalRespawnSendClear").checked=n.sendClear??!0,document.getElementById("modalRespawnSendInit").checked=n.sendInit??!0,document.getElementById("modalRespawnKickstart").value=n.kickstartPrompt||"",document.getElementById("modalRespawnAutoAccept").checked=n.autoAcceptPrompts??!0,n.durationMinutes&&(document.querySelector(`.duration-preset-btn[data-minutes="${n.durationMinutes}"]`)?this.selectDurationPreset(String(n.durationMinutes)):(this.selectDurationPreset("custom"),document.getElementById("modalRespawnDuration").value=n.durationMinutes))}}catch{}},selectDurationPreset(e){document.querySelectorAll(".duration-preset-btn").forEach(a=>a.classList.remove("active"));const t=document.querySelector(`.duration-preset-btn[data-minutes="${e}"]`);t&&t.classList.add("active");const s=document.querySelector(".duration-custom-input"),n=document.getElementById("modalRespawnDuration");e==="custom"?(s.classList.add("visible"),n.focus()):(s.classList.remove("visible"),n.value="")},getSelectedDuration(){const e=document.querySelector(".duration-custom-input"),t=document.getElementById("modalRespawnDuration");if(e.classList.contains("visible"))return t.value?parseInt(t.value):null;{const n=document.querySelector(".duration-preset-btn.active")?.dataset.minutes;return n?parseInt(n):null}},switchOptionsTab(e){document.querySelectorAll("#sessionOptionsModal .set-rail-item").forEach(s=>{s.classList.toggle("active",s.dataset.tab===e)}),document.getElementById("respawn-tab").classList.toggle("hidden",e!=="respawn"),document.getElementById("context-tab").classList.toggle("hidden",e!=="context"),document.getElementById("ralph-tab").classList.toggle("hidden",e!=="ralph"),document.getElementById("summary-tab").classList.toggle("hidden",e!=="summary");const t=document.getElementById("sessionOptionsDoc");t&&(t.scrollTop=0),e==="summary"&&this.editingSessionId&&this.loadRunSummary(this.editingSessionId)},getRalphConfig(){return{enabled:document.getElementById("modalRalphEnabled").checked,completionPhrase:document.getElementById("modalRalphPhrase").value.trim(),maxIterations:parseInt(document.getElementById("modalRalphMaxIterations").value)||0,maxTodos:parseInt(document.getElementById("modalRalphMaxTodos").value)||50,todoExpirationMinutes:parseInt(document.getElementById("modalRalphTodoExpiration").value)||60}},populateRalphForm(e){document.getElementById("modalRalphEnabled").checked=e?.enabled??!1,document.getElementById("modalRalphPhrase").value=e?.completionPhrase||"",document.getElementById("modalRalphMaxIterations").value=e?.maxIterations||0,document.getElementById("modalRalphMaxTodos").value=e?.maxTodos||50,document.getElementById("modalRalphTodoExpiration").value=e?.todoExpirationMinutes||60},async saveRalphConfig(){if(!this.editingSessionId){this.showToast("No session selected","warning");return}const e=this.getRalphConfig();e.enabled&&this.ralphClosedSessions.delete(this.editingSessionId);try{const s=await(await fetch(`/api/sessions/${this.editingSessionId}/ralph-config`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(e)})).json();if(s.error)throw new Error(s.error);this.showToast("Ralph config saved","success")}catch(t){this.showToast("Failed to save Ralph config: "+t.message,"error")}},startInlineRename(e){const t=this.sessions.get(e);if(!t)return;this._activeRename?.cancel();const s=document.querySelector(`.tab-name[data-session-id="${e}"]`);if(!s)return;this._inlineRenameActive=!0,s.classList.add("tab-name-renaming");const n=this.getSessionName(t),a=parseSessionPrefix(t.name),i=s.textContent,o=[...s.childNodes].map(p=>p.cloneNode(!0)),r=()=>{s.replaceChildren(...o.map(p=>p.cloneNode(!0)))};for(s.textContent="";s.firstChild;)s.removeChild(s.firstChild);if(a){const p=document.createElement("span");p.className="tab-rename-prefix",p.textContent=a.prefix+": ",p.style.cssText="color: var(--text-muted); font-size: 0.75rem; white-space: nowrap;",s.appendChild(p)}const c=document.createElement("input");c.type="text",c.value=a?a.suffix:t.name||"",c.placeholder=a?"Add description...":n,c.className="tab-rename-input";const d=s.closest(".tab-rail")?"auto":this.isSessionSidebarActive?.()?"100%":"80px";c.style.cssText=`width: ${d}; min-width: 0; font-size: 0.75rem; padding: 2px 4px; background: var(--bg-input); border: 1px solid var(--accent); border-radius: 3px; color: var(--text); outline: none;`,s.appendChild(c),c.focus(),c.select();let l=!1,m=!1,h=!1;const C=()=>{this._activeRename===k&&(this._inlineRenameActive=!1)},u=()=>{this._activeRename===k&&(h=!0,C(),this._activeRename=null,this.renderSessionTabs())},f=()=>{m||h||(m=!0,l=!0,s.classList.remove("tab-name-renaming"),r(),u())},y=async({commit:p})=>{if(l||m)return;if(l=!0,s.classList.remove("tab-name-renaming"),!p){f();return}if(this._activeRename!==k)return;C();const w=c.value.trim(),g=a?a.prefix+(w?": "+w:""):w;if(g===t.name?r():s.textContent=g||i,this.sessions.has(e)&&g!==t.name){const b=await this._putSessionName(e,g);if(m||this._activeRename!==k||!this.sessions.has(e))return;b===null?(r(),this.showToast("Failed to rename","error")):this._applyLocalSessionName(e,b)}u()},k={sessionId:e,cancel:f};this._activeRename=k,c.addEventListener("blur",()=>y({commit:!0})),c.addEventListener("keydown",p=>{p.isComposing||p.keyCode===229||(p.key==="Enter"?(p.preventDefault(),c.blur()):p.key==="Escape"&&(p.preventDefault(),f()))})},toggleCaseSettings(){const e=document.getElementById("caseSettingsPopover");if(e.classList.contains("hidden")){const t=document.getElementById("quickStartCase").value||"testcase",s=this.getCaseSettings(t);document.getElementById("caseAgentTeams").checked=s.agentTeams,document.getElementById("caseOpusContext1m").checked=s.opusContext1m,e.classList.remove("hidden");const n=a=>{!e.contains(a.target)&&!a.target.classList.contains("btn-case-settings")&&(e.classList.add("hidden"),document.removeEventListener("click",n))};setTimeout(()=>document.addEventListener("click",n),0)}else e.classList.add("hidden")},getCaseSettings(e){try{const t=localStorage.getItem("caseSettings_"+e);if(t)return JSON.parse(t)}catch{}return{agentTeams:!1,opusContext1m:!0}},saveCaseSettings(e,t){localStorage.setItem("caseSettings_"+e,JSON.stringify(t))},onCaseSettingChanged(){const e=document.getElementById("quickStartCase").value||"testcase",t=this.getCaseSettings(e);t.agentTeams=document.getElementById("caseAgentTeams").checked,t.opusContext1m=document.getElementById("caseOpusContext1m").checked,this.saveCaseSettings(e,t);const s=document.getElementById("caseAgentTeamsMobile");s&&(s.checked=t.agentTeams);const n=document.getElementById("caseOpusContext1mMobile");n&&(n.checked=t.opusContext1m)},toggleCaseSettingsMobile(){const e=document.getElementById("caseSettingsPopoverMobile");if(e.classList.contains("hidden")){const t=document.getElementById("quickStartCase").value||"testcase",s=this.getCaseSettings(t);document.getElementById("caseAgentTeamsMobile").checked=s.agentTeams,document.getElementById("caseOpusContext1mMobile").checked=s.opusContext1m,e.classList.remove("hidden");const n=a=>{!e.contains(a.target)&&!a.target.classList.contains("btn-case-settings-mobile")&&(e.classList.add("hidden"),document.removeEventListener("click",n))};setTimeout(()=>document.addEventListener("click",n),0)}else e.classList.add("hidden")},onCaseSettingChangedMobile(){const e=document.getElementById("quickStartCase").value||"testcase",t=this.getCaseSettings(e);t.agentTeams=document.getElementById("caseAgentTeamsMobile").checked,t.opusContext1m=document.getElementById("caseOpusContext1mMobile").checked,this.saveCaseSettings(e,t);const s=document.getElementById("caseAgentTeams");s&&(s.checked=t.agentTeams);const n=document.getElementById("caseOpusContext1m");n&&(n.checked=t.opusContext1m)},showCreateCaseModal(){document.getElementById("newCaseName").value="",document.getElementById("newCaseDescription").value="",document.getElementById("linkCaseName").value="",document.getElementById("linkCasePath").value="",["remoteCaseName","remoteCasePath","remoteHostId","remoteHostAddress","remoteHostUsername","remoteHostPort","remoteHostCodexCommand","remoteHostIdentityFile","remoteHostSocksProxy","remoteHostJumpHost","remoteHostExtraSshOptions"].forEach(r=>{const c=document.getElementById(r);c&&(c.value="")}),this._resetCloneForm();const t=document.getElementById("caseCloneTabBtn");t&&(t.style.display=this.isCliAvailable("git")?"":"none"),this.caseModalTab="case-create",this.switchCaseModalTab("case-create");const s=document.getElementById("createCaseModal");s.querySelectorAll(".set-rail-item").forEach(r=>{r.onclick=()=>this.switchCaseModalTab(r.dataset.tab)});const n=document.getElementById("dockerAdoptExisting");n&&(n.onchange=()=>this._syncDockerAdoptMode());const a=document.getElementById("dockerAdoptCheckBtn");a&&(a.onclick=()=>this._dockerAdoptPreflight());const i=document.getElementById("dockerAdoptJumpBtn");i&&(i.onclick=()=>this.jumpToDockerAdopt());const o=document.getElementById("dockerHostId");o&&(o.onchange=()=>{delete document.getElementById("dockerContainerList")?.dataset.loadedFor,document.getElementById("dockerAdoptExisting")?.checked&&this._loadDockerContainerOptions()}),delete document.getElementById("dockerContainerList")?.dataset.loadedFor,this._syncDockerAdoptMode(),s.querySelectorAll('input[type="text"]').forEach(r=>{r._mobileScrollWired||(r._mobileScrollWired=!0,r.addEventListener("focus",()=>{window.innerWidth<=430&&setTimeout(()=>r.scrollIntoView({behavior:"smooth",block:"center"}),300)}))}),s.classList.add("active"),document.getElementById("newCaseName").focus()},switchCaseModalTab(e){this.caseModalTab=e;const t=document.getElementById("createCaseModal");t.querySelectorAll(".set-rail-item").forEach(a=>{a.classList.toggle("active",a.dataset.tab===e)}),t.querySelectorAll(".set-section").forEach(a=>{a.classList.toggle("hidden",a.id!==e)});const s=document.getElementById("createCaseDoc");s&&(s.scrollTop=0);const n=["caseModalSubmit","caseModalSubmitMobile"].map(a=>document.getElementById(a)).filter(Boolean);if(e==="case-manage")n.forEach(a=>{a.style.display="none"}),this.renderCaseManageList(),this.refreshDockerExports();else{const a=e==="case-create"?"Create":e==="case-clone"?"Clone":e==="case-remote"?"Link Remote":e==="case-docker"?"Link Docker":"Link";n.forEach(i=>{i.style.display="",i.textContent=a})}e==="case-create"?document.getElementById("newCaseName").focus():e==="case-clone"?document.getElementById("cloneRepoUrl").focus():e==="case-link"?document.getElementById("linkCaseName").focus():e==="case-remote"?document.getElementById("remoteCaseName").focus():e==="case-docker"&&document.getElementById("dockerCaseName").focus()},closeCreateCaseModal(){document.getElementById("createCaseModal").classList.remove("active")},async submitCaseModal(){const e=["caseModalSubmit","caseModalSubmitMobile"].map(n=>document.getElementById(n)).filter(Boolean),t=e.map(n=>n.textContent),s=this.caseModalTab==="case-create"?"Creating...":this.caseModalTab==="case-clone"?"Cloning...":"Linking...";e.forEach(n=>{n.classList.add("loading"),n.textContent=s,n.disabled=!0});try{this.caseModalTab==="case-create"?await this.createCase():this.caseModalTab==="case-clone"?await this.cloneCase():this.caseModalTab==="case-remote"?await this.linkRemoteCase():this.caseModalTab==="case-docker"?await this.linkDockerCase():await this.linkCase()}finally{e.forEach((n,a)=>{n.classList.remove("loading"),n.disabled=!1,n.textContent=t[a]})}},async createCase(){const e=document.getElementById("newCaseName").value.trim(),t=document.getElementById("newCaseDescription").value.trim();if(!e){this.showToast("Please enter a case name","error");return}if(!/^[a-zA-Z0-9_-]+$/.test(e)){this.showToast("Invalid name. Use only letters, numbers, hyphens, underscores.","error");return}const s=document.getElementById("newCaseDocker")?.checked,n=s?"/api/cases/docker-quickcreate":"/api/cases",a=s?{name:e,description:t,...this._collectDockerQuickSettings()}:{name:e,description:t};try{const o=await(await fetch(n,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(a)})).json();if(o.success)if(this.closeCreateCaseModal(),await this.loadQuickStartCases(e),await this.saveLastUsedCase(e),s){const r=o.data?.capsEnforced===!1?" (resource caps advisory on this engine)":"";this.showToast(`Docker case "${e}" created${r} \u2014 starting session\u2026`,"success"),await this.runClaude()}else this.showToast(`Case "${e}" created`,"success");else this.showToast(o.error||"Failed to create case","error")}catch(i){console.error("Failed to create case:",i),this.showToast("Failed to create case: "+i.message,"error")}},applyDockerTemplate(){const e=document.getElementById("quickDockerTemplate")?.value,s={small:{m:"2g",c:"1",g:""},medium:{m:"",c:"",g:""},large:{m:"8g",c:"4",g:""},gpu:{m:"8g",c:"4",g:"all"}}[e];if(!s)return;const n=(a,i)=>{const o=document.getElementById(a);o&&(o.value=i)};n("quickDockerMemory",s.m),n("quickDockerCpus",s.c),n("quickDockerGpus",s.g)},_collectDockerQuickSettings(){const e=c=>(document.getElementById(c)?.value||"").trim(),t={},s=e("quickDockerMemory");s&&(t.memory=s);const n=e("quickDockerCpus");n&&(t.cpus=n);const a=e("quickDockerGpus");a&&a.toLowerCase()!=="none"&&(t.gpus=a);const i=document.getElementById("quickDockerNetwork")?.value;i&&i!=="bridge"&&(t.network=i);const o=e("quickDockerImage");o&&(t.image=o);const r=document.getElementById("quickDockerMountCreds");return r&&!r.checked&&(t.mountCredentials=!1),t},async linkCase(){const e=document.getElementById("linkCaseName").value.trim(),t=document.getElementById("linkCasePath").value.trim();if(!e){this.showToast("Please enter a case name","error");return}if(!/^[a-zA-Z0-9_-]+$/.test(e)){this.showToast("Invalid name. Use only letters, numbers, hyphens, underscores.","error");return}if(!t){this.showToast("Please enter a folder path","error");return}try{const n=await(await fetch("/api/cases/link",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({name:e,path:t})})).json();n.success?(this.closeCreateCaseModal(),this.showToast(`Case "${e}" linked to ${t}`,"success"),await this.loadQuickStartCases(e),await this.saveLastUsedCase(e)):this.showToast(n.error||"Failed to link case","error")}catch(s){console.error("Failed to link case:",s),this.showToast("Failed to link case: "+s.message,"error")}},_resetCloneForm(){const e=(o,r)=>{const c=document.getElementById(o);c&&(c.value=r)};e("cloneRepoUrl",""),e("cloneCaseName",""),e("cloneRepoRef","");const t=document.getElementById("cloneShallow");t&&(t.checked=!1);const s=document.getElementById("cloneStartSession");s&&(s.checked=!1);const n=document.getElementById("cloneRepoRefOptions");n&&n.replaceChildren();const a=document.getElementById("cloneRefHint");a&&(a.textContent="Leave blank for the repository's default branch."),this._cloneNameEdited=!1,this._clonePreflight=null,clearTimeout(this._clonePreflightTimer),this._clonePreflightAbort?.abort(),this._clonePreflightAbort=null,this._setCloneStatus("Public repositories only: Codeman clones with no credentials.","");const i=document.getElementById("cloneCaseBrain");if(i){for(const r of i.options){const c=r.dataset.cli;r.hidden=!!c&&!this.isCliAvailable(c)}const o=this.runMode||"claude";i.value=[...i.options].some(r=>r.value===o&&!r.hidden)?o:""}},_setCloneStatus(e,t){const s=document.getElementById("cloneRepoStatus");s&&(s.textContent=e,s.className=`form-hint clone-status${t?" clone-status-"+t:""}`)},_repoNameFromUrl(e){const t=(e||"").trim().replace(/\/+$/,"");return t?(t.replace(/^[a-zA-Z][a-zA-Z0-9+.-]*:\/\//,"").replace(/^[^@/]*@/,"").split(/[/:]/).filter(Boolean).pop()||"").replace(/\.git$/i,"").replace(/[^a-zA-Z0-9_-]+/g,"-").replace(/-{2,}/g,"-").replace(/^[-_]+|[-_]+$/g,"").slice(0,64):""},onCloneNameEdited(){this._cloneNameEdited=!!document.getElementById("cloneCaseName")?.value.trim()},onCloneUrlInput(){const e=document.getElementById("cloneRepoUrl")?.value.trim()||"",t=document.getElementById("cloneCaseName");if(t&&!this._cloneNameEdited&&(t.value=this._repoNameFromUrl(e)),clearTimeout(this._clonePreflightTimer),this._clonePreflightAbort?.abort(),this._clonePreflightAbort=null,!e){this._setCloneStatus("Public repositories only: Codeman clones with no credentials.","");return}if(this.isCliAvailable("git")===!1){this._setCloneStatus("git is not installed on the Codeman host, so cloning is unavailable.","err");return}this._setCloneStatus("Checking the repository\u2026",""),this._clonePreflightTimer=setTimeout(()=>this._runClonePreflight(e),450)},async _runClonePreflight(e){const t=new AbortController;this._clonePreflightAbort=t;try{const n=await(await fetch("/api/cases/clone-preflight",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({repository:e}),signal:t.signal})).json();if(document.getElementById("cloneRepoUrl")?.value.trim()!==e)return;if(!n.success){this._setCloneStatus(n.error||"Could not check that URL.","err");return}this._applyClonePreflight(n.data,e)}catch(s){if(s.name==="AbortError")return;this._setCloneStatus("Could not reach Codeman to check that URL.","err")}},_applyClonePreflight(e,t){this._clonePreflight=e;const s=e?.parse;if(!s?.cloneable){this._setCloneStatus(s?.message||"That URL cannot be cloned.","err");return}const n=document.getElementById("cloneCaseName");n&&!this._cloneNameEdited&&s.suggestedName&&(n.value=s.suggestedName);const a=s.owner?`${s.provider} ${s.owner}/${s.repo}`:`${s.provider} ${s.repo}`;if(e.gitAvailable===!1){this._setCloneStatus(`${a}: git is not installed on the Codeman host.`,"err");return}const i=e.remote;if(i&&!i.reachable){this._setCloneStatus(`${a}: ${i.failure?.message||"the remote could not be read."}`,"err");return}const o=document.getElementById("cloneRefHint"),r=document.getElementById("cloneRepoRefOptions");if(i&&r){r.replaceChildren();for(const d of[...i.branches||[],...i.tags||[]]){const l=document.createElement("option");l.value=d,r.appendChild(l)}if(o){const d=`${i.branches?.length||0} branches, ${i.tags?.length||0} tags`;o.textContent=i.defaultBranch?`Blank clones the default branch (${i.defaultBranch}). ${d} available.`:`Blank clones the default branch. ${d} available.`}}const c=s.warnings?.[0];this._setCloneStatus(c?`${a}: ${c}`:`${a}: ready to clone.`,c?"warn":"ok")},async cloneCase(){const e=document.getElementById("cloneRepoUrl").value.trim(),t=document.getElementById("cloneCaseName").value.trim(),s=document.getElementById("cloneRepoRef").value.trim(),n=!!document.getElementById("cloneShallow")?.checked,a=document.getElementById("cloneCaseBrain")?.value||"",i=!!document.getElementById("cloneStartSession")?.checked;if(!e){this.showToast("Please enter a repository URL","error");return}if(!t){this.showToast("Please enter a case name","error");return}if(!/^[a-zA-Z0-9_-]+$/.test(t)){this.showToast("Invalid name. Use only letters, numbers, hyphens, underscores.","error");return}this._setCloneStatus(`Cloning ${e}\u2026 this can take a while for a large repository.`,"");try{const r=await(await fetch("/api/cases/clone",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({name:t,repository:e,...s?{ref:s}:{},...n?{shallow:!0}:{}})})).json();if(!r.success){this._setCloneStatus(r.error||"Clone failed.","err"),this.showToast(r.error||"Failed to clone repository","error");return}a&&this.setRunMode(a),this.closeCreateCaseModal(),await this.loadQuickStartCases(t),await this.saveLastUsedCase(t),this.showToast(`Cloned into case "${t}"`,"success");for(const c of r.data?.warnings||[])this.showToast(c,"warning");i&&await this.run()}catch(o){console.error("Failed to clone repository:",o),this._setCloneStatus(`Lost the connection while cloning: ${o.message}. If git finishes, the case still appears in the list.`,"warn"),this.showToast("Clone request interrupted \u2014 watch the case list","error")}},openLinkCasePathPicker(){const e=document.getElementById("linkCasePath");PathPicker.open({title:"Select Existing Project Folder",initialPath:e.value.trim(),directoriesOnly:!0,onSelect:t=>{e.value=t;const s=document.getElementById("linkCaseName");if(!s.value.trim()){const n=t.split("/").filter(Boolean).pop()||"";/^[\p{L}\p{N}_-]+$/u.test(n)&&(s.value=n)}e.focus(),e.setSelectionRange(t.length,t.length)}})},openDockerWorkspacePathPicker(){const e=document.getElementById("dockerWorkspacePath");PathPicker.open({title:"Select Host Workspace Folder",initialPath:e.value.trim(),directoriesOnly:!0,onSelect:t=>{e.value=t;const s=document.getElementById("dockerCaseName");if(s&&!s.value.trim()){const n=t.split("/").filter(Boolean).pop()||"";/^[a-zA-Z0-9_-]+$/.test(n)&&(s.value=n)}}})},openDockerWorkdirPicker(){const e=document.getElementById("dockerAdoptWorkdir"),t=document.getElementById("dockerContainerName")?.value.trim(),s=document.getElementById("dockerHostId")?.value.trim()||"local";if(!t){this.showToast("Enter the container name first","error");return}PathPicker.open({title:`Select Folder Inside ${t}`,initialPath:e.value.trim()||"/",directoriesOnly:!0,fetchListing:async n=>{const a=await this._apiJson("/api/docker-cases/browse",{method:"POST",body:{hostId:s,container:t,path:n||"/"}});return a?a.error?{success:!1,error:a.error}:{success:!0,data:{...a,root:"/",roots:[{label:t,path:"/"}],truncated:!1}}:{success:!1,error:`Could not read ${t}. Is it running?`}},onSelect:n=>{e.value=n}})},async linkRemoteCase(){const e=document.getElementById("remoteCaseName").value.trim(),t=document.getElementById("remoteCasePath").value.trim(),s=document.getElementById("remoteHostId").value.trim(),n=document.getElementById("remoteHostAddress").value.trim(),a=document.getElementById("remoteHostUsername").value.trim(),i=document.getElementById("remoteHostCodexCommand").value.trim(),o=document.getElementById("remoteHostPort").value.trim(),r=document.getElementById("remoteHostIdentityFile").value.trim(),c=document.getElementById("remoteHostSocksProxy").value.trim(),d=document.getElementById("remoteHostJumpHost").value.trim(),l=document.getElementById("remoteHostExtraSshOptions").value.split(`
|
|
16
16
|
`).map(h=>h.trim()).filter(h=>h.length>0);if(!e||!t||!s||!n||!a){this.showToast("Please complete all required remote fields","error");return}if(!/^[a-zA-Z0-9_-]+$/.test(e)||!/^[a-zA-Z0-9_-]+$/.test(s)){this.showToast("Invalid name. Use only letters, numbers, hyphens, underscores.","error");return}if(!t.startsWith("/")){this.showToast("Remote path must be absolute","error");return}let m;if(o&&(m=Number(o),!Number.isInteger(m)||m<1||m>65535)){this.showToast("SSH port must be a number between 1 and 65535","error");return}try{const h={id:s,label:s,host:n,username:a,...m?{port:m}:{},...r?{identityFile:r}:{},...c?{socksProxy:c}:{},...d?{jumpHost:d}:{},...l.length?{extraSshOptions:l}:{},...i?{commands:{codex:i}}:{}},u=await(await fetch("/api/remote-hosts",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(h)})).json();if(!u.success&&u.errorCode!=="ALREADY_EXISTS")throw new Error(u.error||"Failed to save remote host");const y=await(await fetch("/api/cases/remote-link",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({name:e,hostId:s,remotePath:t})})).json();y.success?(this.closeCreateCaseModal(),this.showToast(`Remote case "${e}" linked`,"success"),await this.loadQuickStartCases(e),await this.saveLastUsedCase(e)):this.showToast(y.error||"Failed to link remote case","error")}catch(h){console.error("Failed to link remote case:",h),this.showToast("Failed to link remote case: "+h.message,"error")}},_syncDockerAdoptMode(){const e=document.getElementById("createCaseModal");if(!e)return;const t=document.getElementById("dockerAdoptExisting")?.checked;t?e.setAttribute("data-docker-adopt","1"):e.removeAttribute("data-docker-adopt"),t&&this._loadDockerContainerOptions()},async _loadDockerContainerOptions(){const e=document.getElementById("dockerContainerList");if(!e)return;const t=document.getElementById("dockerHostId")?.value.trim()||"local";if(e.dataset.loadedFor===t)return;const n=(await this._apiJson(`/api/docker-hosts/${encodeURIComponent(t)}/containers`))?.containers||[];e.textContent="";for(const a of n){const i=document.createElement("option");i.value=a.name,i.textContent=a.running?`${a.image} \xB7 ${a.status}`:`${a.image} \xB7 ${a.status} (not running)`,e.appendChild(i)}e.dataset.loadedFor=t},jumpToDockerAdopt(){this.switchCaseModalTab("case-docker");const e=document.getElementById("dockerAdoptExisting");e&&(e.checked=!0),this._syncDockerAdoptMode(),document.getElementById("dockerContainerName")?.focus()},async _dockerAdoptPreflight(){const e=document.getElementById("dockerLinkStatus"),t=document.getElementById("dockerContainerName")?.value.trim(),s=document.getElementById("dockerAdoptWorkdir")?.value.trim(),n=document.getElementById("dockerHostId").value.trim()||"local";if(!t){e&&(e.textContent="Enter a container name first.");return}e&&(e.textContent="Inspecting container...");const a=await this._apiJson("/api/docker-cases/adopt-preflight",{method:"POST",body:{hostId:n,container:t,...s?{containerWorkdir:s}:{}}});if(!e)return;if(!a){e.textContent="Could not reach the docker host profile. Save a Host ID first.";return}if(!a.ok){e.textContent=a.error||"Container is not adoptable.";return}const i=(a.availableModes||[]).filter(o=>o!=="shell");e.textContent=i.length?`Running (${a.image||"unknown image"}). Available: ${i.join(", ")}.`:`Running (${a.image||"unknown image"}), but no agent CLI found inside \u2014 only Shell will work.`},async linkDockerCase(){const e=document.getElementById("dockerCaseName").value.trim(),t=document.getElementById("dockerWorkspacePath").value.trim(),s=document.getElementById("dockerHostId").value.trim()||"local",n=!!document.getElementById("dockerAdoptExisting")?.checked,a=document.getElementById("dockerContainerName")?.value.trim()||"",i=document.getElementById("dockerAdoptWorkdir")?.value.trim()||"",o=document.getElementById("dockerImage").value.trim()||"codeman/agent:base",r=document.getElementById("dockerNetwork").value,c=document.getElementById("dockerMemory").value.trim(),d=document.getElementById("dockerCpus").value.trim(),l=document.getElementById("dockerMountCredentials").checked,m=document.getElementById("dockerResumeOnStart").checked,h=document.getElementById("dockerLinkStatus");if(!e||!t){this.showToast("Please enter a case name and workspace path","error");return}if(!/^[a-zA-Z0-9_-]+$/.test(e)||!/^[a-zA-Z0-9_-]+$/.test(s)){this.showToast("Invalid name. Use only letters, numbers, hyphens, underscores.","error");return}if(!t.startsWith("/")){this.showToast("Workspace path must be absolute","error");return}if(n&&!a){this.showToast("Enter the name of the running container to attach to","error");return}try{h&&(h.textContent=n?"Inspecting the existing container...":"Checking docker daemon + base image...");const C={};c&&(C.memory=c),d&&(C.cpus=d);const u={id:s,label:s,image:o,network:r,mountCredentials:l,resumeOnStart:m,...Object.keys(C).length?{resources:C}:{}};let f=await fetch("/api/docker-hosts",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(u)}),y=await f.json();if(!y.success&&y.errorCode==="ALREADY_EXISTS"&&(f=await fetch(`/api/docker-hosts/${encodeURIComponent(s)}`,{method:"PUT",headers:{"Content-Type":"application/json"},body:JSON.stringify(u)}),y=await f.json()),!y.success)throw new Error(y.error||"Failed to save docker host");const p=await(await fetch(n?"/api/cases/docker-adopt":"/api/cases/docker-link",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(n?{name:e,hostId:s,hostWorkspacePath:t,container:a,...i?{containerWorkdir:i}:{}}:{name:e,hostId:s,hostWorkspacePath:t})})).json();if(p.success){this.closeCreateCaseModal();const w=p.data?.capsEnforced===!1?" (resource caps are advisory on this engine)":"",g=(p.data?.availableModes||[]).filter(b=>b!=="shell"),S=n&&g.length?` \u2014 found ${g.join(", ")}`:"";this.showToast(`Docker case "${e}" ${n?"attached":"linked"}${w}${S}`,"success"),await this.loadQuickStartCases(e),await this.saveLastUsedCase(e)}else h&&(h.textContent=p.error||"Failed to link docker case"),this.showToast(p.error||"Failed to link docker case","error")}catch(C){console.error("Failed to link docker case:",C),h&&(h.textContent=C.message),this.showToast("Failed to link docker case: "+C.message,"error")}},async refreshDockerExports(){const e=document.getElementById("dockerExportsList");if(e)try{const n=(await(await fetch("/api/docker-exports")).json())?.data?.exports||[];if(n.length===0){e.innerHTML='<span class="form-hint">No exports yet. Export a docker case from its tab.</span>';return}e.innerHTML=n.map(a=>{const i=(a.sizeBytes/1e6).toFixed(1),o=escapeHtml(a.name);return`<div class="case-manage-item" style="display:flex; align-items:center; gap:8px; justify-content:space-between;">
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/dist/web/public/sw.js.gz
CHANGED
|
Binary file
|
|
Binary file
|