omnish 1.2.4 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,115 +1,104 @@
1
1
  #!/usr/bin/env node
2
- import Fp from"node:dns";import ct from"node:fs";import ns from"node:path";import Al from"node:os";import En from"node:fs";import Bl from"node:crypto";import Mr from"node:fs";import Hl from"node:os";import K from"node:path";function Dl(){let e=process.env.OMNISH_HOME?.trim();if(e)return K.resolve(e);let t=Hl.homedir(),n=K.join(t,".omnish"),r=K.join(t,".whatslive");try{if(Mr.existsSync(n))return n;if(Mr.existsSync(r))return r}catch{}return n}var N=Dl(),J=K.join(N,"auth"),Ae=K.join(N,"jobs"),os=K.join(N,"apps"),ss=K.join(N,"logs"),fe=K.join(ss,"gateway.log"),ne=K.join(N,"gateway.pid"),xt=K.join(N,"gateway-control.json"),zt=K.join(N,"config-ui.json"),_e=K.join(N,"ui.json"),$n=K.join(N,"ui-server.json"),P=K.join(N,"config.json"),Cn=K.join(N,"shortcuts.json"),Rn=K.join(N,"recipes.json"),Mn=K.join(N,"recipes-user.json"),Be=K.join(N,"cowork"),Tr=K.join(Be,"tasks.json"),Ir=K.join(Be,"pending-runs.json"),is=K.join(Be,"completions.sqlite");function qt(e){let t=Bl.createHash("sha1").update(e,"utf8").digest("hex").slice(0,8);return K.join(os,t)}function H(e){Mr.mkdirSync(e,{recursive:!0,mode:448})}function z(){H(N),H(J),H(Ae),H(os),H(ss),H(Be)}var ls=/^(\d+)(?::\d+)?@s\.whatsapp\.net$/i,cs=/^(\d+)@c\.us$/i,us=/^(\d+)@lid$/i;function ds(e){let t=e.trim();for(;;){let n=t;if(t=t.replace(/^whatsapp:/i,"").trim(),t===n)return t}}function jl(e){let t=ds(e);if(!t.toLowerCase().endsWith("@g.us"))return!1;let r=t.slice(0,t.length-5);return!r||r.includes("@")?!1:/^[0-9]+(-[0-9]+)*$/.test(r)}function Ul(e){let t=e.match(ls);if(t)return t[1]??null;let n=e.match(cs);if(n)return n[1]??null;let r=e.match(us);return r?r[1]??null:null}function as(e){let t=e.replace(/\D/g,"");return t?`+${t}`:""}function Qt(e){return`${e.replace(/\D/g,"")}@s.whatsapp.net`}function G(e){let t=ds(e);if(!t||jl(t))return null;if(ls.test(t)||cs.test(t)||us.test(t)){let r=Ul(t);if(!r)return null;let o=as(r);return o.length>1?o:null}if(t.includes("@"))return null;let n=as(t);return n.length>1?n:null}function Tn(e){return e.map(t=>String(t).trim()).filter(t=>!!t).map(t=>t==="*"?t:G(t)).filter(t=>!!t)}function ps(e){let t=new Set;for(let n of e)n!=="*"&&t.add(n);return t}function ue(e){let t=e.trim();for(;;){let n=t.toLowerCase();if(n.startsWith("tg:")){t=t.slice(3).trimStart();continue}if(n.startsWith("telegram:")){t=t.slice(9).trimStart();continue}break}return/^\d+$/.test(t)?t:null}function ms(e){let t=new Set;for(let n of e){let r=ue(String(n));r&&t.add(r)}return t}function Er(e){let t=e.trim();if(!t)return null;let n=t.toLowerCase();if(n.startsWith("tg:")||n.startsWith("telegram:")){let o=ue(t);return o?{kind:"tg",id:o}:null}let r=G(t);return r?{kind:"wa",normalized:r}:null}var F={gatewayMode:"whatsapp",telegramBotToken:"",telegramAllowFrom:[],allowFrom:[],commandPrefix:"!",syncTimeoutMs:3e4,syncMaxBytes:32768,jobLogTailLines:80,shell:"/bin/bash",appsCols:120,appsRows:40,appsFlushMs:300,appsMinIntervalMs:800,appsMaxFlushBytes:8192,appsMaxSessions:5,appsMaxSessionsTotal:20,appsMaxWaChars:3500,appsLogTailLines:80,appsSubmitDelayMs:50,appsClearInput:!0,appsClearInputDelayMs:20,appsClearInputSequence:"^A,^K",fileSendMaxBytes:0,fileReceiveMaxBytes:0,fileInboxSubdir:"inbox",fileReceiveRootMode:"downloads",fileReceiveRootPath:"",recipesAllowDangerousBuiltins:!1,recipesMaxTaskChars:0,recipesMacroDefaultCommand:'claude -p "$OMNISH_TASK"',clusterEnabled:!1,clusterLabel:"",clusterRole:"secondary",clusterSenderBindings:{},serviceInstallFromChat:!1,updateCheckEnabled:!1,updateCheckIntervalMs:864e5,updateCheckPackageName:"omnish",updateInfoUrl:"",chatLlmFallbackEnabled:!1,chatLlmShellCommand:"",chatLlmTimeoutMs:12e4,chatLlmMaxInputChars:16e3,chatLlmMaxOutputChars:24e3,chatLlmNeedsTty:!1,chatLlmWorkDir:""};function Wl(e){return e==="downloads"||e==="omnishData"||e==="sessionCwd"||e==="processCwd"||e==="fixed"?e:F.fileReceiveRootMode}function Gl(e){return e==="primary"?"primary":"secondary"}function Jl(e){if(!e||typeof e!="object")return{};let t={};for(let[n,r]of Object.entries(e)){if(typeof r!="string")continue;let o=r.trim();if(!o)continue;let s=n.trim(),i=s.toLowerCase();if(i.startsWith("wa:")){let l=G(s.slice(3));l&&(t[`wa:${l}`]=o.slice(0,64));continue}if(i.startsWith("tg:")||i.startsWith("telegram:")){let l=ue(s);l&&(t[`tg:${l}`]=o.slice(0,64));continue}let a=G(s);a&&(t[`wa:${a}`]=o.slice(0,64))}return t}function In(e){let t=typeof e.appsFlushMs=="number"&&e.appsFlushMs>=0?e.appsFlushMs:F.appsFlushMs,n=e.gatewayMode==="telegram"||e.gatewayMode==="both"||e.gatewayMode==="whatsapp"?e.gatewayMode:F.gatewayMode,r=typeof e.telegramBotToken=="string"?e.telegramBotToken:F.telegramBotToken,o=Array.isArray(e.telegramAllowFrom)?[...new Set(e.telegramAllowFrom.map(s=>ue(String(s))).filter(s=>!!s))].sort():F.telegramAllowFrom;return{...F,...e,gatewayMode:n,telegramBotToken:r,telegramAllowFrom:o,allowFrom:Array.isArray(e.allowFrom)?Tn(e.allowFrom.map(String)).filter(s=>s!=="*"):F.allowFrom,commandPrefix:(()=>{let s=typeof e.commandPrefix=="string"&&e.commandPrefix.length>0?e.commandPrefix:F.commandPrefix;return s==="! "?"!":s})(),syncTimeoutMs:typeof e.syncTimeoutMs=="number"&&e.syncTimeoutMs>0?e.syncTimeoutMs:F.syncTimeoutMs,syncMaxBytes:typeof e.syncMaxBytes=="number"&&e.syncMaxBytes>0?e.syncMaxBytes:F.syncMaxBytes,jobLogTailLines:typeof e.jobLogTailLines=="number"&&e.jobLogTailLines>0?e.jobLogTailLines:F.jobLogTailLines,shell:typeof e.shell=="string"&&e.shell.length>0?e.shell:F.shell,appsCols:typeof e.appsCols=="number"&&e.appsCols>0&&e.appsCols<=500?Math.floor(e.appsCols):F.appsCols,appsRows:typeof e.appsRows=="number"&&e.appsRows>0&&e.appsRows<=200?Math.floor(e.appsRows):F.appsRows,appsFlushMs:t,appsMinIntervalMs:typeof e.appsMinIntervalMs=="number"&&e.appsMinIntervalMs>=0?e.appsMinIntervalMs:F.appsMinIntervalMs,appsMaxFlushBytes:typeof e.appsMaxFlushBytes=="number"&&e.appsMaxFlushBytes>256?Math.floor(e.appsMaxFlushBytes):F.appsMaxFlushBytes,appsMaxSessions:typeof e.appsMaxSessions=="number"&&e.appsMaxSessions>0?Math.min(50,Math.floor(e.appsMaxSessions)):F.appsMaxSessions,appsMaxSessionsTotal:typeof e.appsMaxSessionsTotal=="number"&&e.appsMaxSessionsTotal>0?Math.min(200,Math.floor(e.appsMaxSessionsTotal)):F.appsMaxSessionsTotal,appsMaxWaChars:typeof e.appsMaxWaChars=="number"&&e.appsMaxWaChars>256?Math.floor(e.appsMaxWaChars):F.appsMaxWaChars,appsLogTailLines:typeof e.appsLogTailLines=="number"&&e.appsLogTailLines>0?Math.min(500,Math.floor(e.appsLogTailLines)):F.appsLogTailLines,appsSubmitDelayMs:typeof e.appsSubmitDelayMs=="number"&&e.appsSubmitDelayMs>=0?Math.min(500,Math.floor(e.appsSubmitDelayMs)):F.appsSubmitDelayMs,appsClearInput:typeof e.appsClearInput=="boolean"?e.appsClearInput:F.appsClearInput,appsClearInputDelayMs:typeof e.appsClearInputDelayMs=="number"&&e.appsClearInputDelayMs>=0?Math.min(200,Math.floor(e.appsClearInputDelayMs)):F.appsClearInputDelayMs,appsClearInputSequence:typeof e.appsClearInputSequence=="string"&&e.appsClearInputSequence.length>0?e.appsClearInputSequence.slice(0,200):F.appsClearInputSequence,fileSendMaxBytes:typeof e.fileSendMaxBytes=="number"&&e.fileSendMaxBytes>=0?e.fileSendMaxBytes===0?0:Math.min(2e9,Math.floor(e.fileSendMaxBytes)):F.fileSendMaxBytes,fileReceiveMaxBytes:typeof e.fileReceiveMaxBytes=="number"&&e.fileReceiveMaxBytes>=0?e.fileReceiveMaxBytes===0?0:Math.min(2e9,Math.floor(e.fileReceiveMaxBytes)):F.fileReceiveMaxBytes,fileInboxSubdir:typeof e.fileInboxSubdir=="string"&&e.fileInboxSubdir.length>0&&e.fileInboxSubdir.replace(/[/\\]/g,"").slice(0,80)||F.fileInboxSubdir,fileReceiveRootMode:Wl(e.fileReceiveRootMode),fileReceiveRootPath:typeof e.fileReceiveRootPath=="string"?e.fileReceiveRootPath.trim().slice(0,4096):F.fileReceiveRootPath,recipesAllowDangerousBuiltins:typeof e.recipesAllowDangerousBuiltins=="boolean"?e.recipesAllowDangerousBuiltins:F.recipesAllowDangerousBuiltins,recipesMaxTaskChars:typeof e.recipesMaxTaskChars=="number"&&e.recipesMaxTaskChars>=0?e.recipesMaxTaskChars===0?0:Math.min(Number.MAX_SAFE_INTEGER,Math.floor(e.recipesMaxTaskChars)):F.recipesMaxTaskChars,recipesMacroDefaultCommand:typeof e.recipesMacroDefaultCommand=="string"&&e.recipesMacroDefaultCommand.trim().length>0?e.recipesMacroDefaultCommand.trim().slice(0,4096):F.recipesMacroDefaultCommand,clusterEnabled:typeof e.clusterEnabled=="boolean"?e.clusterEnabled:F.clusterEnabled,clusterLabel:typeof e.clusterLabel=="string"?e.clusterLabel.trim().slice(0,128):F.clusterLabel,clusterRole:Gl(e.clusterRole),clusterSenderBindings:Jl(e.clusterSenderBindings),serviceInstallFromChat:typeof e.serviceInstallFromChat=="boolean"?e.serviceInstallFromChat:F.serviceInstallFromChat,updateCheckEnabled:typeof e.updateCheckEnabled=="boolean"?e.updateCheckEnabled:F.updateCheckEnabled,updateCheckIntervalMs:typeof e.updateCheckIntervalMs=="number"&&e.updateCheckIntervalMs>0?Math.min(6048e5,Math.max(36e5,Math.floor(e.updateCheckIntervalMs))):F.updateCheckIntervalMs,updateCheckPackageName:typeof e.updateCheckPackageName=="string"&&e.updateCheckPackageName.trim().length>0?e.updateCheckPackageName.trim().slice(0,214):F.updateCheckPackageName,updateInfoUrl:typeof e.updateInfoUrl=="string"?e.updateInfoUrl.trim().slice(0,2048):F.updateInfoUrl,chatLlmFallbackEnabled:typeof e.chatLlmFallbackEnabled=="boolean"?e.chatLlmFallbackEnabled:F.chatLlmFallbackEnabled,chatLlmShellCommand:typeof e.chatLlmShellCommand=="string"?e.chatLlmShellCommand.trim().slice(0,8192):F.chatLlmShellCommand,chatLlmTimeoutMs:typeof e.chatLlmTimeoutMs=="number"&&e.chatLlmTimeoutMs>0?Math.min(9e5,Math.floor(e.chatLlmTimeoutMs)):F.chatLlmTimeoutMs,chatLlmMaxInputChars:typeof e.chatLlmMaxInputChars=="number"&&e.chatLlmMaxInputChars>0?Math.min(5e5,Math.floor(e.chatLlmMaxInputChars)):F.chatLlmMaxInputChars,chatLlmMaxOutputChars:typeof e.chatLlmMaxOutputChars=="number"&&e.chatLlmMaxOutputChars>0?Math.min(2e6,Math.floor(e.chatLlmMaxOutputChars)):F.chatLlmMaxOutputChars,chatLlmNeedsTty:typeof e.chatLlmNeedsTty=="boolean"?e.chatLlmNeedsTty:F.chatLlmNeedsTty,chatLlmWorkDir:typeof e.chatLlmWorkDir=="string"?e.chatLlmWorkDir.trim().slice(0,4096):F.chatLlmWorkDir}}function R(){if(z(),!En.existsSync(P)){let e=In({});return He(e),e}try{let e=En.readFileSync(P,"utf8"),t=JSON.parse(e);return In(t)}catch{return In({})}}function He(e){z();let t=In(e);En.writeFileSync(P,JSON.stringify(t,null,2)+`
3
- `,{mode:384})}function An(e){let t=Er(e);if(!t)throw new Error("Invalid entry. Use +E164 (WhatsApp) or tg:<user_id> (Telegram).");let n=R();if(t.kind==="wa"){if(t.normalized==="*")throw new Error("Wildcards are not allowed.");let r=new Set(n.allowFrom);r.add(t.normalized),n.allowFrom=[...r].sort()}else{let r=new Set(n.telegramAllowFrom);r.add(t.id),n.telegramAllowFrom=[...r].sort()}return He(n),n}function Ln(e){let t=Er(e);if(!t)throw new Error("Invalid entry. Use +E164 (WhatsApp) or tg:<user_id> (Telegram).");let n=R();return t.kind==="wa"?n.allowFrom=n.allowFrom.filter(r=>r!==t.normalized):n.telegramAllowFrom=n.telegramAllowFrom.filter(r=>ue(r)!==t.id),He(n),n}function oe(e){let t=typeof process.env.TELEGRAM_BOT_TOKEN=="string"?process.env.TELEGRAM_BOT_TOKEN.trim():"";return t||(e.telegramBotToken??"").trim()}function Ze(e){let t=e.trim();return/^\d{6,}:[A-Za-z0-9_-]{20,}$/.test(t)}function vt(e){let t=R();return t.telegramBotToken=e.trim(),He(t),R()}function On(e){let t=e.trim().toLowerCase();return t==="whatsapp"||t==="wa"||t==="w"?"whatsapp":t==="telegram"||t==="tg"||t==="t"?"telegram":t==="both"||t==="all"||t==="b"?"both":null}function Pn(e){let t=R();return t.gatewayMode=e,He(t),R()}function Fn(e){let t=R();return t.clusterEnabled=e,He(t),R()}function Le(){try{return En.readdirSync(J).length>0}catch{return!1}}import fs from"node:fs";import gs from"node:path";var zl=new Set(["jpg","jpeg","png","gif","webp","bmp","tif","tiff","heic","avif"]),ql=new Set(["mp4","mov","webm","mkv","avi","m4v","3gp"]),Ql=new Set(["mp3","ogg","opus","wav","m4a","flac","aac","wma"]),Nn={jpg:"image/jpeg",jpeg:"image/jpeg",png:"image/png",gif:"image/gif",webp:"image/webp",bmp:"image/bmp",tif:"image/tiff",tiff:"image/tiff",heic:"image/heic",avif:"image/avif",mp4:"video/mp4",mov:"video/quicktime",webm:"video/webm",mkv:"video/x-matroska",avi:"video/x-msvideo",m4v:"video/x-m4v","3gp":"video/3gpp",mp3:"audio/mpeg",ogg:"audio/ogg",opus:"audio/opus",wav:"audio/wav",m4a:"audio/mp4",flac:"audio/flac",aac:"audio/aac",wma:"audio/x-ms-wma",pdf:"application/pdf",zip:"application/zip",gz:"application/gzip",txt:"text/plain",json:"application/json",xml:"application/xml",csv:"text/csv"};function Yl(e){let t=e.replace(/^\./,"").toLowerCase();return zl.has(t)?{category:"image",mimetype:Nn[t]??"image/jpeg"}:ql.has(t)?{category:"video",mimetype:Nn[t]??"video/mp4"}:Ql.has(t)?{category:"audio",mimetype:Nn[t]??"audio/mpeg"}:{category:"document",mimetype:Nn[t]??"application/octet-stream"}}function et(e,t){let n;try{n=fs.realpathSync(e)}catch{return{error:"File not found or unreadable."}}let r;try{r=fs.lstatSync(n)}catch{return{error:"Cannot stat file."}}if(!r.isFile())return{error:"Not a regular file (directories and special files are not supported)."};if(t>0&&r.size>t)return{error:`File too large (max ${t} bytes).`};let o=gs.basename(n),s=gs.extname(n).slice(1),{category:i,mimetype:a}=Yl(s);return{absPath:n,category:i,mimetype:a,displayName:o}}import hs from"node:path";import{glob as Vl,stat as Kl}from"node:fs/promises";function _n(e){let t=e.trim();if(!t||t.startsWith("-- ")||t==="--")return null;let n=t.indexOf(" -- "),r,o;return n!==-1?(r=t.slice(0,n).trim(),o=t.slice(n+4).trim()||void 0):r=t,(r.startsWith('"')&&r.endsWith('"')||r.startsWith("'")&&r.endsWith("'"))&&(r=r.slice(1,-1)),r.trim()?{selectorPart:r,caption:o}:null}function Xl(e){return/[*?[]/.test(e)}function Zl(e){return e.split(",").map(t=>t.trim()).filter(t=>t.length>0)}async function Yt(e,t){let n=Zl(t),r=new Set,o=[];for(let s of n){if(Xl(s)){for await(let a of Vl(s,{cwd:e,withFileTypes:!1})){let l=hs.resolve(e,a);r.has(l)||(r.add(l),o.push(l))}continue}let i=hs.resolve(e,s);r.has(i)||(r.add(i),o.push(i))}return o}async function Vt(e){for(let t of e)try{if(!(await Kl(t)).isFile())return{ok:!1,error:`Not a file: ${t}`}}catch{return{ok:!1,error:`File not found: ${t}`}}return{ok:!0}}import nc from"node:os";import Hn from"node:path";import Ar from"node:fs";import ws from"node:os";import tt from"node:path";var bs=tt.join(N,"sessions.json"),$t=new Map;function ks(){return{cwd:tt.resolve(ws.homedir())}}function ec(e){return e.startsWith("wa:")||e.startsWith("tg:")?e:`wa:${e}`}function tc(){try{let e=Ar.readFileSync(bs,"utf8"),t=JSON.parse(e);for(let[n,r]of Object.entries(t)){if(!r||typeof r!="object")continue;let o=ec(n),i={cwd:typeof r.cwd=="string"&&r.cwd.length>0?tt.resolve(r.cwd):ks().cwd};r.fileReceiveRoot==="sessionCwd"&&(i.fileReceiveRoot="sessionCwd"),$t.set(o,i)}}catch{}}function Ss(){H(N);let e={};for(let[t,n]of $t){let r={cwd:n.cwd};n.fileReceiveRoot==="sessionCwd"&&(r.fileReceiveRoot="sessionCwd"),e[t]=r}Ar.writeFileSync(bs,JSON.stringify(e,null,2)+`
4
- `,{mode:384})}var ys=!1;function Lr(){ys||(tc(),ys=!0)}function q(e){Lr();let t=$t.get(e);return t||(t=ks(),$t.set(e,t)),t}function Bn(e,t){Lr();let n=tt.resolve(t),r=q(e);r.cwd=n,$t.set(e,r),Ss()}function xs(e){return q(e).fileReceiveRoot==="sessionCwd"?"sessionCwd":"default"}function Or(e,t){Lr();let n=q(e);t==="default"?delete n.fileReceiveRoot:n.fileReceiveRoot="sessionCwd",$t.set(e,n),Ss()}function vs(e){let t=e.trim();if(/[&|;\n]/.test(t)||!t.startsWith("cd"))return null;if(t==="cd")return{kind:"home"};if(t.length>2&&t[2]!==" "&&t[2]!==" ")return null;let n=t.slice(2).trimStart();return n?{kind:"path",value:n}:{kind:"home"}}function $s(e,t){if(t.kind==="home")return tt.resolve(ws.homedir());let n=t.value.replace(/^['"]|['"]$/g,"");return tt.isAbsolute(n)?tt.normalize(n):tt.resolve(e,n)}function Cs(e){try{return Ar.statSync(e).isDirectory()?{ok:!0}:{ok:!1,error:`Not a directory: ${e}`}}catch(t){return{ok:!1,error:String(t)}}}var rc="Omnish";function Rs(){return Hn.join(nc.homedir(),"Downloads",rc)}function Ct(e,t){let n=q(t);if(n.fileReceiveRoot==="sessionCwd")return n.cwd;switch(e.fileReceiveRootMode){case"downloads":return Rs();case"omnishData":return Hn.join(N,e.fileInboxSubdir);case"sessionCwd":return n.cwd;case"processCwd":return process.cwd();case"fixed":{let r=(e.fileReceiveRootPath??"").trim();if(!r)throw new Error('fileReceiveRootPath is required when fileReceiveRootMode is "fixed".');if(!Hn.isAbsolute(r))throw new Error('fileReceiveRootPath must be an absolute path when fileReceiveRootMode is "fixed".');return Hn.resolve(r)}default:return Rs()}}import Je from"node:fs";import Ts from"node:path";import Kt from"node:process";var ut="\x1B";function oc(e){return e.replace(/\u001B\[[\d;]*m/g,"")}function Dn(e){return oc(e).length}function Pr(e,t,n,r,o=2){let s=Math.max(0,t-Dn(n));return`${e}${n}${" ".repeat(s)}${" ".repeat(o)}${r}`}function We(e,t,n,r){if(n.length===0)return[];let o=n.map(i=>r(i.left)),s=Math.max(...o.map(Dn));return n.map((i,a)=>Pr(t,s,o[a],v(e,i.right)))}var nt={primary:"#b4ff24",foreground:"#eef2f4",muted:"#8a9199",border:"#2a3139",error:"#ef4444",warn:"#a0e614",warnAlt:"#8cce04"};function sc(e){let t=e.replace(/^#/,"");return t.length!==6||!/^[0-9a-fA-F]+$/.test(t)?null:{r:parseInt(t.slice(0,2),16),g:parseInt(t.slice(2,4),16),b:parseInt(t.slice(4,6),16)}}function rt(e){let t=sc(e);return t?`${ut}[38;2;${t.r};${t.g};${t.b}m`:""}function jn(e){if(process.env.NO_COLOR!==void 0&&process.env.NO_COLOR!=="")return!1;let t=process.env.FORCE_COLOR;return t==="1"||t==="true"?!0:t==="0"||t==="false"?!1:e.isTTY===!0}function Ge(e,t,n){return!t||!jn(e)?n:`${t}${n}${ut}[0m`}function X(e,t){return Ge(e,`${ut}[1m`,t)}function ic(e,t){return jn(e)?`${rt(nt.foreground)}${ut}[1m${t}${ut}[0m`:t}function se(e,t){return Ge(e,`${ut}[2m`,t)}function ie(e,t){return Ge(e,`${rt(nt.primary)}${ut}[1m`,t)}function Z(e,t){return Ge(e,rt(nt.primary),t)}function v(e,t){return Ge(e,rt(nt.foreground),t)}function b(e,t){return Ge(e,rt(nt.muted),t)}function ac(e,t){return Ge(e,rt(nt.border),t)}function Un(e,t){return Ge(e,rt(nt.error),t)}function xe(e,t){return Ge(e,rt(nt.warn),t)}function dt(e,t=60,n="\u2500"){let r=n.repeat(Math.max(1,t));return ac(e,r)}function ae(e,t){return jn(e)?`${`${b(e,"[")}${Z(e,"omnish")}${b(e,"]")}`} ${t}`:`[omnish] ${t}`}function O(e,t){return jn(e)?`${`${Un(e,"[omnish]")}`} ${t}`:`[omnish] ${t}`}function Ms(e,t){let n=[];for(let r of e)switch(r.kind){case"title":n.push("",ie(t,r.text),"");break;case"sub":n.push("",ic(t,r.text),"");break;case"gap":n.push("");break;case"p":n.push(v(t,r.text));break;case"bullet":n.push(`${b(t,"\u2022")} ${v(t,r.text)}`);break}return n.join(`
5
- `).replace(/^\n+/,"").trimEnd()}function Wn(e){return(e&4)!==0}function Fr(e){return(e&2)!==0}var Is={error:3,warn:2,info:1};function Es(e,t){let n=Is[t];return e.filter(r=>Is[r.severity]>=n)}function ot(e,t={}){let n=[],r=t.gatewayMode??e.gatewayMode,o=r==="whatsapp"||r==="both",s=r==="telegram"||r==="both";if(e.allowFrom.includes("*")&&n.push({severity:"error",code:"allow-wildcard",message:'allowFrom contains "*" \u2014 this must never be used; remove it from config immediately.',detail:`Edit ${P} and delete wildcard entries.`,fixHint:`Edit ${P} and remove any "*" entries from allowFrom.`}),o&&e.allowFrom.length===0&&n.push({severity:"warn",code:"allow-wa-empty",message:"allowFrom is empty \u2014 no WhatsApp identity can run commands.",detail:"Add your number: omnish allow +<E164>",fixHint:"omnish allow +<your_E164_number>"}),s&&e.telegramAllowFrom.length===0&&n.push({severity:"warn",code:"allow-tg-empty",message:"telegramAllowFrom is empty \u2014 no Telegram user can run commands.",detail:"Add your user id: omnish allow tg:<id>",fixHint:"omnish allow tg:<your_telegram_user_id>"}),e.recipesAllowDangerousBuiltins&&n.push({severity:"warn",code:"recipes-dangerous",message:"recipesAllowDangerousBuiltins is enabled \u2014 built-in recipe helpers may add permissive Claude Code flags.",fixHint:`Set recipesAllowDangerousBuiltins to false in ${P} unless you trust every recipe.`}),e.serviceInstallFromChat&&n.push({severity:"warn",code:"service-install-from-chat",message:"serviceInstallFromChat is enabled \u2014 allowlisted users can run /service install and write user-level systemd or LaunchAgent units.",fixHint:`Set serviceInstallFromChat to false in ${P} unless you trust every allowlisted identity.`}),!Ts.isAbsolute(e.shell))n.push({severity:"warn",code:"shell-not-absolute",message:`Shell path is not absolute: ${e.shell}`,fixHint:`Set "shell" to an absolute path (e.g. /bin/bash) in ${P}.`});else try{Je.existsSync(e.shell)||n.push({severity:"error",code:"shell-missing",message:`Configured shell does not exist: ${e.shell}`,fixHint:`Install the shell or update "shell" in ${P} to a valid binary.`})}catch{n.push({severity:"warn",code:"shell-stat-failed",message:`Could not verify shell path: ${e.shell}`,fixHint:`Check permissions and that "shell" in ${P} points to a real file.`})}if(e.fileReceiveRootMode==="fixed"){let a=e.fileReceiveRootPath.trim();a?Ts.isAbsolute(a)||n.push({severity:"error",code:"receive-fixed-not-absolute",message:`fileReceiveRootPath must be absolute when fileReceiveRootMode is "fixed": ${a}`,fixHint:`Use an absolute path for fileReceiveRootPath in ${P}.`}):n.push({severity:"error",code:"receive-fixed-empty",message:'fileReceiveRootMode is "fixed" but fileReceiveRootPath is empty.',fixHint:`Set fileReceiveRootPath to an absolute directory in ${P}, or change fileReceiveRootMode.`})}if(Kt.platform!=="win32"){try{if(Je.existsSync(P)){let u=Je.statSync(P);(Wn(u.mode)||Fr(u.mode))&&n.push({severity:"warn",code:"config-permissions",message:"config.json is accessible to users other than the owner (group/world).",detail:`Recommended: chmod 600 ${P}`,fixHint:`chmod 600 ${P}`})}}catch{}(typeof Kt.getuid=="function"?Kt.getuid():null)===0&&n.push({severity:"warn",code:"run-as-root",message:"Process is running as root \u2014 allowlisted remote access has full system privileges.",fixHint:"Run omnish as a non-root user (systemd user service, container user, etc.)."});let l=!1;try{if(Je.existsSync(N)){let u=Je.statSync(N);(Wn(u.mode)||Fr(u.mode))&&(l=!0,n.push({severity:"warn",code:"data-dir-permissive",message:"Omnish data directory is accessible to group or other users \u2014 auth, jobs, and logs may be exposed.",detail:`Recommended: chmod 700 ${N}`,fixHint:`chmod 700 ${N}`}))}}catch{}try{if(!l&&Je.existsSync(Ae)){let u=Je.statSync(Ae);(Wn(u.mode)||Fr(u.mode))&&n.push({severity:"warn",code:"jobs-dir-permissive",message:"Jobs log directory is accessible to group or other users \u2014 command output may leak.",detail:`Recommended: chmod 700 ${Ae}`,fixHint:`chmod 700 ${Ae}`})}}catch{}try{if(Je.existsSync(J)){let u=Je.statSync(J);Wn(u.mode)&&n.push({severity:"warn",code:"auth-dir-world-readable",message:"WhatsApp auth directory is readable by others \u2014 session material may be exposed.",detail:`Recommended: chmod 700 ${J}`,fixHint:`chmod 700 ${J}`})}}catch{}}return(typeof Kt.env.TELEGRAM_BOT_TOKEN=="string"?Kt.env.TELEGRAM_BOT_TOKEN.trim():"")&&n.push({severity:"info",code:"telegram-token-env",message:"TELEGRAM_BOT_TOKEN is set in the environment; it overrides config.json until unset.",fixHint:"Unset TELEGRAM_BOT_TOKEN in the shell/service env if you want config.json to apply."}),s&&!oe(e)&&n.push({severity:"error",code:"telegram-no-token",message:"Telegram is enabled for this gateway but no bot token is configured.",detail:"Set telegramBotToken in config or TELEGRAM_BOT_TOKEN.",fixHint:`Set telegramBotToken in ${P} or export TELEGRAM_BOT_TOKEN=<token>.`}),e.clusterEnabled&&s&&n.push({severity:"info",code:"cluster-telegram-tokens",message:"Cluster mode with Telegram: use a distinct BotFather bot/token per omnish host if several gateways run Telegram \u2014 the same token cannot be long-polled twice.",fixHint:"Create separate bots for each machine or run Telegram on fewer hosts."}),n}function Gn(e){return e.some(t=>t.severity==="error")}function lc(e,t){switch(t){case"error":return Un(e,"[ERROR]");case"warn":return xe(e,"[WARN]");case"info":return b(e,"[INFO]")}}function Nr(e,t,n,r){if(r.length!==0){t.push(X(e,`${n} (${r.length}):`));for(let o of r)t.push(` ${lc(e,o.severity)} ${b(e,`${o.code}:`)} ${v(e,o.message)}`),o.detail&&t.push(` ${b(e,o.detail)}`),o.fixHint&&t.push(` ${b(e,`Fix: ${o.fixHint}`)}`);t.push("")}}function _r(e,t){if(e.length===0)return[`${ie(t,"Security check:")} ${v(t,"no issues reported by automated rules.")}`,"",b(t,"Note: allowlisted remote shell access is still equivalent to sharing credentials with those identities.")].join(`
6
- `);let n=e.filter(a=>a.severity==="error"),r=e.filter(a=>a.severity==="warn"),o=e.filter(a=>a.severity==="info"),i=[`${ie(t,"Security check:")} `+v(t,`${n.length} error(s), ${r.length} warning(s), ${o.length} note(s).`),""];return Nr(t,i,"Errors",n),Nr(t,i,"Warnings",r),Nr(t,i,"Notes",o),i.push(b(t,"Allowlisted identities can run commands as this user; treat them like passwords.")),i.join(`
7
- `).trimEnd()}function Br(e){return{errors:e.filter(t=>t.severity==="error").length,warns:e.filter(t=>t.severity==="warn").length,infos:e.filter(t=>t.severity==="info").length}}function As(e){return`${JSON.stringify({findings:e,summary:Br(e)},null,2)}
8
- `}function Ls(e,t){let n=e.filter(a=>a.severity==="error").length,r=e.filter(a=>a.severity==="warn").length,o=e.filter(a=>a.severity==="info").length;if(n===0&&r===0&&o===0)return"security: ok (no automated findings)";let s=[];n&&s.push(`${n} error(s)`),r&&s.push(`${r} warning(s)`),o&&s.push(`${o} note(s)`);let i=t??"run `omnish security` for details";return`security: ${s.join(", ")} \u2014 ${i}`}function Os(e,t,n){let r=e.filter(l=>l.severity==="error").length,o=e.filter(l=>l.severity==="warn").length,s=e.filter(l=>l.severity==="info").length;if(r===0&&o===0&&s===0)return`${ie(t,"security:")} ${v(t,"ok (no automated findings)")}`;let i=[];r&&i.push(Un(t,`${r} error(s)`)),o&&i.push(xe(t,`${o} warning(s)`)),s&&i.push(b(t,`${s} note(s)`));let a=n??"run `omnish security` for details";return`${ie(t,"security:")} ${i.join(", ")} ${b(t,`\u2014 ${a}`)}`}var ge="- ";function p(e){return{wa:e,tg:e}}function Y(e,t){return{wa:e,tg:t,tgHtml:!0}}function De(e,t){return t==="whatsapp"?{text:e.wa}:e.tgHtml?{text:e.tg,parseModeHtml:!0}:{text:e.tg}}function D(e){return e.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;")}function le(e){return e.replace(/[*_~`]/g,t=>({"*":"\u2217",_:"\uFF3F","~":"\u02DC","`":"\u2032"})[t]??t)}function cc(e){let t=[];for(let n of e)switch(n.kind){case"title":t.push("",`*${n.text}*`,"");break;case"sub":t.push("",`_${n.text}_`,"");break;case"gap":t.push("");break;case"p":t.push(n.text);break;case"bullet":t.push(`${ge}${n.text}`);break}return t.join(`
9
- `).replace(/^\n+/,"").trimEnd()}function uc(e){let t=[];for(let n of e)switch(n.kind){case"title":t.push("",`<b>${D(n.text)}</b>`,"");break;case"sub":t.push("",`<i>${D(n.text)}</i>`,"");break;case"gap":t.push("");break;case"p":t.push(D(n.text));break;case"bullet":t.push(`\u2022 ${D(n.text)}`);break}return t.join(`
10
- `).replace(/^\n+/,"").trimEnd()}function j(e){return{wa:cc(e),tg:uc(e),tgHtml:!0}}function Rt(e){return[{kind:"title",text:"Omnish \u2014 quick help"},{kind:"p",text:"Per-chat shell cwd is stored under your data dir (see /wa help)."},{kind:"gap"},{kind:"sub",text:"Run commands"},{kind:"bullet",text:`${e.commandPrefix}<command> \u2014 sync shell in session cwd (timeout ${e.syncTimeoutMs} ms)`},{kind:"bullet",text:`${e.commandPrefix}cd <dir> \u2014 change session cwd (${e.commandPrefix}cd alone \u2192 home)`},{kind:"bullet",text:"!!start | !!stop \u2014 free shell (plain \u2192 sync shell only when no focused /apps session); space optional"},{kind:"bullet",text:"/bg <cmd> \u2014 background job; optional -n name; /jobs, /log, /tail, /kill (id or name)"},{kind:"bullet",text:"/apps \u2026 \u2014 interactive app sessions (/apps help)"},{kind:"bullet",text:"/send selectors \u2014 host files \u2192 chat (/file); caption: selectors -- note"},{kind:"bullet",text:"/files \u2014 full help for /send and saving inbound media"},{kind:"bullet",text:"/receive \u2014 set this chat\u2019s inbound folder (e.g. session cwd)"},{kind:"bullet",text:"/computers \xB7 /pcs \xB7 /c \u2014 chat-driven cluster (/c here to take over, /c help)"},{kind:"bullet",text:"/config \u2014 view or change server settings (/config help, /config keys)"},{kind:"bullet",text:"/service \u2014 background service status, install hints, logs (/service help)"},{kind:"bullet",text:"/shortcut \u2014 this chat & shared shortcuts (/shortcut help); !name or /name expands chat override first, then shared"},{kind:"bullet",text:"/run \u2014 recipe-based task runs (/r); per-chat or gateway-shared /run add \u2014 /run list, /run help"},{kind:"bullet",text:"/cowork | /cw \u2014 scheduled & on-demand shell tasks; logs to ~/Cowork/\u2026 (/cowork help)"},{kind:"gap"},{kind:"sub",text:"Setup & gateway"},{kind:"bullet",text:"/wa help \u2014 WhatsApp link, allowlist"},{kind:"bullet",text:"/tg help \u2014 Telegram; /tg token <paste>"},{kind:"bullet",text:"/reload | /restart \u2014 apply config"},{kind:"bullet",text:"/updates \u2014 npm latest + optional notice URL; /updates cached \u2014 last snapshot"},{kind:"bullet",text:"/security \u2014 posture report; /security summary; /security tips; /security help"},{kind:"bullet",text:"/gateway | /gw | /mode \u2014 show or set gatewayMode"},{kind:"bullet",text:"/allow +E164 | /allow tg:id \u2014 allowlist; /deny \u2026; /allowlist"},{kind:"bullet",text:"/help \u2014 this message"}]}function Ps(e){let t=e.serviceInstallFromChat?[{kind:"bullet",text:"/service install \u2014 user-level systemd (Linux) or LaunchAgent (macOS)"},{kind:"bullet",text:"/service uninstall \u2014 remove that unit"}]:[{kind:"bullet",text:"/service install / uninstall \u2014 off by default. Enable: /config set serviceInstallFromChat true (same trust as shell)"}];return[{kind:"title",text:"Service and boot"},{kind:"p",text:"Inspect the gateway, get OS-specific install steps, or (if enabled) write the user service from chat. Same from the host terminal: omnish service help."},{kind:"bullet",text:"/service status \u2014 platform, data dir, pidfile, node + entry script"},{kind:"bullet",text:"/service instructions \u2014 copy-paste commands for this machine"},{kind:"bullet",text:"/service logs [n] \u2014 tail default gateway log (default 80 lines)"},...t,{kind:"gap"},{kind:"p",text:"See https://omnish.dev and docs/guides/background-and-boot.md."}]}function Fs(){return[{kind:"title",text:"Chat config"},{kind:"p",text:`Same trust as shell \u2014 allowlisted senders can change many keys saved to ${P}.`},{kind:"gap"},{kind:"bullet",text:"/config show \u2014 full snapshot (telegramBotToken masked)"},{kind:"bullet",text:"/config get <key> \u2014 single field"},{kind:"bullet",text:"/config set <key> <value> \u2014 values can be quoted for paths with spaces"},{kind:"bullet",text:"/config keys \u2014 comma-separated whitelist of keys"},{kind:"gap"},{kind:"p",text:"After edits: /reload while omnish run is active (needed for gatewayMode and telegramBotToken)."}]}function Hr(){return[{kind:"title",text:"Gateway mode"},{kind:"p",text:"Which transports omnish uses:"},{kind:"bullet",text:"whatsapp (wa, w) \u2014 WhatsApp DMs only"},{kind:"bullet",text:"telegram (tg, t) \u2014 Telegram bot only"},{kind:"bullet",text:"both (all, b) \u2014 WhatsApp + Telegram"},{kind:"gap"},{kind:"sub",text:"Commands"},{kind:"bullet",text:"/gateway \u2014 current mode + hints (includes last update snapshot if any)"},{kind:"bullet",text:"/gateway telegram \u2014 save (auto-reloads Telegram if gateway is up)"},{kind:"bullet",text:"/gw both \u2014 short form; /mode wa \u2014 same"},{kind:"gap"},{kind:"p",text:`Config: ${P}`}]}function Ns(e){let t=["*Gateway status*","",`Mode: *${e.gatewayMode}*`,"- whatsapp \u2014 WhatsApp only","- telegram \u2014 Telegram only","- both \u2014 both channels","",`WhatsApp auth: ${e.authPresent?"present":"missing (omnish link)"}`,`Telegram token: ${e.tokenSet?"set":"not set"}`,`Allowlists: ${e.allowN} WA \xB7 ${e.tgAllowN} Telegram`,""];e.updateBrief&&t.push(`Updates: ${le(e.updateBrief)}`,""),t.push("Change: /gateway both \xB7 /gw tg","/updates \u2014 npm + optional notice URL",`Config: ${le(P)}`);let n=["<b>Gateway status</b>","",`<b>${D(e.gatewayMode)}</b> <i>current mode</i>`,"\u2022 whatsapp \u2014 WhatsApp only","\u2022 telegram \u2014 Telegram only","\u2022 both \u2014 both channels","",`${e.authPresent?"\u2713":"\u26A0"} WhatsApp auth: ${e.authPresent?"present":"missing \u2014 run omnish link"}`,`${e.tokenSet?"\u2713":"\u26A0"} Telegram token: ${e.tokenSet?"set":"not set"}`,`Allowlists: ${e.allowN} WA \xB7 ${e.tgAllowN} Telegram`,""];return e.updateBrief&&n.push(`<b>Updates</b> ${D(e.updateBrief)}`,""),n.push("<i>Change:</i> /gateway both \xB7 /gw tg","<i>/updates</i> \u2014 npm + optional notice URL",D(`Config: ${P}`)),Y(t.join(`
2
+ var dd=Object.defineProperty;var En=(e,t)=>()=>(e&&(t=e(e=0)),t);var pd=(e,t)=>{for(var n in t)dd(e,n,{get:t[n],enumerable:!0})};import md from"node:crypto";import Do from"node:fs";import fd from"node:os";import se from"node:path";function gd(){let e=process.env.OMNISH_HOME?.trim();if(e)return se.resolve(e);let t=fd.homedir(),n=se.join(t,".omnish"),r=se.join(t,".whatslive");try{if(Do.existsSync(n))return n;if(Do.existsSync(r))return r}catch{}return n}function An(e){let t=md.createHash("sha1").update(e,"utf8").digest("hex").slice(0,8);return se.join(Di,t)}function W(e){Do.mkdirSync(e,{recursive:!0,mode:448})}function V(){W(U),W(X),W(We),W(Di),W(Hi),W(Xe)}var U,X,We,Di,Hi,Ce,ne,Qt,In,Ve,sr,ir,L,ar,lr,cr,Xe,Ho,Bo,Bi,j=En(()=>{"use strict";U=gd(),X=se.join(U,"auth"),We=se.join(U,"jobs"),Di=se.join(U,"apps"),Hi=se.join(U,"logs"),Ce=se.join(Hi,"gateway.log"),ne=se.join(U,"gateway.pid"),Qt=se.join(U,"gateway-control.json"),In=se.join(U,"config-ui.json"),Ve=se.join(U,"ui.json"),sr=se.join(U,"tunnel-auth.json"),ir=se.join(U,"ui-server.json"),L=se.join(U,"config.json"),ar=se.join(U,"shortcuts.json"),lr=se.join(U,"recipes.json"),cr=se.join(U,"recipes-user.json"),Xe=se.join(U,"cowork"),Ho=se.join(Xe,"tasks.json"),Bo=se.join(Xe,"pending-runs.json"),Bi=se.join(Xe,"completions.sqlite")});function zi(e){let t=e.trim();for(;;){let n=t;if(t=t.replace(/^whatsapp:/i,"").trim(),t===n)return t}}function hd(e){let t=zi(e);if(!t.toLowerCase().endsWith("@g.us"))return!1;let r=t.slice(0,t.length-5);return!r||r.includes("@")?!1:/^[0-9]+(-[0-9]+)*$/.test(r)}function yd(e){let t=e.match(Wi);if(t)return t[1]??null;let n=e.match(Gi);if(n)return n[1]??null;let r=e.match(Ji);return r?r[1]??null:null}function ji(e){let t=e.replace(/\D/g,"");return t?`+${t}`:""}function Nt(e){return`${e.replace(/\D/g,"")}@s.whatsapp.net`}function q(e){let t=zi(e);if(!t||hd(t))return null;if(Wi.test(t)||Gi.test(t)||Ji.test(t)){let r=yd(t);if(!r)return null;let o=ji(r);return o.length>1?o:null}if(t.includes("@"))return null;let n=ji(t);return n.length>1?n:null}function ur(e){return e.map(t=>String(t).trim()).filter(t=>!!t).map(t=>t==="*"?t:q(t)).filter(t=>!!t)}function dr(e){let t=new Set;for(let n of e){if(n==="*")continue;let r=q(String(n));r&&t.add(r)}return t}function ye(e){let t=e.trim();for(;;){let n=t.toLowerCase();if(n.startsWith("tg:")){t=t.slice(3).trimStart();continue}if(n.startsWith("telegram:")){t=t.slice(9).trimStart();continue}break}return/^\d+$/.test(t)?t:null}function pr(e){let t=new Set;for(let n of e){let r=ye(String(n));r&&t.add(r)}return t}function jo(e){let t=e.trim();if(!t)return null;let n=t.toLowerCase();if(n.startsWith("tg:")||n.startsWith("telegram:")){let o=ye(t);return o?{kind:"tg",id:o}:null}let r=q(t);return r?{kind:"wa",normalized:r}:null}function Ki(e,t){let n=q(t);return n?e.has(n):!1}var Wi,Gi,Ji,Ue=En(()=>{"use strict";Wi=/^(\d+)(?::\d+)?@s\.whatsapp\.net$/i,Gi=/^(\d+)@c\.us$/i,Ji=/^(\d+)@lid$/i});import mr from"node:fs";function wd(e){return e==="downloads"||e==="omnishData"||e==="sessionCwd"||e==="processCwd"||e==="fixed"?e:O.fileReceiveRootMode}function kd(e){return e==="primary"?"primary":"secondary"}function bd(e){if(!e||typeof e!="object")return{};let t={};for(let[n,r]of Object.entries(e)){if(typeof r!="string")continue;let o=r.trim();if(!o)continue;let s=n.trim(),i=s.toLowerCase();if(i.startsWith("wa:")){let l=q(s.slice(3));l&&(t[`wa:${l}`]=o.slice(0,64));continue}if(i.startsWith("tg:")||i.startsWith("telegram:")){let l=ye(s);l&&(t[`tg:${l}`]=o.slice(0,64));continue}let a=q(s);a&&(t[`wa:${a}`]=o.slice(0,64))}return t}function Pn(e){let t=typeof e.appsFlushMs=="number"&&e.appsFlushMs>=0?e.appsFlushMs:O.appsFlushMs,n=e.gatewayMode==="telegram"||e.gatewayMode==="both"||e.gatewayMode==="whatsapp"?e.gatewayMode:O.gatewayMode,r=typeof e.telegramBotToken=="string"?e.telegramBotToken:O.telegramBotToken,o=Array.isArray(e.telegramAllowFrom)?[...new Set(e.telegramAllowFrom.map(s=>ye(String(s))).filter(s=>!!s))].sort():O.telegramAllowFrom;return{...O,...e,gatewayMode:n,telegramBotToken:r,telegramAllowFrom:o,allowFrom:Array.isArray(e.allowFrom)?ur(e.allowFrom.map(String)).filter(s=>s!=="*"):O.allowFrom,commandPrefix:(()=>{let s=typeof e.commandPrefix=="string"&&e.commandPrefix.length>0?e.commandPrefix:O.commandPrefix;return s==="! "?"!":s})(),syncTimeoutMs:typeof e.syncTimeoutMs=="number"&&e.syncTimeoutMs>0?e.syncTimeoutMs:O.syncTimeoutMs,syncMaxBytes:typeof e.syncMaxBytes=="number"&&e.syncMaxBytes>0?e.syncMaxBytes:O.syncMaxBytes,jobLogTailLines:typeof e.jobLogTailLines=="number"&&e.jobLogTailLines>0?e.jobLogTailLines:O.jobLogTailLines,shell:typeof e.shell=="string"&&e.shell.length>0?e.shell:O.shell,appsCols:typeof e.appsCols=="number"&&e.appsCols>0&&e.appsCols<=500?Math.floor(e.appsCols):O.appsCols,appsRows:typeof e.appsRows=="number"&&e.appsRows>0&&e.appsRows<=200?Math.floor(e.appsRows):O.appsRows,appsFlushMs:t,appsMinIntervalMs:typeof e.appsMinIntervalMs=="number"&&e.appsMinIntervalMs>=0?e.appsMinIntervalMs:O.appsMinIntervalMs,appsMaxFlushBytes:typeof e.appsMaxFlushBytes=="number"&&e.appsMaxFlushBytes>256?Math.floor(e.appsMaxFlushBytes):O.appsMaxFlushBytes,appsMaxSessions:typeof e.appsMaxSessions=="number"&&e.appsMaxSessions>0?Math.min(50,Math.floor(e.appsMaxSessions)):O.appsMaxSessions,appsMaxSessionsTotal:typeof e.appsMaxSessionsTotal=="number"&&e.appsMaxSessionsTotal>0?Math.min(200,Math.floor(e.appsMaxSessionsTotal)):O.appsMaxSessionsTotal,appsMaxWaChars:typeof e.appsMaxWaChars=="number"&&e.appsMaxWaChars>256?Math.floor(e.appsMaxWaChars):O.appsMaxWaChars,appsLogTailLines:typeof e.appsLogTailLines=="number"&&e.appsLogTailLines>0?Math.min(500,Math.floor(e.appsLogTailLines)):O.appsLogTailLines,appsSubmitDelayMs:typeof e.appsSubmitDelayMs=="number"&&e.appsSubmitDelayMs>=0?Math.min(500,Math.floor(e.appsSubmitDelayMs)):O.appsSubmitDelayMs,appsClearInput:typeof e.appsClearInput=="boolean"?e.appsClearInput:O.appsClearInput,appsClearInputDelayMs:typeof e.appsClearInputDelayMs=="number"&&e.appsClearInputDelayMs>=0?Math.min(200,Math.floor(e.appsClearInputDelayMs)):O.appsClearInputDelayMs,appsClearInputSequence:typeof e.appsClearInputSequence=="string"&&e.appsClearInputSequence.length>0?e.appsClearInputSequence.slice(0,200):O.appsClearInputSequence,fileSendMaxBytes:typeof e.fileSendMaxBytes=="number"&&e.fileSendMaxBytes>=0?e.fileSendMaxBytes===0?0:Math.min(2e9,Math.floor(e.fileSendMaxBytes)):O.fileSendMaxBytes,fileReceiveMaxBytes:typeof e.fileReceiveMaxBytes=="number"&&e.fileReceiveMaxBytes>=0?e.fileReceiveMaxBytes===0?0:Math.min(2e9,Math.floor(e.fileReceiveMaxBytes)):O.fileReceiveMaxBytes,fileInboxSubdir:typeof e.fileInboxSubdir=="string"&&e.fileInboxSubdir.length>0&&e.fileInboxSubdir.replace(/[/\\]/g,"").slice(0,80)||O.fileInboxSubdir,fileReceiveRootMode:wd(e.fileReceiveRootMode),fileReceiveRootPath:typeof e.fileReceiveRootPath=="string"?e.fileReceiveRootPath.trim().slice(0,4096):O.fileReceiveRootPath,recipesAllowDangerousBuiltins:typeof e.recipesAllowDangerousBuiltins=="boolean"?e.recipesAllowDangerousBuiltins:O.recipesAllowDangerousBuiltins,recipesMaxTaskChars:typeof e.recipesMaxTaskChars=="number"&&e.recipesMaxTaskChars>=0?e.recipesMaxTaskChars===0?0:Math.min(Number.MAX_SAFE_INTEGER,Math.floor(e.recipesMaxTaskChars)):O.recipesMaxTaskChars,recipesMacroDefaultCommand:typeof e.recipesMacroDefaultCommand=="string"&&e.recipesMacroDefaultCommand.trim().length>0?e.recipesMacroDefaultCommand.trim().slice(0,4096):O.recipesMacroDefaultCommand,clusterEnabled:typeof e.clusterEnabled=="boolean"?e.clusterEnabled:O.clusterEnabled,clusterLabel:typeof e.clusterLabel=="string"?e.clusterLabel.trim().slice(0,128):O.clusterLabel,clusterRole:kd(e.clusterRole),clusterSenderBindings:bd(e.clusterSenderBindings),serviceInstallFromChat:typeof e.serviceInstallFromChat=="boolean"?e.serviceInstallFromChat:O.serviceInstallFromChat,updateCheckEnabled:typeof e.updateCheckEnabled=="boolean"?e.updateCheckEnabled:O.updateCheckEnabled,updateCheckIntervalMs:typeof e.updateCheckIntervalMs=="number"&&e.updateCheckIntervalMs>0?Math.min(6048e5,Math.max(36e5,Math.floor(e.updateCheckIntervalMs))):O.updateCheckIntervalMs,updateCheckPackageName:typeof e.updateCheckPackageName=="string"&&e.updateCheckPackageName.trim().length>0?e.updateCheckPackageName.trim().slice(0,214):O.updateCheckPackageName,updateInfoUrl:typeof e.updateInfoUrl=="string"?e.updateInfoUrl.trim().slice(0,2048):O.updateInfoUrl,chatLlmFallbackEnabled:typeof e.chatLlmFallbackEnabled=="boolean"?e.chatLlmFallbackEnabled:O.chatLlmFallbackEnabled,chatLlmShellCommand:typeof e.chatLlmShellCommand=="string"?e.chatLlmShellCommand.trim().slice(0,8192):O.chatLlmShellCommand,chatLlmTimeoutMs:typeof e.chatLlmTimeoutMs=="number"&&e.chatLlmTimeoutMs>0?Math.min(9e5,Math.floor(e.chatLlmTimeoutMs)):O.chatLlmTimeoutMs,chatLlmMaxInputChars:typeof e.chatLlmMaxInputChars=="number"&&e.chatLlmMaxInputChars>0?Math.min(5e5,Math.floor(e.chatLlmMaxInputChars)):O.chatLlmMaxInputChars,chatLlmMaxOutputChars:typeof e.chatLlmMaxOutputChars=="number"&&e.chatLlmMaxOutputChars>0?Math.min(2e6,Math.floor(e.chatLlmMaxOutputChars)):O.chatLlmMaxOutputChars,chatLlmNeedsTty:typeof e.chatLlmNeedsTty=="boolean"?e.chatLlmNeedsTty:O.chatLlmNeedsTty,chatLlmWorkDir:typeof e.chatLlmWorkDir=="string"?e.chatLlmWorkDir.trim().slice(0,4096):O.chatLlmWorkDir,tunnelEnabled:typeof e.tunnelEnabled=="boolean"?e.tunnelEnabled:O.tunnelEnabled,tunnelRelayUrl:typeof e.tunnelRelayUrl=="string"&&e.tunnelRelayUrl.trim().length>0?e.tunnelRelayUrl.trim().slice(0,2048):O.tunnelRelayUrl,tunnelMaxActive:typeof e.tunnelMaxActive=="number"&&e.tunnelMaxActive>0?Math.min(50,Math.floor(e.tunnelMaxActive)):O.tunnelMaxActive,platformToken:typeof e.platformToken=="string"?e.platformToken.trim():O.platformToken,platformDeviceId:typeof e.platformDeviceId=="string"?e.platformDeviceId.trim().slice(0,128):O.platformDeviceId,webhookEnabled:typeof e.webhookEnabled=="boolean"?e.webhookEnabled:O.webhookEnabled,webhookPort:typeof e.webhookPort=="number"&&e.webhookPort>=0?Math.min(65535,Math.floor(e.webhookPort)):O.webhookPort,webhookHost:typeof e.webhookHost=="string"&&e.webhookHost.trim().length>0?e.webhookHost.trim():O.webhookHost,webhookToken:typeof e.webhookToken=="string"?e.webhookToken.trim():O.webhookToken}}function Y(e){let t=$(),n=Pn({...t,...e});return Re(n),n}function $(){if(V(),!mr.existsSync(L)){let e=Pn({});return Re(e),e}try{let e=mr.readFileSync(L,"utf8"),t=JSON.parse(e);return Pn(t)}catch{return Pn({})}}function Re(e){V();let t=Pn(e);mr.writeFileSync(L,JSON.stringify(t,null,2)+`
3
+ `,{mode:384})}function fr(e){let t=jo(e);if(!t)throw new Error("Invalid entry. Use +E164 (WhatsApp) or tg:<user_id> (Telegram).");let n=$();if(t.kind==="wa"){if(t.normalized==="*")throw new Error("Wildcards are not allowed.");let r=new Set(n.allowFrom);r.add(t.normalized),n.allowFrom=[...r].sort()}else{let r=new Set(n.telegramAllowFrom);r.add(t.id),n.telegramAllowFrom=[...r].sort()}return Re(n),n}function gr(e){let t=jo(e);if(!t)throw new Error("Invalid entry. Use +E164 (WhatsApp) or tg:<user_id> (Telegram).");let n=$();return t.kind==="wa"?n.allowFrom=n.allowFrom.filter(r=>r!==t.normalized):n.telegramAllowFrom=n.telegramAllowFrom.filter(r=>ye(r)!==t.id),Re(n),n}function pe(e){let t=typeof process.env.TELEGRAM_BOT_TOKEN=="string"?process.env.TELEGRAM_BOT_TOKEN.trim():"";return t||(e.telegramBotToken??"").trim()}function Ze(e){let t=e.trim();return/^\d{6,}:[A-Za-z0-9_-]{20,}$/.test(t)}function ft(e){let t=$();return t.telegramBotToken=e.trim(),Re(t),$()}function Vt(e){let t=e.trim().toLowerCase();return t==="whatsapp"||t==="wa"||t==="w"?"whatsapp":t==="telegram"||t==="tg"||t==="t"?"telegram":t==="both"||t==="all"||t==="b"?"both":null}function hr(e){let t=$();return t.gatewayMode=e,Re(t),$()}function yr(e){let t=$();return t.clusterEnabled=e,Re(t),$()}function Ge(){try{return mr.readdirSync(X).length>0}catch{return!1}}var O,de=En(()=>{"use strict";j();Ue();O={gatewayMode:"whatsapp",telegramBotToken:"",telegramAllowFrom:[],allowFrom:[],commandPrefix:"!",syncTimeoutMs:3e4,syncMaxBytes:32768,jobLogTailLines:80,shell:"/bin/bash",appsCols:120,appsRows:40,appsFlushMs:300,appsMinIntervalMs:800,appsMaxFlushBytes:8192,appsMaxSessions:5,appsMaxSessionsTotal:20,appsMaxWaChars:3500,appsLogTailLines:80,appsSubmitDelayMs:50,appsClearInput:!0,appsClearInputDelayMs:20,appsClearInputSequence:"^A,^K",fileSendMaxBytes:0,fileReceiveMaxBytes:0,fileInboxSubdir:"inbox",fileReceiveRootMode:"downloads",fileReceiveRootPath:"",recipesAllowDangerousBuiltins:!1,recipesMaxTaskChars:0,recipesMacroDefaultCommand:'claude -p "$OMNISH_TASK"',clusterEnabled:!1,clusterLabel:"",clusterRole:"secondary",clusterSenderBindings:{},serviceInstallFromChat:!1,updateCheckEnabled:!1,updateCheckIntervalMs:864e5,updateCheckPackageName:"omnish",updateInfoUrl:"",chatLlmFallbackEnabled:!1,chatLlmShellCommand:"",chatLlmTimeoutMs:12e4,chatLlmMaxInputChars:16e3,chatLlmMaxOutputChars:24e3,chatLlmNeedsTty:!1,chatLlmWorkDir:"",tunnelEnabled:!1,tunnelRelayUrl:"https://tunnel.omnish.dev",platformToken:"",platformDeviceId:"",tunnelMaxActive:5,webhookEnabled:!1,webhookPort:0,webhookHost:"127.0.0.1",webhookToken:""}});import Qd from"pino";function rn(){return process.env.OMNISH_VERBOSE==="1"||process.env.WHATSVERBOSE==="1"}function cl(){return I.child({module:"baileys"})}var Vd,I,Se=En(()=>{"use strict";Vd=rn()?"info":"silent",I=Qd({level:Vd,base:{app:"omnish"}})});var Ic={};pd(Ic,{fetchPlatformAccount:()=>bn,getAttachedConfig:()=>ei,getAttachedPlatformSnapshot:()=>gf,mergeAttachedPlatformPolicy:()=>Ec,parsePlatformMeResponse:()=>Mc,setAttachedPlatformSnapshot:()=>Xs,setPlatformDefaultDevice:()=>ti,snapshotFromRegisteredAccount:()=>Tc,syncAttachedPlatformPolicy:()=>mo,updatePlatformAllowlists:()=>po});function Zs(e){let t=e.replace(/\/$/,"");return/^https?:\/\//i.test(t)?t:`http://${t}`}function mf(e){return e==="telegram"||e==="both"||e==="whatsapp"?e:"whatsapp"}function ff(e){if(!e||typeof e!="object")return{};let t={};for(let[n,r]of Object.entries(e)){if(!r||typeof r!="object")continue;let o=r,s=typeof o.status=="string"?o.status:"idle";t[n]={status:s,linked:o.linked===!0||s==="linked",...typeof o.tokenConfigured=="boolean"?{tokenConfigured:o.tokenConfigured}:{}}}return t}function Mc(e){let t=e.routing,n=t&&typeof t=="object"?t:null,r=Array.isArray(n?.onlineDeviceIds)?n.onlineDeviceIds.map(String):[],o=n?.defaultDeviceId!=null?String(n.defaultDeviceId):e.defaultDeviceId!=null?String(e.defaultDeviceId):null;return{allowFrom:Array.isArray(e.allowFrom)?e.allowFrom.map(String):[],telegramAllowFrom:Array.isArray(e.telegramAllowFrom)?e.telegramAllowFrom.map(String):[],gatewayMode:mf(e.gatewayMode),connectors:ff(e.connectors),defaultDeviceId:o,routing:{defaultDeviceId:o,onlineDeviceIds:r,onlineCount:typeof n?.onlineCount=="number"?n.onlineCount:r.length}}}function Tc(e,t=[],n=[]){let r=e.defaultDeviceId??null;return{allowFrom:t,telegramAllowFrom:n,gatewayMode:e.gatewayMode,connectors:e.connectors,defaultDeviceId:r,routing:{defaultDeviceId:r,onlineDeviceIds:[],onlineCount:0}}}function Ec(e,t){return{...e,allowFrom:[...t.allowFrom],telegramAllowFrom:[...t.telegramAllowFrom],gatewayMode:t.gatewayMode}}function Xs(e){uo=e}function gf(){return uo}function ei(){let e=$();return uo?Ec(e,uo):e}async function bn(e){let t=await fetch(`${Zs(e.platformUrl)}/v1/me`,{headers:{Authorization:`Bearer ${e.token}`}});if(!t.ok)throw new Error(`Platform GET /v1/me failed: HTTP ${t.status}`);let n=await t.json();return Mc(n)}async function po(e,t){let n=await fetch(`${Zs(e.platformUrl)}/v1/me/allowlists`,{method:"PUT",headers:{"content-type":"application/json",Authorization:`Bearer ${e.token}`},body:JSON.stringify(t)}),r=await n.json().catch(()=>({}));if(!n.ok)throw new Error(r.error||`Platform PUT /v1/me/allowlists failed: HTTP ${n.status}`);return{allowFrom:Array.isArray(r.allowFrom)?r.allowFrom.map(String):[],telegramAllowFrom:Array.isArray(r.telegramAllowFrom)?r.telegramAllowFrom.map(String):[]}}async function ti(e,t){let n=await fetch(`${Zs(e.platformUrl)}/v1/me/default-device`,{method:"PUT",headers:{"content-type":"application/json",Authorization:`Bearer ${e.token}`},body:JSON.stringify({deviceId:t})});if(!n.ok){let r=await n.json().catch(()=>({}));throw new Error(r.error||`Platform PUT /v1/me/default-device failed: HTTP ${n.status}`)}}async function mo(e,t){try{let n=await bn(e);return Xs(n),I.info({gatewayMode:n.gatewayMode,waLinked:n.connectors.whatsapp?.linked===!0,tgLinked:n.connectors.telegram?.linked===!0,allowFromCount:n.allowFrom.length,telegramAllowFromCount:n.telegramAllowFrom.length},"attached platform policy loaded"),n}catch(n){if(t){let r=Tc(t);return Xs(r),I.warn({err:String(n)},"platform GET /v1/me failed; using register ack for gatewayMode/connectors only (allowlists from local config until /v1/me works)"),r}return I.warn({err:String(n)},"platform GET /v1/me failed; attached inbound uses local config.json allowlists"),null}}var uo,fo=En(()=>{"use strict";de();Se();uo=null});de();import sh from"node:dns";import ih from"node:crypto";import Pt from"node:fs";import Fi from"node:path";import od from"node:os";de();j();de();j();import xd from"node:os";import kr from"node:path";j();import Wo from"node:fs";import Yi from"node:os";import gt from"node:path";var Qi=gt.join(U,"sessions.json"),Xt=new Map;function Vi(){return{cwd:gt.resolve(Yi.homedir())}}function Sd(e){return e.startsWith("wa:")||e.startsWith("tg:")?e:`wa:${e}`}function vd(){try{let e=Wo.readFileSync(Qi,"utf8"),t=JSON.parse(e);for(let[n,r]of Object.entries(t)){if(!r||typeof r!="object")continue;let o=Sd(n),i={cwd:typeof r.cwd=="string"&&r.cwd.length>0?gt.resolve(r.cwd):Vi().cwd};r.fileReceiveRoot==="sessionCwd"&&(i.fileReceiveRoot="sessionCwd"),Xt.set(o,i)}}catch{}}function Xi(){W(U);let e={};for(let[t,n]of Xt){let r={cwd:n.cwd};n.fileReceiveRoot==="sessionCwd"&&(r.fileReceiveRoot="sessionCwd"),e[t]=r}Wo.writeFileSync(Qi,JSON.stringify(e,null,2)+`
4
+ `,{mode:384})}var qi=!1;function Go(){qi||(vd(),qi=!0)}function ee(e){Go();let t=Xt.get(e);return t||(t=Vi(),Xt.set(e,t)),t}function wr(e,t){Go();let n=gt.resolve(t),r=ee(e);r.cwd=n,Xt.set(e,r),Xi()}function Zi(e){return ee(e).fileReceiveRoot==="sessionCwd"?"sessionCwd":"default"}function Jo(e,t){Go();let n=ee(e);t==="default"?delete n.fileReceiveRoot:n.fileReceiveRoot="sessionCwd",Xt.set(e,n),Xi()}function ea(e){let t=e.trim();if(/[&|;\n]/.test(t)||!t.startsWith("cd"))return null;if(t==="cd")return{kind:"home"};if(t.length>2&&t[2]!==" "&&t[2]!==" ")return null;let n=t.slice(2).trimStart();return n?{kind:"path",value:n}:{kind:"home"}}function ta(e,t){if(t.kind==="home")return gt.resolve(Yi.homedir());let n=t.value.replace(/^['"]|['"]$/g,"");return gt.isAbsolute(n)?gt.normalize(n):gt.resolve(e,n)}function na(e){try{return Wo.statSync(e).isDirectory()?{ok:!0}:{ok:!1,error:`Not a directory: ${e}`}}catch(t){return{ok:!1,error:String(t)}}}var $d="Omnish";function ra(){return kr.join(xd.homedir(),"Downloads",$d)}function Zt(e,t){let n=ee(t);if(n.fileReceiveRoot==="sessionCwd")return n.cwd;switch(e.fileReceiveRootMode){case"downloads":return ra();case"omnishData":return kr.join(U,e.fileInboxSubdir);case"sessionCwd":return n.cwd;case"processCwd":return process.cwd();case"fixed":{let r=(e.fileReceiveRootPath??"").trim();if(!r)throw new Error('fileReceiveRootPath is required when fileReceiveRootMode is "fixed".');if(!kr.isAbsolute(r))throw new Error('fileReceiveRootPath must be an absolute path when fileReceiveRootMode is "fixed".');return kr.resolve(r)}default:return ra()}}j();import at from"node:fs";import sa from"node:path";import kt from"node:process";var _t="\x1B";function Cd(e){return e.replace(/\u001B\[[\d;]*m/g,"")}function br(e){return Cd(e).length}function zo(e,t,n,r,o=2){let s=Math.max(0,t-br(n));return`${e}${n}${" ".repeat(s)}${" ".repeat(o)}${r}`}function ot(e,t,n,r){if(n.length===0)return[];let o=n.map(i=>r(i.left)),s=Math.max(...o.map(br));return n.map((i,a)=>zo(t,s,o[a],k(e,i.right)))}var ht={primary:"#b4ff24",foreground:"#eef2f4",muted:"#8a9199",border:"#2a3139",error:"#ef4444",warn:"#a0e614",warnAlt:"#8cce04"};function Rd(e){let t=e.replace(/^#/,"");return t.length!==6||!/^[0-9a-fA-F]+$/.test(t)?null:{r:parseInt(t.slice(0,2),16),g:parseInt(t.slice(2,4),16),b:parseInt(t.slice(4,6),16)}}function yt(e){let t=Rd(e);return t?`${_t}[38;2;${t.r};${t.g};${t.b}m`:""}function Sr(e){if(process.env.NO_COLOR!==void 0&&process.env.NO_COLOR!=="")return!1;let t=process.env.FORCE_COLOR;return t==="1"||t==="true"?!0:t==="0"||t==="false"?!1:e.isTTY===!0}function st(e,t,n){return!t||!Sr(e)?n:`${t}${n}${_t}[0m`}function B(e,t){return st(e,`${_t}[1m`,t)}function Md(e,t){return Sr(e)?`${yt(ht.foreground)}${_t}[1m${t}${_t}[0m`:t}function J(e,t){return st(e,`${_t}[2m`,t)}function me(e,t){return st(e,`${yt(ht.primary)}${_t}[1m`,t)}function re(e,t){return st(e,yt(ht.primary),t)}function k(e,t){return st(e,yt(ht.foreground),t)}function g(e,t){return st(e,yt(ht.muted),t)}function Td(e,t){return st(e,yt(ht.border),t)}function vr(e,t){return st(e,yt(ht.error),t)}function ae(e,t){return st(e,yt(ht.warn),t)}function wt(e,t=60,n="\u2500"){let r=n.repeat(Math.max(1,t));return Td(e,r)}function N(e,t){return Sr(e)?`${`${g(e,"[")}${re(e,"omnish")}${g(e,"]")}`} ${t}`:`[omnish] ${t}`}function C(e,t){return Sr(e)?`${`${vr(e,"[omnish]")}`} ${t}`:`[omnish] ${t}`}function oa(e,t){let n=[];for(let r of e)switch(r.kind){case"title":n.push("",me(t,r.text),"");break;case"sub":n.push("",Md(t,r.text),"");break;case"gap":n.push("");break;case"p":n.push(k(t,r.text));break;case"bullet":n.push(`${g(t,"\u2022")} ${k(t,r.text)}`);break}return n.join(`
5
+ `).replace(/^\n+/,"").trimEnd()}de();j();de();j();import Ko from"node:fs";function it(){try{let e=Ko.readFileSync(sr,"utf8"),t=JSON.parse(e);return!t||typeof t.token!="string"||!t.token.trim()?null:{token:t.token.trim(),relayUrl:typeof t.relayUrl=="string"?t.relayUrl.trim():void 0}}catch{return null}}function et(e){V(),Ko.writeFileSync(sr,JSON.stringify({token:e.token.trim(),...e.relayUrl?{relayUrl:e.relayUrl.trim()}:{}},null,2)+`
6
+ `,{mode:384})}function xr(){try{Ko.unlinkSync(sr)}catch{}}function Ed(){return process.env.OMNISH_TOKEN?.trim()||process.env.OMNISH_TUNNEL_TOKEN?.trim()||process.env.OMNISH_DEVICE_TOKEN?.trim()||""}function tt(){let e=Ed();if(e)return e;let t=$().platformToken.trim();return t||(it()?.token??"")}function qo(e){let t=process.env.OMNISH_PLATFORM_URL?.trim()||process.env.OMNISH_COMM_LAYER_URL?.trim()||process.env.OMNISH_TUNNEL_RELAY?.trim();if(t)return t.replace(/\/$/,"");let n=$().tunnelRelayUrl.trim();if(n)return n.replace(/\/$/,"");let r=it()?.relayUrl?.trim();return r?r.replace(/\/$/,""):e.replace(/\/$/,"")}function Je(e){return qo(e)}var be="https://tunnel.omnish.dev";function $r(e){return(e&4)!==0}function Yo(e){return(e&2)!==0}var ia={error:3,warn:2,info:1};function aa(e,t){let n=ia[t];return e.filter(r=>ia[r.severity]>=n)}function lt(e,t={}){let n=[],r=t.gatewayMode??e.gatewayMode,o=r==="whatsapp"||r==="both",s=r==="telegram"||r==="both";if(e.allowFrom.includes("*")&&n.push({severity:"error",code:"allow-wildcard",message:'allowFrom contains "*" \u2014 this must never be used; remove it from config immediately.',detail:`Edit ${L} and delete wildcard entries.`,fixHint:`Edit ${L} and remove any "*" entries from allowFrom.`}),o&&e.allowFrom.length===0&&n.push({severity:"warn",code:"allow-wa-empty",message:"allowFrom is empty \u2014 no WhatsApp identity can run commands.",detail:"Add your number: omnish allow +<E164>",fixHint:"omnish allow +<your_E164_number>"}),s&&e.telegramAllowFrom.length===0&&n.push({severity:"warn",code:"allow-tg-empty",message:"telegramAllowFrom is empty \u2014 no Telegram user can run commands.",detail:"Add your user id: omnish allow tg:<id>",fixHint:"omnish allow tg:<your_telegram_user_id>"}),e.recipesAllowDangerousBuiltins&&n.push({severity:"warn",code:"recipes-dangerous",message:"recipesAllowDangerousBuiltins is enabled \u2014 built-in recipe helpers may add permissive Claude Code flags.",fixHint:`Set recipesAllowDangerousBuiltins to false in ${L} unless you trust every recipe.`}),e.serviceInstallFromChat&&n.push({severity:"warn",code:"service-install-from-chat",message:"serviceInstallFromChat is enabled \u2014 allowlisted users can run /service install and write user-level systemd or LaunchAgent units.",fixHint:`Set serviceInstallFromChat to false in ${L} unless you trust every allowlisted identity.`}),!sa.isAbsolute(e.shell))n.push({severity:"warn",code:"shell-not-absolute",message:`Shell path is not absolute: ${e.shell}`,fixHint:`Set "shell" to an absolute path (e.g. /bin/bash) in ${L}.`});else try{at.existsSync(e.shell)||n.push({severity:"error",code:"shell-missing",message:`Configured shell does not exist: ${e.shell}`,fixHint:`Install the shell or update "shell" in ${L} to a valid binary.`})}catch{n.push({severity:"warn",code:"shell-stat-failed",message:`Could not verify shell path: ${e.shell}`,fixHint:`Check permissions and that "shell" in ${L} points to a real file.`})}if(e.fileReceiveRootMode==="fixed"){let a=e.fileReceiveRootPath.trim();a?sa.isAbsolute(a)||n.push({severity:"error",code:"receive-fixed-not-absolute",message:`fileReceiveRootPath must be absolute when fileReceiveRootMode is "fixed": ${a}`,fixHint:`Use an absolute path for fileReceiveRootPath in ${L}.`}):n.push({severity:"error",code:"receive-fixed-empty",message:'fileReceiveRootMode is "fixed" but fileReceiveRootPath is empty.',fixHint:`Set fileReceiveRootPath to an absolute directory in ${L}, or change fileReceiveRootMode.`})}if(kt.platform!=="win32"){try{if(at.existsSync(L)){let c=at.statSync(L);($r(c.mode)||Yo(c.mode))&&n.push({severity:"warn",code:"config-permissions",message:"config.json is accessible to users other than the owner (group/world).",detail:`Recommended: chmod 600 ${L}`,fixHint:`chmod 600 ${L}`})}}catch{}(typeof kt.getuid=="function"?kt.getuid():null)===0&&n.push({severity:"warn",code:"run-as-root",message:"Process is running as root \u2014 allowlisted remote access has full system privileges.",fixHint:"Run omnish as a non-root user (systemd user service, container user, etc.)."});let l=!1;try{if(at.existsSync(U)){let c=at.statSync(U);($r(c.mode)||Yo(c.mode))&&(l=!0,n.push({severity:"warn",code:"data-dir-permissive",message:"Omnish data directory is accessible to group or other users \u2014 auth, jobs, and logs may be exposed.",detail:`Recommended: chmod 700 ${U}`,fixHint:`chmod 700 ${U}`}))}}catch{}try{if(!l&&at.existsSync(We)){let c=at.statSync(We);($r(c.mode)||Yo(c.mode))&&n.push({severity:"warn",code:"jobs-dir-permissive",message:"Jobs log directory is accessible to group or other users \u2014 command output may leak.",detail:`Recommended: chmod 700 ${We}`,fixHint:`chmod 700 ${We}`})}}catch{}try{if(at.existsSync(X)){let c=at.statSync(X);$r(c.mode)&&n.push({severity:"warn",code:"auth-dir-world-readable",message:"WhatsApp auth directory is readable by others \u2014 session material may be exposed.",detail:`Recommended: chmod 700 ${X}`,fixHint:`chmod 700 ${X}`})}}catch{}}return(typeof kt.env.TELEGRAM_BOT_TOKEN=="string"?kt.env.TELEGRAM_BOT_TOKEN.trim():"")&&n.push({severity:"info",code:"telegram-token-env",message:"TELEGRAM_BOT_TOKEN is set in the environment; it overrides config.json until unset.",fixHint:"Unset TELEGRAM_BOT_TOKEN in the shell/service env if you want config.json to apply."}),s&&!pe(e)&&n.push({severity:"error",code:"telegram-no-token",message:"Telegram is enabled for this gateway but no bot token is configured.",detail:"Set telegramBotToken in config or TELEGRAM_BOT_TOKEN.",fixHint:`Set telegramBotToken in ${L} or export TELEGRAM_BOT_TOKEN=<token>.`}),e.clusterEnabled&&s&&n.push({severity:"info",code:"cluster-telegram-tokens",message:"Cluster mode with Telegram: use a distinct BotFather bot/token per omnish host if several gateways run Telegram \u2014 the same token cannot be long-polled twice.",fixHint:"Create separate bots for each machine or run Telegram on fewer hosts."}),e.platformToken.trim()&&n.push({severity:"info",code:"platform-token-config",message:"platformToken is stored in config.json (same trust boundary as your shell account).",fixHint:"Use OMNISH_TOKEN in the environment for CI/ephemeral hosts; env overrides config."}),(kt.env.OMNISH_TOKEN?.trim()||kt.env.OMNISH_TUNNEL_TOKEN?.trim()||kt.env.OMNISH_DEVICE_TOKEN?.trim())&&n.push({severity:"info",code:"platform-token-env",message:"OMNISH_TOKEN (or OMNISH_TUNNEL_TOKEN / OMNISH_DEVICE_TOKEN) is set in the environment.",fixHint:"Unset platform token env vars if you want config.json / tunnel-auth.json to apply."}),e.tunnelEnabled&&(n.push({severity:"warn",code:"tunnel-enabled",message:"Chat tunneling is enabled \u2014 allowlisted users can publish public URLs to local HTTP/TCP services.",fixHint:"Disable tunnelEnabled or restrict allowlists if you do not want remote tunnel control."}),tt()||n.push({severity:"warn",code:"tunnel-no-token",message:"Chat tunneling is enabled but no tunnel token is configured.",fixHint:"Run `omnish tunnel login` or set OMNISH_TUNNEL_TOKEN for the gateway process."})),e.tunnelRelayUrl.trim()&&e.tunnelRelayUrl.trim()!==be&&n.push({severity:"info",code:"tunnel-relay-custom",message:`Custom tunnel relay configured: ${e.tunnelRelayUrl.trim()}`,fixHint:"Use the default relay only if you trust the operator of that endpoint."}),n}function On(e){return e.some(t=>t.severity==="error")}function Id(e,t){switch(t){case"error":return vr(e,"[ERROR]");case"warn":return ae(e,"[WARN]");case"info":return g(e,"[INFO]")}}function Qo(e,t,n,r){if(r.length!==0){t.push(B(e,`${n} (${r.length}):`));for(let o of r)t.push(` ${Id(e,o.severity)} ${g(e,`${o.code}:`)} ${k(e,o.message)}`),o.detail&&t.push(` ${g(e,o.detail)}`),o.fixHint&&t.push(` ${g(e,`Fix: ${o.fixHint}`)}`);t.push("")}}function Vo(e,t){if(e.length===0)return[`${me(t,"Security check:")} ${k(t,"no issues reported by automated rules.")}`,"",g(t,"Note: allowlisted remote shell access is still equivalent to sharing credentials with those identities.")].join(`
7
+ `);let n=e.filter(a=>a.severity==="error"),r=e.filter(a=>a.severity==="warn"),o=e.filter(a=>a.severity==="info"),i=[`${me(t,"Security check:")} `+k(t,`${n.length} error(s), ${r.length} warning(s), ${o.length} note(s).`),""];return Qo(t,i,"Errors",n),Qo(t,i,"Warnings",r),Qo(t,i,"Notes",o),i.push(g(t,"Allowlisted identities can run commands as this user; treat them like passwords.")),i.join(`
8
+ `).trimEnd()}function Xo(e){return{errors:e.filter(t=>t.severity==="error").length,warns:e.filter(t=>t.severity==="warn").length,infos:e.filter(t=>t.severity==="info").length}}function la(e){return`${JSON.stringify({findings:e,summary:Xo(e)},null,2)}
9
+ `}function ca(e,t){let n=e.filter(a=>a.severity==="error").length,r=e.filter(a=>a.severity==="warn").length,o=e.filter(a=>a.severity==="info").length;if(n===0&&r===0&&o===0)return"security: ok (no automated findings)";let s=[];n&&s.push(`${n} error(s)`),r&&s.push(`${r} warning(s)`),o&&s.push(`${o} note(s)`);let i=t??"run `omnish security` for details";return`security: ${s.join(", ")} \u2014 ${i}`}function ua(e,t,n){let r=e.filter(l=>l.severity==="error").length,o=e.filter(l=>l.severity==="warn").length,s=e.filter(l=>l.severity==="info").length;if(r===0&&o===0&&s===0)return`${me(t,"security:")} ${k(t,"ok (no automated findings)")}`;let i=[];r&&i.push(vr(t,`${r} error(s)`)),o&&i.push(ae(t,`${o} warning(s)`)),s&&i.push(g(t,`${s} note(s)`));let a=n??"run `omnish security` for details";return`${me(t,"security:")} ${i.join(", ")} ${g(t,`\u2014 ${a}`)}`}var Me="- ";function m(e){return{wa:e,tg:e}}function ie(e,t){return{wa:e,tg:t,tgHtml:!0}}function Pe(e,t){return t==="whatsapp"?{text:e.wa}:e.tgHtml?{text:e.tg,parseModeHtml:!0}:{text:e.tg}}function G(e){return e.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;")}function fe(e){return e.replace(/[*_~`]/g,t=>({"*":"\u2217",_:"\uFF3F","~":"\u02DC","`":"\u2032"})[t]??t)}function Ad(e){let t=[];for(let n of e)switch(n.kind){case"title":t.push("",`*${n.text}*`,"");break;case"sub":t.push("",`_${n.text}_`,"");break;case"gap":t.push("");break;case"p":t.push(n.text);break;case"bullet":t.push(`${Me}${n.text}`);break}return t.join(`
10
+ `).replace(/^\n+/,"").trimEnd()}function Pd(e){let t=[];for(let n of e)switch(n.kind){case"title":t.push("",`<b>${G(n.text)}</b>`,"");break;case"sub":t.push("",`<i>${G(n.text)}</i>`,"");break;case"gap":t.push("");break;case"p":t.push(G(n.text));break;case"bullet":t.push(`\u2022 ${G(n.text)}`);break}return t.join(`
11
+ `).replace(/^\n+/,"").trimEnd()}function z(e){return{wa:Ad(e),tg:Pd(e),tgHtml:!0}}function en(e){return[{kind:"title",text:"Omnish \u2014 quick help"},{kind:"p",text:"Per-chat shell cwd is stored under your data dir (see /wa help)."},{kind:"gap"},{kind:"sub",text:"Run commands"},{kind:"bullet",text:`${e.commandPrefix}<command> \u2014 sync shell in session cwd (timeout ${e.syncTimeoutMs} ms)`},{kind:"bullet",text:`${e.commandPrefix}cd <dir> \u2014 change session cwd (${e.commandPrefix}cd alone \u2192 home)`},{kind:"bullet",text:"!!start | !!stop \u2014 free shell (plain \u2192 sync shell only when no focused /apps session); space optional"},{kind:"bullet",text:"/bg <cmd> \u2014 background job; optional -n name; /jobs, /log, /tail, /kill (id or name)"},{kind:"bullet",text:e.tunnelEnabled?"/tunnel login|logout|status|http|tcp \u2014 login/status anytime; /tunnels needs tunnelEnabled":"/tunnel expose/list off until tunnelEnabled true (login/status still work)"},{kind:"bullet",text:"/apps \u2026 \u2014 interactive app sessions (/apps help)"},{kind:"bullet",text:"/send selectors \u2014 host files \u2192 chat (/file); caption: selectors -- note"},{kind:"bullet",text:"/files \u2014 full help for /send and saving inbound media"},{kind:"bullet",text:"/receive \u2014 set this chat\u2019s inbound folder (e.g. session cwd)"},{kind:"bullet",text:"/computers \xB7 /pcs \xB7 /c \u2014 chat-driven cluster (/c here to take over, /c help)"},{kind:"bullet",text:"/config \u2014 view or change server settings (/config help, /config keys)"},{kind:"bullet",text:"/service \u2014 background service status, install hints, logs (/service help)"},{kind:"bullet",text:"/shortcut \u2014 this chat & shared shortcuts (/shortcut help); !name or /name expands chat override first, then shared"},{kind:"bullet",text:"/run \u2014 recipe-based task runs (/r); per-chat or gateway-shared /run add \u2014 /run list, /run help"},{kind:"bullet",text:"/cowork | /cw \u2014 scheduled & on-demand shell tasks; logs to ~/Cowork/\u2026 (/cowork help)"},{kind:"gap"},{kind:"sub",text:"Setup & gateway"},{kind:"bullet",text:"/wa help \u2014 WhatsApp link, allowlist"},{kind:"bullet",text:"/tg help \u2014 Telegram; /tg token <paste>"},{kind:"bullet",text:"/reload | /restart \u2014 apply config"},{kind:"bullet",text:"/updates \u2014 npm latest + optional notice URL; /updates cached \u2014 last snapshot"},{kind:"bullet",text:"/security \u2014 posture report; /security summary; /security tips; /security help"},{kind:"bullet",text:"/gateway | /gw | /mode \u2014 show or set gatewayMode"},{kind:"bullet",text:"/allow +E164 | /allow tg:id \u2014 allowlist; /deny \u2026; /allowlist"},{kind:"bullet",text:"/help \u2014 this message"},{kind:"gap"},{kind:"sub",text:"Terms"},{kind:"bullet",text:"Gateway = omnish on this host; shell runs here. Standalone = messengers on this host; attached = communication layer routes chat here."},{kind:"bullet",text:'Communication layer (when shipped) = link chats once, device token on this CLI; shell stays here. Standalone = no layer. /service "platform" = OS.'}]}function da(e){let t=e.serviceInstallFromChat?[{kind:"bullet",text:"/service install \u2014 user-level systemd (Linux) or LaunchAgent (macOS)"},{kind:"bullet",text:"/service uninstall \u2014 remove that unit"}]:[{kind:"bullet",text:"/service install / uninstall \u2014 off by default. Enable: /config set serviceInstallFromChat true (same trust as shell)"}];return[{kind:"title",text:"Service and boot"},{kind:"p",text:"Inspect the gateway, get OS-specific install steps, or (if enabled) write the user service from chat. Same from the host terminal: omnish service help."},{kind:"p",text:'"platform" in status means your OS (Linux/macOS/Windows), not a hosted omnish account.'},{kind:"bullet",text:"/service status \u2014 platform, data dir, pidfile, node + entry script"},{kind:"bullet",text:"/service instructions \u2014 copy-paste commands for this machine"},{kind:"bullet",text:"/service logs [n] \u2014 tail default gateway log (default 80 lines)"},...t,{kind:"gap"},{kind:"p",text:"See https://omnish.dev and docs/guides/background-and-boot.md."}]}function pa(){return[{kind:"title",text:"Chat config"},{kind:"p",text:`Same trust as shell \u2014 allowlisted senders can change many keys saved to ${L}.`},{kind:"gap"},{kind:"bullet",text:"/config show \u2014 full snapshot (telegramBotToken masked)"},{kind:"bullet",text:"/config get <key> \u2014 single field"},{kind:"bullet",text:"/config set <key> <value> \u2014 values can be quoted for paths with spaces"},{kind:"bullet",text:"/config keys \u2014 allowed keys for /config set (includes tunnel*, chatLlm*, cluster*, \u2026)"},{kind:"gap"},{kind:"p",text:"After edits: /reload while omnish run is active (needed for gatewayMode and telegramBotToken)."}]}function Zo(){return[{kind:"title",text:"Gateway mode"},{kind:"p",text:"Which transports omnish uses:"},{kind:"bullet",text:"whatsapp (wa, w) \u2014 WhatsApp DMs only"},{kind:"bullet",text:"telegram (tg, t) \u2014 Telegram bot only"},{kind:"bullet",text:"both (all, b) \u2014 WhatsApp + Telegram"},{kind:"gap"},{kind:"sub",text:"Commands"},{kind:"bullet",text:"/gateway \u2014 current mode + hints (includes last update snapshot if any)"},{kind:"bullet",text:"/gateway telegram \u2014 save (auto-reloads Telegram if gateway is up)"},{kind:"bullet",text:"/gw both \u2014 short form; /mode wa \u2014 same"},{kind:"gap"},{kind:"p",text:`Config: ${L}`}]}function ma(e){let t=["*Gateway status*","",`Mode: *${e.gatewayMode}*`,"- whatsapp \u2014 WhatsApp only","- telegram \u2014 Telegram only","- both \u2014 both channels","",`WhatsApp auth: ${e.authPresent?"present":"missing (omnish link)"}`,`Telegram token: ${e.tokenSet?"set":"not set"}`,`Allowlists: ${e.allowN} WA \xB7 ${e.tgAllowN} Telegram`,""];e.updateBrief&&t.push(`Updates: ${fe(e.updateBrief)}`,""),t.push("Change: /gateway both \xB7 /gw tg","/updates \u2014 npm + optional notice URL",`Config: ${fe(L)}`);let n=["<b>Gateway status</b>","",`<b>${G(e.gatewayMode)}</b> <i>current mode</i>`,"\u2022 whatsapp \u2014 WhatsApp only","\u2022 telegram \u2014 Telegram only","\u2022 both \u2014 both channels","",`${e.authPresent?"\u2713":"\u26A0"} WhatsApp auth: ${e.authPresent?"present":"missing \u2014 run omnish link"}`,`${e.tokenSet?"\u2713":"\u26A0"} Telegram token: ${e.tokenSet?"set":"not set"}`,`Allowlists: ${e.allowN} WA \xB7 ${e.tgAllowN} Telegram`,""];return e.updateBrief&&n.push(`<b>Updates</b> ${G(e.updateBrief)}`,""),n.push("<i>Change:</i> /gateway both \xB7 /gw tg","<i>/updates</i> \u2014 npm + optional notice URL",G(`Config: ${L}`)),ie(t.join(`
11
12
  `),n.join(`
12
- `))}function Xt(e){let t=["*Update check*","",`Running: *${le(e.runningVersion)}*`,`Checked (UTC): ${e.checkedAtIso}`,`npm package: ${le(e.registryPackage)}`];e.registryError?t.push("",`Registry: ${le(e.registryError)}`):e.registryLatest&&t.push("",e.updateAvailable?`*Newer version on npm:* ${le(e.registryLatest)} (upgrade when ready).`:`npm latest: ${le(e.registryLatest)} (this install is current or newer).`),e.infoError?t.push("",`Notice URL: ${le(e.infoError)}`):e.infoMessage&&(t.push("",`Notice: ${le(e.infoMessage)}`),e.infoLink&&t.push(le(e.infoLink))),t.push("","Config: updateCheckEnabled, updateCheckIntervalMs, updateCheckPackageName, updateInfoUrl");let n=["<b>Update check</b>","",`<b>Running</b> <code>${D(e.runningVersion)}</code>`,`<b>Checked (UTC)</b> ${D(e.checkedAtIso)}`,`<b>npm package</b> <code>${D(e.registryPackage)}</code>`];return e.registryError?n.push("",`<b>Registry</b> ${D(e.registryError)}`):e.registryLatest&&n.push("",e.updateAvailable?`<b>Newer on npm</b> <code>${D(e.registryLatest)}</code>`:`<b>npm latest</b> <code>${D(e.registryLatest)}</code> (current or newer here)`),e.infoError?n.push("",`<b>Notice URL</b> ${D(e.infoError)}`):e.infoMessage&&(n.push("",`<b>Notice</b> ${D(e.infoMessage)}`),e.infoLink&&n.push(D(e.infoLink))),n.push("","<i>Config keys:</i> updateCheckEnabled, updateCheckIntervalMs, updateCheckPackageName, updateInfoUrl"),Y(t.join(`
13
+ `))}function Ln(e){let t=["*Update check*","",`Running: *${fe(e.runningVersion)}*`,`Checked (UTC): ${e.checkedAtIso}`,`npm package: ${fe(e.registryPackage)}`];e.registryError?t.push("",`Registry: ${fe(e.registryError)}`):e.registryLatest&&t.push("",e.updateAvailable?`*Newer version on npm:* ${fe(e.registryLatest)} (upgrade when ready).`:`npm latest: ${fe(e.registryLatest)} (this install is current or newer).`),e.infoError?t.push("",`Notice URL: ${fe(e.infoError)}`):e.infoMessage&&(t.push("",`Notice: ${fe(e.infoMessage)}`),e.infoLink&&t.push(fe(e.infoLink))),t.push("","Config: updateCheckEnabled, updateCheckIntervalMs, updateCheckPackageName, updateInfoUrl");let n=["<b>Update check</b>","",`<b>Running</b> <code>${G(e.runningVersion)}</code>`,`<b>Checked (UTC)</b> ${G(e.checkedAtIso)}`,`<b>npm package</b> <code>${G(e.registryPackage)}</code>`];return e.registryError?n.push("",`<b>Registry</b> ${G(e.registryError)}`):e.registryLatest&&n.push("",e.updateAvailable?`<b>Newer on npm</b> <code>${G(e.registryLatest)}</code>`:`<b>npm latest</b> <code>${G(e.registryLatest)}</code> (current or newer here)`),e.infoError?n.push("",`<b>Notice URL</b> ${G(e.infoError)}`):e.infoMessage&&(n.push("",`<b>Notice</b> ${G(e.infoMessage)}`),e.infoLink&&n.push(G(e.infoLink))),n.push("","<i>Config keys:</i> updateCheckEnabled, updateCheckIntervalMs, updateCheckPackageName, updateInfoUrl"),ie(t.join(`
13
14
  `),n.join(`
14
- `))}function _s(e){return[{kind:"title",text:"WhatsApp setup"},{kind:"p",text:"Do these on the machine running omnish (QR is shown in the terminal, not in chat)."},{kind:"gap"},{kind:"sub",text:"Steps"},{kind:"bullet",text:"Install omnish; build native deps (node-pty, Baileys)."},{kind:"bullet",text:`Data dir: ${N} (override: OMNISH_HOME). Auth: <data-dir>/auth/`},{kind:"bullet",text:"Run: omnish link \u2014 scan QR in WhatsApp \u2192 Linked devices."},{kind:"bullet",text:"Allow your number: omnish allow +<E164>"},{kind:"bullet",text:`Set gatewayMode to "whatsapp" or "both" in ${P}`},{kind:"bullet",text:"Run: omnish run \u2014 then use !cmd or /help here."},{kind:"gap"},{kind:"sub",text:"Troubleshooting"},{kind:"bullet",text:"Denied in logs but allowlist OK: may be LID mapping \u2014 try OMNISH_VERBOSE=1."},{kind:"bullet",text:"401 on link: omnish link --force after pnpm approve-builds && pnpm install"},{kind:"gap"},{kind:"p",text:`Current gatewayMode: ${e.gatewayMode}`}]}function Bs(e){let t=!!oe(e);return[{kind:"title",text:"Telegram setup"},{kind:"p",text:"Configure on the host that runs omnish."},{kind:"gap"},{kind:"sub",text:"Steps"},{kind:"bullet",text:"@BotFather \u2192 /newbot \u2014 copy the API token."},{kind:"bullet",text:`Token: /tg token <paste>, or ${P}, or TELEGRAM_BOT_TOKEN env (env wins).`},{kind:"bullet",text:'Your numeric id: @userinfobot / @getidsbot, or logs on "telegram denied".'},{kind:"bullet",text:"Allow: omnish allow tg:<user_id>"},{kind:"bullet",text:`gatewayMode: "telegram" or "both" in ${P}; list ids in telegramAllowFrom.`},{kind:"bullet",text:"omnish run \u2014 then DM the bot with !cmd or /help."},{kind:"bullet",text:"After edits: /reload or /gateway both (saves + reloads when running)."},{kind:"gap"},{kind:"sub",text:"Notes"},{kind:"bullet",text:"Private text DMs only; groups ignored. Token stays secret."},{kind:"bullet",text:"/tg token stores token in config; chat history may retain it \u2014 prefer SSH/editor if worried."},{kind:"gap"},{kind:"p",text:`Token on host: ${t?"set (env or config)":"not set \u2014 add telegramBotToken or TELEGRAM_BOT_TOKEN"}`},{kind:"p",text:`gatewayMode: ${e.gatewayMode} \xB7 telegramAllowFrom: ${e.telegramAllowFrom.length} entries`}]}function Hs(e){let t=["*Allowlists*",""],n=["<b>Allowlists</b>",""];for(let r of e){let o=r.items.length?r.items.join(", "):"(none)";t.push(`*${r.label}* (${r.items.length})`,le(o),""),n.push(`<b>${D(r.label)}</b> (${r.items.length})`,D(o),"")}return Y(t.join(`
15
+ `))}function fa(e){return[{kind:"title",text:"WhatsApp setup"},{kind:"p",text:"Do these on the machine running omnish (QR is shown in the terminal, not in chat)."},{kind:"gap"},{kind:"sub",text:"Steps"},{kind:"bullet",text:"Install omnish; build native deps (node-pty, Baileys)."},{kind:"bullet",text:`Data dir: ${U} (override: OMNISH_HOME). Auth: <data-dir>/auth/`},{kind:"bullet",text:"Run: omnish link \u2014 scan QR in WhatsApp \u2192 Linked devices."},{kind:"bullet",text:"Allow your number: omnish allow +<E164>"},{kind:"bullet",text:`Set gatewayMode to "whatsapp" or "both" in ${L}`},{kind:"bullet",text:"Run: omnish run \u2014 then use !cmd or /help here."},{kind:"gap"},{kind:"sub",text:"Troubleshooting"},{kind:"bullet",text:"Denied in logs but allowlist OK: may be LID mapping \u2014 try OMNISH_VERBOSE=1."},{kind:"bullet",text:"401 on link: omnish link --force after pnpm approve-builds && pnpm install"},{kind:"gap"},{kind:"p",text:`Current gatewayMode: ${e.gatewayMode}`}]}function ga(e){let t=!!pe(e);return[{kind:"title",text:"Telegram setup"},{kind:"p",text:"Configure on the host that runs omnish."},{kind:"gap"},{kind:"sub",text:"Steps"},{kind:"bullet",text:"@BotFather \u2192 /newbot \u2014 copy the API token."},{kind:"bullet",text:`Token: /tg token <paste>, or ${L}, or TELEGRAM_BOT_TOKEN env (env wins).`},{kind:"bullet",text:'Your numeric id: @userinfobot / @getidsbot, or logs on "telegram denied".'},{kind:"bullet",text:"Allow: omnish allow tg:<user_id>"},{kind:"bullet",text:`gatewayMode: "telegram" or "both" in ${L}; list ids in telegramAllowFrom.`},{kind:"bullet",text:"omnish run \u2014 then DM the bot with !cmd or /help."},{kind:"bullet",text:"After edits: /reload or /gateway both (saves + reloads when running)."},{kind:"gap"},{kind:"sub",text:"Notes"},{kind:"bullet",text:"Private text DMs only; groups ignored. Token stays secret."},{kind:"bullet",text:"/tg token stores token in config; chat history may retain it \u2014 prefer SSH/editor if worried."},{kind:"gap"},{kind:"p",text:`Token on host: ${t?"set (env or config)":"not set \u2014 add telegramBotToken or TELEGRAM_BOT_TOKEN"}`},{kind:"p",text:`gatewayMode: ${e.gatewayMode} \xB7 telegramAllowFrom: ${e.telegramAllowFrom.length} entries`}]}function ha(e){let t=["*Allowlists*",""],n=["<b>Allowlists</b>",""];for(let r of e){let o=r.items.length?r.items.join(", "):"(none)";t.push(`*${r.label}* (${r.items.length})`,fe(o),""),n.push(`<b>${G(r.label)}</b> (${r.items.length})`,G(o),"")}return ie(t.join(`
15
16
  `).trimEnd(),n.join(`
16
- `).trimEnd())}function Dr(e){let t=[{label:"allowFrom (WhatsApp)",items:e.allowFrom},{label:"telegramAllowFrom",items:e.telegramAllowFrom}],n=["*Allowlist updated*","","Saved to config.",""],r=["<b>Allowlist updated</b>","","Saved to config.",""];for(let o of t){let s=o.items.length?o.items.join(", "):"(none)";n.push(`*${o.label}* (${o.items.length})`,le(s),""),r.push(`<b>${D(o.label)}</b> (${o.items.length})`,D(s),"")}return Y(n.join(`
17
+ `).trimEnd())}function es(e){let t=[{label:"allowFrom (WhatsApp)",items:e.allowFrom},{label:"telegramAllowFrom",items:e.telegramAllowFrom}],n=["*Allowlist updated*","","Saved to config.",""],r=["<b>Allowlist updated</b>","","Saved to config.",""];for(let o of t){let s=o.items.length?o.items.join(", "):"(none)";n.push(`*${o.label}* (${o.items.length})`,fe(s),""),r.push(`<b>${G(o.label)}</b> (${o.items.length})`,G(s),"")}return ie(n.join(`
17
18
  `).trimEnd(),r.join(`
18
- `).trimEnd())}function Ds(e){let t=["*Token saved*","","telegramBotToken written to config (not echoed).",`Config: ${le(P)}`,...e.flatMap(r=>["",r]),"","Send /reload so the gateway picks it up."].join(`
19
- `),n=["<b>Token saved</b>","","telegramBotToken written to config (not echoed).",D(`Config: ${P}`),...e.map(r=>`<i>${D(r)}</i>`),"","Send /reload so the gateway picks it up."].join(`
19
+ `).trimEnd())}function ya(e){let t=["*Token saved*","","telegramBotToken written to config (not echoed).",`Config: ${fe(L)}`,...e.flatMap(r=>["",r]),"","Send /reload so the gateway picks it up."].join(`
20
+ `),n=["<b>Token saved</b>","","telegramBotToken written to config (not echoed).",G(`Config: ${L}`),...e.map(r=>`<i>${G(r)}</i>`),"","Send /reload so the gateway picks it up."].join(`
20
21
  `)+`
21
- `;return Y(t.trimEnd(),n.trimEnd())}function js(){return j([{kind:"title",text:"That does not look like a bot token"},{kind:"p",text:"Expected from @BotFather: one token, no spaces, like 123456789:AA_heG\u2026"},{kind:"bullet",text:"Example: /tg token 123456789:AAAbcd\u2026"}])}function Us(e,t,n){let r=t?`
22
+ `;return ie(t.trimEnd(),n.trimEnd())}function wa(){return z([{kind:"title",text:"That does not look like a bot token"},{kind:"p",text:"Expected from @BotFather: one token, no spaces, like 123456789:AA_heG\u2026"},{kind:"bullet",text:"Example: /tg token 123456789:AAAbcd\u2026"}])}function ka(e,t,n){let r=t?`
22
23
 
23
24
  ${t}`:n?`
24
25
 
25
26
  Start omnish run on the host to apply (or /reload from a running gateway).`:"",o=t?`
26
27
 
27
- ${D(t)}`:n?`
28
+ ${G(t)}`:n?`
28
29
 
29
30
  Start omnish run on the host to apply (or /reload from a running gateway).`:"",s=`*gatewayMode saved*
30
31
 
31
- "${le(e)}"
32
- ${le(P)}${r}`,i=`<b>gatewayMode saved</b>
32
+ "${fe(e)}"
33
+ ${fe(L)}${r}`,i=`<b>gatewayMode saved</b>
33
34
 
34
- <code>${D(e)}</code>
35
- ${D(P)}${o}`;return Y(s,i)}function Ws(){return j([{kind:"title",text:"/allow \u2014 add to allowlist"},{kind:"bullet",text:"/allow +<E164> \u2014 WhatsApp (country code, no spaces)"},{kind:"bullet",text:"/allow tg:<user_id> \u2014 Telegram numeric id"},{kind:"gap"},{kind:"p",text:"Examples: /allow +15551234567 \xB7 /allow tg:987654321"}])}function Gs(){return j([{kind:"title",text:"/deny \u2014 remove from allowlist"},{kind:"p",text:"Same forms as /allow: +E164 or tg:id"}])}function Js(){return j([{kind:"title",text:"/bg \u2014 background job"},{kind:"p",text:"Usage: /bg <shell command>"},{kind:"bullet",text:"Optional name: /bg -n mybuild npm run build \xB7 /bg --name mybuild \u2026 \xB7 /bg --name=mybuild \u2026"},{kind:"bullet",text:"Then use /log mybuild, /tail mybuild, /kill mybuild (or the 8-char id). Names: letters, digits, . _ - up to 64 chars."},{kind:"bullet",text:"Example: /bg sleep 30 && echo done"}])}function jr(){return j([{kind:"title",text:"/send \u2014 push a host file to this chat"},{kind:"p",text:"Usage: /send <selectors> [-- caption] \u2014 selectors resolve from session cwd."},{kind:"bullet",text:"/send ./photo.png"},{kind:"bullet",text:"/send ./clip1.mp4,./clip2.mp4 -- Launch set"},{kind:"bullet",text:"/send **/*.mp4"},{kind:"bullet",text:"/send /abs/path/report.pdf -- Q4 draft"},{kind:"bullet",text:"Selectors: file1,file2 | *.ext | **/*.ext"},{kind:"bullet",text:"Alias: /file \u2026"},{kind:"gap"},{kind:"p",text:"Caps: fileSendMaxBytes / fileReceiveMaxBytes (0 = no omnish cap). Where inbound media is saved: fileReceiveRootMode + fileReceiveRootPath / fileInboxSubdir \u2014 send /files for details."}])}function Ur(){return j([{kind:"title",text:"Files \u2014 send & receive"},{kind:"sub",text:"Host \u2192 chat"},{kind:"bullet",text:"/send <selectors> or /file \u2026 \u2014 selectors support file1,file2, *.ext, **/*.ext from this chat\u2019s session cwd (same as ! shell); optional caption after -- "},{kind:"bullet",text:"fileSendMaxBytes in config caps outbound size (0 = no omnish cap)."},{kind:"gap"},{kind:"sub",text:"Chat \u2192 host"},{kind:"bullet",text:"Send a photo, video, document, audio, etc. in this DM; omnish saves it and replies with Saved: <path> (or an error)."},{kind:"bullet",text:"Folders: <root>/<peer>/<YYYY-MM-DD>/<filename> \u2014 root comes from config below."},{kind:"bullet",text:"fileReceiveRootMode: downloads (home/Downloads/Omnish) \xB7 omnishData (data dir + fileInboxSubdir) \xB7 sessionCwd \xB7 processCwd \xB7 fixed (needs absolute fileReceiveRootPath)."},{kind:"bullet",text:"fileReceiveMaxBytes \u2014 inbound size cap (0 = no omnish cap)."},{kind:"gap"},{kind:"p",text:`Edit config on the host (${P}) then use /reload. Tip: omnish status shows the data directory.`},{kind:"gap"},{kind:"p",text:"Per-chat folder: /receive here saves inbound files under your session cwd (!cd); /receive default clears."}])}function Wr(){return j([{kind:"title",text:"/receive \u2014 inbound files for this chat"},{kind:"p",text:"Stored on the host with your session (sessions.json). Does not edit config.json."},{kind:"bullet",text:"/receive \u2014 show current setting and resolved save folder"},{kind:"bullet",text:"/receive here \u2014 save uploads under this chat\u2019s session cwd (same as !cd); subfolders: peer / date / file"},{kind:"bullet",text:"/receive default \u2014 clear per-chat rule; use global fileReceiveRootMode (/files, config.json)"},{kind:"gap"},{kind:"p",text:"Aliases: here = cwd = session = dir \xB7 default = global = reset"}])}function zs(e,t){let n=xs(t),r=q(t),o="";try{o=Ct(e,t)}catch(s){o=`(${String(s)})`}return j(n==="sessionCwd"?[{kind:"title",text:"/receive \u2014 this chat"},{kind:"p",text:"Per-chat: inbound media saves under your session folder (updated when you !cd)."},{kind:"bullet",text:`Session cwd: ${r.cwd}`},{kind:"bullet",text:`Next file root: ${o}`},{kind:"p",text:"Send /receive default to follow server config instead."}]:[{kind:"title",text:"/receive \u2014 this chat"},{kind:"p",text:"Per-chat override: off \u2014 using global fileReceiveRootMode from config."},{kind:"bullet",text:`Global mode: ${e.fileReceiveRootMode}`},{kind:"bullet",text:`Next file root: ${o}`},{kind:"p",text:"Send /receive here to pin saves to your current session folder."}])}function qs(){return j([{kind:"title",text:"Unknown /wa command"},{kind:"p",text:"Send /wa help for setup."}])}function Qs(){return j([{kind:"title",text:"Unknown /tg command"},{kind:"p",text:"Send /tg help or /tg token <botfather_token>."}])}function Ys(){return j([{kind:"title",text:"Free shell mode on"},{kind:"p",text:"Plain messages run as sync shell (no command prefix)."},{kind:"bullet",text:"Send !!stop to turn off."},{kind:"bullet",text:"Apps: plain DMs no longer go to the focused app \u2014 use >name text or /apps send."}])}function Vs(e){return j([{kind:"title",text:"Unknown command"},{kind:"p",text:`Try /help or ${e.commandPrefix}<command>.`},{kind:"gap"},...Rt(e)])}function Ks(e){return j([{kind:"title",text:"No command matched"},{kind:"p",text:`Use ${JSON.stringify(e.commandPrefix)}, a slash command, or /apps help.`},{kind:"gap"},...Rt(e)])}function Xs(){let e=[{kind:"title",text:"Unknown mode"},{kind:"p",text:"Use: whatsapp | telegram | both (short: wa, tg, b)"},{kind:"bullet",text:"/gateway both \xB7 /gw wa \xB7 /mode telegram"},{kind:"gap"},...Hr()];return j(e)}function Gr(){return[{kind:"title",text:"Shortcuts (this gateway)"},{kind:"p",text:"Stored per-chat or shared for every chat on this gateway. Expansion: this chat wins, then shared. Bare token only \u2014 for task injection use /run recipes."},{kind:"p",text:"Scope flags: -g or --global = shared on this gateway; -p or --chat = private to this chat."},{kind:"gap"},{kind:"sub",text:"Manage"},{kind:"bullet",text:"/shortcut add <name> <command\u2026> \u2014 private by default; add -g|--global to share on this gateway"},{kind:"bullet",text:"/shortcut add -p|--chat <name> <command\u2026> \u2014 explicit private (this chat)"},{kind:"bullet",text:"/shortcut set <name> <command\u2026> \u2014 overwrite in chosen bucket; scope-only (same line): /shortcut set -g <name> or /shortcut set <name> -g (share); /shortcut set -p <name> or /shortcut set <name> -p (private)"},{kind:"bullet",text:"/shortcut list \u2014 merged view (/shortcuts); list --chat | -p | list --global | -g"},{kind:"bullet",text:"/shortcut show <name>; show --global|-g|--chat|-p to read one bucket"},{kind:"bullet",text:"/shortcut remove <name> \u2014 private bucket; remove --global|-g drops shared \xB7 rm, del \xB7 --chat|-p"},{kind:"bullet",text:"/alias \u2026 \u2014 same as /shortcut \u2026 (including /aliases \u2026)"},{kind:"gap"},{kind:"sub",text:"Run"},{kind:"bullet",text:"!name \u2014 expands once to the saved line (e.g. !cd \u2026 updates session cwd)"},{kind:"bullet",text:"/name \u2014 same expansion for slash-style navigation"},{kind:"p",text:"Shortcut bodies may start with !, /, etc.; nested shortcuts are not expanded."}]}function Zs(e){if(e.length===0)return p("(no shortcuts \u2014 /shortcut add <name> <command\u2026> or /shortcut add --global <name> <command\u2026>)");let t=e.every(l=>l.scope==="global"),n=e.every(l=>l.scope==="chat"),r=t?"_Shared (every chat)_":n?"_This chat only_":"_This chat + shared_",o=t?"<i>Shared (every chat)</i>":n?"<i>This chat only</i>":"<i>This chat + shared</i>",s=e.some(l=>l.scope==="chat")&&e.some(l=>l.scope==="global"),i=["*Shortcuts*",r,"",...e.map(l=>{let u=s?l.scope==="chat"?"[chat] ":"[global] ":"";return`${ge}\`${u}${l.name}\` \u2192 ${l.body}`})].join(`
36
- `),a=["<b>Shortcuts</b>",o,"",...e.map(l=>{let c=`${s?l.scope==="chat"?"[chat] ":"[global] ":""}${l.name}`;return`\u2022 <code>${D(c)}</code> \u2192 ${D(l.body)}`})].join(`
37
- `);return Y(i,a)}function Zt(e,t,n="chat"){return p(`Shortcut saved: ${e}
35
+ <code>${G(e)}</code>
36
+ ${G(L)}${o}`;return ie(s,i)}function ba(){return z([{kind:"title",text:"/allow \u2014 add to allowlist"},{kind:"bullet",text:"/allow +<E164> \u2014 WhatsApp (country code, no spaces)"},{kind:"bullet",text:"/allow tg:<user_id> \u2014 Telegram numeric id"},{kind:"gap"},{kind:"p",text:"Examples: /allow +15551234567 \xB7 /allow tg:987654321"}])}function Sa(){return z([{kind:"title",text:"/deny \u2014 remove from allowlist"},{kind:"p",text:"Same forms as /allow: +E164 or tg:id"}])}function va(){return z([{kind:"title",text:"/bg \u2014 background job"},{kind:"p",text:"Usage: /bg [flags] <shell command>"},{kind:"bullet",text:"Optional name: /bg -n mybuild npm run build \xB7 /bg --name mybuild \u2026 \xB7 /bg --name=mybuild \u2026"},{kind:"bullet",text:"Notify on completion: /bg --notify <cmd> or /bg -N <cmd> \u2014 sends a message when the job finishes (with exit status)."},{kind:"bullet",text:"Combine flags: /bg -N -n deploy git pull && docker compose up -d"},{kind:"bullet",text:"Then use /log mybuild, /tail mybuild, /kill mybuild (or the 8-char id). Names: letters, digits, . _ - up to 64 chars."},{kind:"bullet",text:"Example: /bg sleep 30 && echo done"}])}function ts(){return z([{kind:"title",text:"/send \u2014 push a host file to this chat"},{kind:"p",text:"Usage: /send <selectors> [-- caption] \u2014 selectors resolve from session cwd."},{kind:"bullet",text:"/send ./photo.png"},{kind:"bullet",text:"/send ./clip1.mp4,./clip2.mp4 -- Launch set"},{kind:"bullet",text:"/send **/*.mp4"},{kind:"bullet",text:"/send /abs/path/report.pdf -- Q4 draft"},{kind:"bullet",text:"Selectors: file1,file2 | *.ext | **/*.ext"},{kind:"bullet",text:"Alias: /file \u2026"},{kind:"gap"},{kind:"p",text:"Caps: fileSendMaxBytes / fileReceiveMaxBytes (0 = no omnish cap). Where inbound media is saved: fileReceiveRootMode + fileReceiveRootPath / fileInboxSubdir \u2014 send /files for details."}])}function ns(){return z([{kind:"title",text:"Files \u2014 send & receive"},{kind:"sub",text:"Host \u2192 chat"},{kind:"bullet",text:"/send <selectors> or /file \u2026 \u2014 selectors support file1,file2, *.ext, **/*.ext from this chat\u2019s session cwd (same as ! shell); optional caption after -- "},{kind:"bullet",text:"fileSendMaxBytes in config caps outbound size (0 = no omnish cap)."},{kind:"gap"},{kind:"sub",text:"Chat \u2192 host"},{kind:"bullet",text:"Send a photo, video, document, audio, etc. in this DM; omnish saves it and replies with Saved: <path> (or an error)."},{kind:"bullet",text:"Folders: <root>/<peer>/<YYYY-MM-DD>/<filename> \u2014 root comes from config below."},{kind:"bullet",text:"fileReceiveRootMode: downloads (home/Downloads/Omnish) \xB7 omnishData (data dir + fileInboxSubdir) \xB7 sessionCwd \xB7 processCwd \xB7 fixed (needs absolute fileReceiveRootPath)."},{kind:"bullet",text:"fileReceiveMaxBytes \u2014 inbound size cap (0 = no omnish cap)."},{kind:"gap"},{kind:"p",text:`Edit config on the host (${L}) then use /reload. Tip: omnish status shows the data directory.`},{kind:"gap"},{kind:"p",text:"Per-chat folder: /receive here saves inbound files under your session cwd (!cd); /receive default clears."}])}function rs(){return z([{kind:"title",text:"/receive \u2014 inbound files for this chat"},{kind:"p",text:"Stored on the host with your session (sessions.json). Does not edit config.json."},{kind:"bullet",text:"/receive \u2014 show current setting and resolved save folder"},{kind:"bullet",text:"/receive here \u2014 save uploads under this chat\u2019s session cwd (same as !cd); subfolders: peer / date / file"},{kind:"bullet",text:"/receive default \u2014 clear per-chat rule; use global fileReceiveRootMode (/files, config.json)"},{kind:"gap"},{kind:"p",text:"Aliases: here = cwd = session = dir \xB7 default = global = reset"}])}function xa(e,t){let n=Zi(t),r=ee(t),o="";try{o=Zt(e,t)}catch(s){o=`(${String(s)})`}return z(n==="sessionCwd"?[{kind:"title",text:"/receive \u2014 this chat"},{kind:"p",text:"Per-chat: inbound media saves under your session folder (updated when you !cd)."},{kind:"bullet",text:`Session cwd: ${r.cwd}`},{kind:"bullet",text:`Next file root: ${o}`},{kind:"p",text:"Send /receive default to follow server config instead."}]:[{kind:"title",text:"/receive \u2014 this chat"},{kind:"p",text:"Per-chat override: off \u2014 using global fileReceiveRootMode from config."},{kind:"bullet",text:`Global mode: ${e.fileReceiveRootMode}`},{kind:"bullet",text:`Next file root: ${o}`},{kind:"p",text:"Send /receive here to pin saves to your current session folder."}])}function $a(){return z([{kind:"title",text:"Unknown /wa command"},{kind:"p",text:"Send /wa help for setup."}])}function Ca(){return z([{kind:"title",text:"Unknown /tg command"},{kind:"p",text:"Send /tg help or /tg token <botfather_token>."}])}function Ra(){return z([{kind:"title",text:"Free shell mode on"},{kind:"p",text:"Plain messages run as sync shell (no command prefix)."},{kind:"bullet",text:"Send !!stop to turn off."},{kind:"bullet",text:"Apps: plain DMs no longer go to the focused app \u2014 use >name text or /apps send."}])}function Ma(e){return z([{kind:"title",text:"Unknown command"},{kind:"p",text:`Try /help or ${e.commandPrefix}<command>.`},{kind:"gap"},...en(e)])}function Ta(e){return z([{kind:"title",text:"No command matched"},{kind:"p",text:`Use ${JSON.stringify(e.commandPrefix)}, a slash command, or /apps help.`},{kind:"gap"},...en(e)])}function Ea(){let e=[{kind:"title",text:"Unknown mode"},{kind:"p",text:"Use: whatsapp | telegram | both (short: wa, tg, b)"},{kind:"bullet",text:"/gateway both \xB7 /gw wa \xB7 /mode telegram"},{kind:"gap"},...Zo()];return z(e)}function os(){return[{kind:"title",text:"Shortcuts (this gateway)"},{kind:"p",text:"Stored per-chat or shared for every chat on this gateway. Expansion: this chat wins, then shared. Bare token only \u2014 for task injection use /run recipes."},{kind:"p",text:"Scope flags: -g or --global = shared on this gateway; -p or --chat = private to this chat."},{kind:"gap"},{kind:"sub",text:"Manage"},{kind:"bullet",text:"/shortcut add <name> <command\u2026> \u2014 private by default; add -g|--global to share on this gateway"},{kind:"bullet",text:"/shortcut add -p|--chat <name> <command\u2026> \u2014 explicit private (this chat)"},{kind:"bullet",text:"/shortcut set <name> <command\u2026> \u2014 overwrite in chosen bucket; scope-only (same line): /shortcut set -g <name> or /shortcut set <name> -g (share); /shortcut set -p <name> or /shortcut set <name> -p (private)"},{kind:"bullet",text:"/shortcut list \u2014 merged view (/shortcuts); list --chat | -p | list --global | -g"},{kind:"bullet",text:"/shortcut show <name>; show --global|-g|--chat|-p to read one bucket"},{kind:"bullet",text:"/shortcut remove <name> \u2014 private bucket; remove --global|-g drops shared \xB7 rm, del \xB7 --chat|-p"},{kind:"bullet",text:"/alias \u2026 \u2014 same as /shortcut \u2026 (including /aliases \u2026)"},{kind:"gap"},{kind:"sub",text:"Run"},{kind:"bullet",text:"!name \u2014 expands once to the saved line (e.g. !cd \u2026 updates session cwd)"},{kind:"bullet",text:"/name \u2014 same expansion for slash-style navigation"},{kind:"p",text:"Shortcut bodies may start with !, /, etc.; nested shortcuts are not expanded."}]}function Ia(e){if(e.length===0)return m("(no shortcuts \u2014 /shortcut add <name> <command\u2026> or /shortcut add --global <name> <command\u2026>)");let t=e.every(l=>l.scope==="global"),n=e.every(l=>l.scope==="chat"),r=t?"_Shared (every chat)_":n?"_This chat only_":"_This chat + shared_",o=t?"<i>Shared (every chat)</i>":n?"<i>This chat only</i>":"<i>This chat + shared</i>",s=e.some(l=>l.scope==="chat")&&e.some(l=>l.scope==="global"),i=["*Shortcuts*",r,"",...e.map(l=>{let c=s?l.scope==="chat"?"[chat] ":"[global] ":"";return`${Me}\`${c}${l.name}\` \u2192 ${l.body}`})].join(`
37
+ `),a=["<b>Shortcuts</b>",o,"",...e.map(l=>{let u=`${s?l.scope==="chat"?"[chat] ":"[global] ":""}${l.name}`;return`\u2022 <code>${G(u)}</code> \u2192 ${G(l.body)}`})].join(`
38
+ `);return ie(i,a)}function Nn(e,t,n="chat"){return m(`Shortcut saved: ${e}
38
39
  \u2192 ${t}
39
- (${n==="global"?"Shared on this gateway (every chat unless this chat overrides the name).":"Stored for this chat only."})`)}function ei(e,t="chat"){return p(`Shortcut removed (${t==="global"?"shared":"this chat"}): ${e}`)}function ti(e){return p(`Unknown shortcut: ${e}`)}function ni(e,t){return p(`Unknown shortcut "${e}" in ${t==="global"?"shared shortcuts":"this chat"}.`)}function Jr(e,t,n){let r=n?`
40
+ (${n==="global"?"Shared on this gateway (every chat unless this chat overrides the name).":"Stored for this chat only."})`)}function Aa(e,t="chat"){return m(`Shortcut removed (${t==="global"?"shared":"this chat"}): ${e}`)}function Pa(e){return m(`Unknown shortcut: ${e}`)}function Oa(e,t){return m(`Unknown shortcut "${e}" in ${t==="global"?"shared shortcuts":"this chat"}.`)}function ss(e,t,n){let r=n?`
40
41
 
41
- ${n}`:"";return p(`${e}
42
- \u2192 ${t}${r}`)}function ri(){return[{kind:"title",text:"/run \u2014 recipe templates"},{kind:"sub",text:"Invoke"},{kind:"bullet",text:"/run <name> <task\u2026> \u2014 start attached session (plain DMs go to it until /apps detach)"},{kind:"bullet",text:"/r <name> <task\u2026> \u2014 short alias for /run"},{kind:"gap"},{kind:"sub",text:"Queue"},{kind:"bullet",text:"/run <name> -q <task\u2026> or /run <name> --queue <task\u2026> \u2014 FIFO per chat: head starts immediately; others wait in memory until the head exits cleanly (code=0, signal=0)"},{kind:"bullet",text:"Pending counts only jobs not yet started \u2014 the first queued item becomes Active right away, so /run queue can show Pending: 0 while a queued run is still executing"},{kind:"bullet",text:"/run queue \u2014 active session + recipe, numbered waiting list, paused flag"},{kind:"bullet",text:"/run queue resume \u2014 after a pause or non-clean exit, clear pause and start the next waiting item"},{kind:"bullet",text:"/run queue load <file.json> \u2014 enqueue jobs from JSON (paths relative to session cwd); /run queue load json [\u2026] \u2014 same payload inline; attach a file with caption /run queue load"},{kind:"bullet",text:'Queue JSON: [ { "recipe": "<name>", "task": "<text>" }, \u2026 ] or { "tasks": [ \u2026 ] } (max 64 jobs per load; same rules as /run <name> -q)'},{kind:"gap"},{kind:"sub",text:"Discover"},{kind:"bullet",text:"/run list \u2014 featured, gateway-shared, this chat, host templates; list --chat | -p | list --global | -g"},{kind:"bullet",text:"/run show <name> \u2014 merged resolution; show --global|-g|--chat|-p reads one user bucket"},{kind:"gap"},{kind:"sub",text:"Manage"},{kind:"p",text:"Recipe scope flags: -g or --global = shared on this gateway; -p or --chat = private to this chat."},{kind:"bullet",text:'/run add <name> <command\u2026> [--template "\u2026"] \u2014 this chat; add --global|-g share on this gateway'},{kind:"bullet",text:"/run set <name> <command\u2026> \u2014 overwrite in chosen bucket (--global|-g|--chat|-p); scope-only (same body): /run set -g <name> or /run set <name> -g (share); /run set -p <name> or /run set <name> -p (this chat only)"},{kind:"bullet",text:"/run remove <name> \u2014 this chat storage; remove --global|-g clears gateway-shared \xB7 rm, del \xB7 --chat|-p"},{kind:"gap"},{kind:"sub",text:"Notes"},{kind:"bullet",text:"Commands must reference the task env variable (default `$OMNISH_TASK`)."},{kind:"bullet",text:"Template placeholders `<<<OMNISH_TASK>>>`, `<<<```$OMNISH_TASK```>>>`, and `$OMNISH_TASK` are replaced with task text."},{kind:"bullet",text:"Host overrides file: "+Rn},{kind:"bullet",text:"Built-in Claude recipes omit dangerous flags unless recipesAllowDangerousBuiltins=true."}]}function dc(e){switch(e){case"builtin":return"built-in";case"global":return"host recipes.json";case"shared":return"gateway-shared (/run add --global)";case"peer":return"this chat (/run add)";default:return e}}function oi(e){let t=["*Recipes*","`/run <name> <task>`",""],n=["<b>Recipes</b>","<code>/run &lt;name&gt; &lt;task&gt;</code>",""],r=(o,s)=>{let i=o.description?` \u2014 ${o.description}`:"",a=s&&o.dangerous?" [dangerous flags]":"";t.push(`${ge}${o.name} \u2014 ${o.label??o.name}${i}${a}`);let l=o.description?` \u2014 ${D(o.description)}`:"",u=s&&o.dangerous?D(" [dangerous flags]"):"";n.push(`\u2022 ${D(o.name)} \u2014 ${D(o.label??o.name)}${l}${u}`)};if(e.featured.length>0){t.push("_Featured:_"),n.push("<i>Featured:</i>");for(let o of e.featured)r(o,!0);t.push(""),n.push("")}if(e.shared.length>0){t.push("_Shared (every chat):_"),n.push("<i>Shared (every chat):</i>");for(let o of e.shared)r(o,!1);t.push(""),n.push("")}if(e.yours.length>0){t.push("_This chat:_"),n.push("<i>This chat:</i>");for(let o of e.yours)r(o,!1);t.push(""),n.push("")}if(e.more.length>0){t.push("_More:_"),n.push("<i>More:</i>");for(let o of e.more)r(o,!1)}return e.featured.length===0&&e.shared.length===0&&e.yours.length===0&&e.more.length===0&&(t.push("(no recipes \u2014 add host file or /run add)"),n.push(D("(no recipes \u2014 add host file or /run add)"))),Y(t.join(`
42
+ ${n}`:"";return m(`${e}
43
+ \u2192 ${t}${r}`)}function La(){return[{kind:"title",text:"/run \u2014 recipe templates"},{kind:"sub",text:"Invoke"},{kind:"bullet",text:"/run <name> <task\u2026> \u2014 start attached session (plain DMs go to it until /apps detach)"},{kind:"bullet",text:"/r <name> <task\u2026> \u2014 short alias for /run"},{kind:"gap"},{kind:"sub",text:"Queue"},{kind:"bullet",text:"/run <name> -q <task\u2026> or /run <name> --queue <task\u2026> \u2014 FIFO per chat: head starts immediately; others wait in memory until the head exits cleanly (code=0, signal=0)"},{kind:"bullet",text:"Pending counts only jobs not yet started \u2014 the first queued item becomes Active right away, so /run queue can show Pending: 0 while a queued run is still executing"},{kind:"bullet",text:"/run queue \u2014 active session + recipe, numbered waiting list, paused flag"},{kind:"bullet",text:"/run queue resume \u2014 after a pause or non-clean exit, clear pause and start the next waiting item"},{kind:"bullet",text:"/run queue load <file.json> \u2014 enqueue jobs from JSON (paths relative to session cwd); /run queue load json [\u2026] \u2014 same payload inline; attach a file with caption /run queue load"},{kind:"bullet",text:'Queue JSON: [ { "recipe": "<name>", "task": "<text>" }, \u2026 ] or { "tasks": [ \u2026 ] } (max 64 jobs per load; same rules as /run <name> -q)'},{kind:"gap"},{kind:"sub",text:"Discover"},{kind:"bullet",text:"/run list \u2014 featured, gateway-shared, this chat, host templates; list --chat | -p | list --global | -g"},{kind:"bullet",text:"/run show <name> \u2014 merged resolution; show --global|-g|--chat|-p reads one user bucket"},{kind:"gap"},{kind:"sub",text:"Manage"},{kind:"p",text:"Recipe scope flags: -g or --global = shared on this gateway; -p or --chat = private to this chat."},{kind:"bullet",text:'/run add <name> <command\u2026> [--template "\u2026"] \u2014 this chat; add --global|-g share on this gateway'},{kind:"bullet",text:"/run set <name> <command\u2026> \u2014 overwrite in chosen bucket (--global|-g|--chat|-p); scope-only (same body): /run set -g <name> or /run set <name> -g (share); /run set -p <name> or /run set <name> -p (this chat only)"},{kind:"bullet",text:"/run remove <name> \u2014 this chat storage; remove --global|-g clears gateway-shared \xB7 rm, del \xB7 --chat|-p"},{kind:"gap"},{kind:"sub",text:"Notes"},{kind:"bullet",text:"Commands must reference the task env variable (default `$OMNISH_TASK`)."},{kind:"bullet",text:"Template placeholders `<<<OMNISH_TASK>>>`, `<<<```$OMNISH_TASK```>>>`, and `$OMNISH_TASK` are replaced with task text."},{kind:"bullet",text:"Host overrides file: "+lr},{kind:"bullet",text:"Built-in Claude recipes omit dangerous flags unless recipesAllowDangerousBuiltins=true."}]}function Od(e){switch(e){case"builtin":return"built-in";case"global":return"host recipes.json";case"shared":return"gateway-shared (/run add --global)";case"peer":return"this chat (/run add)";default:return e}}function Na(e){let t=["*Recipes*","`/run <name> <task>`",""],n=["<b>Recipes</b>","<code>/run &lt;name&gt; &lt;task&gt;</code>",""],r=(o,s)=>{let i=o.description?` \u2014 ${o.description}`:"",a=s&&o.dangerous?" [dangerous flags]":"";t.push(`${Me}${o.name} \u2014 ${o.label??o.name}${i}${a}`);let l=o.description?` \u2014 ${G(o.description)}`:"",c=s&&o.dangerous?G(" [dangerous flags]"):"";n.push(`\u2022 ${G(o.name)} \u2014 ${G(o.label??o.name)}${l}${c}`)};if(e.featured.length>0){t.push("_Featured:_"),n.push("<i>Featured:</i>");for(let o of e.featured)r(o,!0);t.push(""),n.push("")}if(e.shared.length>0){t.push("_Shared (every chat):_"),n.push("<i>Shared (every chat):</i>");for(let o of e.shared)r(o,!1);t.push(""),n.push("")}if(e.yours.length>0){t.push("_This chat:_"),n.push("<i>This chat:</i>");for(let o of e.yours)r(o,!1);t.push(""),n.push("")}if(e.more.length>0){t.push("_More:_"),n.push("<i>More:</i>");for(let o of e.more)r(o,!1)}return e.featured.length===0&&e.shared.length===0&&e.yours.length===0&&e.more.length===0&&(t.push("(no recipes \u2014 add host file or /run add)"),n.push(G("(no recipes \u2014 add host file or /run add)"))),ie(t.join(`
43
44
  `).trimEnd(),n.join(`
44
- `).trimEnd())}function zr(e,t){let n=e.taskEnv??"OMNISH_TASK",r=[`Recipe: ${e.name}`,`Source: ${dc(e.source)}`,`Label: ${e.label??"(none)"}`,`Task env: ${n}`,`Command: ${e.command}`];if(e.promptTemplate){r.push(`Template: ${e.promptTemplate.length} chars`);let s=e.promptTemplate.replace(/\s+/g," ").trim().slice(0,200);r.push(`Preview: ${s}${e.promptTemplate.length>200?"\u2026":""}`)}return e.category&&r.push(`Category: ${e.category}`),e.description&&r.push(`Description: ${e.description}`),e.dangerous&&r.push("Note: built-in includes gated dangerous CLI flags."),t&&r.push("",t),p(r.join(`
45
- `))}function en(e,t,n="chat"){let r=n==="global"?"Shared on this gateway (every chat unless this chat overrides the name).":"Stored for this chat only.",o=[`Recipe saved: ${e}`,`\u2192 ${t.command}`,`(task env: ${t.taskEnv??"OMNISH_TASK"})`];return t.promptTemplate&&o.push(`Template stored: ${t.promptTemplate.length} chars`),o.push(`(${r})`),p(o.join(`
46
- `))}function si(e,t="chat"){return p(`Recipe removed (${t==="global"?"gateway-shared storage":"this chat"}): ${e}`)}function ii(e,t){return p(`Unknown recipe "${e}" in ${t==="global"?"gateway-shared storage":"this chat storage"}.`)}function ai(e,t){let n=t==="global"?"_Gateway-shared recipes_":"_This chat recipes_",r=t==="global"?"<i>Gateway-shared recipes</i>":"<i>This chat recipes</i>";if(e.length===0)return p(t==="global"?"(no gateway-shared recipes \u2014 /run add --global <name> <command\u2026>)":"(no recipes in this chat \u2014 /run add <name> <command\u2026>)");let o=["*Recipes*",n,""],s=["<b>Recipes</b>",r,""];for(let i of e){let a=i.description?` \u2014 ${i.description}`:"";o.push(`${ge}${i.name} \u2014 ${i.label??i.name}${a}`);let l=i.description?` \u2014 ${D(i.description)}`:"";s.push(`\u2022 ${D(i.name)} \u2014 ${D(i.label??i.name)}${l}`)}return Y(o.join(`
45
+ `).trimEnd())}function is(e,t){let n=e.taskEnv??"OMNISH_TASK",r=[`Recipe: ${e.name}`,`Source: ${Od(e.source)}`,`Label: ${e.label??"(none)"}`];if(e.steps&&e.steps.length>0){r.push(`Type: runbook (${e.steps.length} steps)`);for(let o=0;o<e.steps.length;o++){let s=e.steps[o],i=s.label?` (${s.label})`:"",a=s.continueOnFail?" [continue-on-fail]":"";r.push(` ${o+1}. ${s.cmd}${i}${a}`)}}else r.push(`Task env: ${n}`),r.push(`Command: ${e.command}`);if(e.promptTemplate){r.push(`Template: ${e.promptTemplate.length} chars`);let s=e.promptTemplate.replace(/\s+/g," ").trim().slice(0,200);r.push(`Preview: ${s}${e.promptTemplate.length>200?"\u2026":""}`)}return e.category&&r.push(`Category: ${e.category}`),e.description&&r.push(`Description: ${e.description}`),e.dangerous&&r.push("Note: built-in includes gated dangerous CLI flags."),t&&r.push("",t),m(r.join(`
46
+ `))}function _n(e,t,n="chat"){let r=n==="global"?"Shared on this gateway (every chat unless this chat overrides the name).":"Stored for this chat only.",o=[`Recipe saved: ${e}`,`\u2192 ${t.command}`,`(task env: ${t.taskEnv??"OMNISH_TASK"})`];return t.promptTemplate&&o.push(`Template stored: ${t.promptTemplate.length} chars`),o.push(`(${r})`),m(o.join(`
47
+ `))}function _a(e,t="chat"){return m(`Recipe removed (${t==="global"?"gateway-shared storage":"this chat"}): ${e}`)}function Fa(e,t){return m(`Unknown recipe "${e}" in ${t==="global"?"gateway-shared storage":"this chat storage"}.`)}function Ua(e,t){let n=t==="global"?"_Gateway-shared recipes_":"_This chat recipes_",r=t==="global"?"<i>Gateway-shared recipes</i>":"<i>This chat recipes</i>";if(e.length===0)return m(t==="global"?"(no gateway-shared recipes \u2014 /run add --global <name> <command\u2026>)":"(no recipes in this chat \u2014 /run add <name> <command\u2026>)");let o=["*Recipes*",n,""],s=["<b>Recipes</b>",r,""];for(let i of e){let a=i.description?` \u2014 ${i.description}`:"";o.push(`${Me}${i.name} \u2014 ${i.label??i.name}${a}`);let l=i.description?` \u2014 ${G(i.description)}`:"";s.push(`\u2022 ${G(i.name)} \u2014 ${G(i.label??i.name)}${l}`)}return ie(o.join(`
47
48
  `).trimEnd(),s.join(`
48
- `).trimEnd())}function qr(e){return p(`Unknown recipe: ${e}
49
- /run list`)}function Qr(){return[{kind:"title",text:"Apps (interactive CLI)"},{kind:"sub",text:"Sessions"},{kind:"bullet",text:"/apps start <name> <command\u2026> \u2014 spawn in session cwd; auto-attaches"},{kind:"bullet",text:"/apps attach <name> | /apps detach"},{kind:"bullet",text:"/apps list | /apps info <name>"},{kind:"gap"},{kind:"sub",text:"Input"},{kind:"bullet",text:"/apps send <name> <text> \u2014 text + newline"},{kind:"bullet",text:">name text \u2014 shorthand for send"},{kind:"bullet",text:"/apps key <name> <KEY[,KEY\u2026]> \u2014 Enter, ^C, Up, Esc, \\x1b, \u2026"},{kind:"gap"},{kind:"sub",text:"Output & control"},{kind:"bullet",text:"/apps tail <name> [lines] | /apps since <name>"},{kind:"bullet",text:"/apps mute|unmute <name> | /apps raw <name> on|off"},{kind:"bullet",text:"/apps resize <name> <cols> <rows>"},{kind:"bullet",text:"/apps stop <name> | /apps kill <name> | /apps rm <name>"},{kind:"gap"},{kind:"p",text:"Attached: plain DMs go to the focused app. Escaped by ! prefix, /commands, or >other."},{kind:"p",text:"Free shell: !!start \u2014 plain DMs run as sync shell when no focused PTY; !!stop \u2014 off. Attached session wins over free shell for plain text."}]}function pc(e){let{errors:t,warns:n,infos:r}=Br(e),o=[{kind:"title",text:"Security check"}];if(e.length===0)return o.push({kind:"p",text:"No issues reported by automated rules."},{kind:"gap"},{kind:"p",text:"Allowlisted remote shell access is still equivalent to sharing credentials with those identities."}),o;o.push({kind:"p",text:`${t} error(s), ${n} warning(s), ${r} note(s).`}),o.push({kind:"gap"});let s=(i,a)=>{if(a.length!==0){o.push({kind:"sub",text:`${i} (${a.length})`});for(let l of a){let u=l.severity.toUpperCase();o.push({kind:"bullet",text:le(`[${u}] ${l.code}: ${l.message}`)}),l.detail&&o.push({kind:"bullet",text:le(l.detail)}),l.fixHint&&o.push({kind:"bullet",text:le(`Fix: ${l.fixHint}`)})}o.push({kind:"gap"})}};return s("Errors",e.filter(i=>i.severity==="error")),s("Warnings",e.filter(i=>i.severity==="warn")),s("Notes",e.filter(i=>i.severity==="info")),o.push({kind:"p",text:"Allowlisted identities can run commands as this user; treat them like passwords."}),o}function li(e){return j(pc(e))}function ci(){return[{kind:"title",text:"Security tips"},{kind:"p",text:"Practical hardening for this gateway:"},{kind:"bullet",text:"Keep allowlists minimal \u2014 only numbers and tg: ids you trust."},{kind:"bullet",text:'Never use "*" in allowFrom; treat allowed identities like passwords.'},{kind:"bullet",text:"chmod 600 config.json and chmod 700 your data dir (~/.omnish or OMNISH_HOME)."},{kind:"bullet",text:"Do not paste bot tokens in group chats; revoke at @BotFather if leaked."},{kind:"bullet",text:"Prefer TELEGRAM_BOT_TOKEN via systemd/env over chat if your Telegram logs worry you."},{kind:"bullet",text:"Run omnish as a normal user, not root, unless you fully accept that risk."},{kind:"gap"},{kind:"p",text:"Send /security on this chat or run omnish security on the host for automated checks."}]}function ui(){return[{kind:"title",text:"/security commands"},{kind:"bullet",text:"/security \u2014 full report (same checks as omnish security)"},{kind:"bullet",text:"/security summary \u2014 one-line counts"},{kind:"bullet",text:"/security tips \u2014 short hardening checklist"},{kind:"bullet",text:"CLI: omnish security [--json] for scripts and monitoring"}]}import mi from"node:fs";import mc from"node:path";var pt="__omnish_shortcuts_global__",di=500,fi=/^[a-zA-Z0-9][a-zA-Z0-9_-]{0,31}$/,fc=new Set(["help","apps","bg","jobs","log","tail","kill","send","file","files","receive","reload","restart","updates","gateway","gw","mode","allow","deny","allowlist","whatsapp","wa","telegram","tg","cowork","cw","shortcut","shortcuts","alias","aliases"]),tn=new Map,pi=!1;function gc(){try{let e=mi.readFileSync(Cn,"utf8"),t=JSON.parse(e);if(t&&typeof t=="object")for(let[n,r]of Object.entries(t)){if(!r||typeof r!="object")continue;let o={};for(let[s,i]of Object.entries(r)){let a=String(s).toLowerCase();typeof i=="string"&&i.length>0&&(o[a]=i.trim())}tn.set(n,o)}}catch{}}function Jn(){H(mc.dirname(Cn));let e={};for(let[t,n]of tn)Object.entries(n).length>0&&(e[t]={...n});mi.writeFileSync(Cn,JSON.stringify(e,null,2)+`
50
- `,{mode:384})}function zn(){pi||(gc(),pi=!0)}function hc(e){return fc.has(e.trim().toLowerCase())}function mt(e){let t=e.trim();if(!t)return{ok:!1,error:"Name is empty."};let n=t.toLowerCase();return fi.test(n)?hc(n)?{ok:!1,error:`Reserved name: ${n}`}:{ok:!0,normalized:n}:{ok:!1,error:`Invalid name (use letters, digits, _ or -; max 32 chars): ${t}`}}function Yr(e){let t=e.replace(/\r\n/g,`
51
- `).replace(/\n/g," ").trim();return t?t.length>di?{ok:!1,error:`Body too long (max ${di} characters).`}:{ok:!0,body:t}:{ok:!1,error:"Body is empty."}}function je(e,t){zn();let n=tn.get(e);return!n&&t&&(n={},tn.set(e,n)),n??{}}function gi(e){let t=e.trim();if(/^--global$/i.test(t))return{mode:"global",remainder:""};if(/^-g$/i.test(t))return{mode:"global",remainder:""};if(/^--chat$/i.test(t))return{mode:"chat",remainder:""};if(/^-p$/i.test(t))return{mode:"chat",remainder:""};let n=/^--global\s+/i.exec(t);if(n)return{mode:"global",remainder:t.slice(n[0].length).trimStart()};let r=/^-g\s+/i.exec(t);if(r)return{mode:"global",remainder:t.slice(r[0].length).trimStart()};let o=/^--chat\s+/i.exec(t);if(o)return{mode:"chat",remainder:t.slice(o[0].length).trimStart()};let s=/^-p\s+/i.exec(t);return s?{mode:"chat",remainder:t.slice(s[0].length).trimStart()}:{mode:"resolved",remainder:t}}function Vr(e){let t=e.trim();if(/^--global$/i.test(t))return{scope:"global",remainder:"",explicit:!0};if(/^-g$/i.test(t))return{scope:"global",remainder:"",explicit:!0};if(/^--chat$/i.test(t))return{scope:"chat",remainder:"",explicit:!0};if(/^-p$/i.test(t))return{scope:"chat",remainder:"",explicit:!0};let n=/^--global\s+([\s\S]*)$/i.exec(t);if(n?.[1]!==void 0)return{scope:"global",remainder:n[1].trimStart(),explicit:!0};let r=/^-g\s+([\s\S]*)$/i.exec(t);if(r?.[1]!==void 0)return{scope:"global",remainder:r[1].trimStart(),explicit:!0};let o=/^--chat\s+([\s\S]*)$/i.exec(t);if(o?.[1]!==void 0)return{scope:"chat",remainder:o[1].trimStart(),explicit:!0};let s=/^-p\s+([\s\S]*)$/i.exec(t);return s?.[1]!==void 0?{scope:"chat",remainder:s[1].trimStart(),explicit:!0}:{scope:"chat",remainder:t,explicit:!1}}function Kr(e){let t=Vr(e);return{scope:t.scope,remainder:t.remainder}}function hi(e){let t=e.trim();return!t||/^-+$/i.test(t)?{filter:"merged"}:/^(?:--global|-g)$/i.test(t)?{filter:"global"}:/^(?:--chat|-p)$/i.test(t)?{filter:"chat"}:{filter:"merged",bad:t}}function yc(e){let t=e.trim().toLowerCase();if(t==="--global"||t==="-g")return"global";if(t==="--chat"||t==="-p")return"chat"}function yi(e){let t=e.trim().match(/^(\S+)\s+(--global|-g|--chat|-p)\s*$/i);if(!t?.[1]||!t[2])return;let n=yc(t[2]);if(n)return{name:t[1],target:n}}function wc(e,t){zn();let n=e,r=je(n,!1),o=je(pt,!1);if(t==="chat")return Object.entries(r).map(([a,l])=>({name:a,body:l,scope:"chat"})).sort((a,l)=>a.name.localeCompare(l.name));if(t==="global")return Object.entries(o).map(([a,l])=>({name:a,body:l,scope:"global"})).sort((a,l)=>a.name.localeCompare(l.name));let s=new Set([...Object.keys(r),...Object.keys(o)]),i=[];for(let a of[...s].sort()){if(a in r){let u=r[a];u!==void 0&&i.push({name:a,body:u,scope:"chat"});continue}let l=o[a];l!==void 0&&i.push({name:a,body:l,scope:"global"})}return i}function wi(e,t="merged"){return wc(e,t)}function Xr(e,t){let n=t.trim().toLowerCase(),r=je(e,!1)[n];return r!==void 0?r:je(pt,!1)[n]}function bi(e,t){let n=t.trim().toLowerCase(),r=je(e,!1)[n];if(r!==void 0)return{body:r,scope:"chat"};let o=je(pt,!1)[n];if(o!==void 0)return{body:o,scope:"global"}}function ze(e,t,n){let r=n.trim().toLowerCase(),o=e==="global"?pt:t;return je(o,!1)[r]}function Zr(e,t,n,r="chat"){let o=mt(t);if(!o.ok)throw new Error(o.error);let s=Yr(n);if(!s.ok)throw new Error(s.error);let i=r==="global"?pt:e,a=je(i,!0);a[o.normalized]=s.body,Jn()}function ki(e,t,n="chat"){let r=t.trim().toLowerCase(),o=n==="global"?pt:e;zn();let s=tn.get(o);return!s||!(r in s)?!1:(delete s[r],Jn(),!0)}function eo(e,t,n){let r=mt(t);if(!r.ok)return{ok:!1,error:r.error};let o=r.normalized,s=e;zn();let i=je(s,!0),a=je(pt,!0),l=i[o],u=a[o];if(n==="global"){if(l!==void 0){let c=Yr(l);return c.ok?(a[o]=c.body,delete i[o],Jn(),{ok:!0,kind:"moved",target:"global",name:o}):{ok:!1,error:c.error}}return u!==void 0?{ok:!0,kind:"noop",message:`Shortcut "${o}" is already shared on this gateway.`}:{ok:!1,error:`No shortcut "${o}" in this chat to share. Add with /shortcut add ${o} \u2026 or make the shared copy private with /shortcut set -p ${o}.`}}if(u!==void 0){let c=Yr(u);return c.ok?(i[o]=c.body,delete a[o],Jn(),{ok:!0,kind:"moved",target:"chat",name:o}):{ok:!1,error:c.error}}return l!==void 0?{ok:!0,kind:"noop",message:`Shortcut "${o}" is already private to this chat.`}:{ok:!1,error:`No shared shortcut "${o}" to make private. Add with /shortcut add --global ${o} \u2026 or share from this chat with /shortcut set -g ${o}.`}}function Si(e){let t=e.trim();return t.length>0&&!/\s/.test(t)&&fi.test(t.toLowerCase())}import bc from"node:crypto";import ro from"node:fs";import kc from"node:path";var oo=/^[a-zA-Z0-9][a-zA-Z0-9_-]{0,31}$/,Tt="__omnish_recipes_global__",so=new Set(["help","list","show","add","set","remove","rm","del","run","r"]),Mt=new Map,xi=!1;function Sc(){try{let e=ro.readFileSync(Mn,"utf8"),t=JSON.parse(e);if(t&&typeof t=="object")for(let[n,r]of Object.entries(t)){if(!r||typeof r!="object")continue;let o={};for(let[s,i]of Object.entries(r)){let a=String(s).toLowerCase();i&&typeof i=="object"&&typeof i.command=="string"&&(o[a]=qe(i))}Mt.set(n,o)}}catch{}}function qe(e){let t=typeof e.taskEnv=="string"&&/^[A-Za-z_][A-Za-z0-9_]*$/.test(e.taskEnv)?e.taskEnv:"OMNISH_TASK",n=typeof e.promptTemplate=="string"&&e.promptTemplate.trim().length>0?e.promptTemplate.trim():void 0,r={command:String(e.command).trim(),taskEnv:t,label:typeof e.label=="string"?e.label.trim().slice(0,80):void 0,description:typeof e.description=="string"?e.description.trim().slice(0,200):void 0,category:typeof e.category=="string"?e.category.trim().slice(0,40):void 0,featured:typeof e.featured=="boolean"?e.featured:void 0,dangerous:typeof e.dangerous=="boolean"?e.dangerous:void 0};return n!==void 0&&(r.promptTemplate=n),r}function Qn(){H(kc.dirname(Mn));let e={};for(let[t,n]of Mt)Object.entries(n).length>0&&(e[t]={...n});ro.writeFileSync(Mn,JSON.stringify(e,null,2)+`
52
- `,{mode:384})}function Yn(){xi||(Sc(),xi=!0)}function vi(e){return Yn(),Mt.get(e)??{}}function to(e){Yn();let t=Mt.get(e);return t||(t={},Mt.set(e,t)),t}function xc(e,t){let n=e.taskEnv??"OMNISH_TASK",r=t.recipesMacroDefaultCommand.trim();return ft(r,n).ok?qe({...e,command:r,promptTemplate:e.command}):e}function vc(e,t){if(e.promptTemplate||ft(e.command,t).ok||!It(e.command,t))return!1;let n=e.command;return n.includes("```")||n.length>2e3||/^"Create\b/i.test(n)||n.includes("<<<")||n.includes(`
53
- ### `)}function io(e,t){let n=qe(e),r=n.taskEnv??"OMNISH_TASK";return!n.promptTemplate&&vc(n,r)&&(n=xc(n,t)),n}function $c(e){try{let t=ro.readFileSync(Rn,"utf8"),n=JSON.parse(t);if(!n||typeof n!="object")return{};let r={};for(let[o,s]of Object.entries(n)){let i=o.trim().toLowerCase();if(!(!oo.test(i)||so.has(i))&&s&&typeof s=="object"&&typeof s.command=="string"){let l=io(s,e),u=nn(l);if(!u.ok){console.warn(`[omnish] recipes.json: skipping "${i}": ${u.error}`);continue}r[i]=l}}return r}catch{return{}}}function Cc(e){return{claude:{command:'claude -p "$OMNISH_TASK" --allowedTools all --dangerously-skip-permissions',taskEnv:"OMNISH_TASK",label:"Claude Code",description:"Anthropic Claude Code CLI (requires `claude` on PATH)",category:"agents",featured:!0,dangerous:e.recipesAllowDangerousBuiltins},codex:{command:'codex "$OMNISH_TASK"',taskEnv:"OMNISH_TASK",label:"Codex CLI",description:"OpenAI Codex CLI if installed; override in recipes.json",category:"agents",featured:!1},gemini:{command:'gemini -p "$OMNISH_TASK"',taskEnv:"OMNISH_TASK",label:"Gemini CLI",description:"Google Gemini CLI if installed; override in recipes.json",category:"agents",featured:!1},cursor:{command:"agent --yolo --force -p ```Before writing any code:\n1. Analyze the codebase and state your full implementation plan\n2. List every file you will touch\n3. Identify any risks or ambiguities\n4. Then execute the plan step by step, <task> $OMNISH_TASK </task>```",taskEnv:"OMNISH_TASK",label:"Cursor Agent",description:"Cursor Agent if installed; override in recipes.json",category:"agents",featured:!0}}}function qn(e,t,n){for(let[r,o]of Object.entries(t)){let s=r.toLowerCase();!oo.test(s)||so.has(s)||o.command.trim()&&e.set(s,{...o,name:s,source:n})}}function no(e,t){let n=vi(e),r={};for(let[o,s]of Object.entries(n)){let i=io(s,t);nn(i).ok&&(r[o]=i)}return r}function $i(e,t){let n=new Map;return qn(n,Cc(t),"builtin"),qn(n,$c(t),"global"),qn(n,no(Tt,t),"shared"),qn(n,no(e,t),"peer"),n}function Re(e,t,n){let r=n.trim().toLowerCase();return $i(e,t).get(r)}function Ci(e){let t=e.trim();if(/^--global$/i.test(t))return{mode:"global",remainder:""};if(/^-g$/i.test(t))return{mode:"global",remainder:""};if(/^--chat$/i.test(t))return{mode:"chat",remainder:""};if(/^-p$/i.test(t))return{mode:"chat",remainder:""};let n=/^--global\s+/i.exec(t);if(n)return{mode:"global",remainder:t.slice(n[0].length).trimStart()};let r=/^-g\s+/i.exec(t);if(r)return{mode:"global",remainder:t.slice(r[0].length).trimStart()};let o=/^--chat\s+/i.exec(t);if(o)return{mode:"chat",remainder:t.slice(o[0].length).trimStart()};let s=/^-p\s+/i.exec(t);return s?{mode:"chat",remainder:t.slice(s[0].length).trimStart()}:{mode:"resolved",remainder:t}}function ao(e){let t=e.trim();if(/^--global$/i.test(t))return{scope:"global",remainder:"",explicit:!0};if(/^-g$/i.test(t))return{scope:"global",remainder:"",explicit:!0};if(/^--chat$/i.test(t))return{scope:"chat",remainder:"",explicit:!0};if(/^-p$/i.test(t))return{scope:"chat",remainder:"",explicit:!0};let n=/^--global\s+([\s\S]*)$/i.exec(t);if(n?.[1]!==void 0)return{scope:"global",remainder:n[1].trimStart(),explicit:!0};let r=/^-g\s+([\s\S]*)$/i.exec(t);if(r?.[1]!==void 0)return{scope:"global",remainder:r[1].trimStart(),explicit:!0};let o=/^--chat\s+([\s\S]*)$/i.exec(t);if(o?.[1]!==void 0)return{scope:"chat",remainder:o[1].trimStart(),explicit:!0};let s=/^-p\s+([\s\S]*)$/i.exec(t);return s?.[1]!==void 0?{scope:"chat",remainder:s[1].trimStart(),explicit:!0}:{scope:"chat",remainder:t,explicit:!1}}function lo(e){let t=ao(e);return{scope:t.scope,remainder:t.remainder}}function Ri(e){let t=e.trim();return!t||/^-+$/i.test(t)?{filter:"merged"}:/^(?:--global|-g)$/i.test(t)?{filter:"global"}:/^(?:--chat|-p)$/i.test(t)?{filter:"chat"}:{filter:"merged",bad:t}}function Rc(e){let t=e.trim().toLowerCase();if(t==="--global"||t==="-g")return"global";if(t==="--chat"||t==="-p")return"chat"}function Mi(e){let t=e.trim().match(/^(\S+)\s+(--global|-g|--chat|-p)\s*$/i);if(!t?.[1]||!t[2])return;let n=Rc(t[2]);if(n)return{name:t[1],target:n}}function Qe(e,t,n,r){let o=r.trim().toLowerCase(),s=e==="global"?Tt:t,i=vi(s)[o];if(i===void 0)return;let a=io(i,n);if(nn(a).ok)return{...a,name:o,source:e==="global"?"shared":"peer"}}function Ti(e,t,n){if(n==="merged")return[];let r=n==="global"?Tt:e,o=n==="global"?"shared":"peer",s=no(r,t);return Object.entries(s).map(([i,a])=>({...a,name:i,source:o})).sort((i,a)=>i.name.localeCompare(a.name))}function gt(e){let t=e.trim();if(!t)return{ok:!1,error:"Name is empty."};let n=t.toLowerCase();return oo.test(n)?so.has(n)?{ok:!1,error:`Reserved name: ${n}`}:{ok:!0,normalized:n}:{ok:!1,error:`Invalid name (use letters, digits, _ or -; max 32 chars): ${t}`}}function Vn(e,t){let n=e.replace(/\r\n/g,`
54
- `).trim();return n?t>0&&n.length>t?{ok:!1,error:`Task too long (max ${t} characters).`}:{ok:!0,task:n}:{ok:!1,error:"Task is empty."}}function It(e,t){return e.includes("$")?e.includes(t):!1}function ft(e,t){let n=e.trim();if(!n)return{ok:!1,error:"Command is empty."};if(!/^[A-Za-z_][A-Za-z0-9_]*$/.test(t))return{ok:!1,error:"Invalid taskEnv (use letters, digits, _)."};if(!It(n,t))return{ok:!1,error:`Command must reference "$${t}" so the task is passed via the environment.`};let r=`"$${t}"`;return n.includes(r)?n.includes("```")?{ok:!1,error:"Recipe command must not contain markdown code fences (```). Use promptTemplate or split with a --- line (see /run help)."}:{ok:!0}:{ok:!1,error:`Command must include ${r} (quoted).`}}function nn(e){let t=e.taskEnv??"OMNISH_TASK",n=ft(e.command,t);return n.ok?e.promptTemplate!==void 0&&e.promptTemplate.trim().length===0?{ok:!1,error:"promptTemplate cannot be empty."}:{ok:!0}:n}function Mc(e){let t=e,n=null,r=!1,o=-1,s=-1;for(let l=0;l<t.length;l+=1){let u=t[l];if(r){r=!1;continue}if(u==="\\"){r=!0;continue}if(n){u===n&&(n=null);continue}if(u==='"'||u==="'"){n=u;continue}if(u!=="-"||t[l+1]!=="-")continue;let c=l===0?"":t[l-1];if(c&&!/\s/.test(c))continue;let d=t.slice(l+2),m=/^(template|tamplate)\b/i.exec(d);if(!m)continue;let y=l+2+m[0].length,g=t[y]??"";if(!g||!/\s/.test(g))continue;o=l;let f=y;for(;f<t.length&&/\s/.test(t[f]);)f+=1;s=f;break}if(o<0||s<0)return{command:t.trim()};let i=t.slice(0,o).trim(),a=t.slice(s).trim();return(a.startsWith('"')&&a.endsWith('"')&&a.length>=2||a.startsWith("'")&&a.endsWith("'")&&a.length>=2)&&(a=a.slice(1,-1)),{command:i,template:a}}function co(e,t){let n=e.replace(/\r\n/g,`
55
- `).trim();if(!n)throw new Error("Recipe body is empty.");let r="OMNISH_TASK",{command:o,template:s}=Mc(n);if(s!==void 0){if(!o)throw new Error("Command part (before --template) is empty.");if(!s.trim())throw new Error("Template part (after --template) is empty.");let c=ft(o,r);if(!c.ok)throw new Error(c.error);return qe({command:o,promptTemplate:s})}let i=/\n---\n/,a=n.search(i);if(a>=0){let c=n.slice(0,a).trim(),d=n.slice(a).replace(/^\n---\n/,"").trim();if(!c)throw new Error("Command part (before ---) is empty.");if(!d)throw new Error("Template part (after ---) is empty.");let m=ft(c,r);if(!m.ok)throw new Error(m.error);return qe({command:c,promptTemplate:d})}if(ft(n,r).ok)return qe({command:n});let l=t.trim(),u=ft(l,r);if(!u.ok)throw new Error(`recipesMacroDefaultCommand invalid (${u.error}). Fix config or paste runner then --- then template.`);return qe({command:l,promptTemplate:n})}function Kn(e,t,n){let r="`".repeat(3),o=`<<<${r}$${t}${r}>>>`,s=e;s.includes(o)&&(s=s.split(o).join(n));let i=`<<<${t}>>>`;s.includes(i)&&(s=s.split(i).join(n));let a=new RegExp(`\\$${t}\\b`,"g");return s.replace(a,n)}function uo(e,t,n,r="chat"){let o=gt(t);if(!o.ok)throw new Error(o.error);let s=qe({...n,command:n.command}),i=nn(s);if(!i.ok)throw new Error(i.error);let a=r==="global"?Tt:e,l=to(a);l[o.normalized]=s,Qn()}function Ii(e,t,n="chat"){let r=t.trim().toLowerCase(),o=n==="global"?Tt:e;Yn();let s=Mt.get(o);return!s||!(r in s)?!1:(delete s[r],Qn(),!0)}function po(e,t,n,r){let o=gt(t);if(!o.ok)return{ok:!1,error:o.error};let s=o.normalized,i=e,a=Tt;Yn();let l=to(i),u=to(a),c=l[s],d=u[s],m=y=>{let g=qe({...y}),f=nn(g);if(!f.ok)throw new Error(f.error);return g};try{if(n==="global"){if(c!==void 0){let f=m(c);return u[s]=f,delete l[s],Qn(),{ok:!0,kind:"moved",target:"global",name:s}}if(d!==void 0)return{ok:!0,kind:"noop",message:`Recipe "${s}" is already gateway-shared.`};let g=Re(e,r,s);return g?.source==="builtin"||g?.source==="global"?{ok:!1,error:`Recipe "${s}" is built-in or from host recipes.json \u2014 not moved via /run set. Edit recipes.json or use a different name.`}:{ok:!1,error:`No user recipe "${s}" in this chat to promote. Add with /run add ${s} \u2026 or demote from global with /run set -p ${s}.`}}if(d!==void 0){let g=m(d);return l[s]=g,delete u[s],Qn(),{ok:!0,kind:"moved",target:"chat",name:s}}if(c!==void 0)return{ok:!0,kind:"noop",message:`Recipe "${s}" is already stored for this chat only.`};let y=Re(e,r,s);return y?.source==="builtin"||y?.source==="global"?{ok:!1,error:`Recipe "${s}" is built-in or from host recipes.json \u2014 not moved via /run set.`}:{ok:!1,error:`No gateway-shared user recipe "${s}" to demote. Add with /run add --global ${s} \u2026 or promote from this chat with /run set -g ${s}.`}}catch(y){return{ok:!1,error:String(y)}}}function Ei(e,t){let n=[...$i(e,t).values()],r=n.filter(a=>a.featured).sort((a,l)=>a.name.localeCompare(l.name)),o=n.filter(a=>a.source==="peer").sort((a,l)=>a.name.localeCompare(l.name)),s=n.filter(a=>a.source==="shared").sort((a,l)=>a.name.localeCompare(l.name)),i=n.filter(a=>!a.featured&&a.source!=="peer"&&a.source!=="shared").sort((a,l)=>a.name.localeCompare(l.name));return{featured:r,shared:s,yours:o,more:i}}function Xn(e){let t=bc.randomBytes(4).toString("hex");return`r-${e.replace(/[^a-zA-Z0-9_-]/g,"").slice(0,12)}-${t}`.slice(0,32)}import Ai from"node:fs";var Li=64,Tc=1024*1024;function Oi(e){return e.fileReceiveMaxBytes>0?e.fileReceiveMaxBytes:Tc}function Pi(e){let t;try{t=JSON.parse(e)}catch(o){return{ok:!1,error:`Invalid JSON: ${String(o)}`}}let n;if(Array.isArray(t))n=t;else if(t&&typeof t=="object"&&!Array.isArray(t)){let o=t,s=Object.keys(o);if(s.length!==1||s[0]!=="tasks")return{ok:!1,error:'JSON must be an array or a single-key object: { "tasks": [ \u2026 ] }.'};let i=o.tasks;if(!Array.isArray(i))return{ok:!1,error:'"tasks" must be an array.'};n=i}else return{ok:!1,error:'JSON must be an array or { "tasks": [ \u2026 ] }.'};if(n.length===0)return{ok:!1,error:"Queue JSON must contain at least one job."};if(n.length>Li)return{ok:!1,error:`Too many jobs (max ${Li}).`};let r=[];for(let o=0;o<n.length;o++){let s=n[o];if(!s||typeof s!="object"||Array.isArray(s))return{ok:!1,error:`Job ${o+1}: must be an object with "recipe" and "task".`};let i=s;if(Object.keys(i).length!==2||typeof i.recipe!="string"||typeof i.task!="string")return{ok:!1,error:`Job ${o+1}: must contain only "recipe" and "task" string fields.`};r.push({recipe:i.recipe,task:i.task})}return{ok:!0,jobs:r}}function Fi(e,t,n){let r=[];for(let o=0;o<n.length;o++){let{recipe:s,task:i}=n[o],a=Re(e,t,s);if(!a)return{ok:!1,error:`Job ${o+1}: unknown recipe "${s}".`};let l=a.taskEnv??"OMNISH_TASK";if(!It(a.command,l))return{ok:!1,error:`Job ${o+1}: recipe "${s}" command must reference "$${l}".`};let u=Vn(i,t.recipesMaxTaskChars);if(!u.ok)return{ok:!1,error:`Job ${o+1}: ${u.error}`};let c=a.promptTemplate?Kn(a.promptTemplate,l,u.task):u.task,d={[l]:c};r.push({command:a.command,extraEnv:d,recipeLabel:s})}return{ok:!0,items:r}}function mo(e,t){let n;try{n=Ai.statSync(e)}catch{return{ok:!1,error:`Cannot read file: ${e}`}}if(!n.isFile())return{ok:!1,error:`Not a file: ${e}`};if(n.size>t)return{ok:!1,error:`File too large (max ${t} bytes for queue load).`};try{return{ok:!0,text:Ai.readFileSync(e,"utf8")}}catch(r){return{ok:!1,error:String(r)}}}import Ic from"node:os";import*as _i from"node-pty";function Ec(e,t){return e.length<=t?e:`${e.slice(0,t)}
56
- [...truncated]`}function Ac(e){if(e===void 0||e===0)return null;let t=Ic.constants.signals;for(let[n,r]of Object.entries(t))if(r===e)return n;return null}function Ni(e){try{process.platform==="win32"?e.kill():e.kill("SIGTERM")}catch{e.kill()}}function Et(e,t,n){return new Promise(r=>{let o=Date.now(),s=!1,i="",a=n.cwd,l=null,u=!1,c=null,d=null,m,y=h=>{if(u)return;u=!0,m!==void 0&&clearTimeout(m),c?.dispose(),c=null,r(h);let x=d;d=null,queueMicrotask(()=>x?.dispose())},f={...n.env??process.env,TERM:"xterm-256color",COLORTERM:"truecolor",...a?{PWD:a}:{}};try{l=_i.spawn(e,["-c",t],{name:"xterm-256color",cols:120,rows:40,cwd:a,env:f})}catch(h){y({code:null,stdout:"",stderr:String(h),durationMs:Date.now()-o,timedOut:!1,signal:null});return}m=setTimeout(()=>{s=!0,l&&Ni(l)},n.timeoutMs),c=l.onData(h=>{i+=h,i.length>n.maxBytes&&(i=Ec(i,n.maxBytes),l&&Ni(l))}),d=l.onExit(h=>{y({code:h.exitCode,stdout:i,stderr:"",durationMs:Date.now()-o,timedOut:s,signal:Ac(h.signal)})})})}import{spawn as Lc}from"node:child_process";import Oc from"node:crypto";import ve from"node:fs";import fo from"node:path";var Bi=64,Pc=/^[a-zA-Z0-9._-]+$/;function Fc(e){let t=e.trim();return t?t.length>Bi?`Job name must be at most ${Bi} characters.`:Pc.test(t)?null:"Job name may only use letters, digits, and . _ -":"Job name must not be empty."}function Hi(e){let t=e.trim();if(!t)return{error:"empty"};let n=null,r=t,o=[/^--name\s*=\s*(\S+)\s+([\s\S]+)$/i,/^--name\s+(\S+)\s+([\s\S]+)$/i,/^-n\s+(\S+)\s+([\s\S]+)$/i];for(let s of o){let i=t.match(s);if(i){n=i[1],r=i[2].trim();break}}if(n===null&&[/^-n\s+\S+\s*$/i,/^--name\s+\S+\s*$/i,/^--name=\S+\s*$/i,/^-n\s*$/i,/^--name\s*$/i].some(i=>i.test(t)))return{error:"Add a shell command after the name."};if(n!==null){let s=Fc(n);if(s)return{error:s};if(!r)return{error:"Add a shell command after the name."}}return{cmd:r,name:n}}function Nc(e,t){let n=t.trim();if(!n)return{ok:!1,error:"Missing job id or name."};let r=n.toLowerCase(),o=/^[a-f0-9]{8}$/;if(o.test(r)&&e.find(i=>i.id===r))return{ok:!0,id:r};for(let s of e)if(s.name&&s.name.toLowerCase()===r)return{ok:!0,id:s.id};return o.test(r)?{ok:!1,error:`Unknown job id: ${r}`}:{ok:!1,error:`Unknown job name: ${n}`}}function At(e,t){ve.writeFileSync(e,JSON.stringify(t,null,2)+`
57
- `,{mode:384})}function rn(e){try{return JSON.parse(ve.readFileSync(e,"utf8"))}catch{return null}}var ht=class{running=new Map;metaPath(t){return fo.join(Ae,`${t}.meta.json`)}logPath(t){return fo.join(Ae,`${t}.log`)}spawnJob(t,n,r={}){z();let o=Oc.randomBytes(4).toString("hex"),s=this.logPath(o),i=this.metaPath(o);ve.writeFileSync(s,"",{flag:"w",mode:384});let a=ve.createWriteStream(s,{flags:"a"}),l=new Date().toISOString(),u=r.cwd,c=Lc(t,["-c",n],{stdio:["ignore","pipe","pipe"],env:{...process.env,TERM:"dumb",...u?{PWD:u}:{}},...u?{cwd:u}:{}});this.running.set(o,c);let d={id:o,cmd:n,...r.name?{name:r.name}:{},pid:c.pid??null,startedAt:l,status:"running",exitCode:null,signal:null};return At(i,d),c.stdout?.on("data",m=>{a.write(m)}),c.stderr?.on("data",m=>{a.write(m)}),c.on("close",(m,y)=>{this.running.delete(o),a.end();let g=rn(i)??d,f={...g,status:g.status==="killed"?"killed":"done",exitCode:m,signal:y??null,finishedAt:new Date().toISOString()};At(i,f)}),c.on("error",m=>{this.running.delete(o),a.end(()=>{try{ve.appendFileSync(s,`
58
- [spawn error] ${String(m)}
59
- `)}catch{}});let y=rn(i)??d;At(i,{...y,status:"done",exitCode:null,signal:null,finishedAt:new Date().toISOString()})}),{id:o,meta:d}}list(){z();let t=[];try{t=ve.readdirSync(Ae)}catch{return[]}let n=[];for(let r of t){if(!r.endsWith(".meta.json"))continue;let o=rn(fo.join(Ae,r));o&&n.push(o)}return n.sort((r,o)=>r.startedAt<o.startedAt?1:-1),n}tailLog(t,n){let r=this.logPath(t);if(!ve.existsSync(r))return"(no log file)";let s=ve.readFileSync(r,"utf8").split(`
60
- `);return s.slice(Math.max(0,s.length-n)).join(`
61
- `).trimEnd()||"(empty log)"}readSince(t,n){let r=this.logPath(t);if(!ve.existsSync(r))return{text:"",nextOffset:n};let s=ve.statSync(r).size;if(n>=s)return{text:"",nextOffset:s};let i=Buffer.alloc(s-n),a=ve.openSync(r,"r");try{ve.readSync(a,i,0,i.length,n)}finally{ve.closeSync(a)}return{text:i.toString("utf8"),nextOffset:s}}resolveJobRef(t){return Nc(this.list(),t)}kill(t){let n=this.metaPath(t),r=rn(n);if(!r)return`Unknown job id: ${t}`;let o=this.running.get(t);if(o&&!o.killed)return o.kill("SIGTERM"),At(n,{...r,status:"killed",signal:"SIGTERM"}),`Sent SIGTERM to job ${t} (pid ${r.pid??"?"})`;if(r.pid)try{return process.kill(r.pid,"SIGTERM"),At(n,{...r,status:"killed",signal:"SIGTERM"}),`Sent SIGTERM to pid ${r.pid} (${t})`}catch{return`Job ${t} is not running (no live process).`}return`Job ${t} is not running.`}killAllRunning(){for(let[t,n]of this.running){n.killed||n.kill("SIGTERM");let r=rn(this.metaPath(t));r&&At(this.metaPath(t),{...r,status:"killed",signal:"SIGTERM"})}this.running.clear()}};import Lt from"node:fs";import wo from"node:os";import on from"node:path";import Wi from"node:crypto";var ho="\u2063omnish/c v1",_c=/([nlra])=([^\s\]]*)/g;function Oe(e){return e.replace(/-/g,"").slice(0,8)}function go(e){return e.replace(/[\s\]\r\n]/g,"_").slice(0,64)}function Bc(e){let t=go(Oe(e.nodeId)),n=go(e.label||""),r=e.role==="primary"?"p":"s",o=go(e.activeNodeId?Oe(e.activeNodeId):"");return`${ho} [n=${t} l=${n} r=${r} a=${o}]`}function Di(e,t){let n=Bc(t);if(e.includes(n))return e;let r=e.replace(/\s+$/,"");return r.length===0?n:`${r}
49
+ `).trimEnd())}function as(e){return m(`Unknown recipe: ${e}
50
+ /run list`)}function ls(){return[{kind:"title",text:"Apps (interactive CLI)"},{kind:"sub",text:"Sessions"},{kind:"bullet",text:"/apps start <name> <command\u2026> \u2014 spawn in session cwd; auto-attaches"},{kind:"bullet",text:"/apps attach <name> | /apps detach"},{kind:"bullet",text:"/apps list | /apps info <name>"},{kind:"gap"},{kind:"sub",text:"Input"},{kind:"bullet",text:"/apps send <name> <text> \u2014 text + newline"},{kind:"bullet",text:">name text \u2014 shorthand for send"},{kind:"bullet",text:"/apps key <name> <KEY[,KEY\u2026]> \u2014 Enter, ^C, Up, Esc, \\x1b, \u2026"},{kind:"gap"},{kind:"sub",text:"Output & control"},{kind:"bullet",text:"/apps tail <name> [lines] | /apps since <name>"},{kind:"bullet",text:"/apps mute|unmute <name> | /apps raw <name> on|off"},{kind:"bullet",text:"/apps resize <name> <cols> <rows>"},{kind:"bullet",text:"/apps stop <name> | /apps kill <name> | /apps rm <name>"},{kind:"gap"},{kind:"p",text:"Attached: plain DMs go to the focused app. Escaped by ! prefix, /commands, or >other."},{kind:"p",text:"Free shell: !!start \u2014 plain DMs run as sync shell when no focused PTY; !!stop \u2014 off. Attached session wins over free shell for plain text."}]}function Ld(e){let{errors:t,warns:n,infos:r}=Xo(e),o=[{kind:"title",text:"Security check"}];if(e.length===0)return o.push({kind:"p",text:"No issues reported by automated rules."},{kind:"gap"},{kind:"p",text:"Allowlisted remote shell access is still equivalent to sharing credentials with those identities."}),o;o.push({kind:"p",text:`${t} error(s), ${n} warning(s), ${r} note(s).`}),o.push({kind:"gap"});let s=(i,a)=>{if(a.length!==0){o.push({kind:"sub",text:`${i} (${a.length})`});for(let l of a){let c=l.severity.toUpperCase();o.push({kind:"bullet",text:fe(`[${c}] ${l.code}: ${l.message}`)}),l.detail&&o.push({kind:"bullet",text:fe(l.detail)}),l.fixHint&&o.push({kind:"bullet",text:fe(`Fix: ${l.fixHint}`)})}o.push({kind:"gap"})}};return s("Errors",e.filter(i=>i.severity==="error")),s("Warnings",e.filter(i=>i.severity==="warn")),s("Notes",e.filter(i=>i.severity==="info")),o.push({kind:"p",text:"Allowlisted identities can run commands as this user; treat them like passwords."}),o}function Da(e){return z(Ld(e))}function Ha(){return[{kind:"title",text:"Security tips"},{kind:"p",text:"Practical hardening for this gateway:"},{kind:"bullet",text:"Keep allowlists minimal \u2014 only numbers and tg: ids you trust."},{kind:"bullet",text:'Never use "*" in allowFrom; treat allowed identities like passwords.'},{kind:"bullet",text:"chmod 600 config.json and chmod 700 your data dir (~/.omnish or OMNISH_HOME)."},{kind:"bullet",text:"Do not paste bot tokens in group chats; revoke at @BotFather if leaked."},{kind:"bullet",text:"Prefer TELEGRAM_BOT_TOKEN via systemd/env over chat if your Telegram logs worry you."},{kind:"bullet",text:"Run omnish as a normal user, not root, unless you fully accept that risk."},{kind:"gap"},{kind:"p",text:"Send /security on this chat or run omnish security on the host for automated checks."}]}function Ba(){return[{kind:"title",text:"/security commands"},{kind:"bullet",text:"/security \u2014 full report (same checks as omnish security)"},{kind:"bullet",text:"/security summary \u2014 one-line counts"},{kind:"bullet",text:"/security tips \u2014 short hardening checklist"},{kind:"bullet",text:"CLI: omnish security [--json] for scripts and monitoring"}]}function ge(e){return e.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;")}function cs(e){let t=e.trim();return t?t.length<=8?"(set)":`${t.slice(0,4)}\u2026${t.slice(-4)}`:"(empty)"}function ja(e){let t=e.trim();return(t.startsWith('"')&&t.endsWith('"')&&t.length>=2||t.startsWith("'")&&t.endsWith("'")&&t.length>=2)&&(t=t.slice(1,-1)),t}function bt(e){let t=e.trim().toLowerCase();return t==="true"||t==="1"||t==="yes"||t==="on"?!0:t==="false"||t==="0"||t==="no"||t==="off"?!1:null}var Wa=new Set(["downloads","omnishData","sessionCwd","processCwd","fixed"]),St=["gatewayMode","clusterEnabled","clusterRole","clusterLabel","clusterSenderBindings","commandPrefix","syncTimeoutMs","syncMaxBytes","jobLogTailLines","shell","appsCols","appsRows","appsFlushMs","appsMinIntervalMs","appsMaxFlushBytes","appsMaxSessions","appsMaxSessionsTotal","appsMaxWaChars","appsLogTailLines","appsSubmitDelayMs","appsClearInput","appsClearInputDelayMs","appsClearInputSequence","fileSendMaxBytes","fileReceiveMaxBytes","fileInboxSubdir","fileReceiveRootMode","fileReceiveRootPath","recipesAllowDangerousBuiltins","recipesMaxTaskChars","recipesMacroDefaultCommand","telegramBotToken","serviceInstallFromChat","updateCheckEnabled","updateCheckIntervalMs","updateCheckPackageName","updateInfoUrl","chatLlmFallbackEnabled","chatLlmShellCommand","chatLlmTimeoutMs","chatLlmMaxInputChars","chatLlmMaxOutputChars","chatLlmNeedsTty","chatLlmWorkDir","tunnelEnabled","tunnelRelayUrl","tunnelMaxActive"];function Ga(e){return St.includes(e)}function Nd(e){let t=pe(e);return["*Config* (secrets masked)","",`gatewayMode: ${e.gatewayMode}`,`commandPrefix: ${e.commandPrefix}`,`shell: ${e.shell}`,`syncTimeoutMs: ${e.syncTimeoutMs}`,`syncMaxBytes: ${e.syncMaxBytes}`,`jobLogTailLines: ${e.jobLogTailLines}`,"","*Cluster*",`clusterEnabled: ${e.clusterEnabled}`,`clusterRole: ${e.clusterRole}`,`clusterLabel: ${e.clusterLabel||"(hostname)"}`,`clusterSenderBindings: ${Object.keys(e.clusterSenderBindings??{}).length} entries`,"","*Telegram*",`telegramBotToken: ${cs(t)}`,`telegramAllowFrom: ${e.telegramAllowFrom.length} entries`,"","*WhatsApp allowFrom*",`${e.allowFrom.length} entries`,"","*Apps*",`appsCols \xD7 appsRows: ${e.appsCols}\xD7${e.appsRows}`,`appsFlushMs / appsMinIntervalMs: ${e.appsFlushMs} / ${e.appsMinIntervalMs}`,`appsMaxFlushBytes: ${e.appsMaxFlushBytes}`,`appsMaxSessions / total: ${e.appsMaxSessions} / ${e.appsMaxSessionsTotal}`,`appsMaxWaChars: ${e.appsMaxWaChars}`,`appsLogTailLines: ${e.appsLogTailLines}`,`appsSubmitDelayMs: ${e.appsSubmitDelayMs}`,`appsClearInput: ${e.appsClearInput}`,`appsClearInputDelayMs: ${e.appsClearInputDelayMs}`,`appsClearInputSequence: ${e.appsClearInputSequence}`,"","*Files*",`fileSendMaxBytes / fileReceiveMaxBytes: ${e.fileSendMaxBytes} / ${e.fileReceiveMaxBytes}`,`fileInboxSubdir: ${e.fileInboxSubdir}`,`fileReceiveRootMode: ${e.fileReceiveRootMode}`,`fileReceiveRootPath: ${e.fileReceiveRootPath||"(empty)"}`,"","*Recipes*",`recipesAllowDangerousBuiltins: ${e.recipesAllowDangerousBuiltins}`,`recipesMaxTaskChars: ${e.recipesMaxTaskChars}`,`recipesMacroDefaultCommand: ${e.recipesMacroDefaultCommand}`,"","*Service (chat)*",`serviceInstallFromChat: ${e.serviceInstallFromChat}`,"","*Updates (optional)*",`updateCheckEnabled: ${e.updateCheckEnabled}`,`updateCheckIntervalMs: ${e.updateCheckIntervalMs}`,`updateCheckPackageName: ${e.updateCheckPackageName}`,`updateInfoUrl: ${e.updateInfoUrl?"(set)":"(empty)"}`,"","*Tunneling (chat /tunnel)*",`tunnelEnabled: ${e.tunnelEnabled}`,`tunnelRelayUrl: ${e.tunnelRelayUrl}`,`tunnelMaxActive: ${e.tunnelMaxActive}`,"","*Chat LLM fallback (optional)*",`chatLlmFallbackEnabled: ${e.chatLlmFallbackEnabled}`,`chatLlmShellCommand: ${e.chatLlmShellCommand?"(set)":"(empty)"}`,`chatLlmTimeoutMs: ${e.chatLlmTimeoutMs}`,`chatLlmMaxInputChars / chatLlmMaxOutputChars: ${e.chatLlmMaxInputChars} / ${e.chatLlmMaxOutputChars}`,`chatLlmNeedsTty: ${e.chatLlmNeedsTty}`,`chatLlmWorkDir: ${e.chatLlmWorkDir||"(empty \u2014 temp dir per run)"}`,"",`File: ${L}`].join(`
51
+ `)}function _d(e){let t=pe(e);return["<b>Config</b> (secrets masked)","",`<b>gatewayMode</b> ${ge(e.gatewayMode)}`,`<b>commandPrefix</b> ${ge(e.commandPrefix)}`,`<b>shell</b> ${ge(e.shell)}`,`<b>syncTimeoutMs</b> ${e.syncTimeoutMs}`,`<b>syncMaxBytes</b> ${e.syncMaxBytes}`,`<b>jobLogTailLines</b> ${e.jobLogTailLines}`,"","<b>Cluster</b>",`clusterEnabled: ${e.clusterEnabled} \xB7 clusterRole: ${ge(e.clusterRole)}`,`clusterLabel: ${ge(e.clusterLabel||"(hostname)")}`,`clusterSenderBindings: ${Object.keys(e.clusterSenderBindings??{}).length} entries`,"","<b>Telegram</b>",`telegramBotToken: ${ge(cs(t))}`,`telegramAllowFrom: ${e.telegramAllowFrom.length} entries`,"",`<b>WhatsApp allowFrom</b> ${e.allowFrom.length} entries`,"","<b>Apps</b>",`${e.appsCols}\xD7${e.appsRows} flush ${e.appsFlushMs}/${e.appsMinIntervalMs} ms \u2026`,"","<b>Files</b>",`${ge(e.fileReceiveRootMode)} \xB7 inbox ${ge(e.fileInboxSubdir)}`,"","<b>Service (chat)</b>",`serviceInstallFromChat: ${e.serviceInstallFromChat}`,"","<b>Updates (optional)</b>",`updateCheckEnabled: ${e.updateCheckEnabled} \xB7 interval ms: ${e.updateCheckIntervalMs}`,`package: <code>${ge(e.updateCheckPackageName)}</code> \xB7 info URL: ${e.updateInfoUrl?"set":"empty"}`,"","<b>Tunneling (chat /tunnel)</b>",`tunnelEnabled: ${e.tunnelEnabled} \xB7 relay <code>${ge(e.tunnelRelayUrl)}</code> \xB7 max active: ${e.tunnelMaxActive}`,"","<b>Chat LLM fallback</b>",`enabled: ${e.chatLlmFallbackEnabled} \xB7 command: ${e.chatLlmShellCommand?"set":"empty"}`,`timeout ms: ${e.chatLlmTimeoutMs} \xB7 in/out chars: ${e.chatLlmMaxInputChars} / ${e.chatLlmMaxOutputChars}`,`needsTty: ${e.chatLlmNeedsTty} \xB7 workDir: ${ge(e.chatLlmWorkDir||"(empty)")}`,"",`<code>${ge(L)}</code>`].join(`
52
+ `)}function Ja(e,t){if(!Ga(t))return null;let n=t;if(n==="telegramBotToken")return`telegramBotToken: ${cs(pe(e))}`;let r=e[n];return`${t}: ${typeof r=="object"?JSON.stringify(r):String(r)}`}function Fd(e,t){let n=Ja(e,t);if(!n)return null;let r=n.split(": ");return r.length<2?ge(n):`<b>${ge(r[0])}</b> ${ge(r.slice(1).join(": "))}`}function Cr(e,t){let n=ja(t),r=!1,o=!1,s=!1;if(e==="telegramBotToken"){if(!Ze(n))throw new Error("Invalid bot token format (expect digits:secret from BotFather).");return ft(n),r=!0,s=!0,{reloadSuggested:r,shellWarning:o,tokenSaved:s}}let i=$();switch(e){case"gatewayMode":{let a=Vt(n);if(!a)throw new Error("gatewayMode: use whatsapp | telegram | both (or wa, tg, b)");i.gatewayMode=a,r=!0;break}case"clusterEnabled":{let a=bt(n);if(a===null)throw new Error("clusterEnabled: true or false");i.clusterEnabled=a;break}case"clusterRole":{let a=n.trim().toLowerCase();if(a!=="primary"&&a!=="secondary")throw new Error("clusterRole: primary | secondary");i.clusterRole=a;break}case"clusterLabel":i.clusterLabel=n.trim().slice(0,128);break;case"clusterSenderBindings":{let a=n.trim();if(a===""||a==="{}"||a.toLowerCase()==="clear"){i.clusterSenderBindings={};break}let l;try{l=JSON.parse(a)}catch{throw new Error('clusterSenderBindings: pass a JSON object, e.g. {"wa:+15551234567":"workshop-box"}')}if(!l||typeof l!="object"||Array.isArray(l))throw new Error("clusterSenderBindings must be a JSON object of sender \u2192 label/id");let c={};for(let[u,d]of Object.entries(l)){if(typeof d!="string"||!d.trim())throw new Error(`clusterSenderBindings.${u}: value must be a non-empty string`);c[u]=d.trim()}i.clusterSenderBindings=c;break}case"commandPrefix":if(!n.trim())throw new Error("commandPrefix cannot be empty");i.commandPrefix=n.trim().slice(0,32);break;case"syncTimeoutMs":{let a=Number.parseInt(n,10);if(!Number.isFinite(a)||a<=0)throw new Error("syncTimeoutMs: positive integer (ms)");i.syncTimeoutMs=a;break}case"syncMaxBytes":{let a=Number.parseInt(n,10);if(!Number.isFinite(a)||a<=0)throw new Error("syncMaxBytes: positive integer");i.syncMaxBytes=a;break}case"jobLogTailLines":{let a=Number.parseInt(n,10);if(!Number.isFinite(a)||a<=0)throw new Error("jobLogTailLines: positive integer");i.jobLogTailLines=a;break}case"shell":if(!n.trim())throw new Error("shell: non-empty path");i.shell=n.trim().slice(0,4096),o=!0;break;case"recipesMacroDefaultCommand":if(!n.trim())throw new Error('recipesMacroDefaultCommand: non-empty shell snippet with "$OMNISH_TASK"');i.recipesMacroDefaultCommand=ja(n).trim().slice(0,4096);break;case"appsCols":case"appsRows":case"appsFlushMs":case"appsMinIntervalMs":case"appsMaxFlushBytes":case"appsMaxSessions":case"appsMaxSessionsTotal":case"appsMaxWaChars":case"appsLogTailLines":case"appsSubmitDelayMs":case"appsClearInputDelayMs":case"recipesMaxTaskChars":case"fileSendMaxBytes":case"fileReceiveMaxBytes":{let a=Number.parseInt(n,10);if(!Number.isFinite(a)||a<0)throw new Error(`${e}: non-negative integer`);i[e]=a;break}case"appsClearInput":{let a=bt(n);if(a===null)throw new Error("appsClearInput: true or false");i.appsClearInput=a;break}case"appsClearInputSequence":i.appsClearInputSequence=n.trim().slice(0,200);break;case"fileInboxSubdir":i.fileInboxSubdir=n.trim().slice(0,80);break;case"fileReceiveRootMode":{let a=n.trim();if(!Wa.has(a))throw new Error(`fileReceiveRootMode: ${[...Wa].join(" | ")}`);i.fileReceiveRootMode=a;break}case"fileReceiveRootPath":i.fileReceiveRootPath=n.trim().slice(0,4096);break;case"recipesAllowDangerousBuiltins":{let a=bt(n);if(a===null)throw new Error("recipesAllowDangerousBuiltins: true or false");i.recipesAllowDangerousBuiltins=a;break}case"serviceInstallFromChat":{let a=bt(n);if(a===null)throw new Error("serviceInstallFromChat: true or false");i.serviceInstallFromChat=a;break}case"updateCheckEnabled":{let a=bt(n);if(a===null)throw new Error("updateCheckEnabled: true or false");i.updateCheckEnabled=a;break}case"updateCheckIntervalMs":{let a=Number.parseInt(n,10);if(!Number.isFinite(a)||a<36e5)throw new Error("updateCheckIntervalMs: integer ms, minimum 3600000 (1 hour)");i.updateCheckIntervalMs=Math.min(6048e5,a);break}case"updateCheckPackageName":if(!n.trim())throw new Error("updateCheckPackageName: non-empty npm package name");i.updateCheckPackageName=n.trim().slice(0,214);break;case"updateInfoUrl":i.updateInfoUrl=n.trim().slice(0,2048);break;case"tunnelEnabled":{let a=bt(n);if(a===null)throw new Error("tunnelEnabled: true or false");return Y({tunnelEnabled:a}),{reloadSuggested:r,shellWarning:o,tokenSaved:s}}case"tunnelRelayUrl":{let a=n.trim();if(!a)throw new Error("tunnelRelayUrl: non-empty URL");let l;try{l=new URL(a)}catch{throw new Error("tunnelRelayUrl: invalid URL")}if(l.protocol!=="http:"&&l.protocol!=="https:")throw new Error("tunnelRelayUrl: use http:// or https://");return Y({tunnelRelayUrl:a}),{reloadSuggested:r,shellWarning:o,tokenSaved:s}}case"tunnelMaxActive":{let a=Number.parseInt(n.trim(),10);if(!Number.isFinite(a)||a<1||a>50)throw new Error("tunnelMaxActive: integer 1\u201350");return Y({tunnelMaxActive:a}),{reloadSuggested:r,shellWarning:o,tokenSaved:s}}case"chatLlmFallbackEnabled":{let a=bt(n);if(a===null)throw new Error("chatLlmFallbackEnabled: true or false");return Y({chatLlmFallbackEnabled:a}),{reloadSuggested:r,shellWarning:o,tokenSaved:s}}case"chatLlmShellCommand":return Y({chatLlmShellCommand:n}),{reloadSuggested:r,shellWarning:o,tokenSaved:s};case"chatLlmTimeoutMs":{let a=Number.parseInt(n.trim(),10);if(!Number.isFinite(a)||a<=0)throw new Error("chatLlmTimeoutMs: positive integer (ms)");return Y({chatLlmTimeoutMs:a}),{reloadSuggested:r,shellWarning:o,tokenSaved:s}}case"chatLlmMaxInputChars":{let a=Number.parseInt(n.trim(),10);if(!Number.isFinite(a)||a<=0)throw new Error("chatLlmMaxInputChars: positive integer");return Y({chatLlmMaxInputChars:a}),{reloadSuggested:r,shellWarning:o,tokenSaved:s}}case"chatLlmMaxOutputChars":{let a=Number.parseInt(n.trim(),10);if(!Number.isFinite(a)||a<=0)throw new Error("chatLlmMaxOutputChars: positive integer");return Y({chatLlmMaxOutputChars:a}),{reloadSuggested:r,shellWarning:o,tokenSaved:s}}case"chatLlmNeedsTty":{let a=bt(n);if(a===null)throw new Error("chatLlmNeedsTty: true or false");return Y({chatLlmNeedsTty:a}),{reloadSuggested:r,shellWarning:o,tokenSaved:s}}case"chatLlmWorkDir":return Y({chatLlmWorkDir:n.trim()}),{reloadSuggested:r,shellWarning:o,tokenSaved:s}}return Re(i),{reloadSuggested:r,shellWarning:o,tokenSaved:s}}async function za(e,t){let n=e.trim(),r=n.toLowerCase();if(!n||r==="help")return z(pa());if(r==="keys"||r==="help keys")return m(["*Configurable keys* (/config set)","",St.join(", "),"","Examples:","/config set clusterLabel work-laptop",'/config set fileReceiveRootPath "/path/with spaces"',"/config set gatewayMode both","/config set tunnelEnabled true","/config set chatLlmFallbackEnabled false"].join(`
53
+ `));if(r==="show"){let i=$();return ie(Nd(i),_d(i))}let o=n.match(/^get\s+(\S+)\s*$/i);if(o){let i=o[1],a=$(),l=Ja(a,i);if(!l)return m(`Unknown key "${i}". /config keys`);let c=Fd(a,i);return ie(`${l}
54
+
55
+ ${L}`,`${c}<br/><br/><code>${ge(L)}</code>`)}let s=n.match(/^set\s+(\S+)\s+([\s\S]+)$/i);if(s){let i=s[1],a=s[2]??"";if(!Ga(i))return m(`Unknown key "${i}". /config keys`);try{let{reloadSuggested:l,shellWarning:c,tokenSaved:u}=Cr(i,a),d="";if(t?.reload&&l){let w=await t.reload();d=w.ok?`
56
+ Reload: ${w.summary}`:`
57
+ Reload failed: ${w.error}`}else l?d=`
58
+ Send /reload while omnish run is active (gatewayMode / Telegram).`:d=`
59
+ Send /reload to pick up changes where applicable.`;let p=c?`
60
+ \u26A0 shell changed \u2014 affects all commands run via omnish.`:"",f=u?`
61
+ telegramBotToken saved (not echoed).`:"",y=`Set *${i}* (saved).${f}${p}${d}`,h=`<b>Set ${ge(i)}</b> (saved).${f?"<br/>token saved (not echoed).":""}${c?"<br/>\u26A0 shell path changed.":""}${ge(d)}`;return ie(y,h)}catch(l){return m(`Error: ${String(l)}`)}}return m("Unknown /config command. Try /config help or /config show")}de();var us=[...St,"platformToken","platformDeviceId"],ds={platform_url:"tunnelRelayUrl",tunnel_relay_url:"tunnelRelayUrl",platform_token:"platformToken",token:"platformToken",omnish_token:"platformToken",platform_device_id:"platformDeviceId",device_id:"platformDeviceId"};for(let e of us)ds[e]=e;function Rr(e){let t=e.trim().toLowerCase().replace(/-/g,"_");return ds[t]??null}function Ka(){return Object.keys(ds).sort()}function vt(e){let t=e.trim().toLowerCase();return t==="true"||t==="1"||t==="yes"||t==="on"?!0:t==="false"||t==="0"||t==="no"||t==="off"?!1:null}function Ud(e){let t=e.trim();return(t.startsWith('"')&&t.endsWith('"')&&t.length>=2||t.startsWith("'")&&t.endsWith("'")&&t.length>=2)&&(t=t.slice(1,-1)),t}function Dd(e){return O[e]}function tn(e,t){let n=Ud(t),r=$();switch(e){case"platformToken":return Y({platformToken:n});case"platformDeviceId":return Y({platformDeviceId:n});case"gatewayMode":{let o=Vt(n);if(!o)throw new Error('gatewayMode: "whatsapp", "telegram", or "both"');return r.gatewayMode=o,Re(r),r}case"telegramBotToken":if(!Ze(n))throw new Error("telegramBotToken: invalid bot token format");return ft(n);case"tunnelRelayUrl":{let o=n.trim();if(!o)throw new Error("tunnelRelayUrl: non-empty URL");let s;try{s=new URL(o)}catch{throw new Error("tunnelRelayUrl: invalid URL")}if(s.protocol!=="http:"&&s.protocol!=="https:")throw new Error("tunnelRelayUrl: use http:// or https://");return Y({tunnelRelayUrl:o})}case"tunnelEnabled":{let o=vt(n);if(o===null)throw new Error("tunnelEnabled: true or false");return Y({tunnelEnabled:o})}case"tunnelMaxActive":{let o=Number.parseInt(n,10);if(!Number.isFinite(o)||o<1||o>50)throw new Error("tunnelMaxActive: integer 1\u201350");return Y({tunnelMaxActive:o})}case"clusterSenderBindings":{let o;try{o=JSON.parse(n)}catch{throw new Error("clusterSenderBindings: valid JSON object")}if(!o||typeof o!="object"||Array.isArray(o))throw new Error("clusterSenderBindings: JSON object required");return Y({clusterSenderBindings:o})}default:break}if(e==="clusterEnabled"){let o=vt(n);if(o===null)throw new Error("clusterEnabled: true or false");r.clusterEnabled=o}else if(e==="clusterRole"){if(n!=="primary"&&n!=="secondary")throw new Error('clusterRole: "primary" or "secondary"');r.clusterRole=n}else if(e==="clusterLabel")r.clusterLabel=n.trim().slice(0,64);else if(e==="commandPrefix"){if(!n)throw new Error("commandPrefix: non-empty");r.commandPrefix=n}else if(e==="shell"){if(!n)throw new Error("shell: non-empty path");r.shell=n}else if(e==="syncTimeoutMs"){let o=Number.parseInt(n,10);if(!Number.isFinite(o)||o<=0)throw new Error("syncTimeoutMs: positive integer");r.syncTimeoutMs=o}else if(e==="syncMaxBytes"){let o=Number.parseInt(n,10);if(!Number.isFinite(o)||o<=0)throw new Error("syncMaxBytes: positive integer");r.syncMaxBytes=o}else if(e==="jobLogTailLines"){let o=Number.parseInt(n,10);if(!Number.isFinite(o)||o<=0)throw new Error("jobLogTailLines: positive integer");r.jobLogTailLines=o}else if(e==="appsCols"||e==="appsRows"){let o=Number.parseInt(n,10);if(!Number.isFinite(o)||o<20)throw new Error(`${e}: integer >= 20`);r[e]=o}else if(e==="appsFlushMs"||e==="appsMinIntervalMs"||e==="appsMaxFlushBytes"||e==="appsMaxWaChars"||e==="appsLogTailLines"||e==="appsSubmitDelayMs"||e==="appsClearInputDelayMs"){let o=Number.parseInt(n,10);if(!Number.isFinite(o)||o<0)throw new Error(`${e}: non-negative integer`);r[e]=o}else if(e==="appsMaxSessions"||e==="appsMaxSessionsTotal"){let o=Number.parseInt(n,10);if(!Number.isFinite(o)||o<1)throw new Error(`${e}: positive integer`);r[e]=o}else if(e==="appsClearInput"){let o=vt(n);if(o===null)throw new Error("appsClearInput: true or false");r.appsClearInput=o}else if(e==="appsClearInputSequence")r.appsClearInputSequence=n;else if(e==="fileSendMaxBytes"||e==="fileReceiveMaxBytes"){let o=Number.parseInt(n,10);if(!Number.isFinite(o)||o<0)throw new Error(`${e}: non-negative integer`);r[e]=o}else if(e==="fileInboxSubdir")r.fileInboxSubdir=n.trim().slice(0,128);else if(e==="fileReceiveRootMode"){if(!new Set(["downloads","omnishData","sessionCwd","processCwd","fixed"]).has(n))throw new Error("fileReceiveRootMode: downloads|omnishData|sessionCwd|processCwd|fixed");r.fileReceiveRootMode=n}else if(e==="fileReceiveRootPath")r.fileReceiveRootPath=n.trim().slice(0,4096);else if(e==="recipesAllowDangerousBuiltins"){let o=vt(n);if(o===null)throw new Error("recipesAllowDangerousBuiltins: true or false");r.recipesAllowDangerousBuiltins=o}else if(e==="recipesMaxTaskChars"){let o=Number.parseInt(n,10);if(!Number.isFinite(o)||o<0)throw new Error("recipesMaxTaskChars: non-negative integer");r.recipesMaxTaskChars=o}else if(e==="recipesMacroDefaultCommand"){if(!n.includes("$OMNISH_TASK"))throw new Error('recipesMacroDefaultCommand: must include "$OMNISH_TASK"');r.recipesMacroDefaultCommand=n}else if(e==="serviceInstallFromChat"){let o=vt(n);if(o===null)throw new Error("serviceInstallFromChat: true or false");r.serviceInstallFromChat=o}else if(e==="updateCheckEnabled"){let o=vt(n);if(o===null)throw new Error("updateCheckEnabled: true or false");r.updateCheckEnabled=o}else if(e==="updateCheckIntervalMs"){let o=Number.parseInt(n,10);if(!Number.isFinite(o)||o<36e5)throw new Error("updateCheckIntervalMs: min 3600000");r.updateCheckIntervalMs=Math.min(6048e5,o)}else if(e==="updateCheckPackageName"){if(!n.trim())throw new Error("updateCheckPackageName: non-empty");r.updateCheckPackageName=n.trim().slice(0,214)}else if(e==="updateInfoUrl")r.updateInfoUrl=n.trim().slice(0,2048);else if(e==="chatLlmFallbackEnabled"){let o=vt(n);if(o===null)throw new Error("chatLlmFallbackEnabled: true or false");return Y({chatLlmFallbackEnabled:o})}else{if(e==="chatLlmShellCommand")return Y({chatLlmShellCommand:n});if(e==="chatLlmTimeoutMs"){let o=Number.parseInt(n,10);if(!Number.isFinite(o)||o<=0)throw new Error("chatLlmTimeoutMs: positive integer");return Y({chatLlmTimeoutMs:o})}else if(e==="chatLlmMaxInputChars"){let o=Number.parseInt(n,10);if(!Number.isFinite(o)||o<=0)throw new Error("chatLlmMaxInputChars: positive integer");return Y({chatLlmMaxInputChars:o})}else if(e==="chatLlmMaxOutputChars"){let o=Number.parseInt(n,10);if(!Number.isFinite(o)||o<=0)throw new Error("chatLlmMaxOutputChars: positive integer");return Y({chatLlmMaxOutputChars:o})}else if(e==="chatLlmNeedsTty"){let o=vt(n);if(o===null)throw new Error("chatLlmNeedsTty: true or false");return Y({chatLlmNeedsTty:o})}else{if(e==="chatLlmWorkDir")return Y({chatLlmWorkDir:n.trim()});throw new Error(`Unsupported key: ${e}`)}}return Re(r),r}function qa(e){let t=Dd(e);if(e==="platformToken"||e==="platformDeviceId")return Y({[e]:t});if(e==="tunnelRelayUrl")return Y({tunnelRelayUrl:t});let n=$();return n[e]=t,Re(n),n}function Ft(e,t){if(e==="platformToken"||e==="telegramBotToken"){let n=t.trim();return n?n.length<=8?"(set)":`${n.slice(0,4)}\u2026${n.slice(-4)}`:"(empty)"}return t}de();j();import nn from"node:fs";import gs from"node:os";import Fn from"node:path";import Xa from"node:crypto";var ms="\u2063omnish/c v1",Hd=/([nlra])=([^\s\]]*)/g;function ze(e){return e.replace(/-/g,"").slice(0,8)}function ps(e){return e.replace(/[\s\]\r\n]/g,"_").slice(0,64)}function Bd(e){let t=ps(ze(e.nodeId)),n=ps(e.label||""),r=e.role==="primary"?"p":"s",o=ps(e.activeNodeId?ze(e.activeNodeId):"");return`${ms} [n=${t} l=${n} r=${r} a=${o}]`}function Ya(e,t){let n=Bd(t);if(e.includes(n))return e;let r=e.replace(/\s+$/,"");return r.length===0?n:`${r}
62
62
 
63
- ${n}`}function ji(e){if(!e||!e.includes(ho))return null;let t=e.indexOf(ho),n=e.slice(t),r=n.indexOf("["),o=n.indexOf("]");if(r<0||o<0||o<r)return null;let s=n.slice(r+1,o),i={};for(let l of s.matchAll(_c))i[l[1]]=l[2]??"";if(!i.n)return null;let a=i.r==="p"?"primary":"secondary";return{nodeId:i.n,label:i.l??"",role:a,activeNodeId:i.a??""}}var Gi=3,Hc="node-id",Dc="cluster-local.json",Ui=8;function Ji(){return on.join(N,Dc)}function bo(){return on.join(N,Hc)}function Me(){H(N);let e=bo();try{if(Lt.existsSync(e)){let n=Lt.readFileSync(e,"utf8").trim();if(/^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(n))return n}}catch{}let t=Wi.randomUUID();return Lt.writeFileSync(e,`${t}
64
- `,{mode:384}),t}function zi(){return{schemaVersion:Gi,updatedAt:new Date().toISOString(),peers:[],senderBindings:{}}}function jc(e){if(!e||typeof e!="object")return{};let t={};for(let[n,r]of Object.entries(e)){if(!r||typeof r!="object")continue;let o=r;if(typeof o.nodeId!="string"||!o.nodeId)continue;let s=o.source==="config"?"config":"chat";t[n]={senderKey:typeof o.senderKey=="string"?o.senderKey:n,nodeId:o.nodeId,sinceIso:typeof o.sinceIso=="string"&&o.sinceIso?o.sinceIso:new Date(0).toISOString(),source:s}}return t}function ee(){let e=Ji();try{let t=Lt.readFileSync(e,"utf8"),n=JSON.parse(t),r=Array.isArray(n.peers)?n.peers.filter(s=>typeof s=="object"&&s!==null&&typeof s.nodeId=="string"&&typeof s.label=="string").map(s=>({nodeId:s.nodeId,label:s.label,role:s.role==="primary"?"primary":"secondary",lastSeenIso:typeof s.lastSeenIso=="string"?s.lastSeenIso:new Date(0).toISOString()})):[],o=jc(n.senderBindings);return{schemaVersion:Gi,updatedAt:typeof n.updatedAt=="string"?n.updatedAt:new Date().toISOString(),peers:r,senderBindings:o}}catch{return zi()}}function Uc(e,t){let n=on.dirname(e);H(n);let r=`${JSON.stringify(t,null,2)}
65
- `,o=on.join(n,`.${on.basename(e)}.tmp.${process.pid}.${Wi.randomBytes(4).toString("hex")}`);Lt.writeFileSync(o,r,{mode:384}),Lt.renameSync(o,e)}function So(e){let t=Ji(),n=zi();for(let r=0;r<Ui;r++){n=ee(),e(n),n.updatedAt=new Date().toISOString();try{return Uc(t,n),n}catch{}}throw new Error(`Could not write cluster local state after ${Ui} attempts: ${t}`)}function qi(e){return(e.clusterLabel??"").trim()||wo.hostname()}function te(e){return e.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;")}function Qi(e,t){let n=e.peers.findIndex(r=>r.nodeId===t.nodeId);n>=0?e.peers[n]=t:e.peers.push(t)}function sn(e){return{nodeId:Oe(Me()),label:qi(e),role:e.clusterRole,lastSeenIso:new Date().toISOString()}}function Yi(e,t,n){let r=n.trim();if(!r)return{ok:!1,reason:"not-found"};let o=sn(t),s=[o];for(let l of e.peers)l.nodeId!==o.nodeId&&s.push(l);let i=r.toLowerCase();if(/^[0-9a-f]{8}$/i.test(r)){let l=s.find(u=>u.nodeId.toLowerCase()===i);if(l)return{ok:!0,peer:l}}let a=s.filter(l=>l.label.toLowerCase()===i);return a.length===1?{ok:!0,peer:a[0]}:a.length>1?{ok:!1,reason:"ambiguous-label",matches:a}:{ok:!1,reason:"not-found"}}function Ye(e,t){let n=ee(),r=n.senderBindings[t];if(r&&r.nodeId)return r;let o=e.clusterSenderBindings?.[t];if(typeof o=="string"&&o.trim()){let s=Yi(n,e,o);if(s.ok)return{senderKey:t,nodeId:s.peer.nodeId,sinceIso:new Date(0).toISOString(),source:"config"}}return null}function ko(e,t,n="chat"){let r=R(),o=ee(),s=Yi(o,r,t);if(!s.ok)return{state:o,resolved:s};let i=new Date().toISOString();return{state:So(l=>{l.senderBindings[e]={senderKey:e,nodeId:s.peer.nodeId,sinceIso:i,source:n},Qi(l,{...s.peer,lastSeenIso:i})}),resolved:s}}function Wc(e){let t=null;return{state:So(r=>{r.senderBindings[e]&&(t=r.senderBindings[e]??null,delete r.senderBindings[e])}),removed:t}}function Vi(e,t){let n=Oe(Me()),r=new Date().toISOString();return So(o=>{if(Qi(o,{nodeId:e.nodeId,label:e.label||e.nodeId,role:e.role,lastSeenIso:r}),e.nodeId!==n&&t&&e.activeNodeId){let s=o.senderBindings[t];(!s||s.nodeId!==e.activeNodeId)&&(o.senderBindings[t]={senderKey:t,nodeId:e.activeNodeId,sinceIso:r,source:"chat"})}})}function Ki(e,t){let n=Ye(t,e);return n?n.nodeId===Oe(Me()):!1}function Gc(e,t){let n=sn(t).nodeId,r=new Set([n]);for(let s of e.peers)r.add(s.nodeId);return[...r].sort()[0]??n}function be(e,t){return Gc(e,t)===Oe(Me())}function Zn(e,t,n){let r=Oe(Me()),o=["*Computers*",""],s=n?Ye(t,n):null;n&&(s?o.push(`Your binding: \`${s.nodeId}\` (${s.source})`):o.push("Your binding: (none \u2014 send /c use <label-or-id>)"),o.push(""));let i=new Map;i.set(r,sn(t));for(let l of e.peers)i.set(l.nodeId,l);let a=[...i.values()].sort((l,u)=>l.label.localeCompare(u.label));if(a.length===0)return o.push("(no peers observed yet \u2014 run /c status from this chat)"),o.join(`
66
- `);for(let l of a){let u=l.nodeId===r,d=[(s?l.nodeId===s.nodeId:!1)?"your binding":null,u?"you":null].filter(Boolean).join(", "),m=d?` (${d})`:"";o.push(`${ge}*${l.label}*${m}
63
+ ${n}`}function Qa(e){if(!e||!e.includes(ms))return null;let t=e.indexOf(ms),n=e.slice(t),r=n.indexOf("["),o=n.indexOf("]");if(r<0||o<0||o<r)return null;let s=n.slice(r+1,o),i={};for(let l of s.matchAll(Hd))i[l[1]]=l[2]??"";if(!i.n)return null;let a=i.r==="p"?"primary":"secondary";return{nodeId:i.n,label:i.l??"",role:a,activeNodeId:i.a??""}}var Za=3,jd="node-id",Wd="cluster-local.json",Va=8;function el(){return Fn.join(U,Wd)}function hs(){return Fn.join(U,jd)}function De(){W(U);let e=hs();try{if(nn.existsSync(e)){let n=nn.readFileSync(e,"utf8").trim();if(/^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(n))return n}}catch{}let t=Xa.randomUUID();return nn.writeFileSync(e,`${t}
64
+ `,{mode:384}),t}function tl(){return{schemaVersion:Za,updatedAt:new Date().toISOString(),peers:[],senderBindings:{}}}function Gd(e){if(!e||typeof e!="object")return{};let t={};for(let[n,r]of Object.entries(e)){if(!r||typeof r!="object")continue;let o=r;if(typeof o.nodeId!="string"||!o.nodeId)continue;let s=o.source==="config"?"config":"chat";t[n]={senderKey:typeof o.senderKey=="string"?o.senderKey:n,nodeId:o.nodeId,sinceIso:typeof o.sinceIso=="string"&&o.sinceIso?o.sinceIso:new Date(0).toISOString(),source:s}}return t}function ce(){let e=el();try{let t=nn.readFileSync(e,"utf8"),n=JSON.parse(t),r=Array.isArray(n.peers)?n.peers.filter(s=>typeof s=="object"&&s!==null&&typeof s.nodeId=="string"&&typeof s.label=="string").map(s=>({nodeId:s.nodeId,label:s.label,role:s.role==="primary"?"primary":"secondary",lastSeenIso:typeof s.lastSeenIso=="string"?s.lastSeenIso:new Date(0).toISOString()})):[],o=Gd(n.senderBindings);return{schemaVersion:Za,updatedAt:typeof n.updatedAt=="string"?n.updatedAt:new Date().toISOString(),peers:r,senderBindings:o}}catch{return tl()}}function Jd(e,t){let n=Fn.dirname(e);W(n);let r=`${JSON.stringify(t,null,2)}
65
+ `,o=Fn.join(n,`.${Fn.basename(e)}.tmp.${process.pid}.${Xa.randomBytes(4).toString("hex")}`);nn.writeFileSync(o,r,{mode:384}),nn.renameSync(o,e)}function ws(e){let t=el(),n=tl();for(let r=0;r<Va;r++){n=ce(),e(n),n.updatedAt=new Date().toISOString();try{return Jd(t,n),n}catch{}}throw new Error(`Could not write cluster local state after ${Va} attempts: ${t}`)}function nl(e){return(e.clusterLabel??"").trim()||gs.hostname()}function ue(e){return e.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;")}function rl(e,t){let n=e.peers.findIndex(r=>r.nodeId===t.nodeId);n>=0?e.peers[n]=t:e.peers.push(t)}function Un(e){return{nodeId:ze(De()),label:nl(e),role:e.clusterRole,lastSeenIso:new Date().toISOString()}}function ol(e,t,n){let r=n.trim();if(!r)return{ok:!1,reason:"not-found"};let o=Un(t),s=[o];for(let l of e.peers)l.nodeId!==o.nodeId&&s.push(l);let i=r.toLowerCase();if(/^[0-9a-f]{8}$/i.test(r)){let l=s.find(c=>c.nodeId.toLowerCase()===i);if(l)return{ok:!0,peer:l}}let a=s.filter(l=>l.label.toLowerCase()===i);return a.length===1?{ok:!0,peer:a[0]}:a.length>1?{ok:!1,reason:"ambiguous-label",matches:a}:{ok:!1,reason:"not-found"}}function ct(e,t){let n=ce(),r=n.senderBindings[t];if(r&&r.nodeId)return r;let o=e.clusterSenderBindings?.[t];if(typeof o=="string"&&o.trim()){let s=ol(n,e,o);if(s.ok)return{senderKey:t,nodeId:s.peer.nodeId,sinceIso:new Date(0).toISOString(),source:"config"}}return null}function ys(e,t,n="chat"){let r=$(),o=ce(),s=ol(o,r,t);if(!s.ok)return{state:o,resolved:s};let i=new Date().toISOString();return{state:ws(l=>{l.senderBindings[e]={senderKey:e,nodeId:s.peer.nodeId,sinceIso:i,source:n},rl(l,{...s.peer,lastSeenIso:i})}),resolved:s}}function zd(e){let t=null;return{state:ws(r=>{r.senderBindings[e]&&(t=r.senderBindings[e]??null,delete r.senderBindings[e])}),removed:t}}function sl(e,t){let n=ze(De()),r=new Date().toISOString();return ws(o=>{if(rl(o,{nodeId:e.nodeId,label:e.label||e.nodeId,role:e.role,lastSeenIso:r}),e.nodeId!==n&&t&&e.activeNodeId){let s=o.senderBindings[t];(!s||s.nodeId!==e.activeNodeId)&&(o.senderBindings[t]={senderKey:t,nodeId:e.activeNodeId,sinceIso:r,source:"chat"})}})}function il(e,t){let n=ct(t,e);return n?n.nodeId===ze(De()):!1}function Kd(e,t){let n=Un(t).nodeId,r=new Set([n]);for(let s of e.peers)r.add(s.nodeId);return[...r].sort()[0]??n}function Oe(e,t){return Kd(e,t)===ze(De())}function Mr(e,t,n){let r=ze(De()),o=["*Computers*",""],s=n?ct(t,n):null;n&&(s?o.push(`Your binding: \`${s.nodeId}\` (${s.source})`):o.push("Your binding: (none \u2014 send /c use <label-or-id>)"),o.push(""));let i=new Map;i.set(r,Un(t));for(let l of e.peers)i.set(l.nodeId,l);let a=[...i.values()].sort((l,c)=>l.label.localeCompare(c.label));if(a.length===0)return o.push("(no peers observed yet \u2014 run /c status from this chat)"),o.join(`
66
+ `);for(let l of a){let c=l.nodeId===r,d=[(s?l.nodeId===s.nodeId:!1)?"your binding":null,c?"you":null].filter(Boolean).join(", "),p=d?` (${d})`:"";o.push(`${Me}*${l.label}*${p}
67
67
  id \`${l.nodeId}\` \xB7 role ${l.role}
68
68
  seen ${l.lastSeenIso}`)}return o.push(""),o.push(`Updated: ${e.updatedAt}`),o.join(`
69
- `)}function yo(e,t,n){let r=Oe(Me()),o=["<b>Computers</b>",""],s=n?Ye(t,n):null;n&&(s?o.push(`Your binding: <code>${te(s.nodeId)}</code> (${te(s.source)})`):o.push("Your binding: (none \u2014 send /c use &lt;label-or-id&gt;)"),o.push(""));let i=new Map;i.set(r,sn(t));for(let l of e.peers)i.set(l.nodeId,l);let a=[...i.values()].sort((l,u)=>l.label.localeCompare(u.label));if(a.length===0)return o.push("(no peers observed yet \u2014 run /c status from this chat)"),o.join(`
70
- `);for(let l of a){let u=l.nodeId===r,d=[(s?l.nodeId===s.nodeId:!1)?"your binding":null,u?"you":null].filter(Boolean).join(", "),m=d?` (${te(d)})`:"";o.push(`\u2022 <b>${te(l.label)}</b>${m}<br/> id <code>${te(l.nodeId)}</code> \xB7 role ${te(l.role)}<br/> seen ${te(l.lastSeenIso)}`)}return o.push(""),o.push(`Updated: ${te(e.updatedAt)}`),o.join(`
71
- `)}function xo(e,t,n){return Zn(e,t,n??null).replace(/\*([^*]+)\*/g,"$1").replace(/`([^`]+)`/g,"$1")}function vo(e,t){let n=Me(),r=Oe(n),o=ee(),s=e.clusterRole,i=qi(e),a=t?Ye(e,t):null,l=a?a.nodeId===r:!1,u=t?a?`your binding: ${a.nodeId} (${a.source}${l?", here":""})`:"your binding: (none)":"",c=["*This computer*","",`label: ${i}`,`node id: \`${r}\` (full id in ${bo()})`,`host: ${wo.hostname()}`,`clusterRole: ${s}`,u,`peers known: ${o.peers.length}`,`senderBindings (chat): ${Object.keys(o.senderBindings).length}`].filter(Boolean).join(`
72
- `),d=["<b>This computer</b>","",`label: ${te(i)}`,`node id: <code>${te(r)}</code> (full id in ${te(bo())})`,`host: ${te(wo.hostname())}`,`clusterRole: ${te(s)}`,t?te(u):"",`peers known: ${o.peers.length}`,`senderBindings (chat): ${Object.keys(o.senderBindings).length}`].filter(Boolean).join(`
73
- `);return{wa:c,tg:d}}function Jc(e){let t=e.clusterRole,n=["*Computers* (per-sender bindings)","","Each allowlisted phone picks one machine to talk to. Other senders are not affected.","Selection persists; defaults can be set in config (clusterSenderBindings).","","Shorthand: /pcs \u2026 \xB7 /c \u2026","",`${ge}/c use <label-or-id> \u2014 bind your messages to that machine`,`${ge}/c here \u2014 bind your messages to THIS machine`,`${ge}/c using \u2014 show your current binding`,`${ge}/c unuse \u2014 clear your chat-set binding (config default still applies, if any)`,`${ge}/c status \u2014 every online host replies with its own paragraph`,`${ge}/c list \u2014 locally known roster (single responder)`,`${ge}/c help \u2014 this help`,"",`clusterRole on this host: ${t}`].join(`
74
- `),r=["<b>Computers</b> (per-sender bindings)","","Each allowlisted phone picks one machine to talk to. Other senders are not affected.","Selection persists; defaults can be set in config (clusterSenderBindings).","","Shorthand: /pcs \u2026 \xB7 /c \u2026","","\u2022 /c use &lt;label-or-id&gt; \u2014 bind your messages to that machine","\u2022 /c here \u2014 bind your messages to THIS machine","\u2022 /c using \u2014 show your current binding","\u2022 /c unuse \u2014 clear your chat-set binding","\u2022 /c status \u2014 every online host replies with its own paragraph","\u2022 /c list \u2014 locally known roster (single responder)","\u2022 /c help \u2014 this help","",`clusterRole on this host: ${te(t)}`].join(`
75
- `);return Y(n,r)}function zc(e,t){if(t.ok)return p("");if(t.reason==="ambiguous-label"){let n=(t.matches??[]).map(r=>`\`${r.nodeId}\` (${r.label})`).join(", ");return p(`Label "${e}" matches multiple machines: ${n}. Use the 8-character id with /c use <id>.`)}return p(`No machine matches "${e}". Send /c list to see ids and labels, or /c status to refresh the roster.`)}function Xi(e,t,n){let r=t.trim().split(/\s+/),o=r[0]?.toLowerCase()??"";if(!o||o==="help"){if(!e.clusterEnabled&&o!=="help"){let l=ee();return be(l,e)?p("Cluster is disabled. Enable with: /config set clusterEnabled true (then /c use <label-or-id>). /c help for the new commands."):null}let a=ee();return be(a,e)?Jc(e):null}let s=Oe(Me());if(o==="use"||o==="bind"){let a=r.slice(1).join(" ").trim();if(!n){let h=ee();return be(h,e)?p("/c use is only available on chats with a known sender (allowlisted WhatsApp/Telegram)."):null}if(!a){let h=ee();return be(h,e)?p("Usage: /c use <label-or-id>"):null}e.clusterEnabled||Fn(!0);let u=ee().senderBindings[n]??null,{state:c,resolved:d}=ko(n,a,"chat");if(!d.ok)return be(c,e)?zc(a,d):null;let m=d.peer.nodeId===s,y=u?u.nodeId===s:!1;if(!m)return null;let g=[`*Bound to ${d.peer.label}*`,`id \`${d.peer.nodeId}\``,"","Your messages now route to this machine. Other senders are not affected.","",Zn(c,e,n)].join(`
76
- `),f=[`<b>Bound to ${te(d.peer.label)}</b>`,`id <code>${te(d.peer.nodeId)}</code>`,"","Your messages now route to this machine. Other senders are not affected.","",yo(c,e,n)].join(`
77
- `);return Y(g,f)}if(o==="here"||o==="take"){if(!n){let d=ee();return be(d,e)?p("/c here is only available on chats with a known sender (allowlisted WhatsApp/Telegram)."):null}e.clusterEnabled||Fn(!0);let{state:a,resolved:l}=ko(n,s,"chat");if(!l.ok)return be(a,e)?p("Could not bind to this machine."):null;let u=["*Bound to this machine.*","","Your messages now route here. Other senders are not affected.","",Zn(a,e,n)].join(`
78
- `),c=["<b>Bound to this machine.</b>","","Your messages now route here. Other senders are not affected.","",yo(a,e,n)].join(`
79
- `);return Y(u,c)}if(o==="using"){if(!n){let u=ee();return be(u,e)?p("/c using is only available on chats with a known sender (allowlisted WhatsApp/Telegram)."):null}let a=Ye(e,n);if(a){if(a.nodeId!==s)return null;let d=[...ee().peers,sn(e)].find(g=>g.nodeId===a.nodeId)?.label??"(unknown label)",m=[`*Bound to ${d}*`,`id \`${a.nodeId}\` \xB7 source ${a.source}`,a.source==="chat"?`since ${a.sinceIso}`:"(from config)"].join(`
80
- `),y=[`<b>Bound to ${te(d)}</b>`,`id <code>${te(a.nodeId)}</code> \xB7 source ${te(a.source)}`,a.source==="chat"?`since ${te(a.sinceIso)}`:"(from config)"].join(`
81
- `);return Y(m,y)}let l=ee();return be(l,e)?p("You are not bound to any machine. Send /c use <label-or-id> to bind, or set a default in config.json (clusterSenderBindings)."):null}if(o==="unuse"||o==="unbind"||o==="release"){if(!n){let m=ee();return be(m,e)?p("/c unuse is only available on chats with a known sender (allowlisted WhatsApp/Telegram)."):null}let l=ee().senderBindings[n]??null,{state:u}=Wc(n);if(l){if(l.nodeId!==s)return null}else if(!be(u,e))return null;let c=Ye(e,n),d=c?`
82
- Config default still applies: \`${c.nodeId}\`.`:`
83
- No config default is set; nobody will answer until you /c use <label-or-id>.`;return p(`Cleared your chat binding.${d}`)}if(o==="step-down"||o==="stepdown"){let a=ee();return be(a,e)?p("/c step-down is no longer used. The cluster is per-sender: send /c unuse to clear your binding."):null}if(o==="status"){let a=vo(e,n);return Y(a.wa,a.tg)}if(o==="list"){let a=ee(),l=n?Ye(e,n):null;if(l){if(l.nodeId!==s)return null}else if(!be(a,e))return null;return Y(Zn(a,e,n??null),yo(a,e,n??null))}let i=ee();return be(i,e)?p(`Unknown /c subcommand "${o}". Try /c help`):null}function Zi(e,t){return Fn(!0),ko(e,t,"chat")}function pe(e){return e.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;")}function $o(e){let t=e.trim();return t?t.length<=8?"(set)":`${t.slice(0,4)}\u2026${t.slice(-4)}`:"(empty)"}function ea(e){let t=e.trim();return(t.startsWith('"')&&t.endsWith('"')&&t.length>=2||t.startsWith("'")&&t.endsWith("'")&&t.length>=2)&&(t=t.slice(1,-1)),t}function an(e){let t=e.trim().toLowerCase();return t==="true"||t==="1"||t==="yes"||t==="on"?!0:t==="false"||t==="0"||t==="no"||t==="off"?!1:null}var ta=new Set(["downloads","omnishData","sessionCwd","processCwd","fixed"]),Ot=["gatewayMode","clusterEnabled","clusterRole","clusterLabel","clusterSenderBindings","commandPrefix","syncTimeoutMs","syncMaxBytes","jobLogTailLines","shell","appsCols","appsRows","appsFlushMs","appsMinIntervalMs","appsMaxFlushBytes","appsMaxSessions","appsMaxSessionsTotal","appsMaxWaChars","appsLogTailLines","appsSubmitDelayMs","appsClearInput","appsClearInputDelayMs","appsClearInputSequence","fileSendMaxBytes","fileReceiveMaxBytes","fileInboxSubdir","fileReceiveRootMode","fileReceiveRootPath","recipesAllowDangerousBuiltins","recipesMaxTaskChars","recipesMacroDefaultCommand","telegramBotToken","serviceInstallFromChat","updateCheckEnabled","updateCheckIntervalMs","updateCheckPackageName","updateInfoUrl"];function na(e){return Ot.includes(e)}function qc(e){let t=oe(e);return["*Config* (secrets masked)","",`gatewayMode: ${e.gatewayMode}`,`commandPrefix: ${e.commandPrefix}`,`shell: ${e.shell}`,`syncTimeoutMs: ${e.syncTimeoutMs}`,`syncMaxBytes: ${e.syncMaxBytes}`,`jobLogTailLines: ${e.jobLogTailLines}`,"","*Cluster*",`clusterEnabled: ${e.clusterEnabled}`,`clusterRole: ${e.clusterRole}`,`clusterLabel: ${e.clusterLabel||"(hostname)"}`,`clusterSenderBindings: ${Object.keys(e.clusterSenderBindings??{}).length} entries`,"","*Telegram*",`telegramBotToken: ${$o(t)}`,`telegramAllowFrom: ${e.telegramAllowFrom.length} entries`,"","*WhatsApp allowFrom*",`${e.allowFrom.length} entries`,"","*Apps*",`appsCols \xD7 appsRows: ${e.appsCols}\xD7${e.appsRows}`,`appsFlushMs / appsMinIntervalMs: ${e.appsFlushMs} / ${e.appsMinIntervalMs}`,`appsMaxFlushBytes: ${e.appsMaxFlushBytes}`,`appsMaxSessions / total: ${e.appsMaxSessions} / ${e.appsMaxSessionsTotal}`,`appsMaxWaChars: ${e.appsMaxWaChars}`,`appsLogTailLines: ${e.appsLogTailLines}`,`appsSubmitDelayMs: ${e.appsSubmitDelayMs}`,`appsClearInput: ${e.appsClearInput}`,`appsClearInputDelayMs: ${e.appsClearInputDelayMs}`,`appsClearInputSequence: ${e.appsClearInputSequence}`,"","*Files*",`fileSendMaxBytes / fileReceiveMaxBytes: ${e.fileSendMaxBytes} / ${e.fileReceiveMaxBytes}`,`fileInboxSubdir: ${e.fileInboxSubdir}`,`fileReceiveRootMode: ${e.fileReceiveRootMode}`,`fileReceiveRootPath: ${e.fileReceiveRootPath||"(empty)"}`,"","*Recipes*",`recipesAllowDangerousBuiltins: ${e.recipesAllowDangerousBuiltins}`,`recipesMaxTaskChars: ${e.recipesMaxTaskChars}`,`recipesMacroDefaultCommand: ${e.recipesMacroDefaultCommand}`,"","*Service (chat)*",`serviceInstallFromChat: ${e.serviceInstallFromChat}`,"","*Updates (optional)*",`updateCheckEnabled: ${e.updateCheckEnabled}`,`updateCheckIntervalMs: ${e.updateCheckIntervalMs}`,`updateCheckPackageName: ${e.updateCheckPackageName}`,`updateInfoUrl: ${e.updateInfoUrl?"(set)":"(empty)"}`,"",`File: ${P}`].join(`
84
- `)}function Qc(e){let t=oe(e);return["<b>Config</b> (secrets masked)","",`<b>gatewayMode</b> ${pe(e.gatewayMode)}`,`<b>commandPrefix</b> ${pe(e.commandPrefix)}`,`<b>shell</b> ${pe(e.shell)}`,`<b>syncTimeoutMs</b> ${e.syncTimeoutMs}`,`<b>syncMaxBytes</b> ${e.syncMaxBytes}`,`<b>jobLogTailLines</b> ${e.jobLogTailLines}`,"","<b>Cluster</b>",`clusterEnabled: ${e.clusterEnabled} \xB7 clusterRole: ${pe(e.clusterRole)}`,`clusterLabel: ${pe(e.clusterLabel||"(hostname)")}`,`clusterSenderBindings: ${Object.keys(e.clusterSenderBindings??{}).length} entries`,"","<b>Telegram</b>",`telegramBotToken: ${pe($o(t))}`,`telegramAllowFrom: ${e.telegramAllowFrom.length} entries`,"",`<b>WhatsApp allowFrom</b> ${e.allowFrom.length} entries`,"","<b>Apps</b>",`${e.appsCols}\xD7${e.appsRows} flush ${e.appsFlushMs}/${e.appsMinIntervalMs} ms \u2026`,"","<b>Files</b>",`${pe(e.fileReceiveRootMode)} \xB7 inbox ${pe(e.fileInboxSubdir)}`,"","<b>Service (chat)</b>",`serviceInstallFromChat: ${e.serviceInstallFromChat}`,"","<b>Updates (optional)</b>",`updateCheckEnabled: ${e.updateCheckEnabled} \xB7 interval ms: ${e.updateCheckIntervalMs}`,`package: <code>${pe(e.updateCheckPackageName)}</code> \xB7 info URL: ${e.updateInfoUrl?"set":"empty"}`,"",`<code>${pe(P)}</code>`].join(`
85
- `)}function ra(e,t){if(!na(t))return null;let n=t;if(n==="telegramBotToken")return`telegramBotToken: ${$o(oe(e))}`;let r=e[n];return`${t}: ${typeof r=="object"?JSON.stringify(r):String(r)}`}function Yc(e,t){let n=ra(e,t);if(!n)return null;let r=n.split(": ");return r.length<2?pe(n):`<b>${pe(r[0])}</b> ${pe(r.slice(1).join(": "))}`}function er(e,t){let n=ea(t),r=!1,o=!1,s=!1;if(e==="telegramBotToken"){if(!Ze(n))throw new Error("Invalid bot token format (expect digits:secret from BotFather).");return vt(n),r=!0,s=!0,{reloadSuggested:r,shellWarning:o,tokenSaved:s}}let i=R();switch(e){case"gatewayMode":{let a=On(n);if(!a)throw new Error("gatewayMode: use whatsapp | telegram | both (or wa, tg, b)");i.gatewayMode=a,r=!0;break}case"clusterEnabled":{let a=an(n);if(a===null)throw new Error("clusterEnabled: true or false");i.clusterEnabled=a;break}case"clusterRole":{let a=n.trim().toLowerCase();if(a!=="primary"&&a!=="secondary")throw new Error("clusterRole: primary | secondary");i.clusterRole=a;break}case"clusterLabel":i.clusterLabel=n.trim().slice(0,128);break;case"clusterSenderBindings":{let a=n.trim();if(a===""||a==="{}"||a.toLowerCase()==="clear"){i.clusterSenderBindings={};break}let l;try{l=JSON.parse(a)}catch{throw new Error('clusterSenderBindings: pass a JSON object, e.g. {"wa:+15551234567":"workshop-box"}')}if(!l||typeof l!="object"||Array.isArray(l))throw new Error("clusterSenderBindings must be a JSON object of sender \u2192 label/id");let u={};for(let[c,d]of Object.entries(l)){if(typeof d!="string"||!d.trim())throw new Error(`clusterSenderBindings.${c}: value must be a non-empty string`);u[c]=d.trim()}i.clusterSenderBindings=u;break}case"commandPrefix":if(!n.trim())throw new Error("commandPrefix cannot be empty");i.commandPrefix=n.trim().slice(0,32);break;case"syncTimeoutMs":{let a=Number.parseInt(n,10);if(!Number.isFinite(a)||a<=0)throw new Error("syncTimeoutMs: positive integer (ms)");i.syncTimeoutMs=a;break}case"syncMaxBytes":{let a=Number.parseInt(n,10);if(!Number.isFinite(a)||a<=0)throw new Error("syncMaxBytes: positive integer");i.syncMaxBytes=a;break}case"jobLogTailLines":{let a=Number.parseInt(n,10);if(!Number.isFinite(a)||a<=0)throw new Error("jobLogTailLines: positive integer");i.jobLogTailLines=a;break}case"shell":if(!n.trim())throw new Error("shell: non-empty path");i.shell=n.trim().slice(0,4096),o=!0;break;case"recipesMacroDefaultCommand":if(!n.trim())throw new Error('recipesMacroDefaultCommand: non-empty shell snippet with "$OMNISH_TASK"');i.recipesMacroDefaultCommand=ea(n).trim().slice(0,4096);break;case"appsCols":case"appsRows":case"appsFlushMs":case"appsMinIntervalMs":case"appsMaxFlushBytes":case"appsMaxSessions":case"appsMaxSessionsTotal":case"appsMaxWaChars":case"appsLogTailLines":case"appsSubmitDelayMs":case"appsClearInputDelayMs":case"recipesMaxTaskChars":case"fileSendMaxBytes":case"fileReceiveMaxBytes":{let a=Number.parseInt(n,10);if(!Number.isFinite(a)||a<0)throw new Error(`${e}: non-negative integer`);i[e]=a;break}case"appsClearInput":{let a=an(n);if(a===null)throw new Error("appsClearInput: true or false");i.appsClearInput=a;break}case"appsClearInputSequence":i.appsClearInputSequence=n.trim().slice(0,200);break;case"fileInboxSubdir":i.fileInboxSubdir=n.trim().slice(0,80);break;case"fileReceiveRootMode":{let a=n.trim();if(!ta.has(a))throw new Error(`fileReceiveRootMode: ${[...ta].join(" | ")}`);i.fileReceiveRootMode=a;break}case"fileReceiveRootPath":i.fileReceiveRootPath=n.trim().slice(0,4096);break;case"recipesAllowDangerousBuiltins":{let a=an(n);if(a===null)throw new Error("recipesAllowDangerousBuiltins: true or false");i.recipesAllowDangerousBuiltins=a;break}case"serviceInstallFromChat":{let a=an(n);if(a===null)throw new Error("serviceInstallFromChat: true or false");i.serviceInstallFromChat=a;break}case"updateCheckEnabled":{let a=an(n);if(a===null)throw new Error("updateCheckEnabled: true or false");i.updateCheckEnabled=a;break}case"updateCheckIntervalMs":{let a=Number.parseInt(n,10);if(!Number.isFinite(a)||a<36e5)throw new Error("updateCheckIntervalMs: integer ms, minimum 3600000 (1 hour)");i.updateCheckIntervalMs=Math.min(6048e5,a);break}case"updateCheckPackageName":if(!n.trim())throw new Error("updateCheckPackageName: non-empty npm package name");i.updateCheckPackageName=n.trim().slice(0,214);break;case"updateInfoUrl":i.updateInfoUrl=n.trim().slice(0,2048);break}return He(i),{reloadSuggested:r,shellWarning:o,tokenSaved:s}}async function oa(e,t){let n=e.trim(),r=n.toLowerCase();if(!n||r==="help")return j(Fs());if(r==="keys"||r==="help keys")return p(["*Configurable keys* (/config set)","",Ot.join(", "),"","Examples:","/config set clusterLabel work-laptop",'/config set fileReceiveRootPath "/path/with spaces"',"/config set gatewayMode both"].join(`
86
- `));if(r==="show"){let i=R();return Y(qc(i),Qc(i))}let o=n.match(/^get\s+(\S+)\s*$/i);if(o){let i=o[1],a=R(),l=ra(a,i);if(!l)return p(`Unknown key "${i}". /config keys`);let u=Yc(a,i);return Y(`${l}
87
-
88
- ${P}`,`${u}<br/><br/><code>${pe(P)}</code>`)}let s=n.match(/^set\s+(\S+)\s+([\s\S]+)$/i);if(s){let i=s[1],a=s[2]??"";if(!na(i))return p(`Unknown key "${i}". /config keys`);try{let{reloadSuggested:l,shellWarning:u,tokenSaved:c}=er(i,a),d="";if(t?.reload&&l){let h=await t.reload();d=h.ok?`
89
- Reload: ${h.summary}`:`
90
- Reload failed: ${h.error}`}else l?d=`
91
- Send /reload while omnish run is active (gatewayMode / Telegram).`:d=`
92
- Send /reload to pick up changes where applicable.`;let m=u?`
93
- \u26A0 shell changed \u2014 affects all commands run via omnish.`:"",y=c?`
94
- telegramBotToken saved (not echoed).`:"",g=`Set *${i}* (saved).${y}${m}${d}`,f=`<b>Set ${pe(i)}</b> (saved).${y?"<br/>token saved (not echoed).":""}${u?"<br/>\u26A0 shell path changed.":""}${pe(d)}`;return Y(g,f)}catch(l){return p(`Error: ${String(l)}`)}}return p("Unknown /config command. Try /config help or /config show")}import aa from"node:fs";import Ft from"node:process";function tr(e){let t=e.nodePath,n=e.scriptPath,r=e.omnishHome,o=["Linux (systemd --user), copy-paste on the host:","","mkdir -p ~/.config/systemd/user","# create ~/.config/systemd/user/omnish.service with ExecStart using:",`# ${t} ${n} run`,`# and Environment=OMNISH_HOME=${r}`,"# The generated unit uses Restart=on-failure and RestartSec=5 (tunable).","","systemctl --user daemon-reload","systemctl --user enable --now omnish.service","",'Optional (run without interactive login): loginctl enable-linger "$USER"',"","Full guide: docs/guides/background-and-boot.md \xB7 https://omnish.dev"].join(`
69
+ `)}function fs(e,t,n){let r=ze(De()),o=["<b>Computers</b>",""],s=n?ct(t,n):null;n&&(s?o.push(`Your binding: <code>${ue(s.nodeId)}</code> (${ue(s.source)})`):o.push("Your binding: (none \u2014 send /c use &lt;label-or-id&gt;)"),o.push(""));let i=new Map;i.set(r,Un(t));for(let l of e.peers)i.set(l.nodeId,l);let a=[...i.values()].sort((l,c)=>l.label.localeCompare(c.label));if(a.length===0)return o.push("(no peers observed yet \u2014 run /c status from this chat)"),o.join(`
70
+ `);for(let l of a){let c=l.nodeId===r,d=[(s?l.nodeId===s.nodeId:!1)?"your binding":null,c?"you":null].filter(Boolean).join(", "),p=d?` (${ue(d)})`:"";o.push(`\u2022 <b>${ue(l.label)}</b>${p}<br/> id <code>${ue(l.nodeId)}</code> \xB7 role ${ue(l.role)}<br/> seen ${ue(l.lastSeenIso)}`)}return o.push(""),o.push(`Updated: ${ue(e.updatedAt)}`),o.join(`
71
+ `)}function ks(e,t,n){return Mr(e,t,n??null).replace(/\*([^*]+)\*/g,"$1").replace(/`([^`]+)`/g,"$1")}function bs(e,t){let n=De(),r=ze(n),o=ce(),s=e.clusterRole,i=nl(e),a=t?ct(e,t):null,l=a?a.nodeId===r:!1,c=t?a?`your binding: ${a.nodeId} (${a.source}${l?", here":""})`:"your binding: (none)":"",u=["*This computer*","",`label: ${i}`,`node id: \`${r}\` (full id in ${hs()})`,`host: ${gs.hostname()}`,`clusterRole: ${s}`,c,`peers known: ${o.peers.length}`,`senderBindings (chat): ${Object.keys(o.senderBindings).length}`].filter(Boolean).join(`
72
+ `),d=["<b>This computer</b>","",`label: ${ue(i)}`,`node id: <code>${ue(r)}</code> (full id in ${ue(hs())})`,`host: ${ue(gs.hostname())}`,`clusterRole: ${ue(s)}`,t?ue(c):"",`peers known: ${o.peers.length}`,`senderBindings (chat): ${Object.keys(o.senderBindings).length}`].filter(Boolean).join(`
73
+ `);return{wa:u,tg:d}}function qd(e){let t=e.clusterRole,n=["*Computers* (per-sender bindings)","","Each allowlisted phone picks one machine to talk to. Other senders are not affected.","Selection persists; defaults can be set in config (clusterSenderBindings).","","Shorthand: /pcs \u2026 \xB7 /c \u2026","",`${Me}/c use <label-or-id> \u2014 bind your messages to that machine`,`${Me}/c here \u2014 bind your messages to THIS machine`,`${Me}/c using \u2014 show your current binding`,`${Me}/c unuse \u2014 clear your chat-set binding (config default still applies, if any)`,`${Me}/c status \u2014 every online host replies with its own paragraph`,`${Me}/c list \u2014 locally known roster (single responder)`,`${Me}/c help \u2014 this help`,"",`clusterRole on this host: ${t}`].join(`
74
+ `),r=["<b>Computers</b> (per-sender bindings)","","Each allowlisted phone picks one machine to talk to. Other senders are not affected.","Selection persists; defaults can be set in config (clusterSenderBindings).","","Shorthand: /pcs \u2026 \xB7 /c \u2026","","\u2022 /c use &lt;label-or-id&gt; \u2014 bind your messages to that machine","\u2022 /c here \u2014 bind your messages to THIS machine","\u2022 /c using \u2014 show your current binding","\u2022 /c unuse \u2014 clear your chat-set binding","\u2022 /c status \u2014 every online host replies with its own paragraph","\u2022 /c list \u2014 locally known roster (single responder)","\u2022 /c help \u2014 this help","",`clusterRole on this host: ${ue(t)}`].join(`
75
+ `);return ie(n,r)}function Yd(e,t){if(t.ok)return m("");if(t.reason==="ambiguous-label"){let n=(t.matches??[]).map(r=>`\`${r.nodeId}\` (${r.label})`).join(", ");return m(`Label "${e}" matches multiple machines: ${n}. Use the 8-character id with /c use <id>.`)}return m(`No machine matches "${e}". Send /c list to see ids and labels, or /c status to refresh the roster.`)}function al(e,t,n){let r=t.trim().split(/\s+/),o=r[0]?.toLowerCase()??"";if(!o||o==="help"){if(!e.clusterEnabled&&o!=="help"){let l=ce();return Oe(l,e)?m("Cluster is disabled. Enable with: /config set clusterEnabled true (then /c use <label-or-id>). /c help for the new commands."):null}let a=ce();return Oe(a,e)?qd(e):null}let s=ze(De());if(o==="use"||o==="bind"){let a=r.slice(1).join(" ").trim();if(!n){let w=ce();return Oe(w,e)?m("/c use is only available on chats with a known sender (allowlisted WhatsApp/Telegram)."):null}if(!a){let w=ce();return Oe(w,e)?m("Usage: /c use <label-or-id>"):null}e.clusterEnabled||yr(!0);let c=ce().senderBindings[n]??null,{state:u,resolved:d}=ys(n,a,"chat");if(!d.ok)return Oe(u,e)?Yd(a,d):null;let p=d.peer.nodeId===s,f=c?c.nodeId===s:!1;if(!p)return null;let y=[`*Bound to ${d.peer.label}*`,`id \`${d.peer.nodeId}\``,"","Your messages now route to this machine. Other senders are not affected.","",Mr(u,e,n)].join(`
76
+ `),h=[`<b>Bound to ${ue(d.peer.label)}</b>`,`id <code>${ue(d.peer.nodeId)}</code>`,"","Your messages now route to this machine. Other senders are not affected.","",fs(u,e,n)].join(`
77
+ `);return ie(y,h)}if(o==="here"||o==="take"){if(!n){let d=ce();return Oe(d,e)?m("/c here is only available on chats with a known sender (allowlisted WhatsApp/Telegram)."):null}e.clusterEnabled||yr(!0);let{state:a,resolved:l}=ys(n,s,"chat");if(!l.ok)return Oe(a,e)?m("Could not bind to this machine."):null;let c=["*Bound to this machine.*","","Your messages now route here. Other senders are not affected.","",Mr(a,e,n)].join(`
78
+ `),u=["<b>Bound to this machine.</b>","","Your messages now route here. Other senders are not affected.","",fs(a,e,n)].join(`
79
+ `);return ie(c,u)}if(o==="using"){if(!n){let c=ce();return Oe(c,e)?m("/c using is only available on chats with a known sender (allowlisted WhatsApp/Telegram)."):null}let a=ct(e,n);if(a){if(a.nodeId!==s)return null;let d=[...ce().peers,Un(e)].find(y=>y.nodeId===a.nodeId)?.label??"(unknown label)",p=[`*Bound to ${d}*`,`id \`${a.nodeId}\` \xB7 source ${a.source}`,a.source==="chat"?`since ${a.sinceIso}`:"(from config)"].join(`
80
+ `),f=[`<b>Bound to ${ue(d)}</b>`,`id <code>${ue(a.nodeId)}</code> \xB7 source ${ue(a.source)}`,a.source==="chat"?`since ${ue(a.sinceIso)}`:"(from config)"].join(`
81
+ `);return ie(p,f)}let l=ce();return Oe(l,e)?m("You are not bound to any machine. Send /c use <label-or-id> to bind, or set a default in config.json (clusterSenderBindings)."):null}if(o==="unuse"||o==="unbind"||o==="release"){if(!n){let p=ce();return Oe(p,e)?m("/c unuse is only available on chats with a known sender (allowlisted WhatsApp/Telegram)."):null}let l=ce().senderBindings[n]??null,{state:c}=zd(n);if(l){if(l.nodeId!==s)return null}else if(!Oe(c,e))return null;let u=ct(e,n),d=u?`
82
+ Config default still applies: \`${u.nodeId}\`.`:`
83
+ No config default is set; nobody will answer until you /c use <label-or-id>.`;return m(`Cleared your chat binding.${d}`)}if(o==="step-down"||o==="stepdown"){let a=ce();return Oe(a,e)?m("/c step-down is no longer used. The cluster is per-sender: send /c unuse to clear your binding."):null}if(o==="status"){let a=bs(e,n);return ie(a.wa,a.tg)}if(o==="list"){let a=ce(),l=n?ct(e,n):null;if(l){if(l.nodeId!==s)return null}else if(!Oe(a,e))return null;return ie(Mr(a,e,n??null),fs(a,e,n??null))}let i=ce();return Oe(i,e)?m(`Unknown /c subcommand "${o}". Try /c help`):null}function ll(e,t){return yr(!0),ys(e,t,"chat")}Ue();Se();function Xd(e){return`wa:${e}`}function Ss(e){return`tg:${e}`}function ul(e){return{peerKey:Xd(e.fromJid),text:e.text,waMessageId:e.messageId,mediaSavedPath:e.mediaSavedPath,mediaError:e.mediaError}}j();function Tr(e){let t=e.nodePath,n=e.scriptPath,r=e.omnishHome,o=["Linux (systemd --user), copy-paste on the host:","","mkdir -p ~/.config/systemd/user","# create ~/.config/systemd/user/omnish.service with ExecStart using:",`# ${t} ${n} run`,`# and Environment=OMNISH_HOME=${r}`,"# The generated unit uses Restart=on-failure and RestartSec=5 (tunable).","","systemctl --user daemon-reload","systemctl --user enable --now omnish.service","",'Optional (run without interactive login): loginctl enable-linger "$USER"',"","Full guide: docs/guides/background-and-boot.md \xB7 https://omnish.dev"].join(`
95
84
  `),s=["macOS (LaunchAgent), copy-paste on the host:","","# Use contrib/dev.omnish.gateway.plist from the repo with paths filled as:",`# Node: ${t}`,`# Script: ${n}`,`# OMNISH_HOME: ${r}`,"# Generated plist uses KeepAlive for crash restart.","","cp \u2026/dev.omnish.gateway.plist ~/Library/LaunchAgents/","launchctl bootstrap gui/$(id -u) ~/Library/LaunchAgents/dev.omnish.gateway.plist","launchctl kickstart -k gui/$(id -u)/dev.omnish.gateway","","https://omnish.dev"].join(`
96
85
  `),i=["Windows: Task Scheduler \u2014 Action = Start a program:","",`Program: ${t}`,`Arguments: "${n}" run`,"","Set user env OMNISH_HOME if needed. Optional XML: contrib/omnish-windows-task.xml","","https://omnish.dev"].join(`
97
86
  `);return process.platform==="linux"?o:process.platform==="darwin"?s:process.platform==="win32"?i:[o,"","---","",s].join(`
98
- `)}import{execFileSync as st}from"node:child_process";import Pt from"node:fs";import cn from"node:os";import Pe from"node:path";import he from"node:process";function yt(){let e=he.execPath,t=he.argv[1];if(!t||!t.trim())return{nodePath:e,scriptPath:"",omnishHome:N,error:"Cannot resolve gateway entry script (missing argv[1]). Run omnish from its CLI entry."};let n=Pe.resolve(t),r=he.env.OMNISH_HOME?.trim(),o=r?Pe.resolve(r):N;return{nodePath:e,scriptPath:n,omnishHome:o}}function sa(e){return/[ "'\\\s]/.test(e)?`"${e.replace(/\\/g,"\\\\").replace(/"/g,'\\"')}"`:e}function Vc(e){return`Environment="OMNISH_HOME=${e.replace(/\\/g,"\\\\").replace(/"/g,'\\"')}"`}function Kc(e){return`[Unit]
87
+ `)}j();import{execFileSync as xt}from"node:child_process";import on from"node:fs";import Hn from"node:os";import Ke from"node:path";import Te from"node:process";function Ut(){let e=Te.execPath,t=Te.argv[1];if(!t||!t.trim())return{nodePath:e,scriptPath:"",omnishHome:U,error:"Cannot resolve gateway entry script (missing argv[1]). Run omnish from its CLI entry."};let n=Ke.resolve(t),r=Te.env.OMNISH_HOME?.trim(),o=r?Ke.resolve(r):U;return{nodePath:e,scriptPath:n,omnishHome:o}}function dl(e){return/[ "'\\\s]/.test(e)?`"${e.replace(/\\/g,"\\\\").replace(/"/g,'\\"')}"`:e}function Zd(e){return`Environment="OMNISH_HOME=${e.replace(/\\/g,"\\\\").replace(/"/g,'\\"')}"`}function ep(e){return`[Unit]
99
88
  Description=omnish gateway (WhatsApp/Telegram)
100
89
  After=network-online.target
101
90
  Wants=network-online.target
102
91
 
103
92
  [Service]
104
93
  Type=simple
105
- ExecStart=${`${sa(e.nodePath)} ${sa(e.scriptPath)} run`}
106
- ${Vc(e.omnishHome)}
94
+ ExecStart=${`${dl(e.nodePath)} ${dl(e.scriptPath)} run`}
95
+ ${Zd(e.omnishHome)}
107
96
  Restart=on-failure
108
97
  RestartSec=5
109
98
 
110
99
  [Install]
111
100
  WantedBy=default.target
112
- `}function ln(e){return e.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;")}function Xc(e){let t=cn.homedir(),n=Pe.join(e.omnishHome,"logs","launchd-stdout.log"),r=Pe.join(e.omnishHome,"logs","launchd-stderr.log");H(Pe.dirname(n));let o=ln(e.nodePath),s=ln(e.scriptPath),i=ln(e.omnishHome),a=ln(n),l=ln(r);return`<?xml version="1.0" encoding="UTF-8"?>
101
+ `}function Dn(e){return e.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;")}function tp(e){let t=Hn.homedir(),n=Ke.join(e.omnishHome,"logs","launchd-stdout.log"),r=Ke.join(e.omnishHome,"logs","launchd-stderr.log");W(Ke.dirname(n));let o=Dn(e.nodePath),s=Dn(e.scriptPath),i=Dn(e.omnishHome),a=Dn(n),l=Dn(r);return`<?xml version="1.0" encoding="UTF-8"?>
113
102
  <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
114
103
  <plist version="1.0">
115
104
  <dict>
@@ -136,20 +125,34 @@ WantedBy=default.target
136
125
  <string>${l}</string>
137
126
  </dict>
138
127
  </plist>
139
- `}function nr(){let e=yt();if(e.error)return{ok:!1,detail:e.error};if(he.platform==="win32")return{ok:!1,detail:"Automatic install is not supported on Windows from chat. Use Task Scheduler (see /service instructions) or omnish-windows-task.xml in the repo contrib folder."};if(he.platform==="darwin")try{let t=Pe.join(cn.homedir(),"Library/LaunchAgents/dev.omnish.gateway.plist");H(Pe.dirname(t));let n=Xc(e);Pt.writeFileSync(t,n,{mode:384});let r=typeof he.getuid=="function"?he.getuid():null;if(r===null||r<0)return{ok:!1,detail:"Could not read user id for launchctl."};let o=`gui/${r}`;try{st("launchctl",["bootout",o,t],{stdio:"pipe"})}catch{}return st("launchctl",["bootstrap",o,t],{stdio:"inherit"}),st("launchctl",["kickstart","-k",`${o}/dev.omnish.gateway`],{stdio:"inherit"}),{ok:!0,detail:"LaunchAgent installed: ~/Library/LaunchAgents/dev.omnish.gateway.plist (label dev.omnish.gateway)."}}catch(t){return{ok:!1,detail:String(t)}}if(he.platform==="linux")try{let t=Pe.join(cn.homedir(),".config","systemd","user");H(t);let n=Pe.join(t,"omnish.service"),r=Kc(e);return Pt.writeFileSync(n,r,{mode:420}),st("systemctl",["--user","daemon-reload"],{stdio:"inherit"}),st("systemctl",["--user","enable","--now","omnish.service"],{stdio:"inherit"}),{ok:!0,detail:`User systemd unit installed: ${n} \u2014 enabled and started.`}}catch(t){return{ok:!1,detail:String(t)}}return{ok:!1,detail:`Unsupported platform: ${he.platform}`}}function rr(){if(he.platform==="win32")return{ok:!0,detail:"Windows: remove the omnish task from Task Scheduler manually on this PC. See /service instructions."};if(he.platform==="darwin")try{let e=Pe.join(cn.homedir(),"Library/LaunchAgents/dev.omnish.gateway.plist"),n=`gui/${typeof he.getuid=="function"?he.getuid():0}`;if(Pt.existsSync(e)){try{st("launchctl",["bootout",n,e],{stdio:"pipe"})}catch{}Pt.unlinkSync(e)}return{ok:!0,detail:"LaunchAgent dev.omnish.gateway removed (if it was present)."}}catch(e){return{ok:!1,detail:String(e)}}if(he.platform==="linux")try{let e=Pe.join(cn.homedir(),".config","systemd","user","omnish.service");try{st("systemctl",["--user","disable","--now","omnish.service"],{stdio:"pipe"})}catch{}Pt.existsSync(e)&&Pt.unlinkSync(e);try{st("systemctl",["--user","daemon-reload"],{stdio:"pipe"})}catch{}return{ok:!0,detail:"User systemd unit omnish.service removed (if it was present)."}}catch(e){return{ok:!1,detail:String(e)}}return{ok:!1,detail:`Unsupported platform: ${he.platform}`}}import ia from"node:fs";var or=48e3;function sr(e,t){try{if(!ia.existsSync(e))return"(no log file yet \u2014 start with `omnish run -d` or the systemd/LaunchAgent service.)";let n=ia.readFileSync(e),o=(n.length>or?n.subarray(n.length-or):n).toString("utf8");return n.length>or&&(o=`\u2026(truncated to last ${or} bytes)
128
+ `}function Er(){let e=Ut();if(e.error)return{ok:!1,detail:e.error};if(Te.platform==="win32")return{ok:!1,detail:"Automatic install is not supported on Windows from chat. Use Task Scheduler (see /service instructions) or omnish-windows-task.xml in the repo contrib folder."};if(Te.platform==="darwin")try{let t=Ke.join(Hn.homedir(),"Library/LaunchAgents/dev.omnish.gateway.plist");W(Ke.dirname(t));let n=tp(e);on.writeFileSync(t,n,{mode:384});let r=typeof Te.getuid=="function"?Te.getuid():null;if(r===null||r<0)return{ok:!1,detail:"Could not read user id for launchctl."};let o=`gui/${r}`;try{xt("launchctl",["bootout",o,t],{stdio:"pipe"})}catch{}return xt("launchctl",["bootstrap",o,t],{stdio:"inherit"}),xt("launchctl",["kickstart","-k",`${o}/dev.omnish.gateway`],{stdio:"inherit"}),{ok:!0,detail:"LaunchAgent installed: ~/Library/LaunchAgents/dev.omnish.gateway.plist (label dev.omnish.gateway)."}}catch(t){return{ok:!1,detail:String(t)}}if(Te.platform==="linux")try{let t=Ke.join(Hn.homedir(),".config","systemd","user");W(t);let n=Ke.join(t,"omnish.service"),r=ep(e);return on.writeFileSync(n,r,{mode:420}),xt("systemctl",["--user","daemon-reload"],{stdio:"inherit"}),xt("systemctl",["--user","enable","--now","omnish.service"],{stdio:"inherit"}),{ok:!0,detail:`User systemd unit installed: ${n} \u2014 enabled and started.`}}catch(t){return{ok:!1,detail:String(t)}}return{ok:!1,detail:`Unsupported platform: ${Te.platform}`}}function Ir(){if(Te.platform==="win32")return{ok:!0,detail:"Windows: remove the omnish task from Task Scheduler manually on this PC. See /service instructions."};if(Te.platform==="darwin")try{let e=Ke.join(Hn.homedir(),"Library/LaunchAgents/dev.omnish.gateway.plist"),n=`gui/${typeof Te.getuid=="function"?Te.getuid():0}`;if(on.existsSync(e)){try{xt("launchctl",["bootout",n,e],{stdio:"pipe"})}catch{}on.unlinkSync(e)}return{ok:!0,detail:"LaunchAgent dev.omnish.gateway removed (if it was present)."}}catch(e){return{ok:!1,detail:String(e)}}if(Te.platform==="linux")try{let e=Ke.join(Hn.homedir(),".config","systemd","user","omnish.service");try{xt("systemctl",["--user","disable","--now","omnish.service"],{stdio:"pipe"})}catch{}on.existsSync(e)&&on.unlinkSync(e);try{xt("systemctl",["--user","daemon-reload"],{stdio:"pipe"})}catch{}return{ok:!0,detail:"User systemd unit omnish.service removed (if it was present)."}}catch(e){return{ok:!1,detail:String(e)}}return{ok:!1,detail:`Unsupported platform: ${Te.platform}`}}import pl from"node:fs";var Ar=48e3;function Pr(e,t){try{if(!pl.existsSync(e))return"(no log file yet \u2014 start with `omnish run -d` or the systemd/LaunchAgent service.)";let n=pl.readFileSync(e),o=(n.length>Ar?n.subarray(n.length-Ar):n).toString("utf8");return n.length>Ar&&(o=`\u2026(truncated to last ${Ar} bytes)
140
129
  ${o}`),o.split(/\r?\n/).slice(-t).join(`
141
- `).trimEnd()||"(empty)"}catch(n){return`Could not read log: ${String(n)}`}}var Zc=120;function Ve(e){return e.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;")}function la(e){let t=e.trim();return t==="/service"||t.startsWith("/service ")?t.slice(8).trim():null}async function ca(e,t){let n=t.trim().split(/\s+/),r=(n[0]??"").toLowerCase();if(!t.trim()||r==="help")return j(Ps(e));if(r==="status"){let s=yt(),i=(()=>{try{return aa.existsSync(ne)?`gateway.pid: ${aa.readFileSync(ne,"utf8").trim()}`:"gateway.pid: (missing)"}catch(m){return`gateway.pid: (read error: ${String(m)})`}})(),a=Ft.env.OMNISH_BACKGROUND_GATEWAY==="1"?"This process: background gateway (OMNISH_BACKGROUND_GATEWAY=1).":"This process: foreground gateway session.",l=typeof Ft.env.OMNISH_HOME=="string"&&Ft.env.OMNISH_HOME.trim()?`OMNISH_HOME env: ${Ft.env.OMNISH_HOME.trim()}`:"OMNISH_HOME env: (not set \u2014 using default data dir)",u=s.error?s.error:`Node: ${s.nodePath}
142
- Script: ${s.scriptPath}`,c=["*Service status*","",`platform: ${Ft.platform}`,a,l,`data dir: ${N}`,i,`default log: ${fe}`,"",u,"",e.serviceInstallFromChat?"Install from chat: enabled (/service install).":"Install from chat: off \u2014 `/config set serviceInstallFromChat true` to allow /service install."].join(`
143
- `),d=["<b>Service status</b>","",`<code>${Ve(Ft.platform)}</code>`,`<br/><code>${Ve(a)}</code>`,`<br/><code>${Ve(l)}</code>`,`<br/>data dir: <code>${Ve(N)}</code>`,`<br/><code>${Ve(i)}</code>`,`<br/>default log: <code>${Ve(fe)}</code>`,"",`<pre>${Ve(u)}</pre>`,"",e.serviceInstallFromChat?"Install from chat: enabled.":"Install from chat: off \u2014 <code>/config set serviceInstallFromChat true</code>."].join(`
144
- `);return Y(c,d)}if(r==="instructions"){let s=yt();if(s.error)return p(s.error);let i=tr(s);return p(`*Install hints*
145
-
146
- ${i}`)}if(r==="logs"){let s=n.length>=2?Number.parseInt(n[1],10):80,i=Number.isFinite(s)&&s>0?Math.min(s,Zc):80,a=sr(fe,i),l=[`*Gateway log* (last ${i} lines)
147
- ${fe}
148
- `,"```",a,"```"].join(`
149
- `),u=`<b>Gateway log</b> (last ${i} lines)<br/><code>${Ve(fe)}</code><pre>${Ve(a)}</pre>`;return Y(l,u)}if(r==="install"){if(!e.serviceInstallFromChat)return p("Install from chat is disabled. Same trust as shell \u2014 enable with:\n`/config set serviceInstallFromChat true`\nThen `/service install` again.");let s=nr();return p(s.ok?`*Installed*
150
- ${s.detail}`:`*Install failed*
151
- ${s.detail}`)}if(r==="uninstall"){if(!e.serviceInstallFromChat)return p("Uninstall from chat is disabled. Enable with `/config set serviceInstallFromChat true` or remove files on the host manually.");let s=rr();return p(`*Uninstall*
152
- ${s.detail}`)}return p("Unknown /service command. Try /service help")}function ua(e){let t=e.trim().match(/^(\d+)\.(\d+)\.(\d+)/);return t?[Number(t[1]),Number(t[2]),Number(t[3])]:null}function da(e,t){let n=ua(e),r=ua(t);if(!n||!r)return e.trim().localeCompare(t.trim());for(let o=0;o<3;o++)if(n[o]!==r[o])return n[o]<r[o]?-1:1;return 0}var eu="https://registry.npmjs.org",pa=null,Co=0;function un(){return pa}function tu(e){let t=e.trim();return t.length<1||t.length>214?!1:!/\s/.test(t)}function ma(e){let t=e.trim();if(!t||t.length>2048)return null;try{let n=new URL(t);return n.protocol!=="https:"?null:n.href}catch{return null}}async function nu(e){let t=e.trim(),n=`${eu}/${encodeURIComponent(t)}/latest`;try{let r=await fetch(n,{headers:{Accept:"application/json"},signal:AbortSignal.timeout(2e4)});if(!r.ok)return{error:`npm registry: HTTP ${r.status}`};let o=await r.json(),s=typeof o.version=="string"?o.version.trim():"";return s?{version:s}:{error:"npm registry: missing version in response"}}catch(r){return{error:`npm registry: ${String(r)}`}}}async function ru(e){try{let t=await fetch(e,{headers:{Accept:"application/json"},signal:AbortSignal.timeout(15e3)});if(!t.ok)return{error:`updateInfoUrl: HTTP ${t.status}`};let n=await t.arrayBuffer(),r=n.byteLength>65536?n.slice(0,65536):n,o=new TextDecoder("utf-8",{fatal:!1}).decode(r),s;try{s=JSON.parse(o)}catch{return{error:"updateInfoUrl: body is not JSON"}}if(!s||typeof s!="object"||Array.isArray(s))return{error:"updateInfoUrl: JSON must be an object"};let i=s,a=null;typeof i.message=="string"&&i.message.trim()&&(a=i.message.trim().slice(0,1500));let l=null,u=i.link??i.url;return typeof u=="string"&&u.trim()&&(l=ma(u)),{message:a,link:l}}catch(t){return{error:`updateInfoUrl: ${String(t)}`}}}function ou(e){return!Number.isFinite(e)||e<36e5?36e5:e>6048e5?6048e5:Math.floor(e)}async function dn(e,t){let n=tu(t.updateCheckPackageName)?t.updateCheckPackageName.trim():"omnish",r=await nu(n),o="version"in r?r.version:null,s="error"in r?r.error:null,i=!1;o&&!s&&(i=da(e,o)<0);let a=null,l=null,u=null,c=ma(t.updateInfoUrl);if(c){let m=await ru(c);"error"in m?u=m.error:(a=m.message,l=m.link)}let d={runningVersion:e,checkedAtIso:new Date().toISOString(),registryPackage:n,registryLatest:o,registryError:s,updateAvailable:i,infoMessage:a,infoLink:l,infoError:u};return pa=d,d}function ir(e){if(!e)return;let t=[];if(e.registryError?t.push(`npm check: ${e.registryError}`):e.registryLatest&&t.push(e.updateAvailable?`npm latest *${e.registryLatest}* (running ${e.runningVersion})`:`npm latest ${e.registryLatest} (up to date)`),e.infoError?t.push(`info URL: ${e.infoError}`):e.infoMessage&&t.push(`notice: ${e.infoMessage}`),t.length!==0)return t.join(" \xB7 ")}function fa(e){let t=!1,n=async()=>{if(t)return;let s=e.getConfig();if(!s.updateCheckEnabled)return;let i=ou(s.updateCheckIntervalMs),a=Date.now();if(!(Co!==0&&a-Co<i)){Co=a;try{let l=await dn(e.getRunningVersion(),s);l.updateAvailable?e.log.info({updateAvailable:!0,running:l.runningVersion,npmLatest:l.registryLatest,pkg:l.registryPackage},"omnish update check: newer npm version available"):e.log.info({running:l.runningVersion,npmLatest:l.registryLatest,pkg:l.registryPackage},"omnish update check ok"),l.infoMessage&&e.log.info({len:l.infoMessage.length},"omnish update info message present")}catch(l){e.log.warn({err:String(l)},"omnish update check failed")}}},r=setInterval(()=>void n(),6e4),o=setTimeout(()=>void n(),3e4);return()=>{t=!0,clearInterval(r),clearTimeout(o)}}import su from"node:fs";import ga from"node:path";import{fileURLToPath as iu}from"node:url";var ar=null;function Ue(){if(ar!==null)return ar;let e=ga.dirname(iu(import.meta.url)),t=ga.join(e,"..","package.json"),n=su.readFileSync(t,"utf8"),r=JSON.parse(n);return ar=typeof r.version=="string"&&r.version.trim()?r.version.trim():"0.0.0",ar}function pn(e,t,n){let r=new Date(e),o=r.getFullYear(),s=r.getMonth(),i=r.getDate(),a=new Date(o,s,i,t,n,0,0).getTime();return a<=e&&(a=new Date(o,s,i+1,t,n,0,0).getTime()),a}function au(e,t,n){let r=e;for(let o=0;o<14;o++){let s=pn(r,t,n),i=new Date(s).getDay();if(i>=1&&i<=5)return s;r=s}return pn(r,t,n)}function lu(e,t,n,r){let o=e;for(let s=0;s<370;s++){let i=pn(o,n,r);if(new Date(i).getDay()===t)return i;o=i}return pn(o,n,r)}function cu(e,t){let n=new Date(e),r=n.getFullYear(),o=n.getMonth(),s=n.getDate(),i=n.getHours(),a=new Date(r,o,s,i,t,0,0).getTime();return a<=e&&(a=new Date(r,o,s,i+1,t,0,0).getTime()),a}function uu(e,t){return e.kind==="ondemand"?Number.POSITIVE_INFINITY:e.kind==="daily"?pn(t,e.hour,e.minute):e.kind==="weekdays"?au(t,e.hour,e.minute):e.kind==="hourly"?cu(t,e.minute):lu(t,e.weekday,e.hour,e.minute)}function ha(e,t,n,r,o=32){if(e.kind==="ondemand")return[];let s=t??n-1,i=[],a=s;for(;i.length<o;){let l=uu(e,a);if(l>r)break;i.push(l),a=l}return i}var du={sun:0,sunday:0,mon:1,monday:1,tue:2,tues:2,tuesday:2,wed:3,wednesday:3,thu:4,thur:4,thurs:4,thursday:4,fri:5,friday:5,sat:6,saturday:6};function Ro(e){let t=/^(\d{1,2}):(\d{2})$/.exec(e.trim());if(!t)return null;let n=Number(t[1]),r=Number(t[2]);return!Number.isFinite(n)||!Number.isFinite(r)||n>23||r>59?null:{hour:n,minute:r}}function pu(e){let t=e.trim(),n=/^:(\d{1,2})$/.exec(t),r=/^(\d{1,2})$/.exec(t),o=n?n[1]:r?r[1]:null;if(o===null)return null;let s=Number(o);return!Number.isInteger(s)||s<0||s>59?null:s}function Mo(e){if(e.length===0)return{ok:!1,error:"Missing schedule (try: ondemand, hourly [:MM], daily HH:MM, weekdays HH:MM, weekly <dow> HH:MM)."};let t=e[0].toLowerCase();if(t==="ondemand"||t==="manual")return e.length!==1?{ok:!1,error:"ondemand takes no extra tokens."}:{ok:!0,schedule:{kind:"ondemand"}};if(t==="daily"){if(e.length!==2)return{ok:!1,error:"Usage: daily HH:MM"};let n=Ro(e[1]);return n?{ok:!0,schedule:{kind:"daily",hour:n.hour,minute:n.minute}}:{ok:!1,error:"daily needs HH:MM (24h)."}}if(t==="weekdays"){if(e.length!==2)return{ok:!1,error:"Usage: weekdays HH:MM"};let n=Ro(e[1]);return n?{ok:!0,schedule:{kind:"weekdays",hour:n.hour,minute:n.minute}}:{ok:!1,error:"weekdays needs HH:MM (24h)."}}if(t==="hourly"){if(e.length===1)return{ok:!0,schedule:{kind:"hourly",minute:0}};if(e.length!==2)return{ok:!1,error:"Usage: hourly | hourly :MM | hourly MM (minute 0\u201359)."};let n=pu(e[1]);return n===null?{ok:!1,error:"hourly needs :MM or MM (minute 0\u201359), e.g. hourly :30 or hourly 15."}:{ok:!0,schedule:{kind:"hourly",minute:n}}}if(t==="weekly"){if(e.length!==3)return{ok:!1,error:"Usage: weekly <mon|tue|\u2026|sun> HH:MM"};let n=e[1].toLowerCase(),r=du[n];if(r===void 0){let s=Number(n);Number.isInteger(s)&&s>=0&&s<=6&&(r=s)}if(r===void 0)return{ok:!1,error:`Unknown weekday "${e[1]}". Use mon\u2026sun or 0\u20136 (Sun=0).`};let o=Ro(e[2]);return o?{ok:!0,schedule:{kind:"weekly",weekday:r,hour:o.hour,minute:o.minute}}:{ok:!1,error:"weekly needs HH:MM (24h) after weekday."}}return{ok:!1,error:`Unknown schedule kind "${t}".`}}function mn(e){let t=r=>r<10?`0${r}`:String(r),n=(r,o)=>`${t(r)}:${t(o)}`;switch(e.kind){case"ondemand":return"ondemand";case"daily":return`daily ${n(e.hour,e.minute)}`;case"weekdays":return`weekdays ${n(e.hour,e.minute)}`;case"hourly":return e.minute===0?"hourly":`hourly :${t(e.minute)}`;case"weekly":return`weekly ${["sun","mon","tue","wed","thu","fri","sat"][e.weekday]??e.weekday} ${n(e.hour,e.minute)}`}}import gu from"better-sqlite3";import mu from"pino";function lr(){return process.env.OMNISH_VERBOSE==="1"||process.env.WHATSVERBOSE==="1"}var fu=lr()?"info":"silent",E=mu({level:fu,base:{app:"omnish"}});function ya(){return E.child({module:"baileys"})}var wa=1,it=null;function ba(){H(Be);let e=new gu(is);e.pragma("journal_mode = WAL"),e.exec(`
130
+ `).trimEnd()||"(empty)"}catch(n){return`Could not read log: ${String(n)}`}}import $t from"node:fs";import kp from"node:path";j();import np from"node:crypto";import $s from"node:fs";import rp from"node:path";var Cs=/^[a-zA-Z0-9][a-zA-Z0-9_-]{0,31}$/,an="__omnish_recipes_global__",Rs=new Set(["help","list","show","add","set","remove","rm","del","run","r"]),sn=new Map,ml=!1;function op(){try{let e=$s.readFileSync(cr,"utf8"),t=JSON.parse(e);if(t&&typeof t=="object")for(let[n,r]of Object.entries(t)){if(!r||typeof r!="object")continue;let o={};for(let[s,i]of Object.entries(r)){let a=String(s).toLowerCase();i&&typeof i=="object"&&typeof i.command=="string"&&(o[a]=nt(i))}sn.set(n,o)}}catch{}}function sp(e){if(!Array.isArray(e)||e.length===0)return;let t=[];for(let n of e)if(typeof n=="string"&&n.trim())t.push({cmd:n.trim()});else if(n&&typeof n=="object"){let r=n,o=typeof r.cmd=="string"?r.cmd.trim():"";if(!o)continue;t.push({cmd:o,label:typeof r.label=="string"?r.label.trim().slice(0,80):void 0,continueOnFail:typeof r.continueOnFail=="boolean"?r.continueOnFail:void 0})}return t.length>0?t:void 0}function nt(e){let t=typeof e.taskEnv=="string"&&/^[A-Za-z_][A-Za-z0-9_]*$/.test(e.taskEnv)?e.taskEnv:"OMNISH_TASK",n=typeof e.promptTemplate=="string"&&e.promptTemplate.trim().length>0?e.promptTemplate.trim():void 0,r=sp(e.steps),o={command:String(e.command).trim(),taskEnv:t,label:typeof e.label=="string"?e.label.trim().slice(0,80):void 0,description:typeof e.description=="string"?e.description.trim().slice(0,200):void 0,category:typeof e.category=="string"?e.category.trim().slice(0,40):void 0,featured:typeof e.featured=="boolean"?e.featured:void 0,dangerous:typeof e.dangerous=="boolean"?e.dangerous:void 0};return n!==void 0&&(o.promptTemplate=n),r!==void 0&&(o.steps=r),o}function Lr(){W(rp.dirname(cr));let e={};for(let[t,n]of sn)Object.entries(n).length>0&&(e[t]={...n});$s.writeFileSync(cr,JSON.stringify(e,null,2)+`
131
+ `,{mode:384})}function Nr(){ml||(op(),ml=!0)}function fl(e){return Nr(),sn.get(e)??{}}function vs(e){Nr();let t=sn.get(e);return t||(t={},sn.set(e,t)),t}function ip(e,t){let n=e.taskEnv??"OMNISH_TASK",r=t.recipesMacroDefaultCommand.trim();return Dt(r,n).ok?nt({...e,command:r,promptTemplate:e.command}):e}function ap(e,t){if(e.promptTemplate||Dt(e.command,t).ok||!ln(e.command,t))return!1;let n=e.command;return n.includes("```")||n.length>2e3||/^"Create\b/i.test(n)||n.includes("<<<")||n.includes(`
132
+ ### `)}function Ms(e,t){let n=nt(e),r=n.taskEnv??"OMNISH_TASK";return!n.promptTemplate&&ap(n,r)&&(n=ip(n,t)),n}function lp(e){try{let t=$s.readFileSync(lr,"utf8"),n=JSON.parse(t);if(!n||typeof n!="object")return{};let r={};for(let[o,s]of Object.entries(n)){let i=o.trim().toLowerCase();if(!(!Cs.test(i)||Rs.has(i))&&s&&typeof s=="object"&&typeof s.command=="string"){let l=Ms(s,e),c=Bn(l);if(!c.ok){console.warn(`[omnish] recipes.json: skipping "${i}": ${c.error}`);continue}r[i]=l}}return r}catch{return{}}}function cp(e){return{claude:{command:'claude -p "$OMNISH_TASK" --allowedTools all --dangerously-skip-permissions',taskEnv:"OMNISH_TASK",label:"Claude Code",description:"Anthropic Claude Code CLI (requires `claude` on PATH)",category:"agents",featured:!0,dangerous:e.recipesAllowDangerousBuiltins},codex:{command:'codex "$OMNISH_TASK"',taskEnv:"OMNISH_TASK",label:"Codex CLI",description:"OpenAI Codex CLI if installed; override in recipes.json",category:"agents",featured:!1},gemini:{command:'gemini -p "$OMNISH_TASK"',taskEnv:"OMNISH_TASK",label:"Gemini CLI",description:"Google Gemini CLI if installed; override in recipes.json",category:"agents",featured:!1},cursor:{command:"agent --yolo --force -p ```Before writing any code:\n1. Analyze the codebase and state your full implementation plan\n2. List every file you will touch\n3. Identify any risks or ambiguities\n4. Then execute the plan step by step, <task> $OMNISH_TASK </task>```",taskEnv:"OMNISH_TASK",label:"Cursor Agent",description:"Cursor Agent if installed; override in recipes.json",category:"agents",featured:!0}}}function Or(e,t,n){for(let[r,o]of Object.entries(t)){let s=r.toLowerCase();!Cs.test(s)||Rs.has(s)||o.command.trim()&&e.set(s,{...o,name:s,source:n})}}function xs(e,t){let n=fl(e),r={};for(let[o,s]of Object.entries(n)){let i=Ms(s,t);Bn(i).ok&&(r[o]=i)}return r}function gl(e,t){let n=new Map;return Or(n,cp(t),"builtin"),Or(n,lp(t),"global"),Or(n,xs(an,t),"shared"),Or(n,xs(e,t),"peer"),n}function He(e,t,n){let r=n.trim().toLowerCase();return gl(e,t).get(r)}function hl(e){let t=e.trim();if(/^--global$/i.test(t))return{mode:"global",remainder:""};if(/^-g$/i.test(t))return{mode:"global",remainder:""};if(/^--chat$/i.test(t))return{mode:"chat",remainder:""};if(/^-p$/i.test(t))return{mode:"chat",remainder:""};let n=/^--global\s+/i.exec(t);if(n)return{mode:"global",remainder:t.slice(n[0].length).trimStart()};let r=/^-g\s+/i.exec(t);if(r)return{mode:"global",remainder:t.slice(r[0].length).trimStart()};let o=/^--chat\s+/i.exec(t);if(o)return{mode:"chat",remainder:t.slice(o[0].length).trimStart()};let s=/^-p\s+/i.exec(t);return s?{mode:"chat",remainder:t.slice(s[0].length).trimStart()}:{mode:"resolved",remainder:t}}function Ts(e){let t=e.trim();if(/^--global$/i.test(t))return{scope:"global",remainder:"",explicit:!0};if(/^-g$/i.test(t))return{scope:"global",remainder:"",explicit:!0};if(/^--chat$/i.test(t))return{scope:"chat",remainder:"",explicit:!0};if(/^-p$/i.test(t))return{scope:"chat",remainder:"",explicit:!0};let n=/^--global\s+([\s\S]*)$/i.exec(t);if(n?.[1]!==void 0)return{scope:"global",remainder:n[1].trimStart(),explicit:!0};let r=/^-g\s+([\s\S]*)$/i.exec(t);if(r?.[1]!==void 0)return{scope:"global",remainder:r[1].trimStart(),explicit:!0};let o=/^--chat\s+([\s\S]*)$/i.exec(t);if(o?.[1]!==void 0)return{scope:"chat",remainder:o[1].trimStart(),explicit:!0};let s=/^-p\s+([\s\S]*)$/i.exec(t);return s?.[1]!==void 0?{scope:"chat",remainder:s[1].trimStart(),explicit:!0}:{scope:"chat",remainder:t,explicit:!1}}function Es(e){let t=Ts(e);return{scope:t.scope,remainder:t.remainder}}function yl(e){let t=e.trim();return!t||/^-+$/i.test(t)?{filter:"merged"}:/^(?:--global|-g)$/i.test(t)?{filter:"global"}:/^(?:--chat|-p)$/i.test(t)?{filter:"chat"}:{filter:"merged",bad:t}}function up(e){let t=e.trim().toLowerCase();if(t==="--global"||t==="-g")return"global";if(t==="--chat"||t==="-p")return"chat"}function wl(e){let t=e.trim().match(/^(\S+)\s+(--global|-g|--chat|-p)\s*$/i);if(!t?.[1]||!t[2])return;let n=up(t[2]);if(n)return{name:t[1],target:n}}function ut(e,t,n,r){let o=r.trim().toLowerCase(),s=e==="global"?an:t,i=fl(s)[o];if(i===void 0)return;let a=Ms(i,n);if(Bn(a).ok)return{...a,name:o,source:e==="global"?"shared":"peer"}}function kl(e,t,n){if(n==="merged")return[];let r=n==="global"?an:e,o=n==="global"?"shared":"peer",s=xs(r,t);return Object.entries(s).map(([i,a])=>({...a,name:i,source:o})).sort((i,a)=>i.name.localeCompare(a.name))}function Ht(e){let t=e.trim();if(!t)return{ok:!1,error:"Name is empty."};let n=t.toLowerCase();return Cs.test(n)?Rs.has(n)?{ok:!1,error:`Reserved name: ${n}`}:{ok:!0,normalized:n}:{ok:!1,error:`Invalid name (use letters, digits, _ or -; max 32 chars): ${t}`}}function _r(e,t){let n=e.replace(/\r\n/g,`
133
+ `).trim();return n?t>0&&n.length>t?{ok:!1,error:`Task too long (max ${t} characters).`}:{ok:!0,task:n}:{ok:!1,error:"Task is empty."}}function ln(e,t){return e.includes("$")?e.includes(t):!1}function Dt(e,t){let n=e.trim();if(!n)return{ok:!1,error:"Command is empty."};if(!/^[A-Za-z_][A-Za-z0-9_]*$/.test(t))return{ok:!1,error:"Invalid taskEnv (use letters, digits, _)."};if(!ln(n,t))return{ok:!1,error:`Command must reference "$${t}" so the task is passed via the environment.`};let r=`"$${t}"`;return n.includes(r)?n.includes("```")?{ok:!1,error:"Recipe command must not contain markdown code fences (```). Use promptTemplate or split with a --- line (see /run help)."}:{ok:!0}:{ok:!1,error:`Command must include ${r} (quoted).`}}function Bn(e){let t=e.taskEnv??"OMNISH_TASK",n=Dt(e.command,t);return n.ok?e.promptTemplate!==void 0&&e.promptTemplate.trim().length===0?{ok:!1,error:"promptTemplate cannot be empty."}:{ok:!0}:n}function dp(e){let t=e,n=null,r=!1,o=-1,s=-1;for(let l=0;l<t.length;l+=1){let c=t[l];if(r){r=!1;continue}if(c==="\\"){r=!0;continue}if(n){c===n&&(n=null);continue}if(c==='"'||c==="'"){n=c;continue}if(c!=="-"||t[l+1]!=="-")continue;let u=l===0?"":t[l-1];if(u&&!/\s/.test(u))continue;let d=t.slice(l+2),p=/^(template|tamplate)\b/i.exec(d);if(!p)continue;let f=l+2+p[0].length,y=t[f]??"";if(!y||!/\s/.test(y))continue;o=l;let h=f;for(;h<t.length&&/\s/.test(t[h]);)h+=1;s=h;break}if(o<0||s<0)return{command:t.trim()};let i=t.slice(0,o).trim(),a=t.slice(s).trim();return(a.startsWith('"')&&a.endsWith('"')&&a.length>=2||a.startsWith("'")&&a.endsWith("'")&&a.length>=2)&&(a=a.slice(1,-1)),{command:i,template:a}}function Is(e,t){let n=e.replace(/\r\n/g,`
134
+ `).trim();if(!n)throw new Error("Recipe body is empty.");let r=n.match(/^--steps\s+([\s\S]+)$/i);if(r){let p=r[1].trim().split(/\s*;\s*|\n/).filter(y=>y.trim());if(p.length===0)throw new Error("No steps found. Separate steps with ; or newlines.");let f=p.map(y=>{let h=y.startsWith("+"),w=h?y.slice(1).trim():y.trim();if(!w)throw new Error("Empty step in recipe.");return{cmd:w,...h?{continueOnFail:!0}:{}}});return nt({command:f[0].cmd,steps:f})}let o="OMNISH_TASK",{command:s,template:i}=dp(n);if(i!==void 0){if(!s)throw new Error("Command part (before --template) is empty.");if(!i.trim())throw new Error("Template part (after --template) is empty.");let d=Dt(s,o);if(!d.ok)throw new Error(d.error);return nt({command:s,promptTemplate:i})}let a=/\n---\n/,l=n.search(a);if(l>=0){let d=n.slice(0,l).trim(),p=n.slice(l).replace(/^\n---\n/,"").trim();if(!d)throw new Error("Command part (before ---) is empty.");if(!p)throw new Error("Template part (after ---) is empty.");let f=Dt(d,o);if(!f.ok)throw new Error(f.error);return nt({command:d,promptTemplate:p})}if(Dt(n,o).ok)return nt({command:n});let c=t.trim(),u=Dt(c,o);if(!u.ok)throw new Error(`recipesMacroDefaultCommand invalid (${u.error}). Fix config or paste runner then --- then template.`);return nt({command:c,promptTemplate:n})}function Fr(e,t,n){let r="`".repeat(3),o=`<<<${r}$${t}${r}>>>`,s=e;s.includes(o)&&(s=s.split(o).join(n));let i=`<<<${t}>>>`;s.includes(i)&&(s=s.split(i).join(n));let a=new RegExp(`\\$${t}\\b`,"g");return s.replace(a,n)}function As(e,t,n,r="chat"){let o=Ht(t);if(!o.ok)throw new Error(o.error);let s=nt({...n,command:n.command}),i=Bn(s);if(!i.ok)throw new Error(i.error);let a=r==="global"?an:e,l=vs(a);l[o.normalized]=s,Lr()}function bl(e,t,n="chat"){let r=t.trim().toLowerCase(),o=n==="global"?an:e;Nr();let s=sn.get(o);return!s||!(r in s)?!1:(delete s[r],Lr(),!0)}function Ps(e,t,n,r){let o=Ht(t);if(!o.ok)return{ok:!1,error:o.error};let s=o.normalized,i=e,a=an;Nr();let l=vs(i),c=vs(a),u=l[s],d=c[s],p=f=>{let y=nt({...f}),h=Bn(y);if(!h.ok)throw new Error(h.error);return y};try{if(n==="global"){if(u!==void 0){let h=p(u);return c[s]=h,delete l[s],Lr(),{ok:!0,kind:"moved",target:"global",name:s}}if(d!==void 0)return{ok:!0,kind:"noop",message:`Recipe "${s}" is already gateway-shared.`};let y=He(e,r,s);return y?.source==="builtin"||y?.source==="global"?{ok:!1,error:`Recipe "${s}" is built-in or from host recipes.json \u2014 not moved via /run set. Edit recipes.json or use a different name.`}:{ok:!1,error:`No user recipe "${s}" in this chat to promote. Add with /run add ${s} \u2026 or demote from global with /run set -p ${s}.`}}if(d!==void 0){let y=p(d);return l[s]=y,delete c[s],Lr(),{ok:!0,kind:"moved",target:"chat",name:s}}if(u!==void 0)return{ok:!0,kind:"noop",message:`Recipe "${s}" is already stored for this chat only.`};let f=He(e,r,s);return f?.source==="builtin"||f?.source==="global"?{ok:!1,error:`Recipe "${s}" is built-in or from host recipes.json \u2014 not moved via /run set.`}:{ok:!1,error:`No gateway-shared user recipe "${s}" to demote. Add with /run add --global ${s} \u2026 or promote from this chat with /run set -g ${s}.`}}catch(f){return{ok:!1,error:String(f)}}}function Sl(e,t){let n=[...gl(e,t).values()],r=n.filter(a=>a.featured).sort((a,l)=>a.name.localeCompare(l.name)),o=n.filter(a=>a.source==="peer").sort((a,l)=>a.name.localeCompare(l.name)),s=n.filter(a=>a.source==="shared").sort((a,l)=>a.name.localeCompare(l.name)),i=n.filter(a=>!a.featured&&a.source!=="peer"&&a.source!=="shared").sort((a,l)=>a.name.localeCompare(l.name));return{featured:r,shared:s,yours:o,more:i}}function Ur(e){let t=np.randomBytes(4).toString("hex");return`r-${e.replace(/[^a-zA-Z0-9_-]/g,"").slice(0,12)}-${t}`.slice(0,32)}function vl(e,t){let n=[`runbook: ${e}`];for(let o of t){let s=o.skipped?"skip":o.exitCode===0?"ok":"FAIL",i=o.label||`step ${o.index+1}`;if(o.skipped)n.push(`[${s}] ${i}`);else{n.push(`[${s}] ${i}${o.timedOut?" (timeout)":o.exitCode!==0?` (exit ${o.exitCode})`:""}`);let a=o.output.trim();if(a){let l=a.length>300?a.slice(0,300)+"\u2026":a;n.push(l)}}}let r=t.every(o=>o.skipped||o.exitCode===0);return n.push(r?"All steps completed successfully.":"Runbook stopped on failure."),n.join(`
135
+ `)}j();var xl={enter:"\r",cr:"\r",lf:`
136
+ `,return:"\r",tab:" ",esc:"\x1B",escape:"\x1B",space:" ",backspace:"\x7F",bs:"\x7F",delete:"\x1B[3~",up:"\x1B[A",down:"\x1B[B",right:"\x1B[C",left:"\x1B[D",home:"\x1B[H",end:"\x1B[F",pageup:"\x1B[5~",pagedown:"\x1B[6~","ctrl+c":"","ctrl+d":"","ctrl+z":"","ctrl+l":"\f","ctrl+u":"","ctrl+k":"\v"};function pp(e){let t="";for(let n=0;n<e.length;n++){let r=e[n];if(r==="\\"&&n+1<e.length){let o=e[n+1];if(o==="x"&&n+3<e.length){let s=e.slice(n+2,n+4);if(/^[0-9a-fA-F]{2}$/.test(s)){t+=String.fromCharCode(Number.parseInt(s,16)),n+=3;continue}}if(o==="r"){t+="\r",n++;continue}if(o==="n"){t+=`
137
+ `,n++;continue}if(o==="t"){t+=" ",n++;continue}if(o==="\\"){t+="\\",n++;continue}}t+=r}return t}function mp(e){let t=e.trim();if(!t)return"";if(t.startsWith("\\"))return pp(t);let n=t.toLowerCase();if(xl[n])return xl[n];let r=t.match(/^\^([A-Za-z])$/);if(r){let s=r[1].toUpperCase().charCodeAt(0);if(s>=64&&s<=95)return String.fromCharCode(s-64)}let o=n.match(/^ctrl\+(.+)$/);if(o){let s=o[1];if(s.length===1){let i=s.toUpperCase().charCodeAt(0);if(i>=64&&i<=95)return String.fromCharCode(i-64)}}return t}function Os(e){let t=e.split(",").map(n=>n.trim()).filter(Boolean);return t.length===0?"":t.map(n=>mp(n)).join("")}var jn="\x1B";function $l(e){let t=e;return t=t.replace(new RegExp(`${jn}\\[[\\d;?]*[ -/]*[@-~]`,"g"),""),t=t.replace(new RegExp(`${jn}\\][^${jn}\\u0007]*(?:\\u0007|${jn}\\\\)`,"g"),""),t=t.replace(new RegExp(`${jn}[@-Z\\\\-_]`,"g"),""),t=t.replace(/\u0007/g,""),t}function fp(e,t){if(e.length<=t)return e?[e]:[];let n=[];for(let r=0;r<e.length;r+=t)n.push(e.slice(r,r+t));return n}function gp(e,t){return`${e}${t}`}var Dr=class{constructor(t){this.opts=t}pending=new Map;timers=new Map;lastFlushEnd=new Map;flushing=new Set;closed=!1;dispose(){this.closed=!0;for(let t of this.timers.values())clearTimeout(t);this.timers.clear(),this.pending.clear(),this.lastFlushEnd.clear(),this.flushing.clear()}push(t,n,r){if(this.closed||this.opts.isMuted(t,n))return;let o=gp(t,n);this.pending.set(o,(this.pending.get(o)??"")+r);let s=this.timers.get(o);s&&clearTimeout(s);let i=this.opts.getCfg().appsFlushMs,a=setTimeout(()=>{this.timers.delete(o),this.flushNow(o,t,n)},i);this.timers.set(o,a)}async flushNow(t,n,r){if(!(this.closed||this.flushing.has(t))){this.flushing.add(t);try{let o=this.opts.getCfg(),s=o.appsMinIntervalMs,i=this.lastFlushEnd.get(t)??0,a=Math.max(0,i+s-Date.now());if(a>0&&await new Promise(f=>setTimeout(f,a)),this.closed)return;let l=this.pending.get(t)??"";if(this.pending.delete(t),!l||(this.opts.isRaw(n,r)||(l=$l(l)),!l.trim()))return;let c=o.appsMaxFlushBytes;if(l.length>c){let f=l.slice(c);l=l.slice(0,c)+`
138
+ [\u2026+${f.length} chars; /apps since ${r} to read more]`,this.pending.set(t,(this.pending.get(t)??"")+f)}let d=(this.opts.getRunningCount(n)>1?`[${r}] `:"")+l,p=fp(d,o.appsMaxWaChars);for(let f of p){if(this.closed)break;try{await this.opts.send(n,f)}catch{break}}this.lastFlushEnd.set(t,Date.now())}finally{this.flushing.delete(t),!this.closed&&(this.pending.get(t)??"").length>0&&queueMicrotask(()=>void this.flushNow(t,n,r))}}}};j();import hp from"node:fs";import yp from"node:path";import*as Cl from"node-pty";var wp=1024*1024,Hr=class e{peerKey;name;command;cwd;envKeysCount;logPath;term=null;logStream=null;disposeData=null;disposeExit=null;exited=!1;ringChunks=[];ringBytes=0;constructor(t){this.peerKey=t.peerKey,this.name=t.name,this.command=t.command,this.cwd=t.cwd,this.envKeysCount=t.envKeysCount,this.logPath=t.logPath}appendRing(t){for(this.ringChunks.push(t),this.ringBytes+=t.length;this.ringBytes>wp&&this.ringChunks.length>0;){let n=this.ringChunks.shift();this.ringBytes-=n.length}}static start(t){W(An(t.peerKey));let n=yp.join(An(t.peerKey),`${t.name}.log`),r=hp.createWriteStream(n,{flags:"a",mode:384}),o={...process.env,TERM:"xterm-256color",COLORTERM:"truecolor",...t.cwd?{PWD:t.cwd}:{},...t.extraEnv??{}},s=Cl.spawn(t.shell,["-lc",t.command],{name:"xterm-256color",cols:t.cols,rows:t.rows,cwd:t.cwd,env:o}),i=new e({...t,logPath:n});return i.term=s,i.logStream=r,i.disposeData=s.onData(a=>{let l=Buffer.from(a,"utf8");i.appendRing(l),r.write(a),t.router.push(t.peerKey,t.name,a)}),i.disposeExit=s.onExit(a=>{i.exited||(i.exited=!0,i.cleanupTerm(),t.onExit({exitCode:a.exitCode,signal:a.signal}))}),i}get alive(){return this.term!==null&&!this.exited}get ringByteCount(){return this.ringBytes}write(t){this.term?.write(t)}resize(t,n){this.term?.resize(t,n)}kill(t){if(this.term)try{this.term.kill(t)}catch{}}cleanupTerm(){this.disposeData?.dispose(),this.disposeData=null,this.disposeExit?.dispose(),this.disposeExit=null,this.term=null,this.logStream?.end(),this.logStream=null}destroy(){if(this.exited){this.cleanupTerm();return}this.exited=!0,this.kill("SIGHUP"),this.cleanupTerm()}};var bp=/^[a-zA-Z0-9_-]{1,32}$/;function Le(e){return bp.test(e)?null:"Session name must be 1\u201332 chars: letters, digits, _ or -."}function Ee(e,t){return`${e}:${t}`}function Sp(e){let t=e.lastIndexOf(":");return t<=0?null:{peerKey:e.slice(0,t),name:e.slice(t+1)}}function Ls(e){return new Promise(t=>setTimeout(t,e))}function vp(e,t){return e===0&&(t===0||t==null)}async function Ns(e,t,n){let r=n.appsSubmitDelayMs,o=n.appsClearInputDelayMs,i=n.appsClearInput!==!1?Os(n.appsClearInputSequence||"^A,^K"):"",l=t.replace(/\r\n/g,`
139
+ `).replace(/\r/g,"").split(`
140
+ `);i&&(o>0&&await Ls(o),e.write(i));for(let c of l)c.length>0&&e.write(c),r>0&&await Ls(r),e.write("\r"),i&&(o>0&&await Ls(o),e.write(i))}function xp(e,t){try{let r=$t.readFileSync(e,"utf8").split(/\r?\n/);return r.slice(Math.max(0,r.length-t)).join(`
141
+ `).trimEnd()}catch{return""}}var Ct=class{constructor(t,n){this.getCfg=t;this.send=n,this.router=new Dr({getCfg:()=>this.getCfg(),send:n,isMuted:(r,o)=>this.muted.has(Ee(r,o)),isRaw:(r,o)=>this.rawMode.has(Ee(r,o)),getRunningCount:r=>this.countPeerRunning(r)})}sessions=new Map;focus=new Map;muted=new Set;rawMode=new Set;router;killTimers=new Set;send;runQueue=new Map;activeQueuedRunHead=new Map;runQueuePaused=new Map;countPeerRunning(t){let n=`${t}:`,r=0;for(let[o,s]of this.sessions)o.startsWith(n)&&s.alive&&r++;return r}countTotalRunning(){let t=0;for(let n of this.sessions.values())n.alive&&t++;return t}dispose(){for(let t of this.killTimers)clearTimeout(t);this.killTimers.clear(),this.stopAll(),this.router.dispose()}stopAll(){for(let t of this.sessions.values())t.destroy();this.sessions.clear(),this.focus.clear(),this.runQueue.clear(),this.activeQueuedRunHead.clear(),this.runQueuePaused.clear()}getFocus(t){return this.focus.get(t)??null}logPath(t,n){return kp.join(An(t),`${n}.log`)}getSession(t,n){return this.sessions.get(Ee(t,n))}removeSessionRecord(t,n){this.sessions.delete(Ee(t,n)),this.focus.get(t)===n&&this.focus.set(t,null)}async writeFocusedLine(t,n){let r=this.focus.get(t);if(!r)return!1;let o=this.getSession(t,r);return o?.alive?(await Ns(o,n,this.getCfg()),!0):!1}async writeNamedLine(t,n,r){let o=Le(n);if(o)return o;let s=this.getSession(t,n);return s?.alive?(await Ns(s,r,this.getCfg()),null):`No app session "${n}". /apps list`}enqueueQueuedRun(t,n,r){let o=this.runQueue.get(t)??[];o.push(n),this.runQueue.set(t,o);let s=o.length,i=this.runQueuePaused.get(t)??!1,a=this.activeQueuedRunHead.get(t)??null,l=a?.sessionName??null;if(l?this.getSession(t,l)?.alive??!1:!1){let u=a?.recipeLabel?` (recipe: ${a.recipeLabel})`:"",d=s-1,p=d>0?`${d} other job(s) in line before this one.`:"Next in line after the active run finishes.";return`Queued "${n.recipeLabel}" (wait slot ${s} behind active ${l}${u}). ${p}`}return i?`Queued "${n.recipeLabel}" (position ${s}). Run queue is paused \u2014 /run queue resume to continue.`:this.drainNextQueuedRun(t,r)}resumeRunQueue(t,n){this.runQueuePaused.set(t,!1);let r=this.activeQueuedRunHead.get(t)??null,o=r?.sessionName??null;if(o?this.getSession(t,o)?.alive??!1:!1){let a=r?.recipeLabel?` (recipe: ${r.recipeLabel})`:"";return`Run queue: session "${o}"${a} is still running. Wait for exit code=0 and signal=0 before the next starts.`}return(this.runQueue.get(t)??[]).length===0?"Run queue is empty.":(this.activeQueuedRunHead.set(t,null),this.drainNextQueuedRun(t,n))}runQueueStatus(t){let n=this.runQueue.get(t)??[],r=this.activeQueuedRunHead.get(t)??null,o=r?.sessionName??null,s=this.runQueuePaused.get(t)??!1,i=o?this.getSession(t,o)?.alive??!1:!1,a=["Run queue (this chat)"];if(o&&i){let l=r?.recipeLabel?` \xB7 recipe: ${r.recipeLabel}`:"";a.push(`Active: ${o}${l}`)}else o?a.push(`Active: ${o} (exiting or stale)`):a.push("Active: (none)");if(a.push(`Pending: ${n.length} (waiting only \u2014 not counting the active run above)`),a.push(`Paused: ${s}`),n.length>0){a.push("Waiting (FIFO order):");for(let c=0;c<Math.min(n.length,20);c++)a.push(`${c+1}) ${n[c].recipeLabel}`);n.length>20&&a.push(`\u2026 and ${n.length-20} more`)}return a.push("Next auto-starts only after exit code=0 and signal=0."),a.push("Commands: /run queue resume"),a.join(`
142
+ `)}drainNextQueuedRun(t,n){let r=this.runQueue.get(t);if(!r||r.length===0)return"";let o=r.shift();r.length===0?this.runQueue.delete(t):this.runQueue.set(t,r);let s=Ur(o.recipeLabel),i=this.runQueue.get(t)?.length??0,a=this.start(t,s,o.command,n,o.extraEnv);if(!a.includes("started and attached"))return r.unshift(o),this.runQueue.set(t,r),this.runQueuePaused.set(t,!0),this.activeQueuedRunHead.set(t,null),`${a}
143
+ Run queue paused; fix the issue, then /run queue resume.`;this.activeQueuedRunHead.set(t,{sessionName:s,recipeLabel:o.recipeLabel});let c=i>0?`
144
+ Run queue: started head above; ${i} job(s) still waiting in FIFO.`:`
145
+ Run queue: started head above; no further queued jobs.`;return`${a}${c}`}start(t,n,r,o,s){let i=Le(n);if(i)return i;if(!r.trim())return`Usage: /apps start <name> <command\u2026>
146
+ Example: /apps start sh bash`;if(this.sessions.has(Ee(t,n)))return`Session "${n}" already exists. /apps stop ${n} or pick another name.`;if(this.countPeerRunning(t)>=o.appsMaxSessions)return`Per-chat app limit (${o.appsMaxSessions}) reached. /apps stop or /apps rm an old session.`;if(this.countTotalRunning()>=o.appsMaxSessionsTotal)return`Global app limit (${o.appsMaxSessionsTotal}) reached. Stop sessions in other chats first.`;V();let a=ee(t).cwd,l={...process.env,TERM:"xterm-256color",COLORTERM:"truecolor",...a?{PWD:a}:{},...s??{}},c;try{c=Hr.start({peerKey:t,name:n,shell:o.shell,command:r.trim(),cwd:a,cols:o.appsCols,rows:o.appsRows,envKeysCount:Object.keys(l).length,extraEnv:s,router:this.router,onExit:u=>{this.handleSessionExit(t,n,u)}})}catch(u){return`App spawn failed: ${String(u)}
147
+ If install skipped native builds: pnpm approve-builds && pnpm install (needs @whiskeysockets/baileys, sharp, protobufjs, esbuild, node-pty).`}return this.sessions.set(Ee(t,n),c),this.focus.set(t,n),`App "${n}" started and attached.
148
+ [cwd: ${a}]
149
+ Plain DMs go to this session until /apps detach. Use >othername text for another session.`}async handleSessionExit(t,n,r){let o=Ee(t,n);if(!this.sessions.has(o))return;let s=this.activeQueuedRunHead.get(t)?.sessionName===n,i=this.focus.get(t)===n;this.sessions.delete(o),i&&this.focus.set(t,null),this.muted.delete(o),this.rawMode.delete(o);let a=r.signal!=null?` signal=${r.signal}`:"",l=i?" (detached)":"",c=`[${n}] exited code=${r.exitCode}${a}${l}`;try{await this.send(t,c)}catch{}if(!s)return;this.activeQueuedRunHead.set(t,null);let u=this.getCfg(),d=vp(r.exitCode,r.signal),p=this.runQueue.get(t)?.length??0;if(d){if(this.runQueuePaused.set(t,!1),p>0){let f=this.drainNextQueuedRun(t,u);if(f)try{await this.send(t,f)}catch{}}return}if(this.runQueuePaused.set(t,!0),p>0){let f=`Run queue paused (exit code=${r.exitCode}${r.signal!=null?` signal=${r.signal}`:""}). ${p} run(s) waiting. /run queue resume to continue.`;try{await this.send(t,f)}catch{}}}attach(t,n){let r=Le(n);return r||(this.getSession(t,n)?.alive?(this.focus.set(t,n),`Attached to "${n}". Plain messages (no ! or / or >) go to this app. /apps detach to stop.`):`No session "${n}". /apps list`)}detach(t){return this.focus.set(t,null),"Detached (attach mode off)."}list(t){let n=[];for(let[s,i]of this.sessions){let a=Sp(s);if(!a||a.peerKey!==t||!i.alive)continue;let l=this.focus.get(t)===a.name?" *":"";n.push(`${a.name}${l}`)}if(n.length===0)return"(no app sessions; /apps start <name> <cmd>)";let r=this.focus.get(t);return`${r?`attached: ${r}`:"(no focus)"}
150
+ App sessions:
151
+ ${n.join(`
152
+ `)}`}info(t,n){let r=this.getCfg(),o=n??this.focus.get(t)??"";if(!o)return"Usage: /apps info <name> (or /apps get <name>, or attach first)";let s=Le(o);if(s)return s;let i=this.getSession(t,o),a=this.logPath(t,o),l=0;try{l=$t.statSync(a).size}catch{}let c=this.muted.has(Ee(t,o)),u=this.rawMode.has(Ee(t,o));return[`session: ${o}`,`alive: ${i?.alive??!1}`,`cmd: ${i?.command??"(unknown)"}`,`cwd: ${i?.cwd??"(unknown)"}`,`env keys: ${i?.envKeysCount??"?"}`,`terminal size: ${r.appsCols}x${r.appsRows}`,`ring bytes (approx): ${i?.ringByteCount??0}`,`log: ${a} (${l} bytes)`,`mute outbound: ${c}`,`raw outbound: ${u}`].join(`
153
+ `)}async sendText(t,n,r){let o=Le(n);if(o)return o;let s=this.getSession(t,n);if(!s?.alive)return`No session "${n}".`;let i=r.replace(/\r\n/g,`
154
+ `);return await Ns(s,i,this.getCfg()),`Sent to "${n}" (${i.length} chars + Enter per line).`}sendKey(t,n,r){let o=Le(n);if(o)return o;let s=this.getSession(t,n);if(!s?.alive)return`No session "${n}".`;let i=Os(r);return i?(s.write(i),`Sent keys to "${n}".`):"Empty key sequence. Example: /apps key sh Enter or ^C,Up,Enter"}tail(t,n,r){let o=Le(n);if(o)return o;let s=this.logPath(t,n);if(!$t.existsSync(s))return`(no log file for ${n})`;let i=this.getCfg(),a=Number.isFinite(r)&&r>0?Math.min(500,r):i.appsLogTailLines;return xp(s,a)||"(empty log)"}readSince(t,n,r){let o=this.logPath(t,n);try{let i=$t.statSync(o).size,a=$t.openSync(o,"r");try{let l=Math.min(r,i),c=i-l,u=Buffer.alloc(c);return c>0&&$t.readSync(a,u,0,c,l),{text:u.toString("utf8"),nextOffset:i}}finally{$t.closeSync(a)}}catch{return{text:"",nextOffset:r}}}mute(t,n){let r=Le(n);return r||(this.muted.add(Ee(t,n)),`Muted chat output for "${n}" (log still grows). /apps unmute ${n}`)}unmute(t,n){let r=Le(n);return r||(this.muted.delete(Ee(t,n)),`Unmuted "${n}".`)}setRaw(t,n,r){let o=Le(n);if(o)return o;let s=Ee(t,n);return r?this.rawMode.add(s):this.rawMode.delete(s),`raw chat output for "${n}": ${r?"on (ANSI kept)":"off (stripped)"}.`}resize(t,n,r,o){let s=Le(n);if(s)return s;let i=this.getSession(t,n);if(!i?.alive)return`No session "${n}".`;let a=Math.min(500,Math.max(20,Math.floor(r))),l=Math.min(200,Math.max(5,Math.floor(o)));return i.resize(a,l),`Resized "${n}" to ${a}x${l}.`}stop(t,n){let r=Le(n);if(r)return r;let o=this.getSession(t,n);if(!o?.alive)return`No running session "${n}".`;o.kill("SIGTERM");let s=Ee(t,n),i=setTimeout(()=>{this.killTimers.delete(i);let a=this.getSession(t,n);a?.alive&&a.kill("SIGKILL")},5e3);return this.killTimers.add(i),`SIGTERM sent to "${n}". SIGKILL in 5s if still running.`}kill(t,n){let r=Le(n);if(r)return r;let o=this.getSession(t,n);return o?.alive?(o.kill("SIGKILL"),`SIGKILL sent to "${n}".`):`No running session "${n}".`}rm(t,n){let r=Le(n);if(r)return r;if(this.getSession(t,n)?.alive)return`Session "${n}" is still running. /apps stop ${n} first, then /apps rm ${n}.`;this.removeSessionRecord(t,n),this.muted.delete(Ee(t,n)),this.rawMode.delete(Ee(t,n));let s=this.logPath(t,n);try{$t.rmSync(s,{force:!0})}catch{}return`Removed "${n}" metadata and log.`}};Ue();Se();import Jn from"node:fs";import zn from"node:path";import Rl from"node:fs";import Ml from"node:path";var $p=new Set(["jpg","jpeg","png","gif","webp","bmp","tif","tiff","heic","avif"]),Cp=new Set(["mp4","mov","webm","mkv","avi","m4v","3gp"]),Rp=new Set(["mp3","ogg","opus","wav","m4a","flac","aac","wma"]),Br={jpg:"image/jpeg",jpeg:"image/jpeg",png:"image/png",gif:"image/gif",webp:"image/webp",bmp:"image/bmp",tif:"image/tiff",tiff:"image/tiff",heic:"image/heic",avif:"image/avif",mp4:"video/mp4",mov:"video/quicktime",webm:"video/webm",mkv:"video/x-matroska",avi:"video/x-msvideo",m4v:"video/x-m4v","3gp":"video/3gpp",mp3:"audio/mpeg",ogg:"audio/ogg",opus:"audio/opus",wav:"audio/wav",m4a:"audio/mp4",flac:"audio/flac",aac:"audio/aac",wma:"audio/x-ms-wma",pdf:"application/pdf",zip:"application/zip",gz:"application/gzip",txt:"text/plain",json:"application/json",xml:"application/xml",csv:"text/csv"};function Mp(e){let t=e.replace(/^\./,"").toLowerCase();return $p.has(t)?{category:"image",mimetype:Br[t]??"image/jpeg"}:Cp.has(t)?{category:"video",mimetype:Br[t]??"video/mp4"}:Rp.has(t)?{category:"audio",mimetype:Br[t]??"audio/mpeg"}:{category:"document",mimetype:Br[t]??"application/octet-stream"}}function Rt(e,t){let n;try{n=Rl.realpathSync(e)}catch{return{error:"File not found or unreadable."}}let r;try{r=Rl.lstatSync(n)}catch{return{error:"Cannot stat file."}}if(!r.isFile())return{error:"Not a regular file (directories and special files are not supported)."};if(t>0&&r.size>t)return{error:`File too large (max ${t} bytes).`};let o=Ml.basename(n),s=Ml.extname(n).slice(1),{category:i,mimetype:a}=Mp(s);return{absPath:n,category:i,mimetype:a,displayName:o}}import Tp from"node:os";import*as El from"node-pty";function Ep(e,t){return e.length<=t?e:`${e.slice(0,t)}
155
+ [...truncated]`}function Ip(e){if(e===void 0||e===0)return null;let t=Tp.constants.signals;for(let[n,r]of Object.entries(t))if(r===e)return n;return null}function Tl(e){try{process.platform==="win32"?e.kill():e.kill("SIGTERM")}catch{e.kill()}}function Bt(e,t,n){return new Promise(r=>{let o=Date.now(),s=!1,i="",a=n.cwd,l=null,c=!1,u=null,d=null,p,f=w=>{if(c)return;c=!0,p!==void 0&&clearTimeout(p),u?.dispose(),u=null,r(w);let v=d;d=null,queueMicrotask(()=>v?.dispose())},h={...n.env??process.env,TERM:"xterm-256color",COLORTERM:"truecolor",...a?{PWD:a}:{}};try{l=El.spawn(e,["-c",t],{name:"xterm-256color",cols:120,rows:40,cwd:a,env:h})}catch(w){f({code:null,stdout:"",stderr:String(w),durationMs:Date.now()-o,timedOut:!1,signal:null});return}p=setTimeout(()=>{s=!0,l&&Tl(l)},n.timeoutMs),u=l.onData(w=>{i+=w,i.length>n.maxBytes&&(i=Ep(i,n.maxBytes),l&&Tl(l))}),d=l.onExit(w=>{f({code:w.exitCode,stdout:i,stderr:"",durationMs:Date.now()-o,timedOut:s,signal:Ip(w.signal)})})})}function Wn(e,t,n){let r=new Date(e),o=r.getFullYear(),s=r.getMonth(),i=r.getDate(),a=new Date(o,s,i,t,n,0,0).getTime();return a<=e&&(a=new Date(o,s,i+1,t,n,0,0).getTime()),a}function Ap(e,t,n){let r=e;for(let o=0;o<14;o++){let s=Wn(r,t,n),i=new Date(s).getDay();if(i>=1&&i<=5)return s;r=s}return Wn(r,t,n)}function Pp(e,t,n,r){let o=e;for(let s=0;s<370;s++){let i=Wn(o,n,r);if(new Date(i).getDay()===t)return i;o=i}return Wn(o,n,r)}function Op(e,t){let n=new Date(e),r=n.getFullYear(),o=n.getMonth(),s=n.getDate(),i=n.getHours(),a=new Date(r,o,s,i,t,0,0).getTime();return a<=e&&(a=new Date(r,o,s,i+1,t,0,0).getTime()),a}function Lp(e,t){return e.kind==="ondemand"||e.kind==="heartbeat"?Number.POSITIVE_INFINITY:e.kind==="daily"?Wn(t,e.hour,e.minute):e.kind==="weekdays"?Ap(t,e.hour,e.minute):e.kind==="hourly"?Op(t,e.minute):Pp(t,e.weekday,e.hour,e.minute)}function Pl(e,t,n,r,o=32){if(e.kind==="ondemand"||e.kind==="heartbeat")return[];let s=t??n-1,i=[],a=s;for(;i.length<o;){let l=Lp(e,a);if(l>r)break;i.push(l),a=l}return i}var Np={sun:0,sunday:0,mon:1,monday:1,tue:2,tues:2,tuesday:2,wed:3,wednesday:3,thu:4,thur:4,thurs:4,thursday:4,fri:5,friday:5,sat:6,saturday:6};function _s(e){let t=/^(\d{1,2}):(\d{2})$/.exec(e.trim());if(!t)return null;let n=Number(t[1]),r=Number(t[2]);return!Number.isFinite(n)||!Number.isFinite(r)||n>23||r>59?null:{hour:n,minute:r}}function _p(e){let t=e.trim(),n=/^:(\d{1,2})$/.exec(t),r=/^(\d{1,2})$/.exec(t),o=n?n[1]:r?r[1]:null;if(o===null)return null;let s=Number(o);return!Number.isInteger(s)||s<0||s>59?null:s}function jr(e){if(e.length===0)return{ok:!1,error:"Missing schedule (try: ondemand, hourly [:MM], daily HH:MM, weekdays HH:MM, weekly <dow> HH:MM)."};let t=e[0].toLowerCase();if(t==="ondemand"||t==="manual")return e.length!==1?{ok:!1,error:"ondemand takes no extra tokens."}:{ok:!0,schedule:{kind:"ondemand"}};if(t==="daily"){if(e.length!==2)return{ok:!1,error:"Usage: daily HH:MM"};let n=_s(e[1]);return n?{ok:!0,schedule:{kind:"daily",hour:n.hour,minute:n.minute}}:{ok:!1,error:"daily needs HH:MM (24h)."}}if(t==="weekdays"){if(e.length!==2)return{ok:!1,error:"Usage: weekdays HH:MM"};let n=_s(e[1]);return n?{ok:!0,schedule:{kind:"weekdays",hour:n.hour,minute:n.minute}}:{ok:!1,error:"weekdays needs HH:MM (24h)."}}if(t==="hourly"){if(e.length===1)return{ok:!0,schedule:{kind:"hourly",minute:0}};if(e.length!==2)return{ok:!1,error:"Usage: hourly | hourly :MM | hourly MM (minute 0\u201359)."};let n=_p(e[1]);return n===null?{ok:!1,error:"hourly needs :MM or MM (minute 0\u201359), e.g. hourly :30 or hourly 15."}:{ok:!0,schedule:{kind:"hourly",minute:n}}}if(t==="weekly"){if(e.length!==3)return{ok:!1,error:"Usage: weekly <mon|tue|\u2026|sun> HH:MM"};let n=e[1].toLowerCase(),r=Np[n];if(r===void 0){let s=Number(n);Number.isInteger(s)&&s>=0&&s<=6&&(r=s)}if(r===void 0)return{ok:!1,error:`Unknown weekday "${e[1]}". Use mon\u2026sun or 0\u20136 (Sun=0).`};let o=_s(e[2]);return o?{ok:!0,schedule:{kind:"weekly",weekday:r,hour:o.hour,minute:o.minute}}:{ok:!1,error:"weekly needs HH:MM (24h) after weekday."}}if(t==="heartbeat"){if(e.length<2||e.length>3)return{ok:!1,error:"Usage: heartbeat <interval> [grace] \u2014 e.g. heartbeat 1h, heartbeat 30m 10m"};let n=Il(e[1]);if(n===null||n<6e4)return{ok:!1,error:"heartbeat interval must be >= 1m. Use 5m, 1h, 2h, 1d, etc."};let r=Math.floor(n*.5);if(e.length===3){let o=Il(e[2]);if(o===null||o<3e4)return{ok:!1,error:"heartbeat grace must be >= 30s. Use 1m, 5m, etc."};r=o}return{ok:!0,schedule:{kind:"heartbeat",intervalMs:n,graceMs:r}}}return{ok:!1,error:`Unknown schedule kind "${t}".`}}function Il(e){let t=e.trim().toLowerCase();if(!t)return null;let n=t.match(/^(?:(\d+)d)?(?:(\d+)h)?(?:(\d+)m)?(?:(\d+)s)?$/);if(!n||t===""||!n[1]&&!n[2]&&!n[3]&&!n[4])return null;let r=n[1]?Number(n[1]):0,o=n[2]?Number(n[2]):0,s=n[3]?Number(n[3]):0,i=n[4]?Number(n[4]):0;return((r*24+o)*60+s)*6e4+i*1e3}function Al(e){let t=Math.floor(e/1e3),n=Math.floor(t/86400),r=Math.floor(t%86400/3600),o=Math.floor(t%3600/60),s=[];return n>0&&s.push(`${n}d`),r>0&&s.push(`${r}h`),o>0&&s.push(`${o}m`),s.length>0?s.join(""):`${t}s`}function cn(e){let t=r=>r<10?`0${r}`:String(r),n=(r,o)=>`${t(r)}:${t(o)}`;switch(e.kind){case"ondemand":return"ondemand";case"daily":return`daily ${n(e.hour,e.minute)}`;case"weekdays":return`weekdays ${n(e.hour,e.minute)}`;case"hourly":return e.minute===0?"hourly":`hourly :${t(e.minute)}`;case"weekly":return`weekly ${["sun","mon","tue","wed","thu","fri","sat"][e.weekday]??e.weekday} ${n(e.hour,e.minute)}`;case"heartbeat":return`heartbeat every ${Al(e.intervalMs)} grace ${Al(e.graceMs)}`}}Ue();function Wr(e,t,n){if(e==="none")return[];let r=new Set;if((e==="self"||e==="all")&&r.add(t),e==="wa"||e==="all")for(let o of n.allowFrom){let s=q(String(o));s&&r.add(`wa:${Nt(s)}`)}if(e==="tg"||e==="all")for(let o of n.telegramAllowFrom){let s=ye(String(o));s&&r.add(`tg:${s}`)}return[...r]}j();Se();import Fp from"better-sqlite3";var Ol=2,Mt=null;function Ll(){W(Xe);let e=new Fp(Bi);e.pragma("journal_mode = WAL"),e.exec(`
153
156
  CREATE TABLE IF NOT EXISTS cowork_meta (
154
157
  key TEXT PRIMARY KEY,
155
158
  value TEXT NOT NULL
@@ -163,124 +166,171 @@ ${s.detail}`)}return p("Unknown /service command. Try /service help")}function u
163
166
  PRIMARY KEY (task_id, slot_ms)
164
167
  );
165
168
  CREATE INDEX IF NOT EXISTS idx_cowork_completion_task ON cowork_slot_completion(task_id);
166
- `);let t=e.prepare("SELECT value FROM cowork_meta WHERE key = 'schema_version'").get();return(t?Number(t.value):0)<wa&&e.prepare("INSERT OR REPLACE INTO cowork_meta (key, value) VALUES ('schema_version', ?)").run(String(wa)),e}function fn(e){it||(it=ba()),yu(e)}function ka(){if(it){try{it.close()}catch{}it=null}}function To(){return it||(it=ba()),it}function hu(e){let n=To().prepare("SELECT MAX(slot_ms) AS m FROM cowork_slot_completion WHERE task_id = ?").get(e)?.m;return n==null||!Number.isFinite(n)?null:n}function cr(e){let t=hu(e.id);return t??e.lastCompletedSlotMs}function ur(e,t){if(t.length===0)return;let n=To(),r=n.prepare(`
169
+ CREATE TABLE IF NOT EXISTS cowork_task_state (
170
+ task_id TEXT PRIMARY KEY,
171
+ last_ok INTEGER NOT NULL,
172
+ updated_at_ms INTEGER NOT NULL
173
+ );
174
+ `);let t=e.prepare("SELECT value FROM cowork_meta WHERE key = 'schema_version'").get(),n=t?Number(t.value):0;return n<Ol&&(n<2&&e.exec(`
175
+ CREATE TABLE IF NOT EXISTS cowork_task_state (
176
+ task_id TEXT PRIMARY KEY,
177
+ last_ok INTEGER NOT NULL,
178
+ updated_at_ms INTEGER NOT NULL
179
+ );
180
+ `),e.prepare("INSERT OR REPLACE INTO cowork_meta (key, value) VALUES ('schema_version', ?)").run(String(Ol))),e}function jt(e){Mt||(Mt=Ll()),Dp(e)}function Nl(){if(Mt){try{Mt.close()}catch{}Mt=null}}function Wt(){return Mt||(Mt=Ll()),Mt}function Up(e){let n=Wt().prepare("SELECT MAX(slot_ms) AS m FROM cowork_slot_completion WHERE task_id = ?").get(e)?.m;return n==null||!Number.isFinite(n)?null:n}function Gr(e){let t=Up(e.id);return t??e.lastCompletedSlotMs}function Jr(e,t){if(t.length===0)return;let n=Wt(),r=n.prepare(`
167
181
  INSERT INTO cowork_slot_completion (task_id, slot_ms, kind, completed_at_ms, log_path)
168
182
  VALUES (?, ?, ?, ?, ?)
169
- `);n.transaction(()=>{for(let s of t)r.run(e,s.slotMs,s.kind,s.completedAtMs,s.logPath)})()}function yu(e){let n=To().prepare("SELECT COUNT(*) AS c FROM cowork_slot_completion WHERE task_id = ?"),r=Date.now();for(let o of e)if(!(o.lastCompletedSlotMs==null||!Number.isFinite(o.lastCompletedSlotMs)||n.get(o.id).c>0))try{ur(o.id,[{slotMs:o.lastCompletedSlotMs,kind:"migrated",logPath:null,completedAtMs:r}]),E.info({taskId:o.id,slotMs:o.lastCompletedSlotMs},"cowork seeded completion from tasks.json")}catch(i){E.warn({err:String(i),taskId:o.id},"cowork seed from tasks.json failed")}}import Sa from"node:crypto";import gn from"node:fs";import Io from"node:os";import Nt from"node:path";var wu=/^[a-zA-Z0-9][a-zA-Z0-9_-]{0,31}$/;function xa(e){let t=e.trim().toLowerCase();return wu.test(t)?{ok:!0,name:t}:{ok:!1,error:"Name must be alphanumeric with _ or -, max 32 chars."}}function _t(e,t){let n=e.trim();return n===""||n==="."?Nt.resolve(t):n==="~"?Io.homedir():n.startsWith("~/")||n.startsWith("~\\")?Nt.join(Io.homedir(),n.slice(2)):Nt.isAbsolute(n)?n:Nt.resolve(t,n)}function Ao(e){return Nt.join(Io.homedir(),"Cowork",e)}function bu(e){if(!e||typeof e!="object")return null;let t=e,n=typeof t.id=="string"&&t.id.length>=4?t.id.slice(0,32):"",r=typeof t.name=="string"?t.name.trim().toLowerCase():"",o=typeof t.ownerPeerKey=="string"?t.ownerPeerKey:"",s=typeof t.command=="string"?t.command:"";if(!n||!r||!o||!s)return null;let i=typeof t.cwd=="string"?t.cwd:"",a=typeof t.outputDir=="string"&&t.outputDir.trim()?t.outputDir:Ao(r),l=typeof t.enabled=="boolean"?t.enabled:!0,u=typeof t.notify=="string"?t.notify.toLowerCase():"self",c=u==="wa"||u==="whatsapp"?"wa":u==="tg"||u==="telegram"?"tg":u==="all"?"all":u==="none"?"none":"self",d={kind:"ondemand"};if(t.schedule&&typeof t.schedule=="object"){let h=t.schedule,x=typeof h.kind=="string"?h.kind:"";if(x==="ondemand")d={kind:"ondemand"};else if(x==="daily"){let k=Number(h.hour),$=Number(h.minute);Number.isFinite(k)&&Number.isFinite($)&&(d={kind:"daily",hour:k,minute:$})}else if(x==="weekdays"){let k=Number(h.hour),$=Number(h.minute);Number.isFinite(k)&&Number.isFinite($)&&(d={kind:"weekdays",hour:k,minute:$})}else if(x==="hourly"){let k=Number(h.minute);Number.isFinite(k)&&Number.isInteger(k)&&k>=0&&k<=59&&(d={kind:"hourly",minute:k})}else if(x==="weekly"){let k=Number(h.hour),$=Number(h.minute),A=Number(h.weekday);Number.isFinite(k)&&Number.isFinite($)&&Number.isInteger(A)&&(d={kind:"weekly",weekday:A,hour:k,minute:$})}}let m=null;typeof t.lastCompletedSlotMs=="number"&&Number.isFinite(t.lastCompletedSlotMs)&&(m=t.lastCompletedSlotMs);let y=typeof t.createdAtMs=="number"&&Number.isFinite(t.createdAtMs)?t.createdAtMs:Date.now(),g=typeof t.attachLog=="boolean"?t.attachLog:!1,f=Array.isArray(t.attachFiles)?t.attachFiles.filter(h=>typeof h=="string"&&h.trim().length>0):[];return{id:n,name:r,ownerPeerKey:o,command:s,cwd:i,outputDir:a,schedule:d,enabled:l,notify:c,attachLog:g,attachFiles:f,lastCompletedSlotMs:m,createdAtMs:y}}function $e(){try{let e=gn.readFileSync(Tr,"utf8"),t=JSON.parse(e);if(!t||!Array.isArray(t.tasks))return[];let n=[];for(let r of t.tasks){let o=bu(r);o&&n.push(o)}return n}catch{return[]}}function Te(e){H(Be);let t={tasks:e},n=Nt.join(Be,`.tasks.${process.pid}.${Sa.randomBytes(4).toString("hex")}.tmp`);gn.writeFileSync(n,JSON.stringify(t,null,2)+`
170
- `,{mode:384}),gn.renameSync(n,Tr)}function Bt(e,t,n){let r=t.trim().toLowerCase();return e.find(o=>o.name===r&&o.ownerPeerKey===n)}function va(){return Sa.randomBytes(4).toString("hex")}function $a(e){H(Be),gn.writeFileSync(Ir,JSON.stringify(e,null,2)+`
171
- `,{mode:384})}function Ca(e){let t=Eo();t.push(e),$a(t)}function Eo(){try{let e=gn.readFileSync(Ir,"utf8"),t=JSON.parse(e);return Array.isArray(t)?t.filter(n=>n&&typeof n=="object"&&typeof n.ownerPeerKey=="string"&&typeof n.name=="string"):[]}catch{return[]}}function Ra(e){if(e<=0)return{batch:[],remainingAfter:Eo().length};let t=Eo();if(t.length===0)return{batch:[],remainingAfter:0};let n=t.slice(0,e),r=t.slice(e);return $a(r),{batch:n,remainingAfter:r.length}}function ku(){return p(["Cowork \u2014 scheduled shell tasks (gateway must be running).","","add <name> <schedule> -- <command\u2026>"," schedule: ondemand | hourly [:MM] | daily HH:MM | weekdays HH:MM | weekly <mon\u2026sun> HH:MM","set <name> cmd -- <command\u2026> | schedule <\u2026> | out <path> | cwd <path>"," notify self|wa|tg|all|none"," attach on|off \u2014 send run log as a file with notify messages"," files clear | <glob/path \u2026> \u2014 optional artifacts (basename * ?); quote paths with spaces","list | show <name> | run <name> | enable <name> | disable <name> | remove <name>","","Output logs: task outputDir (default ~/Cowork/<name>). Missed times catch up when the gateway is back."].join(`
172
- `))}function Ma(e){let t=[],n=0;for(;n<e.length;){for(;n<e.length&&/\s/.test(e[n]);)n+=1;if(n>=e.length)break;if(e[n]==='"'){let o=n+1,s=e.indexOf('"',o);if(s===-1){t.push(e.slice(o));break}t.push(e.slice(o,s)),n=s+1;continue}let r=n;for(;r<e.length&&!/\s/.test(e[r]);)r+=1;t.push(e.slice(n,r)),n=r}return t}function Su(e){let t=" -- ",n=e.indexOf(t);if(n===-1)return{error:'Missing " -- " before the command. Example: /cowork add tick weekdays 09:00 -- date'};let r=e.slice(0,n).trim(),o=e.slice(n+t.length).trim();if(!o)return{error:"Command after -- is empty."};let s=r.split(/\s+/).filter(Boolean);if(s.length<2)return{error:"Usage: /cowork add <name> <schedule\u2026> -- <command\u2026>"};let i=s[0],a=s.slice(1);return{name:i,scheduleWords:a,command:o}}function Ia(e,t){let n=e.trim();if(!n||/^help$/i.test(n))return ku();let r=n.split(/\s+/)[0].toLowerCase();if(/^list$/i.test(r)){let u=$e().filter(d=>d.ownerPeerKey===t);if(u.length===0)return p("(no cowork tasks for this chat)");let c=u.map(d=>{let m=d.enabled?"":" (disabled)";return`${ge}${d.name} ${mn(d.schedule)} notify=${d.notify}${m}`});return p(c.join(`
173
- `))}let o=n.match(/^show\s+(\S+)\s*$/i);if(o){let u=o[1],c=$e();fn(c);let d=Bt(c,u,t);if(!d)return p(`Unknown task "${u}". /cowork list`);let m=cr(d),y=[`name: ${d.name}`,`id: ${d.id}`,`schedule: ${mn(d.schedule)}`,`enabled: ${d.enabled}`,`notify: ${d.notify}`,`attachLog: ${d.attachLog}`,`files: ${d.attachFiles.length?d.attachFiles.join(", "):"(none)"}`,`cwd: ${d.cwd||"(session cwd)"}`,`out: ${d.outputDir}`,`cmd: ${d.command}`,`last slot: ${m?new Date(m).toLocaleString():"(never)"}`];return p(y.join(`
174
- `))}if(/^add$/i.test(r)){let u=n.slice(3).trim(),c=Su(u);if("error"in c)return p(c.error);let d=xa(c.name);if(!d.ok)return p(d.error);let m=Mo(c.scheduleWords);if(!m.ok)return p(m.error);let y=$e();if(Bt(y,d.name,t))return p(`Task "${d.name}" already exists. Remove it first or pick another name.`);let g=q(t),f=Ao(d.name),h={id:va(),name:d.name,ownerPeerKey:t,command:c.command,cwd:"",outputDir:f,schedule:m.schedule,enabled:!0,notify:"self",attachLog:!1,attachFiles:[],lastCompletedSlotMs:null,createdAtMs:Date.now()};return y.push(h),Te(y),p([`Saved cowork task "${d.name}" (${mn(m.schedule)}).`,`Output: ${f}`,`Notify: self \u2014 change with /cowork set ${d.name} notify wa|tg|all|none`].join(`
175
- `))}let s=n.match(/^run\s+(\S+)\s*$/i);if(s){let u=s[1].toLowerCase(),c=Bt($e(),u,t);return c?c.enabled?(Ca({ownerPeerKey:t,name:c.name,at:Date.now()}),p(`On-demand run queued for "${c.name}" (runs within ~30s while omnish run is active).`)):p(`Cowork "${c.name}" is disabled. /cowork enable ${c.name}`):p(`Unknown task "${s[1]}". /cowork list`)}let i=n.match(/^(?:remove|rm|del)\s+(\S+)\s*$/i);if(i){let u=i[1],c=$e(),d=c.findIndex(m=>m.name===u.toLowerCase()&&m.ownerPeerKey===t);return d===-1?p(`Unknown task "${u}".`):(c.splice(d,1),Te(c),p(`Removed cowork task "${u.toLowerCase()}".`))}let a=n.match(/^enable\s+(\S+)\s*$/i);if(a)return Ta(a[1],t,!0);let l=n.match(/^disable\s+(\S+)\s*$/i);return l?Ta(l[1],t,!1):/^set$/i.test(r)?$u(n.slice(3).trim(),t):p("Unknown /cowork command. Try /cowork help")}function Ta(e,t,n){let r=$e(),o=Bt(r,e,t);return o?(o.enabled=n,Te(r),p(`Cowork "${o.name}" ${n?"enabled":"disabled"}.`)):p(`Unknown task "${e}".`)}function xu(e){let t=e.toLowerCase();return t==="self"?"self":t==="wa"||t==="whatsapp"?"wa":t==="tg"||t==="telegram"?"tg":t==="all"?"all":t==="none"?"none":null}function vu(e){let t=e.toLowerCase();return t==="on"||t==="true"||t==="1"||t==="yes"?!0:t==="off"||t==="false"||t==="0"||t==="no"?!1:null}function $u(e,t){let n=e.match(/^(\S+)\s+([\s\S]+)$/);if(!n)return p("Usage: /cowork set <name> cmd -- \u2026 | schedule \u2026 | out <path> | cwd <path> | notify \u2026 | attach <on|off> | files \u2026");let r=n[1],o=n[2].trim(),s=$e(),i=Bt(s,r,t);if(!i)return p(`Unknown task "${r}".`);if(o.toLowerCase().startsWith("cmd ")){let a=o.slice(4).trim(),l=" -- ",u=a.indexOf(l),c;if(u!==-1)c=a.slice(u+l.length).trim();else if(a.startsWith("--"))c=a.slice(2).trim();else return p("Usage: /cowork set <name> cmd -- <command\u2026>");return c?(i.command=c,Te(s),p(`Updated command for "${i.name}".`)):p("Command is empty.")}if(o.toLowerCase().startsWith("schedule ")){let a=o.slice(9).trim().split(/\s+/).filter(Boolean),l=Mo(a);return l.ok?(i.schedule=l.schedule,Te(s),p(`Schedule for "${i.name}": ${mn(l.schedule)}`)):p(l.error)}if(o.toLowerCase().startsWith("out ")){let a=o.slice(4).trim(),l=q(t);return i.outputDir=_t(a,l.cwd),Te(s),p(`outputDir: ${i.outputDir}`)}if(o.toLowerCase().startsWith("cwd ")){let a=o.slice(4).trim(),l=q(t);return i.cwd=a?_t(a,l.cwd):"",Te(s),p(`cwd: ${i.cwd||"(session cwd at run time)"}`)}if(o.toLowerCase().startsWith("notify ")){let a=o.slice(7).trim(),l=xu(a);return l?(i.notify=l,Te(s),p(`notify: ${l}`)):p("notify must be self, wa, tg, all, or none.")}if(o.toLowerCase().startsWith("attach ")){let a=o.slice(7).trim(),l=Ma(a);if(l.length!==1)return p("Usage: /cowork set <name> attach on|off");let u=vu(l[0]);return u===null?p("attach must be on or off"):(i.attachLog=u,Te(s),p(`attachLog: ${u}`))}if(o.toLowerCase().startsWith("files ")){let a=o.slice(6).trim(),l=Ma(a);return l.length===1&&l[0].toLowerCase()==="clear"?(i.attachFiles=[],Te(s),p("files: (cleared)")):l.length===0?p("Usage: /cowork set <name> files clear | <pattern \u2026> \u2014 quote paths with spaces"):(i.attachFiles=l,Te(s),p(`files: ${i.attachFiles.join(", ")}`))}return p("Unknown set field. Try: cmd, schedule, out, cwd, notify, attach, files")}function Lo(e){let t=e.trim();if(t==="/computers"||t.startsWith("/computers "))return t.slice(10).trim();if(t==="/pcs"||t.startsWith("/pcs "))return t.slice(4).trim();let n=t.match(/^\/c(?:$|\s+(.*))/);return n?(n[1]??"").trim():null}function C(e){return{kind:"text",body:e}}function Cu(e,t){return`${e}:${t}`}function Ru(e,t){return`${e}:apps:${t}`}async function Mu(e,t){let n=e.trim();if(n===""||/^status$/i.test(n)||/^show$/i.test(n)||/^get$/i.test(n)){let a=R();return Ns({gatewayMode:a.gatewayMode,authPresent:Le(),tokenSet:!!oe(a),allowN:a.allowFrom.length,tgAllowN:a.telegramAllowFrom.length,updateBrief:ir(un())})}if(/^help$/i.test(n))return j(Hr());let r=On(n);if(!r)return Xs();Pn(r);let o=R(),s,i=!1;if(t?.reload){let a=await t.reload();s=a.ok?a.summary:`Reload failed: ${a.error}`}else i=!0;return Us(o.gatewayMode,s,i)}function Tu(e){let t=e.trim();if(!Ze(t))return js();let n=vt(t),r=[];return typeof process.env.TELEGRAM_BOT_TOKEN=="string"&&process.env.TELEGRAM_BOT_TOKEN.trim()&&r.push("TELEGRAM_BOT_TOKEN is set in the environment and overrides config until unset."),n.gatewayMode==="whatsapp"&&r.push('Set gatewayMode to "telegram" or "both" for Telegram to receive messages.'),Ds(r)}async function Ea(e,t,n){let r=q(t),o=vs(n);if(o!==null){let a=$s(r.cwd,o),l=Cs(a);return l.ok?(Bn(t,a),p(`cwd: ${a}`)):p(`cd: ${l.error}`)}let s=await Et(e.shell,n,{timeoutMs:e.syncTimeoutMs,maxBytes:e.syncMaxBytes,cwd:r.cwd}),i=[];return i.push(`$ ${n}`),s.stdout.trim()&&i.push(s.stdout.trimEnd()),s.stderr.trim()&&(i.push("\u2014 stderr \u2014"),i.push(s.stderr.trimEnd())),s.timedOut?i.push(`Timed out (${Math.round(e.syncTimeoutMs/1e3)}s limit).`):s.code!==0&&s.code!==null&&i.push(`Exit ${s.code}`),p(i.join(`
176
- `))}async function wt(e,t,n,r,o,s,i,a,l=null,u=!1,c){let d=s.text.trim(),m=s.peerKey,y=d.match(/^!!\s*(start|stop)\s*$/i);if(y)return y[1].toLowerCase()==="start"?(o.set(m,!0),C(Ys())):(o.set(m,!1),C(p("Free shell mode off.")));if(d.startsWith(e.commandPrefix)){let f=d.slice(e.commandPrefix.length).trim();if(!f)return C(p(`Send ${e.commandPrefix}<command> or /help`));if(!u&&Si(f)){let h=Xr(m,f);if(h!==void 0)return await wt(e,t,n,r,o,{...s,text:h},i,a,l,!0,c)}return C(await Ea(e,m,f))}if(/^\/help\s+files$/i.test(d.trim()))return C(Ur());if(d==="/help"||d==="help")return C(j(Rt(e)));if(d.startsWith("/")){if(/^\/files(?:\s+help)?$/i.test(d.trim()))return C(Ur());let f=d.match(/^\/receive\b(?:\s+(\S+))?$/i);if(f){let w=(f[1]??"status").toLowerCase(),S=new Set(["here","cwd","session","dir"]),I=new Set(["default","global","reset"]);if(S.has(w)){Or(m,"sessionCwd");let _=q(m).cwd;return C(p(`Inbound files will save under this chat\u2019s session folder:
177
- ${_}
178
- (layout: \u2026/<peer>/<date>/<file> under that root).
179
-
180
- Change folder with ${e.commandPrefix}cd \u2026 Send /receive default to use the server config again.`))}return I.has(w)?(Or(m,"default"),C(p("Per-chat inbound folder cleared. Uploads now follow fileReceiveRootMode in config.json (/files)."))):C(w==="help"?Wr():w==="status"?zs(e,m):Wr())}if(d==="/reload"||d==="/restart"){if(!a?.reload)return C(p("Reload is only available while the omnish gateway (omnish run) is running."));let w=await a.reload();return C(p(w.ok?w.summary:`Reload failed: ${w.error}`))}if(d==="/updates"||d.startsWith("/updates ")){let w=d.slice(8).trim().toLowerCase();if(w==="cached"||w==="last"){let I=un();return C(I?Xt(I):p("No update snapshot yet. Send /updates (live check) once, or enable updateCheckEnabled and wait for the scheduled check."))}let S=await dn(Ue(),R());return C(Xt(S))}let h=d.match(/^\/security(?:\s+(\S+))?\s*$/i);if(h){let w=(h[1]??"").toLowerCase(),S=R(),I=ot(S);return C(w==="help"||w==="?"?j(ui()):w==="summary"||w==="brief"?p(Ls(I,"Send /security for the full report.")):w==="tips"?j(ci()):w===""||w==="full"||w==="report"?li(I):p("Unknown /security subcommand. Try /security, /security summary, /security tips, or /security help"))}let x=d.match(/^\/(gateway|gw|mode)\b(?:\s+(.*))?$/i);if(x){let w=(x[2]??"").trim();return C(await Mu(w,a))}if(d==="/config"||d.startsWith("/config ")){let w=d.slice(7).trim();return C(await oa(w,a))}let k=la(d);if(k!==null)return C(await ca(e,k));let $=Lo(d);if($!==null){let w=Xi(e,$,l);return w===null?null:C(w)}let A=d.match(/^\/(send|file)\b(?:\s+([\s\S]+))?$/i);if(A){let w=(A[2]??"").trim();if(!w)return C(jr());let S=_n(w);if(!S)return C(jr());let I=q(m).cwd,_=await Yt(I,S.selectorPart);if(_.length===0)return C(p(`No files matched: ${S.selectorPart}`));let B=await Vt(_);if(!B.ok)return C(p(B.error));let V=[];for(let me of _){let Ee=et(me,e.fileSendMaxBytes);if("error"in Ee)return C(p(Ee.error));V.push({absPath:Ee.absPath,category:Ee.category,mimetype:Ee.mimetype,displayName:Ee.displayName,caption:S.caption})}return V.length===1?{kind:"file",spec:V[0]}:{kind:"files",specs:V}}if(d==="/allowlist"){let w=R();return C(Hs([{label:"allowFrom (WhatsApp)",items:w.allowFrom},{label:"telegramAllowFrom",items:w.telegramAllowFrom}]))}let M=d.match(/^\/allow\b\s+(.+)$/i);if(M)try{let w=An(M[1].trim());return C(Dr(w))}catch(w){return C(p(String(w)))}if(/^\/allow\b\s*$/i.test(d))return C(Ws());let L=d.match(/^\/deny\b\s+(.+)$/i);if(L)try{let w=Ln(L[1].trim());return C(Dr(w))}catch(w){return C(p(String(w)))}if(/^\/deny\b\s*$/i.test(d))return C(Gs());let U=d.match(/^\/(whatsapp|wa|telegram|tg)\b(?:\s+(.*))?$/i);if(U){let w=U[1].toLowerCase(),S=(U[2]??"").trim(),I=w==="whatsapp"||w==="wa";if(w==="telegram"||w==="tg"){let B=S.match(/^token\s+(\S+)\s*$/i);return B?C(Tu(B[1]??"")):S===""||/^help$/i.test(S)?C(j(Bs(R()))):C(Qs())}if(I)return S===""||/^help$/i.test(S)?C(j(_s(e))):C(qs())}let re=d.match(/^\/(cowork|cw)\b(?:\s+([\s\S]*))?$/i);if(re){let w=(re[2]??"").trim();return C(Ia(w,m))}if(d==="/apps"||d.startsWith("/apps "))return C(await Ou(d,m,e,i,r));let Se=Iu(d);if(Se!==null)return C(await Au(Se,m,e,i,s.mediaSavedPath));if(d.startsWith("/bg")){let w=d.slice(3).trim();if(!w)return C(Js());let S=Hi(w);if("error"in S)return C(p(S.error==="empty"?"Usage: /bg <command> or /bg -n <name> <command>.":S.error));let I=q(m).cwd,{id:_,meta:B}=t.spawnJob(e.shell,S.cmd,{cwd:I,name:S.name}),V=B.name?`${_} (${B.name})`:_;return C(p(`Job ${V} started.
181
- [cwd: ${I}]
182
- /log ${B.name??_}
183
- /tail ${B.name??_}`))}if(d==="/jobs"){let w=t.list().slice(0,20);return w.length===0?C(p("(no jobs yet)")):C(p(w.map(S=>{let I=S.finishedAt&&S.startedAt?`${((Date.parse(S.finishedAt)-Date.parse(S.startedAt))/1e3).toFixed(1)}s`:"\u2026";return`${S.name?`${S.id} ${S.name}`:S.id} ${S.status} exit=${S.exitCode??"?"} ${I}
184
- ${S.cmd.slice(0,120)}${S.cmd.length>120?"\u2026":""}`}).join(`
185
-
186
- `)))}let we=d.match(/^\/log\s+(\S+)(?:\s+(\d+))?\s*$/i);if(we){let w=we[1],S=t.resolveJobRef(w);if(!S.ok)return C(p(S.error));let I=S.id,_=we[2]?Number.parseInt(we[2],10):e.jobLogTailLines,B=Number.isFinite(_)&&_>0?Math.min(_,500):e.jobLogTailLines;return C(p(t.tailLog(I,B)))}let Ke=d.match(/^\/tail\s+(\S+)\s*$/i);if(Ke){let w=Ke[1],S=t.resolveJobRef(w);if(!S.ok)return C(p(S.error));let I=S.id,_=Cu(m,I),B=n.get(_)??0,{text:V,nextOffset:me}=t.readSince(I,B);return n.set(_,me),C(V?p(V.trimEnd()||"(no new output)"):p(`(no new output; offset ${me})`))}let Xe=d.match(/^\/kill\s+(\S+)\s*$/i);if(Xe){let w=Xe[1],S=t.resolveJobRef(w);return S.ok?C(p(t.kill(S.id))):C(p(S.error))}let T=d.match(/^\/(shortcut|shortcuts|alias|aliases)\b(?:\s+([\s\S]*))?$/i);if(T){let w=T[1].toLowerCase(),S=(T[2]??"").trim();return w==="shortcuts"&&!S?S="list":((w==="alias"||w==="aliases")&&!S||w==="shortcut"&&!S)&&(S="help"),C(Lu(S,m))}if(!u){let w=d.trim().match(/^\/([a-zA-Z0-9][a-zA-Z0-9_-]{0,31})\s*$/);if(w){let S=w[1],I=Xr(m,S);if(I!==void 0)return await wt(e,t,n,r,o,{...s,text:I},i,a,l,!0,c)}}return C(Vs(e))}let g=d.match(/^>(\S+)\s*(.*)$/s);if(g){let f=g[1],h=g[2]??"",x=await i.writeNamedLine(m,f,h);return x?C(p(x)):null}return await i.writeFocusedLine(m,d)?null:o.get(m)&&d?C(await Ea(e,m,d)):e.chatLlmFallbackEnabled&&e.chatLlmShellCommand.trim().length>0&&c?.onPlainTextLlmFallback?(c.onPlainTextLlmFallback(m,d),null):C(Ks(e))}function Iu(e){return e==="/run"||e.startsWith("/run ")?e.slice(4).trim():e==="/r"||e.startsWith("/r ")?e.slice(2).trim():null}function Eu(e){let t=e.trim(),n=t.match(/^(\S+)\s+(?:--queue|-q)\s+([\s\S]+)$/i);if(n)return{recipe:n[1],task:n[2],queued:!0};let r=t.match(/^(\S+)\s+([\s\S]+)$/);return r?{recipe:r[1],task:r[2],queued:!1}:null}async function Au(e,t,n,r,o){let s=e.trim();if(!s||/^help$/i.test(s))return j(ri());let i=/^list\b([\s\S]*)$/i.exec(s);if(i){let g=(i[1]??"").trim(),{filter:f,bad:h}=Ri(g);if(h)return p(`Unknown /run list suffix: "${h}". Use: list | list --chat | list -p | list --global | list -g`);if(f==="merged")return oi(Ei(t,n));let x=Ti(t,n,f);return ai(x,f)}let a=/^show\b([\s\S]*)$/i.exec(s);if(a){let g=(a[1]??"").trim(),{mode:f,remainder:h}=Ci(g),x=/^(\S+)\s*$/i.exec(h);if(!x?.[1])return p("Usage: /run show <name> \u2014 or show --global|-g|--chat|-p <name> (-g shared, -p private)");let k=x[1];if(f==="resolved"){let re=Re(t,n,k);return re?zr(re):qr(k)}let $=f==="global"?"global":"chat",A=Qe($,t,n,k);if(A)return zr(A,$==="global"?"From gateway-shared recipes (--global).":"From this chat only (--chat).");let M=$==="chat"&&Qe("global",t,n,k)?`
187
- (Gateway-shared recipe exists: /run show --global ${k})`:"",L=$==="global"&&Qe("chat",t,n,k)?`
188
- (This chat stores an override: /run show --chat ${k})`:"",U=$==="global"?`Unknown recipe "${k}" in gateway-shared storage.`:`Unknown recipe "${k}" in this chat storage.`;return p(`${U}${M}${L}`)}let l=/^add\b([\s\S]*)$/i.exec(s);if(l){let{scope:g,remainder:f}=lo((l[1]??"").trim()),h=f.match(/^(\S+)\s+([\s\S]+)$/);if(!h)return p(g==="global"?'Usage: /run add --global <name> <command\u2026> [--template "\u2026"] \u2014 cmd must reference "$OMNISH_TASK"':"Usage: /run add [--global|-g|--chat|-p] <name> <command\u2026> \u2014 see /run help");try{let x=co(h[2],n.recipesMacroDefaultCommand);uo(t,h[1],x,g);let k=gt(h[1]),$=k.ok?k.normalized:h[1].toLowerCase(),A=Qe(g,t,n,$)??Re(t,n,$);return A?en($,A,g):p("Recipe save failed.")}catch(x){return p(String(x))}}let u=/^set\b([\s\S]*)$/i.exec(s);if(u){let{scope:g,remainder:f,explicit:h}=ao((u[1]??"").trim()),x=Mi(f);if(x){if(h)return p("Cannot combine a leading scope flag (-g, --global, --chat, -p) with a trailing scope flag on the same line.");let A=gt(x.name);if(!A.ok)return p(A.error);let M=po(t,A.normalized,x.target,n);if(!M.ok)return p(M.error);if(M.kind==="noop")return p(M.message);let L=Qe(M.target,t,n,A.normalized)??Re(t,n,A.normalized);return L?en(A.normalized,L,M.target):p("Recipe scope update failed.")}let k=f.match(/^(\S+)\s*$/);if(k?.[1]&&h){let A=gt(k[1]);if(!A.ok)return p(A.error);let M=po(t,A.normalized,g,n);if(!M.ok)return p(M.error);if(M.kind==="noop")return p(M.message);let L=Qe(M.target,t,n,A.normalized)??Re(t,n,A.normalized);return L?en(A.normalized,L,M.target):p("Recipe scope update failed.")}let $=f.match(/^(\S+)\s+([\s\S]+)$/);if(!$)return p(g==="global"?'Usage: /run set --global <name> <command\u2026> [--template "\u2026"] \u2014 or scope-only: /run set --global <name> | /run set <name> -g':"Usage: /run set [--global|-g|--chat|-p] <name> <command\u2026> \u2014 or move scope without changing the body: /run set -g <name> | /run set <name> -p \u2014 see /run help");try{let A=co($[2],n.recipesMacroDefaultCommand);uo(t,$[1],A,g);let M=gt($[1]),L=M.ok?M.normalized:$[1].toLowerCase(),U=Qe(g,t,n,L)??Re(t,n,L);return U?en(L,U,g):p("Recipe save failed.")}catch(A){return p(String(A))}}let c=/^(?:remove|rm|del)\b([\s\S]*)$/i.exec(s);if(c){let{scope:g,remainder:f}=lo((c[1]??"").trim()),h=f.match(/^(\S+)\s*$/);if(!h?.[1])return p(g==="global"?"Usage: /run remove --global <name> (aliases: rm, del)":"Usage: /run remove [--global|-g|--chat|-p] <name>");let x=h[1];return Ii(t,x,g)?si(x,g):g==="chat"&&Qe("global",t,n,x)?p(`No recipe "${x}" in this chat. There is a gateway-shared recipe with that name \u2014 remove it with:
189
- /run remove --global ${x}`):ii(x,g)}let d=/^queue\s+load\b([\s\S]*)$/i.exec(s);if(d){let g=(d[1]??"").trim(),f=Oi(n),h=null,x=/^json(?:\s+([\s\S]+))?$/i.exec(g);if(x){let M=(x[1]??"").trim();if(!M)return p('Usage: /run queue load json [{"recipe":"\u2026","task":"\u2026"}, \u2026] \u2014 or { "tasks": [ \u2026 ] }');h=M}else if(g.length>0){let M=g;(M.startsWith('"')&&M.endsWith('"')||M.startsWith("'")&&M.endsWith("'"))&&(M=M.slice(1,-1));let L=q(t).cwd,U=await Yt(L,M);if(U.length===0)return p(`No files matched: ${M}`);if(U.length>1)return p("Queue load: specify a single JSON file.");let re=await Vt(U);if(!re.ok)return p(re.error);let Se=mo(U[0],f);if(!Se.ok)return p(Se.error);h=Se.text}else if(o){let M=mo(o,f);if(!M.ok)return p(M.error);h=M.text}else return p("Usage: /run queue load <file.json> \u2014 or /run queue load json [\u2026] \u2014 or attach a file with caption /run queue load");let k=Pi(h);if(!k.ok)return p(k.error);let $=Fi(t,n,k.jobs);if(!$.ok)return p($.error);let A=[];for(let M of $.items)A.push(r.enqueueQueuedRun(t,M,n));return p(A.join(`
190
- `))}if(/^queue$/i.test(s))return p(r.runQueueStatus(t));if(/^queue\s+resume\s*$/i.test(s))return p(r.resumeRunQueue(t,n));let m=Eu(s);if(m){let{recipe:g,task:f,queued:h}=m,x=Re(t,n,g);if(!x)return qr(g);let k=x.taskEnv??"OMNISH_TASK";if(!It(x.command,k))return p(`Recipe "${g}" command must reference "$${k}".`);let $=Vn(f,n.recipesMaxTaskChars);if(!$.ok)return p($.error);let A=x.promptTemplate?Kn(x.promptTemplate,k,$.task):$.task,M={[k]:A};if(h)return p(r.enqueueQueuedRun(t,{command:x.command,extraEnv:M,recipeLabel:g},n));let L=Xn(g);return p(r.start(t,L,x.command,n,M))}let y=s.match(/^(\S+)$/);if(y){let g=y[1],f=g.toLowerCase();return f==="add"||f==="set"?p('Usage: /run add <name> cmd [--template "\u2026"] \u2014 cmd must include "$OMNISH_TASK"'):f==="show"?p("Usage: /run show <name> \u2014 optional show --global|-g|--chat|-p <name>"):f==="remove"||f==="rm"||f==="del"?p("Usage: /run remove [--global|-g|--chat|-p] <name>"):Re(t,n,f)?p(`Usage: /run ${g} <task text\u2026> \u2014 replaces <<<OMNISH_TASK>>> / $OMNISH_TASK in stored templates`):p(`Unknown recipe "${g}". /run list`)}return p("/run: could not parse. /run help")}function Lu(e,t){let n=e.trim();if(!n||/^help$/i.test(n))return j(Gr());let r=/^list\b([\s\S]*)$/i.exec(n);if(r){let l=(r[1]??"").trim(),{filter:u,bad:c}=hi(l);return c?p(`Unknown /shortcut list suffix: "${c}". Use: list | list --chat | list -p | list --global | list -g`):Zs(wi(t,u))}let o=/^show\b([\s\S]*)$/i.exec(n);if(o){let l=(o[1]??"").trim(),{mode:u,remainder:c}=gi(l),d=/^(\S+)\s*$/i.exec(c);if(!d?.[1])return p("Usage: /shortcut show <name> \u2014 or show --global|-g|--chat|-p <name> (-g shared, -p private)");let m=d[1];if(u==="resolved"){let k=bi(t,m);if(!k)return ti(m);let $=k.scope==="global"?"Shared shortcut (all chats use it unless this chat overrides the name).":"This chat only.";return Jr(m,k.body,$)}let y=u==="global"?"global":"chat",g=ze(y,t,m);if(g!==void 0)return Jr(m,g,y==="global"?"From the shared shortcut list (--global / -g).":"From this chat only (--chat / -p).");let f=y==="chat"&&ze("global",t,m)!==void 0?`
191
- (Shared shortcut exists: /shortcut show --global ${m})`:"",h=y==="global"&&ze("chat",t,m)!==void 0?`
192
- (This chat overrides the name: /shortcut show --chat ${m})`:"",x=y==="global"?`Unknown shortcut "${m}" in shared shortcuts.`:`Unknown shortcut "${m}" in this chat.`;return p(`${x}${f}${h}`)}let s=/^add\b([\s\S]*)$/i.exec(n);if(s){let{scope:l,remainder:u}=Kr((s[1]??"").trim()),c=u.match(/^(\S+)\s+([\s\S]+)$/);if(!c)return p(l==="global"?"Usage: /shortcut add --global <name> <command\u2026> (short: add -g <name> <command\u2026>) \u2014 -g shared, -p private":"Usage: /shortcut add [--global|-g|--chat|-p] <name> <command\u2026> \u2014 -g shared, -p private");try{Zr(t,c[1],c[2],l);let d=mt(c[1]),m=d.ok?d.normalized:c[1].trim().toLowerCase(),y=ze(l,t,m)??"";return Zt(m,y,l)}catch(d){return p(String(d))}}let i=/^set\b([\s\S]*)$/i.exec(n);if(i){let{scope:l,remainder:u,explicit:c}=Vr((i[1]??"").trim()),d=yi(u);if(d){if(c)return p("Cannot combine a leading scope flag (-g, --global, --chat, -p) with a trailing flag on the same line.");let g=mt(d.name);if(!g.ok)return p(g.error);let f=eo(t,g.normalized,d.target);if(!f.ok)return p(f.error);if(f.kind==="noop")return p(f.message);let h=ze(f.target,t,g.normalized)??"";return Zt(g.normalized,h,f.target)}let m=u.match(/^(\S+)\s*$/);if(m?.[1]&&c){let g=mt(m[1]);if(!g.ok)return p(g.error);let f=eo(t,g.normalized,l);if(!f.ok)return p(f.error);if(f.kind==="noop")return p(f.message);let h=ze(f.target,t,g.normalized)??"";return Zt(g.normalized,h,f.target)}let y=u.match(/^(\S+)\s+([\s\S]+)$/);if(!y)return p(l==="global"?"Usage: /shortcut set --global <name> <command\u2026> \u2014 or scope-only: /shortcut set -g <name> | /shortcut set <name> -g (-g shared)":"Usage: /shortcut set [--global|-g|--chat|-p] <name> <command\u2026> \u2014 or move scope: /shortcut set -g <name> | /shortcut set <name> -p (-g shared, -p private)");try{Zr(t,y[1],y[2],l);let g=mt(y[1]),f=g.ok?g.normalized:y[1].trim().toLowerCase(),h=ze(l,t,f)??"";return Zt(f,h,l)}catch(g){return p(String(g))}}let a=/^(?:remove|rm|del)\b([\s\S]*)$/i.exec(n);if(a){let{scope:l,remainder:u}=Kr((a[1]??"").trim()),c=u.match(/^(\S+)\s*$/);if(!c?.[1])return p(l==="global"?"Usage: /shortcut remove --global <name> (aliases: rm, del)":"Usage: /shortcut remove [--global|-g|--chat|-p] <name>");let d=c[1];return ki(t,d,l)?ei(d,l):l==="chat"&&ze("global",t,d)!==void 0?p(`No shortcut "${d}" in this chat. There is a shared shortcut with that name \u2014 remove it with:
193
- /shortcut remove --global ${d}`):ni(d,l)}return j(Gr())}async function Ou(e,t,n,r,o){let s=e.slice(5).trim(),i=s.toLowerCase();if(!i||i==="help")return j(Qr());let a=s.match(/^(\S+)\s*(.*)$/s);if(!a)return j(Qr());let l=a[1].toLowerCase(),u=(a[2]??"").trim();switch(l){case"start":{let c=u.match(/^(\S+)\s+([\s\S]+)$/);return c?p(r.start(t,c[1],c[2],n)):p("Usage: /apps start <name> <command\u2026>")}case"attach":{let c=u.split(/\s+/)[0];return c?p(r.attach(t,c)):p("Usage: /apps attach <name>")}case"detach":return p(r.detach(t));case"list":return p(r.list(t));case"info":case"get":{let c=u.split(/\s+/)[0];return p(r.info(t,c||void 0))}case"send":{let c=u.match(/^(\S+)\s+([\s\S]+)$/);return c?p(await r.sendText(t,c[1],c[2])):p("Usage: /apps send <name> <text\u2026>")}case"key":{let c=u.match(/^(\S+)\s+([\s\S]+)$/);return c?p(r.sendKey(t,c[1],c[2].trim())):p("Usage: /apps key <name> <KEY[,KEY\u2026]>")}case"tail":{let c=u.match(/^(\S+)(?:\s+(\d+))?\s*$/);if(!c)return p("Usage: /apps tail <name> [lines]");let d=c[2]?Number.parseInt(c[2],10):n.appsLogTailLines;return p(r.tail(t,c[1],d))}case"since":{let c=u.split(/\s+/)[0];if(!c)return p("Usage: /apps since <name>");let d=Ru(t,c),m=o.get(d)??0,{text:y,nextOffset:g}=r.readSince(t,c,m);return o.set(d,g),p(y.trimEnd()||"(no new log bytes)")}case"mute":{let c=u.split(/\s+/)[0];return c?p(r.mute(t,c)):p("Usage: /apps mute <name>")}case"unmute":{let c=u.split(/\s+/)[0];return c?p(r.unmute(t,c)):p("Usage: /apps unmute <name>")}case"raw":{let c=u.match(/^(\S+)\s+(on|off)\s*$/i);return c?p(r.setRaw(t,c[1],c[2].toLowerCase()==="on")):p("Usage: /apps raw <name> on|off")}case"resize":{let c=u.trim().split(/\s+/).filter(Boolean);if(c.length<3)return p("Usage: /apps resize <name> <cols> <rows>");let d=c[0],m=Number(c[1]),y=Number(c[2]);return!Number.isFinite(m)||!Number.isFinite(y)?p("cols and rows must be numbers."):p(r.resize(t,d,m,y))}case"stop":{let c=u.split(/\s+/)[0];return c?p(r.stop(t,c)):p("Usage: /apps stop <name>")}case"kill":{let c=u.split(/\s+/)[0];return c?p(r.kill(t,c)):p("Usage: /apps kill <name>")}case"rm":{let c=u.split(/\s+/)[0];return c?p(r.rm(t,c)):p("Usage: /apps rm <name>")}default:return p(`Unknown /apps subcommand "${l}". /apps help`)}}function Oo(e,t,n){return!e.clusterEnabled||Lo(t.trim())!==null?!0:n?Ki(n,e):!1}function Pu(e){return`wa:${e}`}function Po(e){return`tg:${e}`}function Aa(e){return{peerKey:Pu(e.fromJid),text:e.text,waMessageId:e.messageId,mediaSavedPath:e.mediaSavedPath,mediaError:e.mediaError}}import at from"node:fs";import Uu from"node:path";var La={enter:"\r",cr:"\r",lf:`
194
- `,return:"\r",tab:" ",esc:"\x1B",escape:"\x1B",space:" ",backspace:"\x7F",bs:"\x7F",delete:"\x1B[3~",up:"\x1B[A",down:"\x1B[B",right:"\x1B[C",left:"\x1B[D",home:"\x1B[H",end:"\x1B[F",pageup:"\x1B[5~",pagedown:"\x1B[6~","ctrl+c":"","ctrl+d":"","ctrl+z":"","ctrl+l":"\f","ctrl+u":"","ctrl+k":"\v"};function Fu(e){let t="";for(let n=0;n<e.length;n++){let r=e[n];if(r==="\\"&&n+1<e.length){let o=e[n+1];if(o==="x"&&n+3<e.length){let s=e.slice(n+2,n+4);if(/^[0-9a-fA-F]{2}$/.test(s)){t+=String.fromCharCode(Number.parseInt(s,16)),n+=3;continue}}if(o==="r"){t+="\r",n++;continue}if(o==="n"){t+=`
195
- `,n++;continue}if(o==="t"){t+=" ",n++;continue}if(o==="\\"){t+="\\",n++;continue}}t+=r}return t}function Nu(e){let t=e.trim();if(!t)return"";if(t.startsWith("\\"))return Fu(t);let n=t.toLowerCase();if(La[n])return La[n];let r=t.match(/^\^([A-Za-z])$/);if(r){let s=r[1].toUpperCase().charCodeAt(0);if(s>=64&&s<=95)return String.fromCharCode(s-64)}let o=n.match(/^ctrl\+(.+)$/);if(o){let s=o[1];if(s.length===1){let i=s.toUpperCase().charCodeAt(0);if(i>=64&&i<=95)return String.fromCharCode(i-64)}}return t}function Fo(e){let t=e.split(",").map(n=>n.trim()).filter(Boolean);return t.length===0?"":t.map(n=>Nu(n)).join("")}var hn="\x1B";function Oa(e){let t=e;return t=t.replace(new RegExp(`${hn}\\[[\\d;?]*[ -/]*[@-~]`,"g"),""),t=t.replace(new RegExp(`${hn}\\][^${hn}\\u0007]*(?:\\u0007|${hn}\\\\)`,"g"),""),t=t.replace(new RegExp(`${hn}[@-Z\\\\-_]`,"g"),""),t=t.replace(/\u0007/g,""),t}function _u(e,t){if(e.length<=t)return e?[e]:[];let n=[];for(let r=0;r<e.length;r+=t)n.push(e.slice(r,r+t));return n}function Bu(e,t){return`${e}${t}`}var dr=class{constructor(t){this.opts=t}pending=new Map;timers=new Map;lastFlushEnd=new Map;flushing=new Set;closed=!1;dispose(){this.closed=!0;for(let t of this.timers.values())clearTimeout(t);this.timers.clear(),this.pending.clear(),this.lastFlushEnd.clear(),this.flushing.clear()}push(t,n,r){if(this.closed||this.opts.isMuted(t,n))return;let o=Bu(t,n);this.pending.set(o,(this.pending.get(o)??"")+r);let s=this.timers.get(o);s&&clearTimeout(s);let i=this.opts.getCfg().appsFlushMs,a=setTimeout(()=>{this.timers.delete(o),this.flushNow(o,t,n)},i);this.timers.set(o,a)}async flushNow(t,n,r){if(!(this.closed||this.flushing.has(t))){this.flushing.add(t);try{let o=this.opts.getCfg(),s=o.appsMinIntervalMs,i=this.lastFlushEnd.get(t)??0,a=Math.max(0,i+s-Date.now());if(a>0&&await new Promise(y=>setTimeout(y,a)),this.closed)return;let l=this.pending.get(t)??"";if(this.pending.delete(t),!l||(this.opts.isRaw(n,r)||(l=Oa(l)),!l.trim()))return;let u=o.appsMaxFlushBytes;if(l.length>u){let y=l.slice(u);l=l.slice(0,u)+`
196
- [\u2026+${y.length} chars; /apps since ${r} to read more]`,this.pending.set(t,(this.pending.get(t)??"")+y)}let d=(this.opts.getRunningCount(n)>1?`[${r}] `:"")+l,m=_u(d,o.appsMaxWaChars);for(let y of m){if(this.closed)break;try{await this.opts.send(n,y)}catch{break}}this.lastFlushEnd.set(t,Date.now())}finally{this.flushing.delete(t),!this.closed&&(this.pending.get(t)??"").length>0&&queueMicrotask(()=>void this.flushNow(t,n,r))}}}};import Hu from"node:fs";import Du from"node:path";import*as Pa from"node-pty";var ju=1024*1024,pr=class e{peerKey;name;command;cwd;envKeysCount;logPath;term=null;logStream=null;disposeData=null;disposeExit=null;exited=!1;ringChunks=[];ringBytes=0;constructor(t){this.peerKey=t.peerKey,this.name=t.name,this.command=t.command,this.cwd=t.cwd,this.envKeysCount=t.envKeysCount,this.logPath=t.logPath}appendRing(t){for(this.ringChunks.push(t),this.ringBytes+=t.length;this.ringBytes>ju&&this.ringChunks.length>0;){let n=this.ringChunks.shift();this.ringBytes-=n.length}}static start(t){H(qt(t.peerKey));let n=Du.join(qt(t.peerKey),`${t.name}.log`),r=Hu.createWriteStream(n,{flags:"a",mode:384}),o={...process.env,TERM:"xterm-256color",COLORTERM:"truecolor",...t.cwd?{PWD:t.cwd}:{},...t.extraEnv??{}},s=Pa.spawn(t.shell,["-lc",t.command],{name:"xterm-256color",cols:t.cols,rows:t.rows,cwd:t.cwd,env:o}),i=new e({...t,logPath:n});return i.term=s,i.logStream=r,i.disposeData=s.onData(a=>{let l=Buffer.from(a,"utf8");i.appendRing(l),r.write(a),t.router.push(t.peerKey,t.name,a)}),i.disposeExit=s.onExit(a=>{i.exited||(i.exited=!0,i.cleanupTerm(),t.onExit({exitCode:a.exitCode,signal:a.signal}))}),i}get alive(){return this.term!==null&&!this.exited}get ringByteCount(){return this.ringBytes}write(t){this.term?.write(t)}resize(t,n){this.term?.resize(t,n)}kill(t){if(this.term)try{this.term.kill(t)}catch{}}cleanupTerm(){this.disposeData?.dispose(),this.disposeData=null,this.disposeExit?.dispose(),this.disposeExit=null,this.term=null,this.logStream?.end(),this.logStream=null}destroy(){if(this.exited){this.cleanupTerm();return}this.exited=!0,this.kill("SIGHUP"),this.cleanupTerm()}};var Wu=/^[a-zA-Z0-9_-]{1,32}$/;function ke(e){return Wu.test(e)?null:"Session name must be 1\u201332 chars: letters, digits, _ or -."}function ye(e,t){return`${e}:${t}`}function Gu(e){let t=e.lastIndexOf(":");return t<=0?null:{peerKey:e.slice(0,t),name:e.slice(t+1)}}function No(e){return new Promise(t=>setTimeout(t,e))}function Ju(e,t){return e===0&&(t===0||t==null)}async function _o(e,t,n){let r=n.appsSubmitDelayMs,o=n.appsClearInputDelayMs,i=n.appsClearInput!==!1?Fo(n.appsClearInputSequence||"^A,^K"):"",l=t.replace(/\r\n/g,`
197
- `).replace(/\r/g,"").split(`
198
- `);i&&(o>0&&await No(o),e.write(i));for(let u of l)u.length>0&&e.write(u),r>0&&await No(r),e.write("\r"),i&&(o>0&&await No(o),e.write(i))}function zu(e,t){try{let r=at.readFileSync(e,"utf8").split(/\r?\n/);return r.slice(Math.max(0,r.length-t)).join(`
199
- `).trimEnd()}catch{return""}}var Ht=class{constructor(t,n){this.getCfg=t;this.send=n,this.router=new dr({getCfg:()=>this.getCfg(),send:n,isMuted:(r,o)=>this.muted.has(ye(r,o)),isRaw:(r,o)=>this.rawMode.has(ye(r,o)),getRunningCount:r=>this.countPeerRunning(r)})}sessions=new Map;focus=new Map;muted=new Set;rawMode=new Set;router;killTimers=new Set;send;runQueue=new Map;activeQueuedRunHead=new Map;runQueuePaused=new Map;countPeerRunning(t){let n=`${t}:`,r=0;for(let[o,s]of this.sessions)o.startsWith(n)&&s.alive&&r++;return r}countTotalRunning(){let t=0;for(let n of this.sessions.values())n.alive&&t++;return t}dispose(){for(let t of this.killTimers)clearTimeout(t);this.killTimers.clear(),this.stopAll(),this.router.dispose()}stopAll(){for(let t of this.sessions.values())t.destroy();this.sessions.clear(),this.focus.clear(),this.runQueue.clear(),this.activeQueuedRunHead.clear(),this.runQueuePaused.clear()}getFocus(t){return this.focus.get(t)??null}logPath(t,n){return Uu.join(qt(t),`${n}.log`)}getSession(t,n){return this.sessions.get(ye(t,n))}removeSessionRecord(t,n){this.sessions.delete(ye(t,n)),this.focus.get(t)===n&&this.focus.set(t,null)}async writeFocusedLine(t,n){let r=this.focus.get(t);if(!r)return!1;let o=this.getSession(t,r);return o?.alive?(await _o(o,n,this.getCfg()),!0):!1}async writeNamedLine(t,n,r){let o=ke(n);if(o)return o;let s=this.getSession(t,n);return s?.alive?(await _o(s,r,this.getCfg()),null):`No app session "${n}". /apps list`}enqueueQueuedRun(t,n,r){let o=this.runQueue.get(t)??[];o.push(n),this.runQueue.set(t,o);let s=o.length,i=this.runQueuePaused.get(t)??!1,a=this.activeQueuedRunHead.get(t)??null,l=a?.sessionName??null;if(l?this.getSession(t,l)?.alive??!1:!1){let c=a?.recipeLabel?` (recipe: ${a.recipeLabel})`:"",d=s-1,m=d>0?`${d} other job(s) in line before this one.`:"Next in line after the active run finishes.";return`Queued "${n.recipeLabel}" (wait slot ${s} behind active ${l}${c}). ${m}`}return i?`Queued "${n.recipeLabel}" (position ${s}). Run queue is paused \u2014 /run queue resume to continue.`:this.drainNextQueuedRun(t,r)}resumeRunQueue(t,n){this.runQueuePaused.set(t,!1);let r=this.activeQueuedRunHead.get(t)??null,o=r?.sessionName??null;if(o?this.getSession(t,o)?.alive??!1:!1){let a=r?.recipeLabel?` (recipe: ${r.recipeLabel})`:"";return`Run queue: session "${o}"${a} is still running. Wait for exit code=0 and signal=0 before the next starts.`}return(this.runQueue.get(t)??[]).length===0?"Run queue is empty.":(this.activeQueuedRunHead.set(t,null),this.drainNextQueuedRun(t,n))}runQueueStatus(t){let n=this.runQueue.get(t)??[],r=this.activeQueuedRunHead.get(t)??null,o=r?.sessionName??null,s=this.runQueuePaused.get(t)??!1,i=o?this.getSession(t,o)?.alive??!1:!1,a=["Run queue (this chat)"];if(o&&i){let l=r?.recipeLabel?` \xB7 recipe: ${r.recipeLabel}`:"";a.push(`Active: ${o}${l}`)}else o?a.push(`Active: ${o} (exiting or stale)`):a.push("Active: (none)");if(a.push(`Pending: ${n.length} (waiting only \u2014 not counting the active run above)`),a.push(`Paused: ${s}`),n.length>0){a.push("Waiting (FIFO order):");for(let u=0;u<Math.min(n.length,20);u++)a.push(`${u+1}) ${n[u].recipeLabel}`);n.length>20&&a.push(`\u2026 and ${n.length-20} more`)}return a.push("Next auto-starts only after exit code=0 and signal=0."),a.push("Commands: /run queue resume"),a.join(`
200
- `)}drainNextQueuedRun(t,n){let r=this.runQueue.get(t);if(!r||r.length===0)return"";let o=r.shift();r.length===0?this.runQueue.delete(t):this.runQueue.set(t,r);let s=Xn(o.recipeLabel),i=this.runQueue.get(t)?.length??0,a=this.start(t,s,o.command,n,o.extraEnv);if(!a.includes("started and attached"))return r.unshift(o),this.runQueue.set(t,r),this.runQueuePaused.set(t,!0),this.activeQueuedRunHead.set(t,null),`${a}
201
- Run queue paused; fix the issue, then /run queue resume.`;this.activeQueuedRunHead.set(t,{sessionName:s,recipeLabel:o.recipeLabel});let u=i>0?`
202
- Run queue: started head above; ${i} job(s) still waiting in FIFO.`:`
203
- Run queue: started head above; no further queued jobs.`;return`${a}${u}`}start(t,n,r,o,s){let i=ke(n);if(i)return i;if(!r.trim())return`Usage: /apps start <name> <command\u2026>
204
- Example: /apps start sh bash`;if(this.sessions.has(ye(t,n)))return`Session "${n}" already exists. /apps stop ${n} or pick another name.`;if(this.countPeerRunning(t)>=o.appsMaxSessions)return`Per-chat app limit (${o.appsMaxSessions}) reached. /apps stop or /apps rm an old session.`;if(this.countTotalRunning()>=o.appsMaxSessionsTotal)return`Global app limit (${o.appsMaxSessionsTotal}) reached. Stop sessions in other chats first.`;z();let a=q(t).cwd,l={...process.env,TERM:"xterm-256color",COLORTERM:"truecolor",...a?{PWD:a}:{},...s??{}},u;try{u=pr.start({peerKey:t,name:n,shell:o.shell,command:r.trim(),cwd:a,cols:o.appsCols,rows:o.appsRows,envKeysCount:Object.keys(l).length,extraEnv:s,router:this.router,onExit:c=>{this.handleSessionExit(t,n,c)}})}catch(c){return`App spawn failed: ${String(c)}
205
- If install skipped native builds: pnpm approve-builds && pnpm install (needs @whiskeysockets/baileys, sharp, protobufjs, esbuild, node-pty).`}return this.sessions.set(ye(t,n),u),this.focus.set(t,n),`App "${n}" started and attached.
206
- [cwd: ${a}]
207
- Plain DMs go to this session until /apps detach. Use >othername text for another session.`}async handleSessionExit(t,n,r){let o=ye(t,n);if(!this.sessions.has(o))return;let s=this.activeQueuedRunHead.get(t)?.sessionName===n,i=this.focus.get(t)===n;this.sessions.delete(o),i&&this.focus.set(t,null),this.muted.delete(o),this.rawMode.delete(o);let a=r.signal!=null?` signal=${r.signal}`:"",l=i?" (detached)":"",u=`[${n}] exited code=${r.exitCode}${a}${l}`;try{await this.send(t,u)}catch{}if(!s)return;this.activeQueuedRunHead.set(t,null);let c=this.getCfg(),d=Ju(r.exitCode,r.signal),m=this.runQueue.get(t)?.length??0;if(d){if(this.runQueuePaused.set(t,!1),m>0){let y=this.drainNextQueuedRun(t,c);if(y)try{await this.send(t,y)}catch{}}return}if(this.runQueuePaused.set(t,!0),m>0){let y=`Run queue paused (exit code=${r.exitCode}${r.signal!=null?` signal=${r.signal}`:""}). ${m} run(s) waiting. /run queue resume to continue.`;try{await this.send(t,y)}catch{}}}attach(t,n){let r=ke(n);return r||(this.getSession(t,n)?.alive?(this.focus.set(t,n),`Attached to "${n}". Plain messages (no ! or / or >) go to this app. /apps detach to stop.`):`No session "${n}". /apps list`)}detach(t){return this.focus.set(t,null),"Detached (attach mode off)."}list(t){let n=[];for(let[s,i]of this.sessions){let a=Gu(s);if(!a||a.peerKey!==t||!i.alive)continue;let l=this.focus.get(t)===a.name?" *":"";n.push(`${a.name}${l}`)}if(n.length===0)return"(no app sessions; /apps start <name> <cmd>)";let r=this.focus.get(t);return`${r?`attached: ${r}`:"(no focus)"}
208
- App sessions:
209
- ${n.join(`
210
- `)}`}info(t,n){let r=this.getCfg(),o=n??this.focus.get(t)??"";if(!o)return"Usage: /apps info <name> (or /apps get <name>, or attach first)";let s=ke(o);if(s)return s;let i=this.getSession(t,o),a=this.logPath(t,o),l=0;try{l=at.statSync(a).size}catch{}let u=this.muted.has(ye(t,o)),c=this.rawMode.has(ye(t,o));return[`session: ${o}`,`alive: ${i?.alive??!1}`,`cmd: ${i?.command??"(unknown)"}`,`cwd: ${i?.cwd??"(unknown)"}`,`env keys: ${i?.envKeysCount??"?"}`,`terminal size: ${r.appsCols}x${r.appsRows}`,`ring bytes (approx): ${i?.ringByteCount??0}`,`log: ${a} (${l} bytes)`,`mute outbound: ${u}`,`raw outbound: ${c}`].join(`
211
- `)}async sendText(t,n,r){let o=ke(n);if(o)return o;let s=this.getSession(t,n);if(!s?.alive)return`No session "${n}".`;let i=r.replace(/\r\n/g,`
212
- `);return await _o(s,i,this.getCfg()),`Sent to "${n}" (${i.length} chars + Enter per line).`}sendKey(t,n,r){let o=ke(n);if(o)return o;let s=this.getSession(t,n);if(!s?.alive)return`No session "${n}".`;let i=Fo(r);return i?(s.write(i),`Sent keys to "${n}".`):"Empty key sequence. Example: /apps key sh Enter or ^C,Up,Enter"}tail(t,n,r){let o=ke(n);if(o)return o;let s=this.logPath(t,n);if(!at.existsSync(s))return`(no log file for ${n})`;let i=this.getCfg(),a=Number.isFinite(r)&&r>0?Math.min(500,r):i.appsLogTailLines;return zu(s,a)||"(empty log)"}readSince(t,n,r){let o=this.logPath(t,n);try{let i=at.statSync(o).size,a=at.openSync(o,"r");try{let l=Math.min(r,i),u=i-l,c=Buffer.alloc(u);return u>0&&at.readSync(a,c,0,u,l),{text:c.toString("utf8"),nextOffset:i}}finally{at.closeSync(a)}}catch{return{text:"",nextOffset:r}}}mute(t,n){let r=ke(n);return r||(this.muted.add(ye(t,n)),`Muted chat output for "${n}" (log still grows). /apps unmute ${n}`)}unmute(t,n){let r=ke(n);return r||(this.muted.delete(ye(t,n)),`Unmuted "${n}".`)}setRaw(t,n,r){let o=ke(n);if(o)return o;let s=ye(t,n);return r?this.rawMode.add(s):this.rawMode.delete(s),`raw chat output for "${n}": ${r?"on (ANSI kept)":"off (stripped)"}.`}resize(t,n,r,o){let s=ke(n);if(s)return s;let i=this.getSession(t,n);if(!i?.alive)return`No session "${n}".`;let a=Math.min(500,Math.max(20,Math.floor(r))),l=Math.min(200,Math.max(5,Math.floor(o)));return i.resize(a,l),`Resized "${n}" to ${a}x${l}.`}stop(t,n){let r=ke(n);if(r)return r;let o=this.getSession(t,n);if(!o?.alive)return`No running session "${n}".`;o.kill("SIGTERM");let s=ye(t,n),i=setTimeout(()=>{this.killTimers.delete(i);let a=this.getSession(t,n);a?.alive&&a.kill("SIGKILL")},5e3);return this.killTimers.add(i),`SIGTERM sent to "${n}". SIGKILL in 5s if still running.`}kill(t,n){let r=ke(n);if(r)return r;let o=this.getSession(t,n);return o?.alive?(o.kill("SIGKILL"),`SIGKILL sent to "${n}".`):`No running session "${n}".`}rm(t,n){let r=ke(n);if(r)return r;if(this.getSession(t,n)?.alive)return`Session "${n}" is still running. /apps stop ${n} first, then /apps rm ${n}.`;this.removeSessionRecord(t,n),this.muted.delete(ye(t,n)),this.rawMode.delete(ye(t,n));let s=this.logPath(t,n);try{at.rmSync(s,{force:!0})}catch{}return`Removed "${n}" metadata and log.`}};import yn from"node:fs";import wn from"node:path";function Fa(e,t,n){if(e==="none")return[];let r=new Set;if((e==="self"||e==="all")&&r.add(t),e==="wa"||e==="all")for(let o of n.allowFrom){let s=G(String(o));s&&r.add(`wa:${Qt(s)}`)}if(e==="tg"||e==="all")for(let o of n.telegramAllowFrom){let s=ue(String(o));s&&r.add(`tg:${s}`)}return[...r]}var qu=8,Na=12e4,_a=10,Qu=1e3;function Yu(e){return e.toISOString().replace(/[:.]/g,"-")}function Vu(e){let t="";for(let n of e)n==="*"?t+="[^/\\\\]*":n==="?"?t+="[^/\\\\]":/[.+^${}()|[\]\\]/.test(n)?t+=`\\${n}`:t+=n;return new RegExp(`^${t}$`)}function Ku(e,t,n){let r=_t(e,t),o=wn.dirname(r),s=wn.basename(r);if(o.includes("*")||o.includes("?"))return[];if(!s.includes("*")&&!s.includes("?")){try{let u=yn.statSync(r);if(u.isFile()&&u.mtimeMs>=n)return[r]}catch{}return[]}let i;try{i=yn.readdirSync(o)}catch{return[]}let a=Vu(s),l=[];for(let u of i){if(!a.test(u))continue;let c=wn.join(o,u);try{let d=yn.statSync(c);d.isFile()&&d.mtimeMs>=n&&l.push(c)}catch{}}return l}function Ba(e,t,n){let r=et(e,t.fileSendMaxBytes);return"error"in r?{ok:!1,displayName:n??wn.basename(e),reason:r.error.replace(/\.$/,"").toLowerCase()}:{ok:!0,spec:{absPath:r.absPath,category:r.category,mimetype:r.mimetype,displayName:n??r.displayName}}}async function Ha(e,t,n,r){let o=q(t.ownerPeerKey),s=t.cwd.trim()?t.cwd:o.cwd,i=_t(t.outputDir,o.cwd);try{yn.mkdirSync(i,{recursive:!0,mode:448})}catch(W){E.warn({err:String(W),outDir:i},"cowork mkdir outputDir")}let a=n.slotMs!==null?new Date(n.slotMs).toLocaleString(void 0,{dateStyle:"short",timeStyle:"short"}):"on-demand",l=n.onDemand?"on-demand":n.catchUp?"catch-up":"scheduled",u=Date.now(),c=await Et(e.shell,t.command,{timeoutMs:e.syncTimeoutMs,maxBytes:e.syncMaxBytes,cwd:s}),d=Date.now()-u,m=`${Yu(new Date)}-${t.id}-${l}.log`,y=wn.join(i,m),f=[`cowork task=${t.name} id=${t.id}`,`slot=${a} kind=${l}`,`cwd=${s}`,`exit=${c.code} timedOut=${c.timedOut} durationMs=${d}`,"---",""].join(`
213
- `)+(c.stdout||"")+(c.stderr?`
183
+ `);n.transaction(()=>{for(let s of t)r.run(e,s.slotMs,s.kind,s.completedAtMs,s.logPath)})()}function Fs(e){Wt().prepare("INSERT OR REPLACE INTO cowork_task_state (task_id, last_ok, updated_at_ms) VALUES (?, 1, ?)").run(e,Date.now())}function zr(e){return Wt().prepare("SELECT updated_at_ms FROM cowork_task_state WHERE task_id = ?").get(e)?.updated_at_ms??null}function Kr(e){let t=Wt().prepare("SELECT last_ok FROM cowork_task_state WHERE task_id = ?").get(e);return t==null?null:t.last_ok===1}function qr(e,t){Wt().prepare("INSERT OR REPLACE INTO cowork_task_state (task_id, last_ok, updated_at_ms) VALUES (?, ?, ?)").run(e,t?1:0,Date.now())}function Dp(e){let n=Wt().prepare("SELECT COUNT(*) AS c FROM cowork_slot_completion WHERE task_id = ?"),r=Date.now();for(let o of e)if(!(o.lastCompletedSlotMs==null||!Number.isFinite(o.lastCompletedSlotMs)||n.get(o.id).c>0))try{Jr(o.id,[{slotMs:o.lastCompletedSlotMs,kind:"migrated",logPath:null,completedAtMs:r}]),I.info({taskId:o.id,slotMs:o.lastCompletedSlotMs},"cowork seeded completion from tasks.json")}catch(i){I.warn({err:String(i),taskId:o.id},"cowork seed from tasks.json failed")}}j();import _l from"node:crypto";import Gn from"node:fs";import Us from"node:os";import un from"node:path";var Hp=/^[a-zA-Z0-9][a-zA-Z0-9_-]{0,31}$/;function Hs(e){let t=e.trim().toLowerCase();return Hp.test(t)?{ok:!0,name:t}:{ok:!1,error:"Name must be alphanumeric with _ or -, max 32 chars."}}function dn(e,t){let n=e.trim();return n===""||n==="."?un.resolve(t):n==="~"?Us.homedir():n.startsWith("~/")||n.startsWith("~\\")?un.join(Us.homedir(),n.slice(2)):un.isAbsolute(n)?n:un.resolve(t,n)}function Yr(e){return un.join(Us.homedir(),"Cowork",e)}function Bp(e){if(!e||typeof e!="object")return null;let t=e,n=typeof t.id=="string"&&t.id.length>=4?t.id.slice(0,32):"",r=typeof t.name=="string"?t.name.trim().toLowerCase():"",o=typeof t.ownerPeerKey=="string"?t.ownerPeerKey:"",s=typeof t.command=="string"?t.command:"";if(!n||!r||!o||!s)return null;let i=typeof t.cwd=="string"?t.cwd:"",a=typeof t.outputDir=="string"&&t.outputDir.trim()?t.outputDir:Yr(r),l=typeof t.enabled=="boolean"?t.enabled:!0,c=typeof t.notify=="string"?t.notify.toLowerCase():"self",u=c==="wa"||c==="whatsapp"?"wa":c==="tg"||c==="telegram"?"tg":c==="all"?"all":c==="none"?"none":"self",d={kind:"ondemand"};if(t.schedule&&typeof t.schedule=="object"){let x=t.schedule,T=typeof x.kind=="string"?x.kind:"";if(T==="ondemand")d={kind:"ondemand"};else if(T==="daily"){let M=Number(x.hour),E=Number(x.minute);Number.isFinite(M)&&Number.isFinite(E)&&(d={kind:"daily",hour:M,minute:E})}else if(T==="weekdays"){let M=Number(x.hour),E=Number(x.minute);Number.isFinite(M)&&Number.isFinite(E)&&(d={kind:"weekdays",hour:M,minute:E})}else if(T==="hourly"){let M=Number(x.minute);Number.isFinite(M)&&Number.isInteger(M)&&M>=0&&M<=59&&(d={kind:"hourly",minute:M})}else if(T==="weekly"){let M=Number(x.hour),E=Number(x.minute),R=Number(x.weekday);Number.isFinite(M)&&Number.isFinite(E)&&Number.isInteger(R)&&(d={kind:"weekly",weekday:R,hour:M,minute:E})}else if(T==="heartbeat"){let M=Number(x.intervalMs),E=Number(x.graceMs);Number.isFinite(M)&&M>0&&Number.isFinite(E)&&E>0&&(d={kind:"heartbeat",intervalMs:M,graceMs:E})}}let p=null;typeof t.lastCompletedSlotMs=="number"&&Number.isFinite(t.lastCompletedSlotMs)&&(p=t.lastCompletedSlotMs);let f=typeof t.createdAtMs=="number"&&Number.isFinite(t.createdAtMs)?t.createdAtMs:Date.now(),y=typeof t.attachLog=="boolean"?t.attachLog:!1,h=Array.isArray(t.attachFiles)?t.attachFiles.filter(x=>typeof x=="string"&&x.trim().length>0):[],w=typeof t.notifyWhen=="string"?t.notifyWhen.toLowerCase():"always";return{id:n,name:r,ownerPeerKey:o,command:s,cwd:i,outputDir:a,schedule:d,enabled:l,notify:u,notifyWhen:w==="failure"?"failure":w==="state-change"?"state-change":"always",attachLog:y,attachFiles:h,lastCompletedSlotMs:p,createdAtMs:f}}function Ie(){try{let e=Gn.readFileSync(Ho,"utf8"),t=JSON.parse(e);if(!t||!Array.isArray(t.tasks))return[];let n=[];for(let r of t.tasks){let o=Bp(r);o&&n.push(o)}return n}catch{return[]}}function Ne(e){W(Xe);let t={tasks:e},n=un.join(Xe,`.tasks.${process.pid}.${_l.randomBytes(4).toString("hex")}.tmp`);Gn.writeFileSync(n,JSON.stringify(t,null,2)+`
184
+ `,{mode:384}),Gn.renameSync(n,Ho)}function Tt(e,t,n){let r=t.trim().toLowerCase();return e.find(o=>o.name===r&&o.ownerPeerKey===n)}function Bs(){return _l.randomBytes(4).toString("hex")}function Fl(e){W(Xe),Gn.writeFileSync(Bo,JSON.stringify(e,null,2)+`
185
+ `,{mode:384})}function Ul(e){let t=Ds();t.push(e),Fl(t)}function Ds(){try{let e=Gn.readFileSync(Bo,"utf8"),t=JSON.parse(e);return Array.isArray(t)?t.filter(n=>n&&typeof n=="object"&&typeof n.ownerPeerKey=="string"&&typeof n.name=="string"):[]}catch{return[]}}function Dl(e){if(e<=0)return{batch:[],remainingAfter:Ds().length};let t=Ds();if(t.length===0)return{batch:[],remainingAfter:0};let n=t.slice(0,e),r=t.slice(e);return Fl(r),{batch:n,remainingAfter:r.length}}function js(e,t){if(e.startsWith("tg:")){let n=e.slice(3);return pr(t.telegramAllowFrom).has(n)}return Ki(dr(t.allowFrom),e.replace(/^wa:/,""))}var jp=8,Hl=12e4,Bl=10,Wp=1e3;function Gp(e){return e.toISOString().replace(/[:.]/g,"-")}function Jp(e){let t="";for(let n of e)n==="*"?t+="[^/\\\\]*":n==="?"?t+="[^/\\\\]":/[.+^${}()|[\]\\]/.test(n)?t+=`\\${n}`:t+=n;return new RegExp(`^${t}$`)}function zp(e,t,n){let r=dn(e,t),o=zn.dirname(r),s=zn.basename(r);if(o.includes("*")||o.includes("?"))return[];if(!s.includes("*")&&!s.includes("?")){try{let c=Jn.statSync(r);if(c.isFile()&&c.mtimeMs>=n)return[r]}catch{}return[]}let i;try{i=Jn.readdirSync(o)}catch{return[]}let a=Jp(s),l=[];for(let c of i){if(!a.test(c))continue;let u=zn.join(o,c);try{let d=Jn.statSync(u);d.isFile()&&d.mtimeMs>=n&&l.push(u)}catch{}}return l}function jl(e,t,n){let r=Rt(e,t.fileSendMaxBytes);return"error"in r?{ok:!1,displayName:n??zn.basename(e),reason:r.error.replace(/\.$/,"").toLowerCase()}:{ok:!0,spec:{absPath:r.absPath,category:r.category,mimetype:r.mimetype,displayName:n??r.displayName}}}async function Wl(e,t,n,r){let o=ee(t.ownerPeerKey),s=t.cwd.trim()?t.cwd:o.cwd,i=dn(t.outputDir,o.cwd);try{Jn.mkdirSync(i,{recursive:!0,mode:448})}catch(S){I.warn({err:String(S),outDir:i},"cowork mkdir outputDir")}let a=n.slotMs!==null?new Date(n.slotMs).toLocaleString(void 0,{dateStyle:"short",timeStyle:"short"}):"on-demand",l=n.onDemand?"on-demand":n.catchUp?"catch-up":"scheduled",c=Date.now(),u=await Bt(e.shell,t.command,{timeoutMs:e.syncTimeoutMs,maxBytes:e.syncMaxBytes,cwd:s}),d=Date.now()-c,p=`${Gp(new Date)}-${t.id}-${l}.log`,f=zn.join(i,p),h=[`cowork task=${t.name} id=${t.id}`,`slot=${a} kind=${l}`,`cwd=${s}`,`exit=${u.code} timedOut=${u.timedOut} durationMs=${d}`,"---",""].join(`
186
+ `)+(u.stdout||"")+(u.stderr?`
214
187
  --- stderr ---
215
- ${c.stderr}`:"");try{yn.writeFileSync(y,f,{mode:384})}catch(W){E.warn({err:String(W),logPath:y},"cowork write log")}let h=c.code===0&&!c.timedOut&&c.signal===null,k=$e().find(W=>W.id===t.id&&W.ownerPeerKey===t.ownerPeerKey),$=k?.notify??t.notify,A=k?.attachLog??t.attachLog,M=k?.attachFiles??t.attachFiles,L=c.timedOut?"timeout":c.signal?`signal ${c.signal}`:c.code!==0&&c.code!==null?`exit ${c.code}`:null,U=`slot: ${a} \xB7 ${l}${L?` \xB7 ${L}`:""}`,re=(c.stdout||"").replace(/\s+$/,""),Se=(c.stderr||"").trim(),we=re||(Se?`(stderr) ${Se}`:"(no output)"),Ke=`${t.name} : ${L?`[${L}] `:""}${we}`,Xe=n.onDemand?[U,`output: ${we}`]:[Ke],T=[],w=[],S=u-Qu,I=[],_=new Set;if(Array.isArray(M))for(let W of M){let Ce;try{Ce=Ku(W,s,S)}catch(Ne){E.warn({err:String(Ne),pat:W},"cowork attach glob"),T.push(`attach: ${W} skipped (glob error)`);continue}if(Ce.length!==0)for(let Ne of Ce)_.has(Ne)||(_.add(Ne),I.push(Ne))}if(A){let W=Ba(y,e,`${t.name}-${l}.log`);W.ok?w.push(W.spec):T.push(`attach: ${W.displayName} skipped (${W.reason})`)}let B=0;for(let W of I){if(w.length>=_a){B+=1;continue}let Ce=Ba(W,e);Ce.ok?w.push(Ce.spec):T.push(`attach: ${Ce.displayName} skipped (${Ce.reason})`)}B>0&&T.push(`attached: skipped ${B} file(s) over cap ${_a}`),T.length>0&&Xe.push(...T);let V=Xe.join(`
216
- `),me=p(V),Ee=Fa($,t.ownerPeerKey,e);for(let W of Ee){let Ce=De(me,W.startsWith("tg:")?"telegram":"whatsapp").text;try{await r.sendToPeer(W,Ce)}catch(Ne){E.warn({err:String(Ne),pk:W},"cowork notify failed")}for(let Ne of w)try{await r.sendMediaToPeer(W,Ne)}catch(_l){E.warn({err:String(_l),pk:W,file:Ne.displayName},"cowork media notify failed")}}return{commandOk:h,logPath:y}}function Da(e){let t=!1;fn($e());let n=async()=>{if(t)return;t=!0;let s=Date.now(),i=0,a=0,l=0,u=0,c=0;try{let d=e.getConfig(),{batch:m,remainingAfter:y}=Ra(qu);a=m.length,l=y,i=m.length+y;for(let h of m)try{let k=$e().find($=>$.name===h.name.toLowerCase()&&$.ownerPeerKey===h.ownerPeerKey);k&&k.enabled&&(u+=1,await Ha(d,k,{slotMs:null,catchUp:!1,onDemand:!0},e))}catch(x){E.warn({err:String(x),pending:h.name},"cowork on-demand run failed")}let g=$e();fn(g);let f=Date.now();for(let h of g){if(!h.enabled||h.schedule.kind==="ondemand")continue;let x=cr(h),k=ha(h.schedule,x,h.createdAtMs,f);if(k.length===0)continue;let $=k[k.length-1],A=f-$>Na;try{c+=1;let{commandOk:M,logPath:L}=await Ha(d,h,{slotMs:$,catchUp:A,onDemand:!1},e);if(M){let U=Date.now(),re=f-$<=Na?"on_time":"catch_up";if(k.length===1)ur(h.id,[{slotMs:$,kind:re,logPath:L,completedAtMs:U}]);else{let we=k.slice(0,-1).map(Ke=>({slotMs:Ke,kind:"coalesced",logPath:null,completedAtMs:U}));we.push({slotMs:$,kind:re,logPath:L,completedAtMs:U}),ur(h.id,we)}}}catch(M){E.warn({err:String(M),task:h.name},"cowork scheduled run failed")}}}finally{let d=Date.now()-s;E.info({tickMs:d,pendingDepthStart:i,pendingDequeued:a,pendingRemainingAfter:l,pendingRunsStarted:u,scheduledRan:c},"cowork tick"),t=!1}},r=setInterval(()=>void n(),3e4),o=setTimeout(()=>void n(),5e3);return()=>{clearInterval(r),clearTimeout(o),ka()}}import{spawn as Xu}from"node:child_process";import ja from"node:fs";import Ua from"node:path";var Zu=new Set(["PATH","HOME","USER","LOGNAME","SHELL","LANG","LC_ALL","LC_CTYPE","LC_MESSAGES","TMPDIR","TZ"]);function ed(e,t){let n={OMNISH_PEER_KEY:e,OMNISH_CHAT_MESSAGE:t};for(let r of Zu){let o=process.env[r];o!==void 0&&(n[r]=o)}for(let[r,o]of Object.entries(process.env))o&&(r.startsWith("OMNISH_")||/_API_KEY$/i.test(r)||/_TOKEN$/i.test(r))&&(n[r]=o);return n}var Wa=new Map;function td(e,t){let r=(Wa.get(e)??Promise.resolve()).then(t).catch(o=>{E.warn({peerKey:e,err:String(o)},"chat LLM fallback queue task failed")});Wa.set(e,r)}function nd(e){z();let t=e.chatLlmWorkDir.trim();if(t){let r=Ua.resolve(t);return H(r),{cwd:r,cleanup:()=>{}}}let n=ja.mkdtempSync(Ua.join(N,"chat-llm-"));return{cwd:n,cleanup:()=>{try{ja.rmSync(n,{recursive:!0,force:!0})}catch{}}}}function rd(e,t){return e.length<=t?e:`${e.slice(0,t)}
217
- [...input truncated]`}function od(e,t){let n=[],r=e.stdout.trimEnd(),o=e.stderr.trimEnd();return r&&n.push(r.length>t?`${r.slice(0,t)}
188
+ ${u.stderr}`:"");try{Jn.writeFileSync(f,h,{mode:384})}catch(S){I.warn({err:String(S),logPath:f},"cowork write log")}let w=u.code===0&&!u.timedOut&&u.signal===null,x=Ie().find(S=>S.id===t.id&&S.ownerPeerKey===t.ownerPeerKey),T=x?.notify??t.notify,M=x?.notifyWhen??t.notifyWhen??"always",E=x?.attachLog??t.attachLog,R=x?.attachFiles??t.attachFiles,_=!0;if(M==="failure")_=!w;else if(M==="state-change"){let S=Kr(t.id);_=S===null||S!==w}qr(t.id,w);let H=u.timedOut?"timeout":u.signal?`signal ${u.signal}`:u.code!==0&&u.code!==null?`exit ${u.code}`:null,oe=`slot: ${a} \xB7 ${l}${H?` \xB7 ${H}`:""}`,F=(u.stdout||"").replace(/\s+$/,""),le=(u.stderr||"").trim(),Z=F||(le?`(stderr) ${le}`:"(no output)"),ve=M==="state-change"?w?" [recovered]":" [failing]":"",_e=`${t.name}${ve} : ${H?`[${H}] `:""}${Z}`,b=n.onDemand?[oe,`output: ${Z}`]:[_e];if(_){let S=[],A=[],D=c-Wp,K=[],Q=new Set;if(Array.isArray(R))for(let ke of R){let Ye;try{Ye=zp(ke,s,D)}catch(Qe){I.warn({err:String(Qe),pat:ke},"cowork attach glob"),S.push(`attach: ${ke} skipped (glob error)`);continue}if(Ye.length!==0)for(let Qe of Ye)Q.has(Qe)||(Q.add(Qe),K.push(Qe))}if(E){let ke=jl(f,e,`${t.name}-${l}.log`);ke.ok?A.push(ke.spec):S.push(`attach: ${ke.displayName} skipped (${ke.reason})`)}let xe=0;for(let ke of K){if(A.length>=Bl){xe+=1;continue}let Ye=jl(ke,e);Ye.ok?A.push(Ye.spec):S.push(`attach: ${Ye.displayName} skipped (${Ye.reason})`)}xe>0&&S.push(`attached: skipped ${xe} file(s) over cap ${Bl}`),S.length>0&&b.push(...S);let $e=b.join(`
189
+ `),Ot=m($e),Lt=Wr(T,t.ownerPeerKey,e);for(let ke of Lt){let Ye=Pe(Ot,ke.startsWith("tg:")?"telegram":"whatsapp").text;try{await r.sendToPeer(ke,Ye)}catch(Qe){I.warn({err:String(Qe),pk:ke},"cowork notify failed")}for(let Qe of A)try{await r.sendMediaToPeer(ke,Qe)}catch(ud){I.warn({err:String(ud),pk:ke,file:Qe.displayName},"cowork media notify failed")}}}return{commandOk:w,logPath:f}}function Qr(e){let t=!1;jt(Ie());let n=async()=>{if(t)return;t=!0;let s=Date.now(),i=0,a=0,l=0,c=0,u=0;try{let d=e.getConfig(),{batch:p,remainingAfter:f}=Dl(jp);a=p.length,l=f,i=p.length+f;for(let w of p)try{let x=Ie().find(T=>T.name===w.name.toLowerCase()&&T.ownerPeerKey===w.ownerPeerKey);if(x&&x.enabled){if(!js(x.ownerPeerKey,d)){I.warn({task:x.name,peer:x.ownerPeerKey},"cowork: skipping on-demand run \u2014 owner no longer on allowlist");continue}c+=1,await Wl(d,x,{slotMs:null,catchUp:!1,onDemand:!0},e)}}catch(v){I.warn({err:String(v),pending:w.name},"cowork on-demand run failed")}let y=Ie();jt(y);let h=Date.now();for(let w of y){if(!w.enabled||w.schedule.kind!=="heartbeat"||!js(w.ownerPeerKey,d))continue;let v=zr(w.id);if(v===null)continue;let x=v+w.schedule.intervalMs+w.schedule.graceMs;if(h>x){if(Kr(w.id)===!1)continue;qr(w.id,!1);let M=Math.round((h-v)/6e4),E=`${w.name} [heartbeat missed] \u2014 last check-in ${M}m ago`,R=w.notify,_=Wr(R,w.ownerPeerKey,d);for(let H of _)try{await e.sendToPeer(H,E)}catch(oe){I.warn({err:String(oe),pk:H},"cowork heartbeat notify failed")}}else if(h<=x&&Kr(w.id)===!1){qr(w.id,!0);let M=`${w.name} [heartbeat recovered]`,E=w.notify,R=Wr(E,w.ownerPeerKey,d);for(let _ of R)try{await e.sendToPeer(_,M)}catch(H){I.warn({err:String(H),pk:_},"cowork heartbeat recovery notify failed")}}}for(let w of y){if(!w.enabled||w.schedule.kind==="ondemand"||w.schedule.kind==="heartbeat")continue;if(!js(w.ownerPeerKey,d)){I.warn({task:w.name,peer:w.ownerPeerKey},"cowork: skipping scheduled run \u2014 owner no longer on allowlist");continue}let v=Gr(w),x=Pl(w.schedule,v,w.createdAtMs,h);if(x.length===0)continue;let T=x[x.length-1],M=h-T>Hl;try{u+=1;let{commandOk:E,logPath:R}=await Wl(d,w,{slotMs:T,catchUp:M,onDemand:!1},e);if(E){let _=Date.now(),H=h-T<=Hl?"on_time":"catch_up";if(x.length===1)Jr(w.id,[{slotMs:T,kind:H,logPath:R,completedAtMs:_}]);else{let F=x.slice(0,-1).map(le=>({slotMs:le,kind:"coalesced",logPath:null,completedAtMs:_}));F.push({slotMs:T,kind:H,logPath:R,completedAtMs:_}),Jr(w.id,F)}}}catch(E){I.warn({err:String(E),task:w.name},"cowork scheduled run failed")}}}finally{let d=Date.now()-s;I.info({tickMs:d,pendingDepthStart:i,pendingDequeued:a,pendingRemainingAfter:l,pendingRunsStarted:c,scheduledRan:u},"cowork tick"),t=!1}},r=setInterval(()=>void n(),3e4),o=setTimeout(()=>void n(),5e3);return()=>{clearInterval(r),clearTimeout(o),Nl()}}import Kp from"node:fs";import Gl from"node:path";import{fileURLToPath as qp}from"node:url";var Vr=null;function Be(){if(Vr!==null)return Vr;let e=Gl.dirname(qp(import.meta.url)),t=Gl.join(e,"..","package.json"),n=Kp.readFileSync(t,"utf8"),r=JSON.parse(n);return Vr=typeof r.version=="string"&&r.version.trim()?r.version.trim():"0.0.0",Vr}Se();j();import{spawn as Yp}from"node:child_process";import Jl from"node:fs";import zl from"node:path";var Qp=new Set(["PATH","HOME","USER","LOGNAME","SHELL","LANG","LC_ALL","LC_CTYPE","LC_MESSAGES","TMPDIR","TZ"]),Vp=new Set(["OPENAI_API_KEY","ANTHROPIC_API_KEY","GOOGLE_API_KEY","GEMINI_API_KEY","MISTRAL_API_KEY","GROQ_API_KEY","COHERE_API_KEY","HUGGINGFACE_TOKEN","TOGETHER_API_KEY","FIREWORKS_API_KEY","PERPLEXITY_API_KEY","DEEPSEEK_API_KEY","XAI_API_KEY","CURSOR_API_KEY"]);function Xp(e,t){let n={OMNISH_PEER_KEY:e,OMNISH_CHAT_MESSAGE:t};for(let r of Qp){let o=process.env[r];o!==void 0&&(n[r]=o)}for(let[r,o]of Object.entries(process.env))o&&(r.startsWith("OMNISH_")||Vp.has(r))&&(n[r]=o);return n}var Kl=new Map;function Zp(e,t){let r=(Kl.get(e)??Promise.resolve()).then(t).catch(o=>{I.warn({peerKey:e,err:String(o)},"chat LLM fallback queue task failed")});Kl.set(e,r)}function em(e){V();let t=e.chatLlmWorkDir.trim();if(t){let r=zl.resolve(t);return W(r),{cwd:r,cleanup:()=>{}}}let n=Jl.mkdtempSync(zl.join(U,"chat-llm-"));return{cwd:n,cleanup:()=>{try{Jl.rmSync(n,{recursive:!0,force:!0})}catch{}}}}function tm(e,t){return e.length<=t?e:`${e.slice(0,t)}
190
+ [...input truncated]`}function nm(e,t){let n=[],r=e.stdout.trimEnd(),o=e.stderr.trimEnd();return r&&n.push(r.length>t?`${r.slice(0,t)}
218
191
  [...truncated]`:r),o&&(n.push("\u2014 stderr \u2014"),n.push(o.length>t?`${o.slice(0,t)}
219
192
  [...truncated]`:o)),n.length===0&&n.push("(no output)"),e.timedOut?n.push(`(timed out after ${Math.round(e.durationMs/1e3)}s)`):e.code!==0&&e.code!==null?n.push(`(exit ${e.code})`):e.signal&&n.push(`(signal ${e.signal})`),n.join(`
220
- `)}function sd(e,t,n,r){return new Promise(o=>{let s=Date.now(),i=!1,a="",l="",u=!1,c=null,d,m=f=>{u||(u=!0,clearTimeout(d),o(f))},y=r.maxOutChars,g=(f,h)=>{f==="out"?a+=h:l+=h;let x=a.length+l.length;if(x>y){let k=x-y;if(l.length>=k)l=`${l.slice(0,Math.max(0,l.length-k))}
221
- [...truncated]`;else{let $=k-l.length;l="",a=`${a.slice(0,Math.max(0,a.length-$))}
222
- [...truncated]`}try{c?.kill("SIGTERM")}catch{}}};try{c=Xu(e,["-c",t],{cwd:r.cwd,env:r.env,stdio:["pipe","pipe","pipe"]})}catch(f){m({code:null,stdout:"",stderr:String(f),durationMs:Date.now()-s,timedOut:!1,signal:null});return}d=setTimeout(()=>{i=!0;try{c?.kill("SIGTERM")}catch{}},r.timeoutMs),c.stdout?.setEncoding("utf8"),c.stderr?.setEncoding("utf8"),c.stdout?.on("data",f=>g("out",f)),c.stderr?.on("data",f=>g("err",f)),c.stdin?.write(n,"utf8",f=>{if(f){try{c?.kill("SIGTERM")}catch{}m({code:null,stdout:a,stderr:`${l}
223
- ${String(f)}`,durationMs:Date.now()-s,timedOut:i,signal:null});return}c.stdin?.end()}),c.on("error",f=>{m({code:null,stdout:a,stderr:`${l}
224
- ${String(f)}`,durationMs:Date.now()-s,timedOut:i,signal:null})}),c.on("close",(f,h)=>{m({code:f,stdout:a,stderr:l,durationMs:Date.now()-s,timedOut:i,signal:h??null})})})}async function id(e,t,n,r){let o=e.chatLlmShellCommand.trim();if(!o){await r("(chat LLM fallback: chatLlmShellCommand is empty)");return}let{cwd:s,cleanup:i}=nd(e);try{let a=rd(n,e.chatLlmMaxInputChars),l=ed(t,a),u=e.chatLlmMaxOutputChars,c;e.chatLlmNeedsTty?c=await Et(e.shell,o,{cwd:s,timeoutMs:e.chatLlmTimeoutMs,maxBytes:u,env:l}):c=await sd(e.shell,o,a,{cwd:s,timeoutMs:e.chatLlmTimeoutMs,maxOutChars:u,env:l});let d=od(c,u);await r(d)}catch(a){E.warn({peerKey:t,err:String(a)},"chat LLM fallback run failed"),await r(`(assistant error) ${String(a)}`)}finally{i()}}function mr(e,t,n,r){E.info({peerKey:t,len:n.length},"chat LLM fallback enqueued"),td(t,async()=>{await id(e,t,n,r),E.info({peerKey:t},"chat LLM fallback completed")})}import _d from"node:fs";import{Bot as Bd,InputFile as Hd}from"grammy";import Ga from"node:fs";import Dt from"node:path";function ad(e){return e.replace(/[^a-zA-Z0-9._-]+/g,"_").slice(0,120)||"unknown"}function ld(e){let r=Dt.basename(e.trim()||"file").replace(/[^a-zA-Z0-9._-]+/g,"_").replace(/^\.+/,"").slice(0,180);return r.length>0?r:"file"}function cd(e,t){let n=new Date().toISOString().slice(0,10),r=ad(t);return Dt.join(e,r,n)}function fr(e,t,n){let r=cd(e,t);H(r);let o=ld(n),s=Dt.join(r,o);if(!Ga.existsSync(s))return s;let i=Dt.extname(o),a=i?o.slice(0,-i.length):o;for(let l=1;l<1e4;l+=1){let u=`${a}-${l}${i}`;if(s=Dt.join(r,u),!Ga.existsSync(s))return s}return Dt.join(r,`${a}-${Date.now()}${i}`)}import ud from"node:dns";import dd from"node:https";import{URL as pd}from"node:url";function jt(e){if(e instanceof Error){let t=e.cause;return t!=null?`${e.message} (${String(t)})`:e.message}return String(e)}function Ja(e){if("photo"in e&&e.photo&&e.photo.length>0)return{fileId:e.photo[e.photo.length-1].file_id,baseName:"photo.jpg"};if("document"in e&&e.document){let t=e.document,n=t.file_name?.trim();return{fileId:t.file_id,baseName:n&&n.length>0?n:"document.bin"}}if("video"in e&&e.video){let t=e.video,n=t.file_name?.trim();return{fileId:t.file_id,baseName:n&&n.length>0?n:"video.mp4"}}if("audio"in e&&e.audio){let t=e.audio,n=t.file_name?.trim();return{fileId:t.file_id,baseName:n&&n.length>0?n:"audio.m4a"}}if("voice"in e&&e.voice)return{fileId:e.voice.file_id,baseName:"voice.ogg"};if("video_note"in e&&e.video_note)return{fileId:e.video_note.file_id,baseName:"video_note.mp4"};if("sticker"in e&&e.sticker){let t=e.sticker,n=t.is_video?"webm":"webp";return{fileId:t.file_id,baseName:`sticker.${n}`}}return null}function md(e,t){let r=t.split("/").filter(o=>o.length>0).map(encodeURIComponent).join("/");return`https://api.telegram.org/file/bot${e}/${r}`}var za="omnish (Telegram bot; https://github.com/labKnowledge/whatsLive)";function fd(e){let t=new pd(e);return new Promise((n,r)=>{let o=dd.request({hostname:t.hostname,port:t.port||443,path:t.pathname+t.search,method:"GET",headers:{"User-Agent":za,Accept:"*/*"},lookup(s,i,a){ud.lookup(s,{family:4},a)}},s=>{let i=s.statusCode??0,a=[];s.on("data",l=>a.push(l)),s.on("end",()=>n({status:i,buffer:Buffer.concat(a)})),s.on("error",r)});o.on("error",r),o.end()})}async function qa(e,t,n){let r;try{r=await e.api.getFile(t)}catch(i){return E.warn({err:String(i),fileId:t},"telegram getFile failed"),{error:"Could not resolve file on Telegram."}}if(!r.file_path)return{error:"Telegram did not return a file path."};let o=md(e.token,r.file_path),s;try{let i=await fetch(o,{headers:{"User-Agent":za,Accept:"*/*"}});if(!i.ok)return E.warn({status:i.status,statusText:i.statusText,fileId:t},"telegram file fetch HTTP error"),{error:`Could not download file from Telegram (HTTP ${i.status}${i.statusText?` ${i.statusText}`:""}).`};try{s=Buffer.from(await i.arrayBuffer())}catch(a){return E.warn({err:String(a),fileId:t},"telegram file read body failed"),{error:`Could not read file from Telegram (${String(a)}).`}}}catch(i){E.warn({err:String(i),fileId:t},"telegram file fetch failed; retrying via HTTPS IPv4");try{let a=await fd(o);if(a.status<200||a.status>=300)return E.warn({status:a.status,fileId:t},"telegram HTTPS IPv4 fallback HTTP error"),{error:`Could not download file from Telegram (HTTP ${a.status}).`};s=a.buffer}catch(a){return E.warn({err:String(i),err2:String(a),fileId:t},"telegram file download failed (fetch and IPv4 fallback)"),{error:`Could not download file from Telegram (network: ${jt(i)}; IPv4 fallback: ${jt(a)}).`}}}return n>0&&s.byteLength>n?{error:`Media too large (max ${n} bytes).`}:{buffer:s}}import{downloadMediaMessage as gd,extensionForMediaMessage as hd,getContentType as gr,isJidGroup as Va,isLidUser as yd}from"@whiskeysockets/baileys";import wd from"node:fs";function bd(e){if(!e||typeof e!="object")return"";let t=e;if(typeof t.conversation=="string")return t.conversation;let n=t.extendedTextMessage;return n&&typeof n.text=="string"?n.text:""}function kd(e){if(!e||typeof e!="object")return"";let t=e;for(let n of["imageMessage","videoMessage","documentMessage","audioMessage","stickerMessage"]){let r=t[n];if(r&&typeof r.caption=="string"&&r.caption.trim())return r.caption}return""}function Ka(e){return[bd(e),kd(e)].filter(n=>n.trim().length>0).join(`
225
- `).trim()}function Bo(e){return e.replace(/[\u0000-\u0008\u000B\u000C\u000E-\u001F\u007F]/g,"").replace(/\uFEFF/g,"").trim()}function Xa(e){let t=gr(e??void 0);return t==="imageMessage"||t==="videoMessage"||t==="audioMessage"||t==="documentMessage"||t==="stickerMessage"}function Sd(e){let t=gr(e??void 0);if(!t)return;let r=e?.[t]?.fileLength;if(typeof r=="number"&&Number.isFinite(r))return r;if(r&&typeof r=="object"&&"toNumber"in r&&typeof r.toNumber=="function")try{let o=r.toNumber();return Number.isFinite(o)?o:void 0}catch{return}}function Qa(e){try{if(!e)return"file.bin";let t=hd(e);return`file${t&&t.length>0?t.startsWith(".")?t:`.${t}`:".bin"}`}catch{return`${(gr(e??void 0)??"file").replace("Message","")||"file"}.bin`}}function xd(e){let t=gr(e??void 0);if(!t)return Qa(e);let n=e?.[t];return n?.fileName&&typeof n.fileName=="string"&&n.fileName.trim()?n.fileName.trim():Qa(e)}async function Ya(e,t,n,r){let o=t.message??void 0;if(!Xa(o))return{};let s=r.fileReceiveMaxBytes,i=Sd(o);if(s>0&&i!==void 0&&i>s)return{error:`Media too large (max ${s} bytes).`};let a;try{a=await gd(t,"buffer",{},{logger:e.logger,reuploadRequest:e.updateMediaMessage})}catch(d){return E.warn({err:String(d),detail:jt(d)},"whatsapp downloadMediaMessage failed"),{error:`Could not download media from WhatsApp (${jt(d)}).`}}if(s>0&&a.length>s)return{error:`Media too large (max ${s} bytes).`};let l;try{l=Ct(r,n)}catch(d){return{error:String(d)}}let u=xd(o),c=fr(l,n,u);try{wd.writeFileSync(c,a,{mode:384})}catch{return{error:"Could not write media to inbox."}}return{path:c}}async function vd(e,t){let n=t.remoteJid??"";if(!n)return{fromJid:"",fromE164:""};if(t.remoteJidAlt){let r=G(t.remoteJidAlt)??G(n)??"";return{fromJid:n,fromE164:r}}if(yd(n))try{let r=await e.signalRepository?.lidMapping?.getPNForLID?.(n);if(r){let o=G(r)??"";if(o)return{fromJid:n,fromE164:o}}}catch{}return{fromJid:n,fromE164:G(n)??""}}function $d(e){try{if(!R().clusterEnabled)return;let n=Ka(e.message??void 0);if(!n)return;let r=ji(n);if(!r)return;let o=e.key?.remoteJid??"",s=null;if(o&&!Va(o)){let i=G(o);i&&(s=`wa:${i}`)}Vi(r,s)}catch(t){E.warn({err:String(t)},"cluster footer observation failed")}}function Za(e,t){let n=r=>{r.type==="notify"&&(async()=>{for(let o of r.messages){let s=o.key;if(!s)continue;if(s.fromMe){$d(o);continue}let i=s.remoteJid;if(!i||Va(i)||i.toLowerCase().endsWith("@status")||i==="status@broadcast")continue;let l=Bo(Ka(o.message??void 0)),{fromJid:u,fromE164:c}=await vd(e,s),d=`wa:${u}`,m=Xa(o.message??void 0);if(m&&!l){(async()=>{try{let f=R(),h=await Ya(e,o,d,f);await t({fromJid:u,fromE164:c,text:"",messageId:s.id??void 0,mediaSavedPath:h.path,mediaError:h.error})}catch(f){E.error({err:String(f),fromJid:u},"whatsapp media-only background task failed")}})();continue}let y,g;if(m){let f=R(),h=await Ya(e,o,d,f);y=h.path,g=h.error}!l&&!y&&!g||await t({fromJid:u,fromE164:c,text:l,messageId:s.id??void 0,mediaSavedPath:y,mediaError:g})}})()};return e.ev.on("messages.upsert",n),()=>{e.ev.off("messages.upsert",n)}}import Ld from"node:fs";import Cd from"node:process";import Rd,{DisconnectReason as bt,fetchLatestBaileysVersion as Md,makeCacheableSignalKeyStore as Td,useMultiFileAuthState as Id}from"@whiskeysockets/baileys";import Ed from"qrcode-terminal";var Ad="0.1.0";function Ho(e){return e?.error?.output?.statusCode}async function hr(e={}){z();let t=e.authDir??J,n=e.verbose===!0,r=ya(),{state:o,saveCreds:s}=await Id(t),{version:i}=await Md(),a=Rd({version:i,logger:r,printQRInTerminal:!1,browser:["omnish","cli",Ad],auth:{creds:o.creds,keys:Td(o.keys,r)},syncFullHistory:!1,markOnlineOnConnect:!1});return a.ev.on("creds.update",s),a.ev.on("connection.update",l=>{let{connection:u,lastDisconnect:c,qr:d}=l;if(d&&(e.onQr?.(d),e.printQr)){let m=Cd.stdout,y=b(m,"\xB7".repeat(42));console.log(se(m,"Scan with WhatsApp \u2192 Linked devices")),console.log(y),Ed.generate(d,{small:!0}),console.log(y)}if(u==="close"){let m=Ho(c);n&&m===bt.loggedOut&&r.warn("WhatsApp session logged out (401).")}u==="open"&&n&&r.info("WhatsApp Web connected.")}),a.ws&&typeof a.ws.on=="function"&&a.ws.on("error",l=>{r.error({err:String(l)},"WebSocket error")}),a}function Ut(e){try{e.end(new Error("omnish: socket closed"))}catch{e.ws?.close()}}function Do(e){return e?.output?.statusCode??e?.status??e?.error?.output?.statusCode}function yr(e){return e.user?.id?Promise.resolve():new Promise((t,n)=>{let r=o=>{if(o.connection==="open"){e.ev.off("connection.update",r),t();return}if(o.connection==="close"){e.ev.off("connection.update",r);let i=o.lastDisconnect?.error??o.lastDisconnect??new Error("Connection closed");n(i)}};e.ev.on("connection.update",r)})}function kt(e,t,n){return new Promise((r,o)=>{let s=setTimeout(()=>o(new Error(n)),t);e.then(i=>{clearTimeout(s),r(i)},i=>{clearTimeout(s),o(i)})})}var ol=3500,Od=400,el=18e4,tl=9e4,nl=3e5;function jo(e,t=ol){if(e.length<=t)return[e];let n=[],r=0;for(;r<e.length;){let o=Math.min(r+t,e.length);if(o<e.length){let i=e.slice(r,o).lastIndexOf(`
193
+ `)}function Xr(e){let t=e&&typeof e=="object"&&"code"in e?String(e.code):"";return t==="EPIPE"||t==="EOF"}function ql(e){try{e?.stdin?.end()}catch(t){if(!Xr(t))throw t}}function rm(e,t,n,r){return new Promise(o=>{let s=Date.now(),i=!1,a="",l="",c=!1,u=null,d,p=v=>{c||(c=!0,clearTimeout(d),o(v))},f=r.maxOutChars,y=(v,x)=>{v==="out"?a+=x:l+=x;let T=a.length+l.length;if(T>f){let M=T-f;if(l.length>=M)l=`${l.slice(0,Math.max(0,l.length-M))}
194
+ [...truncated]`;else{let E=M-l.length;l="",a=`${a.slice(0,Math.max(0,a.length-E))}
195
+ [...truncated]`}try{u?.kill("SIGTERM")}catch{}}};try{u=Yp(e,["-c",t],{cwd:r.cwd,env:r.env,stdio:["pipe","pipe","pipe"]})}catch(v){p({code:null,stdout:"",stderr:String(v),durationMs:Date.now()-s,timedOut:!1,signal:null});return}d=setTimeout(()=>{i=!0;try{u?.kill("SIGTERM")}catch{}},r.timeoutMs),u.stdout?.setEncoding("utf8"),u.stderr?.setEncoding("utf8"),u.stdout?.on("data",v=>y("out",v)),u.stderr?.on("data",v=>y("err",v));let h=u.stdin;h&&h.on("error",v=>{Xr(v)||p({code:null,stdout:a,stderr:`${l}
196
+ ${String(v)}`,durationMs:Date.now()-s,timedOut:i,signal:null})}),(()=>{if(!(!h||c))try{h.write(n,"utf8",v=>{if(v&&!Xr(v)){try{u?.kill("SIGTERM")}catch{}p({code:null,stdout:a,stderr:`${l}
197
+ ${String(v)}`,durationMs:Date.now()-s,timedOut:i,signal:null});return}ql(u)})}catch(v){if(!Xr(v)){p({code:null,stdout:a,stderr:`${l}
198
+ ${String(v)}`,durationMs:Date.now()-s,timedOut:i,signal:null});return}ql(u)}})(),u.on("error",v=>{p({code:null,stdout:a,stderr:`${l}
199
+ ${String(v)}`,durationMs:Date.now()-s,timedOut:i,signal:null})}),u.on("close",(v,x)=>{p({code:v,stdout:a,stderr:l,durationMs:Date.now()-s,timedOut:i,signal:x??null})})})}async function om(e,t,n,r){let o=e.chatLlmShellCommand.trim();if(!o){await r("(chat LLM fallback: chatLlmShellCommand is empty)");return}let{cwd:s,cleanup:i}=em(e);try{let a=tm(n,e.chatLlmMaxInputChars),l=Xp(t,a),c=e.chatLlmMaxOutputChars,u;e.chatLlmNeedsTty?u=await Bt(e.shell,o,{cwd:s,timeoutMs:e.chatLlmTimeoutMs,maxBytes:c,env:l}):u=await rm(e.shell,o,a,{cwd:s,timeoutMs:e.chatLlmTimeoutMs,maxOutChars:c,env:l});let d=nm(u,c);await r(d)}catch(a){I.warn({peerKey:t,err:String(a)},"chat LLM fallback run failed"),await r(`(assistant error) ${String(a)}`)}finally{i()}}function pn(e,t,n,r){I.info({peerKey:t,len:n.length},"chat LLM fallback enqueued"),Zp(t,async()=>{await om(e,t,n,r),I.info({peerKey:t},"chat LLM fallback completed")})}j();import{spawn as sm}from"node:child_process";import im from"node:crypto";import Fe from"node:fs";import Ws from"node:path";var Yl=64,am=/^[a-zA-Z0-9._-]+$/;function lm(e){let t=e.trim();return t?t.length>Yl?`Job name must be at most ${Yl} characters.`:am.test(t)?null:"Job name may only use letters, digits, and . _ -":"Job name must not be empty."}function Ql(e){let t=e.trim();if(!t)return{error:"empty"};let n=null,r=!1,o=!0;for(;o;){o=!1;let i=t.trim();if(/^--notify(?:\s|$)/i.test(i)){r=!0,t=i.slice(8).trim(),o=!0;continue}if(/^-N(?:\s|$)/.test(i)){r=!0,t=i.slice(2).trim(),o=!0;continue}let a=[[/^--name\s*=\s*(\S+)\s+([\s\S]+)$/i,1,2],[/^--name\s+(\S+)\s+([\s\S]+)$/i,1,2],[/^-n\s+(\S+)\s+([\s\S]+)$/i,1,2]];for(let[l,c,u]of a){let d=i.match(l);if(d){n=d[c],t=d[u].trim(),o=!0;break}}}let s=t.trim();if(n===null&&[/^-n\s+\S+\s*$/i,/^--name\s+\S+\s*$/i,/^--name=\S+\s*$/i,/^-n\s*$/i,/^--name\s*$/i].some(a=>a.test(s)))return{error:"Add a shell command after the name."};if(n!==null){let i=lm(n);if(i)return{error:i}}return s?{cmd:s,name:n,notify:r}:{error:"Add a shell command after the flags."}}function cm(e,t){let n=t.trim();if(!n)return{ok:!1,error:"Missing job id or name."};let r=n.toLowerCase(),o=/^[a-f0-9]{8}$/;if(o.test(r)&&e.find(i=>i.id===r))return{ok:!0,id:r};for(let s of e)if(s.name&&s.name.toLowerCase()===r)return{ok:!0,id:s.id};return o.test(r)?{ok:!1,error:`Unknown job id: ${r}`}:{ok:!1,error:`Unknown job name: ${n}`}}function mn(e,t){Fe.writeFileSync(e,JSON.stringify(t,null,2)+`
200
+ `,{mode:384})}function Kn(e){try{return JSON.parse(Fe.readFileSync(e,"utf8"))}catch{return null}}function Vl(e){let t=e.name?`${e.id} (${e.name})`:e.id,n=e.finishedAt&&e.startedAt?Date.parse(e.finishedAt)-Date.parse(e.startedAt):null,r=n!==null?`${(n/1e3).toFixed(1)}s`:"?",o=e.exitCode===0&&!e.signal,s=e.status==="killed"?`killed (signal ${e.signal??"?"})`:o?"completed successfully":e.signal?`signal ${e.signal}`:`exit ${e.exitCode??"?"}`,i=e.cmd.length>100?`${e.cmd.slice(0,100)}\u2026`:e.cmd;return[`Job ${t} ${s}`,`duration: ${r}`,`$ ${i}`,`/log ${e.name??e.id}`].join(`
201
+ `)}var dt=class{running=new Map;metaPath(t){return Ws.join(We,`${t}.meta.json`)}logPath(t){return Ws.join(We,`${t}.log`)}spawnJob(t,n,r={}){V();let o=im.randomBytes(4).toString("hex"),s=this.logPath(o),i=this.metaPath(o);Fe.writeFileSync(s,"",{flag:"w",mode:384});let a=Fe.createWriteStream(s,{flags:"a"}),l=new Date().toISOString(),c=r.cwd,u=sm(t,["-c",n],{stdio:["ignore","pipe","pipe"],env:{...process.env,TERM:"dumb",...c?{PWD:c}:{}},...c?{cwd:c}:{}});this.running.set(o,u);let d={id:o,cmd:n,...r.name?{name:r.name}:{},pid:u.pid??null,startedAt:l,status:"running",exitCode:null,signal:null,...r.notifyPeerKey?{notifyPeerKey:r.notifyPeerKey}:{}};return mn(i,d),u.stdout?.on("data",p=>{a.write(p)}),u.stderr?.on("data",p=>{a.write(p)}),u.on("close",(p,f)=>{this.running.delete(o),a.end();let y=Kn(i)??d,h={...y,status:y.status==="killed"?"killed":"done",exitCode:p,signal:f??null,finishedAt:new Date().toISOString()};mn(i,h),r.onComplete&&r.onComplete(h)}),u.on("error",p=>{this.running.delete(o),a.end(()=>{try{Fe.appendFileSync(s,`
202
+ [spawn error] ${String(p)}
203
+ `)}catch{}});let y={...Kn(i)??d,status:"done",exitCode:null,signal:null,finishedAt:new Date().toISOString()};mn(i,y),r.onComplete&&r.onComplete(y)}),{id:o,meta:d}}list(){V();let t=[];try{t=Fe.readdirSync(We)}catch{return[]}let n=[];for(let r of t){if(!r.endsWith(".meta.json"))continue;let o=Kn(Ws.join(We,r));o&&n.push(o)}return n.sort((r,o)=>r.startedAt<o.startedAt?1:-1),n}tailLog(t,n){let r=this.logPath(t);if(!Fe.existsSync(r))return"(no log file)";let s=Fe.readFileSync(r,"utf8").split(`
204
+ `);return s.slice(Math.max(0,s.length-n)).join(`
205
+ `).trimEnd()||"(empty log)"}readSince(t,n){let r=this.logPath(t);if(!Fe.existsSync(r))return{text:"",nextOffset:n};let s=Fe.statSync(r).size;if(n>=s)return{text:"",nextOffset:s};let i=Buffer.alloc(s-n),a=Fe.openSync(r,"r");try{Fe.readSync(a,i,0,i.length,n)}finally{Fe.closeSync(a)}return{text:i.toString("utf8"),nextOffset:s}}resolveJobRef(t){return cm(this.list(),t)}kill(t){let n=this.metaPath(t),r=Kn(n);if(!r)return`Unknown job id: ${t}`;let o=this.running.get(t);if(o&&!o.killed)return o.kill("SIGTERM"),mn(n,{...r,status:"killed",signal:"SIGTERM"}),`Sent SIGTERM to job ${t} (pid ${r.pid??"?"})`;if(r.pid)try{return process.kill(r.pid,"SIGTERM"),mn(n,{...r,status:"killed",signal:"SIGTERM"}),`Sent SIGTERM to pid ${r.pid} (${t})`}catch{return`Job ${t} is not running (no live process).`}return`Job ${t} is not running.`}killAllRunning(){for(let[t,n]of this.running){n.killed||n.kill("SIGTERM");let r=Kn(this.metaPath(t));r&&mn(this.metaPath(t),{...r,status:"killed",signal:"SIGTERM"})}this.running.clear()}};import Hm from"node:fs";import{Bot as Bm,InputFile as jm}from"grammy";j();import Xl from"node:fs";import fn from"node:path";function um(e){return e.replace(/[^a-zA-Z0-9._-]+/g,"_").slice(0,120)||"unknown"}function dm(e){let r=fn.basename(e.trim()||"file").replace(/[^a-zA-Z0-9._-]+/g,"_").replace(/^\.+/,"").slice(0,180);return r.length>0?r:"file"}function pm(e,t){let n=new Date().toISOString().slice(0,10),r=um(t);return fn.join(e,r,n)}function Zr(e,t,n){let r=pm(e,t);W(r);let o=dm(n),s=fn.join(r,o);if(!Xl.existsSync(s))return s;let i=fn.extname(o),a=i?o.slice(0,-i.length):o;for(let l=1;l<1e4;l+=1){let c=`${a}-${l}${i}`;if(s=fn.join(r,c),!Xl.existsSync(s))return s}return fn.join(r,`${a}-${Date.now()}${i}`)}Se();import mm from"node:dns";import fm from"node:https";import{URL as gm}from"node:url";function gn(e){if(e instanceof Error){let t=e.cause;return t!=null?`${e.message} (${String(t)})`:e.message}return String(e)}Se();function Zl(e){if("photo"in e&&e.photo&&e.photo.length>0)return{fileId:e.photo[e.photo.length-1].file_id,baseName:"photo.jpg"};if("document"in e&&e.document){let t=e.document,n=t.file_name?.trim();return{fileId:t.file_id,baseName:n&&n.length>0?n:"document.bin"}}if("video"in e&&e.video){let t=e.video,n=t.file_name?.trim();return{fileId:t.file_id,baseName:n&&n.length>0?n:"video.mp4"}}if("audio"in e&&e.audio){let t=e.audio,n=t.file_name?.trim();return{fileId:t.file_id,baseName:n&&n.length>0?n:"audio.m4a"}}if("voice"in e&&e.voice)return{fileId:e.voice.file_id,baseName:"voice.ogg"};if("video_note"in e&&e.video_note)return{fileId:e.video_note.file_id,baseName:"video_note.mp4"};if("sticker"in e&&e.sticker){let t=e.sticker,n=t.is_video?"webm":"webp";return{fileId:t.file_id,baseName:`sticker.${n}`}}return null}function hm(e,t){let r=t.split("/").filter(o=>o.length>0).map(encodeURIComponent).join("/");return`https://api.telegram.org/file/bot${e}/${r}`}var ec="omnish (Telegram bot; https://github.com/labKnowledge/whatsLive)";function ym(e){let t=new gm(e);return new Promise((n,r)=>{let o=fm.request({hostname:t.hostname,port:t.port||443,path:t.pathname+t.search,method:"GET",headers:{"User-Agent":ec,Accept:"*/*"},lookup(s,i,a){mm.lookup(s,{family:4},a)}},s=>{let i=s.statusCode??0,a=[];s.on("data",l=>a.push(l)),s.on("end",()=>n({status:i,buffer:Buffer.concat(a)})),s.on("error",r)});o.on("error",r),o.end()})}async function tc(e,t,n){let r;try{r=await e.api.getFile(t)}catch(i){return I.warn({err:String(i),fileId:t},"telegram getFile failed"),{error:"Could not resolve file on Telegram."}}if(!r.file_path)return{error:"Telegram did not return a file path."};let o=hm(e.token,r.file_path),s;try{let i=await fetch(o,{headers:{"User-Agent":ec,Accept:"*/*"}});if(!i.ok)return I.warn({status:i.status,statusText:i.statusText,fileId:t},"telegram file fetch HTTP error"),{error:`Could not download file from Telegram (HTTP ${i.status}${i.statusText?` ${i.statusText}`:""}).`};try{s=Buffer.from(await i.arrayBuffer())}catch(a){return I.warn({err:String(a),fileId:t},"telegram file read body failed"),{error:`Could not read file from Telegram (${String(a)}).`}}}catch(i){I.warn({err:String(i),fileId:t},"telegram file fetch failed; retrying via HTTPS IPv4");try{let a=await ym(o);if(a.status<200||a.status>=300)return I.warn({status:a.status,fileId:t},"telegram HTTPS IPv4 fallback HTTP error"),{error:`Could not download file from Telegram (HTTP ${a.status}).`};s=a.buffer}catch(a){return I.warn({err:String(i),err2:String(a),fileId:t},"telegram file download failed (fetch and IPv4 fallback)"),{error:`Could not download file from Telegram (network: ${gn(i)}; IPv4 fallback: ${gn(a)}).`}}}return n>0&&s.byteLength>n?{error:`Media too large (max ${n} bytes).`}:{buffer:s}}de();Ue();import{downloadMediaMessage as wm,extensionForMediaMessage as km,getContentType as eo,isJidGroup as oc,isLidUser as bm}from"@whiskeysockets/baileys";import Sm from"node:fs";Se();function vm(e){if(!e||typeof e!="object")return"";let t=e;if(typeof t.conversation=="string")return t.conversation;let n=t.extendedTextMessage;return n&&typeof n.text=="string"?n.text:""}function xm(e){if(!e||typeof e!="object")return"";let t=e;for(let n of["imageMessage","videoMessage","documentMessage","audioMessage","stickerMessage"]){let r=t[n];if(r&&typeof r.caption=="string"&&r.caption.trim())return r.caption}return""}function sc(e){return[vm(e),xm(e)].filter(n=>n.trim().length>0).join(`
206
+ `).trim()}function Gs(e){return e.replace(/[\u0000-\u0008\u000B\u000C\u000E-\u001F\u007F]/g,"").replace(/\uFEFF/g,"").trim()}function ic(e){let t=eo(e??void 0);return t==="imageMessage"||t==="videoMessage"||t==="audioMessage"||t==="documentMessage"||t==="stickerMessage"}function $m(e){let t=eo(e??void 0);if(!t)return;let r=e?.[t]?.fileLength;if(typeof r=="number"&&Number.isFinite(r))return r;if(r&&typeof r=="object"&&"toNumber"in r&&typeof r.toNumber=="function")try{let o=r.toNumber();return Number.isFinite(o)?o:void 0}catch{return}}function nc(e){try{if(!e)return"file.bin";let t=km(e);return`file${t&&t.length>0?t.startsWith(".")?t:`.${t}`:".bin"}`}catch{return`${(eo(e??void 0)??"file").replace("Message","")||"file"}.bin`}}function Cm(e){let t=eo(e??void 0);if(!t)return nc(e);let n=e?.[t];return n?.fileName&&typeof n.fileName=="string"&&n.fileName.trim()?n.fileName.trim():nc(e)}async function rc(e,t,n,r){let o=t.message??void 0;if(!ic(o))return{};let s=r.fileReceiveMaxBytes,i=$m(o);if(s>0&&i!==void 0&&i>s)return{error:`Media too large (max ${s} bytes).`};let a;try{a=await wm(t,"buffer",{},{logger:e.logger,reuploadRequest:e.updateMediaMessage})}catch(d){return I.warn({err:String(d),detail:gn(d)},"whatsapp downloadMediaMessage failed"),{error:`Could not download media from WhatsApp (${gn(d)}).`}}if(s>0&&a.length>s)return{error:`Media too large (max ${s} bytes).`};let l;try{l=Zt(r,n)}catch(d){return{error:String(d)}}let c=Cm(o),u=Zr(l,n,c);try{Sm.writeFileSync(u,a,{mode:384})}catch{return{error:"Could not write media to inbox."}}return{path:u}}async function Rm(e,t){let n=t.remoteJid??"";if(!n)return{fromJid:"",fromE164:""};if(t.remoteJidAlt){let r=q(t.remoteJidAlt)??q(n)??"";return{fromJid:n,fromE164:r}}if(bm(n))try{let r=await e.signalRepository?.lidMapping?.getPNForLID?.(n);if(r){let o=q(r)??"";if(o)return{fromJid:n,fromE164:o}}}catch{}return{fromJid:n,fromE164:q(n)??""}}function Mm(e){try{if(!$().clusterEnabled)return;let n=sc(e.message??void 0);if(!n)return;let r=Qa(n);if(!r)return;let o=e.key?.remoteJid??"",s=null;if(o&&!oc(o)){let i=q(o);i&&(s=`wa:${i}`)}sl(r,s)}catch(t){I.warn({err:String(t)},"cluster footer observation failed")}}function ac(e,t){let n=r=>{r.type==="notify"&&(async()=>{for(let o of r.messages){let s=o.key;if(!s)continue;if(s.fromMe){Mm(o);continue}let i=s.remoteJid;if(!i||oc(i)||i.toLowerCase().endsWith("@status")||i==="status@broadcast")continue;let l=Gs(sc(o.message??void 0)),{fromJid:c,fromE164:u}=await Rm(e,s),d=`wa:${c}`,p=ic(o.message??void 0);if(p&&!l){(async()=>{try{let h=$(),w=await rc(e,o,d,h);await t({fromJid:c,fromE164:u,text:"",messageId:s.id??void 0,mediaSavedPath:w.path,mediaError:w.error})}catch(h){I.error({err:String(h),fromJid:c},"whatsapp media-only background task failed")}})();continue}let f,y;if(p){let h=$(),w=await rc(e,o,d,h);f=w.path,y=w.error}!l&&!f&&!y||await t({fromJid:c,fromE164:u,text:l,messageId:s.id??void 0,mediaSavedPath:f,mediaError:y})}})()};return e.ev.on("messages.upsert",n),()=>{e.ev.off("messages.upsert",n)}}Se();import Nm from"node:fs";import Tm from"node:process";import Em,{DisconnectReason as Gt,fetchLatestBaileysVersion as Im,makeCacheableSignalKeyStore as Am,useMultiFileAuthState as Pm}from"@whiskeysockets/baileys";import Om from"qrcode-terminal";j();Se();var Lm="0.1.0";function Js(e){return e?.error?.output?.statusCode}async function to(e={}){V();let t=e.authDir??X,n=e.verbose===!0,r=cl(),{state:o,saveCreds:s}=await Pm(t),{version:i}=await Im(),a=Em({version:i,logger:r,printQRInTerminal:!1,browser:["omnish","cli",Lm],auth:{creds:o.creds,keys:Am(o.keys,r)},syncFullHistory:!1,markOnlineOnConnect:!1});return a.ev.on("creds.update",s),a.ev.on("connection.update",l=>{let{connection:c,lastDisconnect:u,qr:d}=l;if(d&&(e.onQr?.(d),e.printQr)){let p=Tm.stdout,f=g(p,"\xB7".repeat(42));console.log(J(p,"Scan with WhatsApp \u2192 Linked devices")),console.log(f),Om.generate(d,{small:!0}),console.log(f)}if(c==="close"){let p=Js(u);n&&p===Gt.loggedOut&&r.warn("WhatsApp session logged out (401).")}c==="open"&&n&&r.info("WhatsApp Web connected.")}),a.ws&&typeof a.ws.on=="function"&&a.ws.on("error",l=>{r.error({err:String(l)},"WebSocket error")}),a}function hn(e){try{e.end(new Error("omnish: socket closed"))}catch{e.ws?.close()}}function zs(e){return e?.output?.statusCode??e?.status??e?.error?.output?.statusCode}function no(e){return e.user?.id?Promise.resolve():new Promise((t,n)=>{let r=o=>{if(o.connection==="open"){e.ev.off("connection.update",r),t();return}if(o.connection==="close"){e.ev.off("connection.update",r);let i=o.lastDisconnect?.error??o.lastDisconnect??new Error("Connection closed");n(i)}};e.ev.on("connection.update",r)})}function Jt(e,t,n){return new Promise((r,o)=>{let s=setTimeout(()=>o(new Error(n)),t);e.then(i=>{clearTimeout(s),r(i)},i=>{clearTimeout(s),o(i)})})}var pc=3500,_m=400,lc=18e4,cc=9e4,uc=3e5;function Ks(e,t=pc){if(e.length<=t)return[e];let n=[],r=0;for(;r<e.length;){let o=Math.min(r+t,e.length);if(o<e.length){let i=e.slice(r,o).lastIndexOf(`
226
207
 
227
- `);i>Math.floor(t*.35)&&(o=r+i+2)}n.push(e.slice(r,o)),r=o}return n}function wr(e){return new Promise(t=>setTimeout(t,e))}async function rl(e,t){await Promise.race([e,wr(el).then(()=>{E.warn({jid:t,ms:el},"whatsapp outbound self-heal: prior send chain waited too long; continuing")})])}async function Pd(e,t,n,r=3){let o;for(let s=1;s<=r;s+=1)try{await kt(e.sendMessage(t,{text:n}),tl,`whatsapp sendMessage timed out after ${tl}ms`);return}catch(i){o=i;let a=String(i);if(/not connected|closed|timed out|timeout/i.test(a)&&s<r){await wr(800*s);continue}throw i}throw o}function Fd(e,t){let n=e.caption;switch(e.category){case"image":return{image:t,caption:n,mimetype:e.mimetype};case"video":return{video:t,caption:n,mimetype:e.mimetype};case"audio":return{audio:t,mimetype:e.mimetype,ptt:!1};case"document":return{document:t,mimetype:e.mimetype,fileName:e.displayName,caption:n}}}async function Nd(e,t,n,r=3){let o;for(let s=1;s<=r;s+=1)try{await kt(e.sendMessage(t,n),nl,`whatsapp sendMedia timed out after ${nl}ms`);return}catch(i){o=i;let a=String(i);if(/not connected|closed|timed out|timeout/i.test(a)&&s<r){await wr(800*s);continue}throw i}throw o}function sl(e,t={}){let n=new Map,r=t.decorate??(o=>o);return{async sendText(o,s){let i=r(s,o),a=n.get(o)??Promise.resolve(),l=rl(a,o).then(async()=>{let u=jo(i,ol);for(let c=0;c<u.length;c+=1)await Pd(e,o,u[c]??""),c<u.length-1&&await wr(Od)}).catch(u=>{E.error({err:String(u),jid:o},"sendText failed")});n.set(o,l),await l.finally(()=>{n.get(o)===l&&n.delete(o)})},async sendMedia(o,s){let i=Ld.readFileSync(s.absPath),a=Fd(s,i),l=n.get(o)??Promise.resolve(),u=rl(l,o).then(async()=>{await Nd(e,o,a)}).catch(c=>{E.error({err:String(c),jid:o},"sendMedia failed")});n.set(o,u),await u.finally(()=>{n.get(o)===u&&n.delete(o)})}}}var Dd=400;async function il(e,t,n,r){let o=t(),s=await qa(e,r.fileId,o.fileReceiveMaxBytes);if("error"in s)return{mediaError:s.error};let i;try{i=Ct(o,n)}catch(l){return{mediaError:String(l)}}let a=fr(i,n,r.baseName);try{return _d.writeFileSync(a,s.buffer,{mode:384}),{mediaSavedPath:a}}catch{return{mediaError:"Could not write media to inbox."}}}function jd(e){return new Promise(t=>setTimeout(t,e))}async function Uo(e,t,n,r={}){let o=new Bd(e);await o.api.deleteWebhook({drop_pending_updates:!1});let s=new Map,i=r.decorate??(c=>c);async function a(c,d){let m=Math.min(t().appsMaxWaChars,4096),y=De(d,"telegram"),g=i(y.text,Po(c)),f=y.parseModeHtml,x=(s.get(c)??Promise.resolve()).then(async()=>{let k=jo(g,m);for(let $=0;$<k.length;$+=1){let A=k[$]??"",M=f?{parse_mode:"HTML"}:void 0;try{await o.api.sendMessage(c,A,M)}catch(L){if(f){E.warn({err:String(L),chatId:c},"telegram HTML send failed; retrying plain");let U=A.replace(/<[^>]+>/g,"");await o.api.sendMessage(c,U)}else throw L}$<k.length-1&&await jd(Dd)}}).catch(k=>{E.error({err:String(k),chatId:c},"telegram sendText failed")});s.set(c,x),await x.finally(()=>{s.get(c)===x&&s.delete(c)})}async function l(c,d){let m=new Hd(d.absPath,d.displayName),y=d.caption,f=(s.get(c)??Promise.resolve()).then(async()=>{switch(d.category){case"image":await o.api.sendPhoto(c,m,y?{caption:y}:void 0);break;case"video":await o.api.sendVideo(c,m,y?{caption:y}:void 0);break;case"audio":await o.api.sendAudio(c,m,y?{caption:y}:void 0);break;case"document":await o.api.sendDocument(c,m,y?{caption:y}:void 0);break}}).catch(h=>{E.error({err:String(h),chatId:c},"telegram sendMedia failed")});s.set(c,f),await f.finally(()=>{s.get(c)===f&&s.delete(c)})}o.on("message",async c=>{let d=c.chat,m=c.message;if(!d||d.type!=="private"||!c.from||!m)return;let y="text"in m&&m.text?m.text:"",g="caption"in m&&m.caption?m.caption:"",f=Bo([y,g].filter(Boolean).join(`
228
- `)),h=Ja(m),x=Po(d.id),k=d.id,$=async L=>{if(L.kind==="file")await l(k,L.spec);else if(L.kind==="files")for(let U of L.specs)await l(k,U);else await a(k,L.body)};if(h&&!f){(async()=>{try{let L=await il(o,t,x,h);if(!L.mediaSavedPath&&!L.mediaError)return;await n({peerKey:x,text:"",tgChatId:d.id,tgReplyToMessageId:m.message_id,mediaSavedPath:L.mediaSavedPath,mediaError:L.mediaError},$)}catch(L){E.error({err:String(L),chatId:k},"telegram media-only background task failed")}})();return}let A,M;if(h){let L=await il(o,t,x,h);A=L.mediaSavedPath,M=L.mediaError}!f&&!A&&!M||await n({peerKey:x,text:f,tgChatId:d.id,tgReplyToMessageId:m.message_id,mediaSavedPath:A,mediaError:M},$)}),o.catch(c=>{E.error({err:String(c)},"telegram bot error")});let u=o.start();return{bot:o,sendText:a,sendMedia:l,stop:async()=>{await o.stop(),await u.catch(()=>{})}}}import Ud from"node:fs";import Wd from"node:path";function Gd(e){let t=Wd.join(e,"creds.json");try{let n=Ud.readFileSync(t,"utf8"),r=JSON.parse(n);if(!r.me||typeof r.me!="object"||r.me===null)return null;let o=r.me,s=typeof o.id=="string"?o.id:void 0,i=typeof o.phoneNumber=="string"?o.phoneNumber:void 0;return!s&&!i?null:{id:s,phoneNumber:i}}catch{return null}}function al(e){let t=Gd(e);if(!t)return null;let n=t.phoneNumber?.trim();if(n){let s=G(n);return s?`phone ${s}`:`phone ${n}`}let r=t.id?.trim();if(!r)return null;let o=r.toLowerCase();if(o.endsWith("@s.whatsapp.net")||o.endsWith("@c.us")){let s=G(r);return s?`phone ${s}`:`id ${r}`}return o.endsWith("@lid")?`device ${r} (LID \u2014 not an E.164; your number may appear after the gateway runs)`:`id ${r}`}import kr from"node:fs";import Ie from"node:process";import ll from"node:fs";var Jd="Timed out after 10 minutes. Phone stuck on \u201CLogging in\u201D usually means: run `pnpm approve-builds && pnpm install`, check network/firewall, then try `omnish link --force` again.";function br(e){let t=String(e).toLowerCase();return t.includes("401")||t.includes("logged out")?!0:Do(e)===bt.loggedOut}function zd(e){return new Promise((t,n)=>{let r=()=>{n(new Error("Pairing cancelled."))};if(e.aborted){r();return}e.addEventListener("abort",r,{once:!0})})}async function Wo(e){let t=!1;for(let n=0;n<3;n++){if(e.signal?.aborted)throw new Error("Pairing cancelled.");let r=await hr({authDir:e.authDir,printQr:e.printQr===!0,verbose:e.verbose===!0,onQr:e.onQr});e.onSocketReady?.(r);try{let o=kt(yr(r),6e5,Jd);e.signal?await Promise.race([o,zd(e.signal)]):await o;return}catch(o){if(o instanceof Error&&o.message==="Pairing cancelled.")throw o;if(Do(o)===bt.restartRequired&&!t){t=!0,e.onRestart515?.(),await new Promise(i=>setTimeout(i,1500));continue}throw o}finally{e.onSocketClosed?.(),Ut(r)}}throw new Error("Pairing failed after restart (515) retries.")}async function cl(e){let t=e.authDir??J;z();for(let n=1;n<=2;n++)try{await Wo({...e,authDir:t});return}catch(r){if(r instanceof Error&&r.message==="Pairing cancelled.")throw r;if(n===1&&br(r)){ll.rmSync(t,{recursive:!0,force:!0}),ll.mkdirSync(t,{recursive:!0,mode:448});continue}throw r}}async function qd(e,t){let n=Ie.stdout;console.log(`
229
- ${ie(n,"omnish link")} ${b(n,"\u2014 QR appears below; scan from WhatsApp \u2192 Linked devices.")}
230
- `),await Wo({authDir:e,verbose:t,printQr:!0,onRestart515:()=>{console.warn(`
231
- ${ae(Ie.stderr,"WhatsApp requested a restart after pairing (code 515). This is normal. Opening a new connection\u2026")}
232
- `)}})}async function ul(e={}){let t=e.authDir??J,n=e.verbose===!0;z(),e.force&&(kr.rmSync(t,{recursive:!0,force:!0}),kr.mkdirSync(t,{recursive:!0,mode:448}),console.log(`${xe(Ie.stdout,"Cleared saved session (--force).")} ${b(Ie.stdout,"Requesting a new QR\u2026")}
233
- `));for(let r=1;r<=2;r++)try{await qd(t,n),console.log(`
234
- ${Z(Ie.stdout,"Linked.")} ${v(Ie.stdout,"Session saved. You can run")} ${Z(Ie.stdout,"omnish run")} ${v(Ie.stdout,"now.")}
235
- `);return}catch(o){if(r===1&&br(o)){console.warn(`
236
- ${ae(Ie.stderr,"WhatsApp returned logged-out (401). This often happens after Ctrl+C during link or corrupt auth files.")}
237
- ${ae(Ie.stderr,"Clearing auth dir and retrying once with a fresh QR\u2026")}
238
- `),kr.rmSync(t,{recursive:!0,force:!0}),kr.mkdirSync(t,{recursive:!0,mode:448});continue}throw br(o)&&console.error(`
239
- ${O(Ie.stderr,"Still failing after a clean auth directory. Try:")}
240
- ${b(Ie.stderr,` pnpm approve-builds && pnpm install
208
+ `);i>Math.floor(t*.35)&&(o=r+i+2)}n.push(e.slice(r,o)),r=o}return n}function ro(e){return new Promise(t=>setTimeout(t,e))}async function dc(e,t){await Promise.race([e,ro(lc).then(()=>{I.warn({jid:t,ms:lc},"whatsapp outbound self-heal: prior send chain waited too long; continuing")})])}async function Fm(e,t,n,r=3){let o;for(let s=1;s<=r;s+=1)try{await Jt(e.sendMessage(t,{text:n}),cc,`whatsapp sendMessage timed out after ${cc}ms`);return}catch(i){o=i;let a=String(i);if(/not connected|closed|timed out|timeout/i.test(a)&&s<r){await ro(800*s);continue}throw i}throw o}function Um(e,t){let n=e.caption;switch(e.category){case"image":return{image:t,caption:n,mimetype:e.mimetype};case"video":return{video:t,caption:n,mimetype:e.mimetype};case"audio":return{audio:t,mimetype:e.mimetype,ptt:!1};case"document":return{document:t,mimetype:e.mimetype,fileName:e.displayName,caption:n}}}async function Dm(e,t,n,r=3){let o;for(let s=1;s<=r;s+=1)try{await Jt(e.sendMessage(t,n),uc,`whatsapp sendMedia timed out after ${uc}ms`);return}catch(i){o=i;let a=String(i);if(/not connected|closed|timed out|timeout/i.test(a)&&s<r){await ro(800*s);continue}throw i}throw o}function mc(e,t={}){let n=new Map,r=t.decorate??(o=>o);return{async sendText(o,s){let i=r(s,o),a=n.get(o)??Promise.resolve(),l=dc(a,o).then(async()=>{let c=Ks(i,pc);for(let u=0;u<c.length;u+=1)await Fm(e,o,c[u]??""),u<c.length-1&&await ro(_m)}).catch(c=>{I.error({err:String(c),jid:o},"sendText failed")});n.set(o,l),await l.finally(()=>{n.get(o)===l&&n.delete(o)})},async sendMedia(o,s){let i=Nm.readFileSync(s.absPath),a=Um(s,i),l=n.get(o)??Promise.resolve(),c=dc(l,o).then(async()=>{await Dm(e,o,a)}).catch(u=>{I.error({err:String(u),jid:o},"sendMedia failed")});n.set(o,c),await c.finally(()=>{n.get(o)===c&&n.delete(o)})}}}function fc(e){let t=e.trim();return t?/^\/id(?:@[\w_]+)?$/i.test(t):!1}function gc(e){let t=String(e).replace(/\D/g,"");return`Your Telegram user id: ${t}
209
+ Add to allowlist on this gateway host:
210
+ omnish allow tg:${t}`}var Wm=400;async function hc(e,t,n,r){let o=t(),s=await tc(e,r.fileId,o.fileReceiveMaxBytes);if("error"in s)return{mediaError:s.error};let i;try{i=Zt(o,n)}catch(l){return{mediaError:String(l)}}let a=Zr(i,n,r.baseName);try{return Hm.writeFileSync(a,s.buffer,{mode:384}),{mediaSavedPath:a}}catch{return{mediaError:"Could not write media to inbox."}}}function Gm(e){return new Promise(t=>setTimeout(t,e))}async function qs(e,t,n,r={}){let o=new Bm(e);await o.api.deleteWebhook({drop_pending_updates:!1});let s=new Map,i=r.decorate??(u=>u);async function a(u,d){let p=Math.min(t().appsMaxWaChars,4096),f=Pe(d,"telegram"),y=i(f.text,Ss(u)),h=f.parseModeHtml,v=(s.get(u)??Promise.resolve()).then(async()=>{let x=Ks(y,p);for(let T=0;T<x.length;T+=1){let M=x[T]??"",E=h?{parse_mode:"HTML"}:void 0;try{await o.api.sendMessage(u,M,E)}catch(R){if(h){I.warn({err:String(R),chatId:u},"telegram HTML send failed; retrying plain");let _=M.replace(/<[^>]+>/g,"");await o.api.sendMessage(u,_)}else throw R}T<x.length-1&&await Gm(Wm)}}).catch(x=>{I.error({err:String(x),chatId:u},"telegram sendText failed")});s.set(u,v),await v.finally(()=>{s.get(u)===v&&s.delete(u)})}async function l(u,d){let p=new jm(d.absPath,d.displayName),f=d.caption,h=(s.get(u)??Promise.resolve()).then(async()=>{switch(d.category){case"image":await o.api.sendPhoto(u,p,f?{caption:f}:void 0);break;case"video":await o.api.sendVideo(u,p,f?{caption:f}:void 0);break;case"audio":await o.api.sendAudio(u,p,f?{caption:f}:void 0);break;case"document":await o.api.sendDocument(u,p,f?{caption:f}:void 0);break}}).catch(w=>{I.error({err:String(w),chatId:u},"telegram sendMedia failed")});s.set(u,h),await h.finally(()=>{s.get(u)===h&&s.delete(u)})}o.on("message",async u=>{let d=u.chat,p=u.message;if(!d||d.type!=="private"||!u.from||!p)return;let f="text"in p&&p.text?p.text:"",y="caption"in p&&p.caption?p.caption:"",h=Gs([f,y].filter(Boolean).join(`
211
+ `));if(h&&fc(h)){let R=u.from.id;await o.api.sendMessage(d.id,gc(R));return}let w=Zl(p),v=Ss(d.id),x=d.id,T=async R=>{if(R.kind==="file")await l(x,R.spec);else if(R.kind==="files")for(let _ of R.specs)await l(x,_);else await a(x,R.body)};if(w&&!h){(async()=>{try{let R=await hc(o,t,v,w);if(!R.mediaSavedPath&&!R.mediaError)return;await n({peerKey:v,text:"",tgChatId:d.id,tgReplyToMessageId:p.message_id,mediaSavedPath:R.mediaSavedPath,mediaError:R.mediaError},T)}catch(R){I.error({err:String(R),chatId:x},"telegram media-only background task failed")}})();return}let M,E;if(w){let R=await hc(o,t,v,w);M=R.mediaSavedPath,E=R.mediaError}!h&&!M&&!E||await n({peerKey:v,text:h,tgChatId:d.id,tgReplyToMessageId:p.message_id,mediaSavedPath:M,mediaError:E},T)}),o.catch(u=>{I.error({err:String(u)},"telegram bot error")});let c=o.start();return{bot:o,sendText:a,sendMedia:l,stop:async()=>{await o.stop(),await c.catch(()=>{})}}}Ue();import Jm from"node:fs";import zm from"node:path";function Km(e){let t=zm.join(e,"creds.json");try{let n=Jm.readFileSync(t,"utf8"),r=JSON.parse(n);if(!r.me||typeof r.me!="object"||r.me===null)return null;let o=r.me,s=typeof o.id=="string"?o.id:void 0,i=typeof o.phoneNumber=="string"?o.phoneNumber:void 0;return!s&&!i?null:{id:s,phoneNumber:i}}catch{return null}}function yc(e){let t=Km(e);if(!t)return null;let n=t.phoneNumber?.trim();if(n){let s=q(n);return s?`phone ${s}`:`phone ${n}`}let r=t.id?.trim();if(!r)return null;let o=r.toLowerCase();if(o.endsWith("@s.whatsapp.net")||o.endsWith("@c.us")){let s=q(r);return s?`phone ${s}`:`id ${r}`}return o.endsWith("@lid")?`device ${r} (LID \u2014 not an E.164; your number may appear after the gateway runs)`:`id ${r}`}import so from"node:fs";import je from"node:process";j();j();import wc from"node:fs";var qm="Timed out after 10 minutes. Phone stuck on \u201CLogging in\u201D usually means: run `pnpm approve-builds && pnpm install`, check network/firewall, then try `omnish link --force` again.";function oo(e){let t=String(e).toLowerCase();return t.includes("401")||t.includes("logged out")?!0:zs(e)===Gt.loggedOut}function Ym(e){return new Promise((t,n)=>{let r=()=>{n(new Error("Pairing cancelled."))};if(e.aborted){r();return}e.addEventListener("abort",r,{once:!0})})}async function Ys(e){let t=!1;for(let n=0;n<3;n++){if(e.signal?.aborted)throw new Error("Pairing cancelled.");let r=await to({authDir:e.authDir,printQr:e.printQr===!0,verbose:e.verbose===!0,onQr:e.onQr});e.onSocketReady?.(r);try{let o=Jt(no(r),6e5,qm);e.signal?await Promise.race([o,Ym(e.signal)]):await o;return}catch(o){if(o instanceof Error&&o.message==="Pairing cancelled.")throw o;if(zs(o)===Gt.restartRequired&&!t){t=!0,e.onRestart515?.(),await new Promise(i=>setTimeout(i,1500));continue}throw o}finally{e.onSocketClosed?.(),hn(r)}}throw new Error("Pairing failed after restart (515) retries.")}async function kc(e){let t=e.authDir??X;V();for(let n=1;n<=2;n++)try{await Ys({...e,authDir:t});return}catch(r){if(r instanceof Error&&r.message==="Pairing cancelled.")throw r;if(n===1&&oo(r)){wc.rmSync(t,{recursive:!0,force:!0}),wc.mkdirSync(t,{recursive:!0,mode:448});continue}throw r}}async function Qm(e,t){let n=je.stdout;console.log(`
212
+ ${me(n,"omnish link")} ${g(n,"\u2014 QR appears below; scan from WhatsApp \u2192 Linked devices.")}
213
+ `),await Ys({authDir:e,verbose:t,printQr:!0,onRestart515:()=>{console.warn(`
214
+ ${N(je.stderr,"WhatsApp requested a restart after pairing (code 515). This is normal. Opening a new connection\u2026")}
215
+ `)}})}async function bc(e={}){let t=e.authDir??X,n=e.verbose===!0;V(),e.force&&(so.rmSync(t,{recursive:!0,force:!0}),so.mkdirSync(t,{recursive:!0,mode:448}),console.log(`${ae(je.stdout,"Cleared saved session (--force).")} ${g(je.stdout,"Requesting a new QR\u2026")}
216
+ `));for(let r=1;r<=2;r++)try{await Qm(t,n),console.log(`
217
+ ${re(je.stdout,"Linked.")} ${k(je.stdout,"Session saved. You can run")} ${re(je.stdout,"omnish run")} ${k(je.stdout,"now.")}
218
+ `);return}catch(o){if(r===1&&oo(o)){console.warn(`
219
+ ${N(je.stderr,"WhatsApp returned logged-out (401). This often happens after Ctrl+C during link or corrupt auth files.")}
220
+ ${N(je.stderr,"Clearing auth dir and retrying once with a fresh QR\u2026")}
221
+ `),so.rmSync(t,{recursive:!0,force:!0}),so.mkdirSync(t,{recursive:!0,mode:448});continue}throw oo(o)&&console.error(`
222
+ ${C(je.stderr,"Still failing after a clean auth directory. Try:")}
223
+ ${g(je.stderr,` pnpm approve-builds && pnpm install
241
224
  (pnpm may have skipped Baileys/sharp/protobuf build scripts.)
242
225
  Then: omnish link --force
243
- `)}`),o}}import{spawn as Qd,spawnSync as Yd}from"node:child_process";import Wt from"node:fs";import Vd from"node:path";import Gt from"node:process";function Sr(e){z(),H(Vd.dirname(e));let t=Gt.argv[1];if(!t)return{ok:!1,message:"Cannot resolve entry script; invoke via node path/to/dist/index.js."};let n=Wt.openSync(e,"a"),r=Qd(Gt.execPath,[t,"run"],{detached:!0,stdio:["ignore",n,n],env:{...Gt.env,OMNISH_BACKGROUND_GATEWAY:"1"}});return Wt.closeSync(n),r.unref(),r.pid?{ok:!0,pid:r.pid}:{ok:!1,message:"Failed to start background gateway."}}function xr(){if(z(),!Wt.existsSync(ne))return{outcome:"no_pidfile"};let e=Wt.readFileSync(ne,"utf8").trim(),t=Number(e);if(!Number.isFinite(t)||t<=0){try{Wt.unlinkSync(ne)}catch{}return{outcome:"invalid_pidfile"}}try{Gt.kill(t,0)}catch{try{Wt.unlinkSync(ne)}catch{}return{outcome:"stale_cleaned",pid:t}}try{return Gt.kill(t,"SIGTERM"),{outcome:"sent_signal",pid:t}}catch(n){return Gt.platform==="win32"&&Yd("taskkill",["/PID",String(t),"/T"],{windowsHide:!0}).status===0?{outcome:"taskkill_ok",pid:t}:{outcome:"failed",message:`could not signal process: ${String(n)}`}}}import Kd from"node:crypto";import Go from"node:fs";import Xd from"node:net";var bn=null;function Zd(){try{let e=Go.readFileSync(xt,"utf8"),t=JSON.parse(e);return typeof t.token=="string"?t.token:""}catch{return""}}function ep(e){Go.writeFileSync(xt,JSON.stringify(e,null,2)+`
244
- `,{mode:384})}function tp(){try{Go.unlinkSync(xt)}catch{}}async function np(e,t){let n=t.getCfg(),r=typeof e.absPath=="string"?e.absPath.trim():"";if(!r)return{ok:!1,error:"Missing absPath."};let o=typeof e.caption=="string"&&e.caption.length>0?e.caption:void 0,s=et(r,n.fileSendMaxBytes);if("error"in s)return{ok:!1,error:s.error};let i={absPath:s.absPath,category:s.category,mimetype:s.mimetype,displayName:s.displayName,caption:o};if(e.channel==="whatsapp"){let a=t.getWaOutbound();if(!a)return{ok:!1,error:"WhatsApp outbound is not connected."};let l=n.gatewayMode;if(l!=="whatsapp"&&l!=="both")return{ok:!1,error:"gatewayMode does not include WhatsApp."};let u=typeof e.e164=="string"?e.e164.trim():"";if(!u.startsWith("+"))return{ok:!1,error:"WhatsApp destination must be E.164 (e.g. +15551234567)."};let c=Qt(u);try{return await a.sendMedia(c,i),{ok:!0}}catch(d){return{ok:!1,error:String(d)}}}if(e.channel==="telegram"){let a=t.getTgSendMedia();if(!a)return{ok:!1,error:"Telegram outbound is not connected."};let l=n.gatewayMode;if(l!=="telegram"&&l!=="both")return{ok:!1,error:"gatewayMode does not include Telegram."};if(!Number.isFinite(e.chatId))return{ok:!1,error:"Invalid Telegram chat id."};try{return await a(e.chatId,i),{ok:!0}}catch(u){return{ok:!1,error:String(u)}}}return{ok:!1,error:"Unknown channel."}}async function rp(e,t){let n=t.getCfg(),r=typeof e.text=="string"?e.text.trim():"";if(!r)return{ok:!1,error:"Missing or empty text."};if(e.channel==="whatsapp"){let o=t.getWaOutbound();if(!o)return{ok:!1,error:"WhatsApp outbound is not connected."};let s=n.gatewayMode;if(s!=="whatsapp"&&s!=="both")return{ok:!1,error:"gatewayMode does not include WhatsApp."};let i=typeof e.e164=="string"?e.e164.trim():"";if(!i.startsWith("+"))return{ok:!1,error:"WhatsApp destination must be E.164 (e.g. +15551234567)."};let a=Qt(i);try{return await o.sendText(a,r),{ok:!0}}catch(l){return{ok:!1,error:String(l)}}}if(e.channel==="telegram"){let o=t.getTgSendText();if(!o)return{ok:!1,error:"Telegram outbound is not connected."};let s=n.gatewayMode;if(s!=="telegram"&&s!=="both")return{ok:!1,error:"gatewayMode does not include Telegram."};if(!Number.isFinite(e.chatId))return{ok:!1,error:"Invalid Telegram chat id."};try{return await o(e.chatId,p(r)),{ok:!0}}catch(i){return{ok:!1,error:String(i)}}}return{ok:!1,error:"Unknown channel."}}async function op(e,t,n){let r;try{r=JSON.parse(e)}catch{return{ok:!1,error:"Invalid JSON."}}return!r||typeof r!="object"?{ok:!1,error:"Invalid request."}:typeof r.token!="string"||!n||r.token!==n?{ok:!1,error:"Unauthorized."}:r.op==="sendMedia"?np(r,t):r.op==="sendText"?rp(r,t):{ok:!1,error:"Unsupported operation."}}function dl(e){if(bn)return;let t=Kd.randomBytes(32).toString("hex"),n=Xd.createServer(r=>{let o="";r.setTimeout(12e4),r.on("data",s=>{o+=s.toString("utf8");let i=o.indexOf(`
245
- `);if(i===-1)return;let a=o.slice(0,i).trim();o=o.slice(i+1);let l=Zd();op(a,e,l).then(u=>{r.write(`${JSON.stringify(u)}
246
- `),r.end()})}),r.on("error",()=>{})});n.listen(0,"127.0.0.1",()=>{let r=n.address();if(!r||typeof r=="string"){E.error("gateway control: could not read listen address");return}let o={token:t,host:r.address,port:r.port};ep(o),E.info({port:r.port},"gateway control listening")}),n.on("error",r=>{E.error({err:String(r)},"gateway control server error")}),bn=n}function vr(){if(bn){try{bn.close()}catch{}bn=null,tp()}}function $r(){let e=R(),t=e.gatewayMode,n=t==="whatsapp"||t==="both",r=t==="telegram"||t==="both",o=oe(e);if(r&&!o)return{ok:!1,message:`Telegram enabled (gatewayMode) but no bot token. Set telegramBotToken in config or TELEGRAM_BOT_TOKEN.
247
- config: ${P}`};if(n&&!Le())return{ok:!1,message:"WhatsApp enabled but no session. Run `omnish link` first."};let s=ot(e);return Gn(s)?{ok:!1,message:`Fix security errors before starting the gateway (or change gatewayMode / token).
226
+ `)}`),o}}j();import{spawn as Vm,spawnSync as Xm}from"node:child_process";import yn from"node:fs";import Zm from"node:path";import wn from"node:process";function io(e){V(),W(Zm.dirname(e));let t=wn.argv[1];if(!t)return{ok:!1,message:"Cannot resolve entry script; invoke via node path/to/dist/index.js."};let n=yn.openSync(e,"a"),r=Vm(wn.execPath,[t,"run"],{detached:!0,stdio:["ignore",n,n],env:{...wn.env,OMNISH_BACKGROUND_GATEWAY:"1"}});return yn.closeSync(n),r.unref(),r.pid?{ok:!0,pid:r.pid}:{ok:!1,message:"Failed to start background gateway."}}function ao(){if(V(),!yn.existsSync(ne))return{outcome:"no_pidfile"};let e=yn.readFileSync(ne,"utf8").trim(),t=Number(e);if(!Number.isFinite(t)||t<=0){try{yn.unlinkSync(ne)}catch{}return{outcome:"invalid_pidfile"}}try{wn.kill(t,0)}catch{try{yn.unlinkSync(ne)}catch{}return{outcome:"stale_cleaned",pid:t}}try{return wn.kill(t,"SIGTERM"),{outcome:"sent_signal",pid:t}}catch(n){return wn.platform==="win32"&&Xm("taskkill",["/PID",String(t),"/T"],{windowsHide:!0}).status===0?{outcome:"taskkill_ok",pid:t}:{outcome:"failed",message:`could not signal process: ${String(n)}`}}}import Sc from"node:crypto";import Qs from"node:fs";import ef from"node:net";Ue();Se();j();var qn=null;function tf(){try{let e=Qs.readFileSync(Qt,"utf8"),t=JSON.parse(e);return typeof t.token=="string"?t.token:""}catch{return""}}function nf(e){Qs.writeFileSync(Qt,JSON.stringify(e,null,2)+`
227
+ `,{mode:384})}function rf(){try{Qs.unlinkSync(Qt)}catch{}}async function of(e,t){let n=t.getCfg(),r=typeof e.absPath=="string"?e.absPath.trim():"";if(!r)return{ok:!1,error:"Missing absPath."};let o=typeof e.caption=="string"&&e.caption.length>0?e.caption:void 0,s=Rt(r,n.fileSendMaxBytes);if("error"in s)return{ok:!1,error:s.error};let i={absPath:s.absPath,category:s.category,mimetype:s.mimetype,displayName:s.displayName,caption:o};if(e.channel==="whatsapp"){let a=t.getWaOutbound();if(!a)return{ok:!1,error:"WhatsApp outbound is not connected."};let l=n.gatewayMode;if(l!=="whatsapp"&&l!=="both")return{ok:!1,error:"gatewayMode does not include WhatsApp."};let c=typeof e.e164=="string"?e.e164.trim():"";if(!c.startsWith("+"))return{ok:!1,error:"WhatsApp destination must be E.164 (e.g. +15551234567)."};let u=Nt(c);try{return await a.sendMedia(u,i),{ok:!0}}catch(d){return{ok:!1,error:String(d)}}}if(e.channel==="telegram"){let a=t.getTgSendMedia();if(!a)return{ok:!1,error:"Telegram outbound is not connected."};let l=n.gatewayMode;if(l!=="telegram"&&l!=="both")return{ok:!1,error:"gatewayMode does not include Telegram."};if(!Number.isFinite(e.chatId))return{ok:!1,error:"Invalid Telegram chat id."};try{return await a(e.chatId,i),{ok:!0}}catch(c){return{ok:!1,error:String(c)}}}return{ok:!1,error:"Unknown channel."}}async function sf(e,t){let n=t.getCfg(),r=typeof e.text=="string"?e.text.trim():"";if(!r)return{ok:!1,error:"Missing or empty text."};if(e.channel==="whatsapp"){let o=t.getWaOutbound();if(!o)return{ok:!1,error:"WhatsApp outbound is not connected."};let s=n.gatewayMode;if(s!=="whatsapp"&&s!=="both")return{ok:!1,error:"gatewayMode does not include WhatsApp."};let i=typeof e.e164=="string"?e.e164.trim():"";if(!i.startsWith("+"))return{ok:!1,error:"WhatsApp destination must be E.164 (e.g. +15551234567)."};let a=Nt(i);try{return await o.sendText(a,r),{ok:!0}}catch(l){return{ok:!1,error:String(l)}}}if(e.channel==="telegram"){let o=t.getTgSendText();if(!o)return{ok:!1,error:"Telegram outbound is not connected."};let s=n.gatewayMode;if(s!=="telegram"&&s!=="both")return{ok:!1,error:"gatewayMode does not include Telegram."};if(!Number.isFinite(e.chatId))return{ok:!1,error:"Invalid Telegram chat id."};try{return await o(e.chatId,m(r)),{ok:!0}}catch(i){return{ok:!1,error:String(i)}}}return{ok:!1,error:"Unknown channel."}}async function af(e,t,n){let r;try{r=JSON.parse(e)}catch{return{ok:!1,error:"Invalid JSON."}}if(!r||typeof r!="object")return{ok:!1,error:"Invalid request."};if(typeof r.token!="string"||!n)return{ok:!1,error:"Unauthorized."};try{let o=Buffer.from(r.token,"utf8"),s=Buffer.from(n,"utf8");if(o.length!==s.length||!Sc.timingSafeEqual(o,s))return{ok:!1,error:"Unauthorized."}}catch{return{ok:!1,error:"Unauthorized."}}return r.op==="sendMedia"?of(r,t):r.op==="sendText"?sf(r,t):{ok:!1,error:"Unsupported operation."}}function lo(e){if(qn)return;let t=Sc.randomBytes(32).toString("hex"),n=ef.createServer(r=>{let o="";r.setTimeout(12e4),r.on("data",s=>{o+=s.toString("utf8");let i=o.indexOf(`
228
+ `);if(i===-1)return;let a=o.slice(0,i).trim();o=o.slice(i+1);let l=tf();af(a,e,l).then(c=>{r.write(`${JSON.stringify(c)}
229
+ `),r.end()})}),r.on("error",()=>{})});n.listen(0,"127.0.0.1",()=>{let r=n.address();if(!r||typeof r=="string"){I.error("gateway control: could not read listen address");return}let o={token:t,host:r.address,port:r.port};nf(o),I.info({port:r.port},"gateway control listening")}),n.on("error",r=>{I.error({err:String(r)},"gateway control server error")}),qn=n}function kn(){if(qn){try{qn.close()}catch{}qn=null,rf()}}Se();import lf from"node:crypto";import cf from"node:http";var uf=256*1024;function df(e,t){if(!e||!t)return!1;try{let n=Buffer.from(e,"utf8"),r=Buffer.from(t,"utf8");return n.length===r.length&&lf.timingSafeEqual(n,r)}catch{return!1}}function pf(e){let t=typeof e.source=="string"?e.source:"webhook";if(e.action==="completed"&&e.workflow_run&&typeof e.workflow_run=="object"){let a=e.workflow_run,l=a.name??"?",c=a.conclusion??"?",u=a.head_branch??"?",d=a.html_url??"",p=e.repository?.full_name??"?";return`[${t}] ${p} \u2014 ${l}
230
+ result: ${c}
231
+ branch: ${u}${d?`
232
+ ${d}`:""}`}if(e.object_kind==="pipeline"&&e.object_attributes&&typeof e.object_attributes=="object"){let a=e.object_attributes,l=a.status??"?",c=a.ref??"?",u=a.id??"?",d=e.project?.path_with_namespace??"?";return`[${t}] ${d} \u2014 pipeline #${u}
233
+ status: ${l}
234
+ ref: ${c}`}let n=typeof e.text=="string"?e.text:null,r=typeof e.message=="string"?e.message:null,o=typeof e.title=="string"?e.title:null,s=typeof e.status=="string"?e.status:null;if(n)return`[${t}] ${n}`;let i=[`[${t}]`];if(o&&i.push(o),r&&i.push(r),s&&i.push(`status: ${s}`),i.length===1){let a=JSON.stringify(e).slice(0,500);i.push(a)}return i.join(`
235
+ `)}var Vs=null;function co(e,t){if(Vs)return{stop:()=>{}};let n=cf.createServer((r,o)=>{if(r.method!=="POST"){o.writeHead(405,{"Content-Type":"application/json"}),o.end(JSON.stringify({ok:!1,error:"Method not allowed"}));return}let s=r.headers.authorization,i=s?.startsWith("Bearer ")?s.slice(7):void 0,a=new URL(r.url??"/",`http://${r.headers.host??"localhost"}`).searchParams.get("token")??void 0;if(!df(i??a,e.token)){o.writeHead(401,{"Content-Type":"application/json"}),o.end(JSON.stringify({ok:!1,error:"Unauthorized"}));return}let c="",u=0;r.on("data",d=>{if(u+=d.length,u>uf){o.writeHead(413,{"Content-Type":"application/json"}),o.end(JSON.stringify({ok:!1,error:"Payload too large"})),r.destroy();return}c+=d.toString("utf8")}),r.on("end",()=>{let d;try{d=JSON.parse(c)}catch{o.writeHead(400,{"Content-Type":"application/json"}),o.end(JSON.stringify({ok:!1,error:"Invalid JSON"}));return}let f=new URL(r.url??"/",`http://${r.headers.host??"localhost"}`).searchParams.get("source")??r.headers["x-webhook-source"]??void 0;f&&(d.source=f);let y=typeof d.peerKey=="string"&&d.peerKey||t.getDefaultPeerKey();if(!y){o.writeHead(400,{"Content-Type":"application/json"}),o.end(JSON.stringify({ok:!1,error:"No target peer. Set peerKey in body or configure an allowlisted identity."}));return}let h=pf(d);t.sendToPeer(y,h).then(()=>{o.writeHead(200,{"Content-Type":"application/json"}),o.end(JSON.stringify({ok:!0}))},w=>{I.warn({err:String(w)},"webhook: sendToPeer failed"),o.writeHead(502,{"Content-Type":"application/json"}),o.end(JSON.stringify({ok:!1,error:"Failed to deliver message"}))})})});return n.listen(e.port,e.host,()=>{let r=n.address(),o=typeof r=="object"&&r?r.port:e.port;I.info({port:o,host:e.host},"webhook receiver listening")}),n.on("error",r=>{I.error({err:String(r)},"webhook receiver error")}),Vs=n,{stop:()=>{try{n.close()}catch{}Vs=null}}}de();import ig from"node:crypto";import yi from"node:fs";Se();import $c from"node:fs";function vc(e){try{return JSON.parse(e)}catch{return null}}function Cc(e){if($c.statSync(e.absPath).size>8388608)throw new Error(`File too large for attached mode (max ${8388608} bytes).`);let n=$c.readFileSync(e.absPath).toString("base64");return{name:e.displayName,mimetype:e.mimetype,category:e.category,dataBase64:n,...e.caption?{caption:e.caption}:{}}}function Rc(e,t,n,r){return t.kind==="text"?{body:Pe(t.body,r).text,...n?{messageId:n}:{}}:t.kind==="file"?{...n?{messageId:n}:{},files:[Cc(t.spec)]}:{...n?{messageId:n}:{},files:t.specs.map(Cc)}}j();fo();Se();import kf from"ws";import yf from"ws";var hf=["/platform/device","/control/device"];function go(e){let t=e.replace(/\/$/,"");return hf.map(n=>{let r=new URL(n,t);return r.protocol=r.protocol==="https:"?"wss:":"ws:",r.toString()})}function wf(e){let t=String(e instanceof Error?e.message:e);return/Unexpected server response:\s*400/.test(t)||/Unexpected server response:\s*404/.test(t)}async function Ac(e,t){let n=go(e),r=null;for(let s of n)try{return{ws:await new Promise((a,l)=>{let c=new yf(s,{headers:{Authorization:`Bearer ${t}`}});c.once("open",()=>a(c)),c.once("error",l)}),pathname:new URL(s).pathname}}catch(i){if(r=i instanceof Error?i:new Error(String(i)),wf(i))continue;throw r}let o="Could not open a device WebSocket. Redeploy the relay (for /control/device fallback) and ensure /platform/* or /control/* route to port 8788.";throw r&&/Unexpected server response:\s*400/.test(r.message)?new Error(`${r.message} \u2014 ${o}`):new Error(r?`${r.message} \u2014 ${o}`:o)}var ho=class{constructor(t){this.options=t}ws=null;stopped=!1;pingTimer=null;registerResolve=null;registerReject=null;registeredAccount=null;getRegisteredAccount(){return this.registeredAccount}async connect(){let{env:t}=this.options,n=new Promise((a,l)=>{this.registerResolve=a,this.registerReject=l}),{ws:r,pathname:o}=await Ac(t.platformUrl,t.token);this.ws=r,o!=="/platform/device"&&I.info({pathname:o},"platform device websocket connected via fallback path"),r.on("message",a=>{this.handleMessage(a.toString())}),r.on("close",()=>{this.stopped||I.warn("platform device websocket closed")});let s=setTimeout(()=>{this.registerReject?.(new Error("Platform device register timeout (15s)"))},15e3);this.send({type:"register",...t.deviceId?{deviceId:t.deviceId}:{},label:process.env.OMNISH_DEVICE_LABEL?.trim()||"default"});let i=await n;return clearTimeout(s),this.pingTimer=setInterval(()=>{this.send({type:"ping"})},3e4),i}async handleMessage(t){let n=vc(t);if(n){if(n.type==="registered"&&"deviceId"in n){n.account&&(this.registeredAccount=n.account),this.registerResolve?.({deviceId:n.deviceId,...n.account?{account:n.account}:{}}),this.registerResolve=null,this.registerReject=null;return}if(n.type==="message"){await this.options.onMessage(n);return}n.type==="error"&&(I.warn({message:n.message},"platform error"),this.registerReject?.(new Error(n.message)),this.registerResolve=null,this.registerReject=null)}}sendReply(t,n,r,o){this.send({type:"reply",peerKey:t,...n?{body:n}:{},...r?{messageId:r}:{},...o?.length?{files:o}:{}})}sendRoutedReply(t,n){this.send({type:"reply",peerKey:t,...n})}send(t){this.ws?.readyState===kf.OPEN&&this.ws.send(JSON.stringify(t))}stop(){this.stopped=!0,this.pingTimer&&(clearInterval(this.pingTimer),this.pingTimer=null);try{this.ws?.close()}catch{}this.ws=null}};import si from"node:readline/promises";import{stdin as ii,stdout as xo}from"node:process";de();var bf=/^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$/i;function Oc(e){let t=e.trim().toLowerCase();return t?t.length>63?"Tunnel name must be at most 63 characters.":bf.test(t)?null:"Tunnel name may only use letters, digits, and hyphens.":"Tunnel name must not be empty."}function Lc(e){let t=Number.parseInt(e,10);return!Number.isInteger(t)||t<1||t>65535?null:t}function Pc(e,t){let n="127.0.0.1",r,o,s=!1,i=[];for(let c=0;c<t.length;c++){let u=t[c];if(u==="--host"){let d=t[++c];if(!d)return{kind:"error",message:"--host requires an address."};n=d;continue}if(u.startsWith("--host=")){n=u.slice(7);continue}if(u==="--relay"){let d=t[++c];if(!d)return{kind:"error",message:"--relay requires a URL."};r=d;continue}if(u.startsWith("--relay=")){r=u.slice(8);continue}if(u==="--name"){let d=t[++c];if(!d)return{kind:"error",message:"--name requires a slug."};o=d;continue}if(u.startsWith("--name=")){o=u.slice(7);continue}if(u==="--background"||u==="-b"){s=!0;continue}i.push(u)}let a=i[0];if(!a)return{kind:"error",message:`Usage: omnish tunnel ${e} <port> [--host <addr>] [--name <slug>] [--relay <url>] [--background]`};let l=Lc(a);if(l===null)return{kind:"error",message:"Port must be an integer between 1 and 65535."};if(o){let c=Oc(o);if(c)return{kind:"error",message:c}}return{kind:"expose",options:{kind:e,port:l,host:n,relayUrl:r??"",name:o,background:s}}}function ni(e){let[t,...n]=e,r=(t??"").trim().toLowerCase();if(!r||r==="help"||r==="-h"||r==="--help")return{kind:"help"};if(r==="signup"){let o,s,i,a;for(let l=0;l<n.length;l++){let c=n[l];if(c==="--email"){o=n[++l];continue}if(c.startsWith("--email=")){o=c.slice(8);continue}if(c==="--phone"){s=n[++l];continue}if(c.startsWith("--phone=")){s=c.slice(8);continue}if(c==="--password"){i=n[++l];continue}if(c.startsWith("--password=")){i=c.slice(11);continue}if(c==="--relay"){a=n[++l];continue}if(c.startsWith("--relay=")){a=c.slice(8);continue}}return{kind:"signup",email:o,phone:s,password:i,relayUrl:a}}if(r==="login"){let o,s,i,a,l;for(let c=0;c<n.length;c++){let u=n[c];if(u==="--token"){o=n[++c];continue}if(u.startsWith("--token=")){o=u.slice(8);continue}if(u==="--email"){s=n[++c];continue}if(u.startsWith("--email=")){s=u.slice(8);continue}if(u==="--phone"){i=n[++c];continue}if(u.startsWith("--phone=")){i=u.slice(8);continue}if(u==="--password"){a=n[++c];continue}if(u.startsWith("--password=")){a=u.slice(11);continue}if(u==="--relay"){l=n[++c];continue}if(u.startsWith("--relay=")){l=u.slice(8);continue}o||(o=u)}return{kind:"login",token:o,email:s,phone:i,password:a,relayUrl:l}}if(r==="logout")return{kind:"logout"};if(r==="list")return{kind:"list"};if(r==="status"){let o;for(let s=0;s<n.length;s++){let i=n[s];if(i==="--relay"){o=n[s+1],s++;continue}i.startsWith("--relay=")&&(o=i.slice(8))}return{kind:"status",relayUrl:o}}if(r==="stop"){let o=n[0]?.trim();return o?{kind:"stop",target:o}:{kind:"error",message:"Usage: omnish tunnel stop <id|slug>"}}return r==="http"?Pc("http",n):r==="tcp"?Pc("tcp",n):{kind:"error",message:`Unknown tunnel subcommand "${t}". Try: omnish tunnel help`}}function Sf(e){let t=[],n="",r=null;for(let o=0;o<e.length;o++){let s=e[o];if(r){s===r?r=null:n+=s;continue}if(s==='"'||s==="'"){r=s;continue}if(/\s/.test(s)){n.length&&(t.push(n),n="");continue}n+=s}return n.length&&t.push(n),t}function Nc(e){let t=e.trim();if(!t||t==="help")return{kind:"help"};let n=Sf(t),r=n[0]?.toLowerCase();if(r==="login"||r==="logout"||r==="status"||r==="signup")return ni(n);if(t.toLowerCase()==="list"||t.toLowerCase()==="ls")return{kind:"list"};let o=t.match(/^stop\s+(\S+)\s*$/i);if(o)return{kind:"stop",target:o[1]};let s=t.match(/^(http|tcp)\s+(\d+)(?:\s+--name\s+(\S+))?(?:\s+--host\s+(\S+))?\s*$/i);if(s){let i=s[1].toLowerCase(),a=Lc(s[2]);if(a===null)return{kind:"error",message:"Port must be an integer between 1 and 65535."};let l=s[3],c=s[4]??"127.0.0.1";if(l){let u=Oc(l);if(u)return{kind:"error",message:u}}return{kind:"expose",options:{kind:i,port:a,host:c,relayUrl:"",name:l,background:!0}}}return{kind:"error",message:"Usage: /tunnel login \u2026 | /tunnel status | /tunnel http <port> | /tunnel tcp <port> | /tunnels | /tunnel stop <id>"}}import vf from"ws";import{URL as _c}from"node:url";function Sn(e){let t=new _c(e);return t.protocol=t.protocol==="https:"?"wss:":"ws:",(!t.pathname||t.pathname==="/")&&(t.pathname="/control"),t.toString()}function Fc(e){return new _c("/health",e).toString()}async function yo(e,t,n=1e4){let r=t.trim();if(!r)return{ok:!1,healthOk:!1,controlOk:!1,error:"Tunnel token is missing."};let o=Fc(e),s=!1,i;try{let c=await fetch(o,{method:"GET",signal:AbortSignal.timeout(n)});if(!c.ok)return{ok:!1,healthOk:!1,controlOk:!1,error:`Health HTTP ${c.status} (${o})`};let u=await c.json();if(!u?.ok)return{ok:!1,healthOk:!1,controlOk:!1,error:"Health JSON missing ok:true"};s=!0,typeof u.version=="string"&&(i=u.version)}catch(c){return{ok:!1,healthOk:!1,controlOk:!1,error:`Health fetch failed: ${String(c)}`}}let a=Sn(e),l=!1;try{await new Promise((c,u)=>{let d=new vf(a,{headers:{Authorization:`Bearer ${r}`}}),p=setTimeout(()=>{d.terminate(),u(new Error("WSS auth timeout"))},n);d.once("message",f=>{clearTimeout(p);try{let y=JSON.parse(f.toString());if(y.type!=="auth_ok"){u(new Error(`Expected auth_ok, got ${y.type??"?"}`));return}d.close(),c()}catch(y){u(y)}}),d.once("error",f=>{clearTimeout(p),u(f)})}),l=!0}catch(c){return{ok:!1,healthOk:!0,healthVersion:i,controlOk:!1,error:`Control WebSocket: ${String(c)}`}}return{ok:!0,healthOk:s,healthVersion:i,controlOk:!0}}import Hc from"node:crypto";import xf from"node:http";import $f from"node:net";import Et from"ws";function wo(e,t,n=Buffer.alloc(0)){let r=Buffer.allocUnsafe(9+n.length);return r.writeUInt8(e,0),r.writeUInt32BE(t,1),r.writeUInt32BE(n.length,5),n.copy(r,9),r}function Uc(e){if(e.length<9)return null;let t=e.readUInt8(0),n=e.readUInt32BE(1),r=e.readUInt32BE(5);return e.length<9+r?null:{frameType:t,streamId:n,payload:e.subarray(9,9+r)}}function ri(e){try{let t=JSON.parse(e);return!t||typeof t!="object"||typeof t.type!="string"?null:t}catch{return null}}function vn(e){return JSON.stringify(e)}function Cf(e){let t=Hc.createHash("sha1").update(e,"utf8").digest("hex").slice(0,8);return Number.parseInt(t,16)>>>0}var ko=class{constructor(t){this.opts=t;let n=Hc.randomBytes(4).toString("hex");this.record={id:n,kind:t.expose.kind,slug:t.expose.name?.trim().toLowerCase()??"",localHost:t.expose.host,localPort:t.expose.port,publicUrl:"",status:"connecting",startedAt:new Date().toISOString()}}ws=null;record;tcpStreams=new Map;tcpStreamIds=new Map;stopped=!1;pingTimer=null;getRecord(){return{...this.record}}setStatus(t,n){this.record.status=t,this.record.error=n,this.opts.onStatus?.(this.getRecord())}async start(){if(this.stopped)throw new Error("Tunnel client already stopped.");let t=Sn(this.opts.relayUrl),n=new Et(t,{headers:{Authorization:`Bearer ${this.opts.token}`}});this.ws=n,await new Promise((o,s)=>{let i=c=>{l(),s(c)},a=()=>{l(),o()},l=()=>{n.off("error",i),n.off("open",a)};n.once("error",i),n.once("open",a)}),n.on("message",(o,s)=>{if(s){this.handleBinary(Buffer.isBuffer(o)?o:Buffer.from(o));return}let i=typeof o=="string"?o:o.toString("utf8");this.handleControl(i)}),n.on("close",()=>{this.stopped||this.setStatus("error","Relay connection closed."),this.cleanupTcpStreams()}),n.on("error",o=>{this.stopped||this.setStatus("error",String(o))}),this.pingTimer=setInterval(()=>{n.readyState===Et.OPEN&&n.send(vn({type:"ping"}))},3e4),n.send(vn({type:"register",id:this.record.id,kind:this.opts.expose.kind,localHost:this.opts.expose.host,localPort:this.opts.expose.port,...this.opts.expose.name?{name:this.opts.expose.name}:{}}));let r=await this.waitForRegistered();return this.record.slug=r.slug,this.record.publicUrl=r.publicUrl,this.setStatus("active"),this.getRecord()}waitForRegistered(){let t=this.ws;return t?new Promise((n,r)=>{let o=s=>{if(typeof s!="string"&&!Buffer.isBuffer(s))return;let i=typeof s=="string"?s:s.toString("utf8"),a=ri(i);if(a){if(a.type==="registered"&&a.id===this.record.id){t.off("message",o),n(a);return}a.type==="error"&&(t.off("message",o),r(new Error(a.message)))}};t.on("message",o)}):Promise.reject(new Error("WebSocket not connected."))}async handleControl(t){let n=ri(t);if(n&&!(n.type==="pong"||n.type==="auth_ok")){if(n.type==="error"){this.setStatus("error",n.message);return}if(n.type==="http"){await this.handleHttpRequest(n);return}if(n.type==="tcp_open"){await this.handleTcpOpen(n.streamId);return}n.type==="tcp_close"&&this.closeTcpStream(n.streamId)}}handleBinary(t){let n=Uc(t);if(!n)return;let r=[...this.tcpStreamIds.entries()].find(([,s])=>s===n.streamId)?.[0];if(!r)return;let o=this.tcpStreams.get(r);if(o){if(n.frameType===2){o.write(n.payload);return}n.frameType===3&&(o.end(),this.tcpStreams.delete(r),this.tcpStreamIds.delete(r))}}async handleHttpRequest(t){let n=this.ws;if(!n||n.readyState!==Et.OPEN)return;let r=t.bodyBase64?Buffer.from(t.bodyBase64,"base64"):void 0,o={host:this.opts.expose.host,port:this.opts.expose.port,method:t.method,path:t.path,headers:{...t.headers}};await new Promise(s=>{let i=xf.request(o,a=>{let l=[];a.on("data",c=>l.push(Buffer.isBuffer(c)?c:Buffer.from(c))),a.on("end",()=>{let c=Buffer.concat(l);n.send(vn({type:"http_res",requestId:t.requestId,status:a.statusCode??502,headers:a.headers,...c.length>0?{bodyBase64:c.toString("base64")}:{}})),s()})});i.on("error",a=>{n.send(vn({type:"http_res",requestId:t.requestId,status:502,headers:{"content-type":"text/plain"},bodyBase64:Buffer.from(String(a)).toString("base64")})),s()}),r&&r.length>0&&i.write(r),i.end()})}async handleTcpOpen(t){let n=this.ws;if(!n||n.readyState!==Et.OPEN)return;let r=Cf(t);this.tcpStreamIds.set(t,r);let o=$f.connect({host:this.opts.expose.host,port:this.opts.expose.port});this.tcpStreams.set(t,o),o.on("data",s=>{n.readyState===Et.OPEN&&n.send(wo(2,r,Buffer.isBuffer(s)?s:Buffer.from(s)))}),o.on("close",()=>{n.readyState===Et.OPEN&&n.send(wo(3,r)),this.tcpStreams.delete(t),this.tcpStreamIds.delete(t)}),o.on("error",()=>{n.readyState===Et.OPEN&&n.send(wo(3,r)),this.tcpStreams.delete(t),this.tcpStreamIds.delete(t)})}closeTcpStream(t){let n=this.tcpStreams.get(t);n&&n.destroy(),this.tcpStreams.delete(t),this.tcpStreamIds.delete(t)}cleanupTcpStreams(){for(let t of this.tcpStreams.values())t.destroy();this.tcpStreams.clear(),this.tcpStreamIds.clear()}async stop(){if(this.stopped)return;this.stopped=!0,this.pingTimer&&(clearInterval(this.pingTimer),this.pingTimer=null);let t=this.ws;t&&t.readyState===Et.OPEN&&(t.send(vn({type:"unregister",id:this.record.id})),t.close()),this.cleanupTcpStreams(),this.setStatus("stopped")}};var bo=class{clients=new Map;list(){return[...new Set(this.clients.values())].map(t=>t.getRecord())}getActiveCount(){return[...new Set(this.clients.values())].filter(t=>t.getRecord().status==="active").length}async expose(t,n){let r=tt();if(!r)throw new Error("No tunnel token configured. Run `omnish tunnel login` or set OMNISH_TUNNEL_TOKEN.");let o=t.tunnelMaxActive>0?t.tunnelMaxActive:5;if(this.getActiveCount()>=o)throw new Error(`Active tunnel limit reached (${o}). Stop one with \`omnish tunnel stop <id>\`.`);let s=n.relayUrl||Je(t.tunnelRelayUrl||be),i=new ko({relayUrl:s,token:r,expose:n,onStatus:l=>{(l.status==="stopped"||l.status==="error")&&this.clients.delete(l.id)}}),a=await i.start();return this.clients.set(a.id,i),a.slug&&this.clients.set(a.slug,i),a}async stop(t){let n=t.trim().toLowerCase(),r=this.clients.get(n)??this.clients.get(t.trim());if(!r)return null;let o=r.getRecord();return await r.stop(),this.clients.delete(o.id),o.slug&&this.clients.delete(o.slug),r.getRecord()}async stopAll(){let t=[...new Set(this.clients.values())];await Promise.all(t.map(n=>n.stop())),this.clients.clear()}};async function Bc(e,t){let n=await fetch(e,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(t),signal:AbortSignal.timeout(15e3)}),r=await n.json();return!n.ok||!r.token?{ok:!1,error:r.error??`HTTP ${n.status}`}:{ok:!0,token:r.token}}function So(e,t){let n=new URL("/auth/signup",e).toString();return Bc(n,{...t.email?{email:t.email}:{},...t.phone?{phone:t.phone}:{},password:t.password})}function vo(e,t){let n=new URL("/auth/login",e).toString();return Bc(n,{...t.email?{email:t.email}:{},...t.phone?{phone:t.phone}:{},password:t.password})}var xn=new bo;function zt(){return xn}function Rf(e){let t=[B(e,"omnish tunnel"),g(e,"Expose local HTTP or TCP ports through the omnish relay."),"",B(e,"Usage:"),` ${k(e,"omnish tunnel signup [--email <email>] [--phone <phone>] [--password <pass>] [--relay <url>]")}`,` ${k(e,"omnish tunnel login [--token <token>] [--relay <url>]")}`,` ${k(e,"omnish tunnel login --email <email> --password <pass> [--relay <url>]")}`,` ${k(e,"omnish tunnel logout")}`,` ${k(e,"omnish tunnel status [--relay <url>]")}`,` ${k(e,"omnish tunnel http <port> [--host <addr>] [--name <slug>] [--relay <url>] [--background]")}`,` ${k(e,"omnish tunnel tcp <port> [--host <addr>] [--name <slug>] [--relay <url>] [--background]")}`,` ${k(e,"omnish tunnel list")}`,` ${k(e,"omnish tunnel stop <id|slug>")}`,"",g(e,"Secrets live in ~/.omnish/tunnel-auth.json or OMNISH_TUNNEL_TOKEN."),g(e,`Default relay: ${be}`),""];console.log(t.join(`
236
+ `))}async function Mf(){let e=si.createInterface({input:ii,output:xo});try{return(await e.question("Tunnel token: ")).trim()}finally{e.close()}}async function jc(e){let t=ni(e),n=xo,r=process.stderr;if(t.kind==="help"){Rf(n);return}if(t.kind==="error"){console.error(C(r,t.message)),process.exitCode=1;return}let o=$();if(t.kind==="signup"){let s=t.relayUrl||Je(o.tunnelRelayUrl||be),i=si.createInterface({input:ii,output:xo});try{let a=t.email?.trim()||(await i.question("Email (or leave empty for phone): ")).trim(),l=t.phone?.trim()||"";if(a||(l=l||(await i.question("Phone: ")).trim()),!a&&!l){console.error(C(r,"Email or phone is required.")),process.exitCode=1;return}let c=t.password||(await i.question("Password (min 8 chars): ")).trim();if(c.length<8){console.error(C(r,"Password must be at least 8 characters.")),process.exitCode=1;return}let u=await So(s,{...a?{email:a}:{},...l?{phone:l}:{},password:c});if(!u.ok){console.error(C(r,u.error)),process.exitCode=1;return}et({token:u.token,...t.relayUrl?{relayUrl:t.relayUrl}:{}}),console.log(N(n,"Account created. Token saved."))}finally{i.close()}return}if(t.kind==="login"){if(t.email||t.phone){let a=t.relayUrl||Je(o.tunnelRelayUrl||be),l=si.createInterface({input:ii,output:xo});try{let c=t.password||(await l.question("Password: ")).trim(),u=await vo(a,{...t.email?{email:t.email}:{},...t.phone?{phone:t.phone}:{},password:c});if(!u.ok){console.error(C(r,u.error)),process.exitCode=1;return}et({token:u.token,...t.relayUrl?{relayUrl:t.relayUrl}:{}}),console.log(N(n,"Logged in. Token saved."))}finally{l.close()}return}let i=t.token?.trim()||await Mf();if(!i){console.error(C(r,"Tunnel token is required.")),process.exitCode=1;return}et({token:i,...t.relayUrl?{relayUrl:t.relayUrl}:{}}),console.log(N(n,"Tunnel token saved."));return}if(t.kind==="logout"){xr(),console.log(N(n,"Tunnel token removed."));return}if(t.kind==="status"){let s=t.relayUrl||Je(o.tunnelRelayUrl||be),i=tt(),a=!!process.env.OMNISH_TUNNEL_TOKEN?.trim(),l=await yo(s,i);console.log(`${g(n,"relay:")} ${k(n,s)}`),console.log(`${g(n,"token:")} ${k(n,i?`configured${a?" (OMNISH_TUNNEL_TOKEN)":""}`:C(r,"missing"))}`),console.log(`${g(n,"health:")} ${l.healthOk?k(n,`ok${l.healthVersion?` (${l.healthVersion})`:""}`):C(r,"fail")}`),console.log(`${g(n,"control:")} ${l.controlOk?k(n,"auth ok"):C(r,"fail")}${l.error&&!l.ok?` \u2014 ${l.error}`:""}`),console.log(`${g(n,"active:")} ${k(n,String(xn.getActiveCount()))}`);return}if(t.kind==="list"){let s=xn.list();if(s.length===0){console.log(N(n,"(no active tunnels)"));return}for(let i of s)console.log(`${k(n,i.id)} ${i.kind} ${i.status} ${i.publicUrl||"(pending)"}
237
+ ${g(n,`${i.localHost}:${i.localPort}`)}`);return}if(t.kind==="stop"){let s=await xn.stop(t.target);if(!s){console.error(C(r,`No active tunnel matched "${t.target}".`)),process.exitCode=1;return}console.log(N(n,`Stopped tunnel ${s.id}.`));return}if(t.kind==="expose"){let s=t.options.relayUrl||Je(o.tunnelRelayUrl||be),i=await xn.expose(o,{...t.options,relayUrl:s});console.log(N(n,`${i.kind.toUpperCase()} tunnel active`)),console.log(`${g(n,"public:")} ${k(n,i.publicUrl)}`),console.log(`${g(n,"local:")} ${k(n,`${i.localHost}:${i.localPort}`)}`),console.log(`${g(n,"id:")} ${k(n,i.id)}`),t.options.background||(console.log(g(n,"Press Ctrl+C to stop.")),await new Promise(a=>{let l=async()=>{await xn.stop(i.id),a()};process.once("SIGINT",l),process.once("SIGTERM",l)}))}}function Wc(e){let t=e.trim().match(/^(\d+)\.(\d+)\.(\d+)/);return t?[Number(t[1]),Number(t[2]),Number(t[3])]:null}function Gc(e,t){let n=Wc(e),r=Wc(t);if(!n||!r)return e.trim().localeCompare(t.trim());for(let o=0;o<3;o++)if(n[o]!==r[o])return n[o]<r[o]?-1:1;return 0}var Tf="https://registry.npmjs.org",Jc=null,ai=0;function Yn(){return Jc}function Ef(e){let t=e.trim();return t.length<1||t.length>214?!1:!/\s/.test(t)}function zc(e){let t=e.trim();if(!t||t.length>2048)return null;try{let n=new URL(t);return n.protocol!=="https:"?null:n.href}catch{return null}}async function If(e){let t=e.trim(),n=`${Tf}/${encodeURIComponent(t)}/latest`;try{let r=await fetch(n,{headers:{Accept:"application/json"},signal:AbortSignal.timeout(2e4)});if(!r.ok)return{error:`npm registry: HTTP ${r.status}`};let o=await r.json(),s=typeof o.version=="string"?o.version.trim():"";return s?{version:s}:{error:"npm registry: missing version in response"}}catch(r){return{error:`npm registry: ${String(r)}`}}}async function Af(e){try{let t=await fetch(e,{headers:{Accept:"application/json"},signal:AbortSignal.timeout(15e3)});if(!t.ok)return{error:`updateInfoUrl: HTTP ${t.status}`};let n=await t.arrayBuffer(),r=n.byteLength>65536?n.slice(0,65536):n,o=new TextDecoder("utf-8",{fatal:!1}).decode(r),s;try{s=JSON.parse(o)}catch{return{error:"updateInfoUrl: body is not JSON"}}if(!s||typeof s!="object"||Array.isArray(s))return{error:"updateInfoUrl: JSON must be an object"};let i=s,a=null;typeof i.message=="string"&&i.message.trim()&&(a=i.message.trim().slice(0,1500));let l=null,c=i.link??i.url;return typeof c=="string"&&c.trim()&&(l=zc(c)),{message:a,link:l}}catch(t){return{error:`updateInfoUrl: ${String(t)}`}}}function Pf(e){return!Number.isFinite(e)||e<36e5?36e5:e>6048e5?6048e5:Math.floor(e)}async function Qn(e,t){let n=Ef(t.updateCheckPackageName)?t.updateCheckPackageName.trim():"omnish",r=await If(n),o="version"in r?r.version:null,s="error"in r?r.error:null,i=!1;o&&!s&&(i=Gc(e,o)<0);let a=null,l=null,c=null,u=zc(t.updateInfoUrl);if(u){let p=await Af(u);"error"in p?c=p.error:(a=p.message,l=p.link)}let d={runningVersion:e,checkedAtIso:new Date().toISOString(),registryPackage:n,registryLatest:o,registryError:s,updateAvailable:i,infoMessage:a,infoLink:l,infoError:c};return Jc=d,d}function $o(e){if(!e)return;let t=[];if(e.registryError?t.push(`npm check: ${e.registryError}`):e.registryLatest&&t.push(e.updateAvailable?`npm latest *${e.registryLatest}* (running ${e.runningVersion})`:`npm latest ${e.registryLatest} (up to date)`),e.infoError?t.push(`info URL: ${e.infoError}`):e.infoMessage&&t.push(`notice: ${e.infoMessage}`),t.length!==0)return t.join(" \xB7 ")}function Co(e){let t=!1,n=async()=>{if(t)return;let s=e.getConfig();if(!s.updateCheckEnabled)return;let i=Pf(s.updateCheckIntervalMs),a=Date.now();if(!(ai!==0&&a-ai<i)){ai=a;try{let l=await Qn(e.getRunningVersion(),s);l.updateAvailable?e.log.info({updateAvailable:!0,running:l.runningVersion,npmLatest:l.registryLatest,pkg:l.registryPackage},"omnish update check: newer npm version available"):e.log.info({running:l.runningVersion,npmLatest:l.registryLatest,pkg:l.registryPackage},"omnish update check ok"),l.infoMessage&&e.log.info({len:l.infoMessage.length},"omnish update info message present")}catch(l){e.log.warn({err:String(l)},"omnish update check failed")}}},r=setInterval(()=>void n(),6e4),o=setTimeout(()=>void n(),3e4);return()=>{t=!0,clearInterval(r),clearTimeout(o)}}de();import Kc from"node:path";import{glob as Of,stat as Lf}from"node:fs/promises";function Ro(e){let t=e.trim();if(!t||t.startsWith("-- ")||t==="--")return null;let n=t.indexOf(" -- "),r,o;return n!==-1?(r=t.slice(0,n).trim(),o=t.slice(n+4).trim()||void 0):r=t,(r.startsWith('"')&&r.endsWith('"')||r.startsWith("'")&&r.endsWith("'"))&&(r=r.slice(1,-1)),r.trim()?{selectorPart:r,caption:o}:null}function Nf(e){return/[*?[]/.test(e)}function _f(e){return e.split(",").map(t=>t.trim()).filter(t=>t.length>0)}async function Vn(e,t){let n=_f(t),r=new Set,o=[];for(let s of n){if(Nf(s)){for await(let a of Of(s,{cwd:e,withFileTypes:!1})){let l=Kc.resolve(e,a);r.has(l)||(r.add(l),o.push(l))}continue}let i=Kc.resolve(e,s);r.has(i)||(r.add(i),o.push(i))}return o}async function Xn(e){for(let t of e)try{if(!(await Lf(t)).isFile())return{ok:!1,error:`Not a file: ${t}`}}catch{return{ok:!1,error:`File not found: ${t}`}}return{ok:!0}}j();import Qc from"node:fs";import Ff from"node:path";var Kt="__omnish_shortcuts_global__",qc=500,Vc=/^[a-zA-Z0-9][a-zA-Z0-9_-]{0,31}$/,Uf=new Set(["help","apps","bg","jobs","log","tail","kill","send","file","files","receive","reload","restart","updates","gateway","gw","mode","allow","deny","allowlist","whatsapp","wa","telegram","tg","cowork","cw","shortcut","shortcuts","alias","aliases"]),Zn=new Map,Yc=!1;function Df(){try{let e=Qc.readFileSync(ar,"utf8"),t=JSON.parse(e);if(t&&typeof t=="object")for(let[n,r]of Object.entries(t)){if(!r||typeof r!="object")continue;let o={};for(let[s,i]of Object.entries(r)){let a=String(s).toLowerCase();typeof i=="string"&&i.length>0&&(o[a]=i.trim())}Zn.set(n,o)}}catch{}}function Mo(){W(Ff.dirname(ar));let e={};for(let[t,n]of Zn)Object.entries(n).length>0&&(e[t]={...n});Qc.writeFileSync(ar,JSON.stringify(e,null,2)+`
238
+ `,{mode:384})}function To(){Yc||(Df(),Yc=!0)}function Hf(e){return Uf.has(e.trim().toLowerCase())}function qt(e){let t=e.trim();if(!t)return{ok:!1,error:"Name is empty."};let n=t.toLowerCase();return Vc.test(n)?Hf(n)?{ok:!1,error:`Reserved name: ${n}`}:{ok:!0,normalized:n}:{ok:!1,error:`Invalid name (use letters, digits, _ or -; max 32 chars): ${t}`}}function li(e){let t=e.replace(/\r\n/g,`
239
+ `).replace(/\n/g," ").trim();return t?t.length>qc?{ok:!1,error:`Body too long (max ${qc} characters).`}:{ok:!0,body:t}:{ok:!1,error:"Body is empty."}}function rt(e,t){To();let n=Zn.get(e);return!n&&t&&(n={},Zn.set(e,n)),n??{}}function Xc(e){let t=e.trim();if(/^--global$/i.test(t))return{mode:"global",remainder:""};if(/^-g$/i.test(t))return{mode:"global",remainder:""};if(/^--chat$/i.test(t))return{mode:"chat",remainder:""};if(/^-p$/i.test(t))return{mode:"chat",remainder:""};let n=/^--global\s+/i.exec(t);if(n)return{mode:"global",remainder:t.slice(n[0].length).trimStart()};let r=/^-g\s+/i.exec(t);if(r)return{mode:"global",remainder:t.slice(r[0].length).trimStart()};let o=/^--chat\s+/i.exec(t);if(o)return{mode:"chat",remainder:t.slice(o[0].length).trimStart()};let s=/^-p\s+/i.exec(t);return s?{mode:"chat",remainder:t.slice(s[0].length).trimStart()}:{mode:"resolved",remainder:t}}function ci(e){let t=e.trim();if(/^--global$/i.test(t))return{scope:"global",remainder:"",explicit:!0};if(/^-g$/i.test(t))return{scope:"global",remainder:"",explicit:!0};if(/^--chat$/i.test(t))return{scope:"chat",remainder:"",explicit:!0};if(/^-p$/i.test(t))return{scope:"chat",remainder:"",explicit:!0};let n=/^--global\s+([\s\S]*)$/i.exec(t);if(n?.[1]!==void 0)return{scope:"global",remainder:n[1].trimStart(),explicit:!0};let r=/^-g\s+([\s\S]*)$/i.exec(t);if(r?.[1]!==void 0)return{scope:"global",remainder:r[1].trimStart(),explicit:!0};let o=/^--chat\s+([\s\S]*)$/i.exec(t);if(o?.[1]!==void 0)return{scope:"chat",remainder:o[1].trimStart(),explicit:!0};let s=/^-p\s+([\s\S]*)$/i.exec(t);return s?.[1]!==void 0?{scope:"chat",remainder:s[1].trimStart(),explicit:!0}:{scope:"chat",remainder:t,explicit:!1}}function ui(e){let t=ci(e);return{scope:t.scope,remainder:t.remainder}}function Zc(e){let t=e.trim();return!t||/^-+$/i.test(t)?{filter:"merged"}:/^(?:--global|-g)$/i.test(t)?{filter:"global"}:/^(?:--chat|-p)$/i.test(t)?{filter:"chat"}:{filter:"merged",bad:t}}function Bf(e){let t=e.trim().toLowerCase();if(t==="--global"||t==="-g")return"global";if(t==="--chat"||t==="-p")return"chat"}function eu(e){let t=e.trim().match(/^(\S+)\s+(--global|-g|--chat|-p)\s*$/i);if(!t?.[1]||!t[2])return;let n=Bf(t[2]);if(n)return{name:t[1],target:n}}function jf(e,t){To();let n=e,r=rt(n,!1),o=rt(Kt,!1);if(t==="chat")return Object.entries(r).map(([a,l])=>({name:a,body:l,scope:"chat"})).sort((a,l)=>a.name.localeCompare(l.name));if(t==="global")return Object.entries(o).map(([a,l])=>({name:a,body:l,scope:"global"})).sort((a,l)=>a.name.localeCompare(l.name));let s=new Set([...Object.keys(r),...Object.keys(o)]),i=[];for(let a of[...s].sort()){if(a in r){let c=r[a];c!==void 0&&i.push({name:a,body:c,scope:"chat"});continue}let l=o[a];l!==void 0&&i.push({name:a,body:l,scope:"global"})}return i}function tu(e,t="merged"){return jf(e,t)}function di(e,t){let n=t.trim().toLowerCase(),r=rt(e,!1)[n];return r!==void 0?r:rt(Kt,!1)[n]}function nu(e,t){let n=t.trim().toLowerCase(),r=rt(e,!1)[n];if(r!==void 0)return{body:r,scope:"chat"};let o=rt(Kt,!1)[n];if(o!==void 0)return{body:o,scope:"global"}}function pt(e,t,n){let r=n.trim().toLowerCase(),o=e==="global"?Kt:t;return rt(o,!1)[r]}function pi(e,t,n,r="chat"){let o=qt(t);if(!o.ok)throw new Error(o.error);let s=li(n);if(!s.ok)throw new Error(s.error);let i=r==="global"?Kt:e,a=rt(i,!0);a[o.normalized]=s.body,Mo()}function ru(e,t,n="chat"){let r=t.trim().toLowerCase(),o=n==="global"?Kt:e;To();let s=Zn.get(o);return!s||!(r in s)?!1:(delete s[r],Mo(),!0)}function mi(e,t,n){let r=qt(t);if(!r.ok)return{ok:!1,error:r.error};let o=r.normalized,s=e;To();let i=rt(s,!0),a=rt(Kt,!0),l=i[o],c=a[o];if(n==="global"){if(l!==void 0){let u=li(l);return u.ok?(a[o]=u.body,delete i[o],Mo(),{ok:!0,kind:"moved",target:"global",name:o}):{ok:!1,error:u.error}}return c!==void 0?{ok:!0,kind:"noop",message:`Shortcut "${o}" is already shared on this gateway.`}:{ok:!1,error:`No shortcut "${o}" in this chat to share. Add with /shortcut add ${o} \u2026 or make the shared copy private with /shortcut set -p ${o}.`}}if(c!==void 0){let u=li(c);return u.ok?(i[o]=u.body,delete a[o],Mo(),{ok:!0,kind:"moved",target:"chat",name:o}):{ok:!1,error:u.error}}return l!==void 0?{ok:!0,kind:"noop",message:`Shortcut "${o}" is already private to this chat.`}:{ok:!1,error:`No shared shortcut "${o}" to make private. Add with /shortcut add --global ${o} \u2026 or share from this chat with /shortcut set -g ${o}.`}}function ou(e){let t=e.trim();return t.length>0&&!/\s/.test(t)&&Vc.test(t.toLowerCase())}import su from"node:fs";var iu=64,Wf=1024*1024;function au(e){return e.fileReceiveMaxBytes>0?e.fileReceiveMaxBytes:Wf}function lu(e){let t;try{t=JSON.parse(e)}catch(o){return{ok:!1,error:`Invalid JSON: ${String(o)}`}}let n;if(Array.isArray(t))n=t;else if(t&&typeof t=="object"&&!Array.isArray(t)){let o=t,s=Object.keys(o);if(s.length!==1||s[0]!=="tasks")return{ok:!1,error:'JSON must be an array or a single-key object: { "tasks": [ \u2026 ] }.'};let i=o.tasks;if(!Array.isArray(i))return{ok:!1,error:'"tasks" must be an array.'};n=i}else return{ok:!1,error:'JSON must be an array or { "tasks": [ \u2026 ] }.'};if(n.length===0)return{ok:!1,error:"Queue JSON must contain at least one job."};if(n.length>iu)return{ok:!1,error:`Too many jobs (max ${iu}).`};let r=[];for(let o=0;o<n.length;o++){let s=n[o];if(!s||typeof s!="object"||Array.isArray(s))return{ok:!1,error:`Job ${o+1}: must be an object with "recipe" and "task".`};let i=s;if(Object.keys(i).length!==2||typeof i.recipe!="string"||typeof i.task!="string")return{ok:!1,error:`Job ${o+1}: must contain only "recipe" and "task" string fields.`};r.push({recipe:i.recipe,task:i.task})}return{ok:!0,jobs:r}}function cu(e,t,n){let r=[];for(let o=0;o<n.length;o++){let{recipe:s,task:i}=n[o],a=He(e,t,s);if(!a)return{ok:!1,error:`Job ${o+1}: unknown recipe "${s}".`};let l=a.taskEnv??"OMNISH_TASK";if(!ln(a.command,l))return{ok:!1,error:`Job ${o+1}: recipe "${s}" command must reference "$${l}".`};let c=_r(i,t.recipesMaxTaskChars);if(!c.ok)return{ok:!1,error:`Job ${o+1}: ${c.error}`};let u=a.promptTemplate?Fr(a.promptTemplate,l,c.task):c.task,d={[l]:u};r.push({command:a.command,extraEnv:d,recipeLabel:s})}return{ok:!0,items:r}}function fi(e,t){let n;try{n=su.statSync(e)}catch{return{ok:!1,error:`Cannot read file: ${e}`}}if(!n.isFile())return{ok:!1,error:`Not a file: ${e}`};if(n.size>t)return{ok:!1,error:`File too large (max ${t} bytes for queue load).`};try{return{ok:!0,text:su.readFileSync(e,"utf8")}}catch(r){return{ok:!1,error:String(r)}}}j();import uu from"node:fs";import $n from"node:process";var Gf=120;function mt(e){return e.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;")}function du(e){let t=e.trim();return t==="/service"||t.startsWith("/service ")?t.slice(8).trim():null}async function pu(e,t){let n=t.trim().split(/\s+/),r=(n[0]??"").toLowerCase();if(!t.trim()||r==="help")return z(da(e));if(r==="status"){let s=Ut(),i=(()=>{try{return uu.existsSync(ne)?`gateway.pid: ${uu.readFileSync(ne,"utf8").trim()}`:"gateway.pid: (missing)"}catch(p){return`gateway.pid: (read error: ${String(p)})`}})(),a=$n.env.OMNISH_BACKGROUND_GATEWAY==="1"?"This process: background gateway (OMNISH_BACKGROUND_GATEWAY=1).":"This process: foreground gateway session.",l=typeof $n.env.OMNISH_HOME=="string"&&$n.env.OMNISH_HOME.trim()?`OMNISH_HOME env: ${$n.env.OMNISH_HOME.trim()}`:"OMNISH_HOME env: (not set \u2014 using default data dir)",c=s.error?s.error:`Node: ${s.nodePath}
240
+ Script: ${s.scriptPath}`,u=["*Service status*","",`platform: ${$n.platform}`,a,l,`data dir: ${U}`,i,`default log: ${Ce}`,"",c,"",e.serviceInstallFromChat?"Install from chat: enabled (/service install).":"Install from chat: off \u2014 `/config set serviceInstallFromChat true` to allow /service install."].join(`
241
+ `),d=["<b>Service status</b>","",`<code>${mt($n.platform)}</code>`,`<br/><code>${mt(a)}</code>`,`<br/><code>${mt(l)}</code>`,`<br/>data dir: <code>${mt(U)}</code>`,`<br/><code>${mt(i)}</code>`,`<br/>default log: <code>${mt(Ce)}</code>`,"",`<pre>${mt(c)}</pre>`,"",e.serviceInstallFromChat?"Install from chat: enabled.":"Install from chat: off \u2014 <code>/config set serviceInstallFromChat true</code>."].join(`
242
+ `);return ie(u,d)}if(r==="instructions"){let s=Ut();if(s.error)return m(s.error);let i=Tr(s);return m(`*Install hints*
243
+
244
+ ${i}`)}if(r==="logs"){let s=n.length>=2?Number.parseInt(n[1],10):80,i=Number.isFinite(s)&&s>0?Math.min(s,Gf):80,a=Pr(Ce,i),l=[`*Gateway log* (last ${i} lines)
245
+ ${Ce}
246
+ `,"```",a,"```"].join(`
247
+ `),c=`<b>Gateway log</b> (last ${i} lines)<br/><code>${mt(Ce)}</code><pre>${mt(a)}</pre>`;return ie(l,c)}if(r==="install"){if(!e.serviceInstallFromChat)return m("Install from chat is disabled. Same trust as shell \u2014 enable with:\n`/config set serviceInstallFromChat true`\nThen `/service install` again.");let s=Er();return m(s.ok?`*Installed*
248
+ ${s.detail}`:`*Install failed*
249
+ ${s.detail}`)}if(r==="uninstall"){if(!e.serviceInstallFromChat)return m("Uninstall from chat is disabled. Enable with `/config set serviceInstallFromChat true` or remove files on the host manually.");let s=Ir();return m(`*Uninstall*
250
+ ${s.detail}`)}return m("Unknown /service command. Try /service help")}function Jf(){return m(["Cowork \u2014 scheduled shell tasks (gateway must be running).","","add <name> <schedule> -- <command\u2026>"," schedule: ondemand | hourly [:MM] | daily HH:MM | weekdays HH:MM | weekly <mon\u2026sun> HH:MM","add <name> heartbeat <interval> [grace] \u2014 dead-man's-switch (no command needed)"," e.g. /cowork add backup heartbeat 1h 10m","set <name> cmd -- <command\u2026> | schedule <\u2026> | out <path> | cwd <path>"," notify self|wa|tg|all|none"," when always|failure|state-change \u2014 notify condition (default: always)"," attach on|off \u2014 send run log as a file with notify messages"," files clear | <glob/path \u2026> \u2014 optional artifacts (basename * ?); quote paths with spaces","list | show <name> | run <name> | checkin <name> | enable <name> | disable <name> | remove <name>","","Output logs: task outputDir (default ~/Cowork/<name>). Missed times catch up when the gateway is back."].join(`
251
+ `))}function mu(e){let t=[],n=0;for(;n<e.length;){for(;n<e.length&&/\s/.test(e[n]);)n+=1;if(n>=e.length)break;if(e[n]==='"'){let o=n+1,s=e.indexOf('"',o);if(s===-1){t.push(e.slice(o));break}t.push(e.slice(o,s)),n=s+1;continue}let r=n;for(;r<e.length&&!/\s/.test(e[r]);)r+=1;t.push(e.slice(n,r)),n=r}return t}function zf(e){let t=" -- ",n=e.indexOf(t);if(n===-1)return{error:'Missing " -- " before the command. Example: /cowork add tick weekdays 09:00 -- date'};let r=e.slice(0,n).trim(),o=e.slice(n+t.length).trim();if(!o)return{error:"Command after -- is empty."};let s=r.split(/\s+/).filter(Boolean);if(s.length<2)return{error:"Usage: /cowork add <name> <schedule\u2026> -- <command\u2026>"};let i=s[0],a=s.slice(1);return{name:i,scheduleWords:a,command:o}}function gu(e,t){let n=e.trim();if(!n||/^help$/i.test(n))return Jf();let r=n.split(/\s+/)[0].toLowerCase();if(/^list$/i.test(r)){let u=Ie().filter(p=>p.ownerPeerKey===t);if(u.length===0)return m("(no cowork tasks for this chat)");let d=u.map(p=>{let f=p.enabled?"":" (disabled)",y=p.notifyWhen&&p.notifyWhen!=="always"?` when=${p.notifyWhen}`:"";return`${Me}${p.name} ${cn(p.schedule)} notify=${p.notify}${y}${f}`});return m(d.join(`
252
+ `))}let o=n.match(/^show\s+(\S+)\s*$/i);if(o){let u=o[1],d=Ie();jt(d);let p=Tt(d,u,t);if(!p)return m(`Unknown task "${u}". /cowork list`);let f=Gr(p),y=[`name: ${p.name}`,`id: ${p.id}`,`schedule: ${cn(p.schedule)}`,`enabled: ${p.enabled}`,`notify: ${p.notify}`,`notifyWhen: ${p.notifyWhen??"always"}`];if(p.schedule.kind==="heartbeat"){let h=zr(p.id);y.push(`lastCheckin: ${h?new Date(h).toLocaleString():"(never \u2014 send /cowork checkin "+p.name+")"}`)}else y.push(`attachLog: ${p.attachLog}`),y.push(`files: ${p.attachFiles.length?p.attachFiles.join(", "):"(none)"}`),y.push(`cwd: ${p.cwd||"(session cwd)"}`),y.push(`out: ${p.outputDir}`),y.push(`cmd: ${p.command}`),y.push(`last slot: ${f?new Date(f).toLocaleString():"(never)"}`);return m(y.join(`
253
+ `))}if(/^add$/i.test(r)){let u=n.slice(3).trim(),d=u.match(/^(\S+)\s+(heartbeat\s+.+)$/i);if(d){let T=Hs(d[1]);if(!T.ok)return m(T.error);let M=d[2].split(/\s+/).filter(Boolean),E=jr(M);if(!E.ok)return m(E.error);let R=Ie();if(Tt(R,T.name,t))return m(`Task "${T.name}" already exists. Remove it first or pick another name.`);let _=Yr(T.name),H={id:Bs(),name:T.name,ownerPeerKey:t,command:"",cwd:"",outputDir:_,schedule:E.schedule,enabled:!0,notify:"self",notifyWhen:"always",attachLog:!1,attachFiles:[],lastCompletedSlotMs:null,createdAtMs:Date.now()};return R.push(H),Ne(R),jt(R),Fs(H.id),m([`Saved heartbeat task "${T.name}" (${cn(E.schedule)}).`,`Send /cowork checkin ${T.name} to record a heartbeat.`,"Alerts if no check-in arrives within the expected interval + grace period."].join(`
254
+ `))}let p=zf(u);if("error"in p)return m(p.error);let f=Hs(p.name);if(!f.ok)return m(f.error);let y=jr(p.scheduleWords);if(!y.ok)return m(y.error);let h=Ie();if(Tt(h,f.name,t))return m(`Task "${f.name}" already exists. Remove it first or pick another name.`);let w=ee(t),v=Yr(f.name),x={id:Bs(),name:f.name,ownerPeerKey:t,command:p.command,cwd:"",outputDir:v,schedule:y.schedule,enabled:!0,notify:"self",notifyWhen:"always",attachLog:!1,attachFiles:[],lastCompletedSlotMs:null,createdAtMs:Date.now()};return h.push(x),Ne(h),m([`Saved cowork task "${f.name}" (${cn(y.schedule)}).`,`Output: ${v}`,`Notify: self \u2014 change with /cowork set ${f.name} notify wa|tg|all|none`].join(`
255
+ `))}let s=n.match(/^run\s+(\S+)\s*$/i);if(s){let u=s[1].toLowerCase(),d=Tt(Ie(),u,t);return d?d.enabled?(Ul({ownerPeerKey:t,name:d.name,at:Date.now()}),m(`On-demand run queued for "${d.name}" (runs within ~30s while omnish run is active).`)):m(`Cowork "${d.name}" is disabled. /cowork enable ${d.name}`):m(`Unknown task "${s[1]}". /cowork list`)}let i=n.match(/^checkin\s+(\S+)\s*$/i);if(i){let u=i[1].toLowerCase(),d=Ie();jt(d);let p=Tt(d,u,t);return p?p.schedule.kind!=="heartbeat"?m(`"${p.name}" is not a heartbeat task.`):(Fs(p.id),m(`Heartbeat recorded for "${p.name}".`)):m(`Unknown task "${i[1]}". /cowork list`)}let a=n.match(/^(?:remove|rm|del)\s+(\S+)\s*$/i);if(a){let u=a[1],d=Ie(),p=d.findIndex(f=>f.name===u.toLowerCase()&&f.ownerPeerKey===t);return p===-1?m(`Unknown task "${u}".`):(d.splice(p,1),Ne(d),m(`Removed cowork task "${u.toLowerCase()}".`))}let l=n.match(/^enable\s+(\S+)\s*$/i);if(l)return fu(l[1],t,!0);let c=n.match(/^disable\s+(\S+)\s*$/i);return c?fu(c[1],t,!1):/^set$/i.test(r)?Qf(n.slice(3).trim(),t):m("Unknown /cowork command. Try /cowork help")}function fu(e,t,n){let r=Ie(),o=Tt(r,e,t);return o?(o.enabled=n,Ne(r),m(`Cowork "${o.name}" ${n?"enabled":"disabled"}.`)):m(`Unknown task "${e}".`)}function Kf(e){let t=e.toLowerCase();return t==="self"?"self":t==="wa"||t==="whatsapp"?"wa":t==="tg"||t==="telegram"?"tg":t==="all"?"all":t==="none"?"none":null}function qf(e){let t=e.toLowerCase();return t==="always"||t==="all"?"always":t==="failure"||t==="fail"||t==="failures"?"failure":t==="state-change"||t==="statechange"||t==="change"||t==="transition"?"state-change":null}function Yf(e){let t=e.toLowerCase();return t==="on"||t==="true"||t==="1"||t==="yes"?!0:t==="off"||t==="false"||t==="0"||t==="no"?!1:null}function Qf(e,t){let n=e.match(/^(\S+)\s+([\s\S]+)$/);if(!n)return m("Usage: /cowork set <name> cmd -- \u2026 | schedule \u2026 | out <path> | cwd <path> | notify \u2026 | when \u2026 | attach <on|off> | files \u2026");let r=n[1],o=n[2].trim(),s=Ie(),i=Tt(s,r,t);if(!i)return m(`Unknown task "${r}".`);if(o.toLowerCase().startsWith("cmd ")){let a=o.slice(4).trim(),l=" -- ",c=a.indexOf(l),u;if(c!==-1)u=a.slice(c+l.length).trim();else if(a.startsWith("--"))u=a.slice(2).trim();else return m("Usage: /cowork set <name> cmd -- <command\u2026>");return u?(i.command=u,Ne(s),m(`Updated command for "${i.name}".`)):m("Command is empty.")}if(o.toLowerCase().startsWith("schedule ")){let a=o.slice(9).trim().split(/\s+/).filter(Boolean),l=jr(a);return l.ok?(i.schedule=l.schedule,Ne(s),m(`Schedule for "${i.name}": ${cn(l.schedule)}`)):m(l.error)}if(o.toLowerCase().startsWith("out ")){let a=o.slice(4).trim(),l=ee(t);return i.outputDir=dn(a,l.cwd),Ne(s),m(`outputDir: ${i.outputDir}`)}if(o.toLowerCase().startsWith("cwd ")){let a=o.slice(4).trim(),l=ee(t);return i.cwd=a?dn(a,l.cwd):"",Ne(s),m(`cwd: ${i.cwd||"(session cwd at run time)"}`)}if(o.toLowerCase().startsWith("notify ")){let a=o.slice(7).trim(),l=Kf(a);return l?(i.notify=l,Ne(s),m(`notify: ${l}`)):m("notify must be self, wa, tg, all, or none.")}if(o.toLowerCase().startsWith("when ")){let a=o.slice(5).trim(),l=qf(a);return l?(i.notifyWhen=l,Ne(s),m(`notifyWhen: ${l}`)):m("when must be always, failure, or state-change.")}if(o.toLowerCase().startsWith("attach ")){let a=o.slice(7).trim(),l=mu(a);if(l.length!==1)return m("Usage: /cowork set <name> attach on|off");let c=Yf(l[0]);return c===null?m("attach must be on or off"):(i.attachLog=c,Ne(s),m(`attachLog: ${c}`))}if(o.toLowerCase().startsWith("files ")){let a=o.slice(6).trim(),l=mu(a);return l.length===1&&l[0].toLowerCase()==="clear"?(i.attachFiles=[],Ne(s),m("files: (cleared)")):l.length===0?m("Usage: /cowork set <name> files clear | <pattern \u2026> \u2014 quote paths with spaces"):(i.attachFiles=l,Ne(s),m(`files: ${i.attachFiles.join(", ")}`))}return m("Unknown set field. Try: cmd, schedule, out, cwd, notify, when, attach, files")}de();function hu(){return m(["Tunnel commands:",'/tunnel signup --email "you@example.com" --password "yourpass"','/tunnel signup --phone "+1234567890" --password "yourpass"','/tunnel login --email "you@example.com" --password "yourpass"','/tunnel login --token "<token>" [--relay <url>]',"/tunnel logout","/tunnel status [--relay <url>]","/tunnel http <port> [--name <slug>] [--host <addr>]","/tunnel tcp <port> [--name <slug>] [--host <addr>]","/tunnels","/tunnel stop <id|slug>"].join(`
256
+ `))}async function gi(e,t,n){let r=Nc(e);if(r.kind==="help")return hu();if(r.kind==="error")return m(r.message);if(r.kind==="signup"){let o=r.email?.trim()||"",s=r.phone?.trim()||"",i=r.password||"";if(!o&&!s)return m('Usage: /tunnel signup --email "you@example.com" --password "yourpass"');if(i.length<8)return m("Password must be at least 8 characters.");let a=$(),l=r.relayUrl?.trim()||Je(a.tunnelRelayUrl||be),c=await So(l,{...o?{email:o}:{},...s?{phone:s}:{},password:i});return c.ok?(et({token:c.token,...r.relayUrl?.trim()?{relayUrl:r.relayUrl.trim()}:{}}),m("Account created. Token saved.")):m(`Signup failed: ${c.error}`)}if(r.kind==="login"){if(r.email||r.phone){let i=r.password||"";if(!i)return m('Usage: /tunnel login --email "you@example.com" --password "yourpass"');let a=$(),l=r.relayUrl?.trim()||Je(a.tunnelRelayUrl||be),c=await vo(l,{...r.email?{email:r.email}:{},...r.phone?{phone:r.phone}:{},password:i});return c.ok?(et({token:c.token,...r.relayUrl?.trim()?{relayUrl:r.relayUrl.trim()}:{}}),m("Logged in. Token saved.")):m(`Login failed: ${c.error}`)}let s=r.token?.trim();return s?(et({token:s,...r.relayUrl?.trim()?{relayUrl:r.relayUrl.trim()}:{}}),m("Tunnel token saved on this host (not shown). If OMNISH_TUNNEL_TOKEN is set in the gateway environment, it overrides the file until unset.")):m('Usage: /tunnel login --token "<token>" [--relay <url>]')}if(r.kind==="logout")return xr(),m("Tunnel token file removed. Unset OMNISH_TUNNEL_TOKEN in the gateway environment if you rely on it.");if(r.kind==="status"){let o=$(),s=r.relayUrl?.trim()||Je(o.tunnelRelayUrl||be),i=tt(),a=!!process.env.OMNISH_TUNNEL_TOKEN?.trim(),c=!!it()?.token?.trim(),u=await yo(s,i),d=[`Relay: ${s}`,`Token: ${i?"configured":"missing"}${a?" (OMNISH_TUNNEL_TOKEN)":c?" (tunnel-auth.json)":""}`,`Health: ${u.healthOk?`ok${u.healthVersion?` (version ${u.healthVersion})`:""}`:"fail"}`,`Control (WSS): ${u.controlOk?"auth ok":"fail"}`,`Active tunnels: ${n.getActiveCount()}`];return!u.ok&&u.error&&d.push(`Detail: ${u.error}`),m(d.join(`
257
+ `))}if(!t.tunnelEnabled)return m("Chat tunneling is disabled. Use /config set tunnelEnabled true (then expose with /tunnel http \u2026).");if(r.kind==="list"){let o=n.list();return o.length===0?m("(no active tunnels)"):m(o.map(s=>`${s.id} ${s.kind} ${s.status}
258
+ ${s.publicUrl||"(pending)"}
259
+ ${s.localHost}:${s.localPort}`).join(`
260
+
261
+ `))}if(r.kind==="stop"){let o=await n.stop(r.target);return o?m(`Stopped tunnel ${o.id}.`):m(`No active tunnel matched "${r.target}".`)}if(r.kind==="expose"){let o=await n.expose(t,r.options);return m(`${o.kind.toUpperCase()} tunnel active
262
+ public: ${o.publicUrl}
263
+ local: ${o.localHost}:${o.localPort}
264
+ id: ${o.id}`)}return hu()}function hi(e){let t=e.trim();if(t==="/computers"||t.startsWith("/computers "))return t.slice(10).trim();if(t==="/pcs"||t.startsWith("/pcs "))return t.slice(4).trim();let n=t.match(/^\/c(?:$|\s+(.*))/);return n?(n[1]??"").trim():null}function P(e){return{kind:"text",body:e}}function Vf(e,t){return`${e}:${t}`}function Xf(e,t){return`${e}:apps:${t}`}async function Zf(e,t){let n=e.trim();if(n===""||/^status$/i.test(n)||/^show$/i.test(n)||/^get$/i.test(n)){let a=$();return ma({gatewayMode:a.gatewayMode,authPresent:Ge(),tokenSet:!!pe(a),allowN:a.allowFrom.length,tgAllowN:a.telegramAllowFrom.length,updateBrief:$o(Yn())})}if(/^help$/i.test(n))return z(Zo());let r=Vt(n);if(!r)return Ea();hr(r);let o=$(),s,i=!1;if(t?.reload){let a=await t.reload();s=a.ok?a.summary:`Reload failed: ${a.error}`}else i=!0;return ka(o.gatewayMode,s,i)}function eg(e){let t=e.trim();if(!Ze(t))return wa();let n=ft(t),r=[];return typeof process.env.TELEGRAM_BOT_TOKEN=="string"&&process.env.TELEGRAM_BOT_TOKEN.trim()&&r.push("TELEGRAM_BOT_TOKEN is set in the environment and overrides config until unset."),n.gatewayMode==="whatsapp"&&r.push('Set gatewayMode to "telegram" or "both" for Telegram to receive messages.'),ya(r)}async function yu(e,t,n){let r=ee(t),o=ea(n);if(o!==null){let a=ta(r.cwd,o),l=na(a);return l.ok?(wr(t,a),m(`cwd: ${a}`)):m(`cd: ${l.error}`)}let s=await Bt(e.shell,n,{timeoutMs:e.syncTimeoutMs,maxBytes:e.syncMaxBytes,cwd:r.cwd}),i=[];return i.push(`$ ${n}`),s.stdout.trim()&&i.push(s.stdout.trimEnd()),s.stderr.trim()&&(i.push("\u2014 stderr \u2014"),i.push(s.stderr.trimEnd())),s.timedOut?i.push(`Timed out (${Math.round(e.syncTimeoutMs/1e3)}s limit).`):s.code!==0&&s.code!==null&&i.push(`Exit ${s.code}`),m(i.join(`
265
+ `))}async function Cn(e,t,n,r,o,s,i,a,l=null,c=!1,u){let d=s.text.trim(),p=s.peerKey,f=d.match(/^!!\s*(start|stop)\s*$/i);if(f)return f[1].toLowerCase()==="start"?(o.set(p,!0),P(Ra())):(o.set(p,!1),P(m("Free shell mode off.")));if(d.startsWith(e.commandPrefix)){let h=d.slice(e.commandPrefix.length).trim();if(!h)return P(m(`Send ${e.commandPrefix}<command> or /help`));if(!c&&ou(h)){let w=di(p,h);if(w!==void 0)return await Cn(e,t,n,r,o,{...s,text:w},i,a,l,!0,u)}return P(await yu(e,p,h))}if(/^\/help\s+files$/i.test(d.trim()))return P(ns());if(d==="/help"||d==="help")return P(z(en(e)));if(d.startsWith("/")){if(/^\/files(?:\s+help)?$/i.test(d.trim()))return P(ns());let h=d.match(/^\/receive\b(?:\s+(\S+))?$/i);if(h){let b=(h[1]??"status").toLowerCase(),S=new Set(["here","cwd","session","dir"]),A=new Set(["default","global","reset"]);if(S.has(b)){Jo(p,"sessionCwd");let D=ee(p).cwd;return P(m(`Inbound files will save under this chat\u2019s session folder:
266
+ ${D}
267
+ (layout: \u2026/<peer>/<date>/<file> under that root).
268
+
269
+ Change folder with ${e.commandPrefix}cd \u2026 Send /receive default to use the server config again.`))}return A.has(b)?(Jo(p,"default"),P(m("Per-chat inbound folder cleared. Uploads now follow fileReceiveRootMode in config.json (/files)."))):P(b==="help"?rs():b==="status"?xa(e,p):rs())}if(d==="/reload"||d==="/restart"){if(!a?.reload)return P(m("Reload is only available while the omnish gateway (omnish run) is running."));let b=await a.reload();return P(m(b.ok?b.summary:`Reload failed: ${b.error}`))}if(d==="/updates"||d.startsWith("/updates ")){let b=d.slice(8).trim().toLowerCase();if(b==="cached"||b==="last"){let A=Yn();return P(A?Ln(A):m("No update snapshot yet. Send /updates (live check) once, or enable updateCheckEnabled and wait for the scheduled check."))}let S=await Qn(Be(),$());return P(Ln(S))}let w=d.match(/^\/security(?:\s+(\S+))?\s*$/i);if(w){let b=(w[1]??"").toLowerCase(),S=$(),A=lt(S);return P(b==="help"||b==="?"?z(Ba()):b==="summary"||b==="brief"?m(ca(A,"Send /security for the full report.")):b==="tips"?z(Ha()):b===""||b==="full"||b==="report"?Da(A):m("Unknown /security subcommand. Try /security, /security summary, /security tips, or /security help"))}let v=d.match(/^\/(gateway|gw|mode)\b(?:\s+(.*))?$/i);if(v){let b=(v[2]??"").trim();return P(await Zf(b,a))}if(d==="/config"||d.startsWith("/config ")){let b=d.slice(7).trim();return P(await za(b,a))}let x=du(d);if(x!==null)return P(await pu(e,x));let T=hi(d);if(T!==null){let b=al(e,T,l);return b===null?null:P(b)}let M=d.match(/^\/(send|file)\b(?:\s+([\s\S]+))?$/i);if(M){let b=(M[2]??"").trim();if(!b)return P(ts());let S=Ro(b);if(!S)return P(ts());let A=ee(p).cwd,D=await Vn(A,S.selectorPart);if(D.length===0)return P(m(`No files matched: ${S.selectorPart}`));let K=await Xn(D);if(!K.ok)return P(m(K.error));let Q=[];for(let xe of D){let $e=Rt(xe,e.fileSendMaxBytes);if("error"in $e)return P(m($e.error));Q.push({absPath:$e.absPath,category:$e.category,mimetype:$e.mimetype,displayName:$e.displayName,caption:S.caption})}return Q.length===1?{kind:"file",spec:Q[0]}:{kind:"files",specs:Q}}if(d==="/allowlist"){let b=$();return P(ha([{label:"allowFrom (WhatsApp)",items:b.allowFrom},{label:"telegramAllowFrom",items:b.telegramAllowFrom}]))}let E=d.match(/^\/allow\b\s+(.+)$/i);if(E)try{let b=fr(E[1].trim());return P(es(b))}catch(b){return P(m(String(b)))}if(/^\/allow\b\s*$/i.test(d))return P(ba());let R=d.match(/^\/deny\b\s+(.+)$/i);if(R)try{let b=gr(R[1].trim());return P(es(b))}catch(b){return P(m(String(b)))}if(/^\/deny\b\s*$/i.test(d))return P(Sa());let _=d.match(/^\/(whatsapp|wa|telegram|tg)\b(?:\s+(.*))?$/i);if(_){let b=_[1].toLowerCase(),S=(_[2]??"").trim(),A=b==="whatsapp"||b==="wa";if(b==="telegram"||b==="tg"){let K=S.match(/^token\s+(\S+)\s*$/i);return K?P(eg(K[1]??"")):S===""||/^help$/i.test(S)?P(z(ga($()))):P(Ca())}if(A)return S===""||/^help$/i.test(S)?P(z(fa(e))):P($a())}let H=d.match(/^\/(cowork|cw)\b(?:\s+([\s\S]*))?$/i);if(H){let b=(H[2]??"").trim();return P(gu(b,p))}if(d==="/apps"||d.startsWith("/apps "))return P(await sg(d,p,e,i,r));let oe=tg(d);if(oe!==null)return P(await rg(oe,p,e,i,s.mediaSavedPath,u));if(d.startsWith("/bg")){let b=d.slice(3).trim();if(!b)return P(va());let S=Ql(b);if("error"in S)return P(m(S.error==="empty"?"Usage: /bg <command> or /bg -n <name> <command>.":S.error));let A=ee(p).cwd,D=S.notify&&u?.sendToPeer?Ot=>{let Lt=Vl(Ot);u.sendToPeer(p,Lt).catch(()=>{})}:void 0,{id:K,meta:Q}=t.spawnJob(e.shell,S.cmd,{cwd:A,name:S.name,notifyPeerKey:S.notify?p:null,onComplete:D}),xe=Q.name?`${K} (${Q.name})`:K,$e=S.notify?`
270
+ Notify on completion: on`:"";return P(m(`Job ${xe} started.
271
+ [cwd: ${A}]
272
+ /log ${Q.name??K}
273
+ /tail ${Q.name??K}${$e}`))}if(d==="/tunnels")return P(await gi("list",e,zt()));let F=d.match(/^\/tunnel\b(?:\s+([\s\S]*))?$/i);if(F){let b=(F[1]??"").trim();return P(await gi(b||"help",e,zt()))}if(d==="/jobs"){let b=t.list().slice(0,20);return b.length===0?P(m("(no jobs yet)")):P(m(b.map(S=>{let A=S.finishedAt&&S.startedAt?`${((Date.parse(S.finishedAt)-Date.parse(S.startedAt))/1e3).toFixed(1)}s`:"\u2026";return`${S.name?`${S.id} ${S.name}`:S.id} ${S.status} exit=${S.exitCode??"?"} ${A}
274
+ ${S.cmd.slice(0,120)}${S.cmd.length>120?"\u2026":""}`}).join(`
275
+
276
+ `)))}let le=d.match(/^\/log\s+(\S+)(?:\s+(\d+))?\s*$/i);if(le){let b=le[1],S=t.resolveJobRef(b);if(!S.ok)return P(m(S.error));let A=S.id,D=le[2]?Number.parseInt(le[2],10):e.jobLogTailLines,K=Number.isFinite(D)&&D>0?Math.min(D,500):e.jobLogTailLines;return P(m(t.tailLog(A,K)))}let Z=d.match(/^\/tail\s+(\S+)\s*$/i);if(Z){let b=Z[1],S=t.resolveJobRef(b);if(!S.ok)return P(m(S.error));let A=S.id,D=Vf(p,A),K=n.get(D)??0,{text:Q,nextOffset:xe}=t.readSince(A,K);return n.set(D,xe),P(Q?m(Q.trimEnd()||"(no new output)"):m(`(no new output; offset ${xe})`))}let ve=d.match(/^\/kill\s+(\S+)\s*$/i);if(ve){let b=ve[1],S=t.resolveJobRef(b);return S.ok?P(m(t.kill(S.id))):P(m(S.error))}let _e=d.match(/^\/(shortcut|shortcuts|alias|aliases)\b(?:\s+([\s\S]*))?$/i);if(_e){let b=_e[1].toLowerCase(),S=(_e[2]??"").trim();return b==="shortcuts"&&!S?S="list":((b==="alias"||b==="aliases")&&!S||b==="shortcut"&&!S)&&(S="help"),P(og(S,p))}if(!c){let b=d.trim().match(/^\/([a-zA-Z0-9][a-zA-Z0-9_-]{0,31})\s*$/);if(b){let S=b[1],A=di(p,S);if(A!==void 0)return await Cn(e,t,n,r,o,{...s,text:A},i,a,l,!0,u)}}return P(Ma(e))}let y=d.match(/^>(\S+)\s*(.*)$/s);if(y){let h=y[1],w=y[2]??"",v=await i.writeNamedLine(p,h,w);return v?P(m(v)):null}return await i.writeFocusedLine(p,d)?null:o.get(p)&&d?P(await yu(e,p,d)):e.chatLlmFallbackEnabled&&e.chatLlmShellCommand.trim().length>0&&u?.onPlainTextLlmFallback?(u.onPlainTextLlmFallback(p,d),null):P(Ta(e))}function tg(e){return e==="/run"||e.startsWith("/run ")?e.slice(4).trim():e==="/r"||e.startsWith("/r ")?e.slice(2).trim():null}function ng(e){let t=e.trim(),n=t.match(/^(\S+)\s+(?:--queue|-q)\s+([\s\S]+)$/i);if(n)return{recipe:n[1],task:n[2],queued:!0};let r=t.match(/^(\S+)\s+([\s\S]+)$/);return r?{recipe:r[1],task:r[2],queued:!1}:null}async function rg(e,t,n,r,o,s){let i=e.trim();if(!i||/^help$/i.test(i))return z(La());let a=/^list\b([\s\S]*)$/i.exec(i);if(a){let h=(a[1]??"").trim(),{filter:w,bad:v}=yl(h);if(v)return m(`Unknown /run list suffix: "${v}". Use: list | list --chat | list -p | list --global | list -g`);if(w==="merged")return Na(Sl(t,n));let x=kl(t,n,w);return Ua(x,w)}let l=/^show\b([\s\S]*)$/i.exec(i);if(l){let h=(l[1]??"").trim(),{mode:w,remainder:v}=hl(h),x=/^(\S+)\s*$/i.exec(v);if(!x?.[1])return m("Usage: /run show <name> \u2014 or show --global|-g|--chat|-p <name> (-g shared, -p private)");let T=x[1];if(w==="resolved"){let oe=He(t,n,T);return oe?is(oe):as(T)}let M=w==="global"?"global":"chat",E=ut(M,t,n,T);if(E)return is(E,M==="global"?"From gateway-shared recipes (--global).":"From this chat only (--chat).");let R=M==="chat"&&ut("global",t,n,T)?`
277
+ (Gateway-shared recipe exists: /run show --global ${T})`:"",_=M==="global"&&ut("chat",t,n,T)?`
278
+ (This chat stores an override: /run show --chat ${T})`:"",H=M==="global"?`Unknown recipe "${T}" in gateway-shared storage.`:`Unknown recipe "${T}" in this chat storage.`;return m(`${H}${R}${_}`)}let c=/^add\b([\s\S]*)$/i.exec(i);if(c){let{scope:h,remainder:w}=Es((c[1]??"").trim()),v=w.match(/^(\S+)\s+([\s\S]+)$/);if(!v)return m(h==="global"?'Usage: /run add --global <name> <command\u2026> [--template "\u2026"] \u2014 cmd must reference "$OMNISH_TASK"':"Usage: /run add [--global|-g|--chat|-p] <name> <command\u2026> \u2014 see /run help");try{let x=Is(v[2],n.recipesMacroDefaultCommand);As(t,v[1],x,h);let T=Ht(v[1]),M=T.ok?T.normalized:v[1].toLowerCase(),E=ut(h,t,n,M)??He(t,n,M);return E?_n(M,E,h):m("Recipe save failed.")}catch(x){return m(String(x))}}let u=/^set\b([\s\S]*)$/i.exec(i);if(u){let{scope:h,remainder:w,explicit:v}=Ts((u[1]??"").trim()),x=wl(w);if(x){if(v)return m("Cannot combine a leading scope flag (-g, --global, --chat, -p) with a trailing scope flag on the same line.");let E=Ht(x.name);if(!E.ok)return m(E.error);let R=Ps(t,E.normalized,x.target,n);if(!R.ok)return m(R.error);if(R.kind==="noop")return m(R.message);let _=ut(R.target,t,n,E.normalized)??He(t,n,E.normalized);return _?_n(E.normalized,_,R.target):m("Recipe scope update failed.")}let T=w.match(/^(\S+)\s*$/);if(T?.[1]&&v){let E=Ht(T[1]);if(!E.ok)return m(E.error);let R=Ps(t,E.normalized,h,n);if(!R.ok)return m(R.error);if(R.kind==="noop")return m(R.message);let _=ut(R.target,t,n,E.normalized)??He(t,n,E.normalized);return _?_n(E.normalized,_,R.target):m("Recipe scope update failed.")}let M=w.match(/^(\S+)\s+([\s\S]+)$/);if(!M)return m(h==="global"?'Usage: /run set --global <name> <command\u2026> [--template "\u2026"] \u2014 or scope-only: /run set --global <name> | /run set <name> -g':"Usage: /run set [--global|-g|--chat|-p] <name> <command\u2026> \u2014 or move scope without changing the body: /run set -g <name> | /run set <name> -p \u2014 see /run help");try{let E=Is(M[2],n.recipesMacroDefaultCommand);As(t,M[1],E,h);let R=Ht(M[1]),_=R.ok?R.normalized:M[1].toLowerCase(),H=ut(h,t,n,_)??He(t,n,_);return H?_n(_,H,h):m("Recipe save failed.")}catch(E){return m(String(E))}}let d=/^(?:remove|rm|del)\b([\s\S]*)$/i.exec(i);if(d){let{scope:h,remainder:w}=Es((d[1]??"").trim()),v=w.match(/^(\S+)\s*$/);if(!v?.[1])return m(h==="global"?"Usage: /run remove --global <name> (aliases: rm, del)":"Usage: /run remove [--global|-g|--chat|-p] <name>");let x=v[1];return bl(t,x,h)?_a(x,h):h==="chat"&&ut("global",t,n,x)?m(`No recipe "${x}" in this chat. There is a gateway-shared recipe with that name \u2014 remove it with:
279
+ /run remove --global ${x}`):Fa(x,h)}let p=/^queue\s+load\b([\s\S]*)$/i.exec(i);if(p){let h=(p[1]??"").trim(),w=au(n),v=null,x=/^json(?:\s+([\s\S]+))?$/i.exec(h);if(x){let R=(x[1]??"").trim();if(!R)return m('Usage: /run queue load json [{"recipe":"\u2026","task":"\u2026"}, \u2026] \u2014 or { "tasks": [ \u2026 ] }');v=R}else if(h.length>0){let R=h;(R.startsWith('"')&&R.endsWith('"')||R.startsWith("'")&&R.endsWith("'"))&&(R=R.slice(1,-1));let _=ee(t).cwd,H=await Vn(_,R);if(H.length===0)return m(`No files matched: ${R}`);if(H.length>1)return m("Queue load: specify a single JSON file.");let oe=await Xn(H);if(!oe.ok)return m(oe.error);let F=fi(H[0],w);if(!F.ok)return m(F.error);v=F.text}else if(o){let R=fi(o,w);if(!R.ok)return m(R.error);v=R.text}else return m("Usage: /run queue load <file.json> \u2014 or /run queue load json [\u2026] \u2014 or attach a file with caption /run queue load");let T=lu(v);if(!T.ok)return m(T.error);let M=cu(t,n,T.jobs);if(!M.ok)return m(M.error);let E=[];for(let R of M.items)E.push(r.enqueueQueuedRun(t,R,n));return m(E.join(`
280
+ `))}if(/^queue$/i.test(i))return m(r.runQueueStatus(t));if(/^queue\s+resume\s*$/i.test(i))return m(r.resumeRunQueue(t,n));let f=ng(i);if(f){let{recipe:h,task:w,queued:v}=f,x=He(t,n,h);if(!x)return as(h);if(x.steps&&x.steps.length>0){let H=ee(t).cwd,oe=x.steps.length,F=x.steps;if(s?.sendToPeer){let le=s.sendToPeer;(async()=>{let Z=[],ve=!1;for(let b=0;b<F.length;b++){let S=F[b];if(ve){Z.push({index:b,label:S.label??`step ${b+1}`,cmd:S.cmd,exitCode:null,timedOut:!1,skipped:!0,output:""});continue}let A=await Bt(n.shell,S.cmd,{timeoutMs:n.syncTimeoutMs,maxBytes:n.syncMaxBytes,cwd:H}),D=[A.stdout,A.stderr].filter(Boolean).join(`
281
+ `).trim(),K=A.code===0&&!A.timedOut;Z.push({index:b,label:S.label??`step ${b+1}`,cmd:S.cmd,exitCode:A.code,timedOut:A.timedOut,skipped:!1,output:D}),!K&&!S.continueOnFail&&(ve=!0)}let _e=vl(h,Z);await le(t,_e)})().catch(()=>{})}return m(`Runbook "${h}" started (${oe} steps). Results will be sent when complete.`)}let T=x.taskEnv??"OMNISH_TASK";if(!ln(x.command,T))return m(`Recipe "${h}" command must reference "$${T}".`);let M=_r(w,n.recipesMaxTaskChars);if(!M.ok)return m(M.error);let E=x.promptTemplate?Fr(x.promptTemplate,T,M.task):M.task,R={[T]:E};if(v)return m(r.enqueueQueuedRun(t,{command:x.command,extraEnv:R,recipeLabel:h},n));let _=Ur(h);return m(r.start(t,_,x.command,n,R))}let y=i.match(/^(\S+)$/);if(y){let h=y[1],w=h.toLowerCase();return w==="add"||w==="set"?m('Usage: /run add <name> cmd [--template "\u2026"] \u2014 cmd must include "$OMNISH_TASK"'):w==="show"?m("Usage: /run show <name> \u2014 optional show --global|-g|--chat|-p <name>"):w==="remove"||w==="rm"||w==="del"?m("Usage: /run remove [--global|-g|--chat|-p] <name>"):He(t,n,w)?m(`Usage: /run ${h} <task text\u2026> \u2014 replaces <<<OMNISH_TASK>>> / $OMNISH_TASK in stored templates`):m(`Unknown recipe "${h}". /run list`)}return m("/run: could not parse. /run help")}function og(e,t){let n=e.trim();if(!n||/^help$/i.test(n))return z(os());let r=/^list\b([\s\S]*)$/i.exec(n);if(r){let l=(r[1]??"").trim(),{filter:c,bad:u}=Zc(l);return u?m(`Unknown /shortcut list suffix: "${u}". Use: list | list --chat | list -p | list --global | list -g`):Ia(tu(t,c))}let o=/^show\b([\s\S]*)$/i.exec(n);if(o){let l=(o[1]??"").trim(),{mode:c,remainder:u}=Xc(l),d=/^(\S+)\s*$/i.exec(u);if(!d?.[1])return m("Usage: /shortcut show <name> \u2014 or show --global|-g|--chat|-p <name> (-g shared, -p private)");let p=d[1];if(c==="resolved"){let x=nu(t,p);if(!x)return Pa(p);let T=x.scope==="global"?"Shared shortcut (all chats use it unless this chat overrides the name).":"This chat only.";return ss(p,x.body,T)}let f=c==="global"?"global":"chat",y=pt(f,t,p);if(y!==void 0)return ss(p,y,f==="global"?"From the shared shortcut list (--global / -g).":"From this chat only (--chat / -p).");let h=f==="chat"&&pt("global",t,p)!==void 0?`
282
+ (Shared shortcut exists: /shortcut show --global ${p})`:"",w=f==="global"&&pt("chat",t,p)!==void 0?`
283
+ (This chat overrides the name: /shortcut show --chat ${p})`:"",v=f==="global"?`Unknown shortcut "${p}" in shared shortcuts.`:`Unknown shortcut "${p}" in this chat.`;return m(`${v}${h}${w}`)}let s=/^add\b([\s\S]*)$/i.exec(n);if(s){let{scope:l,remainder:c}=ui((s[1]??"").trim()),u=c.match(/^(\S+)\s+([\s\S]+)$/);if(!u)return m(l==="global"?"Usage: /shortcut add --global <name> <command\u2026> (short: add -g <name> <command\u2026>) \u2014 -g shared, -p private":"Usage: /shortcut add [--global|-g|--chat|-p] <name> <command\u2026> \u2014 -g shared, -p private");try{pi(t,u[1],u[2],l);let d=qt(u[1]),p=d.ok?d.normalized:u[1].trim().toLowerCase(),f=pt(l,t,p)??"";return Nn(p,f,l)}catch(d){return m(String(d))}}let i=/^set\b([\s\S]*)$/i.exec(n);if(i){let{scope:l,remainder:c,explicit:u}=ci((i[1]??"").trim()),d=eu(c);if(d){if(u)return m("Cannot combine a leading scope flag (-g, --global, --chat, -p) with a trailing flag on the same line.");let y=qt(d.name);if(!y.ok)return m(y.error);let h=mi(t,y.normalized,d.target);if(!h.ok)return m(h.error);if(h.kind==="noop")return m(h.message);let w=pt(h.target,t,y.normalized)??"";return Nn(y.normalized,w,h.target)}let p=c.match(/^(\S+)\s*$/);if(p?.[1]&&u){let y=qt(p[1]);if(!y.ok)return m(y.error);let h=mi(t,y.normalized,l);if(!h.ok)return m(h.error);if(h.kind==="noop")return m(h.message);let w=pt(h.target,t,y.normalized)??"";return Nn(y.normalized,w,h.target)}let f=c.match(/^(\S+)\s+([\s\S]+)$/);if(!f)return m(l==="global"?"Usage: /shortcut set --global <name> <command\u2026> \u2014 or scope-only: /shortcut set -g <name> | /shortcut set <name> -g (-g shared)":"Usage: /shortcut set [--global|-g|--chat|-p] <name> <command\u2026> \u2014 or move scope: /shortcut set -g <name> | /shortcut set <name> -p (-g shared, -p private)");try{pi(t,f[1],f[2],l);let y=qt(f[1]),h=y.ok?y.normalized:f[1].trim().toLowerCase(),w=pt(l,t,h)??"";return Nn(h,w,l)}catch(y){return m(String(y))}}let a=/^(?:remove|rm|del)\b([\s\S]*)$/i.exec(n);if(a){let{scope:l,remainder:c}=ui((a[1]??"").trim()),u=c.match(/^(\S+)\s*$/);if(!u?.[1])return m(l==="global"?"Usage: /shortcut remove --global <name> (aliases: rm, del)":"Usage: /shortcut remove [--global|-g|--chat|-p] <name>");let d=u[1];return ru(t,d,l)?Aa(d,l):l==="chat"&&pt("global",t,d)!==void 0?m(`No shortcut "${d}" in this chat. There is a shared shortcut with that name \u2014 remove it with:
284
+ /shortcut remove --global ${d}`):Oa(d,l)}return z(os())}async function sg(e,t,n,r,o){let s=e.slice(5).trim(),i=s.toLowerCase();if(!i||i==="help")return z(ls());let a=s.match(/^(\S+)\s*(.*)$/s);if(!a)return z(ls());let l=a[1].toLowerCase(),c=(a[2]??"").trim();switch(l){case"start":{let u=c.match(/^(\S+)\s+([\s\S]+)$/);return u?m(r.start(t,u[1],u[2],n)):m("Usage: /apps start <name> <command\u2026>")}case"attach":{let u=c.split(/\s+/)[0];return u?m(r.attach(t,u)):m("Usage: /apps attach <name>")}case"detach":return m(r.detach(t));case"list":return m(r.list(t));case"info":case"get":{let u=c.split(/\s+/)[0];return m(r.info(t,u||void 0))}case"send":{let u=c.match(/^(\S+)\s+([\s\S]+)$/);return u?m(await r.sendText(t,u[1],u[2])):m("Usage: /apps send <name> <text\u2026>")}case"key":{let u=c.match(/^(\S+)\s+([\s\S]+)$/);return u?m(r.sendKey(t,u[1],u[2].trim())):m("Usage: /apps key <name> <KEY[,KEY\u2026]>")}case"tail":{let u=c.match(/^(\S+)(?:\s+(\d+))?\s*$/);if(!u)return m("Usage: /apps tail <name> [lines]");let d=u[2]?Number.parseInt(u[2],10):n.appsLogTailLines;return m(r.tail(t,u[1],d))}case"since":{let u=c.split(/\s+/)[0];if(!u)return m("Usage: /apps since <name>");let d=Xf(t,u),p=o.get(d)??0,{text:f,nextOffset:y}=r.readSince(t,u,p);return o.set(d,y),m(f.trimEnd()||"(no new log bytes)")}case"mute":{let u=c.split(/\s+/)[0];return u?m(r.mute(t,u)):m("Usage: /apps mute <name>")}case"unmute":{let u=c.split(/\s+/)[0];return u?m(r.unmute(t,u)):m("Usage: /apps unmute <name>")}case"raw":{let u=c.match(/^(\S+)\s+(on|off)\s*$/i);return u?m(r.setRaw(t,u[1],u[2].toLowerCase()==="on")):m("Usage: /apps raw <name> on|off")}case"resize":{let u=c.trim().split(/\s+/).filter(Boolean);if(u.length<3)return m("Usage: /apps resize <name> <cols> <rows>");let d=u[0],p=Number(u[1]),f=Number(u[2]);return!Number.isFinite(p)||!Number.isFinite(f)?m("cols and rows must be numbers."):m(r.resize(t,d,p,f))}case"stop":{let u=c.split(/\s+/)[0];return u?m(r.stop(t,u)):m("Usage: /apps stop <name>")}case"kill":{let u=c.split(/\s+/)[0];return u?m(r.kill(t,u)):m("Usage: /apps kill <name>")}case"rm":{let u=c.split(/\s+/)[0];return u?m(r.rm(t,u)):m("Usage: /apps rm <name>")}default:return m(`Unknown /apps subcommand "${l}". /apps help`)}}function wu(e,t,n){return!e.clusterEnabled||hi(t.trim())!==null?!0:n?il(n,e):!1}Se();Ue();async function er(e,t,n,r,o,s,i,a,l,c,u,d){if((d?.surface??(s.peerKey.startsWith("tg:")?"telegram":"whatsapp"))==="telegram"){let f=pr(e.telegramAllowFrom),y=s.peerKey.startsWith("tg:")?s.peerKey.slice(3):"";if(!y||!f.has(y)){I.warn({denied:s.peerKey,uid:y},"telegram denied");return}}else{let f=dr(e.allowFrom),y=l.startsWith("wa:")&&/^wa:\+\d+$/.test(l)?l.slice(3):s.peerKey.replace(/^wa:/,""),h=q(y)||"";if(!h||!f.has(h)){I.warn({denied:s.peerKey,phone:h,senderKey:l},"denied");return}}try{if(!wu(e,s.text,l))return;if(s.mediaError&&await u({kind:"text",body:m(s.mediaError)}),s.mediaSavedPath&&await u({kind:"text",body:m(`Saved: ${s.mediaSavedPath}`)}),s.text.trim()){let f=await Cn(e,t,n,r,o,s,i,a,l,!1,c);f!==null&&await u(f)}}catch(f){I.error({err:String(f)},"inbound handler error"),await u({kind:"text",body:m(`Error: ${String(f)}`)}).catch(()=>{})}}function ag(){if(process.env.OMNISH_BACKGROUND_GATEWAY==="1")try{yi.readFileSync(ne,"utf8").trim()===String(process.pid)&&yi.unlinkSync(ne)}catch{}}async function ku(e){let t=new dt,n=new Map,r=new Map,o=new Map,s=null,i=new Map,a=async(F,le)=>{let Z=i.get(F);s?.sendReply(F,le,Z)},l={onPlainTextLlmFallback(F,le){pn($(),F,le,Z=>a(F,Z))},sendToPeer:a},c=new Ct(()=>$(),a),u={async reload(){return{ok:!0,summary:"Attached mode: config reloaded from disk. Messengers run on the omnish platform; this device executes commands locally."}}};if(s=new ho({env:e,onMessage:async F=>{let le=ei(),Z=F.peerKey;i.set(Z,F.messageId);let ve=F.surface==="telegram"?"telegram":"whatsapp",_e=F.senderE164&&ve==="whatsapp"?`wa:${F.senderE164}`:Z;rn()&&I.info({peerKey:Z,senderKey:_e,surface:ve},"platform inbound message");let b={peerKey:Z,text:F.text,...F.mediaSavedPath?{mediaSavedPath:F.mediaSavedPath}:{},...F.mediaError?{mediaError:F.mediaError}:{}};await er(le,t,n,r,o,b,c,u,_e,l,async S=>{try{if(S.kind==="text"){let D=Pe(S.body,ve);s?.sendReply(Z,D.text,F.messageId);return}let A=Rc(Z,S,F.messageId,ve);s?.sendRoutedReply(Z,A)}catch(A){s?.sendReply(Z,`Error sending file: ${String(A)}`,F.messageId)}},{surface:ve})}}),process.env.OMNISH_BACKGROUND_GATEWAY==="1")try{yi.writeFileSync(ne,`${process.pid}
285
+ `,{mode:384})}catch(F){I.warn({err:String(F)},"could not write gateway pidfile")}lo({getCfg:()=>$(),getWaOutbound:()=>null,getTgSendMedia:()=>null,getTgSendText:()=>null});let d=null,p=$();if(p.webhookEnabled){let F=p.webhookToken||ig.randomBytes(32).toString("hex");p.webhookToken||Y({webhookToken:F}),d=co({port:p.webhookPort,host:p.webhookHost,token:F},{sendToPeer:a,getDefaultPeerKey:()=>null}).stop}let f=Co({getRunningVersion:Be,getConfig:$,log:I}),y=Qr({getConfig:$,sendToPeer:a,sendMediaToPeer:async()=>{}}),{deviceId:h,account:w}=await s.connect(),v=await mo(e,w??null),x=5*60*1e3,T=setInterval(()=>{mo(e,null).catch(()=>{})},x);T.unref?.();let M=e.deviceId?.trim();if(M){M!==h&&I.warn({configuredDeviceId:M,registeredDeviceId:h},"platform_device_id does not match registered device id");try{await ti(e,M)}catch(F){I.warn({err:String(F)},"could not set platform default device")}}let E=v?.gatewayMode??w?.gatewayMode,R=v?.connectors.whatsapp??w?.connectors?.whatsapp,_=v?.connectors.telegram??w?.connectors?.telegram,H=[R?`whatsapp:${R.linked?"linked":R.status}`:null,_?`telegram:${_.linked?"linked":_.status}`:null].filter(Boolean).join(", ");console.error(`omnish attached to platform (device ${h})`+(E?` \u2014 gatewayMode=${E}`:"")+(H?` \u2014 ${H}`:""));let oe=()=>{clearInterval(T),y(),f?.(),d?.(),ag(),kn(),s?.stop(),c.dispose(),t.killAllRunning(),zt().stopAll().catch(()=>{}),console.error(`
286
+ ${C(process.stderr,"shutting down\u2026")}`),process.exit(0)};process.on("SIGINT",oe),process.on("SIGTERM",oe),await new Promise(()=>{})}function wi(e,t){return async n=>{if(t.surface==="whatsapp"&&t.waJid){if(n.kind==="file")await e.sendWaMedia?.(t.waJid,n.spec);else if(n.kind==="files")for(let r of n.specs)await e.sendWaMedia?.(t.waJid,r);else await e.sendWaText?.(t.waJid,Pe(n.body,"whatsapp").text);return}t.surface==="telegram"&&e.sendTg&&await e.sendTg(n)}}de();de();function It(e){return(e??"").trim()}function lg(){return It(process.env.OMNISH_PLATFORM_URL)||It(process.env.OMNISH_COMM_LAYER_URL)||It(process.env.OMNISH_TUNNEL_RELAY)}function cg(){return It(process.env.OMNISH_TOKEN)||It(process.env.OMNISH_DEVICE_TOKEN)||It(process.env.OMNISH_TUNNEL_TOKEN)}function ki(){return tt()}function Rn(){return cg()?"env":$().platformToken.trim()?"config":it()?.token?"file":"default"}function Eo(){let e=$();return qo(e.tunnelRelayUrl||be)}function Mn(){return lg()?"env":$().tunnelRelayUrl.trim()?"config":it()?.relayUrl?.trim()?"file":"default"}function bi(){let e=It(process.env.OMNISH_DEVICE_ID);if(e)return e;let t=$().platformDeviceId.trim();if(t)return t}function Si(){return It(process.env.OMNISH_DEVICE_ID)?"env":$().platformDeviceId.trim()?"config":"default"}function Ae(){let e=Eo(),t=ki();if(!e||!t)return null;let n=bi();return{platformUrl:e.replace(/\/$/,""),token:t,deviceId:n}}j();function Io(){if(Ae()){let i=$(),a=lt(i);return On(a)?{ok:!1,message:`Fix security errors before starting the gateway.
287
+
288
+ ${a.filter(u=>u.severity==="error").map(u=>{let d=[u.message];return u.detail&&d.push(u.detail),u.fixHint&&d.push(`Fix: ${u.fixHint}`),d.join(`
289
+ `)}).join(`
290
+
291
+ `)}`}:{ok:!0}}let e=$(),t=e.gatewayMode,n=t==="whatsapp"||t==="both",r=t==="telegram"||t==="both",o=pe(e);if(r&&!o)return{ok:!1,message:`Telegram enabled (gatewayMode) but no bot token. Set telegramBotToken in config or TELEGRAM_BOT_TOKEN.
292
+ config: ${L}`};if(n&&!Ge())return{ok:!1,message:"WhatsApp enabled but no session. Run `omnish link` first."};let s=lt(e);return On(s)?{ok:!1,message:`Fix security errors before starting the gateway (or change gatewayMode / token).
248
293
 
249
- ${s.filter(l=>l.severity==="error").map(l=>{let u=[l.message];return l.detail&&u.push(l.detail),l.fixHint&&u.push(`Fix: ${l.fixHint}`),u.join(`
294
+ ${s.filter(l=>l.severity==="error").map(l=>{let c=[l.message];return l.detail&&c.push(l.detail),l.fixHint&&c.push(`Fix: ${l.fixHint}`),c.join(`
250
295
  `)}).join(`
251
296
 
252
- `)}`}:{ok:!0}}import fl from"node:crypto";import St from"node:fs";import gl from"node:path";function sp(){return fl.randomBytes(24).toString("hex")}function pl(){return fl.randomBytes(32).toString("hex")}function ml(e){try{let t=St.readFileSync(e,"utf8"),n=JSON.parse(t);if(typeof n.token=="string"&&n.token.length>=16&&typeof n.secret=="string"&&n.secret.length>=16)return{token:n.token,secret:n.secret}}catch{}return null}function ip(){let e=ml(_e);if(e)return e;if(e=ml(zt),e){H(gl.dirname(_e)),St.writeFileSync(_e,JSON.stringify(e,null,2)+`
253
- `,{mode:384});try{St.unlinkSync(zt)}catch{}return e}return null}function hl(e){H(gl.dirname(_e));let t=ip(),n=(e??"").trim();if(n){let o={token:n,secret:t?.secret??pl()};St.writeFileSync(_e,JSON.stringify(o,null,2)+`
254
- `,{mode:384});try{St.existsSync(zt)&&St.unlinkSync(zt)}catch{}return o}if(t)return t;let r={token:sp(),secret:pl()};return St.writeFileSync(_e,JSON.stringify(r,null,2)+`
255
- `,{mode:384}),r}import lt from"node:fs";import pp from"node:http";import de from"node:path";import Fe from"node:process";import{fileURLToPath as mp}from"node:url";import fp from"node:os";import Jo from"node:crypto";var zo="omnish_cfg_sess",yl=7*24*60*60*1e3;function qo(e){let t=Date.now()+yl,n=Buffer.from(JSON.stringify({exp:t}),"utf8").toString("base64url"),r=Jo.createHmac("sha256",e).update(n).digest("hex");return`${n}.${r}`}function wl(e,t){let n=ap(t??"")[zo];if(!n||!n.includes("."))return!1;let r=n.lastIndexOf("."),o=n.slice(0,r),s=n.slice(r+1),i=Jo.createHmac("sha256",e).update(o).digest("hex");try{let a=Buffer.from(s,"hex"),l=Buffer.from(i,"hex");if(a.length!==l.length||!Jo.timingSafeEqual(a,l))return!1}catch{return!1}try{let a=JSON.parse(Buffer.from(o,"base64url").toString("utf8"));return!(typeof a.exp!="number"||a.exp<Date.now())}catch{return!1}}function Qo(e){let t=Math.floor(yl/1e3);return`${zo}=${e}; HttpOnly; Path=/; SameSite=Lax; Max-Age=${t}`}function bl(){return`${zo}=; HttpOnly; Path=/; SameSite=Lax; Max-Age=0`}function ap(e){let t={};for(let n of e.split(";")){let r=n.indexOf("=");if(r===-1)continue;let o=n.slice(0,r).trim(),s=n.slice(r+1).trim();o&&(t[o]=decodeURIComponent(s))}return t}import Yo from"node:fs";import Cr from"node:process";function kl(e){try{return Cr.kill(e,0),!0}catch{return!1}}function Sl(e){let t=Date.now()+e;for(;Date.now()<t;);}function lp(){try{let e=Yo.readFileSync($n,"utf8"),t=JSON.parse(e);if(!t||typeof t!="object")return null;let n=t,r=typeof n.pid=="number"?n.pid:Number(n.pid),o=typeof n.port=="number"?n.port:Number(n.port),s=typeof n.host=="string"?n.host:"",i=typeof n.startedAt=="string"?n.startedAt:"";return!Number.isFinite(r)||r<=0||!Number.isFinite(o)||o<=0||o>65535?null:{pid:r,port:o,host:s,startedAt:i}}catch{return null}}function xl(e){Yo.writeFileSync($n,`${JSON.stringify(e)}
256
- `,{mode:384})}function kn(){try{Yo.unlinkSync($n)}catch{}}function vl(e){let t=lp();if(t&&t.port===e&&t.pid!==Cr.pid){if(!kl(t.pid)){kn();return}try{Cr.kill(t.pid,"SIGTERM")}catch{}if(Sl(350),kl(t.pid)){try{Cr.kill(t.pid,"SIGKILL")}catch{}Sl(100)}kn()}}import Vo from"node:fs";import cp from"node:process";function up(e){try{return cp.kill(e,0),!0}catch{return!1}}function dp(){try{let e=Vo.readFileSync(ne,"utf8").trim(),t=Number.parseInt(e,10);return Number.isFinite(t)&&t>0?t:null}catch{return null}}function Rr(){let e=dp();return e===null?!1:up(e)}var Ko=class{busy=!1;subscribers=new Set;abort=null;currentSock=null;subscribe(t){return this.subscribers.add(t),()=>{this.subscribers.delete(t)}}emit(t){for(let n of this.subscribers)try{n(t)}catch{}}requestCancel(){this.abort?.abort(),this.currentSock&&(Ut(this.currentSock),this.currentSock=null)}beginPairing(t){if(this.busy)throw new Error("Pairing already in progress.");if(Rr())throw new Error("Gateway appears to be running (gateway.pid). Stop `omnish run` or your service before pairing from the browser.");if(!t.force&&Le())throw new Error("WhatsApp session already linked. Use \u201CReplace session\u201D or run `omnish link --force` from the CLI.");z(),t.force&&(Vo.rmSync(J,{recursive:!0,force:!0}),Vo.mkdirSync(J,{recursive:!0,mode:448})),this.busy=!0,this.abort=new AbortController;let n=this.abort.signal;(async()=>{try{await cl({authDir:J,verbose:!1,printQr:!1,signal:n,onQr:r=>this.emit({type:"qr",payload:r}),onRestart515:()=>this.emit({type:"restart"}),onSocketReady:r=>{this.currentSock=r},onSocketClosed:()=>{this.currentSock=null}}),this.emit({type:"open"}),this.emit({type:"done",ok:!0})}catch(r){let o=r instanceof Error?r.message:String(r);o==="Pairing cancelled."?this.emit({type:"error",message:"Cancelled."}):this.emit({type:"error",message:o}),this.emit({type:"done",ok:!1})}finally{this.busy=!1,this.abort=null,this.currentSock=null}})()}},Sn=new Ko;var Q="application/json; charset=utf-8",xn=null;function gp(){kn();let e=xn;xn=null,e?e.close(()=>{Fe.exit(0)}):Fe.exit(0),setTimeout(()=>Fe.exit(0),4e3).unref()}function hp(){let e=Fe.env.OMNISH_CONFIG_UI_STATIC?.trim(),t=Fe.env.OMNISH_UI_STATIC?.trim()||e;if(t&&lt.existsSync(de.join(t,"index.html")))return t;let n=de.dirname(mp(import.meta.url)),r=de.join(n,"ui");if(lt.existsSync(de.join(r,"index.html")))return r;let o=de.join(n,"..","..","dist","ui");if(lt.existsSync(de.join(o,"index.html")))return o;let s=de.join(Fe.cwd(),"dist","ui");if(lt.existsSync(de.join(s,"index.html")))return s;throw new Error("omnish ui static files not found (expected dist/ui/index.html). Run `pnpm build`.")}function yp(e){let t=de.extname(e).toLowerCase();return{".html":"text/html; charset=utf-8",".js":"text/javascript; charset=utf-8",".css":"text/css; charset=utf-8",".svg":"image/svg+xml",".png":"image/png",".jpg":"image/jpeg",".jpeg":"image/jpeg",".webp":"image/webp",".ico":"image/x-icon",".woff2":"font/woff2",".json":"application/json; charset=utf-8"}[t]??"application/octet-stream"}function wp(e,t){let r=decodeURIComponent(t.split("?")[0]??"").replace(/^\/+/,""),o=de.normalize(de.join(e,r));return!o.startsWith(de.normalize(e+de.sep))&&o!==de.normalize(e)?null:o}async function Xo(e,t=512e3){let n=[],r=0;for await(let s of e){if(r+=s.length,r>t)throw new Error("Body too large.");n.push(s)}if(n.length===0)return;let o=Buffer.concat(n).toString("utf8");return JSON.parse(o)}function bp(e){return Ot.includes(e)}function Zo(e){let t=(e.telegramBotToken??"").trim(),n=t.length===0?"":t.length<=8?"(set)":`${t.slice(0,4)}\u2026${t.slice(-4)}`;return{...e,telegramBotToken:n,telegramBotTokenConfigured:!!oe(e),telegramBotTokenEnvOverride:typeof Fe.env.TELEGRAM_BOT_TOKEN=="string"&&Fe.env.TELEGRAM_BOT_TOKEN.trim().length>0}}function kp(){let e=R();return{version:Ue(),dataDir:N,configPath:P,waAuthDir:J,whatsappLinked:Le(),gatewayPidHint:lt.existsSync(de.join(N,"gateway.pid")),gatewayRunning:Rr(),gatewayLogFile:fe,gatewayMode:e.gatewayMode,telegramBotTokenMasked:oe(e).length===0?"":Zo(e).telegramBotToken,telegramBotTokenEnvOverride:typeof Fe.env.TELEGRAM_BOT_TOKEN=="string"&&Fe.env.TELEGRAM_BOT_TOKEN.trim().length>0}}function Sp(e,t){if(!Array.isArray(e)||!Array.isArray(t))throw new Error("allowFrom and telegramAllowFrom must be arrays of strings.");let n=e.map(a=>String(a).trim()).filter(Boolean),r=t.map(a=>String(a).trim()).filter(Boolean),o=Tn(n).filter(a=>a!=="*"),s=[];for(let a of r){let l=ue(a);if(!l)throw new Error(`Invalid Telegram allow entry: ${a}`);s.push(l)}let i=R();i.allowFrom=o.sort(),i.telegramAllowFrom=[...new Set(s)].sort(),He(i)}function xp(e){return typeof e=="string"?e:typeof e=="boolean"||typeof e=="number"?String(e):JSON.stringify(e)}async function $l(e){let t=hp(),{meta:n}=e;vl(e.port);let r=pp.createServer(async(o,s)=>{try{let i=new URL(o.url??"/",`http://${o.headers.host??"localhost"}`),a=i.pathname;if(o.method==="GET"&&a==="/"){let m=i.searchParams.get("token")?.trim();if(m&&m===n.token){let y=qo(n.secret);s.statusCode=302,s.setHeader("Location","/"),s.setHeader("Set-Cookie",Qo(y)),s.end();return}}if(a.startsWith("/api/")){let m=o.headers.cookie,y=wl(n.secret,m);if(o.method==="POST"&&a==="/api/session"){let g=await Xo(o),f=typeof g?.token=="string"?g.token.trim():"";if(!f||f!==n.token){s.statusCode=401,s.setHeader("Content-Type",Q),s.end(JSON.stringify({ok:!1,error:"Invalid token."}));return}let h=qo(n.secret);s.statusCode=200,s.setHeader("Content-Type",Q),s.setHeader("Set-Cookie",Qo(h)),s.end(JSON.stringify({ok:!0}));return}if(o.method==="GET"&&a==="/api/me"){s.statusCode=y?200:401,s.setHeader("Content-Type",Q),s.end(JSON.stringify({ok:y}));return}if(!y){s.statusCode=401,s.setHeader("Content-Type",Q),s.end(JSON.stringify({ok:!1,error:"Unauthorized."}));return}if(o.method==="GET"&&a==="/api/status"){s.statusCode=200,s.setHeader("Content-Type",Q),s.end(JSON.stringify({ok:!0,...kp()}));return}if(o.method==="GET"&&a==="/api/config"){s.statusCode=200,s.setHeader("Content-Type",Q),s.end(JSON.stringify({ok:!0,config:Zo(R())}));return}if(o.method==="PUT"&&a==="/api/config"){let g=await Xo(o);if(!g||typeof g!="object"){s.statusCode=400,s.setHeader("Content-Type",Q),s.end(JSON.stringify({ok:!1,error:"Expected JSON object."}));return}let f=g;for(let[h,x]of Object.entries(f))if(x!==void 0&&!(h==="allowFrom"||h==="telegramAllowFrom")){if(h==="telegramBotToken"){let k=typeof x=="string"?x.trim():"";if(!k||k==="(set)"||k.includes("\u2026"))continue;if(!Ze(k))throw new Error("Invalid Telegram bot token format.");er("telegramBotToken",k);continue}if(!bp(h))throw new Error(`Unknown or unsupported config key: ${h}`);er(h,xp(x))}if("allowFrom"in f||"telegramAllowFrom"in f){let h=R();Sp("allowFrom"in f?f.allowFrom:h.allowFrom,"telegramAllowFrom"in f?f.telegramAllowFrom:h.telegramAllowFrom)}s.statusCode=200,s.setHeader("Content-Type",Q),s.end(JSON.stringify({ok:!0,config:Zo(R())}));return}if(o.method==="POST"&&a==="/api/logout"){s.statusCode=200,s.setHeader("Content-Type",Q),s.setHeader("Set-Cookie",bl()),s.end(JSON.stringify({ok:!0}));return}if(o.method==="POST"&&a==="/api/shutdown"){s.statusCode=200,s.setHeader("Content-Type",Q),s.end(JSON.stringify({ok:!0})),setImmediate(()=>{Sn.requestCancel(),gp()});return}if(o.method==="POST"&&a==="/api/gateway/start"){if(Rr()){s.statusCode=409,s.setHeader("Content-Type",Q),s.end(JSON.stringify({ok:!1,error:"Gateway already appears to be running (pidfile + live process). Stop it first if you need to restart."}));return}let g=$r();if(!g.ok){s.statusCode=400,s.setHeader("Content-Type",Q),s.end(JSON.stringify({ok:!1,error:g.message}));return}let f=fe,h=Sr(f);if(!h.ok){s.statusCode=500,s.setHeader("Content-Type",Q),s.end(JSON.stringify({ok:!1,error:h.message}));return}s.statusCode=200,s.setHeader("Content-Type",Q),s.end(JSON.stringify({ok:!0,pid:h.pid,logFile:f}));return}if(o.method==="POST"&&a==="/api/gateway/stop"){let g=xr();switch(g.outcome){case"no_pidfile":s.statusCode=400,s.setHeader("Content-Type",Q),s.end(JSON.stringify({ok:!1,error:"No gateway pidfile \u2014 background gateway does not appear to be running."}));return;case"invalid_pidfile":s.statusCode=400,s.setHeader("Content-Type",Q),s.end(JSON.stringify({ok:!1,error:"Invalid pidfile (removed)."}));return;case"stale_cleaned":s.statusCode=200,s.setHeader("Content-Type",Q),s.end(JSON.stringify({ok:!0,stale:!0,pid:g.pid,message:"Process was not running; stale pidfile removed."}));return;case"sent_signal":s.statusCode=200,s.setHeader("Content-Type",Q),s.end(JSON.stringify({ok:!0,pid:g.pid}));return;case"taskkill_ok":s.statusCode=200,s.setHeader("Content-Type",Q),s.end(JSON.stringify({ok:!0,pid:g.pid,taskkill:!0}));return;case"failed":s.statusCode=500,s.setHeader("Content-Type",Q),s.end(JSON.stringify({ok:!1,error:g.message}));return}}if(o.method==="GET"&&a==="/api/wa/link/events"){s.writeHead(200,{"Content-Type":"text/event-stream; charset=utf-8","Cache-Control":"no-store",Connection:"keep-alive"}),s.write(`: connected
297
+ `)}`}:{ok:!0}}j();import Lo from"node:fs";import No from"node:path";de();import{spawn as ug}from"node:child_process";import bu from"node:fs";import{stdout as dg}from"node:process";j();var vi=["tunnelRelayUrl","platformToken","platformDeviceId"],Su=[{label:"Platform (attached gateway + tunnel)",keys:["tunnelRelayUrl","platformToken","platformDeviceId"]},{label:"Tunnel",keys:["tunnelEnabled","tunnelMaxActive"]},{label:"Gateway",keys:["gatewayMode","commandPrefix","shell"]},{label:"Cluster",keys:["clusterEnabled","clusterRole","clusterLabel","clusterSenderBindings"]},{label:"Telegram",keys:["telegramBotToken"]},{label:"Apps",keys:["appsCols","appsRows","appsFlushMs","appsMinIntervalMs","appsMaxFlushBytes","appsMaxSessions","appsMaxSessionsTotal","appsMaxWaChars","appsLogTailLines","appsSubmitDelayMs","appsClearInput","appsClearInputDelayMs","appsClearInputSequence"]},{label:"Files",keys:["fileSendMaxBytes","fileReceiveMaxBytes","fileInboxSubdir","fileReceiveRootMode","fileReceiveRootPath"]},{label:"Recipes",keys:["recipesAllowDangerousBuiltins","recipesMaxTaskChars","recipesMacroDefaultCommand"]},{label:"Sync / jobs",keys:["syncTimeoutMs","syncMaxBytes","jobLogTailLines"]},{label:"Service / updates",keys:["serviceInstallFromChat","updateCheckEnabled","updateCheckIntervalMs","updateCheckPackageName","updateInfoUrl"]},{label:"Chat LLM fallback",keys:["chatLlmFallbackEnabled","chatLlmShellCommand","chatLlmTimeoutMs","chatLlmMaxInputChars","chatLlmMaxOutputChars","chatLlmNeedsTty","chatLlmWorkDir"]}];function Po(e){let t=[B(e,"omnish config"),g(e,"Manage ~/.omnish/config.json (env vars still override for platform credentials)."),"",B(e,"Usage:"),` ${k(e,"omnish config add <key> <value> [key value ...]")}`,` ${k(e,"omnish config show <key>[,<key>|*]")}`,` ${k(e,"omnish config edit")}`,` ${k(e,"omnish config edit <key> <value>")}`,` ${k(e,"omnish config delete <key>[,<key>]")}`,"",g(e,"Platform aliases: platform_url \u2192 tunnelRelayUrl, platform_token \u2192 platformToken,"),g(e," platform_device_id \u2192 platformDeviceId"),` ${k(e,"omnish config show platform")} ${g(e,"\u2014 attached-mode diagnostics (URL, token, devices)")}`,g(e," Full setup: omnish help platform"),g(e,"add sets/overwrites scalar keys (not array append). show * lists all keys."),""];console.log(t.join(`
298
+ `))}function vu(e){return e.split(/[,\s]+/).map(t=>t.trim()).filter(Boolean)}function xi(e){if(e.length===0)return[];let t=e.join(" ");if(t.includes(",")){let n=[];for(let r of t.split(",")){let o=r.trim();if(!o)continue;let s=o.split(/\s+/);if(s.length<2)throw new Error(`Invalid pair "${o}" \u2014 expected "key value"`);n.push({key:s[0],value:s.slice(1).join(" ")})}return n}if(e.length===2)return[{key:e[0],value:e[1]}];if(e.length%2===0){let n=[];for(let r=0;r<e.length;r+=2)n.push({key:e[r],value:e[r+1]});return n}return[{key:e[0],value:e.slice(1).join(" ")}]}function Ao(e,t){if(e==="clusterSenderBindings")return JSON.stringify(t.clusterSenderBindings);let n=String(t[e]??"");return e==="platformToken"||e==="telegramBotToken"?Ft(e,n):n}function xu(e){return e==="tunnelRelayUrl"?{value:Eo(),source:Mn()}:e==="platformToken"?{value:Ft(e,ki()),source:Rn()}:e==="platformDeviceId"?{value:(bi()??"")||"(empty)",source:Si()}:null}function pg(e){if(e!=="platformToken"&&e!=="tunnelRelayUrl")return;let n=$().platformToken.trim()||it()?.token?.trim()||"";n&&et({token:n,relayUrl:Eo()})}async function mg(){let e=process.env.VISUAL?.trim()||process.env.EDITOR?.trim()||(process.platform==="win32"?"notepad":"nano"),t=bu.readFileSync(L,"utf8");await new Promise((n,r)=>{let o=ug(e,[L],{stdio:"inherit"});o.on("error",r),o.on("exit",s=>{s===0?n():r(new Error(`Editor exited with code ${s??1}`))})});try{$()}catch(n){throw bu.writeFileSync(L,t,{mode:384}),new Error(`Invalid config after edit: ${n instanceof Error?n.message:String(n)}`)}}async function $u(e){let t=dg,n=process.stderr,r=(e[0]??"").trim().toLowerCase(),o=e.slice(1);if(!r||r==="help"||r==="--help"||r==="-h"){Po(t);return}if(r==="add"||r==="edit"){try{if(r==="edit"&&o.length===0){await mg(),console.log(N(t,`Updated ${L}`));return}let s=xi(o);if(s.length===0){console.error(C(n,"Expected at least one key/value pair.")),process.exitCode=1;return}for(let{key:i,value:a}of s){let l=Rr(i);if(!l){console.error(C(n,`Unknown key "${i}". Try: ${Ka().slice(0,8).join(", ")}\u2026 (omnish config show *)`)),process.exitCode=1;return}tn(l,a),pg(l);let c=$(),u=l==="tunnelRelayUrl"?c.tunnelRelayUrl:vi.includes(l)?Ft(l,String(c[l]??"")):Ao(l,c);console.log(N(t,`${i} \u2192 ${u}`))}console.log(N(t,L))}catch(s){console.error(C(n,s instanceof Error?s.message:String(s))),process.exitCode=1}return}if(r==="show"){let s=o.join(" ").trim()||"*";if(s==="*"){let l=$(),c=[B(t,"Config"),g(t,L),""];for(let p of Su){c.push(B(t,p.label));for(let f of p.keys){let y=Ao(f,l),h=xu(f);h&&vi.includes(f)?c.push(` ${k(t,f)}: ${y} ${g(t,`(effective: ${h.value}, source: ${h.source})`)}`):c.push(` ${k(t,f)}: ${y}`)}c.push("")}let u=new Set(Su.flatMap(p=>p.keys)),d=us.filter(p=>!u.has(p));if(d.length>0){c.push(B(t,"Other"));for(let p of d)c.push(` ${k(t,p)}: ${Ao(p,l)}`)}console.log(c.join(`
299
+ `));return}let i=vu(s),a=$();for(let l of i){let c=Rr(l);if(!c){console.error(C(n,`Unknown key "${l}".`)),process.exitCode=1;return}let u=Ao(c,a),d=xu(c);d&&vi.includes(c)?console.log(`${l}: ${u} (effective: ${d.value}, source: ${d.source})`):console.log(`${l}: ${u}`)}return}if(r==="delete"){let s=o.join(" ").trim();if(!s){console.error(C(n,"Expected at least one key to delete.")),process.exitCode=1;return}if(s==="*"){console.error(C(n,'Refusing "delete *". List keys explicitly.')),process.exitCode=1;return}try{for(let i of vu(s)){let a=Rr(i);if(!a){console.error(C(n,`Unknown key "${i}".`)),process.exitCode=1;return}qa(a),console.log(N(t,`cleared ${i}`))}console.log(N(t,L))}catch(i){console.error(C(n,i instanceof Error?i.message:String(i))),process.exitCode=1}return}console.error(C(n,`Unknown subcommand "${r}".`)),Po(t),process.exitCode=1}async function Cu(e){let t=Ae(),n=[];if(n.push(B(e,"platform")),!t){n.push(` ${g(e,"attached:")} ${ae(e,"no")} \u2014 set platform_url + platform_token (omnish config add) or env`);let a=Mn(),l=Rn();return(a==="env"||l==="env")&&n.push(` ${g(e,"note:")} ${J(e,"partial env override (need URL + token)")}`),n}let r=Mn(),o=Rn(),s=Si(),i=r==="env"||o==="env"||s==="env"?` ${g(e,"(env overrides config)")}`:"";n.push(` ${g(e,"attached:")} ${k(e,"yes")}${i}`),n.push(` ${g(e,"url:")} ${k(e,t.platformUrl)} ${J(e,`[${r}]`)}`),n.push(` ${g(e,"token:")} ${k(e,Ft("platformToken",t.token))} ${J(e,`[${o}]`)}`),t.deviceId&&n.push(` ${g(e,"device:")} ${k(e,t.deviceId)} ${J(e,`[${s}]`)}`);try{let{fetchPlatformAccount:a}=await Promise.resolve().then(()=>(fo(),Ic)),l=await a(t);n.push(` ${g(e,"gatewayMode:")} ${k(e,l.gatewayMode)} ${J(e,"(platform)")}`);let c=d=>{let p=l.connectors[d];return p?p.linked?k(e,"linked"):J(e,p.status):ae(e,"idle")};n.push(` ${g(e,"whatsapp:")} ${c("whatsapp")}`),n.push(` ${g(e,"telegram:")} ${c("telegram")}`);let u=l.routing.onlineCount;n.push(` ${g(e,"routing:")} default=${l.routing.defaultDeviceId??J(e,"(none)")} online=${u}`),t.deviceId&&l.routing.defaultDeviceId&&t.deviceId!==l.routing.defaultDeviceId&&n.push(` ${g(e,"warn:")} ${ae(e,"platform_device_id \u2260 platform defaultDeviceId \u2014 run omnish run or set default on dashboard")}`),u===0&&n.push(` ${g(e,"warn:")} ${ae(e,"no device online on platform")}`)}catch{n.push(` ${g(e,"account:")} ${J(e,"(could not fetch /v1/me \u2014 check token and URL)")}`)}return n}fo();function Mu(){let e=Ae();return e||(console.error(C(process.stderr,"Set platform_url + platform_token (omnish config add) or OMNISH_PLATFORM_URL + OMNISH_TOKEN.")),process.exitCode=1,null)}function fg(e){let t=e.trim();return t?t.startsWith("wa:")?{kind:"wa",value:t.slice(3).trim()}:t.startsWith("tg:")?{kind:"tg",value:t.slice(3).trim()}:/^\+?\d{8,}$/.test(t.replace(/\s/g,""))?{kind:"wa",value:t}:/^\d+$/.test(t)?{kind:"tg",value:t}:null:null}function Ru(e){return e.replace(/\D/g,"")}async function Tu(){let e=Mu();if(!e)return;let t=await bn(e),n=t.connectors.whatsapp,r=t.connectors.telegram;console.log(N(process.stdout,"Platform account")),console.log(` gatewayMode: ${t.gatewayMode}`),console.log(` whatsapp: ${n?.linked?"linked":n?.status??"idle"} (allowlist: ${t.allowFrom.length})`);let o=r?.tokenConfigured?"token saved":"no token";console.log(` telegram: ${r?.linked?"linked":r?.status??"idle"} (${o}, allowlist: ${t.telegramAllowFrom.length})`),console.log(` defaultDeviceId: ${t.defaultDeviceId??"\u2014"}`),console.log(` online devices: ${t.routing.onlineCount}`),t.allowFrom.length&&console.log(` allowFrom: ${t.allowFrom.map(s=>`+${s}`).join(", ")}`),t.telegramAllowFrom.length&&console.log(` telegramAllowFrom: ${t.telegramAllowFrom.join(", ")}`)}async function Eu(e){let t=Mu();if(!t)return;let n=(e[0]??"").toLowerCase();if(!n||n==="-h"||n==="--help"){console.log(["Usage:"," omnish platform allow list"," omnish platform allow add wa:+15551234567 tg:123456789"," omnish platform allow set --wa +1555,... --tg 123,...",""].join(`
300
+ `));return}if(n==="list"){let r=await bn(t);console.log(N(process.stdout,"Platform allowlists")),console.log(` WhatsApp (${r.allowFrom.length}): ${r.allowFrom.length?r.allowFrom.map(o=>`+${o}`).join(", "):"(empty \u2014 any sender)"}`),console.log(` Telegram (${r.telegramAllowFrom.length}): ${r.telegramAllowFrom.length?r.telegramAllowFrom.join(", "):"(empty \u2014 any sender)"}`);return}if(n==="add"){let r=await bn(t),o=[...r.allowFrom],s=[...r.telegramAllowFrom];for(let a of e.slice(1)){let l=fg(a);if(!l){console.error(C(process.stderr,`Unrecognized entry: ${a}`)),process.exitCode=1;return}if(l.kind==="wa"){let c=Ru(l.value);c&&!o.includes(c)&&o.push(c)}else{let c=l.value.replace(/\D/g,"");c&&!s.includes(c)&&s.push(c)}}let i=await po(t,{allowFrom:o,telegramAllowFrom:s});console.log(N(process.stdout,`Allowlists updated (${i.allowFrom.length} WhatsApp, ${i.telegramAllowFrom.length} Telegram).`));return}if(n==="set"){let r,o;for(let a=1;a<e.length;a++){let l=e[a];l==="--wa"&&e[a+1]?r=e[++a].split(",").map(c=>Ru(c.trim())).filter(Boolean):l==="--tg"&&e[a+1]&&(o=e[++a].split(",").map(c=>c.trim().replace(/^tg:/i,"").replace(/\D/g,"")).filter(Boolean))}if(r===void 0&&o===void 0){console.error(C(process.stderr,"Use --wa and/or --tg with comma-separated values.")),process.exitCode=1;return}let s={};r!==void 0&&(s.allowFrom=r),o!==void 0&&(s.telegramAllowFrom=o);let i=await po(t,s);console.log(N(process.stdout,`Allowlists set (${i.allowFrom.length} WhatsApp, ${i.telegramAllowFrom.length} Telegram).`));return}console.error(C(process.stderr,`Unknown allow subcommand: ${e[0]}`)),process.exitCode=1}async function Iu(e){let t="",n="",r="";for(let o=0;o<e.length;o++){let s=e[o];(s==="--url"||s==="-u")&&e[o+1]?t=e[++o].trim():(s==="--token"||s==="-t")&&e[o+1]?n=e[++o].trim():s==="--device-id"&&e[o+1]&&(r=e[++o].trim())}if(!t||!n){let o=xi(e);for(let s of o)s.key==="platform_url"&&(t=s.value),s.key==="platform_token"&&(n=s.value),s.key==="platform_device_id"&&(r=s.value)}if(!t||!n){console.error(C(process.stderr,"Usage: omnish platform login --url <url> --token <token> [--device-id <id>]")),process.exitCode=1;return}tn("tunnelRelayUrl",t.replace(/\/$/,"")),tn("platformToken",n),r&&tn("platformDeviceId",r),console.log(N(process.stdout,"Platform credentials saved to config.json.")),console.log(" Run: omnish platform status && omnish run")}import gg from"qrcode-terminal";var hg=1e3,yg=120;function Au(){let e=Ae();return e||(console.error(C(process.stderr,"Set platform_url + platform_token (omnish config add) or OMNISH_PLATFORM_URL + OMNISH_TOKEN.")),process.exitCode=1,null)}function Ci(e){let t=e.replace(/\/$/,"");return/^https?:\/\//i.test(t)?t:`http://${t}`}async function Pu(e){let t=`${Ci(e.platformUrl)}/v1/connectors/whatsapp/status`,n=await fetch(t,{headers:{Authorization:`Bearer ${e.token}`}}),r=await n.json().catch(()=>({}));if(!n.ok)throw new Error(r.error||`HTTP ${n.status}`);return r}async function wg(e){let t=await fetch(`${Ci(e.platformUrl)}/v1/connectors/whatsapp/start`,{method:"POST",headers:{"content-type":"application/json",Authorization:`Bearer ${e.token}`},body:JSON.stringify({})}),n=await t.json().catch(()=>({}));if(!t.ok)throw new Error(n.error||`HTTP ${t.status}`);return n}function Ou(e){let t=process.stdout,n=g(t,"\xB7".repeat(42));console.log(J(t,"Scan with WhatsApp \u2192 Linked devices")),console.log(n),gg.generate(e,{small:!0}),console.log(n)}var Oo="",$i=!1;function kg(e){let t=e.status??"";return t==="qr"||t==="connecting"||t==="reconnecting"||t==="pairing_restart"}async function bg(e){for(let t=0;t<yg;t++){let n=await Pu(e);if(n.status==="linked"||n.linked)return n;if(n.status==="pairing_restart"&&!$i&&($i=!0,console.log(g(process.stdout,"Finishing WhatsApp link after scan (server restart) \u2014 wait a few seconds\u2026"))),n.qr&&n.qr!==Oo&&(Oo=n.qr,Ou(n.qr)),!kg(n))throw new Error(n.error||n.statusMessage||`Unexpected status: ${n.status}`);await new Promise(r=>setTimeout(r,hg))}throw new Error("Timed out waiting for WhatsApp to link (scan the QR within ~2 minutes).")}async function Lu(){let e=Au();if(!e)return;let t=await Pu(e);if(t.status==="linked"||t.linked){console.log(N(process.stdout,"WhatsApp already linked on the platform.")),t.linkedPhoneE164&&(console.log(` Phone: +${t.linkedPhoneE164}`),console.log(` Hint: omnish platform allow add wa:+${t.linkedPhoneE164}`));return}console.log(N(process.stdout,"Starting WhatsApp pairing on the platform\u2026")),$i=!1,Oo="",t=await wg(e),t.qr&&(Ou(t.qr),Oo=t.qr),t=await bg(e),console.log(N(process.stdout,"WhatsApp linked on the platform.")),t.linkedPhoneE164&&(console.log(` Phone: +${t.linkedPhoneE164}`),console.log(` Next: omnish platform allow add wa:+${t.linkedPhoneE164}`),console.log(" Then: omnish run"))}async function Nu(){let e=Au();if(!e)return;let t=await fetch(`${Ci(e.platformUrl)}/v1/connectors/whatsapp/unlink`,{method:"POST",headers:{"content-type":"application/json",Authorization:`Bearer ${e.token}`},body:JSON.stringify({})}),n=await t.json().catch(()=>({}));if(!t.ok){console.error(C(process.stderr,n.error||`HTTP ${t.status}`)),process.exitCode=1;return}console.log(N(process.stdout,`WhatsApp unlinked (status: ${n.status??"idle"}).`))}import Sg from"ws";function _u(e,t,n){return new Promise(r=>{let o=new Sg(e,{headers:{Authorization:`Bearer ${t}`}}),s=setTimeout(()=>{o.terminate(),r({ok:!1,message:"timeout"})},n);o.once("open",()=>{clearTimeout(s),o.close(),r({ok:!0})}),o.once("unexpected-response",(i,a)=>{clearTimeout(s),r({ok:!1,status:a.statusCode,message:`HTTP ${a.statusCode} ${a.statusMessage}`})}),o.once("error",i=>{clearTimeout(s),r({ok:!1,message:String(i?.message??i)})})})}async function Fu(e,t,n=12e3){let r=e.replace(/\/$/,""),o=Sn(r),s=await _u(o,t,n),i,a,l="";for(let c of go(r)){let u=await _u(c,t,n);if(u.ok)return i=new URL(c).pathname,{ok:!0,controlWsOk:s.ok,deviceWsOk:!0,deviceWsPath:i};a=u.status,l=u.message}return a===400?{ok:!1,controlWsOk:s.ok,deviceWsOk:!1,deviceWsStatus:400,error:"Device WebSocket returned HTTP 400 on /platform/device and could not use /control/device. Redeploy the latest relay image (adds /control/device), or route /platform/* to port 8788 in Caddy/EasyPanel."}:{ok:!1,controlWsOk:s.ok,deviceWsOk:!1,deviceWsStatus:a,error:`Device WebSocket failed: ${l}`}}var vg=400,xg=8*1024*1024,$g=2*1024*1024;function Cg(e){let t=X,n=!1,r=!1;for(let o=0;o<e.length;o++){let s=e[o];s==="-h"||s==="--help"?r=!0:s==="--force"?n=!0:(s==="--auth-dir"||s==="--authDir")&&e[o+1]&&(t=No.resolve(e[++o]))}return{authDir:t,force:n,help:r}}function Rg(e){let t={},n=0,r=0;function o(s,i){let a;try{a=Lo.readdirSync(i,{withFileTypes:!0})}catch{return}for(let l of a){let c=l.name;if(c==="."||c==="..")continue;let u=No.join(i,c),p=(s?`${s}/${c}`:c).split(No.sep).join("/");if(!l.isSymbolicLink()){if(l.isDirectory())o(p,u);else if(l.isFile()){let f=Lo.readFileSync(u);if(f.length>$g)throw new Error(`file too large: ${p}`);if(n+=f.length,n>xg)throw new Error("total auth size exceeds limit (8 MiB)");if(r+=1,r>vg)throw new Error("too many files (max 400)");t[p]=f.toString("base64")}}}}return o("",e),t}function _o(e){console.log([re(e,"omnish platform"),g(e,"Attached mode: messengers on the hosted platform; shell on this machine (omnish run)."),"",B(e,"When to use"),g(e," Link WhatsApp/Telegram once on the platform dashboard. Run omnish run on laptops, servers, or containers without local Baileys."),"",B(e,"Prerequisites"),g(e," 1. Platform account token (signup/login on relay dashboard)."),g(e," 2. Messengers linked on the platform (dashboard QR or import-whatsapp)."),g(e," 3. allowFrom / telegramAllowFrom on dashboard Allowlists (Telegram: DM bot /id for your user id)."),"",B(e,"Configure"),` ${k(e,"omnish config add platform_url <url> platform_token <token>")}`,g(e," Optional: platform_device_id <id>"),g(e," Env overrides: OMNISH_PLATFORM_URL, OMNISH_TOKEN, OMNISH_DEVICE_ID"),g(e," (aliases: OMNISH_COMM_LAYER_URL, OMNISH_DEVICE_TOKEN, OMNISH_TUNNEL_*)"),"",B(e,"Workflow"),` ${k(e,"omnish platform login --url <url> --token <t>")} ${g(e,"\u2014 save credentials (or config add)")}`,` ${k(e,"omnish platform status")} ${g(e,"\u2014 account, connectors, allowlists")}`,` ${k(e,"omnish platform allow add tg:<id>")} ${g(e,"\u2014 or dashboard + bot /id")}`,` ${k(e,"omnish platform probe")} ${g(e,"\u2014 test WebSocket routing")}`,` ${k(e,"omnish run")} ${g(e,"\u2014 attach device; expect 'omnish attached to platform'")}`,"",B(e,"Subcommands"),k(e,"omnish platform login --url <url> --token <token> [--device-id <id>]"),g(e," Save platform URL and token to config.json."),"",k(e,"omnish platform status"),g(e," Show gatewayMode, connector status, allowlists, online devices."),"",k(e,"omnish platform allow list|add|set"),g(e," Manage platform allowlists (PUT /v1/me/allowlists)."),"",k(e,"omnish platform probe"),g(e," Test control-plane WebSocket routing (diagnose attached omnish run 400 errors)."),"",k(e,"omnish platform link-whatsapp"),g(e," Pair WhatsApp on the platform (terminal QR + poll until linked)."),"",k(e,"omnish platform unlink-whatsapp"),g(e," Remove platform WhatsApp session and auth files."),"",k(e,"omnish platform import-whatsapp [--auth-dir <path>] [--force]"),g(e," Upload local Baileys auth (after omnish link on this host). Use link-whatsapp or dashboard QR when possible."),g(e," Stop local omnish run before import to avoid WhatsApp session conflicts."),"",g(e,"Standalone omnish link on this host is not used for platform WhatsApp when attached."),g(e,"Docs: docs/guides/platform-reference.md (full API/CLI)"),g(e," docs/guides/platform-attached-mode.md (walkthrough)"),""].join(`
301
+ `))}async function Mg(e){let{authDir:t,force:n,help:r}=Cg(e);if(r){console.log(["Usage: omnish platform import-whatsapp [--auth-dir <path>] [--force]","","Requires OMNISH_PLATFORM_URL and OMNISH_TOKEN.","Default auth directory: ~/.omnish/auth (Baileys useMultiFileAuthState).","Stop `omnish run` on this machine before importing to avoid WhatsApp session conflicts.",""].join(`
302
+ `));return}let o=Ae();if(!o){console.error(C(process.stderr,"Set OMNISH_PLATFORM_URL (control plane URL) and OMNISH_TOKEN (from dashboard signup/login).")),process.exitCode=1;return}if(!Lo.existsSync(t)){console.error(C(process.stderr,`Auth directory not found: ${t}`)),process.exitCode=1;return}V();let s=No.join(t,"creds.json");if(!Lo.existsSync(s)){console.error(C(process.stderr,`No creds.json in ${t} \u2014 link WhatsApp locally first (omnish link).`)),process.exitCode=1;return}let i;try{i=Rg(t)}catch(f){console.error(C(process.stderr,String(f))),process.exitCode=1;return}if(Object.keys(i).length===0){console.error(C(process.stderr,"No files collected (empty auth directory?).")),process.exitCode=1;return}let a=o.platformUrl.replace(/\/$/,""),c=`${/^https?:\/\//i.test(a)?a:`http://${a}`}/v1/connectors/whatsapp/import`,u=await fetch(c,{method:"POST",headers:{"content-type":"application/json",Authorization:`Bearer ${o.token}`},body:JSON.stringify({files:i,force:n})}),d=await u.json().catch(()=>({}));if(!u.ok){console.error(C(process.stderr,d.error||`HTTP ${u.status}`)),process.exitCode=1;return}let p=d.qr?" QR emitted \u2014 open the platform dashboard if you need to scan.":"";console.log(N(process.stdout,`Uploaded Baileys auth. Platform connector status: ${d.status??"unknown"}.${p}`))}async function Tg(){let e=process.stdout,t=process.stderr,n=Ae();if(!n){console.error(C(t,"Set platform_url + platform_token (omnish config add) or OMNISH_PLATFORM_URL + OMNISH_TOKEN.")),process.exitCode=1;return}console.log(`${re(e,"Platform probe")} ${g(e,n.platformUrl)}`);let r=await Fu(n.platformUrl,n.token);console.log(` ${g(e,"wss /control:")} ${r.controlWsOk?k(e,"ok"):ae(e,"fail")}`);for(let o of["/platform/device","/control/device"]){if(r.ok&&r.deviceWsPath&&r.deviceWsPath!==o){console.log(` ${g(e,`wss ${o}:`)} ${J(e,"skipped (fallback used)")}`);continue}if(r.ok&&r.deviceWsPath===o){console.log(` ${g(e,`wss ${o}:`)} ${k(e,"ok")}`);continue}!r.ok&&o==="/platform/device"&&console.log(` ${g(e,`wss ${o}:`)} ${ae(e,"fail")}${r.deviceWsStatus?` (HTTP ${r.deviceWsStatus})`:""}`),!r.ok&&o==="/control/device"&&console.log(` ${g(e,`wss ${o}:`)} ${ae(e,"fail")}`)}if(r.ok){r.deviceWsPath==="/control/device"&&console.log(g(e," Using /control/device fallback (/platform/* not on control port \u2014 redeploy Caddy when convenient).")),console.log(N(e,"Attached omnish run should be able to connect."));return}console.error(C(t,r.error??"Platform probe failed.")),r.controlWsOk&&!r.deviceWsOk&&console.error(C(t,"Fix: redeploy the latest tunnel-relay image (enables wss /control/device), update omnish CLI, then retry. Also route /platform/* to 8788 when you can (Caddyfile.standalone).")),process.exitCode=1}async function Uu(e){let t=(e[0]??"").toLowerCase();if(!t||t==="-h"||t==="--help"){_o(process.stdout);return}if(t==="login"){await Iu(e.slice(1));return}if(t==="status"){await Tu();return}if(t==="allow"){await Eu(e.slice(1));return}if(t==="probe"){await Tg();return}if(t==="link-whatsapp"){await Lu();return}if(t==="unlink-whatsapp"){await Nu();return}if(t==="import-whatsapp"){await Mg(e.slice(1));return}console.error(C(process.stderr,`Unknown platform subcommand: ${e[0]}`)),_o(process.stderr),process.exitCode=1}j();import Bu from"node:crypto";import Yt from"node:fs";import ju from"node:path";function Eg(){return Bu.randomBytes(24).toString("hex")}function Du(){return Bu.randomBytes(32).toString("hex")}function Hu(e){try{let t=Yt.readFileSync(e,"utf8"),n=JSON.parse(t);if(typeof n.token=="string"&&n.token.length>=16&&typeof n.secret=="string"&&n.secret.length>=16)return{token:n.token,secret:n.secret}}catch{}return null}function Ig(){let e=Hu(Ve);if(e)return e;if(e=Hu(In),e){W(ju.dirname(Ve)),Yt.writeFileSync(Ve,JSON.stringify(e,null,2)+`
303
+ `,{mode:384});try{Yt.unlinkSync(In)}catch{}return e}return null}function Wu(e){W(ju.dirname(Ve));let t=Ig(),n=(e??"").trim();if(n){let o={token:n,secret:t?.secret??Du()};Yt.writeFileSync(Ve,JSON.stringify(o,null,2)+`
304
+ `,{mode:384});try{Yt.existsSync(In)&&Yt.unlinkSync(In)}catch{}return o}if(t)return t;let r={token:Eg(),secret:Du()};return Yt.writeFileSync(Ve,JSON.stringify(r,null,2)+`
305
+ `,{mode:384}),r}de();import At from"node:fs";import _g from"node:http";import we from"node:path";import qe from"node:process";import{fileURLToPath as Fg}from"node:url";import Ug from"node:os";Ue();j();import Ri from"node:crypto";var Mi="omnish_cfg_sess",Gu=7*24*60*60*1e3;function Ti(e){let t=Date.now()+Gu,n=Buffer.from(JSON.stringify({exp:t}),"utf8").toString("base64url"),r=Ri.createHmac("sha256",e).update(n).digest("hex");return`${n}.${r}`}function Ju(e,t){let n=Ag(t??"")[Mi];if(!n||!n.includes("."))return!1;let r=n.lastIndexOf("."),o=n.slice(0,r),s=n.slice(r+1),i=Ri.createHmac("sha256",e).update(o).digest("hex");try{let a=Buffer.from(s,"hex"),l=Buffer.from(i,"hex");if(a.length!==l.length||!Ri.timingSafeEqual(a,l))return!1}catch{return!1}try{let a=JSON.parse(Buffer.from(o,"base64url").toString("utf8"));return!(typeof a.exp!="number"||a.exp<Date.now())}catch{return!1}}function Ei(e){let t=Math.floor(Gu/1e3);return`${Mi}=${e}; HttpOnly; Path=/; SameSite=Lax; Max-Age=${t}`}function zu(){return`${Mi}=; HttpOnly; Path=/; SameSite=Lax; Max-Age=0`}function Ag(e){let t={};for(let n of e.split(";")){let r=n.indexOf("=");if(r===-1)continue;let o=n.slice(0,r).trim(),s=n.slice(r+1).trim();o&&(t[o]=decodeURIComponent(s))}return t}j();import Ii from"node:fs";import Fo from"node:process";function Ku(e){try{return Fo.kill(e,0),!0}catch{return!1}}function qu(e){let t=Date.now()+e;for(;Date.now()<t;);}function Pg(){try{let e=Ii.readFileSync(ir,"utf8"),t=JSON.parse(e);if(!t||typeof t!="object")return null;let n=t,r=typeof n.pid=="number"?n.pid:Number(n.pid),o=typeof n.port=="number"?n.port:Number(n.port),s=typeof n.host=="string"?n.host:"",i=typeof n.startedAt=="string"?n.startedAt:"";return!Number.isFinite(r)||r<=0||!Number.isFinite(o)||o<=0||o>65535?null:{pid:r,port:o,host:s,startedAt:i}}catch{return null}}function Yu(e){Ii.writeFileSync(ir,`${JSON.stringify(e)}
306
+ `,{mode:384})}function tr(){try{Ii.unlinkSync(ir)}catch{}}function Qu(e){let t=Pg();if(t&&t.port===e&&t.pid!==Fo.pid){if(!Ku(t.pid)){tr();return}try{Fo.kill(t.pid,"SIGTERM")}catch{}if(qu(350),Ku(t.pid)){try{Fo.kill(t.pid,"SIGKILL")}catch{}qu(100)}tr()}}de();j();import Ai from"node:fs";import Og from"node:process";function Lg(e){try{return Og.kill(e,0),!0}catch{return!1}}function Ng(){try{let e=Ai.readFileSync(ne,"utf8").trim(),t=Number.parseInt(e,10);return Number.isFinite(t)&&t>0?t:null}catch{return null}}function Uo(){let e=Ng();return e===null?!1:Lg(e)}var Pi=class{busy=!1;subscribers=new Set;abort=null;currentSock=null;subscribe(t){return this.subscribers.add(t),()=>{this.subscribers.delete(t)}}emit(t){for(let n of this.subscribers)try{n(t)}catch{}}requestCancel(){this.abort?.abort(),this.currentSock&&(hn(this.currentSock),this.currentSock=null)}beginPairing(t){if(this.busy)throw new Error("Pairing already in progress.");if(Uo())throw new Error("Gateway appears to be running (gateway.pid). Stop `omnish run` or your service before pairing from the browser.");if(!t.force&&Ge())throw new Error("WhatsApp session already linked. Use \u201CReplace session\u201D or run `omnish link --force` from the CLI.");V(),t.force&&(Ai.rmSync(X,{recursive:!0,force:!0}),Ai.mkdirSync(X,{recursive:!0,mode:448})),this.busy=!0,this.abort=new AbortController;let n=this.abort.signal;(async()=>{try{await kc({authDir:X,verbose:!1,printQr:!1,signal:n,onQr:r=>this.emit({type:"qr",payload:r}),onRestart515:()=>this.emit({type:"restart"}),onSocketReady:r=>{this.currentSock=r},onSocketClosed:()=>{this.currentSock=null}}),this.emit({type:"open"}),this.emit({type:"done",ok:!0})}catch(r){let o=r instanceof Error?r.message:String(r);o==="Pairing cancelled."?this.emit({type:"error",message:"Cancelled."}):this.emit({type:"error",message:o}),this.emit({type:"done",ok:!1})}finally{this.busy=!1,this.abort=null,this.currentSock=null}})()}},nr=new Pi;var te="application/json; charset=utf-8",rr=null;function Dg(){tr();let e=rr;rr=null,e?e.close(()=>{qe.exit(0)}):qe.exit(0),setTimeout(()=>qe.exit(0),4e3).unref()}function Hg(){let e=qe.env.OMNISH_CONFIG_UI_STATIC?.trim(),t=qe.env.OMNISH_UI_STATIC?.trim()||e;if(t&&At.existsSync(we.join(t,"index.html")))return t;let n=we.dirname(Fg(import.meta.url)),r=we.join(n,"ui");if(At.existsSync(we.join(r,"index.html")))return r;let o=we.join(n,"..","..","dist","ui");if(At.existsSync(we.join(o,"index.html")))return o;let s=we.join(qe.cwd(),"dist","ui");if(At.existsSync(we.join(s,"index.html")))return s;throw new Error("omnish ui static files not found (expected dist/ui/index.html). Run `pnpm build`.")}function Bg(e){let t=we.extname(e).toLowerCase();return{".html":"text/html; charset=utf-8",".js":"text/javascript; charset=utf-8",".css":"text/css; charset=utf-8",".svg":"image/svg+xml",".png":"image/png",".jpg":"image/jpeg",".jpeg":"image/jpeg",".webp":"image/webp",".ico":"image/x-icon",".woff2":"font/woff2",".json":"application/json; charset=utf-8"}[t]??"application/octet-stream"}function jg(e,t){let r=decodeURIComponent(t.split("?")[0]??"").replace(/^\/+/,""),o=we.normalize(we.join(e,r));return!o.startsWith(we.normalize(e+we.sep))&&o!==we.normalize(e)?null:o}async function Oi(e,t=512e3){let n=[],r=0;for await(let s of e){if(r+=s.length,r>t)throw new Error("Body too large.");n.push(s)}if(n.length===0)return;let o=Buffer.concat(n).toString("utf8");return JSON.parse(o)}function Wg(e){return St.includes(e)}function Li(e){let t=(e.telegramBotToken??"").trim(),n=t.length===0?"":t.length<=8?"(set)":`${t.slice(0,4)}\u2026${t.slice(-4)}`;return{...e,telegramBotToken:n,telegramBotTokenConfigured:!!pe(e),telegramBotTokenEnvOverride:typeof qe.env.TELEGRAM_BOT_TOKEN=="string"&&qe.env.TELEGRAM_BOT_TOKEN.trim().length>0}}function Gg(){let e=$();return{version:Be(),dataDir:U,configPath:L,waAuthDir:X,whatsappLinked:Ge(),gatewayPidHint:At.existsSync(we.join(U,"gateway.pid")),gatewayRunning:Uo(),gatewayLogFile:Ce,gatewayMode:e.gatewayMode,telegramBotTokenMasked:pe(e).length===0?"":Li(e).telegramBotToken,telegramBotTokenEnvOverride:typeof qe.env.TELEGRAM_BOT_TOKEN=="string"&&qe.env.TELEGRAM_BOT_TOKEN.trim().length>0}}function Jg(e,t){if(!Array.isArray(e)||!Array.isArray(t))throw new Error("allowFrom and telegramAllowFrom must be arrays of strings.");let n=e.map(a=>String(a).trim()).filter(Boolean),r=t.map(a=>String(a).trim()).filter(Boolean),o=ur(n).filter(a=>a!=="*"),s=[];for(let a of r){let l=ye(a);if(!l)throw new Error(`Invalid Telegram allow entry: ${a}`);s.push(l)}let i=$();i.allowFrom=o.sort(),i.telegramAllowFrom=[...new Set(s)].sort(),Re(i)}function zg(e){return typeof e=="string"?e:typeof e=="boolean"||typeof e=="number"?String(e):JSON.stringify(e)}async function Vu(e){let t=Hg(),{meta:n}=e;Qu(e.port);let r=_g.createServer(async(o,s)=>{try{let i=new URL(o.url??"/",`http://${o.headers.host??"localhost"}`),a=i.pathname;if(o.method==="GET"&&a==="/"){let p=i.searchParams.get("token")?.trim();if(p&&p===n.token){let f=Ti(n.secret);s.statusCode=302,s.setHeader("Location","/"),s.setHeader("Set-Cookie",Ei(f)),s.end();return}}if(a.startsWith("/api/")){let p=o.headers.cookie,f=Ju(n.secret,p);if(o.method==="POST"&&a==="/api/session"){let y=await Oi(o),h=typeof y?.token=="string"?y.token.trim():"";if(!h||h!==n.token){s.statusCode=401,s.setHeader("Content-Type",te),s.end(JSON.stringify({ok:!1,error:"Invalid token."}));return}let w=Ti(n.secret);s.statusCode=200,s.setHeader("Content-Type",te),s.setHeader("Set-Cookie",Ei(w)),s.end(JSON.stringify({ok:!0}));return}if(o.method==="GET"&&a==="/api/me"){s.statusCode=f?200:401,s.setHeader("Content-Type",te),s.end(JSON.stringify({ok:f}));return}if(!f){s.statusCode=401,s.setHeader("Content-Type",te),s.end(JSON.stringify({ok:!1,error:"Unauthorized."}));return}if(o.method==="GET"&&a==="/api/status"){s.statusCode=200,s.setHeader("Content-Type",te),s.end(JSON.stringify({ok:!0,...Gg()}));return}if(o.method==="GET"&&a==="/api/config"){s.statusCode=200,s.setHeader("Content-Type",te),s.end(JSON.stringify({ok:!0,config:Li($())}));return}if(o.method==="PUT"&&a==="/api/config"){let y=await Oi(o);if(!y||typeof y!="object"){s.statusCode=400,s.setHeader("Content-Type",te),s.end(JSON.stringify({ok:!1,error:"Expected JSON object."}));return}let h=y;for(let[w,v]of Object.entries(h))if(v!==void 0&&!(w==="allowFrom"||w==="telegramAllowFrom")){if(w==="telegramBotToken"){let x=typeof v=="string"?v.trim():"";if(!x||x==="(set)"||x.includes("\u2026"))continue;if(!Ze(x))throw new Error("Invalid Telegram bot token format.");Cr("telegramBotToken",x);continue}if(!Wg(w))throw new Error(`Unknown or unsupported config key: ${w}`);Cr(w,zg(v))}if("allowFrom"in h||"telegramAllowFrom"in h){let w=$();Jg("allowFrom"in h?h.allowFrom:w.allowFrom,"telegramAllowFrom"in h?h.telegramAllowFrom:w.telegramAllowFrom)}s.statusCode=200,s.setHeader("Content-Type",te),s.end(JSON.stringify({ok:!0,config:Li($())}));return}if(o.method==="POST"&&a==="/api/logout"){s.statusCode=200,s.setHeader("Content-Type",te),s.setHeader("Set-Cookie",zu()),s.end(JSON.stringify({ok:!0}));return}if(o.method==="POST"&&a==="/api/shutdown"){s.statusCode=200,s.setHeader("Content-Type",te),s.end(JSON.stringify({ok:!0})),setImmediate(()=>{nr.requestCancel(),Dg()});return}if(o.method==="POST"&&a==="/api/gateway/start"){if(Uo()){s.statusCode=409,s.setHeader("Content-Type",te),s.end(JSON.stringify({ok:!1,error:"Gateway already appears to be running (pidfile + live process). Stop it first if you need to restart."}));return}let y=Io();if(!y.ok){s.statusCode=400,s.setHeader("Content-Type",te),s.end(JSON.stringify({ok:!1,error:y.message}));return}let h=Ce,w=io(h);if(!w.ok){s.statusCode=500,s.setHeader("Content-Type",te),s.end(JSON.stringify({ok:!1,error:w.message}));return}s.statusCode=200,s.setHeader("Content-Type",te),s.end(JSON.stringify({ok:!0,pid:w.pid,logFile:h}));return}if(o.method==="POST"&&a==="/api/gateway/stop"){let y=ao();switch(y.outcome){case"no_pidfile":s.statusCode=400,s.setHeader("Content-Type",te),s.end(JSON.stringify({ok:!1,error:"No gateway pidfile \u2014 background gateway does not appear to be running."}));return;case"invalid_pidfile":s.statusCode=400,s.setHeader("Content-Type",te),s.end(JSON.stringify({ok:!1,error:"Invalid pidfile (removed)."}));return;case"stale_cleaned":s.statusCode=200,s.setHeader("Content-Type",te),s.end(JSON.stringify({ok:!0,stale:!0,pid:y.pid,message:"Process was not running; stale pidfile removed."}));return;case"sent_signal":s.statusCode=200,s.setHeader("Content-Type",te),s.end(JSON.stringify({ok:!0,pid:y.pid}));return;case"taskkill_ok":s.statusCode=200,s.setHeader("Content-Type",te),s.end(JSON.stringify({ok:!0,pid:y.pid,taskkill:!0}));return;case"failed":s.statusCode=500,s.setHeader("Content-Type",te),s.end(JSON.stringify({ok:!1,error:y.message}));return}}if(o.method==="GET"&&a==="/api/wa/link/events"){s.writeHead(200,{"Content-Type":"text/event-stream; charset=utf-8","Cache-Control":"no-store",Connection:"keep-alive"}),s.write(`: connected
257
307
 
258
- `);let g=Sn.subscribe(f=>{s.write(`data: ${JSON.stringify(f)}
308
+ `);let y=nr.subscribe(h=>{s.write(`data: ${JSON.stringify(h)}
259
309
 
260
- `)});o.on("close",()=>{g()});return}if(o.method==="POST"&&a==="/api/wa/link/start"){let f=(await Xo(o))?.force===!0;try{Sn.beginPairing({force:f})}catch(h){let x=String(h),k=x.includes("already in progress")||x.includes("Gateway appears")?409:400;s.statusCode=k,s.setHeader("Content-Type",Q),s.end(JSON.stringify({ok:!1,error:x}));return}s.statusCode=202,s.setHeader("Content-Type",Q),s.end(JSON.stringify({ok:!0}));return}if(o.method==="POST"&&a==="/api/wa/link/cancel"){Sn.requestCancel(),s.statusCode=200,s.setHeader("Content-Type",Q),s.end(JSON.stringify({ok:!0}));return}s.statusCode=404,s.setHeader("Content-Type",Q),s.end(JSON.stringify({ok:!1,error:"Not found."}));return}let l=a==="/"?"index.html":a.replace(/^\/+/,""),u=wp(t,l),c=de.join(t,"index.html");u&&lt.existsSync(u)&&lt.statSync(u).isFile()&&(c=u);let d=lt.readFileSync(c);s.statusCode=200,s.setHeader("Content-Type",yp(c)),s.setHeader("Cache-Control",de.basename(c)==="index.html"?"no-store":"public, max-age=3600"),s.end(d)}catch(i){s.statusCode=500,s.setHeader("Content-Type",Q),s.end(JSON.stringify({ok:!1,error:String(i)}))}});await new Promise((o,s)=>{r.once("error",s),r.listen(e.port,e.host,()=>o())}),xn=r,xl({pid:Fe.pid,port:e.port,host:e.host,startedAt:new Date().toISOString()}),r.on("close",()=>{kn(),xn===r&&(xn=null)})}function Cl(e){let t=fp.networkInterfaces(),n=[];for(let r of Object.values(t))if(r)for(let o of r)o.family==="IPv4"&&!o.internal&&n.push(`http://${o.address}:${e}/`);return[...new Set(n)].sort()}import Tp from"node:readline";import Ml from"node:path";import ce from"node:process";import vp from"node:net";import $p from"node:fs";function Cp(){try{let e=$p.readFileSync(xt,"utf8"),t=JSON.parse(e);return typeof t.host!="string"||typeof t.port!="number"||typeof t.token!="string"||!Number.isFinite(t.port)?null:{host:t.host,port:t.port,token:t.token}}catch{return null}}async function vn(e){let t=Cp();if(!t)return"No gateway control endpoint \u2014 is `omnish run` active? (control metadata missing.)";let n={...e,token:t.token},r=`${JSON.stringify(n)}
261
- `;return new Promise(o=>{let s=!1,i="";function a(u){s||(s=!0,o(u))}let l=vp.connect({host:t.host,port:t.port},()=>{l.write(r)});l.setTimeout(6e5),l.on("data",u=>{i+=u.toString("utf8");let c=i.indexOf(`
262
- `);if(c>=0){let d=i.slice(0,c).trim();try{let m=JSON.parse(d);m.ok?a(null):a(m.error||"Unknown error from gateway control.")}catch{a("Invalid response from gateway control.")}l.destroy()}}),l.on("error",u=>{a(`Control connection failed: ${String(u)}`)}),l.on("timeout",()=>{l.destroy(),a("Gateway control timed out.")}),l.on("close",()=>{!s&&i.trim()===""&&a("Gateway closed the control connection without a response.")})})}function Rp(e){let t=e.trimStart();if(/^--text=/i.test(t)){let n=t.slice(t.indexOf("=")+1).trimEnd();return n.length>0?n:null}if(/^--text(\s+|$)/i.test(t)){let n=t.replace(/^--text\s*/i,"").trim();return n.length>0?n:null}if(/^-t=/i.test(t)){let n=t.slice(t.indexOf("=")+1).trimEnd();return n.length>0?n:null}if(/^-t(\s+|$)/i.test(t)){let n=t.replace(/^-t\s*/i,"").trim();return n.length>0?n:null}}function Mp(e){let t=e.toLowerCase();if(t==="*"||t==="all")return{channel:"all"};if(t==="wa"||t==="whatsapp"||t==="wa:all"||t==="whatsapp:all")return{channel:"whatsapp-all"};if(t==="tg"||t==="telegram"||t==="tg:all"||t==="telegram:all")return{channel:"telegram-all"};if(t.startsWith("tg:")||t.startsWith("telegram:")){let n=ue(e);if(!n)return null;let r=Number(n);return Number.isFinite(r)?{channel:"telegram",chatId:r}:null}if(t.startsWith("wa:")){let n=e.slice(3).trim();if(!n)return null;let r=n.split(",").map(o=>G(o.trim())).filter(o=>!!o);return r.length===0?null:{channel:"whatsapp",e164s:r}}if(e.includes(",")){let n=e.split(",").map(r=>G(r.trim())).filter(r=>!!r);return n.length===0?null:{channel:"whatsapp",e164s:n}}if(e.startsWith("+")){let n=G(e);return n?{channel:"whatsapp",e164s:[n]}:null}return null}function Rl(e){let t=e.trim();if(!/^\/sendto(\s|$)/i.test(t))return null;let n=t.replace(/^\/sendto\s+/i,"").trim();if(!n)return null;let r=n.indexOf(" ");if(r===-1)return null;let o=n.slice(0,r).trim(),s=n.slice(r+1).trim();if(!s)return null;let i=Mp(o);if(!i)return null;let a=Rp(s);if(a===null)return null;if(a!==void 0)return{...i,mode:"text",body:a};let l=_n(s);if(!l)return null;let{selectorPart:u,caption:c}=l;return{...i,mode:"media",selectorPart:u,caption:c}}function es(){return["/sendto wa|tg|* <selectors> [-- caption]","/sendto +E164 or +E164,+E164 <selectors> [-- caption]","/sendto <dest> --text <message> or --text=<msg> or -t <msg>","/sendto tg:<chat_id> <selectors> [-- caption] (compat: single Telegram id)","selectors: file1,file2 or *.mp4 or **/*.mp4 (from current session cwd); a path ./--text sends a file named --text","examples: /sendto wa ./promo.mp4 | /sendto * **/*.mp4 -- Daily clips","examples: /sendto +15551234567 --text Hello | /sendto tg:123 -t=Status OK","examples: /sendto +15550000001,+15550000002 intro.png,deck.pdf -- Launch","Also supports compat aliases: wa:all, whatsapp:all, tg:all, telegram:all.","Requires `omnish run` on this machine."].join(`
263
- `)}var Jt="wa:cli:interactive",Ip={onPlainTextLlmFallback(e,t){mr(R(),e,t,async n=>{n.trim()&&console.log(ae(ce.stdout,n))})}};function Ep(e){let t=e.trim();if(!t)return null;let n=t.toLowerCase();if(n.startsWith("tg:")||n.startsWith("telegram:")){let s=ue(t);return s?`tg:${s}`:null}let r=n.startsWith("wa:")?t.slice(3):t,o=G(r);return o?`wa:${o}`:null}function Ap(e){let t=null,n=null;for(let r=0;r<e.length;){let o=e[r]??"";if(o==="--as"){let s=e[r+1];if(!s||s.startsWith("-"))return{opts:{senderKey:null,oneShot:null},error:"--as requires a sender (wa:+E164 or tg:<id>)."};let i=Ep(s);if(!i)return{opts:{senderKey:null,oneShot:null},error:`Could not parse sender "${s}". Use +E164, wa:+E164, or tg:<user_id>.`};t=i,r+=2;continue}if(o==="-c"||o==="--command"){let s=e[r+1];if(typeof s!="string")return{opts:{senderKey:null,oneShot:null},error:`${o} requires a command string.`};n=s,r+=2;continue}return o==="--help"||o==="-h"?{opts:{senderKey:null,oneShot:null},error:"help"}:{opts:{senderKey:null,oneShot:null},error:`Unknown argument: ${o}`}}return{opts:{senderKey:t,oneShot:n},error:null}}function ts(e){let t=ce.cwd(),n=[`${Z(e,"omnish i")} ${b(e,"[options]")}`,se(e,"Interactive shell \u2014 same commands as WhatsApp/Telegram chat."),"",X(e,"Usage:"),` ${v(e,"omnish i [options]")}`,` ${v(e,"omnish interactive [options]")}`,"",X(e,"Options:"),...We(e," ",[{left:"--as <sender>",right:"Sender key for cluster commands (wa:+E164 or tg:id). Default: synthetic wa:cli:interactive."},{left:"-c, --command <line>",right:"Run one line and exit (non-interactive)."},{left:"-h, --help",right:"Show this help."}],r=>b(e,r)),"",`${b(e,"Trust:")} ${v(e,"Full local access like your shell; not gated by inbox allowlist.")}`,`${b(e,"Jobs:")} ${v(e,"/bg and /jobs apply only to this REPL session (not the gateway process).")}`,`${b(e,"Files:")} ${v(e,"Use /sendto for files or --text for plain messages through the gateway; plain /send needs a chat peer.")}`,`${b(e,"Gateway:")} ${v(e,"/reload and /updates require omnish run; /sendto requires omnish run for WA/TG delivery.")}`,"",es(),"",`${b(e,"cwd:")} ${v(e,`session starts at ${t} (change with !cd or ${R().commandPrefix}cd).`)}`];console.log(n.join(`
264
- `))}function Tl(e,t){let n=new Set,r=new Set;if(e.channel==="whatsapp-all"||e.channel==="all"){let o=new Set;for(let s of t.allowFrom){let i=G(String(s));i&&!o.has(i)&&(o.add(i),n.add(i))}}if(e.channel==="telegram-all"||e.channel==="all")for(let o of t.telegramAllowFrom){let s=ue(String(o));if(!s)continue;let i=Number(s);Number.isFinite(i)&&r.add(i)}if(e.channel==="whatsapp")for(let o of e.e164s)n.add(o);return e.channel==="telegram"&&r.add(e.chatId),{waTargets:n,tgTargets:r}}async function Lp(e){let t=R();if(e.mode==="text"){let{waTargets:d,tgTargets:m}=Tl(e,t);if(d.size===0&&m.size===0)return{error:"No recipients matched the requested /sendto destination."};let y=[];for(let g of d){let f=await vn({op:"sendText",channel:"whatsapp",e164:g,text:e.body});f&&y.push(`[wa:${g}] ${f}`)}for(let g of m){let f=await vn({op:"sendText",channel:"telegram",chatId:g,text:e.body});f&&y.push(`[tg:${g}] ${f}`)}return y.length>0?{error:y.join(`
265
- `)}:{error:null,kind:"text",recipientsSent:d.size+m.size}}let n=q(Jt).cwd,r=await Yt(n,e.selectorPart);if(r.length===0)return{error:`No files matched: ${e.selectorPart}`};let o=await Vt(r);if(!o.ok)return{error:o.error};let s=r.map(d=>et(d,t.fileSendMaxBytes));for(let d of s)if("error"in d)return{error:d.error};let{waTargets:i,tgTargets:a}=Tl(e,t);if(i.size===0&&a.size===0)return{error:"No recipients matched the requested /sendto destination."};let l=[];for(let d of i)for(let m of s){if("error"in m)continue;let y=await vn({op:"sendMedia",channel:"whatsapp",e164:d,absPath:m.absPath,caption:e.caption});y&&l.push(`[wa:${d}] ${m.displayName}: ${y}`)}for(let d of a)for(let m of s){if("error"in m)continue;let y=await vn({op:"sendMedia",channel:"telegram",chatId:d,absPath:m.absPath,caption:e.caption});y&&l.push(`[tg:${d}] ${m.displayName}: ${y}`)}if(l.length>0)return{error:l.join(`
266
- `)};let u=i.size+a.size,c=s.length;return{error:null,kind:"media",recipientsSent:u,filesSent:c,messagesSent:u*c}}async function Op(e,t,n,r,o,s,i){let a=e.trim();if(!a)return;let l=Rl(a);if(l!==null||/^\/sendto(\s|$)/i.test(a)){if(l===null){console.log(O(ce.stderr,`Invalid /sendto.
267
- `+es()));return}let d=await Lp(l);if(d.error!==null)console.log(O(ce.stderr,d.error));else if(d.kind==="text")console.log(ae(ce.stdout,`Sent text to ${d.recipientsSent} recipient(s).`));else{let m=`Sent ${d.filesSent} file(s) to ${d.recipientsSent} recipient(s) (${d.messagesSent} message(s)).`;console.log(ae(ce.stdout,m))}return}let u={peerKey:Jt,text:e},c=await wt(R(),t,n,r,o,u,s,void 0,i,!1,Ip);c!==null&&await Pp(c)}async function Pp(e){if(e===null)return;if(e.kind==="file"||e.kind==="files"){console.log(ae(ce.stdout,["This CLI session has no chat peer to attach to.","Push through the gateway instead, for example:"," /sendto wa:+15551234567 ./my.pdf"," /sendto tg:123456789 ~/photo.jpg -- optional caption","(Requires `omnish run` on this machine.)"].join(`
268
- `)));return}let t=De(e.body,"whatsapp").text;t.trim()&&console.log(t)}async function Il(e){let t=Ap(e);if(t.error==="help"){ts(ce.stdout);return}if(t.error&&t.error!==null){console.error(O(ce.stderr,t.error)),console.error(b(ce.stderr,"Try: omnish i --help")),ce.exitCode=1;return}let{senderKey:n,oneShot:r}=t.opts,o=n??Jt;z(),Bn(Jt,ce.cwd());let s=new ht,i=new Map,a=new Map,l=new Map,u=new Ht(()=>R(),async(y,g)=>{ce.stdout.write(g),g.endsWith(`
269
- `)||ce.stdout.write(`
270
- `)}),c=async y=>{try{await Op(y,s,i,a,l,u,o)}catch(g){console.error(O(ce.stderr,String(g)))}};if(r!==null){await c(r),u.dispose(),s.killAllRunning();return}let d=Tp.createInterface({input:ce.stdin,output:ce.stdout}),m=Ml.basename(q(Jt).cwd);d.setPrompt(`${m}> `),d.on("line",y=>{c(y).then(()=>{let g=Ml.basename(q(Jt).cwd);d.setPrompt(`${g}> `),d.prompt()})}),d.on("close",()=>{u.dispose(),s.killAllRunning(),ce.stdout.write(`
271
- `)}),d.prompt()}Fp.setDefaultResultOrder("ipv4first");function Ll(){let e=process.stdout,t=[`${ie(e,"omnish run")} ${b(e,"[options]")}`,se(e,"Listen for DMs and run shell commands from allowlisted chats."),"",X(e,"Usage:"),` ${v(e,"omnish run [options]")}`,"",X(e,"Options:"),...We(e," ",[{left:"-d, --background",right:"Start the gateway detached; log to --log-file (default: <data>/logs/gateway.log)."},{left:"--log-file <path>",right:`Append stdout/stderr when background (default: ${fe}).`},{left:"-h, --help",right:"Show this help."}],n=>b(e,n)),"",`${v(e,"Config reload:")} ${b(e,"while the gateway runs, edit config then send /reload or /restart from an allowlisted chat (no restart needed for many keys). /updates checks npm (and optional updateInfoUrl).")}`,""];console.log(t.join(`
272
- `))}function Ol(){let e=process.stdout,t=[{left:"omnish link [--force]",right:"WhatsApp: scan QR (Linked devices). --force wipes session first."},{left:"omnish link --tg <bot_token>",right:"Telegram: save token to config; gatewayMode telegram or both if WhatsApp is linked."}],n=t.map(i=>b(e,i.left)),r=Math.max(...n.map(Dn)),o=t.map((i,a)=>Pr(" ",r,n[a],v(e,i.right))),s=[`${ie(e,"omnish link")} ${b(e,"[options]")}`,se(e,"Connect WhatsApp (QR) or save a Telegram bot token."),"",X(e,"Usage:"),` ${v(e,"omnish link [--force]")}`,` ${v(e,"omnish link --tg <bot_token>")}`,"",X(e,"Modes:"),...o,` ${se(e,"Do not combine --tg with --force.")}`,"",X(e,"Options:"),...We(e," ",[{left:"-f, --force",right:"WhatsApp only: delete saved session before pairing."},{left:"-h, --help",right:"Show this help."}],i=>b(e,i)),"",`${v(e,"Next:")} ${Z(e,"omnish allow tg:<your_user_id>")} ${b(e,"then")} ${Z(e,"omnish run")}`,`${b(e,"Config:")} ${v(e,P)}`,""];console.log(s.join(`
273
- `))}function Np(e){let t=!1,n=null;for(let r=0;r<e.length;r++){let o=e[r]??"";if(o==="--help"||o==="-h")return{kind:"help"};if(o==="--force"||o==="-f"){t=!0;continue}if(o==="--tg"||o==="--telegram"){let s=e[r+1];if(!s||s.startsWith("-"))return{kind:"error",message:"[omnish] --tg requires a bot token (from @BotFather)."};n=s,r++;continue}if(o.startsWith("--tg=")||o.startsWith("--telegram=")){let s=o.indexOf("="),i=o.slice(s+1).trim();if(!i)return{kind:"error",message:"[omnish] --tg= requires a non-empty bot token."};n=i;continue}return{kind:"error",message:`[omnish] unknown link argument: ${o}
274
- Try: omnish link --help`}}return n!==null?t?{kind:"error",message:"[omnish] --force applies to WhatsApp only; do not combine with --tg."}:{kind:"tg",token:n}:{kind:"wa",force:t}}function rs(){let e=process.stdout,t=`${ie(e,"omnish")} ${b(e,`v${Ue()}`)}`,n=[{left:"link [--force] [--tg <token>]",right:"WhatsApp (QR) or Telegram bot token \u2014 omnish link --help"},{left:"run [options]",right:"Listen for DMs (WhatsApp and/or Telegram \u2014 see gatewayMode in config)"},{left:"stop",right:`Stop background gateway (pidfile: ${ne})`},{left:"service <subcommand>",right:"Boot install hints, logs, systemd/LaunchAgent \u2014 omnish service help"},{left:"logout",right:"Delete saved WhatsApp session"},{left:"allow +<E164> | tg:<id>",right:"Add allowlist entry"},{left:"deny +<E164> | tg:<id>",right:"Remove allowlist entry"},{left:"status [--check-updates]",right:"Channels, identity, allowlists, jobs, security, cluster (if enabled)"},{left:"commands",right:"Chat commands for allowlisted users (same as /help)"},{left:"security [--json]",right:"Configuration security report (JSON for scripts)"},{left:"cluster [status | use <sender> <label-or-id>]",right:"Per-sender machine bindings"},{left:"i | interactive [options]",right:"Local REPL (chat commands; /sendto needs omnish run)"},{left:"ui [options]",right:"Browser setup UI on LAN (token-gated) \u2014 omnish ui --help"}],r=[{left:"-v, --version",right:"Print version and exit."},{left:"-h, --help",right:"Show this help (same as omnish help)."}],o=[t,se(e,"Allowlisted inbox \u2192 your real shell. No AI."),"",X(e,"Usage:"),` ${v(e,"omnish [options] <command> [args...]")}`,"",X(e,"Options:"),...We(e," ",r,s=>b(e,s)),"",X(e,"Commands:"),...We(e," ",n,s=>b(e,s)),"",`${b(e,"Config:")} ${v(e,`${P} \u2014 gatewayMode: "whatsapp" | "telegram" | "both"`)}`,`${b(e,"Env:")} ${v(e,"OMNISH_VERBOSE=1 (Baileys; legacy WHATSVERBOSE=1 still works), TELEGRAM_BOT_TOKEN (optional)")}`,`${b(e,"Data:")} ${v(e,"~/.omnish by default; ~/.whatslive reused if it already exists. OMNISH_HOME overrides.")}`,`${b(e,"See also:")} ${v(e,"https://omnish.dev")}`,""];console.log(o.join(`
275
- `))}function _p(e){let t=(e??"").trim().toLowerCase(),n=process.stdout;if(!t){rs();return}switch(t){case"link":Ol();return;case"run":Ll();return;case"service":Fl();return;case"i":case"interactive":ts(n);return;case"ui":Nl();return;default:console.error(O(process.stderr,`No detailed help for "${e}". Try: omnish help`)),process.exitCode=1}}function Bp(e){let t=!1,n="",r=!1;for(let s=0;s<e.length;s++){let i=e[s]??"";if(i==="-d"||i==="--background")t=!0;else if(i==="--log-file"||i==="--log"){let a=e[++s];a||(console.error(O(process.stderr,"--log-file requires a path.")),process.exit(1)),n=a}else if(i==="--help"||i==="-h")r=!0;else{let a=process.stderr;console.error(O(a,`unknown run option: ${i}`)),console.error(O(a,"Try: omnish run --help")),process.exit(1)}}let o=n.trim()!==""?ns.isAbsolute(n)?n:ns.resolve(process.cwd(),n):fe;return{background:t,logFile:o,help:r}}function Hp(e){let t=Sr(e);t.ok||(console.error(O(process.stderr,t.message)),process.exit(1));let n=process.stdout;console.log(`${ae(n,`gateway started in background (pid ${t.pid}).`)} ${b(n,`Log: ${e}`)}`)}function Dp(){let e=xr();switch(e.outcome){case"no_pidfile":console.error(O(process.stderr,`no pidfile at ${ne} \u2014 is a background gateway running?`)),process.exitCode=1;return;case"invalid_pidfile":console.error(O(process.stderr,"invalid pidfile.")),process.exitCode=1;return;case"stale_cleaned":console.log(ae(process.stdout,`process ${e.pid} is not running; removing stale pidfile.`));return;case"sent_signal":console.log(ae(process.stdout,`sent SIGTERM to gateway (pid ${e.pid}).`));return;case"taskkill_ok":console.log(ae(process.stdout,`stopped gateway (pid ${e.pid}) using taskkill.`));return;case"failed":console.error(O(process.stderr,e.message)),process.exitCode=1;return}}function El(){if(process.env.OMNISH_BACKGROUND_GATEWAY==="1")try{ct.readFileSync(ne,"utf8").trim()===String(process.pid)&&ct.unlinkSync(ne)}catch{}}function jp(e){return e.length<=8?"(set)":`${e.slice(0,4)}\u2026${e.slice(-4)}`}function Up(){if(!ct.existsSync(ne))return"gateway process: not running (no pid file)";let e=ct.readFileSync(ne,"utf8").trim(),t=Number(e);if(!Number.isFinite(t)||t<=0)return"gateway process: invalid pid file";try{return process.kill(t,0),`gateway process: running (pid ${t})`}catch{return`gateway process: not running (stale pid ${t} in pid file)`}}var Pl=120;function Fl(){let e=process.stdout,t=[{left:"help",right:"This help."},{left:"instructions",right:"Copy-paste install steps for this machine."},{left:"status",right:"Data dir, pidfile, Node + entry script."},{left:"logs [n]",right:`Tail default gateway log (default 80 lines, max ${Pl}).`},{left:"install",right:"Write user systemd / LaunchAgent unit (needs serviceInstallFromChat=true)."},{left:"uninstall",right:"Remove that unit (same gate as install)."}],n=[`${ie(e,"omnish service")} ${b(e,"<subcommand>")}`,se(e,"Boot integration, crash restart policy, and logs (same ideas as /service in chat)."),"",X(e,"Usage:"),` ${v(e,"omnish service <subcommand>")}`,"",X(e,"Subcommands:"),...We(e," ",t,r=>b(e,r)),"",X(e,"Restart and reload:"),` ${v(e,"Process")} ${b(e,"\u2014 Linux user unit: Restart=on-failure, RestartSec=5. macOS: KeepAlive. Full restart loads config from disk.")}`,` ${v(e,"Config live")} ${b(e,"\u2014 allowlisted chat while gateway runs: /reload or /restart")}`,"",`${b(e,"Docs:")} ${v(e,"docs/guides/background-and-boot.md")} ${se(e,"\xB7")} https://omnish.dev`,""];console.log(n.join(`
276
- `))}function Wp(e){let t=process.stdout,n=process.stderr,r=(e[0]??"help").toLowerCase();if(r==="help"||r==="--help"||r==="-h"){Fl();return}if(r==="instructions"){let o=yt();if(o.error){console.error(O(n,o.error)),process.exitCode=1;return}console.log(tr(o));return}if(r==="status"){let o=yt(),s=(()=>{try{return ct.existsSync(ne)?`gateway.pid: ${ct.readFileSync(ne,"utf8").trim()}`:"gateway.pid: (missing)"}catch(d){return`gateway.pid: (read error: ${String(d)})`}})(),i=process.env.OMNISH_BACKGROUND_GATEWAY==="1"?"This process: background gateway (OMNISH_BACKGROUND_GATEWAY=1).":"This process: CLI (not the gateway \u2014 run omnish service status on the host where the gateway runs for live pid info).",a=typeof process.env.OMNISH_HOME=="string"&&process.env.OMNISH_HOME.trim()?`OMNISH_HOME env: ${process.env.OMNISH_HOME.trim()}`:"OMNISH_HOME env: (not set \u2014 using default data dir)",l=o.error?o.error:`Node: ${o.nodePath}
277
- Script: ${o.scriptPath}`,c=R().serviceInstallFromChat?"Install from CLI/chat: enabled (omnish service install / /service install).":"Install from CLI/chat: off \u2014 set serviceInstallFromChat true in config (same trust as shell).";console.log(ie(t,"omnish service status")),console.log(""),console.log(`${b(t,"platform:")} ${v(t,process.platform)}`),console.log(`${b(t,"session:")} ${v(t,i)}`),console.log(`${b(t,"env:")} ${v(t,a)}`),console.log(`${b(t,"data dir:")} ${v(t,N)}`),console.log(`${b(t,"pidfile:")} ${v(t,s)}`),console.log(`${b(t,"default log:")} ${v(t,fe)}`),console.log(""),console.log(l),console.log(""),console.log(v(t,c));return}if(r==="logs"){let o=e.length>=2?Number.parseInt(e[1],10):80,s=Number.isFinite(o)&&o>0?Math.min(o,Pl):80,i=sr(fe,s);console.log(`${b(t,"file:")} ${v(t,fe)}`),console.log(`${b(t,"lines:")} ${v(t,String(s))}`),console.log(""),console.log(i);return}if(r==="install"){if(!R().serviceInstallFromChat){console.error(O(n,"Install is disabled. Set serviceInstallFromChat to true in config (same trust as shell), then run again.")),process.exitCode=1;return}let s=nr();s.ok?console.log(ae(t,s.detail)):(console.error(O(n,s.detail)),process.exitCode=1);return}if(r==="uninstall"){if(!R().serviceInstallFromChat){console.error(O(n,"Uninstall is disabled. Set serviceInstallFromChat to true in config or remove the unit file on the host manually.")),process.exitCode=1;return}let s=rr();s.ok?console.log(ae(t,s.detail)):(console.error(O(n,s.detail)),process.exitCode=1);return}console.error(O(n,`Unknown subcommand "${r}". Try: omnish service help`)),process.exitCode=1}function Gp(e){let t=e.trim();if(!t)return null;let n=t.toLowerCase();if(n.startsWith("tg:")||n.startsWith("telegram:")){let s=ue(t);return s?`tg:${s}`:null}let r=n.startsWith("wa:")?t.slice(3):t,o=G(r);return o?`wa:${o}`:null}async function Jp(){let e=null,t=$r();t.ok||(console.error(O(process.stderr,t.message)),process.exit(1));let n=R(),r=n.gatewayMode,o=r==="whatsapp"||r==="both",s=r==="telegram"||r==="both",i=oe(n),a=ot(n),l=process.stderr,u=Es(a,"warn");if(u.length>0&&(console.warn(`${ae(l,`Security (${u.length} finding(s)):`)}
278
- `),console.warn(_r(u,l))),process.env.OMNISH_BACKGROUND_GATEWAY==="1")try{ct.writeFileSync(ne,`${process.pid}
279
- `,{mode:384})}catch(T){E.warn({err:String(T)},"could not write gateway pidfile")}let c=(T,w)=>{let S=R();if(!S.clusterEnabled)return T;let I=Me(),_=(S.clusterLabel??"").trim()||Al.hostname(),B=null;if(w.startsWith("tg:"))B=w;else if(w){let me=G(w);me&&(B=`wa:${me}`)}let V=B?Ye(S,B):null;return Di(T,{nodeId:I,label:_,role:S.clusterRole,activeNodeId:V?.nodeId??""})},d=new ht,m=new Map,y=new Map,g=new Map,f=null,h={stop:null,sendText:null,sendMedia:null},x=null,k=!1,$=async(T,w)=>{if(T.startsWith("wa:")){let S=T.slice(3);f&&await f.sendText(S,w)}else if(T.startsWith("tg:")){let S=Number(T.slice(3));h.sendText&&Number.isFinite(S)&&await h.sendText(S,p(w))}},A={onPlainTextLlmFallback(T,w){mr(R(),T,w,S=>$(T,S))}},M=async(T,w)=>{if(T.startsWith("wa:")){let S=T.slice(3);f&&await f.sendMedia(S,w)}else if(T.startsWith("tg:")){let S=Number(T.slice(3));h.sendMedia&&Number.isFinite(S)&&await h.sendMedia(S,w)}},L=()=>new Ht(()=>R(),$),U=L();x=U;let re,Se={async reload(){try{E.info("gateway reload requested from chat"),await h.stop?.().catch(()=>{}),h.stop=null,h.sendText=null,h.sendMedia=null;let T=R(),w=T.gatewayMode==="telegram"||T.gatewayMode==="both",S=oe(T);if(w&&S){let B=await Uo(S,()=>R(),re,{decorate:c});h.sendText=B.sendText,h.sendMedia=B.sendMedia,h.stop=B.stop}let I=["Reload complete.",`gatewayMode: ${T.gatewayMode}`,w&&S?"Telegram bot is running with the current token.":"Telegram bot is stopped (enable telegram/both + token if you want it).","Allowlists, shell, app session limits, and timeouts are read from disk on each command."].join(`
280
- `),_=ir(un());return{ok:!0,summary:_?`${I}
310
+ `)});o.on("close",()=>{y()});return}if(o.method==="POST"&&a==="/api/wa/link/start"){let h=(await Oi(o))?.force===!0;try{nr.beginPairing({force:h})}catch(w){let v=String(w),x=v.includes("already in progress")||v.includes("Gateway appears")?409:400;s.statusCode=x,s.setHeader("Content-Type",te),s.end(JSON.stringify({ok:!1,error:v}));return}s.statusCode=202,s.setHeader("Content-Type",te),s.end(JSON.stringify({ok:!0}));return}if(o.method==="POST"&&a==="/api/wa/link/cancel"){nr.requestCancel(),s.statusCode=200,s.setHeader("Content-Type",te),s.end(JSON.stringify({ok:!0}));return}s.statusCode=404,s.setHeader("Content-Type",te),s.end(JSON.stringify({ok:!1,error:"Not found."}));return}let l=a==="/"?"index.html":a.replace(/^\/+/,""),c=jg(t,l),u=we.join(t,"index.html");c&&At.existsSync(c)&&At.statSync(c).isFile()&&(u=c);let d=At.readFileSync(u);s.statusCode=200,s.setHeader("Content-Type",Bg(u)),s.setHeader("Cache-Control",we.basename(u)==="index.html"?"no-store":"public, max-age=3600"),s.end(d)}catch(i){s.statusCode=500,s.setHeader("Content-Type",te),s.end(JSON.stringify({ok:!1,error:String(i)}))}});await new Promise((o,s)=>{r.once("error",s),r.listen(e.port,e.host,()=>o())}),rr=r,Yu({pid:qe.pid,port:e.port,host:e.host,startedAt:new Date().toISOString()}),r.on("close",()=>{tr(),rr===r&&(rr=null)})}function Xu(e){let t=Ug.networkInterfaces(),n=[];for(let r of Object.values(t))if(r)for(let o of r)o.family==="IPv4"&&!o.internal&&n.push(`http://${o.address}:${e}/`);return[...new Set(n)].sort()}import Xg from"node:readline";import ed from"node:path";import he from"node:process";de();Ue();j();import Kg from"node:net";import qg from"node:fs";function Yg(){try{let e=qg.readFileSync(Qt,"utf8"),t=JSON.parse(e);return typeof t.host!="string"||typeof t.port!="number"||typeof t.token!="string"||!Number.isFinite(t.port)?null:{host:t.host,port:t.port,token:t.token}}catch{return null}}async function or(e){let t=Yg();if(!t)return"No gateway control endpoint \u2014 is `omnish run` active? (control metadata missing.)";let n={...e,token:t.token},r=`${JSON.stringify(n)}
311
+ `;return new Promise(o=>{let s=!1,i="";function a(c){s||(s=!0,o(c))}let l=Kg.connect({host:t.host,port:t.port},()=>{l.write(r)});l.setTimeout(6e5),l.on("data",c=>{i+=c.toString("utf8");let u=i.indexOf(`
312
+ `);if(u>=0){let d=i.slice(0,u).trim();try{let p=JSON.parse(d);p.ok?a(null):a(p.error||"Unknown error from gateway control.")}catch{a("Invalid response from gateway control.")}l.destroy()}}),l.on("error",c=>{a(`Control connection failed: ${String(c)}`)}),l.on("timeout",()=>{l.destroy(),a("Gateway control timed out.")}),l.on("close",()=>{!s&&i.trim()===""&&a("Gateway closed the control connection without a response.")})})}Ue();function Qg(e){let t=e.trimStart();if(/^--text=/i.test(t)){let n=t.slice(t.indexOf("=")+1).trimEnd();return n.length>0?n:null}if(/^--text(\s+|$)/i.test(t)){let n=t.replace(/^--text\s*/i,"").trim();return n.length>0?n:null}if(/^-t=/i.test(t)){let n=t.slice(t.indexOf("=")+1).trimEnd();return n.length>0?n:null}if(/^-t(\s+|$)/i.test(t)){let n=t.replace(/^-t\s*/i,"").trim();return n.length>0?n:null}}function Vg(e){let t=e.toLowerCase();if(t==="*"||t==="all")return{channel:"all"};if(t==="wa"||t==="whatsapp"||t==="wa:all"||t==="whatsapp:all")return{channel:"whatsapp-all"};if(t==="tg"||t==="telegram"||t==="tg:all"||t==="telegram:all")return{channel:"telegram-all"};if(t.startsWith("tg:")||t.startsWith("telegram:")){let n=ye(e);if(!n)return null;let r=Number(n);return Number.isFinite(r)?{channel:"telegram",chatId:r}:null}if(t.startsWith("wa:")){let n=e.slice(3).trim();if(!n)return null;let r=n.split(",").map(o=>q(o.trim())).filter(o=>!!o);return r.length===0?null:{channel:"whatsapp",e164s:r}}if(e.includes(",")){let n=e.split(",").map(r=>q(r.trim())).filter(r=>!!r);return n.length===0?null:{channel:"whatsapp",e164s:n}}if(e.startsWith("+")){let n=q(e);return n?{channel:"whatsapp",e164s:[n]}:null}return null}function Zu(e){let t=e.trim();if(!/^\/sendto(\s|$)/i.test(t))return null;let n=t.replace(/^\/sendto\s+/i,"").trim();if(!n)return null;let r=n.indexOf(" ");if(r===-1)return null;let o=n.slice(0,r).trim(),s=n.slice(r+1).trim();if(!s)return null;let i=Vg(o);if(!i)return null;let a=Qg(s);if(a===null)return null;if(a!==void 0)return{...i,mode:"text",body:a};let l=Ro(s);if(!l)return null;let{selectorPart:c,caption:u}=l;return{...i,mode:"media",selectorPart:c,caption:u}}function Ni(){return["/sendto wa|tg|* <selectors> [-- caption]","/sendto +E164 or +E164,+E164 <selectors> [-- caption]","/sendto <dest> --text <message> or --text=<msg> or -t <msg>","/sendto tg:<chat_id> <selectors> [-- caption] (compat: single Telegram id)","selectors: file1,file2 or *.mp4 or **/*.mp4 (from current session cwd); a path ./--text sends a file named --text","examples: /sendto wa ./promo.mp4 | /sendto * **/*.mp4 -- Daily clips","examples: /sendto +15551234567 --text Hello | /sendto tg:123 -t=Status OK","examples: /sendto +15550000001,+15550000002 intro.png,deck.pdf -- Launch","Also supports compat aliases: wa:all, whatsapp:all, tg:all, telegram:all.","Requires `omnish run` on this machine."].join(`
313
+ `)}j();var Tn="wa:cli:interactive",Zg={onPlainTextLlmFallback(e,t){pn($(),e,t,async n=>{n.trim()&&console.log(N(he.stdout,n))})}};function eh(e){let t=e.trim();if(!t)return null;let n=t.toLowerCase();if(n.startsWith("tg:")||n.startsWith("telegram:")){let s=ye(t);return s?`tg:${s}`:null}let r=n.startsWith("wa:")?t.slice(3):t,o=q(r);return o?`wa:${o}`:null}function th(e){let t=null,n=null;for(let r=0;r<e.length;){let o=e[r]??"";if(o==="--as"){let s=e[r+1];if(!s||s.startsWith("-"))return{opts:{senderKey:null,oneShot:null},error:"--as requires a sender (wa:+E164 or tg:<id>)."};let i=eh(s);if(!i)return{opts:{senderKey:null,oneShot:null},error:`Could not parse sender "${s}". Use +E164, wa:+E164, or tg:<user_id>.`};t=i,r+=2;continue}if(o==="-c"||o==="--command"){let s=e[r+1];if(typeof s!="string")return{opts:{senderKey:null,oneShot:null},error:`${o} requires a command string.`};n=s,r+=2;continue}return o==="--help"||o==="-h"?{opts:{senderKey:null,oneShot:null},error:"help"}:{opts:{senderKey:null,oneShot:null},error:`Unknown argument: ${o}`}}return{opts:{senderKey:t,oneShot:n},error:null}}function _i(e){let t=he.cwd(),n=[`${re(e,"omnish i")} ${g(e,"[options]")}`,J(e,"Interactive shell \u2014 same commands as WhatsApp/Telegram chat."),"",B(e,"Usage:"),` ${k(e,"omnish i [options]")}`,` ${k(e,"omnish interactive [options]")}`,"",B(e,"Options:"),...ot(e," ",[{left:"--as <sender>",right:"Sender key for cluster commands (wa:+E164 or tg:id). Default: synthetic wa:cli:interactive."},{left:"-c, --command <line>",right:"Run one line and exit (non-interactive)."},{left:"-h, --help",right:"Show this help."}],r=>g(e,r)),"",`${g(e,"Trust:")} ${k(e,"Full local access like your shell; not gated by inbox allowlist.")}`,`${g(e,"Jobs:")} ${k(e,"/bg and /jobs apply only to this REPL session (not the gateway process).")}`,`${g(e,"Files:")} ${k(e,"Use /sendto for files or --text for plain messages through the gateway; plain /send needs a chat peer.")}`,`${g(e,"Gateway:")} ${k(e,"/reload and /updates require omnish run; /sendto requires omnish run for WA/TG delivery.")}`,"",Ni(),"",`${g(e,"cwd:")} ${k(e,`session starts at ${t} (change with !cd or ${$().commandPrefix}cd).`)}`];console.log(n.join(`
314
+ `))}function td(e,t){let n=new Set,r=new Set;if(e.channel==="whatsapp-all"||e.channel==="all"){let o=new Set;for(let s of t.allowFrom){let i=q(String(s));i&&!o.has(i)&&(o.add(i),n.add(i))}}if(e.channel==="telegram-all"||e.channel==="all")for(let o of t.telegramAllowFrom){let s=ye(String(o));if(!s)continue;let i=Number(s);Number.isFinite(i)&&r.add(i)}if(e.channel==="whatsapp")for(let o of e.e164s)n.add(o);return e.channel==="telegram"&&r.add(e.chatId),{waTargets:n,tgTargets:r}}async function nh(e){let t=$();if(e.mode==="text"){let{waTargets:d,tgTargets:p}=td(e,t);if(d.size===0&&p.size===0)return{error:"No recipients matched the requested /sendto destination."};let f=[];for(let y of d){let h=await or({op:"sendText",channel:"whatsapp",e164:y,text:e.body});h&&f.push(`[wa:${y}] ${h}`)}for(let y of p){let h=await or({op:"sendText",channel:"telegram",chatId:y,text:e.body});h&&f.push(`[tg:${y}] ${h}`)}return f.length>0?{error:f.join(`
315
+ `)}:{error:null,kind:"text",recipientsSent:d.size+p.size}}let n=ee(Tn).cwd,r=await Vn(n,e.selectorPart);if(r.length===0)return{error:`No files matched: ${e.selectorPart}`};let o=await Xn(r);if(!o.ok)return{error:o.error};let s=r.map(d=>Rt(d,t.fileSendMaxBytes));for(let d of s)if("error"in d)return{error:d.error};let{waTargets:i,tgTargets:a}=td(e,t);if(i.size===0&&a.size===0)return{error:"No recipients matched the requested /sendto destination."};let l=[];for(let d of i)for(let p of s){if("error"in p)continue;let f=await or({op:"sendMedia",channel:"whatsapp",e164:d,absPath:p.absPath,caption:e.caption});f&&l.push(`[wa:${d}] ${p.displayName}: ${f}`)}for(let d of a)for(let p of s){if("error"in p)continue;let f=await or({op:"sendMedia",channel:"telegram",chatId:d,absPath:p.absPath,caption:e.caption});f&&l.push(`[tg:${d}] ${p.displayName}: ${f}`)}if(l.length>0)return{error:l.join(`
316
+ `)};let c=i.size+a.size,u=s.length;return{error:null,kind:"media",recipientsSent:c,filesSent:u,messagesSent:c*u}}async function rh(e,t,n,r,o,s,i){let a=e.trim();if(!a)return;let l=Zu(a);if(l!==null||/^\/sendto(\s|$)/i.test(a)){if(l===null){console.log(C(he.stderr,`Invalid /sendto.
317
+ `+Ni()));return}let d=await nh(l);if(d.error!==null)console.log(C(he.stderr,d.error));else if(d.kind==="text")console.log(N(he.stdout,`Sent text to ${d.recipientsSent} recipient(s).`));else{let p=`Sent ${d.filesSent} file(s) to ${d.recipientsSent} recipient(s) (${d.messagesSent} message(s)).`;console.log(N(he.stdout,p))}return}let c={peerKey:Tn,text:e},u=await Cn($(),t,n,r,o,c,s,void 0,i,!1,Zg);u!==null&&await oh(u)}async function oh(e){if(e===null)return;if(e.kind==="file"||e.kind==="files"){console.log(N(he.stdout,["This CLI session has no chat peer to attach to.","Push through the gateway instead, for example:"," /sendto wa:+15551234567 ./my.pdf"," /sendto tg:123456789 ~/photo.jpg -- optional caption","(Requires `omnish run` on this machine.)"].join(`
318
+ `)));return}let t=Pe(e.body,"whatsapp").text;t.trim()&&console.log(t)}async function nd(e){let t=th(e);if(t.error==="help"){_i(he.stdout);return}if(t.error&&t.error!==null){console.error(C(he.stderr,t.error)),console.error(g(he.stderr,"Try: omnish i --help")),he.exitCode=1;return}let{senderKey:n,oneShot:r}=t.opts,o=n??Tn;V(),wr(Tn,he.cwd());let s=new dt,i=new Map,a=new Map,l=new Map,c=new Ct(()=>$(),async(f,y)=>{he.stdout.write(y),y.endsWith(`
319
+ `)||he.stdout.write(`
320
+ `)}),u=async f=>{try{await rh(f,s,i,a,l,c,o)}catch(y){console.error(C(he.stderr,String(y)))}};if(r!==null){await u(r),c.dispose(),s.killAllRunning();return}let d=Xg.createInterface({input:he.stdin,output:he.stdout}),p=ed.basename(ee(Tn).cwd);d.setPrompt(`${p}> `),d.on("line",f=>{u(f).then(()=>{let y=ed.basename(ee(Tn).cwd);d.setPrompt(`${y}> `),d.prompt()})}),d.on("close",()=>{c.dispose(),s.killAllRunning(),he.stdout.write(`
321
+ `)}),d.prompt()}sh.setDefaultResultOrder("ipv4first");function sd(){let e=process.stdout,t=[`${me(e,"omnish run")} ${g(e,"[options]")}`,J(e,"Listen for DMs and run shell commands from allowlisted chats."),"",B(e,"Usage:"),` ${k(e,"omnish run [options]")}`,"",B(e,"Options:"),...ot(e," ",[{left:"-d, --background",right:"Start the gateway detached; log to --log-file (default: <data>/logs/gateway.log)."},{left:"--log-file <path>",right:`Append stdout/stderr when background (default: ${Ce}).`},{left:"-h, --help",right:"Show this help."}],r=>g(e,r)),"",`${k(e,"Config reload:")} ${g(e,"while the gateway runs, edit config then send /reload or /restart from an allowlisted chat (no restart needed for many keys). /updates checks npm (and optional updateInfoUrl).")}`,""],n=Ae();n?t.push(B(e,"Attached mode (platform credentials detected):"),` ${g(e,"url:")} ${k(e,n.platformUrl)} ${J(e,`[${Mn()}]`)}`,` ${g(e,"token:")} ${k(e,Ft("platformToken",n.token))} ${J(e,`[${Rn()}]`)}`,g(e," Messengers run on the platform; allowlist is set on the dashboard. Run: omnish platform probe"),""):t.push(g(e,"Platform attached mode: omnish config add platform_url <url> platform_token <token>"),g(e," then omnish platform probe && omnish run \u2014 see omnish help platform"),""),console.log(t.join(`
322
+ `))}function id(){let e=process.stdout,t=[{left:"omnish link [--force]",right:"WhatsApp: scan QR (Linked devices). --force wipes session first."},{left:"omnish link --tg <bot_token>",right:"Telegram: save token to config; gatewayMode telegram or both if WhatsApp is linked."}],n=t.map(i=>g(e,i.left)),r=Math.max(...n.map(br)),o=t.map((i,a)=>zo(" ",r,n[a],k(e,i.right))),s=[`${me(e,"omnish link")} ${g(e,"[options]")}`,J(e,"Connect WhatsApp (QR) or save a Telegram bot token."),"",B(e,"Usage:"),` ${k(e,"omnish link [--force]")}`,` ${k(e,"omnish link --tg <bot_token>")}`,"",B(e,"Modes:"),...o,` ${J(e,"Do not combine --tg with --force.")}`,"",B(e,"Options:"),...ot(e," ",[{left:"-f, --force",right:"WhatsApp only: delete saved session before pairing."},{left:"-h, --help",right:"Show this help."}],i=>g(e,i)),"",`${k(e,"Next:")} ${re(e,"omnish allow tg:<your_user_id>")} ${g(e,"then")} ${re(e,"omnish run")}`,`${g(e,"Config:")} ${k(e,L)}`,""];Ae()&&s.push(B(e,"Platform attached mode:"),g(e," omnish link on this host is for standalone only. Link WhatsApp on the platform dashboard or:"),` ${k(e,"omnish platform import-whatsapp")} ${g(e,"(after a local omnish link, with omnish run stopped)")}`,g(e," Telegram: set bot token on the platform dashboard, not --tg here."),""),console.log(s.join(`
323
+ `))}function ah(e){let t=!1,n=null;for(let r=0;r<e.length;r++){let o=e[r]??"";if(o==="--help"||o==="-h")return{kind:"help"};if(o==="--force"||o==="-f"){t=!0;continue}if(o==="--tg"||o==="--telegram"){let s=e[r+1];if(!s||s.startsWith("-"))return{kind:"error",message:"[omnish] --tg requires a bot token (from @BotFather)."};n=s,r++;continue}if(o.startsWith("--tg=")||o.startsWith("--telegram=")){let s=o.indexOf("="),i=o.slice(s+1).trim();if(!i)return{kind:"error",message:"[omnish] --tg= requires a non-empty bot token."};n=i;continue}return{kind:"error",message:`[omnish] unknown link argument: ${o}
324
+ Try: omnish link --help`}}return n!==null?t?{kind:"error",message:"[omnish] --force applies to WhatsApp only; do not combine with --tg."}:{kind:"tg",token:n}:{kind:"wa",force:t}}function Ui(){let e=process.stdout,t=`${me(e,"omnish")} ${g(e,`v${Be()}`)}`,n=[{left:"link [--force] [--tg <token>]",right:"WhatsApp (QR) or Telegram bot token \u2014 omnish link --help"},{left:"run [options]",right:"Listen for DMs (WhatsApp and/or Telegram \u2014 see gatewayMode in config)"},{left:"stop",right:`Stop background gateway (pidfile: ${ne})`},{left:"service <subcommand>",right:"Boot install hints, logs, systemd/LaunchAgent \u2014 omnish service help"},{left:"logout",right:"Delete saved WhatsApp session"},{left:"allow +<E164> | tg:<id>",right:"Add allowlist entry"},{left:"deny +<E164> | tg:<id>",right:"Remove allowlist entry"},{left:"status [--check-updates]",right:"Channels, identity, allowlists, jobs, security, cluster (if enabled)"},{left:"commands",right:"Chat commands for allowlisted users (same as /help)"},{left:"security [--json]",right:"Configuration security report (JSON for scripts)"},{left:"cluster [status | use <sender> <label-or-id>]",right:"Per-sender machine bindings"},{left:"i | interactive [options]",right:"Local REPL (chat commands; /sendto needs omnish run)"},{left:"ui [options]",right:"Browser setup UI on LAN (token-gated) \u2014 omnish ui --help"},{left:"config <add|show|edit|delete>",right:"Manage config.json (platform URL/token, tunnel, gateway) \u2014 omnish config help"},{left:"platform <subcommand>",right:"Attached mode: configure, probe, import WA \u2014 omnish help platform"},{left:"tunnel <subcommand>",right:"Expose local HTTP/TCP via omnish relay \u2014 omnish tunnel help"}],r=[{left:"-v, --version",right:"Print version and exit."},{left:"-h, --help",right:"Show this help (same as omnish help)."}],o=[t,J(e,"Allowlisted inbox \u2192 your real shell. No AI."),"",B(e,"Usage:"),` ${k(e,"omnish [options] <command> [args...]")}`,"",B(e,"Options:"),...ot(e," ",r,s=>g(e,s)),"",B(e,"Commands:"),...ot(e," ",n,s=>g(e,s)),"",`${g(e,"Config:")} ${k(e,`${L} \u2014 gatewayMode: "whatsapp" | "telegram" | "both"`)}`,`${g(e,"Platform:")} ${k(e,"platform_url + platform_token \u2192 attached omnish run (omnish help platform)")}`,`${g(e,"Env:")} ${k(e,"OMNISH_VERBOSE=1 (Baileys; legacy WHATSVERBOSE=1 still works), TELEGRAM_BOT_TOKEN (optional)")}`,`${g(e,"Data:")} ${k(e,"~/.omnish by default; ~/.whatslive reused if it already exists. OMNISH_HOME overrides.")}`,`${g(e,"See also:")} ${k(e,"https://omnish.dev")}`,""];console.log(o.join(`
325
+ `))}function lh(e){let t=(e??"").trim().toLowerCase(),n=process.stdout;if(!t){Ui();return}switch(t){case"link":id();return;case"run":sd();return;case"service":ld();return;case"i":case"interactive":_i(n);return;case"ui":cd();return;case"config":Po(n);return;case"platform":_o(n);return;default:console.error(C(process.stderr,`No detailed help for "${e}". Try: omnish help`)),process.exitCode=1}}function ch(e){let t=!1,n="",r=!1;for(let s=0;s<e.length;s++){let i=e[s]??"";if(i==="-d"||i==="--background")t=!0;else if(i==="--log-file"||i==="--log"){let a=e[++s];a||(console.error(C(process.stderr,"--log-file requires a path.")),process.exit(1)),n=a}else if(i==="--help"||i==="-h")r=!0;else{let a=process.stderr;console.error(C(a,`unknown run option: ${i}`)),console.error(C(a,"Try: omnish run --help")),process.exit(1)}}let o=n.trim()!==""?Fi.isAbsolute(n)?n:Fi.resolve(process.cwd(),n):Ce;return{background:t,logFile:o,help:r}}function uh(e){let t=io(e);t.ok||(console.error(C(process.stderr,t.message)),process.exit(1));let n=process.stdout;console.log(`${N(n,`gateway started in background (pid ${t.pid}).`)} ${g(n,`Log: ${e}`)}`)}function dh(){let e=ao();switch(e.outcome){case"no_pidfile":console.error(C(process.stderr,`no pidfile at ${ne} \u2014 is a background gateway running?`)),process.exitCode=1;return;case"invalid_pidfile":console.error(C(process.stderr,"invalid pidfile.")),process.exitCode=1;return;case"stale_cleaned":console.log(N(process.stdout,`process ${e.pid} is not running; removing stale pidfile.`));return;case"sent_signal":console.log(N(process.stdout,`sent SIGTERM to gateway (pid ${e.pid}).`));return;case"taskkill_ok":console.log(N(process.stdout,`stopped gateway (pid ${e.pid}) using taskkill.`));return;case"failed":console.error(C(process.stderr,e.message)),process.exitCode=1;return}}function rd(){if(process.env.OMNISH_BACKGROUND_GATEWAY==="1")try{Pt.readFileSync(ne,"utf8").trim()===String(process.pid)&&Pt.unlinkSync(ne)}catch{}}function ph(e){return e.length<=8?"(set)":`${e.slice(0,4)}\u2026${e.slice(-4)}`}function mh(){if(!Pt.existsSync(ne))return"gateway process: not running (no pid file)";let e=Pt.readFileSync(ne,"utf8").trim(),t=Number(e);if(!Number.isFinite(t)||t<=0)return"gateway process: invalid pid file";try{return process.kill(t,0),`gateway process: running (pid ${t})`}catch{return`gateway process: not running (stale pid ${t} in pid file)`}}var ad=120;function ld(){let e=process.stdout,t=[{left:"help",right:"This help."},{left:"instructions",right:"Copy-paste install steps for this machine."},{left:"status",right:"Data dir, pidfile, Node + entry script."},{left:"logs [n]",right:`Tail default gateway log (default 80 lines, max ${ad}).`},{left:"install",right:"Write user systemd / LaunchAgent unit (needs serviceInstallFromChat=true)."},{left:"uninstall",right:"Remove that unit (same gate as install)."}],n=[`${me(e,"omnish service")} ${g(e,"<subcommand>")}`,J(e,"Boot integration, crash restart policy, and logs (same ideas as /service in chat)."),"",B(e,"Usage:"),` ${k(e,"omnish service <subcommand>")}`,"",B(e,"Subcommands:"),...ot(e," ",t,r=>g(e,r)),"",B(e,"Restart and reload:"),` ${k(e,"Process")} ${g(e,"\u2014 Linux user unit: Restart=on-failure, RestartSec=5. macOS: KeepAlive. Full restart loads config from disk.")}`,` ${k(e,"Config live")} ${g(e,"\u2014 allowlisted chat while gateway runs: /reload or /restart")}`,"",`${g(e,"Docs:")} ${k(e,"docs/guides/background-and-boot.md")} ${J(e,"\xB7")} https://omnish.dev`,""];console.log(n.join(`
326
+ `))}function fh(e){let t=process.stdout,n=process.stderr,r=(e[0]??"help").toLowerCase();if(r==="help"||r==="--help"||r==="-h"){ld();return}if(r==="instructions"){let o=Ut();if(o.error){console.error(C(n,o.error)),process.exitCode=1;return}console.log(Tr(o));return}if(r==="status"){let o=Ut(),s=(()=>{try{return Pt.existsSync(ne)?`gateway.pid: ${Pt.readFileSync(ne,"utf8").trim()}`:"gateway.pid: (missing)"}catch(d){return`gateway.pid: (read error: ${String(d)})`}})(),i=process.env.OMNISH_BACKGROUND_GATEWAY==="1"?"This process: background gateway (OMNISH_BACKGROUND_GATEWAY=1).":"This process: CLI (not the gateway \u2014 run omnish service status on the host where the gateway runs for live pid info).",a=typeof process.env.OMNISH_HOME=="string"&&process.env.OMNISH_HOME.trim()?`OMNISH_HOME env: ${process.env.OMNISH_HOME.trim()}`:"OMNISH_HOME env: (not set \u2014 using default data dir)",l=o.error?o.error:`Node: ${o.nodePath}
327
+ Script: ${o.scriptPath}`,u=$().serviceInstallFromChat?"Install from CLI/chat: enabled (omnish service install / /service install).":"Install from CLI/chat: off \u2014 set serviceInstallFromChat true in config (same trust as shell).";console.log(me(t,"omnish service status")),console.log(""),console.log(`${g(t,"platform:")} ${k(t,process.platform)}`),console.log(`${g(t,"session:")} ${k(t,i)}`),console.log(`${g(t,"env:")} ${k(t,a)}`),console.log(`${g(t,"data dir:")} ${k(t,U)}`),console.log(`${g(t,"pidfile:")} ${k(t,s)}`),console.log(`${g(t,"default log:")} ${k(t,Ce)}`),console.log(""),console.log(l),console.log(""),console.log(k(t,u));return}if(r==="logs"){let o=e.length>=2?Number.parseInt(e[1],10):80,s=Number.isFinite(o)&&o>0?Math.min(o,ad):80,i=Pr(Ce,s);console.log(`${g(t,"file:")} ${k(t,Ce)}`),console.log(`${g(t,"lines:")} ${k(t,String(s))}`),console.log(""),console.log(i);return}if(r==="install"){if(!$().serviceInstallFromChat){console.error(C(n,"Install is disabled. Set serviceInstallFromChat to true in config (same trust as shell), then run again.")),process.exitCode=1;return}let s=Er();s.ok?console.log(N(t,s.detail)):(console.error(C(n,s.detail)),process.exitCode=1);return}if(r==="uninstall"){if(!$().serviceInstallFromChat){console.error(C(n,"Uninstall is disabled. Set serviceInstallFromChat to true in config or remove the unit file on the host manually.")),process.exitCode=1;return}let s=Ir();s.ok?console.log(N(t,s.detail)):(console.error(C(n,s.detail)),process.exitCode=1);return}console.error(C(n,`Unknown subcommand "${r}". Try: omnish service help`)),process.exitCode=1}function gh(e){let t=e.trim();if(!t)return null;let n=t.toLowerCase();if(n.startsWith("tg:")||n.startsWith("telegram:")){let s=ye(t);return s?`tg:${s}`:null}let r=n.startsWith("wa:")?t.slice(3):t,o=q(r);return o?`wa:${o}`:null}async function hh(){let e=null,t=Io();t.ok||(console.error(C(process.stderr,t.message)),process.exit(1));let n=Ae();if(n){await ku(n);return}let r=$(),o=r.gatewayMode,s=o==="whatsapp"||o==="both",i=o==="telegram"||o==="both",a=pe(r),l=lt(r),c=process.stderr,u=aa(l,"warn");if(u.length>0&&(console.warn(`${N(c,`Security (${u.length} finding(s)):`)}
328
+ `),console.warn(Vo(u,c))),process.env.OMNISH_BACKGROUND_GATEWAY==="1")try{Pt.writeFileSync(ne,`${process.pid}
329
+ `,{mode:384})}catch(b){I.warn({err:String(b)},"could not write gateway pidfile")}let d=(b,S)=>{let A=$();if(!A.clusterEnabled)return b;let D=De(),K=(A.clusterLabel??"").trim()||od.hostname(),Q=null;if(S.startsWith("tg:"))Q=S;else if(S){let $e=q(S);$e&&(Q=`wa:${$e}`)}let xe=Q?ct(A,Q):null;return Ya(b,{nodeId:D,label:K,role:A.clusterRole,activeNodeId:xe?.nodeId??""})},p=new dt,f=new Map,y=new Map,h=new Map,w=null,v={stop:null,sendText:null,sendMedia:null},x=null,T=!1,M=async(b,S)=>{if(b.startsWith("wa:")){let A=b.slice(3);w&&await w.sendText(A,S)}else if(b.startsWith("tg:")){let A=Number(b.slice(3));v.sendText&&Number.isFinite(A)&&await v.sendText(A,m(S))}},E={onPlainTextLlmFallback(b,S){pn($(),b,S,A=>M(b,A))},sendToPeer:M},R=async(b,S)=>{if(b.startsWith("wa:")){let A=b.slice(3);w&&await w.sendMedia(A,S)}else if(b.startsWith("tg:")){let A=Number(b.slice(3));v.sendMedia&&Number.isFinite(A)&&await v.sendMedia(A,S)}},_=()=>new Ct(()=>$(),M),H=_();x=H;let oe,F={async reload(){try{I.info("gateway reload requested from chat"),await v.stop?.().catch(()=>{}),v.stop=null,v.sendText=null,v.sendMedia=null;let b=$(),S=b.gatewayMode==="telegram"||b.gatewayMode==="both",A=pe(b);if(S&&A){let Q=await qs(A,()=>$(),oe,{decorate:d});v.sendText=Q.sendText,v.sendMedia=Q.sendMedia,v.stop=Q.stop}let D=["Reload complete.",`gatewayMode: ${b.gatewayMode}`,S&&A?"Telegram bot is running with the current token.":"Telegram bot is stopped (enable telegram/both + token if you want it).","Allowlists, shell, app session limits, and timeouts are read from disk on each command."].join(`
330
+ `),K=$o(Yn());return{ok:!0,summary:K?`${D}
281
331
 
282
- Updates (last check): ${_}`:I}}catch(T){return{ok:!1,error:String(T)}}}};if(re=async(T,w)=>{let S=R(),I=ms(S.telegramAllowFrom),_=T.peerKey.startsWith("tg:")?T.peerKey.slice(3):"";if(!_||!I.has(_)){E.warn({denied:T.peerKey,uid:_},"telegram denied");return}try{let B=T.peerKey;if(!Oo(S,T.text,B))return;if(T.mediaError&&await w({kind:"text",body:p(T.mediaError)}),T.mediaSavedPath&&await w({kind:"text",body:p(`Saved: ${T.mediaSavedPath}`)}),T.text.trim()){let V=await wt(S,d,m,y,g,T,U,Se,B,!1,A);V!==null&&await w(V)}}catch(B){E.error({err:String(B)},"telegram handler error"),await w({kind:"text",body:p(`Error: ${String(B)}`)}).catch(()=>{})}},s){let T=await Uo(i,()=>R(),re,{decorate:c});h.sendText=T.sendText,h.sendMedia=T.sendMedia,h.stop=T.stop}dl({getCfg:()=>R(),getWaOutbound:()=>f,getTgSendMedia:()=>h.sendMedia,getTgSendText:()=>h.sendText}),e=fa({getRunningVersion:Ue,getConfig:R,log:E});let we=Da({getConfig:R,sendToPeer:$,sendMediaToPeer:M}),Ke=!s,Xe=()=>{k=!0,we(),e?.(),e=null,El(),vr(),h.stop?.().catch(()=>{}),x?.dispose(),d.killAllRunning(),console.error(`
283
- ${O(process.stderr,"shutting down\u2026")}`),process.exit(0)};if(process.on("SIGINT",Xe),process.on("SIGTERM",Xe),o)for(;!k;){let T=!1,w;try{w=await hr({printQr:!1,verbose:lr()}),await kt(yr(w),3e5,"Gateway: timed out waiting for WhatsApp connection (5 min).")}catch(I){console.error(O(process.stderr,`connect failed: ${String(I)}`)),await new Promise(_=>setTimeout(_,5e3));continue}f=sl(w,{decorate:c});let S=Za(w,async I=>{let _=R(),B=ps(_.allowFrom),V=I.fromE164||G(I.fromJid)||"";if(!V||!B.has(V)){E.warn({denied:I.fromJid,phone:V},"denied");return}try{let me=Aa(I),Ee=`wa:${V}`;if(!Oo(_,me.text,Ee))return;if(I.mediaError&&await f.sendText(I.fromJid,I.mediaError),I.mediaSavedPath&&await f.sendText(I.fromJid,`Saved: ${I.mediaSavedPath}`),I.text.trim()){let W=await wt(_,d,m,y,g,me,U,Se,Ee,!1,A);if(W!==null)if(W.kind==="file")await f.sendMedia(I.fromJid,W.spec);else if(W.kind==="files")for(let Ce of W.specs)await f.sendMedia(I.fromJid,Ce);else await f.sendText(I.fromJid,De(W.body,"whatsapp").text)}}catch(me){E.error({err:String(me)},"handler error"),await f.sendText(I.fromJid,De(p(`Error: ${String(me)}`),"whatsapp").text).catch(()=>{})}});if(await new Promise(I=>{let _=B=>{B.connection==="close"&&(Ho(B.lastDisconnect)===bt.loggedOut&&(T=!0),w.ev.off("connection.update",_),I())};w.ev.on("connection.update",_)}),S(),Ke&&(U.dispose(),U=L(),x=U),Ut(w),f=null,T&&(console.error(O(process.stderr,"session logged out. Run `omnish link` again.")),we(),e?.(),e=null,El(),vr(),h.stop?.().catch(()=>{}),process.exit(1)),k)break;await new Promise(I=>setTimeout(I,3e3))}else for(;!k;)await new Promise(T=>setTimeout(T,500));we(),e?.(),vr(),h.stop?.().catch(()=>{}),U.dispose()}function zp(e){let t=!1,n="0.0.0.0",r=3789,o;for(let s=0;s<e.length;s++){let i=e[s];if(i==="--help"||i==="-h"){t=!0;continue}if(i==="--host"||i==="-H"){let l=e[++s];l||(console.error(O(process.stderr,"--host requires an address (e.g. 127.0.0.1).")),process.exit(1)),n=l;continue}if(i==="--port"||i==="-p"){let l=e[++s],u=Number.parseInt(l??"",10);Number.isFinite(u)||(console.error(O(process.stderr,"--port requires a number.")),process.exit(1)),r=u;continue}if(i==="--token"||i==="-t"){let l=e[++s];(!l||l.startsWith("-"))&&(console.error(O(process.stderr,"--token requires a secret string.")),process.exit(1)),o=l;continue}let a=process.stderr;console.error(O(a,`unknown ui argument: ${i}`)),console.error(O(a,"Try: omnish ui --help")),process.exit(1)}return{help:t,host:n,port:r,token:o}}function Nl(){let e=process.stdout,t=[`${ie(e,"omnish ui")} ${b(e,"[options]")}`,se(e,"Serve the browser setup panel on your LAN (token-gated). Same trust as editing config on disk."),"",X(e,"Usage:"),` ${v(e,"omnish ui [options]")}`,"",X(e,"Options:"),...We(e," ",[{left:"--host <addr>",right:"Bind address (default 0.0.0.0 \u2014 reachable on LAN). Use 127.0.0.1 for loopback only."},{left:"--port <n>",right:"TCP port (default 3789)."},{left:"--token <secret>",right:`Set or rotate setup token (saved to ${_e}).`},{left:"-h, --help",right:"This help."}],n=>b(e,n)),"",`${b(e,"Warning:")} ${xe(e,"Listening on all interfaces exposes a remote control panel on your network \u2014 use a trusted LAN.")}`,""];console.log(t.join(`
284
- `))}async function qp(){let[,,e,...t]=process.argv;if(e==="--version"||e==="-v"||e==="-V"){let n=process.stdout;console.log(`${ie(n,"omnish")} ${b(n,Ue())}`);return}if(e==="--help"||e==="-h"){rs();return}if(e==="help"){_p(t[0]);return}switch(z(),e){case"link":{let n=Np(t);if(n.kind==="help"){Ol();return}if(n.kind==="error"){let r=process.stderr,o=n.message.replace(/^\[omnish\]\s*/,"");console.error(O(r,o)),process.exitCode=1;return}if(n.kind==="tg"){let r=n.token.trim(),o=process.stderr,s=process.stdout;if(!Ze(r)){console.error(O(o,"That does not look like a Telegram bot token (expect digits:secret from @BotFather).")),process.exitCode=1;return}vt(r);let i=Le()?"both":"telegram";Pn(i),console.log([`${Z(s,"Telegram")} ${v(s,"bot token saved to")} ${b(s,P)}`,`${b(s,"gatewayMode:")} ${Z(s,i)}`,"",v(s,"Next:"),` ${b(s,"1.")} ${v(s,"Find your numeric user id (e.g. t.me/userinfobot), then:")} ${Z(s,"omnish allow tg:<id>")}`,` ${b(s,"2.")} ${Z(s,"omnish run")}`,""].join(`
285
- `));return}await ul({verbose:lr(),force:n.force});return}case"run":{let n=Bp(t);if(n.help){Ll();return}if(n.background){Hp(n.logFile);return}await Jp();return}case"stop":Dp();return;case"logout":{try{ct.rmSync(J,{recursive:!0,force:!0}),console.log(ae(process.stdout,"Session removed. Run `omnish link` to pair again."))}catch(n){console.error(O(process.stderr,`logout failed: ${String(n)}`)),process.exitCode=1}return}case"allow":{let n=t[0],r=process.stdout,o=process.stderr;if(!n){console.error(O(o,"Usage: omnish allow +<E164> or omnish allow tg:<user_id>")),process.exitCode=1;return}try{let s=An(n);console.log(`${b(r,"allowFrom:")} ${v(r,s.allowFrom.join(", ")||"(empty)")}`),console.log(`${b(r,"telegramAllowFrom:")} ${v(r,s.telegramAllowFrom.join(", ")||"(empty)")}`)}catch(s){console.error(O(o,String(s).replace(/^\[omnish\]\s*/,""))),process.exitCode=1}return}case"deny":{let n=t[0],r=process.stdout,o=process.stderr;if(!n){console.error(O(o,"Usage: omnish deny +<E164> or omnish deny tg:<user_id>")),process.exitCode=1;return}try{let s=Ln(n);console.log(`${b(r,"allowFrom:")} ${v(r,s.allowFrom.join(", ")||"(empty)")}`),console.log(`${b(r,"telegramAllowFrom:")} ${v(r,s.telegramAllowFrom.join(", ")||"(empty)")}`)}catch(s){console.error(O(o,String(s).replace(/^\[omnish\]\s*/,""))),process.exitCode=1}return}case"i":case"interactive":{await Il(t);return}case"status":{let n=process.stdout,r=R(),o=t.includes("--check-updates"),s=new ht().list(),i=s.filter(g=>g.status==="running").length,a=oe(r),l=ot(r),u=r.gatewayMode==="whatsapp"||r.gatewayMode==="both",c=r.gatewayMode==="telegram"||r.gatewayMode==="both",d=Le(),m=d?al(J):null,y=[];if(y.push(`${ie(n,"omnish")} ${b(n,Ue())}`,`${b(n,"gatewayMode:")} ${v(n,r.gatewayMode)}`,`${b(n,"data dir:")} ${v(n,ns.dirname(J))}`,"",`${b(n,"gateway process:")} ${v(n,Up().replace(/^gateway process: /,""))}`,"",dt(n),Z(n,"whatsapp"),` ${b(n,"in use:")} ${u?v(n,"yes"):xe(n,"no (gatewayMode is telegram-only)")}`),u){let g=d?v(n,`linked (${J})`):xe(n,"missing \u2014 run omnish link");y.push(` ${b(n,"session:")} ${g}`),d&&m&&y.push(` ${b(n,"linked as:")} ${v(n,m)}`),d&&!m&&y.push(` ${b(n,"linked as:")} ${se(n,"(not in creds yet \u2014 try again after omnish link completes)")}`)}if(y.push(` ${X(n,"Allowed")}`),r.allowFrom.length===0)y.push(` ${se(n,"(none)")}`);else for(let g of r.allowFrom)y.push(` ${b(n,"whatsapp:")} ${v(n,g)}`);if(y.push("",dt(n),Z(n,"telegram")),y.push(` ${b(n,"in use:")} ${c?v(n,"yes"):xe(n,"no (gatewayMode is whatsapp-only)")}`),c){let g=a?v(n,jp(a)):xe(n,"(none) \u2014 omnish link --tg <token> or TELEGRAM_BOT_TOKEN");y.push(` ${b(n,"bot token:")} ${g}`)}if(y.push(` ${X(n,"Allowed")}`),r.telegramAllowFrom.length===0)y.push(` ${se(n,"(none)")}`);else for(let g of r.telegramAllowFrom)y.push(` ${b(n,"telegram:")} ${v(n,g)}`);if(y.push("",dt(n),`${Z(n,"jobs")} ${b(n,`(recent): ${s.length} total, ${i} running`)}`,Os(l,n)),console.log(y.join(`
286
- `)),o){let g=await dn(Ue(),r),f=Xt(g);console.log(""),console.log(dt(n)),console.log(De(f,"whatsapp").text)}if(r.clusterEnabled){let g=Me(),f=ee(),h=Object.keys(f.senderBindings).length,x=Object.keys(r.clusterSenderBindings??{}).length;console.log(""),console.log(dt(n)),console.log(Z(n,"cluster")),console.log(` ${b(n,"\xB7")} ${v(n,`enabled \xB7 label ${r.clusterLabel||Al.hostname()} \xB7 bindings ${h} chat / ${x} default`)}`),console.log(` ${b(n,"node:")} ${v(n,`${g.slice(0,8)}\u2026`)}`)}return}case"commands":{let n=process.stdout,r=R();console.log(Ms(Rt(r),n)),console.log(""),console.log(dt(n)),console.log(v(n,"Keys editable from chat via /config set (same trust as shell):")),console.log(b(n,Ot.join(", "))),console.log(`${b(n,"See also:")} ${v(n,P)}`);return}case"cluster":{let n=process.stdout,r=process.stderr,o=(t[0]??"status").toLowerCase();if(o==="status"){let s=R(),i=Me();if(console.log(`${b(n,"clusterEnabled:")} ${v(n,String(s.clusterEnabled))}`),console.log(`${b(n,"clusterLabel:")} ${v(n,s.clusterLabel||"(hostname)")}`),console.log(`${b(n,"clusterRole:")} ${v(n,`${s.clusterRole} (informational; no longer used to gate traffic)`)}`),console.log(`${b(n,"node id:")} ${Z(n,i)}`),s.clusterEnabled){let a=ee(),l=vo(s,null);console.log(""),console.log(v(n,l.wa.replace(/\*([^*]+)\*/g,"$1").replace(/`([^`]+)`/g,"$1"))),console.log(""),console.log(xo(a,s,null));let u=Object.keys(a.senderBindings).length,c=Object.entries(s.clusterSenderBindings??{});if(u>0){console.log(""),console.log(Z(n,"Chat bindings (cluster-local.json)"));for(let[d,m]of Object.entries(a.senderBindings))console.log(` ${b(n,d)} ${se(n,"->")} ${v(n,`${m.nodeId} (${m.source}, since ${m.sinceIso})`)}`)}if(c.length>0){console.log(""),console.log(Z(n,"Config defaults (clusterSenderBindings)"));for(let[d,m]of c)console.log(` ${b(n,d)} ${se(n,"->")} ${v(n,m)}`)}}else console.log(""),console.log(xe(n,"(cluster disabled \u2014 /config set clusterEnabled true to enable, then /c use <label-or-id> from each sender)"));return}if(o==="use"||o==="bind"){let s=t[1],i=t.slice(2).join(" ").trim();if(!s||!i){console.error(O(r,"Usage: omnish cluster use <senderE164|tg:id> <label-or-id>")),process.exitCode=1;return}let a=Gp(s);if(!a){console.error(O(r,`Could not parse sender "${s}". Use +E164 (WhatsApp) or tg:<user_id> (Telegram).`)),process.exitCode=1;return}let{state:l,resolved:u}=Zi(a,i);if(!u.ok){if(u.reason==="ambiguous-label"){let d=(u.matches??[]).map(m=>`${m.nodeId}(${m.label})`).join(", ");console.error(O(r,`Label "${i}" matches multiple machines: ${d}. Use the 8-character id.`))}else console.error(O(r,`No machine matches "${i}". Run /c status from the chat first to populate the roster, or pass an 8-character node id.`));process.exitCode=1;return}console.log(`${Z(n,"cluster:")} ${v(n,`${a} -> ${u.peer.nodeId} (${u.peer.label}).`)}`);let c=R();console.log(""),console.log(xo(l,c,a));return}if(o==="here"){console.error(O(r,"omnish cluster here is no longer available. Use: omnish cluster use <senderE164|tg:id> <label-or-id>")),console.error(O(r,"Or send /c here from the controller's chat on the machine you want to bind.")),process.exitCode=1;return}console.error(O(r,"Usage: omnish cluster [status | use <sender> <label-or-id>]")),process.exitCode=1;return}case"security":{let n=R(),r=ot(n),o=t.includes("--json");console.log(o?As(r):_r(r,process.stdout)),Gn(r)&&(process.exitCode=1);return}case"service":{Wp(t);return}case"ui":{let n=zp(t);if(n.help){Nl();return}if(!Number.isFinite(n.port)||n.port<1||n.port>65535){console.error(O(process.stderr,"port must be between 1 and 65535.")),process.exitCode=1;return}let r=hl(n.token);await $l({host:n.host,port:n.port,meta:r});let o=process.stdout,s=Cl(n.port);console.log(""),console.log(`${ie(o,"ui")} ${b(o,"listening")}`),console.log(`${b(o,"bind:")} ${v(o,`${n.host}:${n.port}`)}`),console.log(`${b(o,"setup token:")} ${v(o,r.token)}`),console.log(`${b(o,"token file:")} ${se(o,_e)}`),console.log(""),console.log(xe(o,"Anyone on your network who can reach this port needs the token \u2014 do not expose to untrusted Wi\u2011Fi.")),console.log(""),console.log(`${b(o,"Open:")}`),console.log(` ${v(o,`http://127.0.0.1:${n.port}/`)}`);for(let i of s)console.log(` ${v(o,i)}`);console.log(""),console.log(`${b(o,"Quick link (same Wi\u2011Fi):")} ${v(o,`http://127.0.0.1:${n.port}/?token=${encodeURIComponent(r.token)}`)}`),console.log("");return}default:rs(),e&&(process.exitCode=1)}}qp().catch(e=>{console.error(O(process.stderr,String(e))),process.exit(1)});
332
+ Updates (last check): ${K}`:D}}catch(b){return{ok:!1,error:String(b)}}}};if(oe=async(b,S)=>{let A=$();await er(A,p,f,y,h,b,H,F,b.peerKey,E,wi({sendTg:S},{surface:"telegram"}),{surface:"telegram"})},i){let b=await qs(a,()=>$(),oe,{decorate:d});v.sendText=b.sendText,v.sendMedia=b.sendMedia,v.stop=b.stop}lo({getCfg:()=>$(),getWaOutbound:()=>w,getTgSendMedia:()=>v.sendMedia,getTgSendText:()=>v.sendText});let le=null;{let b=$();if(b.webhookEnabled){let S=b.webhookToken||ih.randomBytes(32).toString("hex");b.webhookToken||Y({webhookToken:S}),le=co({port:b.webhookPort,host:b.webhookHost,token:S},{sendToPeer:M,getDefaultPeerKey:()=>{let D=$();return D.allowFrom.length>0?`wa:${Nt(D.allowFrom[0])}`:D.telegramAllowFrom.length>0?`tg:${D.telegramAllowFrom[0]}`:null}}).stop}}e=Co({getRunningVersion:Be,getConfig:$,log:I});let Z=Qr({getConfig:$,sendToPeer:M,sendMediaToPeer:R}),ve=!i,_e=()=>{T=!0,Z(),e?.(),e=null,le?.(),rd(),kn(),v.stop?.().catch(()=>{}),x?.dispose(),p.killAllRunning(),zt().stopAll().catch(()=>{}),console.error(`
333
+ ${C(process.stderr,"shutting down\u2026")}`),process.exit(0)};if(process.on("SIGINT",_e),process.on("SIGTERM",_e),s)for(;!T;){let b=!1,S;try{S=await to({printQr:!1,verbose:rn()}),await Jt(no(S),3e5,"Gateway: timed out waiting for WhatsApp connection (5 min).")}catch(D){console.error(C(process.stderr,`connect failed: ${String(D)}`)),await new Promise(K=>setTimeout(K,5e3));continue}w=mc(S,{decorate:d});let A=ac(S,async D=>{let K=$(),Q=D.fromE164||q(D.fromJid)||"",xe=ul(D),$e=`wa:${Q}`;await er(K,p,f,y,h,xe,H,F,$e,E,wi({sendWaText:(Ot,Lt)=>w.sendText(Ot,Lt),sendWaMedia:(Ot,Lt)=>w.sendMedia(Ot,Lt)},{surface:"whatsapp",waJid:D.fromJid}),{surface:"whatsapp"})});if(await new Promise(D=>{let K=Q=>{Q.connection==="close"&&(Js(Q.lastDisconnect)===Gt.loggedOut&&(b=!0),S.ev.off("connection.update",K),D())};S.ev.on("connection.update",K)}),A(),ve&&(H.dispose(),H=_(),x=H),hn(S),w=null,b&&(console.error(C(process.stderr,"session logged out. Run `omnish link` again.")),Z(),e?.(),e=null,rd(),kn(),v.stop?.().catch(()=>{}),process.exit(1)),T)break;await new Promise(D=>setTimeout(D,3e3))}else for(;!T;)await new Promise(b=>setTimeout(b,500));Z(),e?.(),kn(),v.stop?.().catch(()=>{}),H.dispose()}function yh(e){let t=!1,n="0.0.0.0",r=3789,o;for(let s=0;s<e.length;s++){let i=e[s];if(i==="--help"||i==="-h"){t=!0;continue}if(i==="--host"||i==="-H"){let l=e[++s];l||(console.error(C(process.stderr,"--host requires an address (e.g. 127.0.0.1).")),process.exit(1)),n=l;continue}if(i==="--port"||i==="-p"){let l=e[++s],c=Number.parseInt(l??"",10);Number.isFinite(c)||(console.error(C(process.stderr,"--port requires a number.")),process.exit(1)),r=c;continue}if(i==="--token"||i==="-t"){let l=e[++s];(!l||l.startsWith("-"))&&(console.error(C(process.stderr,"--token requires a secret string.")),process.exit(1)),o=l;continue}let a=process.stderr;console.error(C(a,`unknown ui argument: ${i}`)),console.error(C(a,"Try: omnish ui --help")),process.exit(1)}return{help:t,host:n,port:r,token:o}}function cd(){let e=process.stdout,t=[`${me(e,"omnish ui")} ${g(e,"[options]")}`,J(e,"Serve the browser setup panel on your LAN (token-gated). Same trust as editing config on disk."),"",B(e,"Usage:"),` ${k(e,"omnish ui [options]")}`,"",B(e,"Options:"),...ot(e," ",[{left:"--host <addr>",right:"Bind address (default 0.0.0.0 \u2014 reachable on LAN). Use 127.0.0.1 for loopback only."},{left:"--port <n>",right:"TCP port (default 3789)."},{left:"--token <secret>",right:`Set or rotate setup token (saved to ${Ve}).`},{left:"-h, --help",right:"This help."}],n=>g(e,n)),"",`${g(e,"Warning:")} ${ae(e,"Listening on all interfaces exposes a remote control panel on your network \u2014 use a trusted LAN.")}`,""];console.log(t.join(`
334
+ `))}async function wh(){let[,,e,...t]=process.argv;if(e==="--version"||e==="-v"||e==="-V"){let n=process.stdout;console.log(`${me(n,"omnish")} ${g(n,Be())}`);return}if(e==="--help"||e==="-h"){Ui();return}if(e==="help"){lh(t[0]);return}switch(V(),e){case"link":{let n=ah(t);if(n.kind==="help"){id();return}if(n.kind==="error"){let r=process.stderr,o=n.message.replace(/^\[omnish\]\s*/,"");console.error(C(r,o)),process.exitCode=1;return}if(n.kind==="tg"){let r=n.token.trim(),o=process.stderr,s=process.stdout;if(!Ze(r)){console.error(C(o,"That does not look like a Telegram bot token (expect digits:secret from @BotFather).")),process.exitCode=1;return}ft(r);let i=Ge()?"both":"telegram";hr(i),console.log([`${re(s,"Telegram")} ${k(s,"bot token saved to")} ${g(s,L)}`,`${g(s,"gatewayMode:")} ${re(s,i)}`,"",k(s,"Next:"),` ${g(s,"1.")} ${k(s,"Find your numeric user id (e.g. t.me/userinfobot), then:")} ${re(s,"omnish allow tg:<id>")}`,` ${g(s,"2.")} ${re(s,"omnish run")}`,""].join(`
335
+ `));return}await bc({verbose:rn(),force:n.force});return}case"run":{let n=ch(t);if(n.help){sd();return}if(n.background){uh(n.logFile);return}await hh();return}case"stop":dh();return;case"logout":{try{Pt.rmSync(X,{recursive:!0,force:!0}),console.log(N(process.stdout,"Session removed. Run `omnish link` to pair again."))}catch(n){console.error(C(process.stderr,`logout failed: ${String(n)}`)),process.exitCode=1}return}case"allow":{let n=t[0],r=process.stdout,o=process.stderr;if(!n){console.error(C(o,"Usage: omnish allow +<E164> or omnish allow tg:<user_id>")),process.exitCode=1;return}try{let s=fr(n);console.log(`${g(r,"allowFrom:")} ${k(r,s.allowFrom.join(", ")||"(empty)")}`),console.log(`${g(r,"telegramAllowFrom:")} ${k(r,s.telegramAllowFrom.join(", ")||"(empty)")}`)}catch(s){console.error(C(o,String(s).replace(/^\[omnish\]\s*/,""))),process.exitCode=1}return}case"deny":{let n=t[0],r=process.stdout,o=process.stderr;if(!n){console.error(C(o,"Usage: omnish deny +<E164> or omnish deny tg:<user_id>")),process.exitCode=1;return}try{let s=gr(n);console.log(`${g(r,"allowFrom:")} ${k(r,s.allowFrom.join(", ")||"(empty)")}`),console.log(`${g(r,"telegramAllowFrom:")} ${k(r,s.telegramAllowFrom.join(", ")||"(empty)")}`)}catch(s){console.error(C(o,String(s).replace(/^\[omnish\]\s*/,""))),process.exitCode=1}return}case"i":case"interactive":{await nd(t);return}case"status":{let n=process.stdout,r=$(),o=t.includes("--check-updates"),s=new dt().list(),i=s.filter(y=>y.status==="running").length,a=pe(r),l=lt(r),c=r.gatewayMode==="whatsapp"||r.gatewayMode==="both",u=r.gatewayMode==="telegram"||r.gatewayMode==="both",d=Ge(),p=d?yc(X):null,f=[];if(f.push(`${me(n,"omnish")} ${g(n,Be())}`,`${g(n,"gatewayMode:")} ${k(n,r.gatewayMode)}`,`${g(n,"data dir:")} ${k(n,Fi.dirname(X))}`,"",`${g(n,"gateway process:")} ${k(n,mh().replace(/^gateway process: /,""))}`,"",wt(n),re(n,"whatsapp"),` ${g(n,"in use:")} ${c?k(n,"yes"):ae(n,"no (gatewayMode is telegram-only)")}`),c){let y=d?k(n,`linked (${X})`):ae(n,"missing \u2014 run omnish link");f.push(` ${g(n,"session:")} ${y}`),d&&p&&f.push(` ${g(n,"linked as:")} ${k(n,p)}`),d&&!p&&f.push(` ${g(n,"linked as:")} ${J(n,"(not in creds yet \u2014 try again after omnish link completes)")}`)}if(f.push(` ${B(n,"Allowed")}`),r.allowFrom.length===0)f.push(` ${J(n,"(none)")}`);else for(let y of r.allowFrom)f.push(` ${g(n,"whatsapp:")} ${k(n,y)}`);if(f.push("",wt(n),re(n,"telegram")),f.push(` ${g(n,"in use:")} ${u?k(n,"yes"):ae(n,"no (gatewayMode is whatsapp-only)")}`),u){let y=a?k(n,ph(a)):ae(n,"(none) \u2014 omnish link --tg <token> or TELEGRAM_BOT_TOKEN");f.push(` ${g(n,"bot token:")} ${y}`)}if(f.push(` ${B(n,"Allowed")}`),r.telegramAllowFrom.length===0)f.push(` ${J(n,"(none)")}`);else for(let y of r.telegramAllowFrom)f.push(` ${g(n,"telegram:")} ${k(n,y)}`);if(f.push("",wt(n),...await Cu(n)),f.push("",wt(n),`${re(n,"jobs")} ${g(n,`(recent): ${s.length} total, ${i} running`)}`,ua(l,n)),console.log(f.join(`
336
+ `)),o){let y=await Qn(Be(),r),h=Ln(y);console.log(""),console.log(wt(n)),console.log(Pe(h,"whatsapp").text)}if(r.clusterEnabled){let y=De(),h=ce(),w=Object.keys(h.senderBindings).length,v=Object.keys(r.clusterSenderBindings??{}).length;console.log(""),console.log(wt(n)),console.log(re(n,"cluster")),console.log(` ${g(n,"\xB7")} ${k(n,`enabled \xB7 label ${r.clusterLabel||od.hostname()} \xB7 bindings ${w} chat / ${v} default`)}`),console.log(` ${g(n,"node:")} ${k(n,`${y.slice(0,8)}\u2026`)}`)}return}case"commands":{let n=process.stdout,r=$();console.log(oa(en(r),n)),console.log(""),console.log(wt(n)),console.log(k(n,"Keys editable from chat via /config set (same trust as shell):")),console.log(g(n,St.join(", "))),console.log(`${g(n,"See also:")} ${k(n,L)}`);return}case"cluster":{let n=process.stdout,r=process.stderr,o=(t[0]??"status").toLowerCase();if(o==="status"){let s=$(),i=De();if(console.log(`${g(n,"clusterEnabled:")} ${k(n,String(s.clusterEnabled))}`),console.log(`${g(n,"clusterLabel:")} ${k(n,s.clusterLabel||"(hostname)")}`),console.log(`${g(n,"clusterRole:")} ${k(n,`${s.clusterRole} (informational; no longer used to gate traffic)`)}`),console.log(`${g(n,"node id:")} ${re(n,i)}`),s.clusterEnabled){let a=ce(),l=bs(s,null);console.log(""),console.log(k(n,l.wa.replace(/\*([^*]+)\*/g,"$1").replace(/`([^`]+)`/g,"$1"))),console.log(""),console.log(ks(a,s,null));let c=Object.keys(a.senderBindings).length,u=Object.entries(s.clusterSenderBindings??{});if(c>0){console.log(""),console.log(re(n,"Chat bindings (cluster-local.json)"));for(let[d,p]of Object.entries(a.senderBindings))console.log(` ${g(n,d)} ${J(n,"->")} ${k(n,`${p.nodeId} (${p.source}, since ${p.sinceIso})`)}`)}if(u.length>0){console.log(""),console.log(re(n,"Config defaults (clusterSenderBindings)"));for(let[d,p]of u)console.log(` ${g(n,d)} ${J(n,"->")} ${k(n,p)}`)}}else console.log(""),console.log(ae(n,"(cluster disabled \u2014 /config set clusterEnabled true to enable, then /c use <label-or-id> from each sender)"));return}if(o==="use"||o==="bind"){let s=t[1],i=t.slice(2).join(" ").trim();if(!s||!i){console.error(C(r,"Usage: omnish cluster use <senderE164|tg:id> <label-or-id>")),process.exitCode=1;return}let a=gh(s);if(!a){console.error(C(r,`Could not parse sender "${s}". Use +E164 (WhatsApp) or tg:<user_id> (Telegram).`)),process.exitCode=1;return}let{state:l,resolved:c}=ll(a,i);if(!c.ok){if(c.reason==="ambiguous-label"){let d=(c.matches??[]).map(p=>`${p.nodeId}(${p.label})`).join(", ");console.error(C(r,`Label "${i}" matches multiple machines: ${d}. Use the 8-character id.`))}else console.error(C(r,`No machine matches "${i}". Run /c status from the chat first to populate the roster, or pass an 8-character node id.`));process.exitCode=1;return}console.log(`${re(n,"cluster:")} ${k(n,`${a} -> ${c.peer.nodeId} (${c.peer.label}).`)}`);let u=$();console.log(""),console.log(ks(l,u,a));return}if(o==="here"){console.error(C(r,"omnish cluster here is no longer available. Use: omnish cluster use <senderE164|tg:id> <label-or-id>")),console.error(C(r,"Or send /c here from the controller's chat on the machine you want to bind.")),process.exitCode=1;return}console.error(C(r,"Usage: omnish cluster [status | use <sender> <label-or-id>]")),process.exitCode=1;return}case"security":{let n=$(),r=lt(n),o=t.includes("--json");console.log(o?la(r):Vo(r,process.stdout)),On(r)&&(process.exitCode=1);return}case"service":{fh(t);return}case"config":{await $u(t);return}case"tunnel":{await jc(t);return}case"platform":{await Uu(t);return}case"ui":{let n=yh(t);if(n.help){cd();return}if(!Number.isFinite(n.port)||n.port<1||n.port>65535){console.error(C(process.stderr,"port must be between 1 and 65535.")),process.exitCode=1;return}let r=Wu(n.token);await Vu({host:n.host,port:n.port,meta:r});let o=process.stdout,s=Xu(n.port);console.log(""),console.log(`${me(o,"ui")} ${g(o,"listening")}`),console.log(`${g(o,"bind:")} ${k(o,`${n.host}:${n.port}`)}`),console.log(`${g(o,"setup token:")} ${k(o,r.token)}`),console.log(`${g(o,"token file:")} ${J(o,Ve)}`),console.log(""),console.log(ae(o,"Anyone on your network who can reach this port needs the token \u2014 do not expose to untrusted Wi\u2011Fi.")),console.log(""),console.log(`${g(o,"Open:")}`),console.log(` ${k(o,`http://127.0.0.1:${n.port}/`)}`);for(let i of s)console.log(` ${k(o,i)}`);console.log(""),console.log(`${g(o,"Quick link (same Wi\u2011Fi):")} ${k(o,`http://127.0.0.1:${n.port}/?token=${encodeURIComponent(r.token)}`)}`),console.log("");return}default:Ui(),e&&(process.exitCode=1)}}wh().catch(e=>{console.error(C(process.stderr,String(e))),process.exit(1)});