aicodeman 1.34.0 → 1.35.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -0
- package/dist/config/dependency-registry.d.ts +7 -0
- package/dist/config/dependency-registry.d.ts.map +1 -1
- package/dist/config/dependency-registry.js +15 -0
- package/dist/config/dependency-registry.js.map +1 -1
- package/dist/config/path-probe.d.ts +37 -0
- package/dist/config/path-probe.d.ts.map +1 -0
- package/dist/config/path-probe.js +43 -0
- package/dist/config/path-probe.js.map +1 -0
- package/dist/git-workspace-status.d.ts +220 -0
- package/dist/git-workspace-status.d.ts.map +1 -0
- package/dist/git-workspace-status.js +606 -0
- package/dist/git-workspace-status.js.map +1 -0
- package/dist/hooks-config.d.ts.map +1 -1
- package/dist/hooks-config.js +83 -13
- package/dist/hooks-config.js.map +1 -1
- package/dist/types/api.d.ts +7 -0
- package/dist/types/api.d.ts.map +1 -1
- package/dist/types/api.js.map +1 -1
- package/dist/utils/bounded-path-probe.d.ts +99 -0
- package/dist/utils/bounded-path-probe.d.ts.map +1 -0
- package/dist/utils/bounded-path-probe.js +228 -0
- package/dist/utils/bounded-path-probe.js.map +1 -0
- package/dist/utils/cli-executable-resolver.d.ts +2 -0
- package/dist/utils/cli-executable-resolver.d.ts.map +1 -1
- package/dist/utils/cli-executable-resolver.js +2 -1
- package/dist/utils/cli-executable-resolver.js.map +1 -1
- package/dist/utils/dependency-checker.d.ts +2 -0
- package/dist/utils/dependency-checker.d.ts.map +1 -1
- package/dist/utils/dependency-checker.js +37 -9
- package/dist/utils/dependency-checker.js.map +1 -1
- package/dist/utils/index.d.ts +2 -0
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js +1 -0
- package/dist/utils/index.js.map +1 -1
- package/dist/web/case-path.d.ts +58 -0
- package/dist/web/case-path.d.ts.map +1 -0
- package/dist/web/case-path.js +182 -0
- package/dist/web/case-path.js.map +1 -0
- 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.b9516c7b.js +43 -0
- package/dist/web/public/app.b9516c7b.js.br +0 -0
- package/dist/web/public/app.b9516c7b.js.gz +0 -0
- package/dist/web/public/approvals-ui.js.gz +0 -0
- package/dist/web/public/constants.d95fade9.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/git-status-ui.js +673 -0
- package/dist/web/public/git-status-ui.js.br +0 -0
- package/dist/web/public/git-status-ui.js.gz +0 -0
- package/dist/web/public/home-sessions.js.gz +0 -0
- package/dist/web/public/host-wake-ui.js.gz +0 -0
- package/dist/web/public/i18n.52f5b7cc.js +1 -0
- package/dist/web/public/i18n.52f5b7cc.js.br +0 -0
- package/dist/web/public/i18n.52f5b7cc.js.gz +0 -0
- package/dist/web/public/image-input.a216cf9e.js.gz +0 -0
- package/dist/web/public/index.html +79 -12
- package/dist/web/public/index.html.br +0 -0
- package/dist/web/public/index.html.gz +0 -0
- package/dist/web/public/input-cjk.8bc46081.js.gz +0 -0
- package/dist/web/public/keyboard-accessory.c1e7c7b7.js.gz +0 -0
- package/dist/web/public/mobile-handlers.23a85db1.js.gz +0 -0
- package/dist/web/public/mobile-ime-preview.2dbaf262.js.gz +0 -0
- package/dist/web/public/mobile-overview.js.gz +0 -0
- package/dist/web/public/mobile.1aa4952d.css.gz +0 -0
- package/dist/web/public/notification-manager.50bb87d8.js.gz +0 -0
- package/dist/web/public/orchestrator-panel.js.gz +0 -0
- package/dist/web/public/panels-ui.6a5898d9.js.gz +0 -0
- package/dist/web/public/ralph-panel.b350e2ef.js.gz +0 -0
- package/dist/web/public/ralph-wizard.b6c0dd19.js.gz +0 -0
- package/dist/web/public/readmymind-ui.js.gz +0 -0
- package/dist/web/public/reboot-restore-ui.js.gz +0 -0
- package/dist/web/public/respawn-ui.ff0dae4c.js.gz +0 -0
- package/dist/web/public/sanitize-html.e7147e76.js.gz +0 -0
- package/dist/web/public/session-lineage.js.gz +0 -0
- package/dist/web/public/session-ui.6bddf916.js +89 -0
- package/dist/web/public/session-ui.6bddf916.js.br +0 -0
- package/dist/web/public/session-ui.6bddf916.js.gz +0 -0
- package/dist/web/public/settings-ui.6e798b7b.js +97 -0
- package/dist/web/public/settings-ui.6e798b7b.js.br +0 -0
- package/dist/web/public/settings-ui.6e798b7b.js.gz +0 -0
- package/dist/web/public/styles.2d175c60.css +1 -0
- package/dist/web/public/styles.2d175c60.css.br +0 -0
- package/dist/web/public/styles.2d175c60.css.gz +0 -0
- package/dist/web/public/subagent-windows.e6ca799f.js.gz +0 -0
- package/dist/web/public/sw.js +2 -2
- package/dist/web/public/sw.js.br +0 -0
- package/dist/web/public/sw.js.gz +0 -0
- package/dist/web/public/tab-layout-browser.065fcba4.js +1 -0
- package/dist/web/public/tab-layout-browser.065fcba4.js.br +0 -0
- package/dist/web/public/tab-layout-browser.065fcba4.js.gz +0 -0
- package/dist/web/public/{tab-rail-resize.dcdc5772.js → tab-rail-resize.94cd59f0.js} +1 -1
- package/dist/web/public/tab-rail-resize.94cd59f0.js.br +0 -0
- package/dist/web/public/tab-rail-resize.94cd59f0.js.gz +0 -0
- package/dist/web/public/terminal-keycode229-recovery.fb91b25b.js.gz +0 -0
- package/dist/web/public/terminal-split.js.gz +0 -0
- package/dist/web/public/terminal-ui.a4535311.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.ae5e3d5d.js.gz +0 -0
- package/dist/web/public/vendor/xterm-zerolag-input.bbb5f84c.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/case-routes.d.ts.map +1 -1
- package/dist/web/routes/case-routes.js +136 -18
- package/dist/web/routes/case-routes.js.map +1 -1
- package/dist/web/routes/doctor-routes.d.ts +23 -0
- package/dist/web/routes/doctor-routes.d.ts.map +1 -0
- package/dist/web/routes/doctor-routes.js +83 -0
- package/dist/web/routes/doctor-routes.js.map +1 -0
- package/dist/web/routes/git-status-routes.d.ts +16 -0
- package/dist/web/routes/git-status-routes.d.ts.map +1 -0
- package/dist/web/routes/git-status-routes.js +71 -0
- package/dist/web/routes/git-status-routes.js.map +1 -0
- package/dist/web/routes/index.d.ts +2 -0
- package/dist/web/routes/index.d.ts.map +1 -1
- package/dist/web/routes/index.js +2 -0
- package/dist/web/routes/index.js.map +1 -1
- package/dist/web/routes/session-routes.d.ts.map +1 -1
- package/dist/web/routes/session-routes.js +20 -8
- package/dist/web/routes/session-routes.js.map +1 -1
- package/dist/web/schemas.d.ts +1 -0
- package/dist/web/schemas.d.ts.map +1 -1
- package/dist/web/schemas.js +7 -0
- package/dist/web/schemas.js.map +1 -1
- package/dist/web/server.d.ts.map +1 -1
- package/dist/web/server.js +3 -1
- package/dist/web/server.js.map +1 -1
- package/package.json +1 -1
- package/skills/codeman/reference/verbs.md +3 -0
- package/dist/web/public/app.cf0db8ce.js +0 -43
- package/dist/web/public/app.cf0db8ce.js.br +0 -0
- package/dist/web/public/app.cf0db8ce.js.gz +0 -0
- package/dist/web/public/i18n.398b8331.js +0 -1
- package/dist/web/public/i18n.398b8331.js.br +0 -0
- package/dist/web/public/i18n.398b8331.js.gz +0 -0
- package/dist/web/public/session-ui.bf66c13b.js +0 -89
- package/dist/web/public/session-ui.bf66c13b.js.br +0 -0
- package/dist/web/public/session-ui.bf66c13b.js.gz +0 -0
- package/dist/web/public/settings-ui.331d3b1d.js +0 -97
- package/dist/web/public/settings-ui.331d3b1d.js.br +0 -0
- package/dist/web/public/settings-ui.331d3b1d.js.gz +0 -0
- package/dist/web/public/styles.8c485d26.css +0 -1
- package/dist/web/public/styles.8c485d26.css.br +0 -0
- package/dist/web/public/styles.8c485d26.css.gz +0 -0
- package/dist/web/public/tab-layout-browser.4e0ed50c.js +0 -1
- package/dist/web/public/tab-layout-browser.4e0ed50c.js.br +0 -0
- package/dist/web/public/tab-layout-browser.4e0ed50c.js.gz +0 -0
- package/dist/web/public/tab-rail-resize.dcdc5772.js.br +0 -0
- package/dist/web/public/tab-rail-resize.dcdc5772.js.gz +0 -0
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/dist/web/public/sw.js
CHANGED
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
// not the current one, so the old constant key meant that cleanup never deleted
|
|
27
27
|
// anything — hashed assets from every release ever deployed accumulated in one
|
|
28
28
|
// bucket until the origin hit its storage quota.
|
|
29
|
-
const BUILD_ID = '
|
|
29
|
+
const BUILD_ID = '1562378ee72a';
|
|
30
30
|
const CACHE_NAME = `codeman-${BUILD_ID}`;
|
|
31
31
|
|
|
32
32
|
// Reverse-proxy base path: the worker is served at `<base>/sw.js`, so its own
|
|
@@ -48,7 +48,7 @@ const B = (p) => (p && p[0] === '/' ? SW_BASE + p : p);
|
|
|
48
48
|
// handler caches every successful GET at runtime, but the precache warmed
|
|
49
49
|
// nothing while looking like it did. Deriving it from the same manifest that
|
|
50
50
|
// renames the files is the only thing that keeps the two from drifting again.
|
|
51
|
-
const HASHED_ASSETS = ["styles.
|
|
51
|
+
const HASHED_ASSETS = ["styles.2d175c60.css", "mobile.1aa4952d.css", "constants.d95fade9.js", "i18n.52f5b7cc.js", "mobile-handlers.23a85db1.js", "voice-input.c4b51eb6.js", "notification-manager.50bb87d8.js", "keyboard-accessory.c1e7c7b7.js", "input-cjk.8bc46081.js", "mobile-ime-preview.2dbaf262.js", "terminal-keycode229-recovery.fb91b25b.js", "sanitize-html.e7147e76.js", "tab-layout-browser.065fcba4.js", "app.b9516c7b.js", "tab-rail-resize.94cd59f0.js", "terminal-ui.a4535311.js", "respawn-ui.ff0dae4c.js", "ralph-panel.b350e2ef.js", "settings-ui.6e798b7b.js", "panels-ui.6a5898d9.js", "session-ui.6bddf916.js", "ralph-wizard.b6c0dd19.js", "api-client.c9b1cddc.js", "subagent-windows.e6ca799f.js", "image-input.a216cf9e.js", "vendor/xterm-zerolag-input.bbb5f84c.js", "vendor/xterm-predictive-echo.ae5e3d5d.js"];
|
|
52
52
|
|
|
53
53
|
// Core app shell -- cached on install for instant startup
|
|
54
54
|
const APP_SHELL = [
|
package/dist/web/public/sw.js.br
CHANGED
|
Binary file
|
package/dist/web/public/sw.js.gz
CHANGED
|
Binary file
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";(function(B){"use strict";const O="codeman:tab-groups-collapsed",y=e=>`${e.kind}:${e.id}`,G=e=>!!e&&(e.kind==="session"||e.kind==="webview")&&typeof e.id=="string"&&e.id.length>0,K=e=>Array.isArray(e)?e.filter(n=>typeof n=="string"&&n):[],$=e=>[...new Set(K(e))],P=e=>e.placement==="manual"?{kind:e.kind,id:e.id,placement:"manual"}:{kind:e.kind,id:e.id},H=e=>Array.isArray(e)?e.filter(G).map(P):[];function h(e){if(!e||typeof e!="object")throw new Error("Invalid tab layout");const n=Array.isArray(e.groups)?e.groups:[];return{version:Number.isSafeInteger(e.version)&&e.version>=0?e.version:0,updatedAt:typeof e.updatedAt=="string"?e.updatedAt:"",groups:n.filter(t=>t&&typeof t.id=="string"&&t.id.length>0).map(t=>({id:t.id,name:typeof t.name=="string"?t.name:"",refs:H(t.refs)})),ungrouped:H(e.ungrouped)}}function W(e){return!!e&&Array.isArray(e.groups)&&e.groups.length>0}function V(e){if(e===null)return[];try{const n=JSON.parse(e);return Array.isArray(n)?$(n):null}catch{return null}}function j(e,n){try{const t=V(e.getItem(O)),r=t||[];if(n===void 0)return{ids:r,ok:!0};const o=new Set($(n)),s=r.filter(a=>o.has(a));return(!t||s.length!==r.length)&&e.setItem(O,JSON.stringify(s)),{ids:s,ok:!0}}catch{return{ids:[],ok:!1}}}function z(e,n){const t=$(n);try{return e.setItem(O,JSON.stringify(t)),{ids:t,ok:!0}}catch{return{ids:[],ok:!1}}}function Y(e,n={}){if(!e)return null;const t=h(e);if(!W(t))return null;const r=$(n.liveSessionIds),o=$(n.openWebviewIds),s=new Set(r),a=new Set(o),i=new Set(K(n.collapsedGroupIds)),l=n.activeWebviewId?`webview:${n.activeWebviewId}`:n.activeSessionId?`session:${n.activeSessionId}`:"",d=p=>p.kind==="session"?s.has(p.id):a.has(p.id),f=new Set,u=[],g={},b={},k=[],c=(p,I,w)=>{const _=[],E=[];let q=0;for(const L of p){const S=y(L);if(f.has(S)||!d(L))continue;f.add(S),b[S]=I,q++;const J={kind:L.kind,id:L.id};if(w&&S!==l){g[S]=I,E.push(J);continue}_.push(J),u.push(J)}return{shown:_,hidden:E,count:q}};for(const p of t.groups){const I=i.has(p.id),{shown:w,hidden:_,count:E}=c(p.refs,p.id,I);k.push({id:p.id,name:p.name,refs:w,hidden:_,count:E,collapsed:I})}const m=[...r.map(p=>({kind:"session",id:p})),...o.map(p=>({kind:"webview",id:p}))],v=c([...t.ungrouped,...m],null,!1);return v.count>0&&k.push({id:null,name:"",refs:v.shown,hidden:[],count:v.count,collapsed:!1}),{sections:k,visibleRefs:u,hiddenTabGroupByRef:g,sectionByRef:b}}const C={action:2,idle:1};function Q(e,n){const t={},r=e&&Array.isArray(e.sections)?e.sections:[];for(const o of r){if(o.id===null||!Array.isArray(o.hidden))continue;let s=null;for(const a of o.hidden){if(a.kind!=="session")continue;const i=n(a.id);C[i]&&(!s||C[i]>C[s])&&(s=i)}s&&(t[o.id]=s)}return t}function Z(e,n,t){return n?JSON.stringify({collapsed:$(t).sort(),sections:n.sections.map(r=>[r.id,r.name,r.count,r.refs.map(y),(r.hidden||[]).map(y)])}):null}function ee(e,n,t){return(e&&Array.isArray(e.sections)?e.sections:[]).map((o,s)=>{const a=o.refs.map(b=>n(b)).join("");if(o.id===null)return`<section class="tab-layout-group tab-layout-ungrouped" role="presentation" data-tab-group-id=""><div class="tab-layout-group-header tab-layout-ungrouped-header" aria-hidden="true"><span class="tab-layout-group-name">Ungrouped</span><span class="tab-layout-group-count">${o.count}</span></div><div class="tab-layout-group-refs" role="presentation">${a}</div></section>`;const i=t(o.id),l=`tab-layout-group-refs-${s}`,d=`tab-layout-group-name-${s}`,f=o.count===0,u=!o.collapsed&&!f,g=f?"":` aria-expanded="${u?"true":"false"}"`;return`<section class="tab-layout-group${o.collapsed?" tab-layout-group--collapsed":""}" role="presentation" data-tab-group-id="${i}"><div class="tab-layout-group-header tab-layout-group-toggle" role="treeitem" tabindex="-1" data-tab-group-header="${i}"${g}${u?` aria-owns="${l}"`:""} onclick="app.toggleTabGroupCollapsed(this.dataset.tabGroupHeader)" oncontextmenu="event.preventDefault(); app.openTabGroupMenu(event, this.dataset.tabGroupHeader)"><span class="tab-layout-group-chevron" aria-hidden="true"></span><span class="tab-layout-group-name" id="${d}" data-i18n-skip>${t(o.name)}</span><span class="tab-layout-group-count">${o.count}</span><span class="tab-layout-group-menu" aria-hidden="true" title="Group actions" onclick="event.stopPropagation(); app.openTabGroupMenu(event, this.closest('[data-tab-group-header]').dataset.tabGroupHeader)">⋯</span></div><div class="tab-layout-group-refs" id="${l}" ${u?`role="group" aria-labelledby="${d}"`:'role="presentation"'}>${a}</div></section>`}).join("")}function te(e){const n=Number.isFinite(e.retryDelayMs)?e.retryDelayMs:5e3,t=Number.isFinite(e.maxRetryDelayMs)?e.maxRetryDelayMs:6e4,r=Number.isSafeInteger(e.maxRetries)?e.maxRetries:4;let o=0,s=!1,a=null,i=0;const l=()=>{a!==null&&e.cancelRetry&&e.cancelRetry(a),a=null},d=async()=>{if(s)return!1;const f=++o;l();try{const u=await e.fetchLayout();return s||f!==o?!1:(i=0,e.applyLayout(u),!0)}catch{if(s||f!==o)return!1;if(i++,e.applyFallback(),i<=r){const g=Math.min(t,n*2**(i-1));a=e.scheduleRetry(()=>d(),g)}return!1}};return{load:d,dispose(){s=!0,o++,l()}}}function A(e){throw new Error(`Tab layout edit failed: ${e}`)}const D=(e,n)=>Number.isInteger(e)?Math.max(0,Math.min(e,n)):n;function X(e){const n=typeof e=="string"?e.trim():"";return(!n||n.length>60)&&A("group name must be 1-60 characters"),n}function R(e){return[...e.groups.flatMap(n=>n.refs.map(t=>({groupId:n.id,ref:t}))),...e.ungrouped.map(n=>({groupId:null,ref:n}))]}function F(e,n){if(n===null)return e.ungrouped;const t=e.groups.find(r=>r.id===n);return t||A("unknown group"),t.refs}function M(e,n,t){const r=new Map(R(e).map(i=>[y(i.ref),i.ref])),o=new Map;for(const[i,l]of Object.entries(t||{})){const d=r.get(`session:${i}`);!d||d.placement==="manual"||!r.has(`session:${l}`)||(o.has(l)||o.set(l,[]),o.get(l).push(i))}const s=new Set,a=i=>{if(!s.has(i)&&(s.add(i),i.startsWith("session:")))for(const l of o.get(i.slice(8))||[])a(`session:${l}`)};return a(y(n)),s}function U(e,n,t,r,o,s){const a=h(e),i=M(a,n,s),l=F(a,t).filter(f=>!i.has(y(f)));if(!r)return{groupId:t};const d=l.findIndex(f=>y(f)===y(r));return d<0?{groupId:t}:{groupId:t,index:o==="after"?d+1:d}}function ne(e,n,t,r){const o=h(e);if(!n||!t)return null;if(n.type==="group"){const u=o.groups.findIndex(k=>k.id===n.groupId);if(u<0)return null;const g=t.type==="ungrouped"?null:t.groupId??null,b=g===null?o.groups.length-1:o.groups.findIndex(k=>k.id===g);return b<0||b===u?null:{type:"reorderGroup",groupId:n.groupId,index:b}}if(n.type!=="ref"||!G(n.ref))return null;const s=R(o).find(u=>y(u.ref)===y(n.ref));if(!s)return null;let a,i=null,l="before";if(t.type==="ref"&&G(t.ref)){if(M(o,n.ref,r).has(y(t.ref)))return null;a=t.groupId??null,i=t.ref,l=t.placement==="after"?"after":"before"}else if(t.type==="group"){if(a=t.groupId??null,a===s.groupId)return null}else if(t.type==="ungrouped"){if(a=null,s.groupId===null)return null}else return null;if(a!==null&&!o.groups.some(u=>u.id===a))return null;const d=U(o,n.ref,a,i,l,r),f={type:"moveRef",ref:{kind:n.ref.kind,id:n.ref.id},groupId:d.groupId,...d.index===void 0?{}:{index:d.index},parents:r||{}};return x(N(o,f))===x(o)?null:f}function N(e,n){const t=h(e),r=n||{},o=t.groups.findIndex(s=>s.id===r.groupId);switch(r.type){case"createGroup":{(typeof r.id!="string"||!r.id)&&A("invalid group id");const s=X(r.name);return t.groups.some(a=>a.id===r.id)||(t.groups.length>=32&&A("group limit reached"),t.groups.splice(D(r.index,t.groups.length),0,{id:r.id,name:s,refs:[]})),t}case"renameGroup":return o<0&&A("unknown group"),t.groups[o].name=X(r.name),t;case"deleteGroup":{if(o<0)return t;const[s]=t.groups.splice(o,1);return t.ungrouped.push(...s.refs),t}case"reorderGroup":{o<0&&A("unknown group");const[s]=t.groups.splice(o,1);return t.groups.splice(D(r.index,t.groups.length),0,s),t}case"moveRef":{G(r.ref)||A("invalid row");const s=y(r.ref);R(t).some(u=>y(u.ref)===s)||A("unknown row");const a=r.groupId??null;F(t,a);const i=M(t,r.ref,r.parents),l=R(t).filter(u=>i.has(y(u.ref))).map(u=>P(u.ref)),d=l.find(u=>y(u)===s);r.ref.kind==="session"&&r.parents?.[r.ref.id]&&(d.placement="manual"),l.sort((u,g)=>u===d?-1:g===d?1:0);for(const u of t.groups)u.refs=u.refs.filter(g=>!i.has(y(g)));t.ungrouped=t.ungrouped.filter(u=>!i.has(y(u)));const f=F(t,a);return f.splice(D(r.index,f.length),0,...l),t}default:return A(`unknown operation ${r.type}`)}}function x(e){const n=h(e);return JSON.stringify([n.groups,n.ungrouped])}function T(e,n){let t=h(e);const r=[];let o=0;for(const s of n)try{t=N(t,s),r.push(s)}catch{o++}return{layout:t,kept:r,dropped:o}}function re(e){let n=h(e.initialLayout),t=n,r=[],o=[],s=!1,a=null,i=!1;const l=e.schedule||(c=>setTimeout(c,0)),d=e.cancel||(c=>clearTimeout(c)),f=e.maxAttempts||3,u=c=>e.reportError?.(c),g=c=>e.applyLayout(h(t),c),b=()=>{a===null&&(a=l(k))};async function k(){if(a=null,i||s||r.length===0)return;s=!0,o=r,r=[];let c=!1,m=!1,v=!1;try{for(let p=0;p<f&&o.length;p++){const I=T(n,o);if(o=I.kept,I.dropped&&!m&&(m=!0,u("Tab groups changed elsewhere; part of your edit no longer applies.")),!o.length||x(I.layout)===x(n)){o=[];break}const w=await e.put({baseVersion:n.version,layout:I.layout});if(i)return;if(w?.ok&&w.layout)n=h(w.layout),o=[];else if(w?.status===409&&w.layout)n=h(w.layout);else if(w?.status===400&&e.fetchLayout&&!v){if(v=!0,n=h(await e.fetchLayout()),i)return}else throw new Error("Tab layout save failed")}o.length&&(c=!0,u("Tab groups kept changing elsewhere; your edit was not saved."))}catch{c=!0,u("Could not save tab groups.")}finally{if(o=[],s=!1,!i){const p=T(n,r);p.dropped&&!c&&!m&&u("Tab groups changed elsewhere; part of your edit no longer applies."),r=p.kept,t=p.layout,g({authoritative:!0}),c&&e.onFailure?.(),r.length?b():e.onSettled?.()}}}return{enqueue(c){return t=N(t,c),r.push(c),g({optimistic:!0}),b(),h(t)},restore(c){const m=T(t,Array.isArray(c)?c:[]);return!m.kept.length||x(m.layout)===x(t)?!1:(t=m.layout,r.push(...m.kept),g({optimistic:!0}),b(),!0)},adoptExternal(c){if(i||s)return!1;const m=h(c);if(m.version<n.version)return!1;n=m;const v=T(m,r);return v.dropped&&u("Tab groups changed elsewhere; part of your edit no longer applies."),r=v.kept,t=v.layout,g({authoritative:!0,external:!0}),!0},flush:k,isWriting:()=>s,hasPending:()=>s||r.length>0,pendingOperations:()=>JSON.parse(JSON.stringify([...o,...r])),baseVersion:()=>n.version,getLayout:()=>h(t),dispose(){i=!0,a!==null&&d(a),a=null,r=[]}}}B.CodemanTabLayout={normalizeLayout:h,hasGroups:W,project:Y,hiddenGroupAlerts:Q,structureKey:Z,renderProjection:ee,applyOperation:N,moveDestination:U,movingRefKeys:(e,n,t)=>[...M(h(e),n,t)],dropOperation:ne,contentKey:x,createEditCoordinator:re,createLoadCoordinator:te,loadCollapsedGroupIds:j,saveCollapsedGroupIds:z,MAX_GROUPS:32}})(typeof window<"u"?window:globalThis);
|
|
Binary file
|
|
Binary file
|
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";Object.assign(CodemanApp.prototype,{_getTabRailMinimumTerminalWidth(){const t=this.terminal?._core?._renderService?.dimensions?.css?.cell?.width;return Number.isFinite(t)&&t>0?Math.ceil(t*40+24):420},_getTabRailBounds(){const t=document.querySelector(".main");return{viewportWidth:window.innerWidth,mainWidth:t?.clientWidth||window.innerWidth,minTerminalWidth:this._getTabRailMinimumTerminalWidth()}},_getCurrentTabRailWidth(){const t=Number.parseFloat(document.documentElement.style.getPropertyValue("--tab-rail-width"));if(Number.isFinite(t))return t;const e=document.getElementById("tabRail")?.getBoundingClientRect?.().width;return Number.isFinite(e)&&e>0?e:window.CodemanTabRail?.DEFAULT_WIDTH||256},readTabRailWidthSetting(){const t=document.getElementById("appSettingsTabRailWidth");return t?t.value==="custom"?Number(t.dataset.currentWidth)||this._getCurrentTabRailWidth():Number(t.value)||window.CodemanTabRail?.DEFAULT_WIDTH||256:this._getCurrentTabRailWidth()},syncTabRailWidthSetting(t){const e=document.getElementById("appSettingsTabRailWidth");if(!e)return;const i=Math.round(t);if(e.dataset.currentWidth=String(i),e.querySelector(`option[value="${i}"]`)){e.value=String(i);return}const n=e.querySelector('option[value="custom"]');n&&(n.textContent=`Custom (${i}px)`),e.value="custom"},_setTabRailWidth(t){const e=window.CodemanTabRail;if(!e)return 256;const i=e.resolveWidth({width:t}),a=this._getTabRailBounds(),n=e.resolveWidth({width:i,...a}),h=e.resolveWidth({width:e.MAX_WIDTH,...a}),r=document.documentElement;r.style.setProperty("--tab-rail-width",`${n}px`);const o=r.classList.contains("tab-rail-compact"),s=n<240;if(r.classList.toggle("tab-rail-compact",s),r.classList.toggle("tab-rail-tight",n<288),o!==s){const d=this._tallTabsEnabled;this.applyTabWrapSettings?.(),d!==void 0&&this._tallTabsEnabled!==d||this._fullRenderSessionTabs?.()}const l=document.getElementById("tabRailResizeHandle");return l&&(l.setAttribute("aria-valuemax",String(h)),l.setAttribute("aria-valuenow",String(n))),this.syncTabRailWidthSetting(i),n},_persistTabRailWidth(t){const e=this.loadAppSettingsFromStorage();e.tabRailWidth!==t&&(e.tabRailWidth=t,this.saveAppSettingsToStorage(e))},_claimTabRailResize(){this._tabRailResizeOwnsObserver=!0,clearTimeout(this._tabRailResizeWatchdog),clearTimeout(this._tabRailReleaseTimer),this._tabRailReleaseRaf&&cancelAnimationFrame(this._tabRailReleaseRaf),this._armTabRailResizeWatchdog(),this._resizeRaf&&cancelAnimationFrame(this._resizeRaf),this._resizeTimeout&&clearTimeout(this._resizeTimeout),this._resizeRaf=null,this._resizeTimeout=null},_armTabRailResizeWatchdog(){this._tabRailResizeWatchdog=setTimeout(()=>{if(this._tabRailResizeWatchdog=null,document.body.classList.contains("tab-rail-resizing")){this._armTabRailResizeWatchdog();return}this._tabRailResizeOwnsObserver=!1},1e3)},_releaseTabRailResize(){clearTimeout(this._tabRailResizeWatchdog),this._tabRailResizeWatchdog=null;const t=()=>{clearTimeout(this._tabRailReleaseTimer),this._tabRailReleaseTimer=null,this._tabRailReleaseRaf=null,this._tabRailResizeOwnsObserver=!1};typeof requestAnimationFrame=="function"?(this._tabRailReleaseRaf=requestAnimationFrame(()=>{this._tabRailReleaseRaf=requestAnimationFrame(t)}),this._tabRailReleaseTimer=setTimeout(t,250)):t()},_scheduleTabRailSettle(t,e=t){clearTimeout(this._tabRailSettleTimer),this._tabRailSettleTimer=setTimeout(async()=>{this._tabRailSettleTimer=null,this._persistTabRailWidth(e);try{this.activeSessionId&&this.sendResize?await this.sendResize(this.activeSessionId):this.syncTerminalGeometry?.(),this._updateConnectionLinesImmediate?.()}catch(i){console.warn("Failed to resize terminal after rail resize:",i)}finally{this._releaseTabRailResize()}},150)},_defaultTabRailWidth(){return document.documentElement.dataset.tabRailDetail!=="simple"?window.CodemanTabRail?.RICH_DEFAULT_WIDTH??320:window.CodemanTabRail?.DEFAULT_WIDTH??256},applyTabRailWidth(t={}){const i=this.loadAppSettingsFromStorage().tabRailWidth??this._defaultTabRailWidth(),a=window.CodemanTabRail?.resolveWidth({width:i})??256;t.settle&&this._claimTabRailResize();const n=this._setTabRailWidth(a);return t.persist!==!1&&i!==a&&this._persistTabRailWidth(a),t.settle&&this._scheduleTabRailSettle(n,a),n},_applyTabRailPointerWidth(t){const e=document.querySelector(".main");if(!e)return this._getCurrentTabRailWidth();const i=this._getCurrentTabRailWidth(),a=this._setTabRailWidth(t-e.getBoundingClientRect().left);let n=null;try{n=this.fitAddon?.proposeDimensions?.()}catch{}return n&&n.cols<40?this._setTabRailWidth(i):a},_queueTabRailPointerWidth(t){this._tabRailPendingClientX=t,!this._tabRailPointerRaf&&(this._tabRailPointerRaf=requestAnimationFrame(()=>{this._tabRailPointerRaf=null,this._tabRailDragWidth=this._applyTabRailPointerWidth(this._tabRailPendingClientX)}))},_finishTabRailDrag(t,e){if(!document.body.classList.contains("tab-rail-resizing"))return;this._tabRailPointerRaf&&(cancelAnimationFrame(this._tabRailPointerRaf),this._tabRailPointerRaf=null,this._tabRailDragWidth=this._applyTabRailPointerWidth(this._tabRailPendingClientX)),document.body.classList.remove("tab-rail-resizing");const i=document.getElementById("tabRailResizeShield");i&&(i.hidden=!0);try{t.hasPointerCapture?.(e)&&t.releasePointerCapture(e)}catch{}const a=window.CodemanTabRail?.resolveWidth({width:this._tabRailDragWidth})??this._tabRailDragWidth;this._scheduleTabRailSettle(this._tabRailDragWidth||this._getCurrentTabRailWidth(),a)},_onTabRailKeyDown(t){const e=window.CodemanTabRail?.resolveKeyboardWidth({key:t.key,shiftKey:t.shiftKey,currentWidth:this._getCurrentTabRailWidth(),defaultWidth:this._defaultTabRailWidth?.(),...this._getTabRailBounds()});if(e==null)return;t.preventDefault(),this._claimTabRailResize();const i=this._setTabRailWidth(e);this._scheduleTabRailSettle(i,window.CodemanTabRail.resolveWidth({width:e}))},initTabRailResize(){const t=document.getElementById("tabRailResizeHandle");if(!t||t.dataset.ready==="1")return;t.dataset.ready="1",this.applyTabRailWidth(),t.addEventListener("pointerdown",i=>{if(i.button!==0)return;i.preventDefault(),this.closeTabRailActionMenu(),this._claimTabRailResize(),this._tabRailDragWidth=this._getCurrentTabRailWidth(),this._tabRailPendingClientX=i.clientX,document.body.classList.add("tab-rail-resizing");const a=document.getElementById("tabRailResizeShield");a&&(a.hidden=!1);try{t.setPointerCapture(i.pointerId)}catch{document.body.classList.remove("tab-rail-resizing"),a&&(a.hidden=!0),this._releaseTabRailResize()}}),t.addEventListener("pointermove",i=>{t.hasPointerCapture?.(i.pointerId)&&this._queueTabRailPointerWidth(i.clientX)}),t.addEventListener("pointerup",i=>this._finishTabRailDrag(t,i.pointerId)),t.addEventListener("pointercancel",i=>this._finishTabRailDrag(t,i.pointerId)),t.addEventListener("lostpointercapture",i=>this._finishTabRailDrag(t,i.pointerId)),t.addEventListener("keydown",i=>this._onTabRailKeyDown(i)),t.addEventListener("dblclick",i=>{i.preventDefault(),this._claimTabRailResize();const a=this._defaultTabRailWidth?.()??(window.CodemanTabRail?.DEFAULT_WIDTH||256),n=this._setTabRailWidth(a);this._scheduleTabRailSettle(n,a)});let e=null;window.addEventListener("resize",()=>{clearTimeout(e),e=setTimeout(()=>{this.applyTabOrientation?.(),this.applyTabRailWidth({persist:!1})},100)})},closeTabRailActionMenu(t={}){const e=document.querySelector(".tab-rail-action-menu"),i=this._tabRailActionMenuTrigger;e?.remove(),this._tabRailActionMenuOutside&&(document.removeEventListener("pointerdown",this._tabRailActionMenuOutside,!0),this._tabRailActionMenuOutside=null),this._tabRailActionMenuViewport&&(window.removeEventListener("resize",this._tabRailActionMenuViewport),this._tabRailActionMenuViewport=null),this._tabRailActionMenuTrigger=null,t.restoreFocus&&i?.focus?.()},openTabRailActionMenu(t,e){t.preventDefault(),t.stopPropagation(),this.closeTabRailActionMenu();const i=t.currentTarget,a=document.createElement("div");a.className="tab-rail-action-menu",a.setAttribute("role","menu"),a.setAttribute("aria-label","Session actions");const n=this.loadAppSettingsFromStorage(),h=[{label:"Session options",run:()=>this.openSessionOptions(e)},...n.showTabDetachButton||this.detachedSessions?.has(e)?[{label:"Open in a new window",run:()=>this.detachSession(e)}]:[],{label:"Close session",className:"danger",run:()=>this.requestCloseSession(e)}];for(const s of h){const l=document.createElement("button");l.type="button",l.setAttribute("role","menuitem"),l.textContent=s.label,s.className&&(l.className=s.className),l.addEventListener("click",()=>{this.closeTabRailActionMenu(),s.run()}),a.appendChild(l)}document.body.appendChild(a);const r=i.getBoundingClientRect(),o=a.getBoundingClientRect();a.style.left=`${Math.max(8,Math.min(r.right-o.width,window.innerWidth-o.width-8))}px`,a.style.top=`${Math.max(8,Math.min(r.bottom+4,window.innerHeight-o.height-8))}px`,this._tabRailActionMenuTrigger=i,this._tabRailActionMenuOutside=s=>{!a.contains(s.target)&&s.target!==i&&this.closeTabRailActionMenu()},document.addEventListener("pointerdown",this._tabRailActionMenuOutside,!0),this._tabRailActionMenuViewport=()=>this.closeTabRailActionMenu(),window.addEventListener("resize",this._tabRailActionMenuViewport,{once:!0}),a.addEventListener("keydown",s=>{const l=[...a.querySelectorAll("button")],d=l.indexOf(document.activeElement);if(s.key==="Escape")s.preventDefault(),this.closeTabRailActionMenu({restoreFocus:!0});else if(s.key==="ArrowDown"||s.key==="ArrowUp"){s.preventDefault();const c=s.key==="ArrowDown"?1:-1;l[(d+c+l.length)%l.length]?.focus()}}),a.querySelector("button")?.focus()}});
|
|
1
|
+
"use strict";Object.assign(CodemanApp.prototype,{_getTabRailMinimumTerminalWidth(){const t=this.terminal?._core?._renderService?.dimensions?.css?.cell?.width;return Number.isFinite(t)&&t>0?Math.ceil(t*40+24):420},_getTabRailBounds(){const t=document.querySelector(".main");return{viewportWidth:window.innerWidth,mainWidth:t?.clientWidth||window.innerWidth,minTerminalWidth:this._getTabRailMinimumTerminalWidth()}},_getCurrentTabRailWidth(){const t=Number.parseFloat(document.documentElement.style.getPropertyValue("--tab-rail-width"));if(Number.isFinite(t))return t;const e=document.getElementById("tabRail")?.getBoundingClientRect?.().width;return Number.isFinite(e)&&e>0?e:window.CodemanTabRail?.DEFAULT_WIDTH||256},readTabRailWidthSetting(){const t=document.getElementById("appSettingsTabRailWidth");return t?t.value==="custom"?Number(t.dataset.currentWidth)||this._getCurrentTabRailWidth():Number(t.value)||window.CodemanTabRail?.DEFAULT_WIDTH||256:this._getCurrentTabRailWidth()},syncTabRailWidthSetting(t){const e=document.getElementById("appSettingsTabRailWidth");if(!e)return;const i=Math.round(t);if(e.dataset.currentWidth=String(i),e.querySelector(`option[value="${i}"]`)){e.value=String(i);return}const n=e.querySelector('option[value="custom"]');n&&(n.textContent=`Custom (${i}px)`),e.value="custom"},_setTabRailWidth(t){const e=window.CodemanTabRail;if(!e)return 256;const i=e.resolveWidth({width:t}),a=this._getTabRailBounds(),n=e.resolveWidth({width:i,...a}),h=e.resolveWidth({width:e.MAX_WIDTH,...a}),r=document.documentElement;r.style.setProperty("--tab-rail-width",`${n}px`);const o=r.classList.contains("tab-rail-compact"),s=n<240;if(r.classList.toggle("tab-rail-compact",s),r.classList.toggle("tab-rail-tight",n<288),o!==s){const d=this._tallTabsEnabled;this.applyTabWrapSettings?.(),d!==void 0&&this._tallTabsEnabled!==d||this._fullRenderSessionTabs?.()}const l=document.getElementById("tabRailResizeHandle");return l&&(l.setAttribute("aria-valuemax",String(h)),l.setAttribute("aria-valuenow",String(n))),this.syncTabRailWidthSetting(i),n},_persistTabRailWidth(t){const e=this.loadAppSettingsFromStorage();e.tabRailWidth!==t&&(e.tabRailWidth=t,this.saveAppSettingsToStorage(e))},_claimTabRailResize(){this._tabRailResizeOwnsObserver=!0,clearTimeout(this._tabRailResizeWatchdog),clearTimeout(this._tabRailReleaseTimer),this._tabRailReleaseRaf&&cancelAnimationFrame(this._tabRailReleaseRaf),this._armTabRailResizeWatchdog(),this._resizeRaf&&cancelAnimationFrame(this._resizeRaf),this._resizeTimeout&&clearTimeout(this._resizeTimeout),this._resizeRaf=null,this._resizeTimeout=null},_armTabRailResizeWatchdog(){this._tabRailResizeWatchdog=setTimeout(()=>{if(this._tabRailResizeWatchdog=null,document.body.classList.contains("tab-rail-resizing")){this._armTabRailResizeWatchdog();return}this._tabRailResizeOwnsObserver=!1},1e3)},_releaseTabRailResize(){clearTimeout(this._tabRailResizeWatchdog),this._tabRailResizeWatchdog=null;const t=()=>{clearTimeout(this._tabRailReleaseTimer),this._tabRailReleaseTimer=null,this._tabRailReleaseRaf=null,this._tabRailResizeOwnsObserver=!1};typeof requestAnimationFrame=="function"?(this._tabRailReleaseRaf=requestAnimationFrame(()=>{this._tabRailReleaseRaf=requestAnimationFrame(t)}),this._tabRailReleaseTimer=setTimeout(t,250)):t()},_scheduleTabRailSettle(t,e=t){clearTimeout(this._tabRailSettleTimer),this._tabRailSettleTimer=setTimeout(async()=>{this._tabRailSettleTimer=null,this._persistTabRailWidth(e);try{this.activeSessionId&&this.sendResize?await this.sendResize(this.activeSessionId):this.syncTerminalGeometry?.(),this._updateConnectionLinesImmediate?.()}catch(i){console.warn("Failed to resize terminal after rail resize:",i)}finally{this._releaseTabRailResize()}},150)},_defaultTabRailWidth(){return document.documentElement.dataset.tabRailDetail!=="simple"?window.CodemanTabRail?.RICH_DEFAULT_WIDTH??320:window.CodemanTabRail?.DEFAULT_WIDTH??256},applyTabRailWidth(t={}){const i=this.loadAppSettingsFromStorage().tabRailWidth??this._defaultTabRailWidth(),a=window.CodemanTabRail?.resolveWidth({width:i})??256;t.settle&&this._claimTabRailResize();const n=this._setTabRailWidth(a);return t.persist!==!1&&i!==a&&this._persistTabRailWidth(a),t.settle&&this._scheduleTabRailSettle(n,a),n},_applyTabRailPointerWidth(t){const e=document.querySelector(".main");if(!e)return this._getCurrentTabRailWidth();const i=this._getCurrentTabRailWidth(),a=this._setTabRailWidth(t-e.getBoundingClientRect().left);let n=null;try{n=this.fitAddon?.proposeDimensions?.()}catch{}return n&&n.cols<40?this._setTabRailWidth(i):a},_queueTabRailPointerWidth(t){this._tabRailPendingClientX=t,!this._tabRailPointerRaf&&(this._tabRailPointerRaf=requestAnimationFrame(()=>{this._tabRailPointerRaf=null,this._tabRailDragWidth=this._applyTabRailPointerWidth(this._tabRailPendingClientX)}))},_finishTabRailDrag(t,e){if(!document.body.classList.contains("tab-rail-resizing"))return;this._tabRailPointerRaf&&(cancelAnimationFrame(this._tabRailPointerRaf),this._tabRailPointerRaf=null,this._tabRailDragWidth=this._applyTabRailPointerWidth(this._tabRailPendingClientX)),document.body.classList.remove("tab-rail-resizing");const i=document.getElementById("tabRailResizeShield");i&&(i.hidden=!0);try{t.hasPointerCapture?.(e)&&t.releasePointerCapture(e)}catch{}const a=window.CodemanTabRail?.resolveWidth({width:this._tabRailDragWidth})??this._tabRailDragWidth;this._scheduleTabRailSettle(this._tabRailDragWidth||this._getCurrentTabRailWidth(),a)},_onTabRailKeyDown(t){const e=window.CodemanTabRail?.resolveKeyboardWidth({key:t.key,shiftKey:t.shiftKey,currentWidth:this._getCurrentTabRailWidth(),defaultWidth:this._defaultTabRailWidth?.(),...this._getTabRailBounds()});if(e==null)return;t.preventDefault(),this._claimTabRailResize();const i=this._setTabRailWidth(e);this._scheduleTabRailSettle(i,window.CodemanTabRail.resolveWidth({width:e}))},initTabRailResize(){const t=document.getElementById("tabRailResizeHandle");if(!t||t.dataset.ready==="1")return;t.dataset.ready="1",this.applyTabRailWidth(),t.addEventListener("pointerdown",i=>{if(i.button!==0)return;i.preventDefault(),this.closeTabRailActionMenu(),this._claimTabRailResize(),this._tabRailDragWidth=this._getCurrentTabRailWidth(),this._tabRailPendingClientX=i.clientX,document.body.classList.add("tab-rail-resizing");const a=document.getElementById("tabRailResizeShield");a&&(a.hidden=!1);try{t.setPointerCapture(i.pointerId)}catch{document.body.classList.remove("tab-rail-resizing"),a&&(a.hidden=!0),this._releaseTabRailResize()}}),t.addEventListener("pointermove",i=>{t.hasPointerCapture?.(i.pointerId)&&this._queueTabRailPointerWidth(i.clientX)}),t.addEventListener("pointerup",i=>this._finishTabRailDrag(t,i.pointerId)),t.addEventListener("pointercancel",i=>this._finishTabRailDrag(t,i.pointerId)),t.addEventListener("lostpointercapture",i=>this._finishTabRailDrag(t,i.pointerId)),t.addEventListener("keydown",i=>this._onTabRailKeyDown(i)),t.addEventListener("dblclick",i=>{i.preventDefault(),this._claimTabRailResize();const a=this._defaultTabRailWidth?.()??(window.CodemanTabRail?.DEFAULT_WIDTH||256),n=this._setTabRailWidth(a);this._scheduleTabRailSettle(n,a)});let e=null;window.addEventListener("resize",()=>{clearTimeout(e),e=setTimeout(()=>{this.applyTabOrientation?.(),this.applyTabRailWidth({persist:!1})},100)})},closeTabRailActionMenu(t={}){const e=document.querySelector(".tab-rail-action-menu:not(.tab-layout-group-action-menu)"),i=this._tabRailActionMenuTrigger;e?.remove(),this._tabRailActionMenuOutside&&(document.removeEventListener("pointerdown",this._tabRailActionMenuOutside,!0),this._tabRailActionMenuOutside=null),this._tabRailActionMenuViewport&&(window.removeEventListener("resize",this._tabRailActionMenuViewport),this._tabRailActionMenuViewport=null),this._tabRailActionMenuTrigger=null,t.restoreFocus&&i?.focus?.()},openTabRailActionMenu(t,e){t.preventDefault(),t.stopPropagation(),this.closeTabRailActionMenu();const i=t.currentTarget,a=document.createElement("div");a.className="tab-rail-action-menu",a.setAttribute("role","menu"),a.setAttribute("aria-label","Session actions");const n=this.loadAppSettingsFromStorage(),h=[{label:"Session options",run:()=>this.openSessionOptions(e)},...this._tabRefMoveActions?.({kind:"session",id:e})||[],...n.showTabDetachButton||this.detachedSessions?.has(e)?[{label:"Open in a new window",run:()=>this.detachSession(e)}]:[],{label:"Close session",className:"danger",run:()=>this.requestCloseSession(e)}];for(const s of h){const l=document.createElement("button");l.type="button",l.setAttribute("role","menuitem"),l.textContent=s.label,s.className&&(l.className=s.className),l.addEventListener("click",()=>{this.closeTabRailActionMenu(),s.run()}),a.appendChild(l)}document.body.appendChild(a);const r=i.getBoundingClientRect(),o=a.getBoundingClientRect();a.style.left=`${Math.max(8,Math.min(r.right-o.width,window.innerWidth-o.width-8))}px`,a.style.top=`${Math.max(8,Math.min(r.bottom+4,window.innerHeight-o.height-8))}px`,this._tabRailActionMenuTrigger=i,this._tabRailActionMenuOutside=s=>{!a.contains(s.target)&&s.target!==i&&this.closeTabRailActionMenu()},document.addEventListener("pointerdown",this._tabRailActionMenuOutside,!0),this._tabRailActionMenuViewport=()=>this.closeTabRailActionMenu(),window.addEventListener("resize",this._tabRailActionMenuViewport,{once:!0}),a.addEventListener("keydown",s=>{const l=[...a.querySelectorAll("button")],d=l.indexOf(document.activeElement);if(s.key==="Escape")s.preventDefault(),this.closeTabRailActionMenu({restoreFocus:!0});else if(s.key==="ArrowDown"||s.key==="ArrowUp"){s.preventDefault();const c=s.key==="ArrowDown"?1:-1;l[(d+c+l.length)%l.length]?.focus()}}),a.querySelector("button")?.focus()}});
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"case-routes.d.ts","sourceRoot":"","sources":["../../../src/web/routes/case-routes.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"case-routes.d.ts","sourceRoot":"","sources":["../../../src/web/routes/case-routes.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AA+D1C,OAAO,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAC5E,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AA4D9C;;;;;;;GAOG;AACH,wBAAgB,6BAA6B,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAE1E;AAyGD,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,eAAe,EAAE,GAAG,EAAE,SAAS,GAAG,UAAU,GAAG,WAAW,GAAG,IAAI,CA+tDxG"}
|
|
@@ -16,6 +16,8 @@ import { CreateCaseSchema, CloneCaseSchema, ClonePreflightSchema, LinkCaseSchema
|
|
|
16
16
|
import { exportDockerCase, importDockerBundle, listDockerExports, exportBundleName } from '../../docker-export.js';
|
|
17
17
|
import { cloneRepository, isGitAvailable, isSafeGitRef, parseGitRepositoryUrl, probeGitRemote, } from '../../git-clone.js';
|
|
18
18
|
import { generateClaudeMd } from '../../templates/claude-md.js';
|
|
19
|
+
import { prepareNewCasePath } from '../case-path.js';
|
|
20
|
+
import { boundedPathExists, describeUnknownPath, probePath } from '../../utils/index.js';
|
|
19
21
|
import { readAgentCaseMarker } from '../../agent-case-marker.js';
|
|
20
22
|
import { settingsWriteBlocker, writeHooksConfig } from '../../hooks-config.js';
|
|
21
23
|
import { canAccessOwned, getAuthUser, isAdmin, isWorkingDirAllowed, ownerFor, resolveCasesDir, SETTINGS_PATH, validatePathWithinBase, parseBody, readJsonConfig, } from '../route-helpers.js';
|
|
@@ -81,6 +83,9 @@ function gitDiagnosticLine(stderr) {
|
|
|
81
83
|
* the clone response says so out loud instead of silently merging into them.
|
|
82
84
|
*/
|
|
83
85
|
function repoShipsClaudeSettings(casePath) {
|
|
86
|
+
// Deliberately NOT the bounded path probe: the tree was just cloned into the
|
|
87
|
+
// local case space (and lstat'ed synchronously moments ago), so a bound protects
|
|
88
|
+
// nothing here, while a probe answering "unknown" could silently drop this warning.
|
|
84
89
|
return ['settings.json', 'settings.local.json'].some((file) => existsSync(join(casePath, '.claude', file)));
|
|
85
90
|
}
|
|
86
91
|
/**
|
|
@@ -170,7 +175,7 @@ export function registerCaseRoutes(app, ctx) {
|
|
|
170
175
|
cases.push({
|
|
171
176
|
name: e.name,
|
|
172
177
|
path: casePath,
|
|
173
|
-
hasClaudeMd:
|
|
178
|
+
hasClaudeMd: await boundedPathExists(join(casePath, 'CLAUDE.md')),
|
|
174
179
|
location: 'local',
|
|
175
180
|
...(marker ? { agentCreated: agentCreatedInfo(marker) } : {}),
|
|
176
181
|
});
|
|
@@ -185,15 +190,21 @@ export function registerCaseRoutes(app, ctx) {
|
|
|
185
190
|
const existingNames = new Set(cases.map((c) => c.name));
|
|
186
191
|
if (admin) {
|
|
187
192
|
for (const [name, path] of Object.entries(linkedCases)) {
|
|
188
|
-
if (
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
193
|
+
if (existingNames.has(name) || !SAFE_CASE_NAME.test(name))
|
|
194
|
+
continue;
|
|
195
|
+
const state = await probePath(path);
|
|
196
|
+
if (state === 'absent')
|
|
197
|
+
continue;
|
|
198
|
+
// An unreachable linked case (a dead network mount) stays listed and says
|
|
199
|
+
// so: dropping it would read as "deleted" and invite a same-name local case.
|
|
200
|
+
cases.push({
|
|
201
|
+
name,
|
|
202
|
+
path,
|
|
203
|
+
hasClaudeMd: state === 'present' && (await boundedPathExists(join(path, 'CLAUDE.md'))),
|
|
204
|
+
linked: true,
|
|
205
|
+
location: 'linked-local',
|
|
206
|
+
...(state === 'unknown' ? { unreachable: true } : {}),
|
|
207
|
+
});
|
|
197
208
|
}
|
|
198
209
|
}
|
|
199
210
|
// Get remote cases (owner-scoped; legacy no-owner = admin-only)
|
|
@@ -240,7 +251,7 @@ export function registerCaseRoutes(app, ctx) {
|
|
|
240
251
|
const dockerCaseInfo = {
|
|
241
252
|
name: dockerCase.name,
|
|
242
253
|
path: dockerDisplayPath({ container, path: dockerCase.hostWorkspacePath }),
|
|
243
|
-
hasClaudeMd:
|
|
254
|
+
hasClaudeMd: await boundedPathExists(join(dockerCase.hostWorkspacePath, 'CLAUDE.md')),
|
|
244
255
|
location: 'docker',
|
|
245
256
|
docker: {
|
|
246
257
|
hostId: host.id,
|
|
@@ -325,8 +336,98 @@ export function registerCaseRoutes(app, ctx) {
|
|
|
325
336
|
summaries.sort((a, b) => b.createdAt.localeCompare(a.createdAt));
|
|
326
337
|
return { success: true, data: { cases: summaries } };
|
|
327
338
|
});
|
|
328
|
-
|
|
329
|
-
|
|
339
|
+
/**
|
|
340
|
+
* `POST /api/cases` with a `path`: create the folder (or fill an EMPTY existing one), scaffold it
|
|
341
|
+
* exactly like a normal case, and register it in the linked-cases registry so it lists, resolves
|
|
342
|
+
* and deletes (unlinks, never removes files) like any linked case. Everything is judged before
|
|
343
|
+
* anything is written; a failure after the first write undoes what this call created.
|
|
344
|
+
*/
|
|
345
|
+
async function createCaseInCustomFolder(name, description, customPath, req, reply) {
|
|
346
|
+
const ownCasesDir = resolveCasesDir(getAuthUser(req));
|
|
347
|
+
if (existsSync(join(ownCasesDir, name))) {
|
|
348
|
+
reply.code(409);
|
|
349
|
+
return createErrorResponse(ApiErrorCode.ALREADY_EXISTS, 'A case with this name already exists in codeman-cases.');
|
|
350
|
+
}
|
|
351
|
+
const linkedCases = await readLinkedCases();
|
|
352
|
+
if (linkedCases[name]) {
|
|
353
|
+
reply.code(409);
|
|
354
|
+
return createErrorResponse(ApiErrorCode.ALREADY_EXISTS, `Case "${name}" is already linked to ${linkedCases[name]}`);
|
|
355
|
+
}
|
|
356
|
+
// The caller's own cases dir and the shared one (the same folder outside multi-user mode).
|
|
357
|
+
const casesDirs = [...new Set([ownCasesDir, resolveCasesDir()])];
|
|
358
|
+
const prepared = await prepareNewCasePath(customPath, { home: homedir(), dataDir: getDataDir(), casesDirs });
|
|
359
|
+
if (!prepared.ok) {
|
|
360
|
+
// A parent that did not answer is not a bad request: OPERATION_FAILED (422), like
|
|
361
|
+
// POST /api/sessions for a workingDir on a dead mount.
|
|
362
|
+
if (prepared.code === 'UNREACHABLE') {
|
|
363
|
+
reply.code(422);
|
|
364
|
+
return createErrorResponse(ApiErrorCode.OPERATION_FAILED, prepared.reason);
|
|
365
|
+
}
|
|
366
|
+
const status = prepared.code === 'NOT_FOUND' ? 404 : prepared.code === 'EXISTS' ? 409 : 400;
|
|
367
|
+
reply.code(status);
|
|
368
|
+
const code = prepared.code === 'NOT_FOUND'
|
|
369
|
+
? ApiErrorCode.NOT_FOUND
|
|
370
|
+
: prepared.code === 'EXISTS'
|
|
371
|
+
? ApiErrorCode.ALREADY_EXISTS
|
|
372
|
+
: ApiErrorCode.INVALID_INPUT;
|
|
373
|
+
return createErrorResponse(code, prepared.reason);
|
|
374
|
+
}
|
|
375
|
+
const casePath = prepared.path;
|
|
376
|
+
const alreadyAs = Object.entries(linkedCases).find(([, p]) => p === casePath)?.[0];
|
|
377
|
+
if (alreadyAs) {
|
|
378
|
+
reply.code(409);
|
|
379
|
+
return createErrorResponse(ApiErrorCode.ALREADY_EXISTS, `That folder is already the case "${alreadyAs}"`);
|
|
380
|
+
}
|
|
381
|
+
const made = [];
|
|
382
|
+
try {
|
|
383
|
+
if (!prepared.existedEmpty) {
|
|
384
|
+
mkdirSync(casePath);
|
|
385
|
+
made.push(casePath);
|
|
386
|
+
}
|
|
387
|
+
mkdirSync(join(casePath, 'src'));
|
|
388
|
+
made.push(join(casePath, 'src'));
|
|
389
|
+
const templatePath = await ctx.getDefaultClaudeMdPath();
|
|
390
|
+
writeFileSync(join(casePath, 'CLAUDE.md'), generateClaudeMd(name, description || '', templatePath));
|
|
391
|
+
made.push(join(casePath, 'CLAUDE.md'));
|
|
392
|
+
made.push(join(casePath, '.claude')); // before the write, so a half-written one is undone too
|
|
393
|
+
await writeHooksConfig(casePath);
|
|
394
|
+
const codemanDir = getDataDir();
|
|
395
|
+
if (!existsSync(codemanDir))
|
|
396
|
+
mkdirSync(codemanDir, { recursive: true });
|
|
397
|
+
// Re-read right before writing, so a case linked since the check above is not dropped. This only
|
|
398
|
+
// narrows the window: like POST /api/cases/link, the registry write is not serialized, and two
|
|
399
|
+
// requests that both read before either writes can still lose one entry.
|
|
400
|
+
const fresh = await readLinkedCases();
|
|
401
|
+
if (fresh[name])
|
|
402
|
+
throw Object.assign(new Error(`Case "${name}" was just linked`), { conflict: true });
|
|
403
|
+
fresh[name] = casePath;
|
|
404
|
+
await fs.writeFile(LINKED_CASES_FILE, JSON.stringify(fresh, null, 2));
|
|
405
|
+
ctx.broadcast(SseEvent.CaseCreated, { name, path: casePath });
|
|
406
|
+
return { success: true, data: { case: { name, path: casePath } } };
|
|
407
|
+
}
|
|
408
|
+
catch (err) {
|
|
409
|
+
// Undo only what this call made. The whole folder if we created it, otherwise the scaffold
|
|
410
|
+
// entries inside the empty folder the user picked; never anything else.
|
|
411
|
+
for (const p of made.reverse())
|
|
412
|
+
await fs.rm(p, { recursive: true, force: true }).catch(() => undefined);
|
|
413
|
+
if (err.conflict) {
|
|
414
|
+
reply.code(409);
|
|
415
|
+
return createErrorResponse(ApiErrorCode.ALREADY_EXISTS, getErrorMessage(err));
|
|
416
|
+
}
|
|
417
|
+
reply.code(500);
|
|
418
|
+
return createErrorResponse(ApiErrorCode.OPERATION_FAILED, getErrorMessage(err));
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
app.post('/api/cases', async (req, reply) => {
|
|
422
|
+
const { name, description, path: customPath } = parseBody(CreateCaseSchema, req.body);
|
|
423
|
+
// A custom folder writes outside the cases directory and registers the path in the shared,
|
|
424
|
+
// ownerless linked-cases registry, so it carries the same bar as POST /api/cases/link.
|
|
425
|
+
if (customPath !== undefined) {
|
|
426
|
+
const denied = adminOnly(req, reply);
|
|
427
|
+
if (denied)
|
|
428
|
+
return denied;
|
|
429
|
+
return createCaseInCustomFolder(name, description, customPath, req, reply);
|
|
430
|
+
}
|
|
330
431
|
const casePath = validatePathWithinBase(name, resolveCasesDir(getAuthUser(req)));
|
|
331
432
|
if (!casePath) {
|
|
332
433
|
return createErrorResponse(ApiErrorCode.INVALID_INPUT, 'Invalid case path');
|
|
@@ -1328,7 +1429,7 @@ export function registerCaseRoutes(app, ctx) {
|
|
|
1328
1429
|
return {
|
|
1329
1430
|
name,
|
|
1330
1431
|
path: dockerDisplayPath({ container, path: dockerCase.hostWorkspacePath }),
|
|
1331
|
-
hasClaudeMd:
|
|
1432
|
+
hasClaudeMd: await boundedPathExists(join(dockerCase.hostWorkspacePath, 'CLAUDE.md')),
|
|
1332
1433
|
location: 'docker',
|
|
1333
1434
|
docker: {
|
|
1334
1435
|
hostId: host.id,
|
|
@@ -1342,14 +1443,27 @@ export function registerCaseRoutes(app, ctx) {
|
|
|
1342
1443
|
};
|
|
1343
1444
|
}
|
|
1344
1445
|
const casePath = await resolveCasePath(name, getAuthUser(req));
|
|
1345
|
-
|
|
1446
|
+
const linked = casePath !== join(resolveCasesDir(getAuthUser(req)), name);
|
|
1447
|
+
// NOT_FOUND means DEFINITELY absent: the Run button creates a case on it, so
|
|
1448
|
+
// a path that merely did not answer (a dead network mount) must never get it.
|
|
1449
|
+
// One path, asked for explicitly: probe it even while unrelated mounts are dead.
|
|
1450
|
+
const state = await probePath(casePath, { pastCap: true });
|
|
1451
|
+
if (state === 'absent') {
|
|
1346
1452
|
return createErrorResponse(ApiErrorCode.NOT_FOUND, 'Case not found');
|
|
1347
1453
|
}
|
|
1348
|
-
|
|
1454
|
+
if (state === 'unknown') {
|
|
1455
|
+
// The linked registry knows where the case lives, so say where, and that
|
|
1456
|
+
// it is not answering. A local case has no such record to fall back on.
|
|
1457
|
+
if (!linked) {
|
|
1458
|
+
return createErrorResponse(ApiErrorCode.OPERATION_FAILED, describeUnknownPath('Case folder', casePath, { pastCap: true }));
|
|
1459
|
+
}
|
|
1460
|
+
return { name, path: casePath, hasClaudeMd: false, linked: true, unreachable: true };
|
|
1461
|
+
}
|
|
1349
1462
|
return {
|
|
1350
1463
|
name,
|
|
1351
1464
|
path: casePath,
|
|
1352
|
-
|
|
1465
|
+
// Probed like the folder above, or a healthy case reads as having no CLAUDE.md under the cap.
|
|
1466
|
+
hasClaudeMd: (await probePath(join(casePath, 'CLAUDE.md'), { pastCap: true })) === 'present',
|
|
1353
1467
|
...(linked && { linked: true }),
|
|
1354
1468
|
};
|
|
1355
1469
|
});
|
|
@@ -1362,7 +1476,11 @@ export function registerCaseRoutes(app, ctx) {
|
|
|
1362
1476
|
// Get case path (check linked cases first, then CASES_DIR)
|
|
1363
1477
|
const casePath = await resolveCasePath(name, getAuthUser(req));
|
|
1364
1478
|
const fixPlanPath = join(casePath, '@fix_plan.md');
|
|
1365
|
-
|
|
1479
|
+
const fixPlanState = await probePath(fixPlanPath, { pastCap: true });
|
|
1480
|
+
if (fixPlanState === 'unknown') {
|
|
1481
|
+
return createErrorResponse(ApiErrorCode.OPERATION_FAILED, 'Case folder is not responding or not readable');
|
|
1482
|
+
}
|
|
1483
|
+
if (fixPlanState === 'absent') {
|
|
1366
1484
|
return { exists: false, content: null, todos: [] };
|
|
1367
1485
|
}
|
|
1368
1486
|
try {
|