@omniterm/host
Advanced tools
+10
-2
@@ -44,2 +44,6 @@ #!/usr/bin/env node | ||
| --plugin <path|name> Load a plugin by path or package name (repeatable) | ||
| --env-passthrough <names> | ||
| Comma-separated variable NAMES that may cross into | ||
| terminals, on top of the default allowlist (repeatable; | ||
| env: OMNITERM_ENV_PASSTHROUGH, which overrides) | ||
| --no-telemetry Disable telemetry for this run only | ||
@@ -199,5 +203,9 @@ --version, -v Print version and exit | ||
| const pluginArgs = collectFlag('--plugin'); | ||
| // Flags forwarded VERBATIM to the server entry, which parses them. Everything | ||
| // else the launcher understands is translated into an env var below instead. | ||
| // Anything not listed here never reaches the server: the argv it sees is only | ||
| // what this array passes on. | ||
| const forwardedArgs = [...collectFlag('--plugin'), ...collectFlag('--env-passthrough')]; | ||
| const child = spawn(process.execPath, [serverEntry, ...pluginArgs], { | ||
| const child = spawn(process.execPath, [serverEntry, ...forwardedArgs], { | ||
| env: { | ||
@@ -204,0 +212,0 @@ ...process.env, |
+1
-1
| { | ||
| "name": "@omniterm/host", | ||
| "version": "0.2.26", | ||
| "version": "0.2.27", | ||
| "description": "omniterm — a generic, browser-based dev host: persistent terminals, a browser-view panel, workspace management, and a runtime plugin API. CLI: omniterm.", | ||
@@ -5,0 +5,0 @@ "license": "MIT", |
+40
-0
@@ -153,2 +153,42 @@ # omniterm | ||
| ## Pass environment variables to terminals | ||
| Every terminal omniterm opens starts from a clean environment: omniterm keeps a | ||
| short list of variables (`TERM`, `HOME`, `PATH`, and friends), drops everything | ||
| else, and then runs your login shell so your own profile builds the rest. That | ||
| stops the environment of whatever shell you launched omniterm from leaking into | ||
| every terminal. | ||
| If omniterm runs inside an environment you configured on purpose — a container, | ||
| a remote box, a CI shell — name the variables you want terminals to keep: | ||
| ```bash | ||
| MY_TOOL_TOKEN=… omniterm --env-passthrough MY_TOOL_TOKEN,MY_TOOL_URL | ||
| ``` | ||
| You pass names, never values. omniterm reads the values from its own | ||
| environment, so they never appear in a config file, a log line, or an API | ||
| response. `OMNITERM_ENV_PASSTHROUGH=MY_TOOL_TOKEN,MY_TOOL_URL` does the same | ||
| thing, and takes precedence when both are given. `GET /api/session-env` reports | ||
| the names in effect, so you can check the setting without printing a secret. | ||
| One limit worth knowing: omniterm can only pass on the value that existed when | ||
| its terminal backend started, and that backend keeps running even if omniterm | ||
| itself restarts. If your value changes while omniterm is running — a token that | ||
| rotates, for example — set it from your login profile instead, which every | ||
| terminal re-reads as it opens. | ||
| A program driving the HTTP API can also set variables on a single terminal: | ||
| ```json | ||
| POST /api/create-session | ||
| { "cwd": "/work", "name": "task-42", "env": { "TASK_ID": "42" } } | ||
| ``` | ||
| Those apply to that terminal, to the command it starts with, to the shell it | ||
| returns to when that command exits, and to any pane you split from it — and to | ||
| nothing else. These values are passed to the terminal backend as command | ||
| arguments, so other users on the same machine can see them with `ps`. Use them | ||
| for configuration, not for secrets. | ||
| ## Add plugins | ||
@@ -155,0 +195,0 @@ |
@@ -1,19 +0,19 @@ | ||
| import {createRequire}from'module';import ne,{Router}from'express';import hs from'http';import or from'http-proxy';import x from'path';import {existsSync,readFileSync,readdirSync,copyFileSync,statSync,mkdirSync,writeFileSync,watch,realpathSync,unlinkSync,renameSync}from'fs';import {fileURLToPath,pathToFileURL}from'url';import {execFile,execFileSync,spawn}from'child_process';import po,{homedir}from'os';import {createHash,randomUUID}from'crypto';import {promisify}from'util';import*as bn from'net';import {PostHog}from'posthog-node';import {readdir,stat,readFile,writeFile}from'fs/promises';import No from'ignore';import {marked}from'marked';import {EventEmitter}from'events';createRequire(import.meta.url); | ||
| var mr=Object.defineProperty;var s=(e,t)=>mr(e,"name",{value:t,configurable:true});var Ve=process.env.SETTINGS_DIR||x.join(homedir(),".omniterm"),oe=x.join(Ve,"settings.json");function hr(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}s(hr,"findOmnitermPackageRoot");var Ot=hr(),ue=Ot?x.join(Ot,"bin"):null;function qe(e,t){let r=x.resolve(e,t),n=x.normalize(r);if(!n.startsWith(e))throw new Error("Path traversal detected");return n}s(qe,"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 i=x.normalize(x.resolve(o));if(n===i||n.startsWith(i+x.sep))return n}return null}s(I,"confinePath");var wr=/[^A-Za-z0-9_-]/g,br=8,_e=new Map;function C(e){let t=x.resolve(e),r=x.basename(t).replace(/\.git$/,"");return r||x.basename(x.dirname(t))||"repo"}s(C,"displayNameForPath");function jt(e,t,r){let n=t.replace(wr,"-"),o=createHash("sha256").update(r).digest("hex").slice(0,br);return `${e}${n}-${o}`}s(jt,"buildId");function G(e){let t=`repo ${e}`,r=_e.get(t);if(r!==void 0)return r;let n=x.resolve(e),o=jt("",C(n),n);return _e.set(t,o),o}s(G,"repoIdForPath");function Ye(e){let t=`wt ${e}`,r=_e.get(t);if(r!==void 0)return r;let n=x.resolve(e),o,i;try{o=realpathSync(n),i=!0;}catch{o=n,i=false;}let a=jt("wt-",C(o),o);return i&&_e.set(t,a),a}s(Ye,"worktreeIdForPath");var Nt={trackedRepos:[],trackedDirs:[],lastActiveWorktree:null,sidebarCollapsed:false,namingSchemes:{},terminalFontSize:14,defaultShell:"bash",terminalTabs:{},activeSessionId:null,activePath:null,workspacesPanelMode:"docked",workspacesPanelDockedOpen:true,workspaceFilterActiveOnly:false,filesPanelMode:"docked",filesPanelDockedOpen:false,browserPanelMode:"docked",browserInspectorPosition:"hidden",browserPanelOpen:null,filePanelTabs:{},telemetryEnabled:true,terminalRenderer:"webgl"};function Ft(e){return Array.isArray(e)?e.filter(t=>typeof t=="string"):[]}s(Ft,"onlyStrings");function _r(e,t){let r={...e},n=new Set,o=false;for(let i of t){let a=G(i);if(r[a]!==void 0)continue;let c=C(i);if(n.has(c))continue;let l=e[c];l!==void 0&&(n.add(c),r[a]=l,delete r[c],o=true);}return o?r:e}s(_r,"migrateNamingSchemes");function S(){try{let e=readFileSync(oe,"utf-8"),t=JSON.parse(e);t.tabLayouts&&!t.terminalTabs&&(t.terminalTabs=t.tabLayouts),delete t.tabLayouts,delete t.browserPanel;let r={...Nt,...t};return r.trackedRepos=Ft(r.trackedRepos),r.trackedDirs=Ft(r.trackedDirs),r.namingSchemes&&typeof r.namingSchemes=="object"&&(r.namingSchemes=_r(r.namingSchemes,r.trackedRepos)),r}catch(e){existsSync(oe)&&console.error(`[settings] ${oe} exists but could not be loaded; falling back to defaults. The next save will overwrite it. Cause: ${e instanceof Error?e.message:String(e)}`);let t=homedir(),r="/workspace",n=existsSync(r)&&statSync(r).isDirectory();return {...Nt,trackedDirs:n?[r,t]:[t],activePath:n?r:t}}}s(S,"loadSettings");function L(e){let t=S(),r={...t,...e};return e.terminalTabs&&(r.terminalTabs={...t.terminalTabs,...e.terminalTabs}),e.filePanelTabs&&(r.filePanelTabs={...t.filePanelTabs,...e.filePanelTabs}),mkdirSync(x.dirname(oe),{recursive:true}),writeFileSync(oe,JSON.stringify(r,null,2),"utf-8"),r}s(L,"saveSettings");var Wt={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"]},Ke=Object.keys(Wt);function Er(e){let t=S();if(t.namingSchemes[e])return t.namingSchemes[e];let r=new Set(Object.values(t.namingSchemes)),n=Ke.find(o=>!r.has(o));return n||(n=Ke[Math.floor(Math.random()*Ke.length)]),L({namingSchemes:{...t.namingSchemes,[e]:n}}),n}s(Er,"assignScheme");function Lt(e,t){let r=Er(e),n=Wt[r],o=new Set(t),i=n.filter(l=>!o.has(l));if(i.length>0)return i[Math.floor(Math.random()*i.length)];let a=n[Math.floor(Math.random()*n.length)],c=Math.floor(Math.random()*900)+100;return `${a}-${c}`}s(Lt,"generateName");function ie(e,t){return execFileSync("git",e,{cwd:t,encoding:"utf-8"})}s(ie,"execGit");function Ar(e,t){let r;try{r=ie(["worktree","list","--porcelain"],e);}catch(a){return console.error("git worktree list failed:",a),[]}let n=[],o=r.split(` | ||
| import {createRequire}from'module';import ne,{Router}from'express';import Os from'http';import Sn from'http-proxy';import v from'path';import {existsSync,readFileSync,readdirSync,copyFileSync,statSync,mkdirSync,writeFileSync,watch,realpathSync,unlinkSync,renameSync}from'fs';import {fileURLToPath,pathToFileURL}from'url';import {execFile,execFileSync,spawn}from'child_process';import Eo,{homedir}from'os';import {createHash,randomUUID}from'crypto';import {promisify}from'util';import*as Er from'net';import {PostHog}from'posthog-node';import {readdir,stat,readFile,writeFile}from'fs/promises';import Qo from'ignore';import {marked}from'marked';import {EventEmitter}from'events';createRequire(import.meta.url); | ||
| var En=Object.defineProperty;var s=(e,t)=>En(e,"name",{value:t,configurable:true});var Je=process.env.SETTINGS_DIR||v.join(homedir(),".omniterm"),ie=v.join(Je,"settings.json");function $n(e){let t=e??v.dirname(fileURLToPath(import.meta.url)),r=v.parse(t).root,n=t;for(;n!==r;){let o=v.join(n,"package.json");if(existsSync(o))try{if(JSON.parse(readFileSync(o,"utf-8")).name==="@omniterm/core")return n}catch{}n=v.dirname(n);}for(n=t;n!==r;){if(existsSync(v.join(n,"package.json")))return n;n=v.dirname(n);}return null}s($n,"findOmnitermPackageRoot");var Lt=$n(),me=Lt?v.join(Lt,"bin"):null;function Ze(e,t){let r=v.resolve(e,t),n=v.normalize(r);if(!n.startsWith(e))throw new Error("Path traversal detected");return n}s(Ze,"resolveWorktreePath");function M(e,t){if(!e)return null;let r=e.startsWith("~")?e.replace("~",homedir()):e,n=v.normalize(v.resolve(r));for(let o of t){if(!o)continue;let i=v.normalize(v.resolve(o));if(n===i||n.startsWith(i+v.sep))return n}return null}s(M,"confinePath");var jn=/[^A-Za-z0-9_-]/g,Nn=8,Ie=new Map;function D(e){let t=v.resolve(e),r=v.basename(t).replace(/\.git$/,"");return r||v.basename(v.dirname(t))||"repo"}s(D,"displayNameForPath");function Ut(e,t,r){let n=t.replace(jn,"-"),o=createHash("sha256").update(r).digest("hex").slice(0,Nn);return `${e}${n}-${o}`}s(Ut,"buildId");function q(e){let t=`repo ${e}`,r=Ie.get(t);if(r!==void 0)return r;let n=v.resolve(e),o=Ut("",D(n),n);return Ie.set(t,o),o}s(q,"repoIdForPath");function Qe(e){let t=`wt ${e}`,r=Ie.get(t);if(r!==void 0)return r;let n=v.resolve(e),o,i;try{o=realpathSync(n),i=!0;}catch{o=n,i=false;}let a=Ut("wt-",D(o),o);return i&&Ie.set(t,a),a}s(Qe,"worktreeIdForPath");var Ht={trackedRepos:[],trackedDirs:[],lastActiveWorktree:null,sidebarCollapsed:false,namingSchemes:{},terminalFontSize:14,defaultShell:"bash",terminalTabs:{},activeSessionId:null,activePath:null,workspacesPanelMode:"docked",workspacesPanelDockedOpen:true,workspaceFilterActiveOnly:false,filesPanelMode:"docked",filesPanelDockedOpen:false,browserPanelMode:"docked",browserInspectorPosition:"hidden",browserPanelOpen:null,filePanelTabs:{},telemetryEnabled:true,terminalRenderer:"webgl"};function zt(e){return Array.isArray(e)?e.filter(t=>typeof t=="string"):[]}s(zt,"onlyStrings");function Bn(e,t){let r={...e},n=new Set,o=false;for(let i of t){let a=q(i);if(r[a]!==void 0)continue;let c=D(i);if(n.has(c))continue;let l=e[c];l!==void 0&&(n.add(c),r[a]=l,delete r[c],o=true);}return o?r:e}s(Bn,"migrateNamingSchemes");function S(){try{let e=readFileSync(ie,"utf-8"),t=JSON.parse(e);t.tabLayouts&&!t.terminalTabs&&(t.terminalTabs=t.tabLayouts),delete t.tabLayouts,delete t.browserPanel;let r={...Ht,...t};return r.trackedRepos=zt(r.trackedRepos),r.trackedDirs=zt(r.trackedDirs),r.namingSchemes&&typeof r.namingSchemes=="object"&&(r.namingSchemes=Bn(r.namingSchemes,r.trackedRepos)),r}catch(e){existsSync(ie)&&console.error(`[settings] ${ie} exists but could not be loaded; falling back to defaults. The next save will overwrite it. Cause: ${e instanceof Error?e.message:String(e)}`);let t=homedir(),r="/workspace",n=existsSync(r)&&statSync(r).isDirectory();return {...Ht,trackedDirs:n?[r,t]:[t],activePath:n?r:t}}}s(S,"loadSettings");function U(e){let t=S(),r={...t,...e};return e.terminalTabs&&(r.terminalTabs={...t.terminalTabs,...e.terminalTabs}),e.filePanelTabs&&(r.filePanelTabs={...t.filePanelTabs,...e.filePanelTabs}),mkdirSync(v.dirname(ie),{recursive:true}),writeFileSync(ie,JSON.stringify(r,null,2),"utf-8"),r}s(U,"saveSettings");var Vt={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"]},et=Object.keys(Vt);function Hn(e){let t=S();if(t.namingSchemes[e])return t.namingSchemes[e];let r=new Set(Object.values(t.namingSchemes)),n=et.find(o=>!r.has(o));return n||(n=et[Math.floor(Math.random()*et.length)]),U({namingSchemes:{...t.namingSchemes,[e]:n}}),n}s(Hn,"assignScheme");function Gt(e,t){let r=Hn(e),n=Vt[r],o=new Set(t),i=n.filter(l=>!o.has(l));if(i.length>0)return i[Math.floor(Math.random()*i.length)];let a=n[Math.floor(Math.random()*n.length)],c=Math.floor(Math.random()*900)+100;return `${a}-${c}`}s(Gt,"generateName");function ce(e,t){return execFileSync("git",e,{cwd:t,encoding:"utf-8"})}s(ce,"execGit");function qn(e,t){let r;try{r=ce(["worktree","list","--porcelain"],e);}catch(a){return console.error("git worktree list failed:",a),[]}let n=[],o=r.split(` | ||
| `).filter(a=>a.trim()),i=true;for(let a of o){let c=a.split(` | ||
| `),l="",u="",d=false;for(let h of c)h.startsWith("worktree ")?l=h.substring(9):h.startsWith("branch ")?u=h.substring(7).replace("refs/heads/",""):h==="bare"&&(d=true);if(!l||d){i=false;continue}let p=x.basename(l);n.push({id:Ye(l),repoId:t,branch:u||"HEAD",path:l,name:p,isMain:i}),i=false;}return n}s(Ar,"parseWorktreeList");function R(e,t){return Ar(e,t)}s(R,"listWorktrees");function Or(e){try{return ie(["for-each-ref","--format=%(refname:short)","refs/heads/"],e).trim().split(` | ||
| `).filter(Boolean)}catch{return []}}s(Or,"listBranches");function Ut(e,t,r,n=true){let o=R(e,t),i=o.map(h=>h.name),a=new Set(Or(e)),c=Lt(t,i),l=r||c;if(!r&&a.has(l))throw new Error(`Branch "${l}" already exists`);if(o.some(h=>h.branch===l))throw new Error(`Branch "${l}" already has a worktree`);let u=l.replace(/\//g,"-"),d=`${e}-${u}`;ie(n?["worktree","add","-b",l,d]:["worktree","add",d,l],e);try{for(let h of readdirSync(e,{withFileTypes:!0}))h.isFile()&&h.name.startsWith(".env")&©FileSync(x.join(e,h.name),x.join(d,h.name));}catch{}return {id:Ye(d),repoId:t,branch:l,path:d,name:x.basename(d),isMain:false}}s(Ut,"createWorktree");function Xe(e){try{return ie(["status","--porcelain"],e).trim().length>0}catch{return true}}s(Xe,"isWorktreeDirty");function Ie(e,t,{force:r=false}={}){ie(r?["worktree","remove","--force",t]:["worktree","remove",t],e);}s(Ie,"removeWorktree");function Bt(e,t,r){ie(["branch","-m",t,r],e);}s(Bt,"renameBranch");var Je=parseInt(process.env.OMNITERM_TTYD_PORT_MIN||"7700",10),Ze=parseInt(process.env.OMNITERM_TTYD_PORT_MAX||"7799",10),Ht=globalThis,Me=Ht.__omniterm_ports||new Set;Ht.__omniterm_ports=Me;function jr(){try{let e=execFileSync("lsof",[`-iTCP:${Je}-${Ze}`,"-sTCP:LISTEN","-P","-n","-Fn"],{encoding:"utf-8",stdio:["pipe","pipe","pipe"]}),t=new Set;for(let r of e.split(` | ||
| `)){if(!r.startsWith("n"))continue;let n=r.match(/:(\d+)$/);n&&t.add(parseInt(n[1],10));}return t}catch{return new Set}}s(jr,"getOccupiedPorts");function Qe(){let e=jr();for(let t=Je;t<=Ze;t++)if(!Me.has(t)&&!e.has(t))return Me.add(t),t;throw new Error(`No free ports available in range ${Je}-${Ze}`)}s(Qe,"allocatePort");function pe(e){Me.delete(e);}s(pe,"freePort");var zt=false;function et(){if(!zt){zt=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{}}}s(et,"ensureTmuxBindings");var Fr=["TERM","COLORTERM","TMUX","TMUX_PANE","HOME","USER","LOGNAME","SHELL","TMPDIR","TZ","LANG","LC_ALL","LC_COLLATE","LC_CTYPE","LC_MESSAGES","LC_MONETARY","LC_NUMERIC","LC_TIME","SSH_AUTH_SOCK","DISPLAY","WAYLAND_DISPLAY","XAUTHORITY","XDG_RUNTIME_DIR","DBUS_SESSION_BUS_ADDRESS","OMNITERM_BROWSER_REGISTRY_URL","BROWSER","PATH"],Gt=['shell="$1"; cmd="$2"',"set --",`for v in ${Fr.join(" ")}; do`,' if val=$(printenv "$v"); then set -- "$@" "$v=$val"; fi',"done",'if [ -n "$cmd" ]; then',' exec env -i "$@" "$shell" -lc "$cmd',"",'exec \\"\\$0\\"" "$shell"',"else",' exec env -i "$@" "$shell" -l',"fi"].join(` | ||
| `);function Wr(e){let t=`'${e.replace(/'/g,"'\\''")}'`;return `exec /bin/sh -c '${Gt}' omniterm-clean-env ${t}`}s(Wr,"buildDefaultCommand");function Lr(e,t,r,n,o){let i=["new-session","-d","-s",e];if(r){let a=r==="~"?homedir():r;i.push("-c",a);}if(n)for(let[a,c]of Object.entries(n))i.push("-e",`${a}=${c}`);return i.push("sh","-c",Gt,"omniterm-clean-env",t),o?.initialCommand&&i.push(o.initialCommand),i}s(Lr,"buildNewSessionArgs");function $e(e,t,r,n){let o=S().defaultShell||"bash";execFileSync("tmux",Lr(e,o,t,r,n),{stdio:"ignore"});let i=[["mouse","on"],["default-command",Wr(o)],["history-limit","50000"],["status-position","bottom"],["base-index","1"],["renumber-windows","on"],["escape-time","0"],["focus-events","on"]],a=[];for(let[c,l]of i)a.length>0&&a.push(";"),a.push("set-option","-t",`=${e}:`,c,l);try{execFileSync("tmux",a,{stdio:"ignore"});}catch{}et();}s($e,"createTmuxSession");function tt(e){try{execFileSync("tmux",["kill-session","-t",`=${e}`],{stdio:"ignore"});}catch{}try{unlinkSync(`/tmp/omniterm-silence-${e}`);}catch{}}s(tt,"killTmuxSession");function Ae(){try{let e=execFileSync("tmux",["list-sessions","-F","#{session_name}|||#{session_path}|||#{session_created}"],{encoding:"utf-8"}).trim();return e?e.split(` | ||
| `).map(t=>{let[r,n,o]=t.split("|||");return {name:r,cwd:n||"~",created:o}}):[]}catch{return []}}s(Ae,"listTmuxSessions");function nt(e,t){return !!t&&t!==e&&!/^[a-zA-Z0-9-]+$/.test(t)}s(nt,"isMeaningfulPaneTitle");function Vt(e){try{let t=execFileSync("tmux",["display-message","-t",`=${e}:`,"-p","#{pane_title}"],{encoding:"utf-8"}).trim();return nt(e,t)?t:execFileSync("tmux",["display-message","-t",`=${e}:`,"-p","#{pane_current_command}"],{encoding:"utf-8"}).trim()}catch{return ""}}s(Vt,"getTmuxPaneTitle");function me(e){try{return execFileSync("tmux",["has-session","-t",`=${e}`],{stdio:"ignore"}),!0}catch{return false}}s(me,"tmuxSessionExists");var ge=globalThis,k=ge.__omniterm_sse_clients||new Set;ge.__omniterm_sse_clients=k;function qt(e){k.add(e),console.log(`[events] SSE client connected; clients=${k.size}`),e.on("close",()=>{k.delete(e),console.log(`[events] SSE client disconnected; clients=${k.size}`);}),Br();}s(qt,"addClient");var Ur=3e4,fe=ge.__omniterm_sse_heartbeat??null;function Br(){fe||(fe=setInterval(()=>{if(k.size===0){clearInterval(fe),fe=null,ge.__omniterm_sse_heartbeat=null;return}for(let e of k)try{e.write(`: heartbeat | ||
| `),l="",u="",d=false;for(let m of c)m.startsWith("worktree ")?l=m.substring(9):m.startsWith("branch ")?u=m.substring(7).replace("refs/heads/",""):m==="bare"&&(d=true);if(!l||d){i=false;continue}let p=v.basename(l);n.push({id:Qe(l),repoId:t,branch:u||"HEAD",path:l,name:p,isMain:i}),i=false;}return n}s(qn,"parseWorktreeList");function R(e,t){return qn(e,t)}s(R,"listWorktrees");function Yn(e){try{return ce(["for-each-ref","--format=%(refname:short)","refs/heads/"],e).trim().split(` | ||
| `).filter(Boolean)}catch{return []}}s(Yn,"listBranches");function qt(e,t,r,n=true){let o=R(e,t),i=o.map(m=>m.name),a=new Set(Yn(e)),c=Gt(t,i),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 u=l.replace(/\//g,"-"),d=`${e}-${u}`;ce(n?["worktree","add","-b",l,d]:["worktree","add",d,l],e);try{for(let m of readdirSync(e,{withFileTypes:!0}))m.isFile()&&m.name.startsWith(".env")&©FileSync(v.join(e,m.name),v.join(d,m.name));}catch{}return {id:Qe(d),repoId:t,branch:l,path:d,name:v.basename(d),isMain:false}}s(qt,"createWorktree");function tt(e){try{return ce(["status","--porcelain"],e).trim().length>0}catch{return true}}s(tt,"isWorktreeDirty");function $e(e,t,{force:r=false}={}){ce(r?["worktree","remove","--force",t]:["worktree","remove",t],e);}s($e,"removeWorktree");function Yt(e,t,r){ce(["branch","-m",t,r],e);}s(Yt,"renameBranch");var rt=parseInt(process.env.OMNITERM_TTYD_PORT_MIN||"7700",10),nt=parseInt(process.env.OMNITERM_TTYD_PORT_MAX||"7799",10),Xt=globalThis,Ae=Xt.__omniterm_ports||new Set;Xt.__omniterm_ports=Ae;function Kn(){try{let e=execFileSync("lsof",[`-iTCP:${rt}-${nt}`,"-sTCP:LISTEN","-P","-n","-Fn"],{encoding:"utf-8",stdio:["pipe","pipe","pipe"]}),t=new Set;for(let r of e.split(` | ||
| `)){if(!r.startsWith("n"))continue;let n=r.match(/:(\d+)$/);n&&t.add(parseInt(n[1],10));}return t}catch{return new Set}}s(Kn,"getOccupiedPorts");function ot(){let e=Kn();for(let t=rt;t<=nt;t++)if(!Ae.has(t)&&!e.has(t))return Ae.add(t),t;throw new Error(`No free ports available in range ${rt}-${nt}`)}s(ot,"allocatePort");function fe(e){Ae.delete(e);}s(fe,"freePort");var Jn=/^[A-Za-z_][A-Za-z0-9_]*$/,Zn=new Set(["TMUX","TMUX_PANE"]),C=class extends Error{static{s(this,"EnvNameError");}envName;constructor(t,r){super(r),this.name="EnvNameError",this.envName=t;}};function Oe(e){if(Zn.has(e))throw new C(e,`${e} is reserved by omniterm and cannot be overridden`);if(!Jn.test(e))throw new C(e,`${JSON.stringify(e)} is not a valid environment variable name (letters, digits and underscore; must not start with a digit)`)}s(Oe,"assertValidEnvName");function st(e){let t=new Set,r=[];for(let n of e.split(",")){let o=n.trim();o&&(Oe(o),!t.has(o)&&(t.add(o),r.push(o)));}return r}s(st,"parseEnvPassthrough");function it(e){let t=new Set,r=[];for(let n=0;n<e.length;n++){if(e[n]!=="--env-passthrough")continue;let o=e[n+1];if(!o||o.startsWith("--"))throw new C("","--env-passthrough requires a value (a comma-separated list of variable names)");for(let i of st(o))t.has(i)||(t.add(i),r.push(i));n++;}return r}s(it,"parseEnvPassthroughArgv");var Kt=[];function Jt(e){for(let t of e)Oe(t);Kt=[...e];}s(Jt,"setEnvPassthrough");function je(){return Kt}s(je,"getEnvPassthrough");var Zt=false;function at(){if(!Zt){Zt=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{}}}s(at,"ensureTmuxBindings");var Qt=["TERM","COLORTERM","TMUX","TMUX_PANE","HOME","USER","LOGNAME","SHELL","TMPDIR","TZ","LANG","LC_ALL","LC_COLLATE","LC_CTYPE","LC_MESSAGES","LC_MONETARY","LC_NUMERIC","LC_TIME","SSH_AUTH_SOCK","DISPLAY","WAYLAND_DISPLAY","XAUTHORITY","XDG_RUNTIME_DIR","DBUS_SESSION_BUS_ADDRESS","OMNITERM_BROWSER_REGISTRY_URL","BROWSER","PATH"];function ct(e=[]){let t=[...Qt];for(let r of e)t.includes(r)||t.push(r);return ['shell="$1"; cmd="$2"',"set --",`for v in ${t.join(" ")}; do`,' if val=$(printenv "$v"); then set -- "$@" "$v=$val"; fi',"done",'if [ -n "$cmd" ]; then',' exec env -i "$@" "$shell" -lc "$cmd',"",'exec \\"\\$0\\"" "$shell"',"else",' exec env -i "$@" "$shell" -l',"fi"].join(` | ||
| `)}s(ct,"buildCleanEnvScript");ct();function er(e){let t=[];for(let r of [...je(),...Object.keys(e??{})])!Qt.includes(r)&&!t.includes(r)&&t.push(r);return t}s(er,"sessionEnvNames");function to(e,t=[]){let r=`'${e.replace(/'/g,"'\\''")}'`;return `exec /bin/sh -c '${ct(t)}' omniterm-clean-env ${r}`}s(to,"buildDefaultCommand");function ro(e,t,r,n,o){let i=["new-session","-d","-s",e];if(r){let a=r==="~"?homedir():r;i.push("-c",a);}if(n)for(let[a,c]of Object.entries(n))i.push("-e",`${a}=${c}`);return i.push("sh","-c",ct(er(n)),"omniterm-clean-env",t),o?.initialCommand&&i.push(o.initialCommand),i}s(ro,"buildNewSessionArgs");function Ne(e,t,r,n){let o=S().defaultShell||"bash";execFileSync("tmux",ro(e,o,t,r,n),{stdio:"ignore"});let i=[["mouse","on"],["default-command",to(o,er(r))],["history-limit","50000"],["status-position","bottom"],["base-index","1"],["renumber-windows","on"],["escape-time","0"],["focus-events","on"]],a=[];for(let[c,l]of i)a.length>0&&a.push(";"),a.push("set-option","-t",`=${e}:`,c,l);try{execFileSync("tmux",a,{stdio:"ignore"});}catch{}at();}s(Ne,"createTmuxSession");function lt(e){try{execFileSync("tmux",["kill-session","-t",`=${e}`],{stdio:"ignore"});}catch{}try{unlinkSync(`/tmp/omniterm-silence-${e}`);}catch{}}s(lt,"killTmuxSession");function De(){try{let e=execFileSync("tmux",["list-sessions","-F","#{session_name}|||#{session_path}|||#{session_created}"],{encoding:"utf-8"}).trim();return e?e.split(` | ||
| `).map(t=>{let[r,n,o]=t.split("|||");return {name:r,cwd:n||"~",created:o}}):[]}catch{return []}}s(De,"listTmuxSessions");function dt(e,t){return !!t&&t!==e&&!/^[a-zA-Z0-9-]+$/.test(t)}s(dt,"isMeaningfulPaneTitle");function tr(e){try{let t=execFileSync("tmux",["display-message","-t",`=${e}:`,"-p","#{pane_title}"],{encoding:"utf-8"}).trim();return dt(e,t)?t:execFileSync("tmux",["display-message","-t",`=${e}:`,"-p","#{pane_current_command}"],{encoding:"utf-8"}).trim()}catch{return ""}}s(tr,"getTmuxPaneTitle");function ge(e){try{return execFileSync("tmux",["has-session","-t",`=${e}`],{stdio:"ignore"}),!0}catch{return false}}s(ge,"tmuxSessionExists");var ye=globalThis,k=ye.__omniterm_sse_clients||new Set;ye.__omniterm_sse_clients=k;function rr(e){k.add(e),console.log(`[events] SSE client connected; clients=${k.size}`),e.on("close",()=>{k.delete(e),console.log(`[events] SSE client disconnected; clients=${k.size}`);}),oo();}s(rr,"addClient");var no=3e4,he=ye.__omniterm_sse_heartbeat??null;function oo(){he||(he=setInterval(()=>{if(k.size===0){clearInterval(he),he=null,ye.__omniterm_sse_heartbeat=null;return}for(let e of k)try{e.write(`: heartbeat | ||
| `);}catch{k.delete(e);}},Ur),ge.__omniterm_sse_heartbeat=fe);}s(Br,"startHeartbeat");function $(e,t){let r=JSON.stringify({type:e,...t});console.log(`[events] broadcast type=${e} clients=${k.size} payload=${r}`);for(let n of k)try{n.write(`data: ${r} | ||
| `);}catch{k.delete(e);}},no),ye.__omniterm_sse_heartbeat=he);}s(oo,"startHeartbeat");function A(e,t){let r=JSON.stringify({type:e,...t});console.log(`[events] broadcast type=${e} clients=${k.size} payload=${r}`);for(let n of k)try{n.write(`data: ${r} | ||
| `);}catch{k.delete(n);}}s($,"broadcast");function Yt(){return k.size}s(Yt,"getClientCount");var Kt=promisify(execFile),Vr=3e3,Xt=2e3,qr=15,Yr=20,Kr=2,Xr="#{session_name} #{window_activity} #{window_active} #{pane_active} #{pane_current_path}",Jt=globalThis,b=Jt.__omniterm_silence_monitor??{states:new Map,legacyCleaned:new Set,timer:null,polling:false};Jt.__omniterm_silence_monitor=b;function rt(e){b.states.has(e)||b.states.set(e,{lastActivitySeconds:null,activeSinceSeconds:null,path:""}),no(e),Jr();}s(rt,"trackSilenceSession");function he(e){b.states.delete(e),Zt(e);}s(he,"untrackSilenceSession");function Zt(e){b.legacyCleaned.delete(e),Qt(e),b.states.size===0&&Zr();}s(Zt,"cleanupSilenceSession");function Jr(){b.timer||(b.timer=setInterval(()=>{Qr();},Vr),b.timer.unref?.());}s(Jr,"ensurePoller");function Zr(){b.timer&&(clearInterval(b.timer),b.timer=null);}s(Zr,"stopPoller");async function Qr(){if(!(b.polling||b.states.size===0)){b.polling=true;try{let e=new Set(b.states.keys()),{stdout:t}=await Kt("tmux",["list-panes","-a","-F",Xr],{encoding:"utf-8",timeout:Xt}),r=eo(t,e);to(b.states,r,ro(),n=>{console.log(`[alert] broadcast session-silence session=${n.sessionId} cwd=${n.path} sse_clients=${Yt()}`),$("session-silence",{path:n.path,sessionId:n.sessionId});},{onSessionDeleted:Zt,trackedSessions:e});}catch{}finally{b.polling=false;}}}s(Qr,"pollSilenceSessions");function eo(e,t){let r=new Map;for(let n of e.split(` | ||
| `)){if(!n.trim())continue;let[o,i,a,c,...l]=n.split(" ");if(!o||!t.has(o))continue;let u=i?.trim();if(!u)continue;let d=Number(u);if(!Number.isFinite(d))continue;let p=l.join(" ").trim(),h=a==="1"&&c==="1",w=r.get(o);if(!w){r.set(o,{activitySeconds:d,path:p,activePaneSeen:h});continue}d>w.activitySeconds&&(w.activitySeconds=d),(h||!w.activePaneSeen)&&(w.path=p,w.activePaneSeen=w.activePaneSeen||h);}return new Map([...r].map(([n,o])=>[n,{activitySeconds:o.activitySeconds,path:o.path}]))}s(eo,"parseTmuxActivitySnapshots");function to(e,t,r,n,o={}){let i=o.silenceSeconds??qr,a=o.activeCooldownSeconds??Yr,c=o.absentPollsBeforeDelete??Kr;for(let[l,u]of e){if(o.trackedSessions&&!o.trackedSessions.has(l))continue;let d=t.get(l);if(!d){u.missingPolls=(u.missingPolls??0)+1,u.missingPolls>=c&&(e.delete(l),o.onSessionDeleted?.(l));continue}if(u.missingPolls=0,u.path=d.path,u.lastActivitySeconds===null){u.lastActivitySeconds=d.activitySeconds;continue}if(d.activitySeconds<u.lastActivitySeconds){u.lastActivitySeconds=d.activitySeconds,u.activeSinceSeconds=null;continue}if(d.activitySeconds>u.lastActivitySeconds){u.activeSinceSeconds===null&&(u.activeSinceSeconds=d.activitySeconds),u.lastActivitySeconds=d.activitySeconds;continue}if(u.activeSinceSeconds!==null&&r-u.lastActivitySeconds>=i){if(u.lastActivitySeconds-u.activeSinceSeconds<a){u.activeSinceSeconds=null;continue}u.path&&(n({sessionId:l,path:u.path}),u.activeSinceSeconds=null);}}}s(to,"updateSilenceStates");function no(e){b.legacyCleaned.has(e)||(b.legacyCleaned.add(e),Kt("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:Xt}).catch(()=>{}).finally(()=>Qt(e)));}s(no,"disableLegacySilenceMonitoring");function Qt(e){try{unlinkSync(`/tmp/omniterm-silence-${e}`);}catch{}}s(Qt,"removeLegacyStateFile");function ro(){return Math.floor(Date.now()/1e3)}s(ro,"nowSeconds");var ye=[];function ot(e,t,r=Date.now()){ye.push({op:e,timings:t,at:r}),ye.length>100&&ye.splice(0,ye.length-100);}s(ot,"recordPerf");function en(){return ye.slice()}s(en,"getRecent");var tn="https://us.i.posthog.com";function st(e){if(!e)return false;let t=e.trim().toLowerCase();return t==="1"||t==="true"||t==="yes"||t==="on"}s(st,"truthy");function oo(e){if(e===void 0)return false;let t=e.trim().toLowerCase();return t==="0"||t==="false"||t==="no"||t==="off"}s(oo,"offValue");function nn(e,t){let r=(e.OMNITERM_POSTHOG_KEY??"").trim(),n=(e.OMNITERM_POSTHOG_HOST??tn).trim()||tn;return {enabled:!(st(e.DO_NOT_TRACK)||oo(e.OMNITERM_TELEMETRY)||st(e.OMNITERM_TELEMETRY_DISABLED)||t||st(e.CI)||e.NODE_ENV==="test"||r===""),key:r,host:n}}s(nn,"resolveTelemetryConfig");function Oe(){return x.join(Ve,"telemetry.json")}s(Oe,"telemetryStatePath");function Se(e=Oe()){try{let t=readFileSync(e,"utf-8"),r=JSON.parse(t);return r&&typeof r.installId=="string"&&r.installId?r:null}catch{return null}}s(Se,"readState");function on(e,t=Oe()){try{mkdirSync(x.dirname(t),{recursive:!0});let r=`${t}.${process.pid}.tmp`;writeFileSync(r,JSON.stringify(e,null,2),"utf-8"),renameSync(r,t);}catch{}}s(on,"writeState");function sn(e=Oe()){let t=Se(e);if(t)return t.installId;let r={installId:randomUUID()};if(on(r,e),existsSync(e)){let n=Se(e);if(n)return n.installId}return r.installId}s(sn,"getOrCreateInstallId");function an(e=Oe()){let t=Se(e);t&&(t.noticeShown||on({...t,noticeShown:true},e));}s(an,"markNoticeShown");var Z=null,it="",U={enabled:false,key:"",host:""},cn=false;function fo(){return {app_version:process.env.OMNITERM_VERSION||"unknown",os:po.platform(),node_version:process.version}}s(fo,"baseContext");function go(e){return {...fo(),...e??{}}}s(go,"buildProps");function ho(e){return !e.enabled||!e.hasClient?{enabled:false}:{enabled:true,key:e.key,host:e.host,distinctId:e.distinctId,appVersion:e.appVersion}}s(ho,"projectClientTelemetryConfig");function ln(){return ho({enabled:U.enabled,hasClient:Z!==null,key:U.key,host:U.host,distinctId:it,appVersion:process.env.OMNITERM_VERSION||"unknown"})}s(ln,"getClientTelemetryConfig");function ae(e,t){if(!(!U.enabled||!Z))try{Z.capture({distinctId:it,event:e,properties:go(t)});}catch{}}s(ae,"capture");function yo(){try{let e=Se();if(!e||e.noticeShown)return;process.stderr.write("[omniterm] Pseudonymous usage + performance telemetry is enabled. Event payloads exclude names, contents, paths, repo names, and plugin ids. Opt out anytime with `omniterm telemetry off`, the Settings panel, or OMNITERM_TELEMETRY=0 / DO_NOT_TRACK=1.\n"),an();}catch{}}s(yo,"showFirstRunNoticeOnce");function dn(){if(!cn){cn=true;try{let e=S().telemetryEnabled===!1;if(U=nn(process.env,e),!U.enabled)return;it=sn(),Z=new PostHog(U.key,{host:U.host,flushAt:1,disableGeoip:!0}),yo();}catch{U={enabled:false,key:"",host:""},Z=null;}}}s(dn,"initTelemetry");async function un(){let e=Z;if(Z=null,!e)return;let t,r=new Promise(n=>{t=setTimeout(n,2e3),t.unref?.();});try{await Promise.race([e.shutdown(2e3),r]);}catch{}finally{t&&clearTimeout(t);}}s(un,"shutdownTelemetry");function pn(e){ae("server_started",{session_count:e});}s(pn,"trackServerStarted");function mn(e){e&&ot("session_created",e),ae("session_created",e);}s(mn,"trackSessionCreated");function fn(e){e&&ot("session_adopted",e),ae("session_adopted",e);}s(fn,"trackSessionAdopted");function gn(){ae("session_closed");}s(gn,"trackSessionClosed");function hn(e){ae("file_opened",{language:e});}s(hn,"trackFileOpened");function yn(e){ae("cleanup",e===void 0?void 0:{count:e});}s(yn,"trackCleanup");function Sn(){return en()}s(Sn,"getRecentPerfMetrics");var je=globalThis,B=je.__omniterm_ttyd||new Map,De=je.__omniterm_ttyd_stopped_listeners||new Set;je.__omniterm_ttyd=B;je.__omniterm_ttyd_stopped_listeners=De;function at(e){for(let t of De)try{t(e);}catch(r){let n=r instanceof Error?r.message:String(r);console.error(`[ttyd] stopped listener failed for ${e}: ${n}`);}}s(at,"notifyTtydStopped");function xn(e){return De.add(e),()=>{De.delete(e);}}s(xn,"onTtydStopped");function wo(e,t,r){let n=r.terminalFontSize||18,o=r.terminalRenderer==="dom"?"dom":"webgl";return ["--writable","-i","127.0.0.1","-b",`/t/${e}/`,"-t",`rendererType=${o}`,"-t",`fontSize=${n}`,"-p",String(t),"--","bash","-c",`exec tmux attach-session -t ${e}`]}s(wo,"buildTtydArgs");function we(e,t){let r=wo(e,t,S()),n=spawn("ttyd",r,{stdio:"ignore",detached:true});return n.once("error",o=>{console.error(`[ttyd] failed to spawn for ${e} on port ${t}: ${o.message}`),B.get(e)===n&&(B.delete(e),at(e));}),n.once("exit",(o,i)=>{B.get(e)===n&&(B.delete(e),at(e)),(o!==0||i)&&console.error(`[ttyd] exited for ${e} on port ${t}: code=${o??"null"} signal=${i??"null"}`);}),n.unref(),B.set(e,n),n}s(we,"spawnTtyd");function vn(e,t=3e3){return new Promise((r,n)=>{let o=Date.now()+t;function i(){let a=new bn.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(i,30);}),a.on("timeout",()=>{a.destroy(),Date.now()>=o?n(new Error(`ttyd not ready on port ${e} after ${t}ms`)):setTimeout(i,30);});}s(i,"attempt"),i();})}s(vn,"waitForTtydReady");function be(e){let t=B.get(e);if(t&&t.pid)try{process.kill(t.pid,"SIGTERM");}catch{}B.delete(e)&&at(e);}s(be,"stopTtyd");function ct(e){let t=B.get(e);if(!t||!t.pid)return false;try{return process.kill(t.pid,0),!0}catch{return false}}s(ct,"isTtydAlive");function bo(e,t){if(!e.trim())return [];let r=[];for(let n of e.split(` | ||
| `)){if(!n.includes("ttyd")||!n.includes("--writable"))continue;let o=n.match(/^\s*(\d+)/),i=n.match(/-b \/t\/([^/]+)\//);if(!o||!i)continue;let a=Number(o[1]),c=i[1];t.has(c)||r.push(a);}return r.sort((n,o)=>n-o)}s(bo,"getTtydPidsToKill");function lt(){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(` | ||
| `):[]);}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 bo(t,e))try{process.kill(n,"SIGTERM"),r++;}catch{}r>0&&(console.log(`[cleanup] killed ${r} stale ttyd processes`),yn(r));}catch{}}s(lt,"killOrphanedTtydProcesses");function Tn(){return {readySessionNames:new Set,pendingReadiness:new Map,generations:new Map}}s(Tn,"createSessionTtydReadinessState");function P(e,t){t.readySessionNames.delete(e),t.pendingReadiness.delete(e),t.generations.set(e,(t.generations.get(e)??0)+1);}s(P,"clearSessionTtydReadiness");async function Rn(e,t,r,n){let o=e.tmuxName;if(n.readySessionNames.has(o)){if(r.isTtydAlive(o))return;P(o,n);}let i=n.pendingReadiness.get(o);if(i){if(await i,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)||(P(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 P(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;}s(Rn,"ensureSessionTtydReadyWithState");var kn=globalThis;function Pn(){kn.__omniterm_recovered||(kn.__omniterm_recovered=true,lt());}s(Pn,"recoverAll");var Ce=ue?x.join(ue,"omniterm-browser.js"):null,To=Ce!==null&&existsSync(Ce),En="/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin";function ut(e){let t={OMNITERM_BROWSER_REGISTRY_URL:e};return To&&Ce&&ue?(t.BROWSER=Ce,t.PATH=`${ue}:${En}`):t.PATH=En,t}s(ut,"buildTabEnv");var V=globalThis,A=V.__omniterm_sessions||new Map,dt=V.__omniterm_counters||new Map,N=V.__omniterm_ttyd_readiness||Tn();V.__omniterm_sessions=A;V.__omniterm_counters=dt;V.__omniterm_ttyd_readiness=N;var Ro={isTtydAlive:ct,spawnTtyd:we,waitForTtydReady:vn};V.__omniterm_session_ttyd_listener_registered||(xn(e=>P(e,N)),V.__omniterm_session_ttyd_listener_registered=true);function ko(e){let t=(dt.get(e)||0)+1;for(;me(`${e}-term-${t}`);)t++;return dt.set(e,t),`${e}-term-${t}`}s(ko,"nextId");Pn();function In(e,t,r){let n=Date.now(),o=ko(e),i=Qe(),a=Date.now();$e(o,t,ut(r.registryUrlFor(o)));let c=Date.now(),l={id:o,worktreeId:e,tmuxName:o,port:i,createdAt:new Date().toISOString()};P(l.tmuxName,N),we(l.tmuxName,l.port);let u=Date.now();A.set(o,l),rt(l.tmuxName);let d=Date.now();return console.log(`[session] ${o}: tmux=${c-a}ms ttyd=${u-c}ms total=${d-n}ms`),$("session-created",{sessionId:o,worktreeId:e,path:t}),l}s(In,"createSession");function q(e){let t=A.get(e);if(t){if(!me(t.tmuxName)){P(t.tmuxName,N),be(t.tmuxName),pe(t.port),A.delete(e),he(t.tmuxName);return}return ct(t.tmuxName)||(P(t.tmuxName,N),we(t.tmuxName,t.port)),t}}s(q,"getSession");async function Y(e,t=3e3){await Rn(e,t,Ro,N);}s(Y,"ensureSessionTtydReady");function Mn(e){let t=A.get(e);return t?(P(t.tmuxName,N),be(t.tmuxName),pe(t.port),A.delete(e),he(t.tmuxName),true):false}s(Mn,"unregisterSession");function K(e){let t=Array.from(A.values());return e?t.filter(r=>r.worktreeId===e):t}s(K,"listSessions");function xe(e){let t=A.get(e);return t?(P(t.tmuxName,N),be(t.tmuxName),tt(t.tmuxName),pe(t.port),A.delete(e),he(t.tmuxName),gn(),$("session-closed",{sessionId:e,worktreeId:t.worktreeId}),true):false}s(xe,"deleteSession");function Ne(e){let t=K(e);for(let r of t)P(r.tmuxName,N),be(r.tmuxName),tt(r.tmuxName),pe(r.port),A.delete(r.id),he(r.tmuxName);return t.length>0&&$("session-closed",{worktreeId:e,sessionCount:t.length}),t.length}s(Ne,"deleteSessionsForWorktree");function pt(e,t){let r=Date.now(),n=Qe(),o=Date.now();if(!t?.skipTmuxSetup){et();try{execFileSync("tmux",["set-option","-t",`=${e}:`,"mouse","on"],{stdio:"ignore"});}catch{}}let i=Date.now();P(e,N),we(e,n);let a=Date.now(),c={id:e,worktreeId:"_orphan",tmuxName:e,port:n,createdAt:new Date().toISOString()};A.set(c.id,c),rt(c.tmuxName);let l=o-r,u=a-r;console.log(`[session] adopted ${e}: port=${n} allocate=${l}ms setup=${i-o}ms ttyd=${a-i}ms total=${u}ms skip=${t?.skipTmuxSetup?1:0}`),fn({total_ms:u,allocate_port_ms:l});let d=t?.knownCwd??"";if(!d)try{d=execFileSync("tmux",["display-message","-t",`=${e}:`,"-p","#{pane_current_path}"],{encoding:"utf-8"}).trim();}catch{}return $("session-adopted",{sessionId:e,worktreeId:"_orphan",path:d}),c}s(pt,"adoptSession");function _o(e){let t=x.join(e,".git");try{let n=statSync(t);if(n.isDirectory())return x.join(t,"config");if(n.isFile()){let o=readFileSync(t,"utf-8").match(/^gitdir:\s*(.+)$/m);if(o){let i=x.resolve(e,o[1].trim()),a=x.join(i,"commondir"),c=existsSync(a)?x.resolve(i,readFileSync(a,"utf-8").trim()):i;return x.join(c,"config")}}}catch{}let r=x.join(e,"config");return existsSync(r)?r:null}s(_o,"gitConfigPath");function Eo(e){let t=false;for(let r of e.split(` | ||
| `)){let n=r.trim();if(n.startsWith("[")){t=/^\[remote\s+"origin"\]$/.test(n);continue}if(t){let o=n.match(/^url\s*=\s*(.+)$/);if(o)return o[1].trim()}}return ""}s(Eo,"parseOriginUrl");var $n=new Map;function On(e){let t=_o(e);if(!t)return "";try{let{mtimeMs:r}=statSync(t),n=$n.get(t);if(n&&n.mtimeMs===r)return n.url;let o=Eo(readFileSync(t,"utf-8"));return $n.set(t,{mtimeMs:r,url:o}),o}catch{return ""}}s(On,"getRemoteUrl");function ft(e){return existsSync(x.join(e,".git"))||existsSync(x.join(e,"HEAD"))}s(ft,"isGitRepo");function _(){return S().trackedRepos.filter(t=>existsSync(t)&&ft(t)).map(t=>({id:G(t),name:C(t),remoteUrl:On(t),path:t}))}s(_,"listRepos");function ve(e){let t=S();for(let r of t.trackedRepos)if(G(r)===e&&existsSync(r)&&ft(r))return {id:e,name:C(r),remoteUrl:On(r),path:r}}s(ve,"getRepo");function gt(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)||L({trackedRepos:[...r.trackedRepos,t]}),{type:"repo",path:t};{let n=r.trackedRepos.filter(i=>i!==t),o=r.trackedDirs.includes(t)?r.trackedDirs:[...r.trackedDirs,t];return (n.length!==r.trackedRepos.length||o!==r.trackedDirs)&&L({trackedRepos:n,trackedDirs:o}),{type:"dir",path:t}}}s(gt,"addLocalPath");function Dn(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 L({trackedRepos:[...o.trackedRepos,n]}),{id:G(n),name:C(n),remoteUrl:e,path:n}}s(Dn,"cloneRepo");function jn(e){let t=ve(e);if(!t)throw new Error(`Repo "${e}" not found`);let r=0,n=0,o=[],i=R(t.path,e);for(let c of i)if(n+=Ne(c.id),!c.isMain)try{Ie(t.path,c.path),r++;}catch{o.push(c.path);}let a=S();return L({trackedRepos:a.trackedRepos.filter(c=>c!==t.path)}),{worktreesRemoved:r,worktreesSkipped:o,sessionsKilled:n}}s(jn,"removeRepo");var Q=Router();Q.get("/repos",(e,t)=>{t.json(_());});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 i=n?gt(n):null,a=i?{id:G(i.path),name:C(i.path),...i}:Dn(r,o);t.status(201).json(a);}catch(i){let a=i instanceof Error?i.message:String(i);t.status(a.includes("already")?409:400).json({error:a});}});Q.delete("/repos/:repoId",(e,t)=>{if(!ve(e.params.repoId)){t.status(404).json({error:"Repo not found"});return}let n=jn(e.params.repoId);t.json({deleted:true,...n});});Q.get("/repos/:repoId/worktrees",(e,t)=>{let r=ve(e.params.repoId);if(!r){t.status(404).json({error:"Repo not found"});return}let n=R(r.path,e.params.repoId),o=Ae();t.json(n.map(i=>({...i,sessionCount:o.filter(a=>a.cwd===i.path).length})));});Q.post("/repos/:repoId/worktrees",async(e,t)=>{let r=ve(e.params.repoId);if(!r){t.status(404).json({error:"Repo not found"});return}let{branch:n,newBranch:o=true}=e.body||{};try{let i=Ut(r.path,e.params.repoId,n,o);t.status(201).json(i);}catch(i){let a=i instanceof Error?i.message:String(i);t.status(a.includes("already has a worktree")?409:400).json({error:a});}});var Cn={".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"},Nn={dockerfile:"dockerfile",makefile:"makefile"};[...new Set([...Object.values(Cn),...Object.values(Nn)])];function Fe(e,t){return Cn[e]||Nn[t]||"text"}s(Fe,"detectLanguage");function Mo(e){return e instanceof Error?e.message:String(e)}s(Mo,"errorMessage");function Fn(e,t,r,n){n(t.id),e.status(503).json({error:Mo(r)});}s(Fn,"sendWorktreeSessionReadinessError");var H=Router();function yt(e){for(let t of _()){let r=R(t.path,t.id).find(n=>n.id===e);if(r)return r.path}return null}s(yt,"findWorktreePath");H.delete("/worktrees/:wtId",(e,t)=>{let{wtId:r}=e.params,n=e.query.force==="true"||e.body&&e.body.force===true;for(let o of _()){let i=R(o.path,o.id).find(a=>a.id===r);if(i){if(!n&&!i.isMain&&Xe(i.path)){t.status(409).json({error:"Worktree has uncommitted changes",requiresForce:true});return}try{Ie(o.path,i.path,{force:n});}catch(c){t.status(409).json({error:c instanceof Error?c.message:String(c),requiresForce:false});return}let a=Ne(r);t.json({deleted:true,sessionsKilled:a});return}}t.status(404).json({error:"Worktree not found"});});H.get("/worktrees/:wtId/status",(e,t)=>{let r=yt(e.params.wtId);if(!r){t.status(404).json({error:"Worktree not found"});return}t.json({dirty:Xe(r)});});H.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 _()){let i=R(o.path,o.id).find(a=>a.id===r);if(i){try{Bt(i.path,i.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"});});H.get("/worktrees/:wtId/sessions",(e,t)=>{let r=K(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:Vt(n.tmuxName)})));});H.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",i=In(r,n,{registryUrlFor:s(a=>`http://${o}/t/${a}/registry`,"registryUrlFor")});try{await Y(i);}catch(a){Fn(t,i,a,xe);return}t.status(201).json({id:i.id,worktreeId:i.worktreeId,port:i.port,url:`/t/${i.id}/`,createdAt:i.createdAt});}catch(o){let i=o instanceof Error?o.message:String(o);t.status(i.includes("No free ports")?503:500).json({error:i});}});H.get("/worktrees/:wtId/files",async(e,t)=>{let r=yt(e.params.wtId);if(!r){t.status(404).json({error:"Worktree not found"});return}let n=e.query.path||"",o;try{o=qe(r,n);}catch{t.status(403).json({error:"Path traversal detected"});return}try{let i=await readdir(o,{withFileTypes:!0}),a=await Promise.all(i.filter(c=>!c.name.startsWith(".")).map(async c=>{let l=c.isDirectory()?"directory":"file",u=0;if(l==="file")try{u=(await stat(x.join(o,c.name))).size;}catch{}return {name:c.name,type:l,size:u}}));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"});}});H.get("/worktrees/:wtId/file",async(e,t)=>{let r=yt(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=qe(r,n);}catch{t.status(403).json({error:"Path traversal detected"});return}try{let i=await stat(o);if(i.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(),u=Fe(c,l);t.json({path:n,content:a,language:u,size:i.size});}catch{t.status(404).json({error:"File not found"});}});var Te=null,ce=null,Re=null,We=null,Ln=Un(5e3),Fo=500;function Wo(e){return e.replace(/\\/g,"/")}s(Wo,"normalizeWatchPath");function Lo(e){return e===".git"||e.startsWith(".git/")}s(Lo,"isGitInternalPath");function Un(e){let t=Number.NEGATIVE_INFINITY;return {shouldLog(r=Date.now()){return r-t<e?false:(t=r,true)}}}s(Un,"createWatchEventLogLimiter");function Bn(e){let t=No();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/playwright-report/","**/.playwright-artifacts-*"]),t}s(Bn,"buildWatchIgnoreMatcher");function Uo(e,t,r){let n=Wo(t);return n==="<unknown>"?false:n===".gitignore"||n.endsWith("/.gitignore")?(We=Bn(e),false):Lo(n)?true:r?r.ignores(n):false}s(Uo,"shouldIgnoreWatchPath");function Hn(e){if(ce!==e){Le(),ce=e,We=Bn(e);try{console.log(`[watcher] start watching path=${e}`),Te=watch(e,{recursive:!0},(t,r)=>{let n=typeof r=="string"&&r.length>0?r:"<unknown>";Uo(e,n,We)||(Ln.shouldLog()&&console.log(`[watcher] fs event path=${e} event=${t} file=${n}`),Re&&clearTimeout(Re),Re=setTimeout(()=>{console.log(`[watcher] broadcast files-changed path=${e}`),$("files-changed");},Fo));}),Te.on("error",t=>{let r=t instanceof Error?t.message:String(t);console.error(`[watcher] error path=${e} message=${r}`),Le();});}catch{console.error(`[watcher] failed to watch path=${e}`),ce=null;}}}s(Hn,"watchWorkspace");function Le(){Re&&clearTimeout(Re),Te&&(ce&&console.log(`[watcher] stop watching path=${ce}`),Te.close(),Te=null),Ln=Un(5e3),We=null,ce=null;}s(Le,"stopWatching");var Ue=Router();Ue.get("/settings",(e,t)=>{t.json(S());});Ue.put("/settings",(e,t)=>{let r=e.body||{},n=L(r);n.activePath?Hn(n.activePath):Le(),t.json(n);});function E(){let e=S(),t=new Set([homedir()]);for(let r of e.trackedDirs)t.add(r);for(let r of _()){t.add(r.path);try{for(let n of R(r.path,r.id))t.add(n.path);}catch{}}return [...t]}s(E,"allowedRoots");var ee=Router();ee.get("/fs",async(e,t)=>{let r=e.query.path||"",n=e.query.mode||"list",o=I(r,E());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();if(c.size>1024*1024){t.status(413).json({error:"File too large (>1MB)"});return}let u=await readFile(o,"utf-8"),d=Fe(l,x.basename(o).toLowerCase());hn(d),t.json({path:r,content:u,language:d,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"});}});ee.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,E());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(i){t.status(500).json({error:i instanceof Error?i.message:String(i)});}});ee.get("/browse",async(e,t)=>{let r=e.query.path||homedir(),n=I(r,E());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}),i=(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:i,isGitRepo:a});}catch{t.status(403).json({error:"Cannot read directory"});}});ee.get("/git/status",(e,t)=>{let r=e.query.cwd||"",n=I(r,E());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$/,""),i={};if(o)for(let a of o.split(` | ||
| `)){let c=a.substring(0,2),l=a.substring(3);i[l]=c;}t.json(i);}catch{t.json({});}});ee.get("/git/show",(e,t)=>{let r=e.query.cwd||"",n=e.query.path||"",o=I(r,E());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 i=execFileSync("git",["show",`HEAD:${n}`],{cwd:o,encoding:"utf-8"});t.json({content:i});}catch{t.json({content:""});}});var Be=Router(),Zo=1024*1024,Qo=25*1024*1024;Be.get("/preview/markdown",async(e,t)=>{let r=e.query.path||"",n=I(r,E());if(!n){t.status(403).type("text/plain").send("Path outside allowed roots");return}try{if((await stat(n)).size>Zo){t.status(413).type("text/plain").send("Markdown file too large (>1MB)");return}let i=await readFile(n,"utf-8"),a=marked.parse(i,{async:!1,gfm:!0,breaks:!1});t.type("text/html; charset=utf-8").send(es(x.basename(n),a));}catch{t.status(404).type("text/plain").send("Not found");}});Be.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,E());if(!o){t.status(403).type("text/plain").send("Path outside allowed roots");return}try{let i=await stat(o);if(!i.isFile()){t.status(404).type("text/plain").send("Not a file");return}if(i.size>Qo){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 es(e,t){return `<!DOCTYPE html> | ||
| `);}catch{k.delete(n);}}s(A,"broadcast");function nr(){return k.size}s(nr,"getClientCount");var or=promisify(execFile),co=3e3,sr=2e3,lo=15,uo=20,po=2,mo="#{session_name} #{window_activity} #{window_active} #{pane_active} #{pane_current_path}",ir=globalThis,w=ir.__omniterm_silence_monitor??{states:new Map,legacyCleaned:new Set,timer:null,polling:false};ir.__omniterm_silence_monitor=w;function ut(e){w.states.has(e)||w.states.set(e,{lastActivitySeconds:null,activeSinceSeconds:null,path:""}),wo(e),fo();}s(ut,"trackSilenceSession");function Se(e){w.states.delete(e),ar(e);}s(Se,"untrackSilenceSession");function ar(e){w.legacyCleaned.delete(e),cr(e),w.states.size===0&&go();}s(ar,"cleanupSilenceSession");function fo(){w.timer||(w.timer=setInterval(()=>{ho();},co),w.timer.unref?.());}s(fo,"ensurePoller");function go(){w.timer&&(clearInterval(w.timer),w.timer=null);}s(go,"stopPoller");async function ho(){if(!(w.polling||w.states.size===0)){w.polling=true;try{let e=new Set(w.states.keys()),{stdout:t}=await or("tmux",["list-panes","-a","-F",mo],{encoding:"utf-8",timeout:sr}),r=yo(t,e);So(w.states,r,bo(),n=>{console.log(`[alert] broadcast session-silence session=${n.sessionId} cwd=${n.path} sse_clients=${nr()}`),A("session-silence",{path:n.path,sessionId:n.sessionId});},{onSessionDeleted:ar,trackedSessions:e});}catch{}finally{w.polling=false;}}}s(ho,"pollSilenceSessions");function yo(e,t){let r=new Map;for(let n of e.split(` | ||
| `)){if(!n.trim())continue;let[o,i,a,c,...l]=n.split(" ");if(!o||!t.has(o))continue;let u=i?.trim();if(!u)continue;let d=Number(u);if(!Number.isFinite(d))continue;let p=l.join(" ").trim(),m=a==="1"&&c==="1",b=r.get(o);if(!b){r.set(o,{activitySeconds:d,path:p,activePaneSeen:m});continue}d>b.activitySeconds&&(b.activitySeconds=d),(m||!b.activePaneSeen)&&(b.path=p,b.activePaneSeen=b.activePaneSeen||m);}return new Map([...r].map(([n,o])=>[n,{activitySeconds:o.activitySeconds,path:o.path}]))}s(yo,"parseTmuxActivitySnapshots");function So(e,t,r,n,o={}){let i=o.silenceSeconds??lo,a=o.activeCooldownSeconds??uo,c=o.absentPollsBeforeDelete??po;for(let[l,u]of e){if(o.trackedSessions&&!o.trackedSessions.has(l))continue;let d=t.get(l);if(!d){u.missingPolls=(u.missingPolls??0)+1,u.missingPolls>=c&&(e.delete(l),o.onSessionDeleted?.(l));continue}if(u.missingPolls=0,u.path=d.path,u.lastActivitySeconds===null){u.lastActivitySeconds=d.activitySeconds;continue}if(d.activitySeconds<u.lastActivitySeconds){u.lastActivitySeconds=d.activitySeconds,u.activeSinceSeconds=null;continue}if(d.activitySeconds>u.lastActivitySeconds){u.activeSinceSeconds===null&&(u.activeSinceSeconds=d.activitySeconds),u.lastActivitySeconds=d.activitySeconds;continue}if(u.activeSinceSeconds!==null&&r-u.lastActivitySeconds>=i){if(u.lastActivitySeconds-u.activeSinceSeconds<a){u.activeSinceSeconds=null;continue}u.path&&(n({sessionId:l,path:u.path}),u.activeSinceSeconds=null);}}}s(So,"updateSilenceStates");function wo(e){w.legacyCleaned.has(e)||(w.legacyCleaned.add(e),or("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:sr}).catch(()=>{}).finally(()=>cr(e)));}s(wo,"disableLegacySilenceMonitoring");function cr(e){try{unlinkSync(`/tmp/omniterm-silence-${e}`);}catch{}}s(cr,"removeLegacyStateFile");function bo(){return Math.floor(Date.now()/1e3)}s(bo,"nowSeconds");var we=[];function pt(e,t,r=Date.now()){we.push({op:e,timings:t,at:r}),we.length>100&&we.splice(0,we.length-100);}s(pt,"recordPerf");function lr(){return we.slice()}s(lr,"getRecent");var dr="https://us.i.posthog.com";function mt(e){if(!e)return false;let t=e.trim().toLowerCase();return t==="1"||t==="true"||t==="yes"||t==="on"}s(mt,"truthy");function vo(e){if(e===void 0)return false;let t=e.trim().toLowerCase();return t==="0"||t==="false"||t==="no"||t==="off"}s(vo,"offValue");function ur(e,t){let r=(e.OMNITERM_POSTHOG_KEY??"").trim(),n=(e.OMNITERM_POSTHOG_HOST??dr).trim()||dr;return {enabled:!(mt(e.DO_NOT_TRACK)||vo(e.OMNITERM_TELEMETRY)||mt(e.OMNITERM_TELEMETRY_DISABLED)||t||mt(e.CI)||e.NODE_ENV==="test"||r===""),key:r,host:n}}s(ur,"resolveTelemetryConfig");function Ce(){return v.join(Je,"telemetry.json")}s(Ce,"telemetryStatePath");function be(e=Ce()){try{let t=readFileSync(e,"utf-8"),r=JSON.parse(t);return r&&typeof r.installId=="string"&&r.installId?r:null}catch{return null}}s(be,"readState");function mr(e,t=Ce()){try{mkdirSync(v.dirname(t),{recursive:!0});let r=`${t}.${process.pid}.tmp`;writeFileSync(r,JSON.stringify(e,null,2),"utf-8"),renameSync(r,t);}catch{}}s(mr,"writeState");function fr(e=Ce()){let t=be(e);if(t)return t.installId;let r={installId:randomUUID()};if(mr(r,e),existsSync(e)){let n=be(e);if(n)return n.installId}return r.installId}s(fr,"getOrCreateInstallId");function gr(e=Ce()){let t=be(e);t&&(t.noticeShown||mr({...t,noticeShown:true},e));}s(gr,"markNoticeShown");var Q=null,ft="",B={enabled:false,key:"",host:""},hr=false;function Mo(){return {app_version:process.env.OMNITERM_VERSION||"unknown",os:Eo.platform(),node_version:process.version}}s(Mo,"baseContext");function $o(e){return {...Mo(),...e??{}}}s($o,"buildProps");function Ao(e){return !e.enabled||!e.hasClient?{enabled:false}:{enabled:true,key:e.key,host:e.host,distinctId:e.distinctId,appVersion:e.appVersion}}s(Ao,"projectClientTelemetryConfig");function yr(){return Ao({enabled:B.enabled,hasClient:Q!==null,key:B.key,host:B.host,distinctId:ft,appVersion:process.env.OMNITERM_VERSION||"unknown"})}s(yr,"getClientTelemetryConfig");function le(e,t){if(!(!B.enabled||!Q))try{Q.capture({distinctId:ft,event:e,properties:$o(t)});}catch{}}s(le,"capture");function Oo(){try{let e=be();if(!e||e.noticeShown)return;process.stderr.write("[omniterm] Pseudonymous usage + performance telemetry is enabled. Event payloads exclude names, contents, paths, repo names, and plugin ids. Opt out anytime with `omniterm telemetry off`, the Settings panel, or OMNITERM_TELEMETRY=0 / DO_NOT_TRACK=1.\n"),gr();}catch{}}s(Oo,"showFirstRunNoticeOnce");function Sr(){if(!hr){hr=true;try{let e=S().telemetryEnabled===!1;if(B=ur(process.env,e),!B.enabled)return;ft=fr(),Q=new PostHog(B.key,{host:B.host,flushAt:1,disableGeoip:!0}),Oo();}catch{B={enabled:false,key:"",host:""},Q=null;}}}s(Sr,"initTelemetry");async function wr(){let e=Q;if(Q=null,!e)return;let t,r=new Promise(n=>{t=setTimeout(n,2e3),t.unref?.();});try{await Promise.race([e.shutdown(2e3),r]);}catch{}finally{t&&clearTimeout(t);}}s(wr,"shutdownTelemetry");function br(e){le("server_started",{session_count:e});}s(br,"trackServerStarted");function vr(e){e&&pt("session_created",e),le("session_created",e);}s(vr,"trackSessionCreated");function xr(e){e&&pt("session_adopted",e),le("session_adopted",e);}s(xr,"trackSessionAdopted");function Tr(){le("session_closed");}s(Tr,"trackSessionClosed");function Rr(e){le("file_opened",{language:e});}s(Rr,"trackFileOpened");function kr(e){le("cleanup",e===void 0?void 0:{count:e});}s(kr,"trackCleanup");function Pr(){return lr()}s(Pr,"getRecentPerfMetrics");var Le=globalThis,H=Le.__omniterm_ttyd||new Map,Fe=Le.__omniterm_ttyd_stopped_listeners||new Set;Le.__omniterm_ttyd=H;Le.__omniterm_ttyd_stopped_listeners=Fe;function gt(e){for(let t of Fe)try{t(e);}catch(r){let n=r instanceof Error?r.message:String(r);console.error(`[ttyd] stopped listener failed for ${e}: ${n}`);}}s(gt,"notifyTtydStopped");function Ir(e){return Fe.add(e),()=>{Fe.delete(e);}}s(Ir,"onTtydStopped");function No(e,t,r){let n=r.terminalFontSize||18,o=r.terminalRenderer==="dom"?"dom":"webgl";return ["--writable","-i","127.0.0.1","-b",`/t/${e}/`,"-t",`rendererType=${o}`,"-t",`fontSize=${n}`,"-p",String(t),"--","bash","-c",`exec tmux attach-session -t ${e}`]}s(No,"buildTtydArgs");function ve(e,t){let r=No(e,t,S()),n=spawn("ttyd",r,{stdio:"ignore",detached:true});return n.once("error",o=>{console.error(`[ttyd] failed to spawn for ${e} on port ${t}: ${o.message}`),H.get(e)===n&&(H.delete(e),gt(e));}),n.once("exit",(o,i)=>{H.get(e)===n&&(H.delete(e),gt(e)),(o!==0||i)&&console.error(`[ttyd] exited for ${e} on port ${t}: code=${o??"null"} signal=${i??"null"}`);}),n.unref(),H.set(e,n),n}s(ve,"spawnTtyd");function Mr(e,t=3e3){return new Promise((r,n)=>{let o=Date.now()+t;function i(){let a=new Er.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(i,30);}),a.on("timeout",()=>{a.destroy(),Date.now()>=o?n(new Error(`ttyd not ready on port ${e} after ${t}ms`)):setTimeout(i,30);});}s(i,"attempt"),i();})}s(Mr,"waitForTtydReady");function xe(e){let t=H.get(e);if(t&&t.pid)try{process.kill(t.pid,"SIGTERM");}catch{}H.delete(e)&>(e);}s(xe,"stopTtyd");function ht(e){let t=H.get(e);if(!t||!t.pid)return false;try{return process.kill(t.pid,0),!0}catch{return false}}s(ht,"isTtydAlive");function Do(e,t){if(!e.trim())return [];let r=[];for(let n of e.split(` | ||
| `)){if(!n.includes("ttyd")||!n.includes("--writable"))continue;let o=n.match(/^\s*(\d+)/),i=n.match(/-b \/t\/([^/]+)\//);if(!o||!i)continue;let a=Number(o[1]),c=i[1];t.has(c)||r.push(a);}return r.sort((n,o)=>n-o)}s(Do,"getTtydPidsToKill");function yt(){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(` | ||
| `):[]);}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 Do(t,e))try{process.kill(n,"SIGTERM"),r++;}catch{}r>0&&(console.log(`[cleanup] killed ${r} stale ttyd processes`),kr(r));}catch{}}s(yt,"killOrphanedTtydProcesses");function $r(){return {readySessionNames:new Set,pendingReadiness:new Map,generations:new Map}}s($r,"createSessionTtydReadinessState");function P(e,t){t.readySessionNames.delete(e),t.pendingReadiness.delete(e),t.generations.set(e,(t.generations.get(e)??0)+1);}s(P,"clearSessionTtydReadiness");async function Ar(e,t,r,n){let o=e.tmuxName;if(n.readySessionNames.has(o)){if(r.isTtydAlive(o))return;P(o,n);}let i=n.pendingReadiness.get(o);if(i){if(await i,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)||(P(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 P(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;}s(Ar,"ensureSessionTtydReadyWithState");var Or=globalThis;function jr(){Or.__omniterm_recovered||(Or.__omniterm_recovered=true,yt());}s(jr,"recoverAll");var We=me?v.join(me,"omniterm-browser.js"):null,Lo=We!==null&&existsSync(We),Dr="/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin";function Cr(e){let t={OMNITERM_BROWSER_REGISTRY_URL:e};return Lo&&We&&me?(t.BROWSER=We,t.PATH=`${me}:${Dr}`):t.PATH=Dr,t}s(Cr,"buildTabEnv");function Fr(e,t){return {...Cr(e),...t}}s(Fr,"buildSessionEnv");var Y=globalThis,O=Y.__omniterm_sessions||new Map,St=Y.__omniterm_counters||new Map,F=Y.__omniterm_ttyd_readiness||$r();Y.__omniterm_sessions=O;Y.__omniterm_counters=St;Y.__omniterm_ttyd_readiness=F;var Wo={isTtydAlive:ht,spawnTtyd:ve,waitForTtydReady:Mr};Y.__omniterm_session_ttyd_listener_registered||(Ir(e=>P(e,F)),Y.__omniterm_session_ttyd_listener_registered=true);function Uo(e){let t=(St.get(e)||0)+1;for(;ge(`${e}-term-${t}`);)t++;return St.set(e,t),`${e}-term-${t}`}s(Uo,"nextId");jr();function Lr(e,t,r){let n=Date.now(),o=Uo(e),i=ot(),a=Date.now();Ne(o,t,Cr(r.registryUrlFor(o)));let c=Date.now(),l={id:o,worktreeId:e,tmuxName:o,port:i,createdAt:new Date().toISOString()};P(l.tmuxName,F),ve(l.tmuxName,l.port);let u=Date.now();O.set(o,l),ut(l.tmuxName);let d=Date.now();return console.log(`[session] ${o}: tmux=${c-a}ms ttyd=${u-c}ms total=${d-n}ms`),A("session-created",{sessionId:o,worktreeId:e,path:t}),l}s(Lr,"createSession");function X(e){let t=O.get(e);if(t){if(!ge(t.tmuxName)){P(t.tmuxName,F),xe(t.tmuxName),fe(t.port),O.delete(e),Se(t.tmuxName);return}return ht(t.tmuxName)||(P(t.tmuxName,F),ve(t.tmuxName,t.port)),t}}s(X,"getSession");async function K(e,t=3e3){await Ar(e,t,Wo,F);}s(K,"ensureSessionTtydReady");function Wr(e){let t=O.get(e);return t?(P(t.tmuxName,F),xe(t.tmuxName),fe(t.port),O.delete(e),Se(t.tmuxName),true):false}s(Wr,"unregisterSession");function J(e){let t=Array.from(O.values());return e?t.filter(r=>r.worktreeId===e):t}s(J,"listSessions");function Te(e){let t=O.get(e);return t?(P(t.tmuxName,F),xe(t.tmuxName),lt(t.tmuxName),fe(t.port),O.delete(e),Se(t.tmuxName),Tr(),A("session-closed",{sessionId:e,worktreeId:t.worktreeId}),true):false}s(Te,"deleteSession");function Ue(e){let t=J(e);for(let r of t)P(r.tmuxName,F),xe(r.tmuxName),lt(r.tmuxName),fe(r.port),O.delete(r.id),Se(r.tmuxName);return t.length>0&&A("session-closed",{worktreeId:e,sessionCount:t.length}),t.length}s(Ue,"deleteSessionsForWorktree");function wt(e,t){let r=Date.now(),n=ot(),o=Date.now();if(!t?.skipTmuxSetup){at();try{execFileSync("tmux",["set-option","-t",`=${e}:`,"mouse","on"],{stdio:"ignore"});}catch{}}let i=Date.now();P(e,F),ve(e,n);let a=Date.now(),c={id:e,worktreeId:"_orphan",tmuxName:e,port:n,createdAt:new Date().toISOString()};O.set(c.id,c),ut(c.tmuxName);let l=o-r,u=a-r;console.log(`[session] adopted ${e}: port=${n} allocate=${l}ms setup=${i-o}ms ttyd=${a-i}ms total=${u}ms skip=${t?.skipTmuxSetup?1:0}`),xr({total_ms:u,allocate_port_ms:l});let d=t?.knownCwd??"";if(!d)try{d=execFileSync("tmux",["display-message","-t",`=${e}:`,"-p","#{pane_current_path}"],{encoding:"utf-8"}).trim();}catch{}return A("session-adopted",{sessionId:e,worktreeId:"_orphan",path:d}),c}s(wt,"adoptSession");function Ho(e){let t=v.join(e,".git");try{let n=statSync(t);if(n.isDirectory())return v.join(t,"config");if(n.isFile()){let o=readFileSync(t,"utf-8").match(/^gitdir:\s*(.+)$/m);if(o){let i=v.resolve(e,o[1].trim()),a=v.join(i,"commondir"),c=existsSync(a)?v.resolve(i,readFileSync(a,"utf-8").trim()):i;return v.join(c,"config")}}}catch{}let r=v.join(e,"config");return existsSync(r)?r:null}s(Ho,"gitConfigPath");function zo(e){let t=false;for(let r of e.split(` | ||
| `)){let n=r.trim();if(n.startsWith("[")){t=/^\[remote\s+"origin"\]$/.test(n);continue}if(t){let o=n.match(/^url\s*=\s*(.+)$/);if(o)return o[1].trim()}}return ""}s(zo,"parseOriginUrl");var Ur=new Map;function Hr(e){let t=Ho(e);if(!t)return "";try{let{mtimeMs:r}=statSync(t),n=Ur.get(t);if(n&&n.mtimeMs===r)return n.url;let o=zo(readFileSync(t,"utf-8"));return Ur.set(t,{mtimeMs:r,url:o}),o}catch{return ""}}s(Hr,"getRemoteUrl");function vt(e){return existsSync(v.join(e,".git"))||existsSync(v.join(e,"HEAD"))}s(vt,"isGitRepo");function _(){return S().trackedRepos.filter(t=>existsSync(t)&&vt(t)).map(t=>({id:q(t),name:D(t),remoteUrl:Hr(t),path:t}))}s(_,"listRepos");function Re(e){let t=S();for(let r of t.trackedRepos)if(q(r)===e&&existsSync(r)&&vt(r))return {id:e,name:D(r),remoteUrl:Hr(r),path:r}}s(Re,"getRepo");function xt(e){let t=v.resolve(e);if(!existsSync(t))throw new Error(`Path "${t}" does not exist`);let r=S();if(vt(t))return r.trackedRepos.includes(t)||U({trackedRepos:[...r.trackedRepos,t]}),{type:"repo",path:t};{let n=r.trackedRepos.filter(i=>i!==t),o=r.trackedDirs.includes(t)?r.trackedDirs:[...r.trackedDirs,t];return (n.length!==r.trackedRepos.length||o!==r.trackedDirs)&&U({trackedRepos:n,trackedDirs:o}),{type:"dir",path:t}}}s(xt,"addLocalPath");function zr(e,t){let r=e.match(/\/([^/]+?)(\.git)?$/)?.[1]||"repo",n=t||v.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 U({trackedRepos:[...o.trackedRepos,n]}),{id:q(n),name:D(n),remoteUrl:e,path:n}}s(zr,"cloneRepo");function Vr(e){let t=Re(e);if(!t)throw new Error(`Repo "${e}" not found`);let r=0,n=0,o=[],i=R(t.path,e);for(let c of i)if(n+=Ue(c.id),!c.isMain)try{$e(t.path,c.path),r++;}catch{o.push(c.path);}let a=S();return U({trackedRepos:a.trackedRepos.filter(c=>c!==t.path)}),{worktreesRemoved:r,worktreesSkipped:o,sessionsKilled:n}}s(Vr,"removeRepo");var ee=Router();ee.get("/repos",(e,t)=>{t.json(_());});ee.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 i=n?xt(n):null,a=i?{id:q(i.path),name:D(i.path),...i}:zr(r,o);t.status(201).json(a);}catch(i){let a=i instanceof Error?i.message:String(i);t.status(a.includes("already")?409:400).json({error:a});}});ee.delete("/repos/:repoId",(e,t)=>{if(!Re(e.params.repoId)){t.status(404).json({error:"Repo not found"});return}let n=Vr(e.params.repoId);t.json({deleted:true,...n});});ee.get("/repos/:repoId/worktrees",(e,t)=>{let r=Re(e.params.repoId);if(!r){t.status(404).json({error:"Repo not found"});return}let n=R(r.path,e.params.repoId),o=De();t.json(n.map(i=>({...i,sessionCount:o.filter(a=>a.cwd===i.path).length})));});ee.post("/repos/:repoId/worktrees",async(e,t)=>{let r=Re(e.params.repoId);if(!r){t.status(404).json({error:"Repo not found"});return}let{branch:n,newBranch:o=true}=e.body||{};try{let i=qt(r.path,e.params.repoId,n,o);t.status(201).json(i);}catch(i){let a=i instanceof Error?i.message:String(i);t.status(a.includes("already has a worktree")?409:400).json({error:a});}});var Gr={".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"},qr={dockerfile:"dockerfile",makefile:"makefile"};[...new Set([...Object.values(Gr),...Object.values(qr)])];function Be(e,t){return Gr[e]||qr[t]||"text"}s(Be,"detectLanguage");function Go(e){return e instanceof Error?e.message:String(e)}s(Go,"errorMessage");function Yr(e,t,r,n){n(t.id),e.status(503).json({error:Go(r)});}s(Yr,"sendWorktreeSessionReadinessError");var z=Router();function Rt(e){for(let t of _()){let r=R(t.path,t.id).find(n=>n.id===e);if(r)return r.path}return null}s(Rt,"findWorktreePath");z.delete("/worktrees/:wtId",(e,t)=>{let{wtId:r}=e.params,n=e.query.force==="true"||e.body&&e.body.force===true;for(let o of _()){let i=R(o.path,o.id).find(a=>a.id===r);if(i){if(!n&&!i.isMain&&tt(i.path)){t.status(409).json({error:"Worktree has uncommitted changes",requiresForce:true});return}try{$e(o.path,i.path,{force:n});}catch(c){t.status(409).json({error:c instanceof Error?c.message:String(c),requiresForce:false});return}let a=Ue(r);t.json({deleted:true,sessionsKilled:a});return}}t.status(404).json({error:"Worktree not found"});});z.get("/worktrees/:wtId/status",(e,t)=>{let r=Rt(e.params.wtId);if(!r){t.status(404).json({error:"Worktree not found"});return}t.json({dirty:tt(r)});});z.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 _()){let i=R(o.path,o.id).find(a=>a.id===r);if(i){try{Yt(i.path,i.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"});});z.get("/worktrees/:wtId/sessions",(e,t)=>{let r=J(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:tr(n.tmuxName)})));});z.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",i=Lr(r,n,{registryUrlFor:s(a=>`http://${o}/t/${a}/registry`,"registryUrlFor")});try{await K(i);}catch(a){Yr(t,i,a,Te);return}t.status(201).json({id:i.id,worktreeId:i.worktreeId,port:i.port,url:`/t/${i.id}/`,createdAt:i.createdAt});}catch(o){let i=o instanceof Error?o.message:String(o);t.status(i.includes("No free ports")?503:500).json({error:i});}});z.get("/worktrees/:wtId/files",async(e,t)=>{let r=Rt(e.params.wtId);if(!r){t.status(404).json({error:"Worktree not found"});return}let n=e.query.path||"",o;try{o=Ze(r,n);}catch{t.status(403).json({error:"Path traversal detected"});return}try{let i=await readdir(o,{withFileTypes:!0}),a=await Promise.all(i.filter(c=>!c.name.startsWith(".")).map(async c=>{let l=c.isDirectory()?"directory":"file",u=0;if(l==="file")try{u=(await stat(v.join(o,c.name))).size;}catch{}return {name:c.name,type:l,size:u}}));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"});}});z.get("/worktrees/:wtId/file",async(e,t)=>{let r=Rt(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=Ze(r,n);}catch{t.status(403).json({error:"Path traversal detected"});return}try{let i=await stat(o);if(i.size>1024*1024){t.status(413).json({error:"File too large (>1MB)"});return}let a=await readFile(o,"utf-8"),c=v.extname(o).toLowerCase(),l=v.basename(o).toLowerCase(),u=Be(c,l);t.json({path:n,content:a,language:u,size:i.size});}catch{t.status(404).json({error:"File not found"});}});var ke=null,de=null,Pe=null,He=null,Kr=Jr(5e3),es=500;function ts(e){return e.replace(/\\/g,"/")}s(ts,"normalizeWatchPath");function rs(e){return e===".git"||e.startsWith(".git/")}s(rs,"isGitInternalPath");function Jr(e){let t=Number.NEGATIVE_INFINITY;return {shouldLog(r=Date.now()){return r-t<e?false:(t=r,true)}}}s(Jr,"createWatchEventLogLimiter");function Zr(e){let t=Qo();try{let r=readFileSync(v.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/playwright-report/","**/.playwright-artifacts-*"]),t}s(Zr,"buildWatchIgnoreMatcher");function ns(e,t,r){let n=ts(t);return n==="<unknown>"?false:n===".gitignore"||n.endsWith("/.gitignore")?(He=Zr(e),false):rs(n)?true:r?r.ignores(n):false}s(ns,"shouldIgnoreWatchPath");function Qr(e){if(de!==e){ze(),de=e,He=Zr(e);try{console.log(`[watcher] start watching path=${e}`),ke=watch(e,{recursive:!0},(t,r)=>{let n=typeof r=="string"&&r.length>0?r:"<unknown>";ns(e,n,He)||(Kr.shouldLog()&&console.log(`[watcher] fs event path=${e} event=${t} file=${n}`),Pe&&clearTimeout(Pe),Pe=setTimeout(()=>{console.log(`[watcher] broadcast files-changed path=${e}`),A("files-changed");},es));}),ke.on("error",t=>{let r=t instanceof Error?t.message:String(t);console.error(`[watcher] error path=${e} message=${r}`),ze();});}catch{console.error(`[watcher] failed to watch path=${e}`),de=null;}}}s(Qr,"watchWorkspace");function ze(){Pe&&clearTimeout(Pe),ke&&(de&&console.log(`[watcher] stop watching path=${de}`),ke.close(),ke=null),Kr=Jr(5e3),He=null,de=null;}s(ze,"stopWatching");var Ve=Router();Ve.get("/settings",(e,t)=>{t.json(S());});Ve.put("/settings",(e,t)=>{let r=e.body||{},n=U(r);n.activePath?Qr(n.activePath):ze(),t.json(n);});function E(){let e=S(),t=new Set([homedir()]);for(let r of e.trackedDirs)t.add(r);for(let r of _()){t.add(r.path);try{for(let n of R(r.path,r.id))t.add(n.path);}catch{}}return [...t]}s(E,"allowedRoots");var te=Router();te.get("/fs",async(e,t)=>{let r=e.query.path||"",n=e.query.mode||"list",o=M(r,E());if(!o){t.status(403).json({error:"Path outside allowed roots"});return}try{if(n==="read"){let c=await stat(o),l=v.extname(o).toLowerCase();if(c.size>1024*1024){t.status(413).json({error:"File too large (>1MB)"});return}let u=await readFile(o,"utf-8"),d=Be(l,v.basename(o).toLowerCase());Rr(d),t.json({path:r,content:u,language:d,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"});}});te.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=M(r,E());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(i){t.status(500).json({error:i instanceof Error?i.message:String(i)});}});te.get("/browse",async(e,t)=>{let r=e.query.path||homedir(),n=M(r,E());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}),i=(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(v.join(n,c.name))).isDirectory()?c.name:null}catch{return null}}))).filter(c=>!!c).sort(),a=existsSync(v.join(n,".git"))||existsSync(v.join(n,"HEAD"));t.json({path:n,parent:v.dirname(n),dirs:i,isGitRepo:a});}catch{t.status(403).json({error:"Cannot read directory"});}});te.get("/git/status",(e,t)=>{let r=e.query.cwd||"",n=M(r,E());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$/,""),i={};if(o)for(let a of o.split(` | ||
| `)){let c=a.substring(0,2),l=a.substring(3);i[l]=c;}t.json(i);}catch{t.json({});}});te.get("/git/show",(e,t)=>{let r=e.query.cwd||"",n=e.query.path||"",o=M(r,E());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 i=execFileSync("git",["show",`HEAD:${n}`],{cwd:o,encoding:"utf-8"});t.json({content:i});}catch{t.json({content:""});}});var Ge=Router(),fs=1024*1024,gs=25*1024*1024;Ge.get("/preview/markdown",async(e,t)=>{let r=e.query.path||"",n=M(r,E());if(!n){t.status(403).type("text/plain").send("Path outside allowed roots");return}try{if((await stat(n)).size>fs){t.status(413).type("text/plain").send("Markdown file too large (>1MB)");return}let i=await readFile(n,"utf-8"),a=marked.parse(i,{async:!1,gfm:!0,breaks:!1});t.type("text/html; charset=utf-8").send(hs(v.basename(n),a));}catch{t.status(404).type("text/plain").send("Not found");}});Ge.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=M(n,E());if(!o){t.status(403).type("text/plain").send("Path outside allowed roots");return}try{let i=await stat(o);if(!i.isFile()){t.status(404).type("text/plain").send("Not a file");return}if(i.size>gs){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 hs(e,t){return `<!DOCTYPE html> | ||
| <html lang="en"> | ||
@@ -23,3 +23,3 @@ <head> | ||
| <meta name="viewport" content="width=device-width, initial-scale=1"> | ||
| <title>${ts(e)}</title> | ||
| <title>${ys(e)}</title> | ||
| <style> | ||
@@ -86,9 +86,9 @@ :root { | ||
| </body> | ||
| </html>`}s(es,"renderShell");function ts(e){return e.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""").replace(/'/g,"'")}s(ts,"escapeHtml");function rs(e){return e.flatMap(t=>t.manifest?[t.manifest]:[])}s(rs,"buildManifest");function Yn(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);}}}s(Yn,"validatePluginManifests");function Kn(e){let t=Router(),r=rs(e);return t.get("/plugins",(n,o)=>{o.setHeader("Cache-Control","no-store"),o.json({plugins:r});}),t}s(Kn,"createManifestRouter");var bt=promisify(execFile),xt=5e3,Xn=128,Jn=4096,J=Router();function He(e){return {id:e.id,worktreeId:e.worktreeId,port:e.port,url:`/t/${e.id}/`,createdAt:e.createdAt}}s(He,"serializeSession");async function er(e,t){let r=t??q(e),n=r??pt(e);try{await Y(n);}catch(o){throw Mn(n.id),o}return {session:n,wasRegistered:!!r}}s(er,"adoptAndReady");async function tr(e,t,r){try{let{session:n,wasRegistered:o}=await er(e,r);t.status(o?200:201).json(He(n));}catch(n){t.status(503).json({error:n instanceof Error?n.message:String(n)});}}s(tr,"adoptOrAttach");J.get("/sessions/:sessionId",(e,t)=>{let r=q(e.params.sessionId);if(!r){t.status(404).json({error:"Session not found"});return}t.json(He(r));});J.delete("/sessions/:sessionId",(e,t)=>{if(!q(e.params.sessionId)){t.status(404).json({error:"Session not found"});return}xe(e.params.sessionId),t.json({deleted:true});});J.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=e.body?.initialCommand,o=typeof n=="string"&&n.trim()?n.trim():void 0;if(o!==void 0&&o.length>Jn){t.status(400).json({error:`initialCommand exceeds ${Jn} characters`});return}let i=e.body?.name,a=typeof i=="string"&&i.trim()?i:void 0;if(a!==void 0&&(a.length>Xn||!/^[A-Za-z0-9_-]+$/.test(a))){t.status(400).json({error:`name must be 1\u2013${Xn} characters of letters, digits, dashes, or underscores`});return}if(a){let p=q(a);if(p||me(a)){await tr(a,t,p);return}}let c=(r.split("/").pop()||"session").replace(/[^A-Za-z0-9_-]/g,"-"),l=Date.now().toString(36),u=a??`${c}-${l}`,d;try{let p=Date.now(),w=`http://${e.headers.host??"127.0.0.1:17716"}/t/${u}/registry`;$e(u,r,ut(w),{initialCommand:o});let D=Date.now();d=pt(u,{skipTmuxSetup:!0,knownCwd:r});let j=Date.now();await Y(d);let m=Date.now(),y=D-p,T=j-D,W=m-j,v=m-p;console.log(`[session] create-session: tmux=${y}ms adopt=${T}ms ttyd_ready=${W}ms total=${v}ms`),mn({total_ms:v,tmux_ms:y,adopt_ms:T,ttyd_ready_ms:W}),t.status(201).json(He(d));}catch(p){d&&xe(d.id),t.status(500).json({error:p instanceof Error?p.message:String(p)});}});J.get("/dir-sessions",async(e,t)=>{try{let r=e.query.cwd||"~",n=r==="~"?homedir():r,o;try{let{stdout:d}=await bt("tmux",["list-sessions","-F","#{session_name}|||#{session_path}"],{encoding:"utf-8",timeout:xt});o=d.trim();}catch{t.json([]);return}if(!o){t.json([]);return}let i=[...await nr(),...S().trackedDirs],a=s(d=>{if(!(d===n||d.startsWith(n+"/")))return !1;for(let p of i)if(p!==n&&p.length>n.length&&(d===p||d.startsWith(p+"/")))return !1;return !0},"isMostSpecificMatch"),c=o.split(` | ||
| `).map(d=>{let[p,h]=d.split("|||");return {name:p,path:h}}).filter(d=>a(d.path)),l=new Map(K().map(d=>[d.tmuxName,d])),u=await Promise.all(c.map(async d=>{try{let{session:p}=await er(d.name,l.get(d.name));return {...He(p),command:await cs(p.tmuxName)}}catch(p){let h=p instanceof Error?p.message:String(p);return console.error(`[session] failed to ready adopted session ${d.name}: ${h}`),null}}));t.json(u.filter(d=>d!==null));}catch(r){t.status(500).json({error:r instanceof Error?r.message:String(r)});}});J.post("/adopt-session/:sessionName",async(e,t)=>{await tr(e.params.sessionName,t);});var Zn=new Set,Qn=0;async function nr(){if(Date.now()-Qn<3e4)return Zn;let e=new Set,t=[];for(let{path:n}of _())e.add(n),t.push(n);let r=await Promise.all(t.map(n=>as(n)));for(let n of r)for(let o of n)e.add(o);return Zn=e,Qn=Date.now(),e}s(nr,"getRepoWorktreePaths");async function as(e){try{let{stdout:t}=await bt("git",["worktree","list","--porcelain"],{cwd:e,encoding:"utf-8",timeout:xt}),r=[];for(let n of t.split(` | ||
| `))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),[]}}s(as,"listWorktreePathsAsync");async function cs(e){try{let{stdout:t}=await bt("tmux",["display-message","-t",`=${e}:`,"-p","#{pane_title}|||#{pane_current_command}"],{encoding:"utf-8",timeout:xt}),[r="",n=""]=t.trim().split("|||"),o=r.trim();return nt(e,o)?o:n.trim()}catch{return ""}}s(cs,"getTmuxPaneTitleAsync");function ls(e){let{tmuxSessions:t,managedNames:r,repoWorktreePaths:n,trackedDirs:o,home:i}=e,a={[i]:[]};for(let c of o)!n.has(c)&&c!==i&&(a[c]=[]);for(let c of t)!r.has(c.name)&&!n.has(c.cwd)?(a[c.cwd]||(a[c.cwd]=[]),a[c.cwd].push({name:c.name,created:c.created})):a[c.cwd]&&a[c.cwd].push({name:c.name,created:c.created});return a}s(ls,"bucketDiscoveredSessions");J.get("/discover-sessions",async(e,t)=>{try{let r=ls({tmuxSessions:Ae(),managedNames:new Set(K().map(n=>n.tmuxName)),repoWorktreePaths:await nr(),trackedDirs:S().trackedDirs,home:homedir()});t.json(r);}catch(r){t.status(500).json({error:r instanceof Error?r.message:String(r)});}});var z=new Map;function te(e){return Array.isArray(e)?e[0]??"":e??""}s(te,"paramValue");function rr(e){let t=z.get(e);return t||(t={entries:new Map,nextId:1,emitter:new EventEmitter},z.set(e,t)),t}s(rr,"getOrCreate");function ps(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}`}}s(ps,"derivePageCdpUrlTemplate");function vt(e,t){return {id:e.id,label:e.label,startedAt:e.startedAt,browserCdpUrl:e.cdpUrl,pageCdpUrlTemplate:ps(e.cdpUrl),devtoolsFrontendUrl:t,pid:e.pid}}s(vt,"toView");function ze(e){try{return `http://${new URL(e.cdpUrl).host}/devtools/`}catch{return ""}}s(ze,"defaultDevtoolsFrontendUrl");function ms(e){try{return process.kill(e,0),!0}catch{return false}}s(ms,"isAlive");var fs=5e3,gs=setInterval(()=>{for(let[,e]of z)for(let[t,r]of e.entries)r.pid!==void 0&&!ms(r.pid)&&(e.entries.delete(t),e.emitter.emit("removed",t));},fs);gs.unref();function Tt(e){let t=z.get(e);t&&(t.emitter.removeAllListeners(),z.delete(e));}s(Tt,"cleanupTab");function Rt(e){let t=Router({mergeParams:true}),r=or.createProxyServer({changeOrigin:true,ws:true});return r.on("error",n=>console.error("[tab-registry proxy] error:",n.message)),t.post("/registry/browsers",(n,o)=>{let i=te(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 u=rr(i);for(let[h,w]of u.entries)if(w.cdpUrl===a){o.json({id:h,entry:w,deduped:true});return}let d=String(u.nextId++),p={id:d,cdpUrl:a,label:typeof c=="string"&&c?c:"browser",pid:typeof l=="number"?l:void 0,startedAt:Date.now()};u.entries.set(d,p),u.emitter.emit("added",p),o.json({id:d,entry:p});}),t.delete("/registry/browsers/:browserId",(n,o)=>{let i=te(n.params.tabId),a=te(n.params.browserId),c=z.get(i),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 i=te(n.params.tabId);o.setHeader("Cache-Control","no-store");let a=Array.from(z.get(i)?.entries.values()??[]);o.json({browsers:a.map(c=>vt(c,e.devtoolsFrontendUrl??ze(c)))});}),t.get("/events",(n,o)=>{let i=te(n.params.tabId),a=rr(i);o.setHeader("Content-Type","text/event-stream"),o.setHeader("Cache-Control","no-store"),o.setHeader("Connection","keep-alive"),o.flushHeaders?.();let c=s(p=>{o.write(`data: ${JSON.stringify(p)} | ||
| </html>`}s(hs,"renderShell");function ys(e){return e.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""").replace(/'/g,"'")}s(ys,"escapeHtml");function ws(e){return e.flatMap(t=>t.manifest?[t.manifest]:[])}s(ws,"buildManifest");function on(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);}}}s(on,"validatePluginManifests");function sn(e){let t=Router(),r=ws(e);return t.get("/plugins",(n,o)=>{o.setHeader("Cache-Control","no-store"),o.json({plugins:r});}),t}s(sn,"createManifestRouter");var _t=promisify(execFile),Et=5e3,an=128,cn=4096,ln=32,dn=4096,un=256;function Ts(e){if(e==null)return {};if(typeof e!="object"||Array.isArray(e))return {error:"env must be an object mapping variable names to string values"};let t=Object.entries(e);if(t.length>ln)return {error:`env accepts at most ${ln} variables`};let r={};for(let[n,o]of t){if(n.length>un)return {error:`env variable names must be at most ${un} characters`};try{Oe(n);}catch(i){return {error:i instanceof C?i.message:String(i)}}if(typeof o!="string")return {error:`env.${n} must be a string`};if(o.length>dn)return {error:`env.${n} exceeds ${dn} characters`};if(o.includes("\0"))return {error:`env.${n} must not contain a NUL byte`};r[n]=o;}return {env:r}}s(Ts,"parseSessionEnv");var V=Router();function qe(e){return {id:e.id,worktreeId:e.worktreeId,port:e.port,url:`/t/${e.id}/`,createdAt:e.createdAt}}s(qe,"serializeSession");async function fn(e,t){let r=t??X(e),n=r??wt(e);try{await K(n);}catch(o){throw Wr(n.id),o}return {session:n,wasRegistered:!!r}}s(fn,"adoptAndReady");async function gn(e,t,r){try{let{session:n,wasRegistered:o}=await fn(e,r);t.status(o?200:201).json(qe(n));}catch(n){t.status(503).json({error:n instanceof Error?n.message:String(n)});}}s(gn,"adoptOrAttach");V.get("/session-env",(e,t)=>{t.json({passthrough:je()});});V.get("/sessions/:sessionId",(e,t)=>{let r=X(e.params.sessionId);if(!r){t.status(404).json({error:"Session not found"});return}t.json(qe(r));});V.delete("/sessions/:sessionId",(e,t)=>{if(!X(e.params.sessionId)){t.status(404).json({error:"Session not found"});return}Te(e.params.sessionId),t.json({deleted:true});});V.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=e.body?.initialCommand,o=typeof n=="string"&&n.trim()?n.trim():void 0;if(o!==void 0&&o.length>cn){t.status(400).json({error:`initialCommand exceeds ${cn} characters`});return}let i=e.body?.name,a=typeof i=="string"&&i.trim()?i:void 0;if(a!==void 0&&(a.length>an||!/^[A-Za-z0-9_-]+$/.test(a))){t.status(400).json({error:`name must be 1\u2013${an} characters of letters, digits, dashes, or underscores`});return}let c=Ts(e.body?.env);if("error"in c){t.status(400).json({error:c.error});return}if(a){let m=X(a);if(m||ge(a)){await gn(a,t,m);return}}let l=(r.split("/").pop()||"session").replace(/[^A-Za-z0-9_-]/g,"-"),u=Date.now().toString(36),d=a??`${l}-${u}`,p;try{let m=Date.now(),N=`http://${e.headers.host??"127.0.0.1:17716"}/t/${d}/registry`;Ne(d,r,Fr(N,c.env),{initialCommand:o});let se=Date.now();p=wt(d,{skipTmuxSetup:!0,knownCwd:r});let I=Date.now();await K(p);let f=Date.now(),y=se-m,T=I-se,W=f-I,x=f-m;console.log(`[session] create-session: tmux=${y}ms adopt=${T}ms ttyd_ready=${W}ms total=${x}ms`),vr({total_ms:x,tmux_ms:y,adopt_ms:T,ttyd_ready_ms:W}),t.status(201).json(qe(p));}catch(m){p&&Te(p.id),t.status(500).json({error:m instanceof Error?m.message:String(m)});}});V.get("/dir-sessions",async(e,t)=>{try{let r=e.query.cwd||"~",n=r==="~"?homedir():r,o;try{let{stdout:d}=await _t("tmux",["list-sessions","-F","#{session_name}|||#{session_path}"],{encoding:"utf-8",timeout:Et});o=d.trim();}catch{t.json([]);return}if(!o){t.json([]);return}let i=[...await hn(),...S().trackedDirs],a=s(d=>{if(!(d===n||d.startsWith(n+"/")))return !1;for(let p of i)if(p!==n&&p.length>n.length&&(d===p||d.startsWith(p+"/")))return !1;return !0},"isMostSpecificMatch"),c=o.split(` | ||
| `).map(d=>{let[p,m]=d.split("|||");return {name:p,path:m}}).filter(d=>a(d.path)),l=new Map(J().map(d=>[d.tmuxName,d])),u=await Promise.all(c.map(async d=>{try{let{session:p}=await fn(d.name,l.get(d.name));return {...qe(p),command:await ks(p.tmuxName)}}catch(p){let m=p instanceof Error?p.message:String(p);return console.error(`[session] failed to ready adopted session ${d.name}: ${m}`),null}}));t.json(u.filter(d=>d!==null));}catch(r){t.status(500).json({error:r instanceof Error?r.message:String(r)});}});V.post("/adopt-session/:sessionName",async(e,t)=>{await gn(e.params.sessionName,t);});var pn=new Set,mn=0;async function hn(){if(Date.now()-mn<3e4)return pn;let e=new Set,t=[];for(let{path:n}of _())e.add(n),t.push(n);let r=await Promise.all(t.map(n=>Rs(n)));for(let n of r)for(let o of n)e.add(o);return pn=e,mn=Date.now(),e}s(hn,"getRepoWorktreePaths");async function Rs(e){try{let{stdout:t}=await _t("git",["worktree","list","--porcelain"],{cwd:e,encoding:"utf-8",timeout:Et}),r=[];for(let n of t.split(` | ||
| `))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),[]}}s(Rs,"listWorktreePathsAsync");async function ks(e){try{let{stdout:t}=await _t("tmux",["display-message","-t",`=${e}:`,"-p","#{pane_title}|||#{pane_current_command}"],{encoding:"utf-8",timeout:Et}),[r="",n=""]=t.trim().split("|||"),o=r.trim();return dt(e,o)?o:n.trim()}catch{return ""}}s(ks,"getTmuxPaneTitleAsync");function Ps(e){let{tmuxSessions:t,managedNames:r,repoWorktreePaths:n,trackedDirs:o,home:i}=e,a={[i]:[]};for(let c of o)!n.has(c)&&c!==i&&(a[c]=[]);for(let c of t)!r.has(c.name)&&!n.has(c.cwd)?(a[c.cwd]||(a[c.cwd]=[]),a[c.cwd].push({name:c.name,created:c.created})):a[c.cwd]&&a[c.cwd].push({name:c.name,created:c.created});return a}s(Ps,"bucketDiscoveredSessions");V.get("/discover-sessions",async(e,t)=>{try{let r=Ps({tmuxSessions:De(),managedNames:new Set(J().map(n=>n.tmuxName)),repoWorktreePaths:await hn(),trackedDirs:S().trackedDirs,home:homedir()});t.json(r);}catch(r){t.status(500).json({error:r instanceof Error?r.message:String(r)});}});var G=new Map;function re(e){return Array.isArray(e)?e[0]??"":e??""}s(re,"paramValue");function yn(e){let t=G.get(e);return t||(t={entries:new Map,nextId:1,emitter:new EventEmitter},G.set(e,t)),t}s(yn,"getOrCreate");function Is(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}`}}s(Is,"derivePageCdpUrlTemplate");function It(e,t){return {id:e.id,label:e.label,startedAt:e.startedAt,browserCdpUrl:e.cdpUrl,pageCdpUrlTemplate:Is(e.cdpUrl),devtoolsFrontendUrl:t,pid:e.pid}}s(It,"toView");function Ye(e){try{return `http://${new URL(e.cdpUrl).host}/devtools/`}catch{return ""}}s(Ye,"defaultDevtoolsFrontendUrl");function Ms(e){try{return process.kill(e,0),!0}catch{return false}}s(Ms,"isAlive");var $s=5e3,As=setInterval(()=>{for(let[,e]of G)for(let[t,r]of e.entries)r.pid!==void 0&&!Ms(r.pid)&&(e.entries.delete(t),e.emitter.emit("removed",t));},$s);As.unref();function Mt(e){let t=G.get(e);t&&(t.emitter.removeAllListeners(),G.delete(e));}s(Mt,"cleanupTab");function $t(e){let t=Router({mergeParams:true}),r=Sn.createProxyServer({changeOrigin:true,ws:true});return r.on("error",n=>console.error("[tab-registry proxy] error:",n.message)),t.post("/registry/browsers",(n,o)=>{let i=re(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 u=yn(i);for(let[m,b]of u.entries)if(b.cdpUrl===a){o.json({id:m,entry:b,deduped:true});return}let d=String(u.nextId++),p={id:d,cdpUrl:a,label:typeof c=="string"&&c?c:"browser",pid:typeof l=="number"?l:void 0,startedAt:Date.now()};u.entries.set(d,p),u.emitter.emit("added",p),o.json({id:d,entry:p});}),t.delete("/registry/browsers/:browserId",(n,o)=>{let i=re(n.params.tabId),a=re(n.params.browserId),c=G.get(i),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 i=re(n.params.tabId);o.setHeader("Cache-Control","no-store");let a=Array.from(G.get(i)?.entries.values()??[]);o.json({browsers:a.map(c=>It(c,e.devtoolsFrontendUrl??Ye(c)))});}),t.get("/events",(n,o)=>{let i=re(n.params.tabId),a=yn(i);o.setHeader("Content-Type","text/event-stream"),o.setHeader("Cache-Control","no-store"),o.setHeader("Connection","keep-alive"),o.flushHeaders?.();let c=s(p=>{o.write(`data: ${JSON.stringify(p)} | ||
| `);},"write");for(let p of a.entries.values())c({type:"added",data:vt(p,e.devtoolsFrontendUrl??ze(p))});let l=s(p=>c({type:"added",data:vt(p,e.devtoolsFrontendUrl??ze(p))}),"onAdded"),u=s(p=>c({type:"removed",data:{id:p}}),"onRemoved");a.emitter.on("added",l),a.emitter.on("removed",u);let d=setInterval(()=>{o.write(`: hb | ||
| `);},"write");for(let p of a.entries.values())c({type:"added",data:It(p,e.devtoolsFrontendUrl??Ye(p))});let l=s(p=>c({type:"added",data:It(p,e.devtoolsFrontendUrl??Ye(p))}),"onAdded"),u=s(p=>c({type:"removed",data:{id:p}}),"onRemoved");a.emitter.on("added",l),a.emitter.on("removed",u);let d=setInterval(()=>{o.write(`: hb | ||
| `);},25e3);n.on("close",()=>{a.emitter.off("added",l),a.emitter.off("removed",u),clearInterval(d);});}),t.all("/b/:browserId/devtools/{*rest}",(n,o)=>{let i=te(n.params.tabId),a=te(n.params.browserId),c=z.get(i)?.entries.get(a);if(!c){o.status(404).send("Browser not found");return}let l=e.devtoolsFrontendUrl??ze(c),u;try{u=new URL(l);}catch{o.status(502).send("Invalid devtoolsFrontendUrl");return}let d=n.originalUrl??n.url??"",p=d.indexOf("/devtools/"),h=p>=0?d.slice(p+10):"",w=u.pathname.endsWith("/")?u.pathname:`${u.pathname}/`;n.url=`${w}${h}`,r.web(n,o,{target:`${u.protocol}//${u.host}`,ignorePath:false});}),t}s(Rt,"createTabRegistryRouter");function kt(e,t,r,n){let o=e.url??"",i=n.basePath.replace(/[.+?^${}()|[\]\\]/g,"\\$&"),a=new RegExp(`^${i}/([^/]+)/b/([^/]+)/browser(?:$|\\?)`),c=new RegExp(`^${i}/([^/]+)/b/([^/]+)/page/([^/?]+)(?:$|\\?)`),l=o.match(a),u=o.match(c),d=l??u;if(!d)return false;let p=d[1],h=d[2];if(!p||!h)return t.destroy(),true;let w=z.get(p)?.entries.get(h);if(!w)return t.destroy(),true;let D;try{D=new URL(w.cdpUrl);}catch{return t.destroy(),true}let j=u?D.pathname.replace(/\/devtools\/browser\/[^/]+$/,`/devtools/page/${u[3]}`):D.pathname;e.headers.origin="http://127.0.0.1",e.url=j+(D.search||"");let m=or.createProxyServer({changeOrigin:true,ws:true});return m.on("error",()=>{try{t.destroy();}catch{}}),m.ws(e,t,r,{target:`ws://${D.host}`}),true}s(kt,"handleCdpUpgrade");function Pt(e={}){return {type:"terminal",label:"Terminal",proxyPrefix:"/t/:tabId",createRouter(t){return Rt({devtoolsFrontendUrl:e.devtoolsFrontendUrl})},handleUpgrade(t,r,n){return kt(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){Tt(t);},render:{type:"component",componentPath:"./components/TerminalTabView"}}}s(Pt,"createTerminalPlugin");var sr=3e3;async function bs(e,t,r){let n=e.params.sessionId,o=Array.isArray(n)?n[0]:n,i=o?r.getSession(o):void 0;if(!i){t.status(404).send("Session not found");return}try{await r.ensureSessionTtydReady(i);}catch(a){r.onReadyError?.(i,a),t.status(503).send("Terminal proxy not ready");return}r.proxyWeb(e,t,`http://127.0.0.1:${i.port}`);}s(bs,"handleTtydHttpProxy");function xs(e,t,r,n){let i=(e.url||"").match(/^\/t\/([^/]+)\//);if(!i)return false;let a=n.getSession(i[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)}s(xs,"handleTtydUpgrade");function Et(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 m of ["OMNITERM_PORT","OMNITERM_HOST","OMNITERM_TTYD_PORT_MIN","OMNITERM_TTYD_PORT_MAX"])delete process.env[m];let n=ne();n.disable("x-powered-by"),n.use(ne.json()),e.statusHandler&&n.get("/status",e.statusHandler),e.configureApp?.(n),n.use("/api",Q),n.use("/api",J),n.use("/api",H),n.use("/api",Ue),n.use("/api",ee),n.use("/api",Be);let o;e.devtoolsBundleDir&&(n.use("/devtools",ne.static(e.devtoolsBundleDir,{fallthrough:false})),o=`http://127.0.0.1:${t}/devtools/`,console.error(`[devtools] Serving bundle from ${e.devtoolsBundleDir} at /devtools/`)),e.pdfjsDistDir&&n.use("/pdfjs",ne.static(e.pdfjsDistDir,{maxAge:"1h"}));let i=[...e.excludeDefaults?[]:[Pt({devtoolsFrontendUrl:o})],...e.plugins??[]];Yn(i);let a={broadcast:s((m,y)=>$(m,y),"broadcast"),workspaceRoot:s(()=>null,"workspaceRoot"),allowedRoots:E,confinePath:s((m,y)=>I(m,y??E()),"confinePath"),repos:s(()=>_(),"repos"),worktrees:s((m,y)=>R(m,y),"worktrees")};for(let m of i)m.proxyPrefix?n.use(m.proxyPrefix,m.createRouter(a)):n.use(m.createRouter(a));n.use("/api",Kn(i)),n.get("/api/metrics/perf",(m,y)=>{y.json(Sn());}),n.get("/api/telemetry",(m,y)=>{y.json(ln());}),n.get("/api/events",(m,y)=>{y.setHeader("Content-Type","text/event-stream"),y.setHeader("Cache-Control","no-cache"),y.setHeader("Connection","keep-alive"),y.flushHeaders(),qt(y);});let c=or.createProxyServer({ws:true});c.on("error",m=>{console.error("[proxy] error:",m.message);}),n.all("/t/:sessionId/{*rest}",async(m,y)=>{await bs(m,y,{getSession:q,ensureSessionTtydReady:s(T=>Y(T,sr),"ensureSessionTtydReady"),proxyWeb:s((T,W,v)=>{c.web(T,W,{target:v});},"proxyWeb"),onReadyError:s((T,W)=>{let v=W instanceof Error?W.message:String(W);console.error(`[proxy] ttyd not ready for ${T.id}: ${v}`);},"onReadyError")});});let l=x.dirname(fileURLToPath(import.meta.url)),u=x.resolve(l,"..","dist","client"),d=x.resolve(l,"..","client"),p=e.clientDir??(existsSync(x.join(u,"index.html"))?u:d),h=x.join(p,"index.html"),w=x.resolve(l,"..","public");if(!existsSync(h)){let m=[`[omniterm] missing client bundle at ${p}`,"Reinstall the published package with `npm install -g @omniterm/host@latest`.","When developing from the monorepo, run `pnpm --filter @omniterm/host build` before starting the server."].join(` | ||
| `);throw new Error(m)}let D=readFileSync(h,"utf-8");n.use("/assets",ne.static(x.join(p,"assets"),{immutable:true,maxAge:"1y"})),n.use(ne.static(p)),existsSync(w)&&n.use(ne.static(w)),n.get("/{*rest}",(m,y)=>{y.type("html").send(D);});let j=hs.createServer(n);return j.on("upgrade",(m,y,T)=>{m.url||"";for(let v of i)if(v.handleUpgrade?.(m,y,T))return;xs(m,y,T,{getSession:q,ensureSessionTtydReady:s(v=>Y(v,sr),"ensureSessionTtydReady"),proxyWs:s((v,de,Ge,pr)=>{c.ws(v,de,Ge,{target:pr});},"proxyWs"),onReadyError:s((v,de)=>{let Ge=de instanceof Error?de.message:String(de);console.error(`[proxy] ttyd websocket not ready for ${v.id}: ${Ge}`);},"onReadyError")})||y.destroy();}),j.on("error",m=>{m.code==="EADDRINUSE"&&(console.error(`[omniterm] Port ${t} is already in use.`),process.exit(1)),console.error("[omniterm] server error:",m);}),process.on("uncaughtException",m=>{console.error("[omniterm] uncaughtException:",m);}),process.on("unhandledRejection",m=>{console.error("[omniterm] unhandledRejection:",m);}),new Promise((m,y)=>{j.listen(t,r,()=>{console.log(`[omniterm] Listening on http://${r}:${t}`);try{dn(),pn(K().length);}catch{}m({shutdown:s(async()=>{await new Promise(T=>j.close(()=>T())),await un().catch(()=>{});},"shutdown")});}),j.once("error",y);})}s(Et,"startServer");var F=class extends Error{static{s(this,"PluginSpecError");}spec;constructor(t,r){super(r),this.name="PluginSpecError",this.spec=t;}};function It(e){let t=[];for(let r=0;r<e.length;r++){if(e[r]!=="--plugin")continue;let n=e[r+1];if(!n||n.startsWith("--"))throw new F(null,"--plugin requires a value (a filesystem path or package name)");t.push(n),r++;}return t}s(It,"parsePluginSpecs");function Mt(e,t,r){let n=t.default??t.plugin,o;try{o=typeof n=="function"?n():n;}catch(a){throw new F(e,`factory threw (${a instanceof Error?a.message:String(a)})`)}let i=o;if(!i||typeof i.type!="string"||typeof i.createRouter!="function")throw new F(e,"module must export a TabTypePlugin (default export or no-arg factory)");if(r.has(i.type))throw new F(e,`duplicate tab type "${i.type}"`);return r.add(i.type),i}s(Mt,"validatePluginModule");var lr=createRequire(import.meta.url),ke,ar=process.env.OMNITERM_DEVTOOLS_DIR?.trim();ar&&(ke=x.resolve(ar),existsSync(x.join(ke,"inspector.html"))||(console.error(`[omniterm] OMNITERM_DEVTOOLS_DIR=${ke} has no inspector.html; falling back to each browser's own DevTools frontend.`),ke=void 0));var dr;try{dr=x.dirname(lr.resolve("pdfjs-dist/package.json"));}catch{console.error("[omniterm] pdfjs-dist not found; the in-app PDF viewer will be unavailable.");}async function Rs(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=lr.resolve(e);}r=pathToFileURL(n).href;}return import(r)}s(Rs,"importPluginModule");function $t(e,t){console.error(e?`[omniterm] --plugin ${e}: ${t}`:`[omniterm] ${t}`),process.exit(1);}s($t,"fail");async function ks(e){let t=[],r=new Set;for(let n of e){let o;try{o=await Rs(n);}catch(i){$t(n,`could not resolve/import (${i instanceof Error?i.message:String(i)})`);}try{t.push(Mt(n,o,r));}catch(i){throw i instanceof F&&$t(i.spec??n,i.message),i}}return t}s(ks,"loadPlugins");var Ps=Number(process.env.OMNITERM_PORT??process.env.PORT)||17717,ur;try{ur=It(process.argv.slice(2));}catch(e){throw e instanceof F&&$t(e.spec,e.message),e}var _s=await ks(ur);await Et({port:Ps,devtoolsBundleDir:ke,pdfjsDistDir:dr,plugins:_s}); | ||
| `);},25e3);n.on("close",()=>{a.emitter.off("added",l),a.emitter.off("removed",u),clearInterval(d);});}),t.all("/b/:browserId/devtools/{*rest}",(n,o)=>{let i=re(n.params.tabId),a=re(n.params.browserId),c=G.get(i)?.entries.get(a);if(!c){o.status(404).send("Browser not found");return}let l=e.devtoolsFrontendUrl??Ye(c),u;try{u=new URL(l);}catch{o.status(502).send("Invalid devtoolsFrontendUrl");return}let d=n.originalUrl??n.url??"",p=d.indexOf("/devtools/"),m=p>=0?d.slice(p+10):"",b=u.pathname.endsWith("/")?u.pathname:`${u.pathname}/`;n.url=`${b}${m}`,r.web(n,o,{target:`${u.protocol}//${u.host}`,ignorePath:false});}),t}s($t,"createTabRegistryRouter");function At(e,t,r,n){let o=e.url??"",i=n.basePath.replace(/[.+?^${}()|[\]\\]/g,"\\$&"),a=new RegExp(`^${i}/([^/]+)/b/([^/]+)/browser(?:$|\\?)`),c=new RegExp(`^${i}/([^/]+)/b/([^/]+)/page/([^/?]+)(?:$|\\?)`),l=o.match(a),u=o.match(c),d=l??u;if(!d)return false;let p=d[1],m=d[2];if(!p||!m)return t.destroy(),true;let b=G.get(p)?.entries.get(m);if(!b)return t.destroy(),true;let N;try{N=new URL(b.cdpUrl);}catch{return t.destroy(),true}let se=u?N.pathname.replace(/\/devtools\/browser\/[^/]+$/,`/devtools/page/${u[3]}`):N.pathname;e.headers.origin="http://127.0.0.1",e.url=se+(N.search||"");let I=Sn.createProxyServer({changeOrigin:true,ws:true});return I.on("error",()=>{try{t.destroy();}catch{}}),I.ws(e,t,r,{target:`ws://${N.host}`}),true}s(At,"handleCdpUpgrade");function Ot(e={}){return {type:"terminal",label:"Terminal",proxyPrefix:"/t/:tabId",createRouter(t){return $t({devtoolsFrontendUrl:e.devtoolsFrontendUrl})},handleUpgrade(t,r,n){return At(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){Mt(t);},render:{type:"component",componentPath:"./components/TerminalTabView"}}}s(Ot,"createTerminalPlugin");var wn=3e3;async function Cs(e,t,r){let n=e.params.sessionId,o=Array.isArray(n)?n[0]:n,i=o?r.getSession(o):void 0;if(!i){t.status(404).send("Session not found");return}try{await r.ensureSessionTtydReady(i);}catch(a){r.onReadyError?.(i,a),t.status(503).send("Terminal proxy not ready");return}r.proxyWeb(e,t,`http://127.0.0.1:${i.port}`);}s(Cs,"handleTtydHttpProxy");function Fs(e,t,r,n){let i=(e.url||"").match(/^\/t\/([^/]+)\//);if(!i)return false;let a=n.getSession(i[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)}s(Fs,"handleTtydUpgrade");function Nt(e={}){let t=parseInt(process.env.OMNITERM_PORT??String(e.port??17717),10),r=process.env.OMNITERM_HOST??e.host??"0.0.0.0",n=process.env.OMNITERM_ENV_PASSTHROUGH;Jt(n!==void 0?st(n):e.envPassthrough??[]);for(let f of ["OMNITERM_PORT","OMNITERM_HOST","OMNITERM_TTYD_PORT_MIN","OMNITERM_TTYD_PORT_MAX","OMNITERM_ENV_PASSTHROUGH"])delete process.env[f];let o=ne();o.disable("x-powered-by"),o.use(ne.json()),e.statusHandler&&o.get("/status",e.statusHandler),e.configureApp?.(o),o.use("/api",ee),o.use("/api",V),o.use("/api",z),o.use("/api",Ve),o.use("/api",te),o.use("/api",Ge);let i;e.devtoolsBundleDir&&(o.use("/devtools",ne.static(e.devtoolsBundleDir,{fallthrough:false})),i=`http://127.0.0.1:${t}/devtools/`,console.error(`[devtools] Serving bundle from ${e.devtoolsBundleDir} at /devtools/`)),e.pdfjsDistDir&&o.use("/pdfjs",ne.static(e.pdfjsDistDir,{maxAge:"1h"}));let a=[...e.excludeDefaults?[]:[Ot({devtoolsFrontendUrl:i})],...e.plugins??[]];on(a);let c={broadcast:s((f,y)=>A(f,y),"broadcast"),workspaceRoot:s(()=>null,"workspaceRoot"),allowedRoots:E,confinePath:s((f,y)=>M(f,y??E()),"confinePath"),repos:s(()=>_(),"repos"),worktrees:s((f,y)=>R(f,y),"worktrees")};for(let f of a)f.proxyPrefix?o.use(f.proxyPrefix,f.createRouter(c)):o.use(f.createRouter(c));o.use("/api",sn(a)),o.get("/api/metrics/perf",(f,y)=>{y.json(Pr());}),o.get("/api/telemetry",(f,y)=>{y.json(yr());}),o.get("/api/events",(f,y)=>{y.setHeader("Content-Type","text/event-stream"),y.setHeader("Cache-Control","no-cache"),y.setHeader("Connection","keep-alive"),y.flushHeaders(),rr(y);});let l=Sn.createProxyServer({ws:true});l.on("error",f=>{console.error("[proxy] error:",f.message);}),o.all("/t/:sessionId/{*rest}",async(f,y)=>{await Cs(f,y,{getSession:X,ensureSessionTtydReady:s(T=>K(T,wn),"ensureSessionTtydReady"),proxyWeb:s((T,W,x)=>{l.web(T,W,{target:x});},"proxyWeb"),onReadyError:s((T,W)=>{let x=W instanceof Error?W.message:String(W);console.error(`[proxy] ttyd not ready for ${T.id}: ${x}`);},"onReadyError")});});let u=v.dirname(fileURLToPath(import.meta.url)),d=v.resolve(u,"..","dist","client"),p=v.resolve(u,"..","client"),m=e.clientDir??(existsSync(v.join(d,"index.html"))?d:p),b=v.join(m,"index.html"),N=v.resolve(u,"..","public");if(!existsSync(b)){let f=[`[omniterm] missing client bundle at ${m}`,"Reinstall the published package with `npm install -g @omniterm/host@latest`.","When developing from the monorepo, run `pnpm --filter @omniterm/host build` before starting the server."].join(` | ||
| `);throw new Error(f)}let se=readFileSync(b,"utf-8");o.use("/assets",ne.static(v.join(m,"assets"),{immutable:true,maxAge:"1y"})),o.use(ne.static(m)),existsSync(N)&&o.use(ne.static(N)),o.get("/{*rest}",(f,y)=>{y.type("html").send(se);});let I=Os.createServer(o);return I.on("upgrade",(f,y,T)=>{f.url||"";for(let x of a)if(x.handleUpgrade?.(f,y,T))return;Fs(f,y,T,{getSession:X,ensureSessionTtydReady:s(x=>K(x,wn),"ensureSessionTtydReady"),proxyWs:s((x,pe,Ke,_n)=>{l.ws(x,pe,Ke,{target:_n});},"proxyWs"),onReadyError:s((x,pe)=>{let Ke=pe instanceof Error?pe.message:String(pe);console.error(`[proxy] ttyd websocket not ready for ${x.id}: ${Ke}`);},"onReadyError")})||y.destroy();}),I.on("error",f=>{f.code==="EADDRINUSE"&&(console.error(`[omniterm] Port ${t} is already in use.`),process.exit(1)),console.error("[omniterm] server error:",f);}),process.on("uncaughtException",f=>{console.error("[omniterm] uncaughtException:",f);}),process.on("unhandledRejection",f=>{console.error("[omniterm] unhandledRejection:",f);}),new Promise((f,y)=>{I.listen(t,r,()=>{console.log(`[omniterm] Listening on http://${r}:${t}`);try{Sr(),br(J().length);}catch{}f({shutdown:s(async()=>{await new Promise(T=>I.close(()=>T())),await wr().catch(()=>{});},"shutdown")});}),I.once("error",y);})}s(Nt,"startServer");var L=class extends Error{static{s(this,"PluginSpecError");}spec;constructor(t,r){super(r),this.name="PluginSpecError",this.spec=t;}};function Dt(e){let t=[];for(let r=0;r<e.length;r++){if(e[r]!=="--plugin")continue;let n=e[r+1];if(!n||n.startsWith("--"))throw new L(null,"--plugin requires a value (a filesystem path or package name)");t.push(n),r++;}return t}s(Dt,"parsePluginSpecs");function Ct(e,t,r){let n=t.default??t.plugin,o;try{o=typeof n=="function"?n():n;}catch(a){throw new L(e,`factory threw (${a instanceof Error?a.message:String(a)})`)}let i=o;if(!i||typeof i.type!="string"||typeof i.createRouter!="function")throw new L(e,"module must export a TabTypePlugin (default export or no-arg factory)");if(r.has(i.type))throw new L(e,`duplicate tab type "${i.type}"`);return r.add(i.type),i}s(Ct,"validatePluginModule");var Tn=createRequire(import.meta.url),_e,vn=process.env.OMNITERM_DEVTOOLS_DIR?.trim();vn&&(_e=v.resolve(vn),existsSync(v.join(_e,"inspector.html"))||(console.error(`[omniterm] OMNITERM_DEVTOOLS_DIR=${_e} has no inspector.html; falling back to each browser's own DevTools frontend.`),_e=void 0));var Rn;try{Rn=v.dirname(Tn.resolve("pdfjs-dist/package.json"));}catch{console.error("[omniterm] pdfjs-dist not found; the in-app PDF viewer will be unavailable.");}async function Us(e){let t=e.startsWith(".")||e.startsWith("/")||e.startsWith("file:"),r;if(t){let n=e.startsWith("file:")?fileURLToPath(e):v.resolve(process.cwd(),e);r=pathToFileURL(n).href;}else {let n;try{n=createRequire(v.join(process.cwd(),"noop.js")).resolve(e);}catch{n=Tn.resolve(e);}r=pathToFileURL(n).href;}return import(r)}s(Us,"importPluginModule");function Xe(e,t){console.error(e?`[omniterm] --plugin ${e}: ${t}`:`[omniterm] ${t}`),process.exit(1);}s(Xe,"fail");async function Bs(e){let t=[],r=new Set;for(let n of e){let o;try{o=await Us(n);}catch(i){Xe(n,`could not resolve/import (${i instanceof Error?i.message:String(i)})`);}try{t.push(Ct(n,o,r));}catch(i){throw i instanceof L&&Xe(i.spec??n,i.message),i}}return t}s(Bs,"loadPlugins");var Hs=Number(process.env.OMNITERM_PORT??process.env.PORT)||17717,kn;try{kn=Dt(process.argv.slice(2));}catch(e){throw e instanceof L&&Xe(e.spec,e.message),e}var Pn;try{Pn=it(process.argv.slice(2));}catch(e){throw e instanceof C&&Xe(null,`--env-passthrough: ${e.message}`),e}var zs=await Bs(kn);await Nt({port:Hs,devtoolsBundleDir:_e,pdfjsDistDir:Rn,plugins:zs,envPassthrough:Pn}); |
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
AI-detected potential code anomaly
Supply chain riskAI has identified unusual behaviors that may pose a security risk.
Found 2 instances
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
AI-detected potential code anomaly
Supply chain riskAI has identified unusual behaviors that may pose a security risk.
Found 2 instances
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
1425108
0.35%4703
0.36%286
16.26%45
-2.17%83
2.47%