@omniterm/host 0.2.10

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.
@@ -0,0 +1,21 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" />
6
+ <title>OmniTerm</title>
7
+ <meta name="description" content="Lightweight browser-based dev environment" />
8
+ <link rel="manifest" href="/manifest.json" />
9
+ <link rel="apple-touch-icon" href="/apple-touch-icon.png" />
10
+ <meta name="application-name" content="OmniTerm" />
11
+ <meta name="mobile-web-app-capable" content="yes" />
12
+ <meta name="apple-mobile-web-app-capable" content="yes" />
13
+ <meta name="apple-mobile-web-app-title" content="OmniTerm" />
14
+ <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
15
+ <script type="module" crossorigin src="/assets/index-CN8KP4yl.js"></script>
16
+ <link rel="stylesheet" crossorigin href="/assets/index-C8vaHx63.css">
17
+ </head>
18
+ <body>
19
+ <div id="root"></div>
20
+ </body>
21
+ </html>
@@ -0,0 +1,8 @@
1
+ {
2
+ "name": "OmniTerm",
3
+ "short_name": "OmniTerm",
4
+ "display": "standalone",
5
+ "start_url": "/",
6
+ "background_color": "#1e1e1e",
7
+ "theme_color": "#1e1e1e"
8
+ }
@@ -0,0 +1,8 @@
1
+ {
2
+ "name": "OmniTerm",
3
+ "short_name": "OmniTerm",
4
+ "display": "standalone",
5
+ "start_url": "/",
6
+ "background_color": "#1e1e1e",
7
+ "theme_color": "#1e1e1e"
8
+ }
@@ -0,0 +1,90 @@
1
+ import {createRequire}from'module';import ge,{Router}from'express';import Kr from'http';import ln from'http-proxy';import x from'path';import {existsSync,readFileSync,readdirSync,copyFileSync,statSync,mkdirSync,writeFileSync,watch,unlinkSync}from'fs';import {fileURLToPath,pathToFileURL}from'url';import {execFile,execFileSync,spawn}from'child_process';import {homedir}from'os';import {promisify}from'util';import*as Et from'net';import {readdir,stat,readFile,writeFile}from'fs/promises';import Sr from'ignore';import {marked}from'marked';import {EventEmitter}from'events';createRequire(import.meta.url);
2
+ var yn=Object.defineProperty;var i=(e,t)=>yn(e,"name",{value:t,configurable:true});var bn=process.env.SETTINGS_DIR||x.join(homedir(),".omniterm"),fe=x.join(bn,"settings.json");function xn(e){let t=e??x.dirname(fileURLToPath(import.meta.url)),r=x.parse(t).root,n=t;for(;n!==r;){let o=x.join(n,"package.json");if(existsSync(o))try{if(JSON.parse(readFileSync(o,"utf-8")).name==="@omniterm/core")return n}catch{}n=x.dirname(n);}for(n=t;n!==r;){if(existsSync(x.join(n,"package.json")))return n;n=x.dirname(n);}return null}i(xn,"findOmnitermPackageRoot");var lt=xn(),Q=lt?x.join(lt,"bin"):null;function De(e,t){let r=x.resolve(e,t),n=x.normalize(r);if(!n.startsWith(e))throw new Error("Path traversal detected");return n}i(De,"resolveWorktreePath");function I(e,t){if(!e)return null;let r=e.startsWith("~")?e.replace("~",homedir()):e,n=x.normalize(x.resolve(r));for(let o of t){if(!o)continue;let s=x.normalize(x.resolve(o));if(n===s||n.startsWith(s+x.sep))return n}return null}i(I,"confinePath");var pt={trackedRepos:[],trackedDirs:[],lastActiveWorktree:null,sidebarCollapsed:false,namingSchemes:{},terminalFontSize:14,defaultShell:"bash",tabCustomNames:{},tabOrder:{},tabLayouts:{},activeSessionId:null,activePath:null,workspacesPanelMode:"docked",workspacesPanelDockedOpen:true,filesPanelMode:"docked",filesPanelDockedOpen:false,browserPanel:{},browserPanelOpen:null,filePanelTabs:{}};function S(){try{let e=readFileSync(fe,"utf-8");return {...pt,...JSON.parse(e)}}catch{let e=homedir(),t="/workspace",r=existsSync(t)&&statSync(t).isDirectory();return {...pt,trackedDirs:r?[t,e]:[e],activePath:r?t:e}}}i(S,"loadSettings");function C(e){let t=S(),r={...t,...e};return e.tabLayouts&&(r.tabLayouts={...t.tabLayouts,...e.tabLayouts}),e.filePanelTabs&&(r.filePanelTabs={...t.filePanelTabs,...e.filePanelTabs}),mkdirSync(x.dirname(fe),{recursive:true}),writeFileSync(fe,JSON.stringify(r,null,2),"utf-8"),r}i(C,"saveSettings");var ut={cosmos:["andromeda","apollo","aurora","cassini","comet","cosmos","eclipse","galaxy","horizon","lunar","mars","nebula","nova","orbit","phoenix","pulsar","quasar","saturn","solar","stellar","titan","venus","vega","zenith"],cities:["amsterdam","athens","bangkok","barcelona","berlin","cairo","dublin","florence","havana","istanbul","kyoto","lisbon","london","melbourne","nairobi","oslo","paris","prague","rome","seattle","sydney","tokyo","vancouver","vienna","zurich"],fruits:["apple","apricot","banana","blueberry","cherry","coconut","fig","grape","guava","kiwi","lemon","lime","lychee","mango","melon","orange","papaya","peach","pear","plum","pomelo","quince","raspberry","strawberry","tangerine"],music:["allegro","ballad","bass","blues","bolero","cadence","chord","crescendo","drums","encore","forte","fugue","harmony","jazz","lyric","melody","opera","piano","rhythm","samba","sonata","soprano","symphony","tempo","waltz"],gems:["agate","amber","amethyst","beryl","coral","crystal","diamond","emerald","garnet","jade","jasper","lapis","malachite","moonstone","obsidian","onyx","opal","pearl","quartz","ruby","sapphire","topaz","tourmaline","turquoise","zircon"],myths:["apollo","artemis","athena","atlas","aurora","cerberus","diana","echo","freya","griffin","helios","hermes","iris","juno","loki","medusa","minerva","neptune","odin","olympus","orion","pandora","pegasus","phoenix","thor"],ocean:["anchor","atoll","barnacle","coral","current","dolphin","driftwood","harbor","island","kelp","lagoon","lighthouse","marina","narwhal","nautilus","oyster","pelican","reef","sailboat","seashell","squid","starfish","tide","trident","whale"],spices:["anise","basil","cardamom","cayenne","chili","cinnamon","clove","coriander","cumin","dill","fennel","ginger","juniper","lavender","mint","nutmeg","oregano","paprika","pepper","rosemary","saffron","sage","thyme","turmeric","vanilla"],peaks:["alps","andes","denali","elbrus","everest","fuji","hood","kangchenjunga","kazbek","kilimanjaro","logan","makalu","matterhorn","mckinley","mont-blanc","olympus","rainier","shasta","sierra","sinai","snowdon","teton","vesuvius","whitney","zion"],dogs:["akita","beagle","boxer","bulldog","collie","corgi","dingo","doberman","husky","labrador","malamute","mastiff","pinscher","pointer","poodle","pug","retriever","rottweiler","samoyed","setter","shepherd","spaniel","terrier","vizsla","whippet"],dinos:["ankylo","bronto","compy","deinonychus","diplo","gallimimus","iguanodon","maia","megalosaurus","pachy","parasaur","plesio","pterano","raptor","rex","spinosaurus","stego","thero","trice","trodon","utah","veloci"],cafe:["affogato","americano","brew","cappuccino","chai","cortado","doppio","espresso","frappe","latte","lungo","macchiato","matcha","mocha","oolong","pourover","ristretto","robusta"]},Ne=Object.keys(ut);function En(e){let t=S();if(t.namingSchemes[e])return t.namingSchemes[e];let r=new Set(Object.values(t.namingSchemes)),n=Ne.find(o=>!r.has(o));return n||(n=Ne[Math.floor(Math.random()*Ne.length)]),C({namingSchemes:{...t.namingSchemes,[e]:n}}),n}i(En,"assignScheme");function mt(e,t){let r=En(e),n=ut[r],o=new Set(t),s=n.filter(l=>!o.has(l));if(s.length>0)return s[Math.floor(Math.random()*s.length)];let a=n[Math.floor(Math.random()*n.length)],c=Math.floor(Math.random()*900)+100;return `${a}-${c}`}i(mt,"generateName");function te(e,t){return execFileSync("git",e,{cwd:t,encoding:"utf-8"})}i(te,"execGit");function An(e,t){let r;try{r=te(["worktree","list","--porcelain"],e);}catch(a){return console.error("git worktree list failed:",a),[]}let n=[],o=r.split(`
3
+
4
+ `).filter(a=>a.trim()),s=true;for(let a of o){let c=a.split(`
5
+ `),l="",d="",p=false;for(let m of c)m.startsWith("worktree ")?l=m.substring(9):m.startsWith("branch ")?d=m.substring(7).replace("refs/heads/",""):m==="bare"&&(p=true);if(!l||p){s=false;continue}let u=x.basename(l);n.push({id:`wt-${u}`,repoId:t,branch:d||"HEAD",path:l,name:u,isMain:s}),s=false;}return n}i(An,"parseWorktreeList");function v(e,t){return An(e,t)}i(v,"listWorktrees");function On(e){try{return te(["for-each-ref","--format=%(refname:short)","refs/heads/"],e).trim().split(`
6
+ `).filter(Boolean)}catch{return []}}i(On,"listBranches");function gt(e,t,r,n=true){let o=v(e,t),s=o.map(m=>m.name),a=new Set(On(e)),c=mt(t,s),l=r||c;if(!r&&a.has(l))throw new Error(`Branch "${l}" already exists`);if(o.some(m=>m.branch===l))throw new Error(`Branch "${l}" already has a worktree`);let d=l.replace(/\//g,"-"),p=`${e}-${d}`;te(n?["worktree","add","-b",l,p]:["worktree","add",p,l],e);try{for(let m of readdirSync(e,{withFileTypes:!0}))m.isFile()&&m.name.startsWith(".env")&&copyFileSync(x.join(e,m.name),x.join(p,m.name));}catch{}return {id:`wt-${x.basename(p)}`,repoId:t,branch:l,path:p,name:x.basename(p),isMain:false}}i(gt,"createWorktree");function he(e,t){te(["worktree","remove","--force",t],e);}i(he,"removeWorktree");function ft(e,t,r){te(["branch","-m",t,r],e);}i(ft,"renameBranch");var Ce=parseInt(process.env.OMNITERM_TTYD_PORT_MIN||"7700",10),We=parseInt(process.env.OMNITERM_TTYD_PORT_MAX||"7799",10),ht=globalThis,ye=ht.__omniterm_ports||new Set;ht.__omniterm_ports=ye;function Nn(){try{let e=execFileSync("lsof",[`-iTCP:${Ce}-${We}`,"-sTCP:LISTEN","-P","-n","-Fn"],{encoding:"utf-8",stdio:["pipe","pipe","pipe"]}),t=new Set;for(let r of e.split(`
7
+ `)){if(!r.startsWith("n"))continue;let n=r.match(/:(\d+)$/);n&&t.add(parseInt(n[1],10));}return t}catch{return new Set}}i(Nn,"getOccupiedPorts");function Fe(){let e=Nn();for(let t=Ce;t<=We;t++)if(!ye.has(t)&&!e.has(t))return ye.add(t),t;throw new Error(`No free ports available in range ${Ce}-${We}`)}i(Fe,"allocatePort");function ne(e){ye.delete(e);}i(ne,"freePort");var yt=false;function Ue(){if(!yt){yt=true;try{execFileSync("tmux",["set-option","-g","set-clipboard","on"],{stdio:"ignore"});}catch{}try{execFileSync("tmux",["set-option","-as","terminal-features","*:clipboard"],{stdio:"ignore"});}catch{}for(let e of ["copy-mode","copy-mode-vi"])try{execFileSync("tmux",["bind-key","-T",e,"MouseDragEnd1Pane","send-keys","-X","copy-pipe-and-cancel"],{stdio:"ignore"});}catch{}try{execFileSync("tmux",["bind-key","-n","PageUp","copy-mode"],{stdio:"ignore"});}catch{}}}i(Ue,"ensureTmuxBindings");function we(e,t,r,n){let o=S().defaultShell||"bash",s=["new-session","-d","-s",e];if(t){let l=t==="~"?homedir():t;s.push("-c",l);}if(r)for(let[l,d]of Object.entries(r))s.push("-e",`${l}=${d}`);if(n?.unsetEnv?.length){s.push("env");for(let l of n.unsetEnv)s.push("-u",l);}s.push(o),execFileSync("tmux",s,{stdio:"ignore"});let a=[["mouse","on"],["history-limit","50000"],["status-position","bottom"],["base-index","1"],["renumber-windows","on"],["escape-time","0"],["focus-events","on"]],c=[];for(let[l,d]of a)c.length>0&&c.push(";"),c.push("set-option","-t",e,l,d);try{execFileSync("tmux",c,{stdio:"ignore"});}catch{}Ue();}i(we,"createTmuxSession");function Be(e){try{execFileSync("tmux",["kill-session","-t",e],{stdio:"ignore"});}catch{}try{unlinkSync(`/tmp/omniterm-silence-${e}`);}catch{}}i(Be,"killTmuxSession");function Se(){try{let e=execFileSync("tmux",["list-sessions","-F","#{session_name}|||#{session_path}|||#{session_created}"],{encoding:"utf-8"}).trim();return e?e.split(`
8
+ `).map(t=>{let[r,n,o]=t.split("|||");return {name:r,cwd:n||"~",created:o}}):[]}catch{return []}}i(Se,"listTmuxSessions");function wt(e){try{let t=execFileSync("tmux",["display-message","-t",e,"-p","#{pane_title}"],{encoding:"utf-8"}).trim();return t&&t!==e&&!t.match(/^[a-zA-Z0-9-]+$/)?t:execFileSync("tmux",["display-message","-t",e,"-p","#{pane_current_command}"],{encoding:"utf-8"}).trim()}catch{return ""}}i(wt,"getTmuxPaneTitle");function Le(e){try{return execFileSync("tmux",["has-session","-t",e],{stdio:"ignore"}),!0}catch{return false}}i(Le,"tmuxSessionExists");var oe=globalThis,R=oe.__omniterm_sse_clients||new Set;oe.__omniterm_sse_clients=R;function St(e){R.add(e),console.log(`[events] SSE client connected; clients=${R.size}`),e.on("close",()=>{R.delete(e),console.log(`[events] SSE client disconnected; clients=${R.size}`);}),Un();}i(St,"addClient");var Fn=3e4,re=oe.__omniterm_sse_heartbeat??null;function Un(){re||(re=setInterval(()=>{if(R.size===0){clearInterval(re),re=null,oe.__omniterm_sse_heartbeat=null;return}for(let e of R)try{e.write(`: heartbeat
9
+
10
+ `);}catch{R.delete(e);}},Fn),oe.__omniterm_sse_heartbeat=re);}i(Un,"startHeartbeat");function M(e,t){let r=JSON.stringify({type:e,...t});console.log(`[events] broadcast type=${e} clients=${R.size} payload=${r}`);for(let n of R)try{n.write(`data: ${r}
11
+
12
+ `);}catch{R.delete(n);}}i(M,"broadcast");function bt(){return R.size}i(bt,"getClientCount");var xt=promisify(execFile),Hn=3e3,vt=2e3,qn=15,Gn=20,Vn=2,Yn="#{session_name} #{window_activity} #{window_active} #{pane_active} #{pane_current_path}",Tt=globalThis,b=Tt.__omniterm_silence_monitor??{states:new Map,legacyCleaned:new Set,timer:null,polling:false};Tt.__omniterm_silence_monitor=b;function ze(e){b.states.has(e)||b.states.set(e,{lastActivitySeconds:null,activeSinceSeconds:null,path:""}),er(e),Xn();}i(ze,"trackSilenceSession");function se(e){b.states.delete(e),Rt(e);}i(se,"untrackSilenceSession");function Rt(e){b.legacyCleaned.delete(e),kt(e),b.states.size===0&&Jn();}i(Rt,"cleanupSilenceSession");function Xn(){b.timer||(b.timer=setInterval(()=>{Kn();},Hn),b.timer.unref?.());}i(Xn,"ensurePoller");function Jn(){b.timer&&(clearInterval(b.timer),b.timer=null);}i(Jn,"stopPoller");async function Kn(){if(!(b.polling||b.states.size===0)){b.polling=true;try{let e=new Set(b.states.keys()),{stdout:t}=await xt("tmux",["list-panes","-a","-F",Yn],{encoding:"utf-8",timeout:vt}),r=Zn(t,e);Qn(b.states,r,tr(),n=>{console.log(`[alert] broadcast session-silence session=${n.sessionId} cwd=${n.path} sse_clients=${bt()}`),M("session-silence",{path:n.path,sessionId:n.sessionId});},{onSessionDeleted:Rt,trackedSessions:e});}catch{}finally{b.polling=false;}}}i(Kn,"pollSilenceSessions");function Zn(e,t){let r=new Map;for(let n of e.split(`
13
+ `)){if(!n.trim())continue;let[o,s,a,c,...l]=n.split(" ");if(!o||!t.has(o))continue;let d=s?.trim();if(!d)continue;let p=Number(d);if(!Number.isFinite(p))continue;let u=l.join(" ").trim(),m=a==="1"&&c==="1",w=r.get(o);if(!w){r.set(o,{activitySeconds:p,path:u,activePaneSeen:m});continue}p>w.activitySeconds&&(w.activitySeconds=p),(m||!w.activePaneSeen)&&(w.path=u,w.activePaneSeen=w.activePaneSeen||m);}return new Map([...r].map(([n,o])=>[n,{activitySeconds:o.activitySeconds,path:o.path}]))}i(Zn,"parseTmuxActivitySnapshots");function Qn(e,t,r,n,o={}){let s=o.silenceSeconds??qn,a=o.activeCooldownSeconds??Gn,c=o.absentPollsBeforeDelete??Vn;for(let[l,d]of e){if(o.trackedSessions&&!o.trackedSessions.has(l))continue;let p=t.get(l);if(!p){d.missingPolls=(d.missingPolls??0)+1,d.missingPolls>=c&&(e.delete(l),o.onSessionDeleted?.(l));continue}if(d.missingPolls=0,d.path=p.path,d.lastActivitySeconds===null){d.lastActivitySeconds=p.activitySeconds;continue}if(p.activitySeconds<d.lastActivitySeconds){d.lastActivitySeconds=p.activitySeconds,d.activeSinceSeconds=null;continue}if(p.activitySeconds>d.lastActivitySeconds){d.activeSinceSeconds===null&&(d.activeSinceSeconds=p.activitySeconds),d.lastActivitySeconds=p.activitySeconds;continue}if(d.activeSinceSeconds!==null&&r-d.lastActivitySeconds>=s){if(d.lastActivitySeconds-d.activeSinceSeconds<a){d.activeSinceSeconds=null;continue}d.path&&(n({sessionId:l,path:d.path}),d.activeSinceSeconds=null);}}}i(Qn,"updateSilenceStates");function er(e){b.legacyCleaned.has(e)||(b.legacyCleaned.add(e),xt("tmux",["set-hook","-u","-t",e,"alert-activity",";","set-hook","-u","-t",e,"alert-silence",";","set-option","-t",e,"activity-action","none",";","set-option","-t",e,"silence-action","none",";","set-window-option","-t",e,"monitor-activity","off",";","set-window-option","-t",e,"monitor-silence","0"],{timeout:vt}).catch(()=>{}).finally(()=>kt(e)));}i(er,"disableLegacySilenceMonitoring");function kt(e){try{unlinkSync(`/tmp/omniterm-silence-${e}`);}catch{}}i(kt,"removeLegacyStateFile");function tr(){return Math.floor(Date.now()/1e3)}i(tr,"nowSeconds");function Pt(){return Promise.resolve()}i(Pt,"shutdownTelemetry");function _t(){return []}i(_t,"getRecentPerfMetrics");var xe=globalThis,W=xe.__omniterm_ttyd||new Map,be=xe.__omniterm_ttyd_stopped_listeners||new Set;xe.__omniterm_ttyd=W;xe.__omniterm_ttyd_stopped_listeners=be;function He(e){for(let t of be)try{t(e);}catch(r){let n=r instanceof Error?r.message:String(r);console.error(`[ttyd] stopped listener failed for ${e}: ${n}`);}}i(He,"notifyTtydStopped");function Mt(e){return be.add(e),()=>{be.delete(e);}}i(Mt,"onTtydStopped");function ie(e,t){let r=S().terminalFontSize||18,n=["--writable","-i","127.0.0.1","-b",`/t/${e}/`,"-t","rendererType=dom","-t",`fontSize=${r}`,"-p",String(t),"--","bash","-c",`exec tmux attach-session -t ${e}`],o=spawn("ttyd",n,{stdio:"ignore",detached:true});return o.once("error",s=>{console.error(`[ttyd] failed to spawn for ${e} on port ${t}: ${s.message}`),W.get(e)===o&&(W.delete(e),He(e));}),o.once("exit",(s,a)=>{W.get(e)===o&&(W.delete(e),He(e)),(s!==0||a)&&console.error(`[ttyd] exited for ${e} on port ${t}: code=${s??"null"} signal=${a??"null"}`);}),o.unref(),W.set(e,o),o}i(ie,"spawnTtyd");function $t(e,t=3e3){return new Promise((r,n)=>{let o=Date.now()+t;function s(){let a=new Et.Socket;a.setTimeout(100),a.connect(e,"127.0.0.1",()=>{a.destroy(),r();}),a.on("error",()=>{a.destroy(),Date.now()>=o?n(new Error(`ttyd not ready on port ${e} after ${t}ms`)):setTimeout(s,30);}),a.on("timeout",()=>{a.destroy(),Date.now()>=o?n(new Error(`ttyd not ready on port ${e} after ${t}ms`)):setTimeout(s,30);});}i(s,"attempt"),s();})}i($t,"waitForTtydReady");function ae(e){let t=W.get(e);if(t&&t.pid)try{process.kill(t.pid,"SIGTERM");}catch{}W.delete(e)&&He(e);}i(ae,"stopTtyd");function qe(e){let t=W.get(e);if(!t||!t.pid)return false;try{return process.kill(t.pid,0),!0}catch{return false}}i(qe,"isTtydAlive");function rr(e,t){if(!e.trim())return [];let r=[];for(let n of e.split(`
14
+ `)){if(!n.includes("ttyd")||!n.includes("--writable"))continue;let o=n.match(/^\s*(\d+)/),s=n.match(/-b \/t\/([^/]+)\//);if(!o||!s)continue;let a=Number(o[1]),c=s[1];t.has(c)||r.push(a);}return r.sort((n,o)=>n-o)}i(rr,"getTtydPidsToKill");function Ge(){let e;try{let t=execFileSync("tmux",["list-sessions","-F","#{session_name}"],{encoding:"utf-8",stdio:["pipe","pipe","pipe"]}).trim();e=new Set(t?t.split(`
15
+ `):[]);}catch{e=new Set;}try{let t=execFileSync("ps",["-eo","pid,command"],{encoding:"utf-8"}).trim();if(!t)return;let r=0;for(let n of rr(t,e))try{process.kill(n,"SIGTERM"),r++;}catch{}r>0&&(console.log(`[cleanup] killed ${r} stale ttyd processes`),void 0);}catch{}}i(Ge,"killOrphanedTtydProcesses");function jt(){return {readySessionNames:new Set,pendingReadiness:new Map,generations:new Map}}i(jt,"createSessionTtydReadinessState");function k(e,t){t.readySessionNames.delete(e),t.pendingReadiness.delete(e),t.generations.set(e,(t.generations.get(e)??0)+1);}i(k,"clearSessionTtydReadiness");async function At(e,t,r,n){let o=e.tmuxName;if(n.readySessionNames.has(o)){if(r.isTtydAlive(o))return;k(o,n);}let s=n.pendingReadiness.get(o);if(s){if(await s,n.readySessionNames.has(o)&&r.isTtydAlive(o))return;throw new Error(`ttyd readiness invalidated for ${o}`)}let a=n.generations.get(o)??0;r.isTtydAlive(o)||(k(o,n),a=n.generations.get(o)??0,r.spawnTtyd(o,e.port));let c=r.waitForTtydReady(e.port,t).then(()=>{if((n.generations.get(o)??0)!==a)throw new Error(`ttyd readiness invalidated for ${o}`);if(!r.isTtydAlive(o))throw k(o,n),new Error(`ttyd exited before readiness completed for ${o}`);n.readySessionNames.add(o);}).finally(()=>{n.pendingReadiness.get(o)===c&&n.pendingReadiness.delete(o);});n.pendingReadiness.set(o,c),await c;}i(At,"ensureSessionTtydReadyWithState");var Ot=globalThis;function Dt(){Ot.__omniterm_recovered||(Ot.__omniterm_recovered=true,Ge());}i(Dt,"recoverAll");var ve=Q?x.join(Q,"omniterm-browser.js"):null,ir=ve!==null&&existsSync(ve);function Ye(e){let t={OMNITERM_BROWSER_REGISTRY_URL:e};return ir&&ve&&Q&&(t.BROWSER=ve,t.PATH=`${Q}:${process.env.PATH??""}`),t}i(Ye,"buildTabEnv");var Xe=["OMNITERM_DEVTOOLS_DIR","OMNITERM_OWNER_ID","OMNITERM_PORT","OMNITERM_HOST","OMNITERM_TTYD_PORT_MIN","OMNITERM_TTYD_PORT_MAX","OMNITERM_BROWSER_DISCOVERY_URL"],U=globalThis,$=U.__omniterm_sessions||new Map,Ve=U.__omniterm_counters||new Map,D=U.__omniterm_ttyd_readiness||jt();U.__omniterm_sessions=$;U.__omniterm_counters=Ve;U.__omniterm_ttyd_readiness=D;var ar={isTtydAlive:qe,spawnTtyd:ie,waitForTtydReady:$t};U.__omniterm_session_ttyd_listener_registered||(Mt(e=>k(e,D)),U.__omniterm_session_ttyd_listener_registered=true);function cr(e){let t=(Ve.get(e)||0)+1;for(;Le(`${e}-term-${t}`);)t++;return Ve.set(e,t),`${e}-term-${t}`}i(cr,"nextId");Dt();function Ct(e,t,r){let n=Date.now(),o=cr(e),s=Fe(),a=Date.now();we(o,t,Ye(r.registryUrlFor(o)),{unsetEnv:Xe});let c=Date.now(),l={id:o,worktreeId:e,tmuxName:o,port:s,createdAt:new Date().toISOString()};k(l.tmuxName,D),ie(l.tmuxName,l.port);let d=Date.now();$.set(o,l),ze(l.tmuxName);let p=Date.now();return console.log(`[session] ${o}: tmux=${c-a}ms ttyd=${d-c}ms total=${p-n}ms`),M("session-created",{sessionId:o,worktreeId:e,path:t}),l}i(Ct,"createSession");function H(e){let t=$.get(e);if(t){if(!Le(t.tmuxName)){k(t.tmuxName,D),ae(t.tmuxName),ne(t.port),$.delete(e),se(t.tmuxName);return}return qe(t.tmuxName)||(k(t.tmuxName,D),ie(t.tmuxName,t.port)),t}}i(H,"getSession");async function N(e,t=3e3){await At(e,t,ar,D);}i(N,"ensureSessionTtydReady");function Te(e){let t=$.get(e);return t?(k(t.tmuxName,D),ae(t.tmuxName),ne(t.port),$.delete(e),se(t.tmuxName),true):false}i(Te,"unregisterSession");function B(e){let t=Array.from($.values());return e?t.filter(r=>r.worktreeId===e):t}i(B,"listSessions");function ce(e){let t=$.get(e);return t?(k(t.tmuxName,D),ae(t.tmuxName),Be(t.tmuxName),ne(t.port),$.delete(e),se(t.tmuxName),M("session-closed",{sessionId:e,worktreeId:t.worktreeId}),true):false}i(ce,"deleteSession");function Re(e){let t=B(e);for(let r of t)k(r.tmuxName,D),ae(r.tmuxName),Be(r.tmuxName),ne(r.port),$.delete(r.id),se(r.tmuxName);return t.length>0&&M("session-closed",{worktreeId:e,sessionCount:t.length}),t.length}i(Re,"deleteSessionsForWorktree");function ke(e,t){let r=Date.now(),n=Fe(),o=Date.now();if(!t?.skipTmuxSetup){Ue();try{execFileSync("tmux",["set-option","-t",e,"mouse","on"],{stdio:"ignore"});}catch{}}let s=Date.now();k(e,D),ie(e,n);let a=Date.now(),c={id:e,worktreeId:"_orphan",tmuxName:e,port:n,createdAt:new Date().toISOString()};$.set(c.id,c),ze(c.tmuxName);let l=o-r,d=a-r;console.log(`[session] adopted ${e}: port=${n} allocate=${l}ms setup=${s-o}ms ttyd=${a-s}ms total=${d}ms skip=${t?.skipTmuxSetup?1:0}`);let p=t?.knownCwd??"";if(!p)try{p=execFileSync("tmux",["display-message","-t",e,"-p","#{pane_current_path}"],{encoding:"utf-8"}).trim();}catch{}return M("session-adopted",{sessionId:e,worktreeId:"_orphan",path:p}),c}i(ke,"adoptSession");function lr(e){return x.basename(e).replace(/\.git$/,"")}i(lr,"slugFromPath");function dr(e){try{return execFileSync("git",["config","--get","remote.origin.url"],{cwd:e,encoding:"utf-8"}).trim()}catch{return ""}}i(dr,"getRemoteUrl");function Ft(e){return existsSync(x.join(e,".git"))||existsSync(x.join(e,"HEAD"))}i(Ft,"isGitRepo");function T(){return S().trackedRepos.filter(t=>existsSync(t)&&Ft(t)).map(t=>({id:lr(t),remoteUrl:dr(t),path:t}))}i(T,"listRepos");function pe(e){return T().find(t=>t.id===e)}i(pe,"getRepo");function Je(e){let t=x.resolve(e);if(!existsSync(t))throw new Error(`Path "${t}" does not exist`);let r=S();if(Ft(t))return r.trackedRepos.includes(t)||C({trackedRepos:[...r.trackedRepos,t]}),{type:"repo",path:t};{let n=r.trackedRepos.filter(s=>s!==t),o=r.trackedDirs.includes(t)?r.trackedDirs:[...r.trackedDirs,t];return (n.length!==r.trackedRepos.length||o!==r.trackedDirs)&&C({trackedRepos:n,trackedDirs:o}),{type:"dir",path:t}}}i(Je,"addLocalPath");function Ut(e,t){let r=e.match(/\/([^/]+?)(\.git)?$/)?.[1]||"repo",n=t||x.join(process.env.HOME||"/tmp",r);if(existsSync(n))throw new Error(`"${n}" already exists`);execFileSync("git",["clone",e,n],{encoding:"utf-8",timeout:3e5});let o=S();return C({trackedRepos:[...o.trackedRepos,n]}),{id:r,remoteUrl:e,path:n}}i(Ut,"cloneRepo");function Bt(e){let t=pe(e);if(!t)throw new Error(`Repo "${e}" not found`);let r=0,n=0,o=v(t.path,e);for(let a of o){n+=Re(a.id);try{he(t.path,a.path);}catch{}r++;}let s=S();return C({trackedRepos:s.trackedRepos.filter(a=>a!==t.path)}),{worktreesRemoved:r,sessionsKilled:n}}i(Bt,"removeRepo");var q=Router();q.get("/repos",(e,t)=>{t.json(T());});q.post("/repos",async(e,t)=>{let{url:r,localPath:n,destination:o}=e.body||{};if(!r&&!n){t.status(400).json({error:"url or localPath is required"});return}try{let s=n?Je(n):null,a=s?{id:s.path.split("/").pop(),...s}:Ut(r,o);t.status(201).json(a);}catch(s){let a=s instanceof Error?s.message:String(s);t.status(a.includes("already")?409:400).json({error:a});}});q.delete("/repos/:repoId",(e,t)=>{if(!pe(e.params.repoId)){t.status(404).json({error:"Repo not found"});return}let n=Bt(e.params.repoId);t.json({deleted:true,...n});});q.get("/repos/:repoId/worktrees",(e,t)=>{let r=pe(e.params.repoId);if(!r){t.status(404).json({error:"Repo not found"});return}let n=v(r.path,e.params.repoId),o=Se();t.json(n.map(s=>({...s,sessionCount:o.filter(a=>a.cwd===s.path).length})));});q.post("/repos/:repoId/worktrees",async(e,t)=>{let r=pe(e.params.repoId);if(!r){t.status(404).json({error:"Repo not found"});return}let{branch:n,newBranch:o=true}=e.body||{};try{let s=gt(r.path,e.params.repoId,n,o);t.status(201).json(s);}catch(s){let a=s instanceof Error?s.message:String(s);t.status(a.includes("already has a worktree")?409:400).json({error:a});}});var Lt={".ts":"typescript",".tsx":"tsx",".js":"javascript",".jsx":"jsx",".json":"json",".md":"markdown",".css":"css",".scss":"scss",".html":"html",".py":"python",".rb":"ruby",".go":"go",".rs":"rust",".java":"java",".c":"c",".cpp":"cpp",".h":"c",".sh":"bash",".bash":"bash",".zsh":"bash",".yaml":"yaml",".yml":"yaml",".toml":"toml",".xml":"xml",".sql":"sql",".graphql":"graphql",".dockerfile":"dockerfile",".tf":"hcl"},zt={dockerfile:"dockerfile",makefile:"makefile"};[...new Set([...Object.values(Lt),...Object.values(zt)])];function Pe(e,t){return Lt[e]||zt[t]||"text"}i(Pe,"detectLanguage");function ur(e){return e instanceof Error?e.message:String(e)}i(ur,"errorMessage");function Ht(e,t,r,n){n(t.id),e.status(503).json({error:ur(r)});}i(Ht,"sendWorktreeSessionReadinessError");var L=Router();function Gt(e){for(let t of T()){let r=v(t.path,t.id).find(n=>n.id===e);if(r)return r.path}return null}i(Gt,"findWorktreePath");L.delete("/worktrees/:wtId",(e,t)=>{let{wtId:r}=e.params;for(let n of T()){let o=v(n.path,n.id).find(s=>s.id===r);if(o){let s=Re(r);he(n.path,o.path),t.json({deleted:true,sessionsKilled:s});return}}t.status(404).json({error:"Worktree not found"});});L.post("/worktrees/:wtId/rename",(e,t)=>{let{wtId:r}=e.params,{newName:n}=e.body||{};if(!n){t.status(400).json({error:"newName is required"});return}for(let o of T()){let s=v(o.path,o.id).find(a=>a.id===r);if(s){try{ft(s.path,s.branch,n),t.json({branch:n});}catch(a){t.status(409).json({error:a instanceof Error?a.message:String(a)});}return}}t.status(404).json({error:"Worktree not found"});});L.get("/worktrees/:wtId/sessions",(e,t)=>{let r=B(e.params.wtId);t.json(r.map(n=>({id:n.id,worktreeId:n.worktreeId,port:n.port,url:`/t/${n.id}/`,createdAt:n.createdAt,command:wt(n.tmuxName)})));});L.post("/worktrees/:wtId/sessions",async(e,t)=>{let{wtId:r}=e.params,{worktreePath:n}=e.body||{};if(!n){t.status(400).json({error:"worktreePath is required"});return}try{let o=e.headers.host??"127.0.0.1:17716",s=Ct(r,n,{registryUrlFor:i(a=>`http://${o}/t/${a}/registry`,"registryUrlFor")});try{await N(s);}catch(a){Ht(t,s,a,ce);return}t.status(201).json({id:s.id,worktreeId:s.worktreeId,port:s.port,url:`/t/${s.id}/`,createdAt:s.createdAt});}catch(o){let s=o instanceof Error?o.message:String(o);t.status(s.includes("No free ports")?503:500).json({error:s});}});L.get("/worktrees/:wtId/files",async(e,t)=>{let r=Gt(e.params.wtId);if(!r){t.status(404).json({error:"Worktree not found"});return}let n=e.query.path||"",o;try{o=De(r,n);}catch{t.status(403).json({error:"Path traversal detected"});return}try{let s=await readdir(o,{withFileTypes:!0}),a=await Promise.all(s.filter(c=>!c.name.startsWith(".")).map(async c=>{let l=c.isDirectory()?"directory":"file",d=0;if(l==="file")try{d=(await stat(x.join(o,c.name))).size;}catch{}return {name:c.name,type:l,size:d}}));a.sort((c,l)=>c.type!==l.type?c.type==="directory"?-1:1:c.name.localeCompare(l.name)),t.json({path:n,entries:a});}catch{t.status(404).json({error:"Path not found"});}});L.get("/worktrees/:wtId/file",async(e,t)=>{let r=Gt(e.params.wtId);if(!r){t.status(404).json({error:"Worktree not found"});return}let n=e.query.path||"";if(!n){t.status(400).json({error:"path is required"});return}let o;try{o=De(r,n);}catch{t.status(403).json({error:"Path traversal detected"});return}try{let s=await stat(o);if(s.size>1024*1024){t.status(413).json({error:"File too large (>1MB)"});return}let a=await readFile(o,"utf-8"),c=x.extname(o).toLowerCase(),l=x.basename(o).toLowerCase(),d=Pe(c,l);t.json({path:n,content:a,language:d,size:s.size});}catch{t.status(404).json({error:"File not found"});}});var ue=null,X=null,me=null,_e=null,Vt=Yt(5e3),br=500;function xr(e){return e.replace(/\\/g,"/")}i(xr,"normalizeWatchPath");function vr(e){return e===".git"||e.startsWith(".git/")}i(vr,"isGitInternalPath");function Yt(e){let t=Number.NEGATIVE_INFINITY;return {shouldLog(r=Date.now()){return r-t<e?false:(t=r,true)}}}i(Yt,"createWatchEventLogLimiter");function Xt(e){let t=Sr();try{let r=readFileSync(x.join(e,".gitignore"),"utf-8");t.add(r);}catch{}return t.add(["node_modules",".next","dist","standalone","test-results/","e2e/test-results/","playwright-report/","e2e/shiplight-report/","shiplight-report/","**/.playwright-artifacts-*"]),t}i(Xt,"buildWatchIgnoreMatcher");function Tr(e,t,r){let n=xr(t);return n==="<unknown>"?false:n===".gitignore"||n.endsWith("/.gitignore")?(_e=Xt(e),false):vr(n)?true:r?r.ignores(n):false}i(Tr,"shouldIgnoreWatchPath");function Jt(e){if(X!==e){Ie(),X=e,_e=Xt(e);try{console.log(`[watcher] start watching path=${e}`),ue=watch(e,{recursive:!0},(t,r)=>{let n=typeof r=="string"&&r.length>0?r:"<unknown>";Tr(e,n,_e)||(Vt.shouldLog()&&console.log(`[watcher] fs event path=${e} event=${t} file=${n}`),me&&clearTimeout(me),me=setTimeout(()=>{console.log(`[watcher] broadcast files-changed path=${e}`),M("files-changed");},br));}),ue.on("error",t=>{let r=t instanceof Error?t.message:String(t);console.error(`[watcher] error path=${e} message=${r}`),Ie();});}catch{console.error(`[watcher] failed to watch path=${e}`),X=null;}}}i(Jt,"watchWorkspace");function Ie(){me&&clearTimeout(me),ue&&(X&&console.log(`[watcher] stop watching path=${X}`),ue.close(),ue=null),Vt=Yt(5e3),_e=null,X=null;}i(Ie,"stopWatching");var Ee=Router();Ee.get("/settings",(e,t)=>{t.json(S());});Ee.put("/settings",(e,t)=>{let r=e.body||{},n=C(r);n.activePath?Jt(n.activePath):Ie(),t.json(n);});function P(){let e=S(),t=new Set([homedir()]);for(let r of e.trackedDirs)t.add(r);for(let r of T()){t.add(r.path);try{for(let n of v(r.path,r.id))t.add(n.path);}catch{}}return [...t]}i(P,"allowedRoots");var G=Router(),Er={".png":"image/png",".jpg":"image/jpeg",".jpeg":"image/jpeg",".gif":"image/gif",".webp":"image/webp",".svg":"image/svg+xml",".ico":"image/x-icon",".bmp":"image/bmp"};G.get("/fs",async(e,t)=>{let r=e.query.path||"",n=e.query.mode||"list",o=I(r,P());if(!o){t.status(403).json({error:"Path outside allowed roots"});return}try{if(n==="read"){let c=await stat(o),l=x.extname(o).toLowerCase(),d=Er[l];if(d){if(c.size>10*1024*1024){t.status(413).json({error:"Image too large (>10MB)"});return}let m=await readFile(o),w=`data:${d};base64,${m.toString("base64")}`;t.json({path:r,content:w,language:"image",size:c.size});return}if(c.size>1024*1024){t.status(413).json({error:"File too large (>1MB)"});return}let p=await readFile(o,"utf-8"),u=Pe(l,x.basename(o).toLowerCase());t.json({path:r,content:p,language:u,size:c.size});return}let a=(await readdir(o,{withFileTypes:!0})).map(c=>({name:c.name,type:c.isDirectory()?"directory":"file"}));a.sort((c,l)=>c.type!==l.type?c.type==="directory"?-1:1:c.name.localeCompare(l.name)),t.json({path:r,entries:a});}catch{t.status(404).json({error:"Not found"});}});G.put("/fs",async(e,t)=>{let{path:r,content:n}=e.body||{};if(!r||n===void 0){t.status(400).json({error:"path and content required"});return}let o=I(r,P());if(!o){t.status(403).json({error:"Path outside allowed roots"});return}try{await writeFile(o,n,"utf-8"),t.json({saved:!0,path:r});}catch(s){t.status(500).json({error:s instanceof Error?s.message:String(s)});}});G.get("/browse",async(e,t)=>{let r=e.query.path||homedir(),n=I(r,P());if(!n){t.status(403).json({error:"Path outside allowed roots"});return}if(!existsSync(n)){t.status(404).json({error:"Path not found"});return}try{let o=await readdir(n,{withFileTypes:!0}),s=(await Promise.all(o.map(async c=>{if(c.name.startsWith("."))return null;if(c.isDirectory())return c.name;if(!c.isSymbolicLink())return null;try{return (await stat(x.join(n,c.name))).isDirectory()?c.name:null}catch{return null}}))).filter(c=>!!c).sort(),a=existsSync(x.join(n,".git"))||existsSync(x.join(n,"HEAD"));t.json({path:n,parent:x.dirname(n),dirs:s,isGitRepo:a});}catch{t.status(403).json({error:"Cannot read directory"});}});G.get("/git/status",(e,t)=>{let r=e.query.cwd||"",n=I(r,P());if(!n){t.status(403).json({error:"Path outside allowed roots"});return}try{let o=execFileSync("git",["status","--porcelain","-uall"],{cwd:n,encoding:"utf-8"}).replace(/\n$/,""),s={};if(o)for(let a of o.split(`
16
+ `)){let c=a.substring(0,2),l=a.substring(3);s[l]=c;}t.json(s);}catch{t.json({});}});G.get("/git/show",(e,t)=>{let r=e.query.cwd||"",n=e.query.path||"",o=I(r,P());if(!o){t.status(403).json({error:"Path outside allowed roots"});return}if(!n){t.status(400).json({error:"path is required"});return}try{let s=execFileSync("git",["show",`HEAD:${n}`],{cwd:o,encoding:"utf-8"});t.json({content:s});}catch{t.json({content:""});}});var Me=Router(),Or=1024*1024,Dr=25*1024*1024;Me.get("/preview/markdown",async(e,t)=>{let r=e.query.path||"",n=I(r,P());if(!n){t.status(403).type("text/plain").send("Path outside allowed roots");return}try{if((await stat(n)).size>Or){t.status(413).type("text/plain").send("Markdown file too large (>1MB)");return}let s=await readFile(n,"utf-8"),a=marked.parse(s,{async:!1,gfm:!0,breaks:!1});t.type("text/html; charset=utf-8").send(Nr(x.basename(n),a));}catch{t.status(404).type("text/plain").send("Not found");}});Me.get(/^\/preview\/raw\/(.+)$/,async(e,t)=>{let r=e.params[0]??"";if(!r){t.status(400).type("text/plain").send("Missing path");return}let n="/"+r,o=I(n,P());if(!o){t.status(403).type("text/plain").send("Path outside allowed roots");return}try{let s=await stat(o);if(!s.isFile()){t.status(404).type("text/plain").send("Not a file");return}if(s.size>Dr){t.status(413).type("text/plain").send("File too large (>25MB)");return}t.sendFile(o,{dotfiles:"allow"},a=>{a&&!t.headersSent&&t.status(500).type("text/plain").send("Send failed");});}catch{t.status(404).type("text/plain").send("Not found");}});function Nr(e,t){return `<!DOCTYPE html>
17
+ <html lang="en">
18
+ <head>
19
+ <meta charset="utf-8">
20
+ <meta name="viewport" content="width=device-width, initial-scale=1">
21
+ <title>${Cr(e)}</title>
22
+ <style>
23
+ :root {
24
+ color-scheme: light dark;
25
+ --md-fg: #1f2328;
26
+ --md-bg: #ffffff;
27
+ --md-muted: #59636e;
28
+ --md-border: #d1d9e0;
29
+ --md-code-bg: #f6f8fa;
30
+ --md-link: #0969da;
31
+ --md-quote: #59636e;
32
+ }
33
+ @media (prefers-color-scheme: dark) {
34
+ :root {
35
+ --md-fg: #e6edf3;
36
+ --md-bg: #0d1117;
37
+ --md-muted: #9198a1;
38
+ --md-border: #30363d;
39
+ --md-code-bg: #151b23;
40
+ --md-link: #4493f8;
41
+ --md-quote: #9198a1;
42
+ }
43
+ }
44
+ html, body { background: var(--md-bg); color: var(--md-fg); }
45
+ body {
46
+ margin: 0;
47
+ padding: 32px 48px;
48
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Helvetica Neue", Helvetica, Arial, sans-serif;
49
+ font-size: 16px;
50
+ line-height: 1.6;
51
+ max-width: 980px;
52
+ box-sizing: border-box;
53
+ }
54
+ @media (max-width: 720px) { body { padding: 16px; } }
55
+ h1, h2, h3, h4, h5, h6 { font-weight: 600; line-height: 1.25; margin-top: 1.6em; margin-bottom: 0.6em; }
56
+ h1 { font-size: 2em; padding-bottom: 0.3em; border-bottom: 1px solid var(--md-border); }
57
+ h2 { font-size: 1.5em; padding-bottom: 0.3em; border-bottom: 1px solid var(--md-border); }
58
+ h3 { font-size: 1.25em; }
59
+ h4 { font-size: 1em; }
60
+ p, ul, ol, blockquote, pre, table { margin: 0 0 1em; }
61
+ a { color: var(--md-link); text-decoration: none; }
62
+ a:hover { text-decoration: underline; }
63
+ code, pre, kbd { font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace; }
64
+ code { padding: 0.2em 0.4em; background: var(--md-code-bg); border-radius: 4px; font-size: 85%; }
65
+ pre { padding: 14px; overflow: auto; background: var(--md-code-bg); border-radius: 6px; }
66
+ pre code { padding: 0; background: transparent; font-size: 90%; }
67
+ blockquote {
68
+ margin: 0 0 1em; padding: 0 1em;
69
+ color: var(--md-quote); border-left: 4px solid var(--md-border);
70
+ }
71
+ table { border-collapse: collapse; }
72
+ th, td { padding: 6px 13px; border: 1px solid var(--md-border); }
73
+ tr:nth-child(2n) { background: var(--md-code-bg); }
74
+ hr { height: 1px; background: var(--md-border); border: 0; margin: 24px 0; }
75
+ img { max-width: 100%; }
76
+ ul, ol { padding-left: 2em; }
77
+ li + li { margin-top: 0.25em; }
78
+ </style>
79
+ </head>
80
+ <body>
81
+ ${t}
82
+ </body>
83
+ </html>`}i(Nr,"renderShell");function Cr(e){return e.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;").replace(/'/g,"&#39;")}i(Cr,"escapeHtml");function Fr(e){return e.flatMap(t=>t.manifest?[t.manifest]:[])}i(Fr,"buildManifest");function nn(e){let t=new Set;for(let r of e){if(!(!!r.manifest||r.render?.type==="component"))throw new Error(`[omniterm] plugin "${r.type}" provides neither a \`manifest\` (iframe) nor a \`component\` render; the host client cannot render it. Add a manifest (PluginManifestEntry) to make it a runtime-loadable iframe plugin.`);if(r.proxyPrefix){if(t.has(r.proxyPrefix))throw new Error(`[omniterm] plugin "${r.type}" shares proxyPrefix "${r.proxyPrefix}" with an earlier plugin; their routes would silently shadow. Use distinct prefixes.`);t.add(r.proxyPrefix);}}}i(nn,"validatePluginManifests");function rn(e){let t=Router(),r=Fr(e);return t.get("/plugins",(n,o)=>{o.setHeader("Cache-Control","no-store"),o.json({plugins:r});}),t}i(rn,"createManifestRouter");var et=promisify(execFile),tt=5e3,z=Router();z.get("/sessions/:sessionId",(e,t)=>{let r=H(e.params.sessionId);if(!r){t.status(404).json({error:"Session not found"});return}t.json({id:r.id,worktreeId:r.worktreeId,port:r.port,url:`/t/${r.id}/`,createdAt:r.createdAt});});z.delete("/sessions/:sessionId",(e,t)=>{if(!H(e.params.sessionId)){t.status(404).json({error:"Session not found"});return}ce(e.params.sessionId),t.json({deleted:true});});z.post("/create-session",async(e,t)=>{let r=e.body?.cwd==="~"?homedir():e.body?.cwd;if(!r){t.status(400).json({error:"cwd is required"});return}let n=r.split("/").pop()||"session",o=Date.now().toString(36),s=`${n}-${o}`,a=typeof e.body?.ownerId=="string"&&e.body.ownerId?e.body.ownerId:s,c;try{let l=Date.now(),p=`http://${e.headers.host??"127.0.0.1:17716"}/t/${a}/registry`;we(s,r,Ye(p),{unsetEnv:Xe});let u=Date.now();c=ke(s,{skipTmuxSetup:!0,knownCwd:r});let m=Date.now();await N(c);let w=Date.now(),j=u-l,A=m-u,g=w-m,y=w-l;console.log(`[session] create-session: tmux=${j}ms adopt=${A}ms ttyd_ready=${g}ms total=${y}ms`),t.status(201).json({id:c.id,worktreeId:c.worktreeId,port:c.port,url:`/t/${c.id}/`,createdAt:c.createdAt});}catch(l){c&&ce(c.id),t.status(500).json({error:l instanceof Error?l.message:String(l)});}});z.get("/dir-sessions",async(e,t)=>{try{let r=e.query.cwd||"~",n=r==="~"?homedir():r,o;try{let{stdout:p}=await et("tmux",["list-sessions","-F","#{session_name}|||#{session_path}"],{encoding:"utf-8",timeout:tt});o=p.trim();}catch{t.json([]);return}if(!o){t.json([]);return}let s=[...await an(),...S().trackedDirs],a=i(p=>{if(!(p===n||p.startsWith(n+"/")))return !1;for(let u of s)if(u!==n&&u.length>n.length&&(p===u||p.startsWith(u+"/")))return !1;return !0},"isMostSpecificMatch"),c=o.split(`
84
+ `).map(p=>{let[u,m]=p.split("|||");return {name:u,path:m}}).filter(p=>a(p.path)),l=new Map(B().map(p=>[p.tmuxName,p])),d=await Promise.all(c.map(async p=>{let u=l.get(p.name);u||(u=ke(p.name));try{await N(u);}catch(m){Te(u.id);let w=m instanceof Error?m.message:String(m);return console.error(`[session] failed to ready adopted session ${p.name}: ${w}`),null}return {id:u.id,worktreeId:u.worktreeId,port:u.port,url:`/t/${u.id}/`,createdAt:u.createdAt,command:await Hr(u.tmuxName)}}));t.json(d.filter(p=>p!==null));}catch(r){t.status(500).json({error:r instanceof Error?r.message:String(r)});}});z.post("/adopt-session/:sessionName",async(e,t)=>{let{sessionName:r}=e.params,n=H(r);if(n){try{await N(n);}catch(s){Te(n.id),t.status(503).json({error:s instanceof Error?s.message:String(s)});return}t.json({id:n.id,worktreeId:n.worktreeId,port:n.port,url:`/t/${n.id}/`});return}let o;try{o=ke(r),await N(o),t.status(201).json({id:o.id,worktreeId:o.worktreeId,port:o.port,url:`/t/${o.id}/`});}catch(s){o&&Te(o.id),t.status(503).json({error:s instanceof Error?s.message:String(s)});}});var on=new Set,sn=0;async function an(){if(Date.now()-sn<3e4)return on;let e=new Set,t=[];for(let{path:n}of T())e.add(n),t.push(n);let r=await Promise.all(t.map(n=>zr(n)));for(let n of r)for(let o of n)e.add(o);return on=e,sn=Date.now(),e}i(an,"getRepoWorktreePaths");async function zr(e){try{let{stdout:t}=await et("git",["worktree","list","--porcelain"],{cwd:e,encoding:"utf-8",timeout:tt}),r=[];for(let n of t.split(`
85
+ `))if(n.startsWith("worktree ")){let o=n.substring(9);o&&!r.includes(o)&&r.push(o);}return r}catch(t){return console.error(`git worktree list failed for ${e}:`,t),[]}}i(zr,"listWorktreePathsAsync");async function Hr(e){try{let{stdout:t}=await et("tmux",["display-message","-t",e,"-p","#{pane_title}|||#{pane_current_command}"],{encoding:"utf-8",timeout:tt}),[r="",n=""]=t.trim().split("|||"),o=r.trim();return o&&o!==e&&!o.match(/^[a-zA-Z0-9-]+$/)?o:n.trim()}catch{return ""}}i(Hr,"getTmuxPaneTitleAsync");z.get("/discover-sessions",async(e,t)=>{try{let r=Se(),n=new Set(B().map(d=>d.tmuxName)),o=await an(),s=r.filter(d=>!n.has(d.name)&&!o.has(d.cwd)),a=homedir(),c={[a]:[]},{trackedDirs:l}=S();for(let d of l)!o.has(d)&&d!==a&&(c[d]=[]);for(let d of s)c[d.cwd]||(c[d.cwd]=[]),c[d.cwd].push({name:d.name,created:d.created});t.json(c);}catch(r){t.status(500).json({error:r instanceof Error?r.message:String(r)});}});var F=new Map;function V(e){return Array.isArray(e)?e[0]??"":e??""}i(V,"paramValue");function cn(e){let t=F.get(e);return t||(t={entries:new Map,nextId:1,emitter:new EventEmitter},F.set(e,t)),t}i(cn,"getOrCreate");function Vr(e){let t=e.match(/^(ws:\/\/[^/]+)\/devtools\/browser\/[^/]+$/);if(t)return `${t[1]}/devtools/page/{targetId}`;try{return `ws://${new URL(e).host}/devtools/page/{targetId}`}catch{return `${e.replace(/\/?$/,"")}/devtools/page/{targetId}`}}i(Vr,"derivePageCdpUrlTemplate");function nt(e,t){return {id:e.id,label:e.label,startedAt:e.startedAt,browserCdpUrl:e.cdpUrl,pageCdpUrlTemplate:Vr(e.cdpUrl),devtoolsFrontendUrl:t}}i(nt,"toView");function $e(e){try{return `http://${new URL(e.cdpUrl).host}/devtools/`}catch{return ""}}i($e,"defaultDevtoolsFrontendUrl");function Yr(e){try{return process.kill(e,0),!0}catch{return false}}i(Yr,"isAlive");var Xr=5e3,Jr=setInterval(()=>{for(let[,e]of F)for(let[t,r]of e.entries)r.pid!==void 0&&!Yr(r.pid)&&(e.entries.delete(t),e.emitter.emit("removed",t));},Xr);Jr.unref();function rt(e){let t=F.get(e);t&&(t.emitter.removeAllListeners(),F.delete(e));}i(rt,"cleanupTab");function ot(e){let t=Router({mergeParams:true}),r=ln.createProxyServer({changeOrigin:false,ws:true});return r.on("error",n=>console.error("[tab-registry proxy] error:",n.message)),t.post("/registry/browsers",(n,o)=>{let s=V(n.params.tabId),{cdpUrl:a,label:c,pid:l}=n.body??{};if(typeof a!="string"||!a){o.status(400).json({error:"cdpUrl is required"});return}let d=cn(s);for(let[m,w]of d.entries)if(w.cdpUrl===a){o.json({id:m,entry:w,deduped:true});return}let p=String(d.nextId++),u={id:p,cdpUrl:a,label:typeof c=="string"&&c?c:"browser",pid:typeof l=="number"?l:void 0,startedAt:Date.now()};d.entries.set(p,u),d.emitter.emit("added",u),o.json({id:p,entry:u});}),t.delete("/registry/browsers/:browserId",(n,o)=>{let s=V(n.params.tabId),a=V(n.params.browserId),c=F.get(s),l=c?c.entries.delete(a):false;l&&c.emitter.emit("removed",a),o.status(l?200:404).json({removed:l});}),t.get("/browsers",(n,o)=>{let s=V(n.params.tabId);o.setHeader("Cache-Control","no-store");let a=Array.from(F.get(s)?.entries.values()??[]);o.json({browsers:a.map(c=>nt(c,e.devtoolsFrontendUrl??$e(c)))});}),t.get("/events",(n,o)=>{let s=V(n.params.tabId),a=cn(s);o.setHeader("Content-Type","text/event-stream"),o.setHeader("Cache-Control","no-store"),o.setHeader("Connection","keep-alive"),o.flushHeaders?.();let c=i(u=>{o.write(`data: ${JSON.stringify(u)}
86
+
87
+ `);},"write");for(let u of a.entries.values())c({type:"added",data:nt(u,e.devtoolsFrontendUrl??$e(u))});let l=i(u=>c({type:"added",data:nt(u,e.devtoolsFrontendUrl??$e(u))}),"onAdded"),d=i(u=>c({type:"removed",data:{id:u}}),"onRemoved");a.emitter.on("added",l),a.emitter.on("removed",d);let p=setInterval(()=>{o.write(`: hb
88
+
89
+ `);},25e3);n.on("close",()=>{a.emitter.off("added",l),a.emitter.off("removed",d),clearInterval(p);});}),t.all("/b/:browserId/devtools/{*rest}",(n,o)=>{let s=V(n.params.tabId),a=V(n.params.browserId),c=F.get(s)?.entries.get(a);if(!c){o.status(404).send("Browser not found");return}let l=e.devtoolsFrontendUrl??$e(c),d;try{d=new URL(l);}catch{o.status(502).send("Invalid devtoolsFrontendUrl");return}let p=n.originalUrl??n.url??"",u=p.indexOf("/devtools/"),m=u>=0?p.slice(u+10):"",w=d.pathname.endsWith("/")?d.pathname:`${d.pathname}/`;n.url=`${w}${m}`,r.web(n,o,{target:`${d.protocol}//${d.host}`,ignorePath:false});}),t}i(ot,"createTabRegistryRouter");function st(e,t,r,n){let o=e.url??"",s=n.basePath.replace(/[.+?^${}()|[\]\\]/g,"\\$&"),a=new RegExp(`^${s}/([^/]+)/b/([^/]+)/browser(?:$|\\?)`),c=new RegExp(`^${s}/([^/]+)/b/([^/]+)/page/([^/?]+)(?:$|\\?)`),l=o.match(a),d=o.match(c),p=l??d;if(!p)return false;let u=p[1],m=p[2];if(!u||!m)return t.destroy(),true;let w=F.get(u)?.entries.get(m);if(!w)return t.destroy(),true;let j;try{j=new URL(w.cdpUrl);}catch{return t.destroy(),true}let A=d?j.pathname.replace(/\/devtools\/browser\/[^/]+$/,`/devtools/page/${d[3]}`):j.pathname;e.headers.origin="http://127.0.0.1",e.url=A+(j.search||"");let g=ln.createProxyServer({changeOrigin:false,ws:true});return g.on("error",()=>{try{t.destroy();}catch{}}),g.ws(e,t,r,{target:`ws://${j.host}`}),true}i(st,"handleCdpUpgrade");function it(e={}){return {type:"terminal",label:"Terminal",proxyPrefix:"/t/:tabId",createRouter(t){return ot({devtoolsFrontendUrl:e.devtoolsFrontendUrl})},handleUpgrade(t,r,n){return st(t,r,n,{basePath:"/t"})},async spawn(t){throw new Error("Terminal spawn not yet routed through the plugin \u2014 page.tsx currently calls /api/create-session directly. Commit C will migrate.")},cleanupTab(t){rt(t);},render:{type:"component",componentPath:"./components/TerminalTabView"}}}i(it,"createTerminalPlugin");var pn=3e3;async function to(e,t,r){let n=e.params.sessionId,o=Array.isArray(n)?n[0]:n,s=o?r.getSession(o):void 0;if(!s){t.status(404).send("Session not found");return}try{await r.ensureSessionTtydReady(s);}catch(a){r.onReadyError?.(s,a),t.status(503).send("Terminal proxy not ready");return}r.proxyWeb(e,t,`http://127.0.0.1:${s.port}`);}i(to,"handleTtydHttpProxy");function no(e,t,r,n){let s=(e.url||"").match(/^\/t\/([^/]+)\//);if(!s)return false;let a=n.getSession(s[1]);return a?(t.pause(),n.ensureSessionTtydReady(a).then(()=>{t.destroyed||(t.resume(),n.proxyWs(e,t,r,`http://127.0.0.1:${a.port}`));},c=>{n.onReadyError?.(a,c),t.destroy();}),true):(t.destroy(),true)}i(no,"handleTtydUpgrade");function at(e={}){let t=parseInt(process.env.OMNITERM_PORT??String(e.port??17717),10),r=process.env.OMNITERM_HOST??e.host??"0.0.0.0";for(let g of ["OMNITERM_PORT","OMNITERM_HOST","OMNITERM_TTYD_PORT_MIN","OMNITERM_TTYD_PORT_MAX","OMNITERM_BROWSER_DISCOVERY_URL"])delete process.env[g];let n=ge();n.disable("x-powered-by"),n.use(ge.json()),e.statusHandler&&n.get("/status",e.statusHandler),e.configureApp?.(n),n.use("/api",q),n.use("/api",z),n.use("/api",L),n.use("/api",Ee),n.use("/api",G),n.use("/api",Me);let o;e.devtoolsBundleDir&&(n.use("/devtools",ge.static(e.devtoolsBundleDir,{fallthrough:false})),o=`http://127.0.0.1:${t}/devtools/`,console.error(`[devtools] Serving bundle from ${e.devtoolsBundleDir} at /devtools/`));let s=[...e.excludeDefaults?[]:[it({devtoolsFrontendUrl:o})],...e.plugins??[]];nn(s);let a={broadcast:i((g,y)=>M(g,y),"broadcast"),workspaceRoot:i(()=>null,"workspaceRoot"),allowedRoots:P,confinePath:i((g,y)=>I(g,y??P()),"confinePath"),settings:i(()=>S(),"settings"),repos:i(()=>T(),"repos"),worktrees:i((g,y)=>v(g,y),"worktrees")};for(let g of s)g.proxyPrefix?n.use(g.proxyPrefix,g.createRouter(a)):n.use(g.createRouter(a));n.use("/api",rn(s)),n.get("/api/metrics/perf",(g,y)=>{y.json(_t());}),n.get("/api/events",(g,y)=>{y.setHeader("Content-Type","text/event-stream"),y.setHeader("Cache-Control","no-cache"),y.setHeader("Connection","keep-alive"),y.flushHeaders(),St(y);});let c=ln.createProxyServer({ws:true});c.on("error",g=>{console.error("[proxy] error:",g.message);}),n.all("/t/:sessionId/{*rest}",async(g,y)=>{await to(g,y,{getSession:H,ensureSessionTtydReady:i(O=>N(O,pn),"ensureSessionTtydReady"),proxyWeb:i((O,Y,_)=>{c.web(O,Y,{target:_});},"proxyWeb"),onReadyError:i((O,Y)=>{let _=Y instanceof Error?Y.message:String(Y);console.error(`[proxy] ttyd not ready for ${O.id}: ${_}`);},"onReadyError")});});let l=x.dirname(fileURLToPath(import.meta.url)),d=x.resolve(l,"..","dist","client"),p=x.resolve(l,"..","client"),u=e.clientDir??(existsSync(x.join(d,"index.html"))?d:p),m=x.join(u,"index.html"),w=x.resolve(l,"..","public");if(!existsSync(m)){let g=[`[omniterm] missing client bundle at ${u}`,"Reinstall the published package with `npm install -g omniterm@latest`.","When developing from the monorepo, run `pnpm --filter omniterm build` before starting the server."].join(`
90
+ `);throw new Error(g)}let j=readFileSync(m,"utf-8");n.use(ge.static(u)),n.use(ge.static(w)),n.get("/{*rest}",(g,y)=>{y.type("html").send(j);});let A=Kr.createServer(n);return A.on("upgrade",(g,y,O)=>{g.url||"";for(let _ of s)if(_.handleUpgrade?.(g,y,O))return;no(g,y,O,{getSession:H,ensureSessionTtydReady:i(_=>N(_,pn),"ensureSessionTtydReady"),proxyWs:i((_,Z,Oe,hn)=>{c.ws(_,Z,Oe,{target:hn});},"proxyWs"),onReadyError:i((_,Z)=>{let Oe=Z instanceof Error?Z.message:String(Z);console.error(`[proxy] ttyd websocket not ready for ${_.id}: ${Oe}`);},"onReadyError")})||y.destroy();}),A.on("error",g=>{g.code==="EADDRINUSE"&&(console.error(`[omniterm] Port ${t} is already in use.`),process.exit(1)),console.error("[omniterm] server error:",g);}),process.on("uncaughtException",g=>{console.error("[omniterm] uncaughtException:",g);}),process.on("unhandledRejection",g=>{console.error("[omniterm] unhandledRejection:",g);}),new Promise((g,y)=>{A.listen(t,r,()=>{console.log(`[omniterm] Listening on http://${r}:${t}`);try{B().length;}catch{}g({shutdown:i(async()=>{await new Promise(O=>A.close(()=>O())),await Pt().catch(()=>{});},"shutdown")});}),A.once("error",y);})}i(at,"startServer");var gn=createRequire(import.meta.url),fn;try{fn=x.join(x.dirname(gn.resolve("@shiplightai/devtools-assets/package.json")),"dist");}catch{console.error("[omniterm] Required dependency @shiplightai/devtools-assets is not installed. Reinstall @omniterm/host (it pulls the assets package as a dependency)."),process.exit(1);}function oo(e){let t=[];for(let r=0;r<e.length;r++){if(e[r]!=="--plugin")continue;let n=e[r+1];(!n||n.startsWith("--"))&&(console.error("[omniterm] --plugin requires a value (a filesystem path or package name)"),process.exit(1)),t.push(n),r++;}return t}i(oo,"parsePluginSpecs");async function so(e){let t=e.startsWith(".")||e.startsWith("/")||e.startsWith("file:"),r;if(t){let n=e.startsWith("file:")?fileURLToPath(e):x.resolve(process.cwd(),e);r=pathToFileURL(n).href;}else {let n;try{n=createRequire(x.join(process.cwd(),"noop.js")).resolve(e);}catch{n=gn.resolve(e);}r=pathToFileURL(n).href;}return import(r)}i(so,"importPluginModule");function je(e,t){console.error(`[omniterm] --plugin ${e}: ${t}`),process.exit(1);}i(je,"fail");async function io(e){let t=[],r=new Set;for(let n of e){let o;try{o=await so(n);}catch(l){je(n,`could not resolve/import (${l instanceof Error?l.message:String(l)})`);}let s=o.default??o.plugin,a;try{a=typeof s=="function"?s():s;}catch(l){je(n,`factory threw (${l instanceof Error?l.message:String(l)})`);}let c=a;(!c||typeof c.type!="string"||typeof c.createRouter!="function")&&je(n,"module must export a TabTypePlugin (default export or no-arg factory)"),r.has(c.type)&&je(n,`duplicate tab type "${c.type}"`),r.add(c.type),t.push(c);}return t}i(io,"loadPlugins");var ao=Number(process.env.OMNITERM_PORT??process.env.PORT)||17717,co=await io(oo(process.argv.slice(2)));await at({port:ao,devtoolsBundleDir:fn,plugins:co});