omnish 1.1.2 → 1.1.4

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,102 +1,102 @@
1
1
  #!/usr/bin/env node
2
- import $c from"node:dns";import{spawn as Cc,spawnSync as Rc}from"node:child_process";import fe from"node:fs";import Mn from"node:path";import $i from"node:os";import Dt from"node:fs";import Ii from"node:crypto";import Tn from"node:fs";import Ai from"node:os";import K from"node:path";function Ei(){let e=process.env.OMNISH_HOME?.trim();if(e)return K.resolve(e);let t=Ai.homedir(),n=K.join(t,".omnish"),r=K.join(t,".whatslive");try{if(Tn.existsSync(n))return n;if(Tn.existsSync(r))return r}catch{}return n}var A=Ei(),Q=K.join(A,"auth"),be=K.join(A,"jobs"),Br=K.join(A,"apps"),Dr=K.join(A,"logs"),he=K.join(Dr,"gateway.log"),Z=K.join(A,"gateway.pid"),Ye=K.join(A,"gateway-control.json"),R=K.join(A,"config.json"),Lt=K.join(A,"shortcuts.json"),Ft=K.join(A,"recipes.json"),Nt=K.join(A,"recipes-user.json"),je=K.join(A,"cowork"),In=K.join(je,"tasks.json"),An=K.join(je,"pending-runs.json");function yt(e){let t=Ii.createHash("sha1").update(e,"utf8").digest("hex").slice(0,8);return K.join(Br,t)}function P(e){Tn.mkdirSync(e,{recursive:!0,mode:448})}function Y(){P(A),P(Q),P(be),P(Br),P(Dr),P(je)}var jr=/^(\d+)(?::\d+)?@s\.whatsapp\.net$/i,Wr=/^(\d+)@c\.us$/i,Ur=/^(\d+)@lid$/i;function Gr(e){let t=e.trim();for(;;){let n=t;if(t=t.replace(/^whatsapp:/i,"").trim(),t===n)return t}}function Oi(e){let t=Gr(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 Pi(e){let t=e.match(jr);if(t)return t[1]??null;let n=e.match(Wr);if(n)return n[1]??null;let r=e.match(Ur);return r?r[1]??null:null}function Hr(e){let t=e.replace(/\D/g,"");return t?`+${t}`:""}function _t(e){return`${e.replace(/\D/g,"")}@s.whatsapp.net`}function N(e){let t=Gr(e);if(!t||Oi(t))return null;if(jr.test(t)||Wr.test(t)||Ur.test(t)){let r=Pi(t);if(!r)return null;let o=Hr(r);return o.length>1?o:null}if(t.includes("@"))return null;let n=Hr(t);return n.length>1?n:null}function zr(e){return e.map(t=>String(t).trim()).filter(t=>!!t).map(t=>t==="*"?t:N(t)).filter(t=>!!t)}function Jr(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 qr(e){let t=new Set;for(let n of e){let r=ue(String(n));r&&t.add(r)}return t}function En(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=N(t);return r?{kind:"wa",normalized:r}:null}var I={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,clusterEnabled:!1,clusterLabel:"",clusterRole:"secondary",clusterSenderBindings:{},serviceInstallFromChat:!1,updateCheckEnabled:!1,updateCheckIntervalMs:864e5,updateCheckPackageName:"omnish",updateInfoUrl:""};function Li(e){return e==="downloads"||e==="omnishData"||e==="sessionCwd"||e==="processCwd"||e==="fixed"?e:I.fileReceiveRootMode}function Fi(e){return e==="primary"?"primary":"secondary"}function Ni(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=N(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=N(s);a&&(t[`wa:${a}`]=o.slice(0,64))}return t}function Bt(e){let t=typeof e.appsFlushMs=="number"&&e.appsFlushMs>=0?e.appsFlushMs:I.appsFlushMs,n=e.gatewayMode==="telegram"||e.gatewayMode==="both"||e.gatewayMode==="whatsapp"?e.gatewayMode:I.gatewayMode,r=typeof e.telegramBotToken=="string"?e.telegramBotToken:I.telegramBotToken,o=Array.isArray(e.telegramAllowFrom)?[...new Set(e.telegramAllowFrom.map(s=>ue(String(s))).filter(s=>!!s))].sort():I.telegramAllowFrom;return{...I,...e,gatewayMode:n,telegramBotToken:r,telegramAllowFrom:o,allowFrom:Array.isArray(e.allowFrom)?zr(e.allowFrom.map(String)).filter(s=>s!=="*"):I.allowFrom,commandPrefix:(()=>{let s=typeof e.commandPrefix=="string"&&e.commandPrefix.length>0?e.commandPrefix:I.commandPrefix;return s==="! "?"!":s})(),syncTimeoutMs:typeof e.syncTimeoutMs=="number"&&e.syncTimeoutMs>0?e.syncTimeoutMs:I.syncTimeoutMs,syncMaxBytes:typeof e.syncMaxBytes=="number"&&e.syncMaxBytes>0?e.syncMaxBytes:I.syncMaxBytes,jobLogTailLines:typeof e.jobLogTailLines=="number"&&e.jobLogTailLines>0?e.jobLogTailLines:I.jobLogTailLines,shell:typeof e.shell=="string"&&e.shell.length>0?e.shell:I.shell,appsCols:typeof e.appsCols=="number"&&e.appsCols>0&&e.appsCols<=500?Math.floor(e.appsCols):I.appsCols,appsRows:typeof e.appsRows=="number"&&e.appsRows>0&&e.appsRows<=200?Math.floor(e.appsRows):I.appsRows,appsFlushMs:t,appsMinIntervalMs:typeof e.appsMinIntervalMs=="number"&&e.appsMinIntervalMs>=0?e.appsMinIntervalMs:I.appsMinIntervalMs,appsMaxFlushBytes:typeof e.appsMaxFlushBytes=="number"&&e.appsMaxFlushBytes>256?Math.floor(e.appsMaxFlushBytes):I.appsMaxFlushBytes,appsMaxSessions:typeof e.appsMaxSessions=="number"&&e.appsMaxSessions>0?Math.min(50,Math.floor(e.appsMaxSessions)):I.appsMaxSessions,appsMaxSessionsTotal:typeof e.appsMaxSessionsTotal=="number"&&e.appsMaxSessionsTotal>0?Math.min(200,Math.floor(e.appsMaxSessionsTotal)):I.appsMaxSessionsTotal,appsMaxWaChars:typeof e.appsMaxWaChars=="number"&&e.appsMaxWaChars>256?Math.floor(e.appsMaxWaChars):I.appsMaxWaChars,appsLogTailLines:typeof e.appsLogTailLines=="number"&&e.appsLogTailLines>0?Math.min(500,Math.floor(e.appsLogTailLines)):I.appsLogTailLines,appsSubmitDelayMs:typeof e.appsSubmitDelayMs=="number"&&e.appsSubmitDelayMs>=0?Math.min(500,Math.floor(e.appsSubmitDelayMs)):I.appsSubmitDelayMs,appsClearInput:typeof e.appsClearInput=="boolean"?e.appsClearInput:I.appsClearInput,appsClearInputDelayMs:typeof e.appsClearInputDelayMs=="number"&&e.appsClearInputDelayMs>=0?Math.min(200,Math.floor(e.appsClearInputDelayMs)):I.appsClearInputDelayMs,appsClearInputSequence:typeof e.appsClearInputSequence=="string"&&e.appsClearInputSequence.length>0?e.appsClearInputSequence.slice(0,200):I.appsClearInputSequence,fileSendMaxBytes:typeof e.fileSendMaxBytes=="number"&&e.fileSendMaxBytes>=0?e.fileSendMaxBytes===0?0:Math.min(2e9,Math.floor(e.fileSendMaxBytes)):I.fileSendMaxBytes,fileReceiveMaxBytes:typeof e.fileReceiveMaxBytes=="number"&&e.fileReceiveMaxBytes>=0?e.fileReceiveMaxBytes===0?0:Math.min(2e9,Math.floor(e.fileReceiveMaxBytes)):I.fileReceiveMaxBytes,fileInboxSubdir:typeof e.fileInboxSubdir=="string"&&e.fileInboxSubdir.length>0&&e.fileInboxSubdir.replace(/[/\\]/g,"").slice(0,80)||I.fileInboxSubdir,fileReceiveRootMode:Li(e.fileReceiveRootMode),fileReceiveRootPath:typeof e.fileReceiveRootPath=="string"?e.fileReceiveRootPath.trim().slice(0,4096):I.fileReceiveRootPath,recipesAllowDangerousBuiltins:typeof e.recipesAllowDangerousBuiltins=="boolean"?e.recipesAllowDangerousBuiltins:I.recipesAllowDangerousBuiltins,recipesMaxTaskChars:typeof e.recipesMaxTaskChars=="number"&&e.recipesMaxTaskChars>=0?e.recipesMaxTaskChars===0?0:Math.min(Number.MAX_SAFE_INTEGER,Math.floor(e.recipesMaxTaskChars)):I.recipesMaxTaskChars,clusterEnabled:typeof e.clusterEnabled=="boolean"?e.clusterEnabled:I.clusterEnabled,clusterLabel:typeof e.clusterLabel=="string"?e.clusterLabel.trim().slice(0,128):I.clusterLabel,clusterRole:Fi(e.clusterRole),clusterSenderBindings:Ni(e.clusterSenderBindings),serviceInstallFromChat:typeof e.serviceInstallFromChat=="boolean"?e.serviceInstallFromChat:I.serviceInstallFromChat,updateCheckEnabled:typeof e.updateCheckEnabled=="boolean"?e.updateCheckEnabled:I.updateCheckEnabled,updateCheckIntervalMs:typeof e.updateCheckIntervalMs=="number"&&e.updateCheckIntervalMs>0?Math.min(6048e5,Math.max(36e5,Math.floor(e.updateCheckIntervalMs))):I.updateCheckIntervalMs,updateCheckPackageName:typeof e.updateCheckPackageName=="string"&&e.updateCheckPackageName.trim().length>0?e.updateCheckPackageName.trim().slice(0,214):I.updateCheckPackageName,updateInfoUrl:typeof e.updateInfoUrl=="string"?e.updateInfoUrl.trim().slice(0,2048):I.updateInfoUrl}}function $(){if(Y(),!Dt.existsSync(R)){let e=Bt({});return Oe(e),e}try{let e=Dt.readFileSync(R,"utf8"),t=JSON.parse(e);return Bt(t)}catch{return Bt({})}}function Oe(e){Y();let t=Bt(e);Dt.writeFileSync(R,JSON.stringify(t,null,2)+`
3
- `,{mode:384})}function Ht(e){let t=En(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 Oe(n),n}function jt(e){let t=En(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=>ue(r)!==t.id),Oe(n),n}function de(e){let t=typeof process.env.TELEGRAM_BOT_TOKEN=="string"?process.env.TELEGRAM_BOT_TOKEN.trim():"";return t||(e.telegramBotToken??"").trim()}function Ve(e){let t=e.trim();return/^\d{6,}:[A-Za-z0-9_-]{20,}$/.test(t)}function Qe(e){let t=$();return t.telegramBotToken=e.trim(),Oe(t),$()}function Wt(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 Ut(e){let t=$();return t.gatewayMode=e,Oe(t),$()}function Gt(e){let t=$();return t.clusterEnabled=e,Oe(t),$()}function Ze(){try{return Dt.readdirSync(Q).length>0}catch{return!1}}import Qa from"node:path";import Kr from"node:fs";import Yr from"node:path";var _i=new Set(["jpg","jpeg","png","gif","webp","bmp","tif","tiff","heic","avif"]),Bi=new Set(["mp4","mov","webm","mkv","avi","m4v","3gp"]),Di=new Set(["mp3","ogg","opus","wav","m4a","flac","aac","wma"]),zt={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 Hi(e){let t=e.replace(/^\./,"").toLowerCase();return _i.has(t)?{category:"image",mimetype:zt[t]??"image/jpeg"}:Bi.has(t)?{category:"video",mimetype:zt[t]??"video/mp4"}:Di.has(t)?{category:"audio",mimetype:zt[t]??"audio/mpeg"}:{category:"document",mimetype:zt[t]??"application/octet-stream"}}function Xe(e,t){let n;try{n=Kr.realpathSync(e)}catch{return{error:"File not found or unreadable."}}let r;try{r=Kr.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=Yr.basename(n),s=Yr.extname(n).slice(1),{category:i,mimetype:a}=Hi(s);return{absPath:n,category:i,mimetype:a,displayName:o}}import Ui from"node:os";import qt from"node:path";import On from"node:fs";import Qr from"node:os";import Pe from"node:path";var Zr=Pe.join(A,"sessions.json"),et=new Map;function Xr(){return{cwd:Pe.resolve(Qr.homedir())}}function ji(e){return e.startsWith("wa:")||e.startsWith("tg:")?e:`wa:${e}`}function Wi(){try{let e=On.readFileSync(Zr,"utf8"),t=JSON.parse(e);for(let[n,r]of Object.entries(t)){if(!r||typeof r!="object")continue;let o=ji(n),i={cwd:typeof r.cwd=="string"&&r.cwd.length>0?Pe.resolve(r.cwd):Xr().cwd};r.fileReceiveRoot==="sessionCwd"&&(i.fileReceiveRoot="sessionCwd"),et.set(o,i)}}catch{}}function eo(){P(A);let e={};for(let[t,n]of et){let r={cwd:n.cwd};n.fileReceiveRoot==="sessionCwd"&&(r.fileReceiveRoot="sessionCwd"),e[t]=r}On.writeFileSync(Zr,JSON.stringify(e,null,2)+`
4
- `,{mode:384})}var Vr=!1;function Pn(){Vr||(Wi(),Vr=!0)}function B(e){Pn();let t=et.get(e);return t||(t=Xr(),et.set(e,t)),t}function Jt(e,t){Pn();let n=Pe.resolve(t),r=B(e);r.cwd=n,et.set(e,r),eo()}function to(e){return B(e).fileReceiveRoot==="sessionCwd"?"sessionCwd":"default"}function Ln(e,t){Pn();let n=B(e);t==="default"?delete n.fileReceiveRoot:n.fileReceiveRoot="sessionCwd",et.set(e,n),eo()}function no(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 ro(e,t){if(t.kind==="home")return Pe.resolve(Qr.homedir());let n=t.value.replace(/^['"]|['"]$/g,"");return Pe.isAbsolute(n)?Pe.normalize(n):Pe.resolve(e,n)}function oo(e){try{return On.statSync(e).isDirectory()?{ok:!0}:{ok:!1,error:`Not a directory: ${e}`}}catch(t){return{ok:!1,error:String(t)}}}var Gi="Omnish";function so(){return qt.join(Ui.homedir(),"Downloads",Gi)}function tt(e,t){let n=B(t);if(n.fileReceiveRoot==="sessionCwd")return n.cwd;switch(e.fileReceiveRootMode){case"downloads":return so();case"omnishData":return qt.join(A,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(!qt.isAbsolute(r))throw new Error('fileReceiveRootPath must be an absolute path when fileReceiveRootMode is "fixed".');return qt.resolve(r)}default:return so()}}import Ie from"node:fs";import ao from"node:path";import wt from"node:process";var We="\x1B";function zi(e){return e.replace(/\u001B\[[\d;]*m/g,"")}function Kt(e){return zi(e).length}function Fn(e,t,n,r,o=2){let s=Math.max(0,t-Kt(n));return`${e}${n}${" ".repeat(s)}${" ".repeat(o)}${r}`}function Le(e,t,n,r){if(n.length===0)return[];let o=n.map(i=>r(i.left)),s=Math.max(...o.map(Kt));return n.map((i,a)=>Fn(t,s,o[a],b(e,i.right)))}var Fe={primary:"#b4ff24",foreground:"#eef2f4",muted:"#8a9199",border:"#2a3139",error:"#ef4444",warn:"#a0e614",warnAlt:"#8cce04"};function Ji(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 Ne(e){let t=Ji(e);return t?`${We}[38;2;${t.r};${t.g};${t.b}m`:""}function Yt(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 Te(e,t,n){return!t||!Yt(e)?n:`${t}${n}${We}[0m`}function q(e,t){return Te(e,`${We}[1m`,t)}function qi(e,t){return Yt(e)?`${Ne(Fe.foreground)}${We}[1m${t}${We}[0m`:t}function ee(e,t){return Te(e,`${We}[2m`,t)}function te(e,t){return Te(e,`${Ne(Fe.primary)}${We}[1m`,t)}function H(e,t){return Te(e,Ne(Fe.primary),t)}function b(e,t){return Te(e,Ne(Fe.foreground),t)}function h(e,t){return Te(e,Ne(Fe.muted),t)}function Ki(e,t){return Te(e,Ne(Fe.border),t)}function Vt(e,t){return Te(e,Ne(Fe.error),t)}function ke(e,t){return Te(e,Ne(Fe.warn),t)}function Ue(e,t=60,n="\u2500"){let r=n.repeat(Math.max(1,t));return Ki(e,r)}function ne(e,t){return Yt(e)?`${`${h(e,"[")}${H(e,"omnish")}${h(e,"]")}`} ${t}`:`[omnish] ${t}`}function C(e,t){return Yt(e)?`${`${Vt(e,"[omnish]")}`} ${t}`:`[omnish] ${t}`}function io(e,t){let n=[];for(let r of e)switch(r.kind){case"title":n.push("",te(t,r.text),"");break;case"sub":n.push("",qi(t,r.text),"");break;case"gap":n.push("");break;case"p":n.push(b(t,r.text));break;case"bullet":n.push(`${h(t,"\u2022")} ${b(t,r.text)}`);break}return n.join(`
5
- `).replace(/^\n+/,"").trimEnd()}function Qt(e){return(e&4)!==0}function Nn(e){return(e&2)!==0}var lo={error:3,warn:2,info:1};function co(e,t){let n=lo[t];return e.filter(r=>lo[r.severity]>=n)}function nt(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 ${R} and delete wildcard entries.`,fixHint:`Edit ${R} 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 ${R} 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 ${R} unless you trust every allowlisted identity.`}),!ao.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 ${R}.`});else try{Ie.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 ${R} 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 ${R} points to a real file.`})}if(e.fileReceiveRootMode==="fixed"){let a=e.fileReceiveRootPath.trim();a?ao.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 ${R}.`}):n.push({severity:"error",code:"receive-fixed-empty",message:'fileReceiveRootMode is "fixed" but fileReceiveRootPath is empty.',fixHint:`Set fileReceiveRootPath to an absolute directory in ${R}, or change fileReceiveRootMode.`})}if(wt.platform!=="win32"){try{if(Ie.existsSync(R)){let u=Ie.statSync(R);(Qt(u.mode)||Nn(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 ${R}`,fixHint:`chmod 600 ${R}`})}}catch{}(typeof wt.getuid=="function"?wt.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(Ie.existsSync(A)){let u=Ie.statSync(A);(Qt(u.mode)||Nn(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 ${A}`,fixHint:`chmod 700 ${A}`}))}}catch{}try{if(!l&&Ie.existsSync(be)){let u=Ie.statSync(be);(Qt(u.mode)||Nn(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 ${be}`,fixHint:`chmod 700 ${be}`})}}catch{}try{if(Ie.existsSync(Q)){let u=Ie.statSync(Q);Qt(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 ${Q}`,fixHint:`chmod 700 ${Q}`})}}catch{}}return(typeof wt.env.TELEGRAM_BOT_TOKEN=="string"?wt.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&&!de(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 ${R} 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 Bn(e){return e.some(t=>t.severity==="error")}function Yi(e,t){switch(t){case"error":return Vt(e,"[ERROR]");case"warn":return ke(e,"[WARN]");case"info":return h(e,"[INFO]")}}function _n(e,t,n,r){if(r.length!==0){t.push(q(e,`${n} (${r.length}):`));for(let o of r)t.push(` ${Yi(e,o.severity)} ${h(e,`${o.code}:`)} ${b(e,o.message)}`),o.detail&&t.push(` ${h(e,o.detail)}`),o.fixHint&&t.push(` ${h(e,`Fix: ${o.fixHint}`)}`);t.push("")}}function Zt(e,t){if(e.length===0)return[`${te(t,"Security check:")} ${b(t,"no issues reported by automated rules.")}`,"",h(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=[`${te(t,"Security check:")} `+b(t,`${n.length} error(s), ${r.length} warning(s), ${o.length} note(s).`),""];return _n(t,i,"Errors",n),_n(t,i,"Warnings",r),_n(t,i,"Notes",o),i.push(h(t,"Allowlisted identities can run commands as this user; treat them like passwords.")),i.join(`
7
- `).trimEnd()}function Dn(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 uo(e){return`${JSON.stringify({findings:e,summary:Dn(e)},null,2)}
8
- `}function po(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 mo(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`${te(t,"security:")} ${b(t,"ok (no automated findings)")}`;let i=[];r&&i.push(Vt(t,`${r} error(s)`)),o&&i.push(ke(t,`${o} warning(s)`)),s&&i.push(h(t,`${s} note(s)`));let a=n??"run `omnish security` for details";return`${te(t,"security:")} ${i.join(", ")} ${h(t,`\u2014 ${a}`)}`}function p(e){return{wa:e,tg:e}}function z(e,t){return{wa:e,tg:t,tgHtml:!0}}function Ce(e,t){return t==="whatsapp"?{text:e.wa}:e.tgHtml?{text:e.tg,parseModeHtml:!0}:{text:e.tg}}function _(e){return e.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;")}function V(e){return e.replace(/[*_~`]/g,t=>({"*":"\u2217",_:"\uFF3F","~":"\u02DC","`":"\u2032"})[t]??t)}function Vi(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(`\u2022 ${n.text}`);break}return t.join(`
9
- `).replace(/^\n+/,"").trimEnd()}function Qi(e){let t=[];for(let n of e)switch(n.kind){case"title":t.push("",`<b>${_(n.text)}</b>`,"");break;case"sub":t.push("",`<i>${_(n.text)}</i>`,"");break;case"gap":t.push("");break;case"p":t.push(_(n.text));break;case"bullet":t.push(`\u2022 ${_(n.text)}`);break}return t.join(`
10
- `).replace(/^\n+/,"").trimEnd()}function O(e){return{wa:Vi(e),tg:Qi(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 mode (plain text = shell); space optional"},{kind:"bullet",text:"/bg <cmd> \u2014 background job; /jobs, /log, /tail, /kill"},{kind:"bullet",text:"/apps \u2026 \u2014 interactive app sessions (/apps help)"},{kind:"bullet",text:"/send path \u2014 host file \u2192 chat (/file); caption: path -- 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 per-chat saved commands (/shortcut help); invoke with bare !name or /name"},{kind:"bullet",text:"/run \u2014 parameterized CLI templates with a task (/run list); shorthand /r"},{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 fo(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 go(){return[{kind:"title",text:"Chat config"},{kind:"p",text:`Same trust as shell \u2014 allowlisted senders can change many keys saved to ${R}.`},{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 Hn(){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: ${R}`}]}function ho(e){let t=["*Gateway status*","",`Mode: *${e.gatewayMode}*`," \u2022 whatsapp \u2014 WhatsApp only"," \u2022 telegram \u2014 Telegram only"," \u2022 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: ${V(e.updateBrief)}`,""),t.push("Change: /gateway both \xB7 /gw tg","/updates \u2014 npm + optional notice URL",`Config: ${V(R)}`);let n=["<b>Gateway status</b>","",`<b>${_(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> ${_(e.updateBrief)}`,""),n.push("<i>Change:</i> /gateway both \xB7 /gw tg","<i>/updates</i> \u2014 npm + optional notice URL",_(`Config: ${R}`)),z(t.join(`
2
+ import Vc from"node:dns";import{spawn as Xc,spawnSync as Qc}from"node:child_process";import fe from"node:fs";import _n from"node:path";import Wi from"node:os";import qt from"node:fs";import Ki from"node:crypto";import Bn from"node:fs";import Yi from"node:os";import V from"node:path";function Vi(){let e=process.env.OMNISH_HOME?.trim();if(e)return V.resolve(e);let t=Yi.homedir(),n=V.join(t,".omnish"),r=V.join(t,".whatslive");try{if(Bn.existsSync(n))return n;if(Bn.existsSync(r))return r}catch{}return n}var O=Vi(),te=V.join(O,"auth"),xe=V.join(O,"jobs"),Vr=V.join(O,"apps"),Xr=V.join(O,"logs"),be=V.join(Xr,"gateway.log"),ne=V.join(O,"gateway.pid"),tt=V.join(O,"gateway-control.json"),T=V.join(O,"config.json"),Wt=V.join(O,"shortcuts.json"),Ut=V.join(O,"recipes.json"),Gt=V.join(O,"recipes-user.json"),Ae=V.join(O,"cowork"),Dn=V.join(Ae,"tasks.json"),Hn=V.join(Ae,"pending-runs.json"),Qr=V.join(Ae,"completions.sqlite");function vt(e){let t=Ki.createHash("sha1").update(e,"utf8").digest("hex").slice(0,8);return V.join(Vr,t)}function B(e){Bn.mkdirSync(e,{recursive:!0,mode:448})}function Q(){B(O),B(te),B(xe),B(Vr),B(Xr),B(Ae)}var eo=/^(\d+)(?::\d+)?@s\.whatsapp\.net$/i,to=/^(\d+)@c\.us$/i,no=/^(\d+)@lid$/i;function ro(e){let t=e.trim();for(;;){let n=t;if(t=t.replace(/^whatsapp:/i,"").trim(),t===n)return t}}function Xi(e){let t=ro(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 Qi(e){let t=e.match(eo);if(t)return t[1]??null;let n=e.match(to);if(n)return n[1]??null;let r=e.match(no);return r?r[1]??null:null}function Zr(e){let t=e.replace(/\D/g,"");return t?`+${t}`:""}function zt(e){return`${e.replace(/\D/g,"")}@s.whatsapp.net`}function D(e){let t=ro(e);if(!t||Xi(t))return null;if(eo.test(t)||to.test(t)||no.test(t)){let r=Qi(t);if(!r)return null;let o=Zr(r);return o.length>1?o:null}if(t.includes("@"))return null;let n=Zr(t);return n.length>1?n:null}function oo(e){return e.map(t=>String(t).trim()).filter(t=>!!t).map(t=>t==="*"?t:D(t)).filter(t=>!!t)}function so(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 io(e){let t=new Set;for(let n of e){let r=ue(String(n));r&&t.add(r)}return t}function jn(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=D(t);return r?{kind:"wa",normalized:r}:null}var A={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,clusterEnabled:!1,clusterLabel:"",clusterRole:"secondary",clusterSenderBindings:{},serviceInstallFromChat:!1,updateCheckEnabled:!1,updateCheckIntervalMs:864e5,updateCheckPackageName:"omnish",updateInfoUrl:""};function Zi(e){return e==="downloads"||e==="omnishData"||e==="sessionCwd"||e==="processCwd"||e==="fixed"?e:A.fileReceiveRootMode}function ea(e){return e==="primary"?"primary":"secondary"}function ta(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=D(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=D(s);a&&(t[`wa:${a}`]=o.slice(0,64))}return t}function Jt(e){let t=typeof e.appsFlushMs=="number"&&e.appsFlushMs>=0?e.appsFlushMs:A.appsFlushMs,n=e.gatewayMode==="telegram"||e.gatewayMode==="both"||e.gatewayMode==="whatsapp"?e.gatewayMode:A.gatewayMode,r=typeof e.telegramBotToken=="string"?e.telegramBotToken:A.telegramBotToken,o=Array.isArray(e.telegramAllowFrom)?[...new Set(e.telegramAllowFrom.map(s=>ue(String(s))).filter(s=>!!s))].sort():A.telegramAllowFrom;return{...A,...e,gatewayMode:n,telegramBotToken:r,telegramAllowFrom:o,allowFrom:Array.isArray(e.allowFrom)?oo(e.allowFrom.map(String)).filter(s=>s!=="*"):A.allowFrom,commandPrefix:(()=>{let s=typeof e.commandPrefix=="string"&&e.commandPrefix.length>0?e.commandPrefix:A.commandPrefix;return s==="! "?"!":s})(),syncTimeoutMs:typeof e.syncTimeoutMs=="number"&&e.syncTimeoutMs>0?e.syncTimeoutMs:A.syncTimeoutMs,syncMaxBytes:typeof e.syncMaxBytes=="number"&&e.syncMaxBytes>0?e.syncMaxBytes:A.syncMaxBytes,jobLogTailLines:typeof e.jobLogTailLines=="number"&&e.jobLogTailLines>0?e.jobLogTailLines:A.jobLogTailLines,shell:typeof e.shell=="string"&&e.shell.length>0?e.shell:A.shell,appsCols:typeof e.appsCols=="number"&&e.appsCols>0&&e.appsCols<=500?Math.floor(e.appsCols):A.appsCols,appsRows:typeof e.appsRows=="number"&&e.appsRows>0&&e.appsRows<=200?Math.floor(e.appsRows):A.appsRows,appsFlushMs:t,appsMinIntervalMs:typeof e.appsMinIntervalMs=="number"&&e.appsMinIntervalMs>=0?e.appsMinIntervalMs:A.appsMinIntervalMs,appsMaxFlushBytes:typeof e.appsMaxFlushBytes=="number"&&e.appsMaxFlushBytes>256?Math.floor(e.appsMaxFlushBytes):A.appsMaxFlushBytes,appsMaxSessions:typeof e.appsMaxSessions=="number"&&e.appsMaxSessions>0?Math.min(50,Math.floor(e.appsMaxSessions)):A.appsMaxSessions,appsMaxSessionsTotal:typeof e.appsMaxSessionsTotal=="number"&&e.appsMaxSessionsTotal>0?Math.min(200,Math.floor(e.appsMaxSessionsTotal)):A.appsMaxSessionsTotal,appsMaxWaChars:typeof e.appsMaxWaChars=="number"&&e.appsMaxWaChars>256?Math.floor(e.appsMaxWaChars):A.appsMaxWaChars,appsLogTailLines:typeof e.appsLogTailLines=="number"&&e.appsLogTailLines>0?Math.min(500,Math.floor(e.appsLogTailLines)):A.appsLogTailLines,appsSubmitDelayMs:typeof e.appsSubmitDelayMs=="number"&&e.appsSubmitDelayMs>=0?Math.min(500,Math.floor(e.appsSubmitDelayMs)):A.appsSubmitDelayMs,appsClearInput:typeof e.appsClearInput=="boolean"?e.appsClearInput:A.appsClearInput,appsClearInputDelayMs:typeof e.appsClearInputDelayMs=="number"&&e.appsClearInputDelayMs>=0?Math.min(200,Math.floor(e.appsClearInputDelayMs)):A.appsClearInputDelayMs,appsClearInputSequence:typeof e.appsClearInputSequence=="string"&&e.appsClearInputSequence.length>0?e.appsClearInputSequence.slice(0,200):A.appsClearInputSequence,fileSendMaxBytes:typeof e.fileSendMaxBytes=="number"&&e.fileSendMaxBytes>=0?e.fileSendMaxBytes===0?0:Math.min(2e9,Math.floor(e.fileSendMaxBytes)):A.fileSendMaxBytes,fileReceiveMaxBytes:typeof e.fileReceiveMaxBytes=="number"&&e.fileReceiveMaxBytes>=0?e.fileReceiveMaxBytes===0?0:Math.min(2e9,Math.floor(e.fileReceiveMaxBytes)):A.fileReceiveMaxBytes,fileInboxSubdir:typeof e.fileInboxSubdir=="string"&&e.fileInboxSubdir.length>0&&e.fileInboxSubdir.replace(/[/\\]/g,"").slice(0,80)||A.fileInboxSubdir,fileReceiveRootMode:Zi(e.fileReceiveRootMode),fileReceiveRootPath:typeof e.fileReceiveRootPath=="string"?e.fileReceiveRootPath.trim().slice(0,4096):A.fileReceiveRootPath,recipesAllowDangerousBuiltins:typeof e.recipesAllowDangerousBuiltins=="boolean"?e.recipesAllowDangerousBuiltins:A.recipesAllowDangerousBuiltins,recipesMaxTaskChars:typeof e.recipesMaxTaskChars=="number"&&e.recipesMaxTaskChars>=0?e.recipesMaxTaskChars===0?0:Math.min(Number.MAX_SAFE_INTEGER,Math.floor(e.recipesMaxTaskChars)):A.recipesMaxTaskChars,clusterEnabled:typeof e.clusterEnabled=="boolean"?e.clusterEnabled:A.clusterEnabled,clusterLabel:typeof e.clusterLabel=="string"?e.clusterLabel.trim().slice(0,128):A.clusterLabel,clusterRole:ea(e.clusterRole),clusterSenderBindings:ta(e.clusterSenderBindings),serviceInstallFromChat:typeof e.serviceInstallFromChat=="boolean"?e.serviceInstallFromChat:A.serviceInstallFromChat,updateCheckEnabled:typeof e.updateCheckEnabled=="boolean"?e.updateCheckEnabled:A.updateCheckEnabled,updateCheckIntervalMs:typeof e.updateCheckIntervalMs=="number"&&e.updateCheckIntervalMs>0?Math.min(6048e5,Math.max(36e5,Math.floor(e.updateCheckIntervalMs))):A.updateCheckIntervalMs,updateCheckPackageName:typeof e.updateCheckPackageName=="string"&&e.updateCheckPackageName.trim().length>0?e.updateCheckPackageName.trim().slice(0,214):A.updateCheckPackageName,updateInfoUrl:typeof e.updateInfoUrl=="string"?e.updateInfoUrl.trim().slice(0,2048):A.updateInfoUrl}}function C(){if(Q(),!qt.existsSync(T)){let e=Jt({});return Ne(e),e}try{let e=qt.readFileSync(T,"utf8"),t=JSON.parse(e);return Jt(t)}catch{return Jt({})}}function Ne(e){Q();let t=Jt(e);qt.writeFileSync(T,JSON.stringify(t,null,2)+`
3
+ `,{mode:384})}function Kt(e){let t=jn(e);if(!t)throw new Error("Invalid entry. Use +E164 (WhatsApp) or tg:<user_id> (Telegram).");let n=C();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 Ne(n),n}function Yt(e){let t=jn(e);if(!t)throw new Error("Invalid entry. Use +E164 (WhatsApp) or tg:<user_id> (Telegram).");let n=C();return t.kind==="wa"?n.allowFrom=n.allowFrom.filter(r=>r!==t.normalized):n.telegramAllowFrom=n.telegramAllowFrom.filter(r=>ue(r)!==t.id),Ne(n),n}function de(e){let t=typeof process.env.TELEGRAM_BOT_TOKEN=="string"?process.env.TELEGRAM_BOT_TOKEN.trim():"";return t||(e.telegramBotToken??"").trim()}function nt(e){let t=e.trim();return/^\d{6,}:[A-Za-z0-9_-]{20,}$/.test(t)}function rt(e){let t=C();return t.telegramBotToken=e.trim(),Ne(t),C()}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 Xt(e){let t=C();return t.gatewayMode=e,Ne(t),C()}function Qt(e){let t=C();return t.clusterEnabled=e,Ne(t),C()}function ot(){try{return qt.readdirSync(te).length>0}catch{return!1}}import xl from"node:path";import ao from"node:fs";import lo from"node:path";var na=new Set(["jpg","jpeg","png","gif","webp","bmp","tif","tiff","heic","avif"]),ra=new Set(["mp4","mov","webm","mkv","avi","m4v","3gp"]),oa=new Set(["mp3","ogg","opus","wav","m4a","flac","aac","wma"]),Zt={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 sa(e){let t=e.replace(/^\./,"").toLowerCase();return na.has(t)?{category:"image",mimetype:Zt[t]??"image/jpeg"}:ra.has(t)?{category:"video",mimetype:Zt[t]??"video/mp4"}:oa.has(t)?{category:"audio",mimetype:Zt[t]??"audio/mpeg"}:{category:"document",mimetype:Zt[t]??"application/octet-stream"}}function _e(e,t){let n;try{n=ao.realpathSync(e)}catch{return{error:"File not found or unreadable."}}let r;try{r=ao.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=lo.basename(n),s=lo.extname(n).slice(1),{category:i,mimetype:a}=sa(s);return{absPath:n,category:i,mimetype:a,displayName:o}}import la from"node:os";import tn from"node:path";import Wn from"node:fs";import uo from"node:os";import Be from"node:path";var po=Be.join(O,"sessions.json"),st=new Map;function mo(){return{cwd:Be.resolve(uo.homedir())}}function ia(e){return e.startsWith("wa:")||e.startsWith("tg:")?e:`wa:${e}`}function aa(){try{let e=Wn.readFileSync(po,"utf8"),t=JSON.parse(e);for(let[n,r]of Object.entries(t)){if(!r||typeof r!="object")continue;let o=ia(n),i={cwd:typeof r.cwd=="string"&&r.cwd.length>0?Be.resolve(r.cwd):mo().cwd};r.fileReceiveRoot==="sessionCwd"&&(i.fileReceiveRoot="sessionCwd"),st.set(o,i)}}catch{}}function fo(){B(O);let e={};for(let[t,n]of st){let r={cwd:n.cwd};n.fileReceiveRoot==="sessionCwd"&&(r.fileReceiveRoot="sessionCwd"),e[t]=r}Wn.writeFileSync(po,JSON.stringify(e,null,2)+`
4
+ `,{mode:384})}var co=!1;function Un(){co||(aa(),co=!0)}function j(e){Un();let t=st.get(e);return t||(t=mo(),st.set(e,t)),t}function en(e,t){Un();let n=Be.resolve(t),r=j(e);r.cwd=n,st.set(e,r),fo()}function go(e){return j(e).fileReceiveRoot==="sessionCwd"?"sessionCwd":"default"}function Gn(e,t){Un();let n=j(e);t==="default"?delete n.fileReceiveRoot:n.fileReceiveRoot="sessionCwd",st.set(e,n),fo()}function ho(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 yo(e,t){if(t.kind==="home")return Be.resolve(uo.homedir());let n=t.value.replace(/^['"]|['"]$/g,"");return Be.isAbsolute(n)?Be.normalize(n):Be.resolve(e,n)}function wo(e){try{return Wn.statSync(e).isDirectory()?{ok:!0}:{ok:!1,error:`Not a directory: ${e}`}}catch(t){return{ok:!1,error:String(t)}}}var ca="Omnish";function bo(){return tn.join(la.homedir(),"Downloads",ca)}function it(e,t){let n=j(t);if(n.fileReceiveRoot==="sessionCwd")return n.cwd;switch(e.fileReceiveRootMode){case"downloads":return bo();case"omnishData":return tn.join(O,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(!tn.isAbsolute(r))throw new Error('fileReceiveRootPath must be an absolute path when fileReceiveRootMode is "fixed".');return tn.resolve(r)}default:return bo()}}import Le from"node:fs";import So from"node:path";import $t from"node:process";var qe="\x1B";function ua(e){return e.replace(/\u001B\[[\d;]*m/g,"")}function nn(e){return ua(e).length}function zn(e,t,n,r,o=2){let s=Math.max(0,t-nn(n));return`${e}${n}${" ".repeat(s)}${" ".repeat(o)}${r}`}function De(e,t,n,r){if(n.length===0)return[];let o=n.map(i=>r(i.left)),s=Math.max(...o.map(nn));return n.map((i,a)=>zn(t,s,o[a],k(e,i.right)))}var He={primary:"#b4ff24",foreground:"#eef2f4",muted:"#8a9199",border:"#2a3139",error:"#ef4444",warn:"#a0e614",warnAlt:"#8cce04"};function da(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 je(e){let t=da(e);return t?`${qe}[38;2;${t.r};${t.g};${t.b}m`:""}function rn(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 Oe(e,t,n){return!t||!rn(e)?n:`${t}${n}${qe}[0m`}function X(e,t){return Oe(e,`${qe}[1m`,t)}function pa(e,t){return rn(e)?`${je(He.foreground)}${qe}[1m${t}${qe}[0m`:t}function re(e,t){return Oe(e,`${qe}[2m`,t)}function oe(e,t){return Oe(e,`${je(He.primary)}${qe}[1m`,t)}function z(e,t){return Oe(e,je(He.primary),t)}function k(e,t){return Oe(e,je(He.foreground),t)}function h(e,t){return Oe(e,je(He.muted),t)}function ma(e,t){return Oe(e,je(He.border),t)}function on(e,t){return Oe(e,je(He.error),t)}function ve(e,t){return Oe(e,je(He.warn),t)}function Ke(e,t=60,n="\u2500"){let r=n.repeat(Math.max(1,t));return ma(e,r)}function se(e,t){return rn(e)?`${`${h(e,"[")}${z(e,"omnish")}${h(e,"]")}`} ${t}`:`[omnish] ${t}`}function M(e,t){return rn(e)?`${`${on(e,"[omnish]")}`} ${t}`:`[omnish] ${t}`}function ko(e,t){let n=[];for(let r of e)switch(r.kind){case"title":n.push("",oe(t,r.text),"");break;case"sub":n.push("",pa(t,r.text),"");break;case"gap":n.push("");break;case"p":n.push(k(t,r.text));break;case"bullet":n.push(`${h(t,"\u2022")} ${k(t,r.text)}`);break}return n.join(`
5
+ `).replace(/^\n+/,"").trimEnd()}function sn(e){return(e&4)!==0}function Jn(e){return(e&2)!==0}var xo={error:3,warn:2,info:1};function vo(e,t){let n=xo[t];return e.filter(r=>xo[r.severity]>=n)}function at(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 ${T} and delete wildcard entries.`,fixHint:`Edit ${T} 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 ${T} 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 ${T} unless you trust every allowlisted identity.`}),!So.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 ${T}.`});else try{Le.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 ${T} 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 ${T} points to a real file.`})}if(e.fileReceiveRootMode==="fixed"){let a=e.fileReceiveRootPath.trim();a?So.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 ${T}.`}):n.push({severity:"error",code:"receive-fixed-empty",message:'fileReceiveRootMode is "fixed" but fileReceiveRootPath is empty.',fixHint:`Set fileReceiveRootPath to an absolute directory in ${T}, or change fileReceiveRootMode.`})}if($t.platform!=="win32"){try{if(Le.existsSync(T)){let d=Le.statSync(T);(sn(d.mode)||Jn(d.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 ${T}`,fixHint:`chmod 600 ${T}`})}}catch{}(typeof $t.getuid=="function"?$t.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(Le.existsSync(O)){let d=Le.statSync(O);(sn(d.mode)||Jn(d.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 ${O}`,fixHint:`chmod 700 ${O}`}))}}catch{}try{if(!l&&Le.existsSync(xe)){let d=Le.statSync(xe);(sn(d.mode)||Jn(d.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 ${xe}`,fixHint:`chmod 700 ${xe}`})}}catch{}try{if(Le.existsSync(te)){let d=Le.statSync(te);sn(d.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 ${te}`,fixHint:`chmod 700 ${te}`})}}catch{}}return(typeof $t.env.TELEGRAM_BOT_TOKEN=="string"?$t.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&&!de(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 ${T} 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 Kn(e){return e.some(t=>t.severity==="error")}function fa(e,t){switch(t){case"error":return on(e,"[ERROR]");case"warn":return ve(e,"[WARN]");case"info":return h(e,"[INFO]")}}function qn(e,t,n,r){if(r.length!==0){t.push(X(e,`${n} (${r.length}):`));for(let o of r)t.push(` ${fa(e,o.severity)} ${h(e,`${o.code}:`)} ${k(e,o.message)}`),o.detail&&t.push(` ${h(e,o.detail)}`),o.fixHint&&t.push(` ${h(e,`Fix: ${o.fixHint}`)}`);t.push("")}}function an(e,t){if(e.length===0)return[`${oe(t,"Security check:")} ${k(t,"no issues reported by automated rules.")}`,"",h(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=[`${oe(t,"Security check:")} `+k(t,`${n.length} error(s), ${r.length} warning(s), ${o.length} note(s).`),""];return qn(t,i,"Errors",n),qn(t,i,"Warnings",r),qn(t,i,"Notes",o),i.push(h(t,"Allowlisted identities can run commands as this user; treat them like passwords.")),i.join(`
7
+ `).trimEnd()}function Yn(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 $o(e){return`${JSON.stringify({findings:e,summary:Yn(e)},null,2)}
8
+ `}function Co(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 Ro(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`${oe(t,"security:")} ${k(t,"ok (no automated findings)")}`;let i=[];r&&i.push(on(t,`${r} error(s)`)),o&&i.push(ve(t,`${o} warning(s)`)),s&&i.push(h(t,`${s} note(s)`));let a=n??"run `omnish security` for details";return`${oe(t,"security:")} ${i.join(", ")} ${h(t,`\u2014 ${a}`)}`}function p(e){return{wa:e,tg:e}}function K(e,t){return{wa:e,tg:t,tgHtml:!0}}function Ee(e,t){return t==="whatsapp"?{text:e.wa}:e.tgHtml?{text:e.tg,parseModeHtml:!0}:{text:e.tg}}function H(e){return e.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;")}function Z(e){return e.replace(/[*_~`]/g,t=>({"*":"\u2217",_:"\uFF3F","~":"\u02DC","`":"\u2032"})[t]??t)}function ga(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(`\u2022 ${n.text}`);break}return t.join(`
9
+ `).replace(/^\n+/,"").trimEnd()}function ha(e){let t=[];for(let n of e)switch(n.kind){case"title":t.push("",`<b>${H(n.text)}</b>`,"");break;case"sub":t.push("",`<i>${H(n.text)}</i>`,"");break;case"gap":t.push("");break;case"p":t.push(H(n.text));break;case"bullet":t.push(`\u2022 ${H(n.text)}`);break}return t.join(`
10
+ `).replace(/^\n+/,"").trimEnd()}function P(e){return{wa:ga(e),tg:ha(e),tgHtml:!0}}function lt(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 mode (plain text = shell); space optional"},{kind:"bullet",text:"/bg <cmd> \u2014 background job; /jobs, /log, /tail, /kill"},{kind:"bullet",text:"/apps \u2026 \u2014 interactive app sessions (/apps help)"},{kind:"bullet",text:"/send path \u2014 host file \u2192 chat (/file); caption: path -- 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 per-chat saved commands (/shortcut help); invoke with bare !name or /name"},{kind:"bullet",text:"/run \u2014 parameterized CLI templates with a task (/run list); shorthand /r"},{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 Mo(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 To(){return[{kind:"title",text:"Chat config"},{kind:"p",text:`Same trust as shell \u2014 allowlisted senders can change many keys saved to ${T}.`},{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 Vn(){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: ${T}`}]}function Io(e){let t=["*Gateway status*","",`Mode: *${e.gatewayMode}*`," \u2022 whatsapp \u2014 WhatsApp only"," \u2022 telegram \u2014 Telegram only"," \u2022 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: ${Z(e.updateBrief)}`,""),t.push("Change: /gateway both \xB7 /gw tg","/updates \u2014 npm + optional notice URL",`Config: ${Z(T)}`);let n=["<b>Gateway status</b>","",`<b>${H(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> ${H(e.updateBrief)}`,""),n.push("<i>Change:</i> /gateway both \xB7 /gw tg","<i>/updates</i> \u2014 npm + optional notice URL",H(`Config: ${T}`)),K(t.join(`
11
11
  `),n.join(`
12
- `))}function bt(e){let t=["*Update check*","",`Running: *${V(e.runningVersion)}*`,`Checked (UTC): ${e.checkedAtIso}`,`npm package: ${V(e.registryPackage)}`];e.registryError?t.push("",`Registry: ${V(e.registryError)}`):e.registryLatest&&t.push("",e.updateAvailable?`*Newer version on npm:* ${V(e.registryLatest)} (upgrade when ready).`:`npm latest: ${V(e.registryLatest)} (this install is current or newer).`),e.infoError?t.push("",`Notice URL: ${V(e.infoError)}`):e.infoMessage&&(t.push("",`Notice: ${V(e.infoMessage)}`),e.infoLink&&t.push(V(e.infoLink))),t.push("","Config: updateCheckEnabled, updateCheckIntervalMs, updateCheckPackageName, updateInfoUrl");let n=["<b>Update check</b>","",`<b>Running</b> <code>${_(e.runningVersion)}</code>`,`<b>Checked (UTC)</b> ${_(e.checkedAtIso)}`,`<b>npm package</b> <code>${_(e.registryPackage)}</code>`];return e.registryError?n.push("",`<b>Registry</b> ${_(e.registryError)}`):e.registryLatest&&n.push("",e.updateAvailable?`<b>Newer on npm</b> <code>${_(e.registryLatest)}</code>`:`<b>npm latest</b> <code>${_(e.registryLatest)}</code> (current or newer here)`),e.infoError?n.push("",`<b>Notice URL</b> ${_(e.infoError)}`):e.infoMessage&&(n.push("",`<b>Notice</b> ${_(e.infoMessage)}`),e.infoLink&&n.push(_(e.infoLink))),n.push("","<i>Config keys:</i> updateCheckEnabled, updateCheckIntervalMs, updateCheckPackageName, updateInfoUrl"),z(t.join(`
12
+ `))}function Ct(e){let t=["*Update check*","",`Running: *${Z(e.runningVersion)}*`,`Checked (UTC): ${e.checkedAtIso}`,`npm package: ${Z(e.registryPackage)}`];e.registryError?t.push("",`Registry: ${Z(e.registryError)}`):e.registryLatest&&t.push("",e.updateAvailable?`*Newer version on npm:* ${Z(e.registryLatest)} (upgrade when ready).`:`npm latest: ${Z(e.registryLatest)} (this install is current or newer).`),e.infoError?t.push("",`Notice URL: ${Z(e.infoError)}`):e.infoMessage&&(t.push("",`Notice: ${Z(e.infoMessage)}`),e.infoLink&&t.push(Z(e.infoLink))),t.push("","Config: updateCheckEnabled, updateCheckIntervalMs, updateCheckPackageName, updateInfoUrl");let n=["<b>Update check</b>","",`<b>Running</b> <code>${H(e.runningVersion)}</code>`,`<b>Checked (UTC)</b> ${H(e.checkedAtIso)}`,`<b>npm package</b> <code>${H(e.registryPackage)}</code>`];return e.registryError?n.push("",`<b>Registry</b> ${H(e.registryError)}`):e.registryLatest&&n.push("",e.updateAvailable?`<b>Newer on npm</b> <code>${H(e.registryLatest)}</code>`:`<b>npm latest</b> <code>${H(e.registryLatest)}</code> (current or newer here)`),e.infoError?n.push("",`<b>Notice URL</b> ${H(e.infoError)}`):e.infoMessage&&(n.push("",`<b>Notice</b> ${H(e.infoMessage)}`),e.infoLink&&n.push(H(e.infoLink))),n.push("","<i>Config keys:</i> updateCheckEnabled, updateCheckIntervalMs, updateCheckPackageName, updateInfoUrl"),K(t.join(`
13
13
  `),n.join(`
14
- `))}function yo(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: ${A} (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 ${R}`},{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 wo(e){let t=!!de(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 ${R}, 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 ${R}; 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 bo(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})`,V(o),""),n.push(`<b>${_(r.label)}</b> (${r.items.length})`,_(o),"")}return z(t.join(`
14
+ `))}function Ao(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: ${O} (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 ${T}`},{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 Eo(e){let t=!!de(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 ${T}, 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 ${T}; 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 Oo(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})`,Z(o),""),n.push(`<b>${H(r.label)}</b> (${r.items.length})`,H(o),"")}return K(t.join(`
15
15
  `).trimEnd(),n.join(`
16
- `).trimEnd())}function jn(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})`,V(s),""),r.push(`<b>${_(o.label)}</b> (${o.items.length})`,_(s),"")}return z(n.join(`
16
+ `).trimEnd())}function Xn(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})`,Z(s),""),r.push(`<b>${H(o.label)}</b> (${o.items.length})`,H(s),"")}return K(n.join(`
17
17
  `).trimEnd(),r.join(`
18
- `).trimEnd())}function ko(e){let t=["*Token saved*","","telegramBotToken written to config (not echoed).",`Config: ${V(R)}`,...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).",_(`Config: ${R}`),...e.map(r=>`<i>${_(r)}</i>`),"","Send /reload so the gateway picks it up."].join(`
18
+ `).trimEnd())}function Lo(e){let t=["*Token saved*","","telegramBotToken written to config (not echoed).",`Config: ${Z(T)}`,...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).",H(`Config: ${T}`),...e.map(r=>`<i>${H(r)}</i>`),"","Send /reload so the gateway picks it up."].join(`
20
20
  `)+`
21
- `;return z(t.trimEnd(),n.trimEnd())}function So(){return O([{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 xo(e,t,n){let r=t?`
21
+ `;return K(t.trimEnd(),n.trimEnd())}function Po(){return P([{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 Fo(e,t,n){let r=t?`
22
22
 
23
23
  ${t}`:n?`
24
24
 
25
25
  Start omnish run on the host to apply (or /reload from a running gateway).`:"",o=t?`
26
26
 
27
- ${_(t)}`:n?`
27
+ ${H(t)}`:n?`
28
28
 
29
29
  Start omnish run on the host to apply (or /reload from a running gateway).`:"",s=`*gatewayMode saved*
30
30
 
31
- "${V(e)}"
32
- ${V(R)}${r}`,i=`<b>gatewayMode saved</b>
31
+ "${Z(e)}"
32
+ ${Z(T)}${r}`,i=`<b>gatewayMode saved</b>
33
33
 
34
- <code>${_(e)}</code>
35
- ${_(R)}${o}`;return z(s,i)}function vo(){return O([{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 $o(){return O([{kind:"title",text:"/deny \u2014 remove from allowlist"},{kind:"p",text:"Same forms as /allow: +E164 or tg:id"}])}function Co(){return O([{kind:"title",text:"/bg \u2014 background job"},{kind:"p",text:"Usage: /bg <shell command>"},{kind:"bullet",text:"Example: /bg sleep 30 && echo done"}])}function Wn(){return O([{kind:"title",text:"/send \u2014 push a host file to this chat"},{kind:"p",text:"Path is resolved from your session cwd (same as shell)."},{kind:"bullet",text:"/send ./photo.png"},{kind:"bullet",text:"/send /abs/path/report.pdf -- Q4 draft"},{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 Un(){return O([{kind:"title",text:"Files \u2014 send & receive"},{kind:"sub",text:"Host \u2192 chat"},{kind:"bullet",text:"/send <path> or /file \u2026 \u2014 path is relative to 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 (${R}) 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 Gn(){return O([{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 Ro(e,t){let n=to(t),r=B(t),o="";try{o=tt(e,t)}catch(s){o=`(${String(s)})`}return O(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 Mo(){return O([{kind:"title",text:"Unknown /wa command"},{kind:"p",text:"Send /wa help for setup."}])}function To(){return O([{kind:"title",text:"Unknown /tg command"},{kind:"p",text:"Send /tg help or /tg token <botfather_token>."}])}function Io(){return O([{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 Ao(e){return O([{kind:"title",text:"Unknown command"},{kind:"p",text:`Try /help or ${e.commandPrefix}<command>.`},{kind:"gap"},...rt(e)])}function Eo(e){return O([{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 Oo(){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"},...Hn()];return O(e)}function zn(){return[{kind:"title",text:"User shortcuts (this chat)"},{kind:"p",text:"Macros are stored per chat on the host. Invoke with a bare token only (no extra words)."},{kind:"gap"},{kind:"sub",text:"Manage"},{kind:"bullet",text:"/shortcut add <name> <command\u2026> \u2014 save (name: letters, digits, _ -)"},{kind:"bullet",text:"/shortcut set <name> <command\u2026> \u2014 same as add (overwrite)"},{kind:"bullet",text:"/shortcut list \u2014 or /shortcuts"},{kind:"bullet",text:"/shortcut show <name>"},{kind:"bullet",text:"/shortcut remove <name> \u2014 aliases: rm, del"},{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 Po(e){if(e.length===0)return p("(no shortcuts yet \u2014 /shortcut add <name> <command\u2026>)");let t=e.map(n=>`${n.name} \u2192 ${n.body}`);return p(["Shortcuts (this chat):","",...t].join(`
36
- `))}function Jn(e,t){return p(`Shortcut saved: ${e}
37
- \u2192 ${t}`)}function Lo(e){return p(`Shortcut removed: ${e}`)}function qn(e){return p(`Unknown shortcut: ${e}`)}function Fo(e,t){return p(`${e}
38
- \u2192 ${t}`)}function No(){return[{kind:"title",text:"/run \u2014 recipe templates"},{kind:"p",text:"Runs a command in an app session with your task injected via an environment variable (default OMNISH_TASK). Same host trust model as /apps start (session cwd)."},{kind:"gap"},{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 \u2014 same as /run"},{kind:"gap"},{kind:"sub",text:"Discover"},{kind:"bullet",text:"/run list \u2014 featured, this chat, and other templates"},{kind:"bullet",text:"/run show <name> \u2014 resolved command and metadata"},{kind:"gap"},{kind:"sub",text:"This chat only"},{kind:"bullet",text:'/run add <name> <command\u2026> \u2014 command must reference "$OMNISH_TASK" (or your taskEnv)'},{kind:"bullet",text:"/run set <name> <command\u2026> \u2014 replace"},{kind:"bullet",text:"/run remove <name> \u2014 aliases: rm, del"},{kind:"gap"},{kind:"p",text:`Host overrides: ${Ft}`},{kind:"p",text:"Built-in Claude templates omit --dangerously-skip-permissions unless recipesAllowDangerousBuiltins is true in config."}]}function _o(e){let t=["Recipes (/run <name> <task>)",""];if(e.featured.length>0){t.push("Featured:");for(let n of e.featured){let r=n.description?` \u2014 ${n.description}`:"",o=n.dangerous?" [dangerous flags]":"";t.push(`\u2022 ${n.name} \u2014 ${n.label??n.name}${r}${o}`)}t.push("")}if(e.yours.length>0){t.push("This chat:");for(let n of e.yours){let r=n.description?` \u2014 ${n.description}`:"";t.push(`\u2022 ${n.name} \u2014 ${n.label??n.name}${r}`)}t.push("")}if(e.more.length>0){t.push("More:");for(let n of e.more){let r=n.description?` \u2014 ${n.description}`:"";t.push(`\u2022 ${n.name} \u2014 ${n.label??n.name}${r}`)}}return e.featured.length===0&&e.yours.length===0&&e.more.length===0&&t.push("(no recipes \u2014 add host file or /run add)"),p(t.join(`
39
- `).trimEnd())}function Bo(e){let t=e.taskEnv??"OMNISH_TASK",n=[`Recipe: ${e.name}`,`Source: ${e.source}`,`Label: ${e.label??"(none)"}`,`Task env: ${t}`,`Command: ${e.command}`];return e.category&&n.push(`Category: ${e.category}`),e.description&&n.push(`Description: ${e.description}`),e.dangerous&&n.push("Note: built-in includes gated dangerous CLI flags."),p(n.join(`
40
- `))}function Kn(e,t){return p(`Recipe saved: ${e}
34
+ <code>${H(e)}</code>
35
+ ${H(T)}${o}`;return K(s,i)}function No(){return P([{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 _o(){return P([{kind:"title",text:"/deny \u2014 remove from allowlist"},{kind:"p",text:"Same forms as /allow: +E164 or tg:id"}])}function Bo(){return P([{kind:"title",text:"/bg \u2014 background job"},{kind:"p",text:"Usage: /bg <shell command>"},{kind:"bullet",text:"Example: /bg sleep 30 && echo done"}])}function Qn(){return P([{kind:"title",text:"/send \u2014 push a host file to this chat"},{kind:"p",text:"Path is resolved from your session cwd (same as shell)."},{kind:"bullet",text:"/send ./photo.png"},{kind:"bullet",text:"/send /abs/path/report.pdf -- Q4 draft"},{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 Zn(){return P([{kind:"title",text:"Files \u2014 send & receive"},{kind:"sub",text:"Host \u2192 chat"},{kind:"bullet",text:"/send <path> or /file \u2026 \u2014 path is relative to 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 (${T}) 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 er(){return P([{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 Do(e,t){let n=go(t),r=j(t),o="";try{o=it(e,t)}catch(s){o=`(${String(s)})`}return P(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 Ho(){return P([{kind:"title",text:"Unknown /wa command"},{kind:"p",text:"Send /wa help for setup."}])}function jo(){return P([{kind:"title",text:"Unknown /tg command"},{kind:"p",text:"Send /tg help or /tg token <botfather_token>."}])}function Wo(){return P([{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 Uo(e){return P([{kind:"title",text:"Unknown command"},{kind:"p",text:`Try /help or ${e.commandPrefix}<command>.`},{kind:"gap"},...lt(e)])}function Go(e){return P([{kind:"title",text:"No command matched"},{kind:"p",text:`Use ${JSON.stringify(e.commandPrefix)}, a slash command, or /apps help.`},{kind:"gap"},...lt(e)])}function zo(){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"},...Vn()];return P(e)}function tr(){return[{kind:"title",text:"User shortcuts (this chat)"},{kind:"p",text:"Macros are stored per chat on the host. Invoke with a bare token only (no extra words)."},{kind:"gap"},{kind:"sub",text:"Manage"},{kind:"bullet",text:"/shortcut add <name> <command\u2026> \u2014 save (name: letters, digits, _ -)"},{kind:"bullet",text:"/shortcut set <name> <command\u2026> \u2014 same as add (overwrite)"},{kind:"bullet",text:"/shortcut list \u2014 or /shortcuts"},{kind:"bullet",text:"/shortcut show <name>"},{kind:"bullet",text:"/shortcut remove <name> \u2014 aliases: rm, del"},{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 Jo(e){if(e.length===0)return p("(no shortcuts yet \u2014 /shortcut add <name> <command\u2026>)");let t=e.map(n=>`${n.name} \u2192 ${n.body}`);return p(["Shortcuts (this chat):","",...t].join(`
36
+ `))}function nr(e,t){return p(`Shortcut saved: ${e}
37
+ \u2192 ${t}`)}function qo(e){return p(`Shortcut removed: ${e}`)}function rr(e){return p(`Unknown shortcut: ${e}`)}function Ko(e,t){return p(`${e}
38
+ \u2192 ${t}`)}function Yo(){return[{kind:"title",text:"/run \u2014 recipe templates"},{kind:"p",text:"Runs a command in an app session with your task injected via an environment variable (default OMNISH_TASK). Same host trust model as /apps start (session cwd)."},{kind:"gap"},{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 \u2014 same as /run"},{kind:"gap"},{kind:"sub",text:"Discover"},{kind:"bullet",text:"/run list \u2014 featured, this chat, and other templates"},{kind:"bullet",text:"/run show <name> \u2014 resolved command and metadata"},{kind:"gap"},{kind:"sub",text:"This chat only"},{kind:"bullet",text:'/run add <name> <command\u2026> \u2014 command must reference "$OMNISH_TASK" (or your taskEnv)'},{kind:"bullet",text:"/run set <name> <command\u2026> \u2014 replace"},{kind:"bullet",text:"/run remove <name> \u2014 aliases: rm, del"},{kind:"gap"},{kind:"p",text:`Host overrides: ${Ut}`},{kind:"p",text:"Built-in Claude templates omit --dangerously-skip-permissions unless recipesAllowDangerousBuiltins is true in config."}]}function Vo(e){let t=["Recipes (/run <name> <task>)",""];if(e.featured.length>0){t.push("Featured:");for(let n of e.featured){let r=n.description?` \u2014 ${n.description}`:"",o=n.dangerous?" [dangerous flags]":"";t.push(`\u2022 ${n.name} \u2014 ${n.label??n.name}${r}${o}`)}t.push("")}if(e.yours.length>0){t.push("This chat:");for(let n of e.yours){let r=n.description?` \u2014 ${n.description}`:"";t.push(`\u2022 ${n.name} \u2014 ${n.label??n.name}${r}`)}t.push("")}if(e.more.length>0){t.push("More:");for(let n of e.more){let r=n.description?` \u2014 ${n.description}`:"";t.push(`\u2022 ${n.name} \u2014 ${n.label??n.name}${r}`)}}return e.featured.length===0&&e.yours.length===0&&e.more.length===0&&t.push("(no recipes \u2014 add host file or /run add)"),p(t.join(`
39
+ `).trimEnd())}function Xo(e){let t=e.taskEnv??"OMNISH_TASK",n=[`Recipe: ${e.name}`,`Source: ${e.source}`,`Label: ${e.label??"(none)"}`,`Task env: ${t}`,`Command: ${e.command}`];return e.category&&n.push(`Category: ${e.category}`),e.description&&n.push(`Description: ${e.description}`),e.dangerous&&n.push("Note: built-in includes gated dangerous CLI flags."),p(n.join(`
40
+ `))}function or(e,t){return p(`Recipe saved: ${e}
41
41
  \u2192 ${t.command}
42
- (task env: ${t.taskEnv??"OMNISH_TASK"})`)}function Xt(e){return p(`Unknown recipe: ${e}
43
- /run list`)}function Yn(){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; !!stop \u2014 back to attach."}]}function Zi(e){let{errors:t,warns:n,infos:r}=Dn(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:V(`[${u}] ${l.code}: ${l.message}`)}),l.detail&&o.push({kind:"bullet",text:V(l.detail)}),l.fixHint&&o.push({kind:"bullet",text:V(`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 Do(e){return O(Zi(e))}function Ho(){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 jo(){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 Go from"node:fs";import Xi from"node:path";var Wo=500,zo=/^[a-zA-Z0-9][a-zA-Z0-9_-]{0,31}$/,ea=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"]),en=new Map,Uo=!1;function ta(){try{let e=Go.readFileSync(Lt,"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())}en.set(n,o)}}catch{}}function Jo(){P(Xi.dirname(Lt));let e={};for(let[t,n]of en)Object.entries(n).length>0&&(e[t]={...n});Go.writeFileSync(Lt,JSON.stringify(e,null,2)+`
44
- `,{mode:384})}function na(){Uo||(ta(),Uo=!0)}function ra(e){return ea.has(e.trim().toLowerCase())}function tn(e){let t=e.trim();if(!t)return{ok:!1,error:"Name is empty."};let n=t.toLowerCase();return zo.test(n)?ra(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 oa(e){let t=e.replace(/\r\n/g,`
45
- `).replace(/\n/g," ").trim();return t?t.length>Wo?{ok:!1,error:`Body too long (max ${Wo} characters).`}:{ok:!0,body:t}:{ok:!1,error:"Body is empty."}}function nn(e){na();let t=en.get(e);return t||(t={},en.set(e,t)),t}function qo(e){let t=nn(e);return Object.entries(t).map(([n,r])=>({name:n,body:r})).sort((n,r)=>n.name.localeCompare(r.name))}function ot(e,t){let n=t.trim().toLowerCase();return nn(e)[n]}function Vn(e,t,n){let r=tn(t);if(!r.ok)throw new Error(r.error);let o=oa(n);if(!o.ok)throw new Error(o.error);let s=nn(e);s[r.normalized]=o.body,Jo()}function Ko(e,t){let n=t.trim().toLowerCase(),r=nn(e);return n in r?(delete r[n],Jo(),!0):!1}function Yo(e){let t=e.trim();return t.length>0&&!/\s/.test(t)&&zo.test(t.toLowerCase())}import sa from"node:crypto";import Zn from"node:fs";import ia from"node:path";var Xn=/^[a-zA-Z0-9][a-zA-Z0-9_-]{0,31}$/,er=new Set(["help","list","show","add","set","remove","rm","del","run","r"]),rn=new Map,Vo=!1;function aa(){try{let e=Zn.readFileSync(Nt,"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]=tr(i))}rn.set(n,o)}}catch{}}function tr(e){let t=typeof e.taskEnv=="string"&&/^[A-Za-z_][A-Za-z0-9_]*$/.test(e.taskEnv)?e.taskEnv:"OMNISH_TASK";return{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}}function Qo(){P(ia.dirname(Nt));let e={};for(let[t,n]of rn)Object.entries(n).length>0&&(e[t]={...n});Zn.writeFileSync(Nt,JSON.stringify(e,null,2)+`
46
- `,{mode:384})}function la(){Vo||(aa(),Vo=!0)}function nr(e){la();let t=rn.get(e);return t||(t={},rn.set(e,t)),t}function ca(){try{let e=Zn.readFileSync(Ft,"utf8"),t=JSON.parse(e);if(!t||typeof t!="object")return{};let n={};for(let[r,o]of Object.entries(t)){let s=r.trim().toLowerCase();!Xn.test(s)||er.has(s)||o&&typeof o=="object"&&typeof o.command=="string"&&(n[s]=tr(o))}return n}catch{return{}}}function ua(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();!Xn.test(s)||er.has(s)||o.command.trim()&&e.set(s,{...o,name:s,source:n})}}function Zo(e,t){let n=new Map;return Qn(n,ua(t),"builtin"),Qn(n,ca(),"global"),Qn(n,nr(e),"peer"),n}function st(e,t,n){let r=n.trim().toLowerCase();return Zo(e,t).get(r)}function on(e){let t=e.trim();if(!t)return{ok:!1,error:"Name is empty."};let n=t.toLowerCase();return Xn.test(n)?er.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 Xo(e,t){let n=e.replace(/\r\n/g,`
47
- `).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 rr(e,t){return e.includes("$")?e.includes(t):!1}function da(e){let t=e.command.trim();if(!t)return{ok:!1,error:"Command is empty."};let n=e.taskEnv??"OMNISH_TASK";return/^[A-Za-z_][A-Za-z0-9_]*$/.test(n)?rr(t,n)?{ok:!0}:{ok:!1,error:`Command must reference "$${n}" (or include $${n}) so the task is passed via the environment.`}:{ok:!1,error:"Invalid taskEnv (use letters, digits, _)."}}function or(e,t,n){let r=on(t);if(!r.ok)throw new Error(r.error);let o=tr({...n,command:n.command}),s=da(o);if(!s.ok)throw new Error(s.error);let i=nr(e);i[r.normalized]=o,Qo()}function es(e,t){let n=t.trim().toLowerCase(),r=nr(e);return n in r?(delete r[n],Qo(),!0):!1}function ts(e,t){let n=[...Zo(e,t).values()],r=n.filter(i=>i.featured).sort((i,a)=>i.name.localeCompare(a.name)),o=n.filter(i=>i.source==="peer").sort((i,a)=>i.name.localeCompare(a.name)),s=n.filter(i=>!i.featured&&i.source!=="peer").sort((i,a)=>i.name.localeCompare(a.name));return{featured:r,yours:o,more:s}}function ns(e){let t=sa.randomBytes(4).toString("hex");return`r-${e.replace(/[^a-zA-Z0-9_-]/g,"").slice(0,12)}-${t}`.slice(0,32)}import pa from"node:os";import*as os from"node-pty";function ma(e,t){return e.length<=t?e:`${e.slice(0,t)}
48
- [...truncated]`}function fa(e){if(e===void 0||e===0)return null;let t=pa.constants.signals;for(let[n,r]of Object.entries(t))if(r===e)return n;return null}function rs(e){try{process.platform==="win32"?e.kill():e.kill("SIGTERM")}catch{e.kill()}}function sn(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,g=w=>{if(u)return;u=!0,m!==void 0&&clearTimeout(m),c?.dispose(),c=null,r(w);let v=d;d=null,queueMicrotask(()=>v?.dispose())},y={...process.env,TERM:"xterm-256color",COLORTERM:"truecolor",...a?{PWD:a}:{}};try{l=os.spawn(e,["-c",t],{name:"xterm-256color",cols:120,rows:40,cwd:a,env:y})}catch(w){g({code:null,stdout:"",stderr:String(w),durationMs:Date.now()-o,timedOut:!1,signal:null});return}m=setTimeout(()=>{s=!0,l&&rs(l)},n.timeoutMs),c=l.onData(w=>{i+=w,i.length>n.maxBytes&&(i=ma(i,n.maxBytes),l&&rs(l))}),d=l.onExit(w=>{g({code:w.exitCode,stdout:i,stderr:"",durationMs:Date.now()-o,timedOut:s,signal:fa(w.signal)})})})}import it from"node:fs";import lr from"node:os";import kt from"node:path";import ls from"node:crypto";var ir="\u2063omnish/c v1",ga=/([nlra])=([^\s\]]*)/g;function Se(e){return e.replace(/-/g,"").slice(0,8)}function sr(e){return e.replace(/[\s\]\r\n]/g,"_").slice(0,64)}function ha(e){let t=sr(Se(e.nodeId)),n=sr(e.label||""),r=e.role==="primary"?"p":"s",o=sr(e.activeNodeId?Se(e.activeNodeId):"");return`${ir} [n=${t} l=${n} r=${r} a=${o}]`}function ss(e,t){let n=ha(t);if(e.includes(n))return e;let r=e.replace(/\s+$/,"");return r.length===0?n:`${r}
42
+ (task env: ${t.taskEnv??"OMNISH_TASK"})`)}function ln(e){return p(`Unknown recipe: ${e}
43
+ /run list`)}function sr(){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; !!stop \u2014 back to attach."}]}function ya(e){let{errors:t,warns:n,infos:r}=Yn(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 d=l.severity.toUpperCase();o.push({kind:"bullet",text:Z(`[${d}] ${l.code}: ${l.message}`)}),l.detail&&o.push({kind:"bullet",text:Z(l.detail)}),l.fixHint&&o.push({kind:"bullet",text:Z(`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 Qo(e){return P(ya(e))}function Zo(){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 es(){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 rs from"node:fs";import wa from"node:path";var ts=500,os=/^[a-zA-Z0-9][a-zA-Z0-9_-]{0,31}$/,ba=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"]),cn=new Map,ns=!1;function ka(){try{let e=rs.readFileSync(Wt,"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())}cn.set(n,o)}}catch{}}function ss(){B(wa.dirname(Wt));let e={};for(let[t,n]of cn)Object.entries(n).length>0&&(e[t]={...n});rs.writeFileSync(Wt,JSON.stringify(e,null,2)+`
44
+ `,{mode:384})}function Sa(){ns||(ka(),ns=!0)}function xa(e){return ba.has(e.trim().toLowerCase())}function un(e){let t=e.trim();if(!t)return{ok:!1,error:"Name is empty."};let n=t.toLowerCase();return os.test(n)?xa(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 va(e){let t=e.replace(/\r\n/g,`
45
+ `).replace(/\n/g," ").trim();return t?t.length>ts?{ok:!1,error:`Body too long (max ${ts} characters).`}:{ok:!0,body:t}:{ok:!1,error:"Body is empty."}}function dn(e){Sa();let t=cn.get(e);return t||(t={},cn.set(e,t)),t}function is(e){let t=dn(e);return Object.entries(t).map(([n,r])=>({name:n,body:r})).sort((n,r)=>n.name.localeCompare(r.name))}function ct(e,t){let n=t.trim().toLowerCase();return dn(e)[n]}function ir(e,t,n){let r=un(t);if(!r.ok)throw new Error(r.error);let o=va(n);if(!o.ok)throw new Error(o.error);let s=dn(e);s[r.normalized]=o.body,ss()}function as(e,t){let n=t.trim().toLowerCase(),r=dn(e);return n in r?(delete r[n],ss(),!0):!1}function ls(e){let t=e.trim();return t.length>0&&!/\s/.test(t)&&os.test(t.toLowerCase())}import $a from"node:crypto";import lr from"node:fs";import Ca from"node:path";var cr=/^[a-zA-Z0-9][a-zA-Z0-9_-]{0,31}$/,ur=new Set(["help","list","show","add","set","remove","rm","del","run","r"]),pn=new Map,cs=!1;function Ra(){try{let e=lr.readFileSync(Gt,"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]=dr(i))}pn.set(n,o)}}catch{}}function dr(e){let t=typeof e.taskEnv=="string"&&/^[A-Za-z_][A-Za-z0-9_]*$/.test(e.taskEnv)?e.taskEnv:"OMNISH_TASK";return{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}}function us(){B(Ca.dirname(Gt));let e={};for(let[t,n]of pn)Object.entries(n).length>0&&(e[t]={...n});lr.writeFileSync(Gt,JSON.stringify(e,null,2)+`
46
+ `,{mode:384})}function Ma(){cs||(Ra(),cs=!0)}function pr(e){Ma();let t=pn.get(e);return t||(t={},pn.set(e,t)),t}function Ta(){try{let e=lr.readFileSync(Ut,"utf8"),t=JSON.parse(e);if(!t||typeof t!="object")return{};let n={};for(let[r,o]of Object.entries(t)){let s=r.trim().toLowerCase();!cr.test(s)||ur.has(s)||o&&typeof o=="object"&&typeof o.command=="string"&&(n[s]=dr(o))}return n}catch{return{}}}function Ia(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 ar(e,t,n){for(let[r,o]of Object.entries(t)){let s=r.toLowerCase();!cr.test(s)||ur.has(s)||o.command.trim()&&e.set(s,{...o,name:s,source:n})}}function ds(e,t){let n=new Map;return ar(n,Ia(t),"builtin"),ar(n,Ta(),"global"),ar(n,pr(e),"peer"),n}function ut(e,t,n){let r=n.trim().toLowerCase();return ds(e,t).get(r)}function mn(e){let t=e.trim();if(!t)return{ok:!1,error:"Name is empty."};let n=t.toLowerCase();return cr.test(n)?ur.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 ps(e,t){let n=e.replace(/\r\n/g,`
47
+ `).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 mr(e,t){return e.includes("$")?e.includes(t):!1}function Aa(e){let t=e.command.trim();if(!t)return{ok:!1,error:"Command is empty."};let n=e.taskEnv??"OMNISH_TASK";return/^[A-Za-z_][A-Za-z0-9_]*$/.test(n)?mr(t,n)?{ok:!0}:{ok:!1,error:`Command must reference "$${n}" (or include $${n}) so the task is passed via the environment.`}:{ok:!1,error:"Invalid taskEnv (use letters, digits, _)."}}function fr(e,t,n){let r=mn(t);if(!r.ok)throw new Error(r.error);let o=dr({...n,command:n.command}),s=Aa(o);if(!s.ok)throw new Error(s.error);let i=pr(e);i[r.normalized]=o,us()}function ms(e,t){let n=t.trim().toLowerCase(),r=pr(e);return n in r?(delete r[n],us(),!0):!1}function fs(e,t){let n=[...ds(e,t).values()],r=n.filter(i=>i.featured).sort((i,a)=>i.name.localeCompare(a.name)),o=n.filter(i=>i.source==="peer").sort((i,a)=>i.name.localeCompare(a.name)),s=n.filter(i=>!i.featured&&i.source!=="peer").sort((i,a)=>i.name.localeCompare(a.name));return{featured:r,yours:o,more:s}}function gs(e){let t=$a.randomBytes(4).toString("hex");return`r-${e.replace(/[^a-zA-Z0-9_-]/g,"").slice(0,12)}-${t}`.slice(0,32)}import Ea from"node:os";import*as ys from"node-pty";function Oa(e,t){return e.length<=t?e:`${e.slice(0,t)}
48
+ [...truncated]`}function La(e){if(e===void 0||e===0)return null;let t=Ea.constants.signals;for(let[n,r]of Object.entries(t))if(r===e)return n;return null}function hs(e){try{process.platform==="win32"?e.kill():e.kill("SIGTERM")}catch{e.kill()}}function fn(e,t,n){return new Promise(r=>{let o=Date.now(),s=!1,i="",a=n.cwd,l=null,d=!1,c=null,u=null,m,f=y=>{if(d)return;d=!0,m!==void 0&&clearTimeout(m),c?.dispose(),c=null,r(y);let S=u;u=null,queueMicrotask(()=>S?.dispose())},w={...process.env,TERM:"xterm-256color",COLORTERM:"truecolor",...a?{PWD:a}:{}};try{l=ys.spawn(e,["-c",t],{name:"xterm-256color",cols:120,rows:40,cwd:a,env:w})}catch(y){f({code:null,stdout:"",stderr:String(y),durationMs:Date.now()-o,timedOut:!1,signal:null});return}m=setTimeout(()=>{s=!0,l&&hs(l)},n.timeoutMs),c=l.onData(y=>{i+=y,i.length>n.maxBytes&&(i=Oa(i,n.maxBytes),l&&hs(l))}),u=l.onExit(y=>{f({code:y.exitCode,stdout:i,stderr:"",durationMs:Date.now()-o,timedOut:s,signal:La(y.signal)})})})}import dt from"node:fs";import wr from"node:os";import Rt from"node:path";import Ss from"node:crypto";var hr="\u2063omnish/c v1",Pa=/([nlra])=([^\s\]]*)/g;function $e(e){return e.replace(/-/g,"").slice(0,8)}function gr(e){return e.replace(/[\s\]\r\n]/g,"_").slice(0,64)}function Fa(e){let t=gr($e(e.nodeId)),n=gr(e.label||""),r=e.role==="primary"?"p":"s",o=gr(e.activeNodeId?$e(e.activeNodeId):"");return`${hr} [n=${t} l=${n} r=${r} a=${o}]`}function ws(e,t){let n=Fa(t);if(e.includes(n))return e;let r=e.replace(/\s+$/,"");return r.length===0?n:`${r}
49
49
 
50
- ${n}`}function is(e){if(!e||!e.includes(ir))return null;let t=e.indexOf(ir),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(ga))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 cs=3,ya="node-id",wa="cluster-local.json",as=8;function us(){return kt.join(A,wa)}function cr(){return kt.join(A,ya)}function ye(){P(A);let e=cr();try{if(it.existsSync(e)){let n=it.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=ls.randomUUID();return it.writeFileSync(e,`${t}
51
- `,{mode:384}),t}function ds(){return{schemaVersion:cs,updatedAt:new Date().toISOString(),peers:[],senderBindings:{}}}function ba(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 j(){let e=us();try{let t=it.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=ba(n.senderBindings);return{schemaVersion:cs,updatedAt:typeof n.updatedAt=="string"?n.updatedAt:new Date().toISOString(),peers:r,senderBindings:o}}catch{return ds()}}function ka(e,t){let n=kt.dirname(e);P(n);let r=`${JSON.stringify(t,null,2)}
52
- `,o=kt.join(n,`.${kt.basename(e)}.tmp.${process.pid}.${ls.randomBytes(4).toString("hex")}`);it.writeFileSync(o,r,{mode:384}),it.renameSync(o,e)}function dr(e){let t=us(),n=ds();for(let r=0;r<as;r++){n=j(),e(n),n.updatedAt=new Date().toISOString();try{return ka(t,n),n}catch{}}throw new Error(`Could not write cluster local state after ${as} attempts: ${t}`)}function ps(e){return(e.clusterLabel??"").trim()||lr.hostname()}function W(e){return e.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;")}function ms(e,t){let n=e.peers.findIndex(r=>r.nodeId===t.nodeId);n>=0?e.peers[n]=t:e.peers.push(t)}function St(e){return{nodeId:Se(ye()),label:ps(e),role:e.clusterRole,lastSeenIso:new Date().toISOString()}}function fs(e,t,n){let r=n.trim();if(!r)return{ok:!1,reason:"not-found"};let o=St(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 Ae(e,t){let n=j(),r=n.senderBindings[t];if(r&&r.nodeId)return r;let o=e.clusterSenderBindings?.[t];if(typeof o=="string"&&o.trim()){let s=fs(n,e,o);if(s.ok)return{senderKey:t,nodeId:s.peer.nodeId,sinceIso:new Date(0).toISOString(),source:"config"}}return null}function ur(e,t,n="chat"){let r=$(),o=j(),s=fs(o,r,t);if(!s.ok)return{state:o,resolved:s};let i=new Date().toISOString();return{state:dr(l=>{l.senderBindings[e]={senderKey:e,nodeId:s.peer.nodeId,sinceIso:i,source:n},ms(l,{...s.peer,lastSeenIso:i})}),resolved:s}}function Sa(e){let t=null;return{state:dr(r=>{r.senderBindings[e]&&(t=r.senderBindings[e]??null,delete r.senderBindings[e])}),removed:t}}function gs(e,t){let n=Se(ye()),r=new Date().toISOString();return dr(o=>{if(ms(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 hs(e,t){let n=Ae(t,e);return n?n.nodeId===Se(ye()):!1}function xa(e,t){let n=St(t).nodeId,r=new Set([n]);for(let s of e.peers)r.add(s.nodeId);return[...r].sort()[0]??n}function pe(e,t){return xa(e,t)===Se(ye())}function an(e,t,n){let r=Se(ye()),o=["*Computers*",""],s=n?Ae(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,St(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(`
53
- `);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(`\u2022 *${l.label}*${m}
50
+ ${n}`}function bs(e){if(!e||!e.includes(hr))return null;let t=e.indexOf(hr),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(Pa))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 xs=3,Na="node-id",_a="cluster-local.json",ks=8;function vs(){return Rt.join(O,_a)}function br(){return Rt.join(O,Na)}function ke(){B(O);let e=br();try{if(dt.existsSync(e)){let n=dt.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=Ss.randomUUID();return dt.writeFileSync(e,`${t}
51
+ `,{mode:384}),t}function $s(){return{schemaVersion:xs,updatedAt:new Date().toISOString(),peers:[],senderBindings:{}}}function Ba(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 J(){let e=vs();try{let t=dt.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=Ba(n.senderBindings);return{schemaVersion:xs,updatedAt:typeof n.updatedAt=="string"?n.updatedAt:new Date().toISOString(),peers:r,senderBindings:o}}catch{return $s()}}function Da(e,t){let n=Rt.dirname(e);B(n);let r=`${JSON.stringify(t,null,2)}
52
+ `,o=Rt.join(n,`.${Rt.basename(e)}.tmp.${process.pid}.${Ss.randomBytes(4).toString("hex")}`);dt.writeFileSync(o,r,{mode:384}),dt.renameSync(o,e)}function Sr(e){let t=vs(),n=$s();for(let r=0;r<ks;r++){n=J(),e(n),n.updatedAt=new Date().toISOString();try{return Da(t,n),n}catch{}}throw new Error(`Could not write cluster local state after ${ks} attempts: ${t}`)}function Cs(e){return(e.clusterLabel??"").trim()||wr.hostname()}function q(e){return e.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;")}function Rs(e,t){let n=e.peers.findIndex(r=>r.nodeId===t.nodeId);n>=0?e.peers[n]=t:e.peers.push(t)}function Mt(e){return{nodeId:$e(ke()),label:Cs(e),role:e.clusterRole,lastSeenIso:new Date().toISOString()}}function Ms(e,t,n){let r=n.trim();if(!r)return{ok:!1,reason:"not-found"};let o=Mt(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(d=>d.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 Pe(e,t){let n=J(),r=n.senderBindings[t];if(r&&r.nodeId)return r;let o=e.clusterSenderBindings?.[t];if(typeof o=="string"&&o.trim()){let s=Ms(n,e,o);if(s.ok)return{senderKey:t,nodeId:s.peer.nodeId,sinceIso:new Date(0).toISOString(),source:"config"}}return null}function kr(e,t,n="chat"){let r=C(),o=J(),s=Ms(o,r,t);if(!s.ok)return{state:o,resolved:s};let i=new Date().toISOString();return{state:Sr(l=>{l.senderBindings[e]={senderKey:e,nodeId:s.peer.nodeId,sinceIso:i,source:n},Rs(l,{...s.peer,lastSeenIso:i})}),resolved:s}}function Ha(e){let t=null;return{state:Sr(r=>{r.senderBindings[e]&&(t=r.senderBindings[e]??null,delete r.senderBindings[e])}),removed:t}}function Ts(e,t){let n=$e(ke()),r=new Date().toISOString();return Sr(o=>{if(Rs(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 Is(e,t){let n=Pe(t,e);return n?n.nodeId===$e(ke()):!1}function ja(e,t){let n=Mt(t).nodeId,r=new Set([n]);for(let s of e.peers)r.add(s.nodeId);return[...r].sort()[0]??n}function pe(e,t){return ja(e,t)===$e(ke())}function gn(e,t,n){let r=$e(ke()),o=["*Computers*",""],s=n?Pe(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,Mt(t));for(let l of e.peers)i.set(l.nodeId,l);let a=[...i.values()].sort((l,d)=>l.label.localeCompare(d.label));if(a.length===0)return o.push("(no peers observed yet \u2014 run /c status from this chat)"),o.join(`
53
+ `);for(let l of a){let d=l.nodeId===r,u=[(s?l.nodeId===s.nodeId:!1)?"your binding":null,d?"you":null].filter(Boolean).join(", "),m=u?` (${u})`:"";o.push(`\u2022 *${l.label}*${m}
54
54
  id \`${l.nodeId}\` \xB7 role ${l.role}
55
55
  seen ${l.lastSeenIso}`)}return o.push(""),o.push(`Updated: ${e.updatedAt}`),o.join(`
56
- `)}function ar(e,t,n){let r=Se(ye()),o=["<b>Computers</b>",""],s=n?Ae(t,n):null;n&&(s?o.push(`Your binding: <code>${W(s.nodeId)}</code> (${W(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,St(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(`
57
- `);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?` (${W(d)})`:"";o.push(`\u2022 <b>${W(l.label)}</b>${m}<br/> id <code>${W(l.nodeId)}</code> \xB7 role ${W(l.role)}<br/> seen ${W(l.lastSeenIso)}`)}return o.push(""),o.push(`Updated: ${W(e.updatedAt)}`),o.join(`
58
- `)}function pr(e,t,n){return an(e,t,n??null).replace(/\*([^*]+)\*/g,"$1").replace(/`([^`]+)`/g,"$1")}function mr(e,t){let n=ye(),r=Se(n),o=j(),s=e.clusterRole,i=ps(e),a=t?Ae(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 ${cr()})`,`host: ${lr.hostname()}`,`clusterRole: ${s}`,u,`peers known: ${o.peers.length}`,`senderBindings (chat): ${Object.keys(o.senderBindings).length}`].filter(Boolean).join(`
59
- `),d=["<b>This computer</b>","",`label: ${W(i)}`,`node id: <code>${W(r)}</code> (full id in ${W(cr())})`,`host: ${W(lr.hostname())}`,`clusterRole: ${W(s)}`,t?W(u):"",`peers known: ${o.peers.length}`,`senderBindings (chat): ${Object.keys(o.senderBindings).length}`].filter(Boolean).join(`
60
- `);return{wa:c,tg:d}}function va(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","","\u2022 /c use <label-or-id> \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 (config default still applies, if any)","\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: ${t}`].join(`
61
- `),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: ${W(t)}`].join(`
62
- `);return z(n,r)}function $a(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 ys(e,t,n){let r=t.trim().split(/\s+/),o=r[0]?.toLowerCase()??"";if(!o||o==="help"){if(!e.clusterEnabled&&o!=="help"){let l=j();return pe(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=j();return pe(a,e)?va(e):null}let s=Se(ye());if(o==="use"||o==="bind"){let a=r.slice(1).join(" ").trim();if(!n){let v=j();return pe(v,e)?p("/c use is only available on chats with a known sender (allowlisted WhatsApp/Telegram)."):null}if(!a){let v=j();return pe(v,e)?p("Usage: /c use <label-or-id>"):null}e.clusterEnabled||Gt(!0);let u=j().senderBindings[n]??null,{state:c,resolved:d}=ur(n,a,"chat");if(!d.ok)return pe(c,e)?$a(a,d):null;let m=d.peer.nodeId===s,g=u?u.nodeId===s:!1;if(!m)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.","",an(c,e,n)].join(`
63
- `),w=[`<b>Bound to ${W(d.peer.label)}</b>`,`id <code>${W(d.peer.nodeId)}</code>`,"","Your messages now route to this machine. Other senders are not affected.","",ar(c,e,n)].join(`
64
- `);return z(y,w)}if(o==="here"||o==="take"){if(!n){let d=j();return pe(d,e)?p("/c here is only available on chats with a known sender (allowlisted WhatsApp/Telegram)."):null}e.clusterEnabled||Gt(!0);let{state:a,resolved:l}=ur(n,s,"chat");if(!l.ok)return pe(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.","",an(a,e,n)].join(`
65
- `),c=["<b>Bound to this machine.</b>","","Your messages now route here. Other senders are not affected.","",ar(a,e,n)].join(`
66
- `);return z(u,c)}if(o==="using"){if(!n){let u=j();return pe(u,e)?p("/c using is only available on chats with a known sender (allowlisted WhatsApp/Telegram)."):null}let a=Ae(e,n);if(a){if(a.nodeId!==s)return null;let d=[...j().peers,St(e)].find(y=>y.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(`
67
- `),g=[`<b>Bound to ${W(d)}</b>`,`id <code>${W(a.nodeId)}</code> \xB7 source ${W(a.source)}`,a.source==="chat"?`since ${W(a.sinceIso)}`:"(from config)"].join(`
68
- `);return z(m,g)}let l=j();return pe(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=j();return pe(m,e)?p("/c unuse is only available on chats with a known sender (allowlisted WhatsApp/Telegram)."):null}let l=j().senderBindings[n]??null,{state:u}=Sa(n);if(l){if(l.nodeId!==s)return null}else if(!pe(u,e))return null;let c=Ae(e,n),d=c?`
56
+ `)}function yr(e,t,n){let r=$e(ke()),o=["<b>Computers</b>",""],s=n?Pe(t,n):null;n&&(s?o.push(`Your binding: <code>${q(s.nodeId)}</code> (${q(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,Mt(t));for(let l of e.peers)i.set(l.nodeId,l);let a=[...i.values()].sort((l,d)=>l.label.localeCompare(d.label));if(a.length===0)return o.push("(no peers observed yet \u2014 run /c status from this chat)"),o.join(`
57
+ `);for(let l of a){let d=l.nodeId===r,u=[(s?l.nodeId===s.nodeId:!1)?"your binding":null,d?"you":null].filter(Boolean).join(", "),m=u?` (${q(u)})`:"";o.push(`\u2022 <b>${q(l.label)}</b>${m}<br/> id <code>${q(l.nodeId)}</code> \xB7 role ${q(l.role)}<br/> seen ${q(l.lastSeenIso)}`)}return o.push(""),o.push(`Updated: ${q(e.updatedAt)}`),o.join(`
58
+ `)}function xr(e,t,n){return gn(e,t,n??null).replace(/\*([^*]+)\*/g,"$1").replace(/`([^`]+)`/g,"$1")}function vr(e,t){let n=ke(),r=$e(n),o=J(),s=e.clusterRole,i=Cs(e),a=t?Pe(e,t):null,l=a?a.nodeId===r:!1,d=t?a?`your binding: ${a.nodeId} (${a.source}${l?", here":""})`:"your binding: (none)":"",c=["*This computer*","",`label: ${i}`,`node id: \`${r}\` (full id in ${br()})`,`host: ${wr.hostname()}`,`clusterRole: ${s}`,d,`peers known: ${o.peers.length}`,`senderBindings (chat): ${Object.keys(o.senderBindings).length}`].filter(Boolean).join(`
59
+ `),u=["<b>This computer</b>","",`label: ${q(i)}`,`node id: <code>${q(r)}</code> (full id in ${q(br())})`,`host: ${q(wr.hostname())}`,`clusterRole: ${q(s)}`,t?q(d):"",`peers known: ${o.peers.length}`,`senderBindings (chat): ${Object.keys(o.senderBindings).length}`].filter(Boolean).join(`
60
+ `);return{wa:c,tg:u}}function Wa(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","","\u2022 /c use <label-or-id> \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 (config default still applies, if any)","\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: ${t}`].join(`
61
+ `),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: ${q(t)}`].join(`
62
+ `);return K(n,r)}function Ua(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 As(e,t,n){let r=t.trim().split(/\s+/),o=r[0]?.toLowerCase()??"";if(!o||o==="help"){if(!e.clusterEnabled&&o!=="help"){let l=J();return pe(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=J();return pe(a,e)?Wa(e):null}let s=$e(ke());if(o==="use"||o==="bind"){let a=r.slice(1).join(" ").trim();if(!n){let S=J();return pe(S,e)?p("/c use is only available on chats with a known sender (allowlisted WhatsApp/Telegram)."):null}if(!a){let S=J();return pe(S,e)?p("Usage: /c use <label-or-id>"):null}e.clusterEnabled||Qt(!0);let d=J().senderBindings[n]??null,{state:c,resolved:u}=kr(n,a,"chat");if(!u.ok)return pe(c,e)?Ua(a,u):null;let m=u.peer.nodeId===s,f=d?d.nodeId===s:!1;if(!m)return null;let w=[`*Bound to ${u.peer.label}*`,`id \`${u.peer.nodeId}\``,"","Your messages now route to this machine. Other senders are not affected.","",gn(c,e,n)].join(`
63
+ `),y=[`<b>Bound to ${q(u.peer.label)}</b>`,`id <code>${q(u.peer.nodeId)}</code>`,"","Your messages now route to this machine. Other senders are not affected.","",yr(c,e,n)].join(`
64
+ `);return K(w,y)}if(o==="here"||o==="take"){if(!n){let u=J();return pe(u,e)?p("/c here is only available on chats with a known sender (allowlisted WhatsApp/Telegram)."):null}e.clusterEnabled||Qt(!0);let{state:a,resolved:l}=kr(n,s,"chat");if(!l.ok)return pe(a,e)?p("Could not bind to this machine."):null;let d=["*Bound to this machine.*","","Your messages now route here. Other senders are not affected.","",gn(a,e,n)].join(`
65
+ `),c=["<b>Bound to this machine.</b>","","Your messages now route here. Other senders are not affected.","",yr(a,e,n)].join(`
66
+ `);return K(d,c)}if(o==="using"){if(!n){let d=J();return pe(d,e)?p("/c using is only available on chats with a known sender (allowlisted WhatsApp/Telegram)."):null}let a=Pe(e,n);if(a){if(a.nodeId!==s)return null;let u=[...J().peers,Mt(e)].find(w=>w.nodeId===a.nodeId)?.label??"(unknown label)",m=[`*Bound to ${u}*`,`id \`${a.nodeId}\` \xB7 source ${a.source}`,a.source==="chat"?`since ${a.sinceIso}`:"(from config)"].join(`
67
+ `),f=[`<b>Bound to ${q(u)}</b>`,`id <code>${q(a.nodeId)}</code> \xB7 source ${q(a.source)}`,a.source==="chat"?`since ${q(a.sinceIso)}`:"(from config)"].join(`
68
+ `);return K(m,f)}let l=J();return pe(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=J();return pe(m,e)?p("/c unuse is only available on chats with a known sender (allowlisted WhatsApp/Telegram)."):null}let l=J().senderBindings[n]??null,{state:d}=Ha(n);if(l){if(l.nodeId!==s)return null}else if(!pe(d,e))return null;let c=Pe(e,n),u=c?`
69
69
  Config default still applies: \`${c.nodeId}\`.`:`
70
- 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=j();return pe(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=mr(e,n);return z(a.wa,a.tg)}if(o==="list"){let a=j(),l=n?Ae(e,n):null;if(l){if(l.nodeId!==s)return null}else if(!pe(a,e))return null;return z(an(a,e,n??null),ar(a,e,n??null))}let i=j();return pe(i,e)?p(`Unknown /c subcommand "${o}". Try /c help`):null}function ws(e,t){return Gt(!0),ur(e,t,"chat")}function re(e){return e.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;")}function fr(e){let t=e.trim();return t?t.length<=8?"(set)":`${t.slice(0,4)}\u2026${t.slice(-4)}`:"(empty)"}function Ca(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 xt(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 bs=new Set(["downloads","omnishData","sessionCwd","processCwd","fixed"]),ln=["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","telegramBotToken","serviceInstallFromChat","updateCheckEnabled","updateCheckIntervalMs","updateCheckPackageName","updateInfoUrl"];function ks(e){return ln.includes(e)}function Ra(e){let t=de(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: ${fr(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}`,"","*Service (chat)*",`serviceInstallFromChat: ${e.serviceInstallFromChat}`,"","*Updates (optional)*",`updateCheckEnabled: ${e.updateCheckEnabled}`,`updateCheckIntervalMs: ${e.updateCheckIntervalMs}`,`updateCheckPackageName: ${e.updateCheckPackageName}`,`updateInfoUrl: ${e.updateInfoUrl?"(set)":"(empty)"}`,"",`File: ${R}`].join(`
71
- `)}function Ma(e){let t=de(e);return["<b>Config</b> (secrets masked)","",`<b>gatewayMode</b> ${re(e.gatewayMode)}`,`<b>commandPrefix</b> ${re(e.commandPrefix)}`,`<b>shell</b> ${re(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: ${re(e.clusterRole)}`,`clusterLabel: ${re(e.clusterLabel||"(hostname)")}`,`clusterSenderBindings: ${Object.keys(e.clusterSenderBindings??{}).length} entries`,"","<b>Telegram</b>",`telegramBotToken: ${re(fr(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>",`${re(e.fileReceiveRootMode)} \xB7 inbox ${re(e.fileInboxSubdir)}`,"","<b>Service (chat)</b>",`serviceInstallFromChat: ${e.serviceInstallFromChat}`,"","<b>Updates (optional)</b>",`updateCheckEnabled: ${e.updateCheckEnabled} \xB7 interval ms: ${e.updateCheckIntervalMs}`,`package: <code>${re(e.updateCheckPackageName)}</code> \xB7 info URL: ${e.updateInfoUrl?"set":"empty"}`,"",`<code>${re(R)}</code>`].join(`
72
- `)}function Ss(e,t){if(!ks(t))return null;let n=t;if(n==="telegramBotToken")return`telegramBotToken: ${fr(de(e))}`;let r=e[n];return`${t}: ${typeof r=="object"?JSON.stringify(r):String(r)}`}function Ta(e,t){let n=Ss(e,t);if(!n)return null;let r=n.split(": ");return r.length<2?re(n):`<b>${re(r[0])}</b> ${re(r.slice(1).join(": "))}`}function Ia(e,t){let n=Ca(t),r=!1,o=!1,s=!1;if(e==="telegramBotToken"){if(!Ve(n))throw new Error("Invalid bot token format (expect digits:secret from BotFather).");return Qe(n),r=!0,s=!0,{reloadSuggested:r,shellWarning:o,tokenSaved:s}}let i=$();switch(e){case"gatewayMode":{let a=Wt(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=xt(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"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=xt(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(!bs.has(a))throw new Error(`fileReceiveRootMode: ${[...bs].join(" | ")}`);i.fileReceiveRootMode=a;break}case"fileReceiveRootPath":i.fileReceiveRootPath=n.trim().slice(0,4096);break;case"recipesAllowDangerousBuiltins":{let a=xt(n);if(a===null)throw new Error("recipesAllowDangerousBuiltins: true or false");i.recipesAllowDangerousBuiltins=a;break}case"serviceInstallFromChat":{let a=xt(n);if(a===null)throw new Error("serviceInstallFromChat: true or false");i.serviceInstallFromChat=a;break}case"updateCheckEnabled":{let a=xt(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 Oe(i),{reloadSuggested:r,shellWarning:o,tokenSaved:s}}async function xs(e,t){let n=e.trim(),r=n.toLowerCase();if(!n||r==="help")return O(go());if(r==="keys"||r==="help keys")return p(["*Configurable keys* (/config set)","",ln.join(", "),"","Examples:","/config set clusterLabel work-laptop",'/config set fileReceiveRootPath "/path/with spaces"',"/config set gatewayMode both"].join(`
73
- `));if(r==="show"){let i=$();return z(Ra(i),Ma(i))}let o=n.match(/^get\s+(\S+)\s*$/i);if(o){let i=o[1],a=$(),l=Ss(a,i);if(!l)return p(`Unknown key "${i}". /config keys`);let u=Ta(a,i);return z(`${l}
70
+ No config default is set; nobody will answer until you /c use <label-or-id>.`;return p(`Cleared your chat binding.${u}`)}if(o==="step-down"||o==="stepdown"){let a=J();return pe(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=vr(e,n);return K(a.wa,a.tg)}if(o==="list"){let a=J(),l=n?Pe(e,n):null;if(l){if(l.nodeId!==s)return null}else if(!pe(a,e))return null;return K(gn(a,e,n??null),yr(a,e,n??null))}let i=J();return pe(i,e)?p(`Unknown /c subcommand "${o}". Try /c help`):null}function Es(e,t){return Qt(!0),kr(e,t,"chat")}function ie(e){return e.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;")}function $r(e){let t=e.trim();return t?t.length<=8?"(set)":`${t.slice(0,4)}\u2026${t.slice(-4)}`:"(empty)"}function Ga(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 Tt(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 Os=new Set(["downloads","omnishData","sessionCwd","processCwd","fixed"]),hn=["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","telegramBotToken","serviceInstallFromChat","updateCheckEnabled","updateCheckIntervalMs","updateCheckPackageName","updateInfoUrl"];function Ls(e){return hn.includes(e)}function za(e){let t=de(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: ${$r(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}`,"","*Service (chat)*",`serviceInstallFromChat: ${e.serviceInstallFromChat}`,"","*Updates (optional)*",`updateCheckEnabled: ${e.updateCheckEnabled}`,`updateCheckIntervalMs: ${e.updateCheckIntervalMs}`,`updateCheckPackageName: ${e.updateCheckPackageName}`,`updateInfoUrl: ${e.updateInfoUrl?"(set)":"(empty)"}`,"",`File: ${T}`].join(`
71
+ `)}function Ja(e){let t=de(e);return["<b>Config</b> (secrets masked)","",`<b>gatewayMode</b> ${ie(e.gatewayMode)}`,`<b>commandPrefix</b> ${ie(e.commandPrefix)}`,`<b>shell</b> ${ie(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: ${ie(e.clusterRole)}`,`clusterLabel: ${ie(e.clusterLabel||"(hostname)")}`,`clusterSenderBindings: ${Object.keys(e.clusterSenderBindings??{}).length} entries`,"","<b>Telegram</b>",`telegramBotToken: ${ie($r(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>",`${ie(e.fileReceiveRootMode)} \xB7 inbox ${ie(e.fileInboxSubdir)}`,"","<b>Service (chat)</b>",`serviceInstallFromChat: ${e.serviceInstallFromChat}`,"","<b>Updates (optional)</b>",`updateCheckEnabled: ${e.updateCheckEnabled} \xB7 interval ms: ${e.updateCheckIntervalMs}`,`package: <code>${ie(e.updateCheckPackageName)}</code> \xB7 info URL: ${e.updateInfoUrl?"set":"empty"}`,"",`<code>${ie(T)}</code>`].join(`
72
+ `)}function Ps(e,t){if(!Ls(t))return null;let n=t;if(n==="telegramBotToken")return`telegramBotToken: ${$r(de(e))}`;let r=e[n];return`${t}: ${typeof r=="object"?JSON.stringify(r):String(r)}`}function qa(e,t){let n=Ps(e,t);if(!n)return null;let r=n.split(": ");return r.length<2?ie(n):`<b>${ie(r[0])}</b> ${ie(r.slice(1).join(": "))}`}function Ka(e,t){let n=Ga(t),r=!1,o=!1,s=!1;if(e==="telegramBotToken"){if(!nt(n))throw new Error("Invalid bot token format (expect digits:secret from BotFather).");return rt(n),r=!0,s=!0,{reloadSuggested:r,shellWarning:o,tokenSaved:s}}let i=C();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=Tt(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 d={};for(let[c,u]of Object.entries(l)){if(typeof u!="string"||!u.trim())throw new Error(`clusterSenderBindings.${c}: value must be a non-empty string`);d[c]=u.trim()}i.clusterSenderBindings=d;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"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=Tt(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(!Os.has(a))throw new Error(`fileReceiveRootMode: ${[...Os].join(" | ")}`);i.fileReceiveRootMode=a;break}case"fileReceiveRootPath":i.fileReceiveRootPath=n.trim().slice(0,4096);break;case"recipesAllowDangerousBuiltins":{let a=Tt(n);if(a===null)throw new Error("recipesAllowDangerousBuiltins: true or false");i.recipesAllowDangerousBuiltins=a;break}case"serviceInstallFromChat":{let a=Tt(n);if(a===null)throw new Error("serviceInstallFromChat: true or false");i.serviceInstallFromChat=a;break}case"updateCheckEnabled":{let a=Tt(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 Ne(i),{reloadSuggested:r,shellWarning:o,tokenSaved:s}}async function Fs(e,t){let n=e.trim(),r=n.toLowerCase();if(!n||r==="help")return P(To());if(r==="keys"||r==="help keys")return p(["*Configurable keys* (/config set)","",hn.join(", "),"","Examples:","/config set clusterLabel work-laptop",'/config set fileReceiveRootPath "/path/with spaces"',"/config set gatewayMode both"].join(`
73
+ `));if(r==="show"){let i=C();return K(za(i),Ja(i))}let o=n.match(/^get\s+(\S+)\s*$/i);if(o){let i=o[1],a=C(),l=Ps(a,i);if(!l)return p(`Unknown key "${i}". /config keys`);let d=qa(a,i);return K(`${l}
74
74
 
75
- ${R}`,`${u}<br/><br/><code>${re(R)}</code>`)}let s=n.match(/^set\s+(\S+)\s+([\s\S]+)$/i);if(s){let i=s[1],a=s[2]??"";if(!ks(i))return p(`Unknown key "${i}". /config keys`);try{let{reloadSuggested:l,shellWarning:u,tokenSaved:c}=Ia(i,a),d="";if(t?.reload&&l){let v=await t.reload();d=v.ok?`
76
- Reload: ${v.summary}`:`
77
- Reload failed: ${v.error}`}else l?d=`
78
- Send /reload while omnish run is active (gatewayMode / Telegram).`:d=`
79
- Send /reload to pick up changes where applicable.`;let m=u?`
80
- \u26A0 shell changed \u2014 affects all commands run via omnish.`:"",g=c?`
81
- telegramBotToken saved (not echoed).`:"",y=`Set *${i}* (saved).${g}${m}${d}`,w=`<b>Set ${re(i)}</b> (saved).${g?"<br/>token saved (not echoed).":""}${u?"<br/>\u26A0 shell path changed.":""}${re(d)}`;return z(y,w)}catch(l){return p(`Error: ${String(l)}`)}}return p("Unknown /config command. Try /config help or /config show")}import Cs from"node:fs";import lt from"node:process";function cn(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(`
75
+ ${T}`,`${d}<br/><br/><code>${ie(T)}</code>`)}let s=n.match(/^set\s+(\S+)\s+([\s\S]+)$/i);if(s){let i=s[1],a=s[2]??"";if(!Ls(i))return p(`Unknown key "${i}". /config keys`);try{let{reloadSuggested:l,shellWarning:d,tokenSaved:c}=Ka(i,a),u="";if(t?.reload&&l){let S=await t.reload();u=S.ok?`
76
+ Reload: ${S.summary}`:`
77
+ Reload failed: ${S.error}`}else l?u=`
78
+ Send /reload while omnish run is active (gatewayMode / Telegram).`:u=`
79
+ Send /reload to pick up changes where applicable.`;let m=d?`
80
+ \u26A0 shell changed \u2014 affects all commands run via omnish.`:"",f=c?`
81
+ telegramBotToken saved (not echoed).`:"",w=`Set *${i}* (saved).${f}${m}${u}`,y=`<b>Set ${ie(i)}</b> (saved).${f?"<br/>token saved (not echoed).":""}${d?"<br/>\u26A0 shell path changed.":""}${ie(u)}`;return K(w,y)}catch(l){return p(`Error: ${String(l)}`)}}return p("Unknown /config command. Try /config help or /config show")}import Bs from"node:fs";import mt from"node:process";function yn(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(`
82
82
  `),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(`
83
83
  `),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(`
84
84
  `);return process.platform==="linux"?o:process.platform==="darwin"?s:process.platform==="win32"?i:[o,"","---","",s].join(`
85
- `)}import{execFileSync as _e}from"node:child_process";import at from"node:fs";import $t from"node:os";import xe from"node:path";import ae from"node:process";function Ge(){let e=ae.execPath,t=ae.argv[1];if(!t||!t.trim())return{nodePath:e,scriptPath:"",omnishHome:A,error:"Cannot resolve gateway entry script (missing argv[1]). Run omnish from its CLI entry."};let n=xe.resolve(t),r=ae.env.OMNISH_HOME?.trim(),o=r?xe.resolve(r):A;return{nodePath:e,scriptPath:n,omnishHome:o}}function vs(e){return/[ "'\\\s]/.test(e)?`"${e.replace(/\\/g,"\\\\").replace(/"/g,'\\"')}"`:e}function Aa(e){return`Environment="OMNISH_HOME=${e.replace(/\\/g,"\\\\").replace(/"/g,'\\"')}"`}function Ea(e){return`[Unit]
85
+ `)}import{execFileSync as We}from"node:child_process";import pt from"node:fs";import At from"node:os";import Ce from"node:path";import le from"node:process";function Ye(){let e=le.execPath,t=le.argv[1];if(!t||!t.trim())return{nodePath:e,scriptPath:"",omnishHome:O,error:"Cannot resolve gateway entry script (missing argv[1]). Run omnish from its CLI entry."};let n=Ce.resolve(t),r=le.env.OMNISH_HOME?.trim(),o=r?Ce.resolve(r):O;return{nodePath:e,scriptPath:n,omnishHome:o}}function Ns(e){return/[ "'\\\s]/.test(e)?`"${e.replace(/\\/g,"\\\\").replace(/"/g,'\\"')}"`:e}function Ya(e){return`Environment="OMNISH_HOME=${e.replace(/\\/g,"\\\\").replace(/"/g,'\\"')}"`}function Va(e){return`[Unit]
86
86
  Description=omnish gateway (WhatsApp/Telegram)
87
87
  After=network-online.target
88
88
  Wants=network-online.target
89
89
 
90
90
  [Service]
91
91
  Type=simple
92
- ExecStart=${`${vs(e.nodePath)} ${vs(e.scriptPath)} run`}
93
- ${Aa(e.omnishHome)}
92
+ ExecStart=${`${Ns(e.nodePath)} ${Ns(e.scriptPath)} run`}
93
+ ${Ya(e.omnishHome)}
94
94
  Restart=on-failure
95
95
  RestartSec=5
96
96
 
97
97
  [Install]
98
98
  WantedBy=default.target
99
- `}function vt(e){return e.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;")}function Oa(e){let t=$t.homedir(),n=xe.join(e.omnishHome,"logs","launchd-stdout.log"),r=xe.join(e.omnishHome,"logs","launchd-stderr.log");P(xe.dirname(n));let o=vt(e.nodePath),s=vt(e.scriptPath),i=vt(e.omnishHome),a=vt(n),l=vt(r);return`<?xml version="1.0" encoding="UTF-8"?>
99
+ `}function It(e){return e.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;")}function Xa(e){let t=At.homedir(),n=Ce.join(e.omnishHome,"logs","launchd-stdout.log"),r=Ce.join(e.omnishHome,"logs","launchd-stderr.log");B(Ce.dirname(n));let o=It(e.nodePath),s=It(e.scriptPath),i=It(e.omnishHome),a=It(n),l=It(r);return`<?xml version="1.0" encoding="UTF-8"?>
100
100
  <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
101
101
  <plist version="1.0">
102
102
  <dict>
@@ -123,104 +123,121 @@ WantedBy=default.target
123
123
  <string>${l}</string>
124
124
  </dict>
125
125
  </plist>
126
- `}function un(){let e=Ge();if(e.error)return{ok:!1,detail:e.error};if(ae.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(ae.platform==="darwin")try{let t=xe.join($t.homedir(),"Library/LaunchAgents/dev.omnish.gateway.plist");P(xe.dirname(t));let n=Oa(e);at.writeFileSync(t,n,{mode:384});let r=typeof ae.getuid=="function"?ae.getuid():null;if(r===null||r<0)return{ok:!1,detail:"Could not read user id for launchctl."};let o=`gui/${r}`;try{_e("launchctl",["bootout",o,t],{stdio:"pipe"})}catch{}return _e("launchctl",["bootstrap",o,t],{stdio:"inherit"}),_e("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(ae.platform==="linux")try{let t=xe.join($t.homedir(),".config","systemd","user");P(t);let n=xe.join(t,"omnish.service"),r=Ea(e);return at.writeFileSync(n,r,{mode:420}),_e("systemctl",["--user","daemon-reload"],{stdio:"inherit"}),_e("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: ${ae.platform}`}}function dn(){if(ae.platform==="win32")return{ok:!0,detail:"Windows: remove the omnish task from Task Scheduler manually on this PC. See /service instructions."};if(ae.platform==="darwin")try{let e=xe.join($t.homedir(),"Library/LaunchAgents/dev.omnish.gateway.plist"),n=`gui/${typeof ae.getuid=="function"?ae.getuid():0}`;if(at.existsSync(e)){try{_e("launchctl",["bootout",n,e],{stdio:"pipe"})}catch{}at.unlinkSync(e)}return{ok:!0,detail:"LaunchAgent dev.omnish.gateway removed (if it was present)."}}catch(e){return{ok:!1,detail:String(e)}}if(ae.platform==="linux")try{let e=xe.join($t.homedir(),".config","systemd","user","omnish.service");try{_e("systemctl",["--user","disable","--now","omnish.service"],{stdio:"pipe"})}catch{}at.existsSync(e)&&at.unlinkSync(e);try{_e("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: ${ae.platform}`}}import $s from"node:fs";var pn=48e3;function mn(e,t){try{if(!$s.existsSync(e))return"(no log file yet \u2014 start with `omnish run -d` or the systemd/LaunchAgent service.)";let n=$s.readFileSync(e),o=(n.length>pn?n.subarray(n.length-pn):n).toString("utf8");return n.length>pn&&(o=`\u2026(truncated to last ${pn} bytes)
126
+ `}function wn(){let e=Ye();if(e.error)return{ok:!1,detail:e.error};if(le.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(le.platform==="darwin")try{let t=Ce.join(At.homedir(),"Library/LaunchAgents/dev.omnish.gateway.plist");B(Ce.dirname(t));let n=Xa(e);pt.writeFileSync(t,n,{mode:384});let r=typeof le.getuid=="function"?le.getuid():null;if(r===null||r<0)return{ok:!1,detail:"Could not read user id for launchctl."};let o=`gui/${r}`;try{We("launchctl",["bootout",o,t],{stdio:"pipe"})}catch{}return We("launchctl",["bootstrap",o,t],{stdio:"inherit"}),We("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(le.platform==="linux")try{let t=Ce.join(At.homedir(),".config","systemd","user");B(t);let n=Ce.join(t,"omnish.service"),r=Va(e);return pt.writeFileSync(n,r,{mode:420}),We("systemctl",["--user","daemon-reload"],{stdio:"inherit"}),We("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: ${le.platform}`}}function bn(){if(le.platform==="win32")return{ok:!0,detail:"Windows: remove the omnish task from Task Scheduler manually on this PC. See /service instructions."};if(le.platform==="darwin")try{let e=Ce.join(At.homedir(),"Library/LaunchAgents/dev.omnish.gateway.plist"),n=`gui/${typeof le.getuid=="function"?le.getuid():0}`;if(pt.existsSync(e)){try{We("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(le.platform==="linux")try{let e=Ce.join(At.homedir(),".config","systemd","user","omnish.service");try{We("systemctl",["--user","disable","--now","omnish.service"],{stdio:"pipe"})}catch{}pt.existsSync(e)&&pt.unlinkSync(e);try{We("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: ${le.platform}`}}import _s from"node:fs";var kn=48e3;function Sn(e,t){try{if(!_s.existsSync(e))return"(no log file yet \u2014 start with `omnish run -d` or the systemd/LaunchAgent service.)";let n=_s.readFileSync(e),o=(n.length>kn?n.subarray(n.length-kn):n).toString("utf8");return n.length>kn&&(o=`\u2026(truncated to last ${kn} bytes)
127
127
  ${o}`),o.split(/\r?\n/).slice(-t).join(`
128
- `).trimEnd()||"(empty)"}catch(n){return`Could not read log: ${String(n)}`}}var Pa=120;function Ee(e){return e.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;")}function Rs(e){let t=e.trim();return t==="/service"||t.startsWith("/service ")?t.slice(8).trim():null}async function Ms(e,t){let n=t.trim().split(/\s+/),r=(n[0]??"").toLowerCase();if(!t.trim()||r==="help")return O(fo(e));if(r==="status"){let s=Ge(),i=(()=>{try{return Cs.existsSync(Z)?`gateway.pid: ${Cs.readFileSync(Z,"utf8").trim()}`:"gateway.pid: (missing)"}catch(m){return`gateway.pid: (read error: ${String(m)})`}})(),a=lt.env.OMNISH_BACKGROUND_GATEWAY==="1"?"This process: background gateway (OMNISH_BACKGROUND_GATEWAY=1).":"This process: foreground gateway session.",l=typeof lt.env.OMNISH_HOME=="string"&&lt.env.OMNISH_HOME.trim()?`OMNISH_HOME env: ${lt.env.OMNISH_HOME.trim()}`:"OMNISH_HOME env: (not set \u2014 using default data dir)",u=s.error?s.error:`Node: ${s.nodePath}
129
- Script: ${s.scriptPath}`,c=["*Service status*","",`platform: ${lt.platform}`,a,l,`data dir: ${A}`,i,`default log: ${he}`,"",u,"",e.serviceInstallFromChat?"Install from chat: enabled (/service install).":"Install from chat: off \u2014 `/config set serviceInstallFromChat true` to allow /service install."].join(`
130
- `),d=["<b>Service status</b>","",`<code>${Ee(lt.platform)}</code>`,`<br/><code>${Ee(a)}</code>`,`<br/><code>${Ee(l)}</code>`,`<br/>data dir: <code>${Ee(A)}</code>`,`<br/><code>${Ee(i)}</code>`,`<br/>default log: <code>${Ee(he)}</code>`,"",`<pre>${Ee(u)}</pre>`,"",e.serviceInstallFromChat?"Install from chat: enabled.":"Install from chat: off \u2014 <code>/config set serviceInstallFromChat true</code>."].join(`
131
- `);return z(c,d)}if(r==="instructions"){let s=Ge();if(s.error)return p(s.error);let i=cn(s);return p(`*Install hints*
128
+ `).trimEnd()||"(empty)"}catch(n){return`Could not read log: ${String(n)}`}}var Qa=120;function Fe(e){return e.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;")}function Ds(e){let t=e.trim();return t==="/service"||t.startsWith("/service ")?t.slice(8).trim():null}async function Hs(e,t){let n=t.trim().split(/\s+/),r=(n[0]??"").toLowerCase();if(!t.trim()||r==="help")return P(Mo(e));if(r==="status"){let s=Ye(),i=(()=>{try{return Bs.existsSync(ne)?`gateway.pid: ${Bs.readFileSync(ne,"utf8").trim()}`:"gateway.pid: (missing)"}catch(m){return`gateway.pid: (read error: ${String(m)})`}})(),a=mt.env.OMNISH_BACKGROUND_GATEWAY==="1"?"This process: background gateway (OMNISH_BACKGROUND_GATEWAY=1).":"This process: foreground gateway session.",l=typeof mt.env.OMNISH_HOME=="string"&&mt.env.OMNISH_HOME.trim()?`OMNISH_HOME env: ${mt.env.OMNISH_HOME.trim()}`:"OMNISH_HOME env: (not set \u2014 using default data dir)",d=s.error?s.error:`Node: ${s.nodePath}
129
+ Script: ${s.scriptPath}`,c=["*Service status*","",`platform: ${mt.platform}`,a,l,`data dir: ${O}`,i,`default log: ${be}`,"",d,"",e.serviceInstallFromChat?"Install from chat: enabled (/service install).":"Install from chat: off \u2014 `/config set serviceInstallFromChat true` to allow /service install."].join(`
130
+ `),u=["<b>Service status</b>","",`<code>${Fe(mt.platform)}</code>`,`<br/><code>${Fe(a)}</code>`,`<br/><code>${Fe(l)}</code>`,`<br/>data dir: <code>${Fe(O)}</code>`,`<br/><code>${Fe(i)}</code>`,`<br/>default log: <code>${Fe(be)}</code>`,"",`<pre>${Fe(d)}</pre>`,"",e.serviceInstallFromChat?"Install from chat: enabled.":"Install from chat: off \u2014 <code>/config set serviceInstallFromChat true</code>."].join(`
131
+ `);return K(c,u)}if(r==="instructions"){let s=Ye();if(s.error)return p(s.error);let i=yn(s);return p(`*Install hints*
132
132
 
133
- ${i}`)}if(r==="logs"){let s=n.length>=2?Number.parseInt(n[1],10):80,i=Number.isFinite(s)&&s>0?Math.min(s,Pa):80,a=mn(he,i),l=[`*Gateway log* (last ${i} lines)
134
- ${he}
133
+ ${i}`)}if(r==="logs"){let s=n.length>=2?Number.parseInt(n[1],10):80,i=Number.isFinite(s)&&s>0?Math.min(s,Qa):80,a=Sn(be,i),l=[`*Gateway log* (last ${i} lines)
134
+ ${be}
135
135
  `,"```",a,"```"].join(`
136
- `),u=`<b>Gateway log</b> (last ${i} lines)<br/><code>${Ee(he)}</code><pre>${Ee(a)}</pre>`;return z(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=un();return p(s.ok?`*Installed*
136
+ `),d=`<b>Gateway log</b> (last ${i} lines)<br/><code>${Fe(be)}</code><pre>${Fe(a)}</pre>`;return K(l,d)}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=wn();return p(s.ok?`*Installed*
137
137
  ${s.detail}`:`*Install failed*
138
- ${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=dn();return p(`*Uninstall*
139
- ${s.detail}`)}return p("Unknown /service command. Try /service help")}function Ts(e){let t=e.trim().match(/^(\d+)\.(\d+)\.(\d+)/);return t?[Number(t[1]),Number(t[2]),Number(t[3])]:null}function Is(e,t){let n=Ts(e),r=Ts(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 La="https://registry.npmjs.org",As=null,gr=0;function Ct(){return As}function Fa(e){let t=e.trim();return t.length<1||t.length>214?!1:!/\s/.test(t)}function Es(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 Na(e){let t=e.trim(),n=`${La}/${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 _a(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=Es(u)),{message:a,link:l}}catch(t){return{error:`updateInfoUrl: ${String(t)}`}}}function Ba(e){return!Number.isFinite(e)||e<36e5?36e5:e>6048e5?6048e5:Math.floor(e)}async function Rt(e,t){let n=Fa(t.updateCheckPackageName)?t.updateCheckPackageName.trim():"omnish",r=await Na(n),o="version"in r?r.version:null,s="error"in r?r.error:null,i=!1;o&&!s&&(i=Is(e,o)<0);let a=null,l=null,u=null,c=Es(t.updateInfoUrl);if(c){let m=await _a(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 As=d,d}function fn(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 Os(e){let t=!1,n=async()=>{if(t)return;let s=e.getConfig();if(!s.updateCheckEnabled)return;let i=Ba(s.updateCheckIntervalMs),a=Date.now();if(!(gr!==0&&a-gr<i)){gr=a;try{let l=await Rt(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 Da from"node:fs";import Ps from"node:path";import{fileURLToPath as Ha}from"node:url";var gn=null;function Be(){if(gn!==null)return gn;let e=Ps.dirname(Ha(import.meta.url)),t=Ps.join(e,"..","package.json"),n=Da.readFileSync(t,"utf8"),r=JSON.parse(n);return gn=typeof r.version=="string"&&r.version.trim()?r.version.trim():"0.0.0",gn}function Mt(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 ja(e,t,n){let r=e;for(let o=0;o<14;o++){let s=Mt(r,t,n),i=new Date(s).getDay();if(i>=1&&i<=5)return s;r=s}return Mt(r,t,n)}function Wa(e,t,n,r){let o=e;for(let s=0;s<370;s++){let i=Mt(o,n,r);if(new Date(i).getDay()===t)return i;o=i}return Mt(o,n,r)}function Ua(e,t){return e.kind==="ondemand"?Number.POSITIVE_INFINITY:e.kind==="daily"?Mt(t,e.hour,e.minute):e.kind==="weekdays"?ja(t,e.hour,e.minute):Wa(t,e.weekday,e.hour,e.minute)}function Ls(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=Ua(e,a);if(l>r)break;i.push(l),a=l}return i}var Ga={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 hr(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 yr(e){if(e.length===0)return{ok:!1,error:"Missing schedule (try: ondemand, 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=hr(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=hr(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==="weekly"){if(e.length!==3)return{ok:!1,error:"Usage: weekly <mon|tue|\u2026|sun> HH:MM"};let n=e[1].toLowerCase(),r=Ga[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=hr(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 Tt(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"weekly":return`weekly ${["sun","mon","tue","wed","thu","fri","sat"][e.weekday]??e.weekday} ${n(e.hour,e.minute)}`}}import Fs from"node:crypto";import It from"node:fs";import wr from"node:os";import ct from"node:path";var za=/^[a-zA-Z0-9][a-zA-Z0-9_-]{0,31}$/;function Ns(e){let t=e.trim().toLowerCase();return za.test(t)?{ok:!0,name:t}:{ok:!1,error:"Name must be alphanumeric with _ or -, max 32 chars."}}function At(e,t){let n=e.trim();return n===""||n==="."?ct.resolve(t):n==="~"?wr.homedir():n.startsWith("~/")||n.startsWith("~\\")?ct.join(wr.homedir(),n.slice(2)):ct.isAbsolute(n)?n:ct.resolve(t,n)}function br(e){return ct.join(wr.homedir(),"Cowork",e)}function Ja(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:br(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 y=t.schedule,w=typeof y.kind=="string"?y.kind:"";if(w==="ondemand")d={kind:"ondemand"};else if(w==="daily"){let v=Number(y.hour),E=Number(y.minute);Number.isFinite(v)&&Number.isFinite(E)&&(d={kind:"daily",hour:v,minute:E})}else if(w==="weekdays"){let v=Number(y.hour),E=Number(y.minute);Number.isFinite(v)&&Number.isFinite(E)&&(d={kind:"weekdays",hour:v,minute:E})}else if(w==="weekly"){let v=Number(y.hour),E=Number(y.minute),U=Number(y.weekday);Number.isFinite(v)&&Number.isFinite(E)&&Number.isInteger(U)&&(d={kind:"weekly",weekday:U,hour:v,minute:E})}}let m=null;typeof t.lastCompletedSlotMs=="number"&&Number.isFinite(t.lastCompletedSlotMs)&&(m=t.lastCompletedSlotMs);let g=typeof t.createdAtMs=="number"&&Number.isFinite(t.createdAtMs)?t.createdAtMs:Date.now();return{id:n,name:r,ownerPeerKey:o,command:s,cwd:i,outputDir:a,schedule:d,enabled:l,notify:c,lastCompletedSlotMs:m,createdAtMs:g}}function we(){try{let e=It.readFileSync(In,"utf8"),t=JSON.parse(e);if(!t||!Array.isArray(t.tasks))return[];let n=[];for(let r of t.tasks){let o=Ja(r);o&&n.push(o)}return n}catch{return[]}}function ve(e){P(je);let t={tasks:e},n=ct.join(je,`.tasks.${process.pid}.${Fs.randomBytes(4).toString("hex")}.tmp`);It.writeFileSync(n,JSON.stringify(t,null,2)+`
140
- `,{mode:384}),It.renameSync(n,In)}function ut(e,t,n){let r=t.trim().toLowerCase();return e.find(o=>o.name===r&&o.ownerPeerKey===n)}function _s(){return Fs.randomBytes(4).toString("hex")}function Bs(e){P(je),It.writeFileSync(An,JSON.stringify(e,null,2)+`
141
- `,{mode:384})}function Ds(e){let t=Hs();t.push(e),Bs(t)}function Hs(){try{let e=It.readFileSync(An,"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 js(){let e=Hs();if(e.length===0)return null;let[t,...n]=e;return Bs(n),t??null}function qa(){return p(["Cowork \u2014 scheduled shell tasks (gateway must be running).","","add <name> <schedule> -- <command\u2026>"," schedule: ondemand | 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","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(`
142
- `))}function Ka(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 Us(e,t){let n=e.trim();if(!n||/^help$/i.test(n))return qa();let r=n.split(/\s+/)[0].toLowerCase();if(/^list$/i.test(r)){let u=we().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`\u2022 ${d.name} ${Tt(d.schedule)} notify=${d.notify}${m}`});return p(c.join(`
143
- `))}let o=n.match(/^show\s+(\S+)\s*$/i);if(o){let u=o[1],c=ut(we(),u,t);if(!c)return p(`Unknown task "${u}". /cowork list`);let d=[`name: ${c.name}`,`id: ${c.id}`,`schedule: ${Tt(c.schedule)}`,`enabled: ${c.enabled}`,`notify: ${c.notify}`,`cwd: ${c.cwd||"(session cwd)"}`,`out: ${c.outputDir}`,`cmd: ${c.command}`,`last slot: ${c.lastCompletedSlotMs?new Date(c.lastCompletedSlotMs).toLocaleString():"(never)"}`];return p(d.join(`
144
- `))}if(/^add$/i.test(r)){let u=n.slice(3).trim(),c=Ka(u);if("error"in c)return p(c.error);let d=Ns(c.name);if(!d.ok)return p(d.error);let m=yr(c.scheduleWords);if(!m.ok)return p(m.error);let g=we();if(ut(g,d.name,t))return p(`Task "${d.name}" already exists. Remove it first or pick another name.`);let y=B(t),w=br(d.name),v={id:_s(),name:d.name,ownerPeerKey:t,command:c.command,cwd:"",outputDir:w,schedule:m.schedule,enabled:!0,notify:"self",lastCompletedSlotMs:null,createdAtMs:Date.now()};return g.push(v),ve(g),p([`Saved cowork task "${d.name}" (${Tt(m.schedule)}).`,`Output: ${w}`,`Notify: self \u2014 change with /cowork set ${d.name} notify wa|tg|all|none`].join(`
145
- `))}let s=n.match(/^run\s+(\S+)\s*$/i);if(s){let u=s[1].toLowerCase(),c=ut(we(),u,t);return c?c.enabled?(Ds({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=we(),d=c.findIndex(m=>m.name===u.toLowerCase()&&m.ownerPeerKey===t);return d===-1?p(`Unknown task "${u}".`):(c.splice(d,1),ve(c),p(`Removed cowork task "${u.toLowerCase()}".`))}let a=n.match(/^enable\s+(\S+)\s*$/i);if(a)return Ws(a[1],t,!0);let l=n.match(/^disable\s+(\S+)\s*$/i);return l?Ws(l[1],t,!1):/^set$/i.test(r)?Va(n.slice(3).trim(),t):p("Unknown /cowork command. Try /cowork help")}function Ws(e,t,n){let r=we(),o=ut(r,e,t);return o?(o.enabled=n,ve(r),p(`Cowork "${o.name}" ${n?"enabled":"disabled"}.`)):p(`Unknown task "${e}".`)}function Ya(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 Va(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 <self|wa|tg|all|none>");let r=n[1],o=n[2].trim(),s=we(),i=ut(s,r,t);if(!i)return p(`Unknown task "${r}".`);if(o.toLowerCase().startsWith("cmd ")){let a=o.slice(4).trim(),l=a.indexOf(" -- ");if(l===-1)return p("Usage: /cowork set <name> cmd -- <command\u2026>");let u=a.slice(l+4).trim();return u?(i.command=u,ve(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=yr(a);return l.ok?(i.schedule=l.schedule,ve(s),p(`Schedule for "${i.name}": ${Tt(l.schedule)}`)):p(l.error)}if(o.toLowerCase().startsWith("out ")){let a=o.slice(4).trim(),l=B(t);return i.outputDir=At(a,l.cwd),ve(s),p(`outputDir: ${i.outputDir}`)}if(o.toLowerCase().startsWith("cwd ")){let a=o.slice(4).trim(),l=B(t);return i.cwd=a?At(a,l.cwd):"",ve(s),p(`cwd: ${i.cwd||"(session cwd at run time)"}`)}if(o.toLowerCase().startsWith("notify ")){let a=o.slice(7).trim(),l=Ya(a);return l?(i.notify=l,ve(s),p(`notify: ${l}`)):p("notify must be self, wa, tg, all, or none.")}return p("Unknown set field. Try: cmd, schedule, out, cwd, notify")}function kr(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 S(e){return{kind:"text",body:e}}function Za(e,t){return`${e}:${t}`}function Xa(e,t){return`${e}:apps:${t}`}async function el(e,t){let n=e.trim();if(n===""||/^status$/i.test(n)||/^show$/i.test(n)||/^get$/i.test(n)){let a=$();return ho({gatewayMode:a.gatewayMode,authPresent:Ze(),tokenSet:!!de(a),allowN:a.allowFrom.length,tgAllowN:a.telegramAllowFrom.length,updateBrief:fn(Ct())})}if(/^help$/i.test(n))return O(Hn());let r=Wt(n);if(!r)return Oo();Ut(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 xo(o.gatewayMode,s,i)}function tl(e){let t=e.trim();if(!Ve(t))return So();let n=Qe(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.'),ko(r)}async function Gs(e,t,n){let r=B(t),o=no(n);if(o!==null){let a=ro(r.cwd,o),l=oo(a);return l.ok?(Jt(t,a),p(`cwd: ${a}`)):p(`cd: ${l.error}`)}let s=await sn(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(`
146
- `))}async function ze(e,t,n,r,o,s,i,a,l=null,u=!1){let c=s.text.trim(),d=s.peerKey,m=c.match(/^!!\s*(start|stop)\s*$/i);if(m)return m[1].toLowerCase()==="start"?(o.set(d,!0),S(Io())):(o.set(d,!1),S(p("Free shell mode off.")));if(c.startsWith(e.commandPrefix)){let y=c.slice(e.commandPrefix.length).trim();if(!y)return S(p(`Send ${e.commandPrefix}<command> or /help`));if(!u&&Yo(y)){let w=ot(d,y);if(w!==void 0)return await ze(e,t,n,r,o,{...s,text:w},i,a,l,!0)}return S(await Gs(e,d,y))}if(/^\/help\s+files$/i.test(c.trim()))return S(Un());if(c==="/help"||c==="help")return S(O(rt(e)));if(c.startsWith("/")){if(/^\/files(?:\s+help)?$/i.test(c.trim()))return S(Un());let y=c.match(/^\/receive\b(?:\s+(\S+))?$/i);if(y){let f=(y[1]??"status").toLowerCase(),k=new Set(["here","cwd","session","dir"]),x=new Set(["default","global","reset"]);if(k.has(f)){Ln(d,"sessionCwd");let J=B(d).cwd;return S(p(`Inbound files will save under this chat\u2019s session folder:
147
- ${J}
138
+ ${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=bn();return p(`*Uninstall*
139
+ ${s.detail}`)}return p("Unknown /service command. Try /service help")}function js(e){let t=e.trim().match(/^(\d+)\.(\d+)\.(\d+)/);return t?[Number(t[1]),Number(t[2]),Number(t[3])]:null}function Ws(e,t){let n=js(e),r=js(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 Za="https://registry.npmjs.org",Us=null,Cr=0;function Et(){return Us}function el(e){let t=e.trim();return t.length<1||t.length>214?!1:!/\s/.test(t)}function Gs(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 tl(e){let t=e.trim(),n=`${Za}/${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 nl(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,d=i.link??i.url;return typeof d=="string"&&d.trim()&&(l=Gs(d)),{message:a,link:l}}catch(t){return{error:`updateInfoUrl: ${String(t)}`}}}function rl(e){return!Number.isFinite(e)||e<36e5?36e5:e>6048e5?6048e5:Math.floor(e)}async function Ot(e,t){let n=el(t.updateCheckPackageName)?t.updateCheckPackageName.trim():"omnish",r=await tl(n),o="version"in r?r.version:null,s="error"in r?r.error:null,i=!1;o&&!s&&(i=Ws(e,o)<0);let a=null,l=null,d=null,c=Gs(t.updateInfoUrl);if(c){let m=await nl(c);"error"in m?d=m.error:(a=m.message,l=m.link)}let u={runningVersion:e,checkedAtIso:new Date().toISOString(),registryPackage:n,registryLatest:o,registryError:s,updateAvailable:i,infoMessage:a,infoLink:l,infoError:d};return Us=u,u}function xn(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 zs(e){let t=!1,n=async()=>{if(t)return;let s=e.getConfig();if(!s.updateCheckEnabled)return;let i=rl(s.updateCheckIntervalMs),a=Date.now();if(!(Cr!==0&&a-Cr<i)){Cr=a;try{let l=await Ot(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 ol from"node:fs";import Js from"node:path";import{fileURLToPath as sl}from"node:url";var vn=null;function Ue(){if(vn!==null)return vn;let e=Js.dirname(sl(import.meta.url)),t=Js.join(e,"..","package.json"),n=ol.readFileSync(t,"utf8"),r=JSON.parse(n);return vn=typeof r.version=="string"&&r.version.trim()?r.version.trim():"0.0.0",vn}function Lt(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 il(e,t,n){let r=e;for(let o=0;o<14;o++){let s=Lt(r,t,n),i=new Date(s).getDay();if(i>=1&&i<=5)return s;r=s}return Lt(r,t,n)}function al(e,t,n,r){let o=e;for(let s=0;s<370;s++){let i=Lt(o,n,r);if(new Date(i).getDay()===t)return i;o=i}return Lt(o,n,r)}function ll(e,t){return e.kind==="ondemand"?Number.POSITIVE_INFINITY:e.kind==="daily"?Lt(t,e.hour,e.minute):e.kind==="weekdays"?il(t,e.hour,e.minute):al(t,e.weekday,e.hour,e.minute)}function qs(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=ll(e,a);if(l>r)break;i.push(l),a=l}return i}var cl={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 Rr(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 Mr(e){if(e.length===0)return{ok:!1,error:"Missing schedule (try: ondemand, 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=Rr(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=Rr(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==="weekly"){if(e.length!==3)return{ok:!1,error:"Usage: weekly <mon|tue|\u2026|sun> HH:MM"};let n=e[1].toLowerCase(),r=cl[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=Rr(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 Pt(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"weekly":return`weekly ${["sun","mon","tue","wed","thu","fri","sat"][e.weekday]??e.weekday} ${n(e.hour,e.minute)}`}}import pl from"better-sqlite3";import ul from"pino";function $n(){return process.env.OMNISH_VERBOSE==="1"||process.env.WHATSVERBOSE==="1"}var dl=$n()?"info":"silent",R=ul({level:dl,base:{app:"omnish"}});function Ks(){return R.child({module:"baileys"})}var Ys=1,Ge=null;function Vs(){B(Ae);let e=new pl(Qr);e.pragma("journal_mode = WAL"),e.exec(`
140
+ CREATE TABLE IF NOT EXISTS cowork_meta (
141
+ key TEXT PRIMARY KEY,
142
+ value TEXT NOT NULL
143
+ );
144
+ CREATE TABLE IF NOT EXISTS cowork_slot_completion (
145
+ task_id TEXT NOT NULL,
146
+ slot_ms INTEGER NOT NULL,
147
+ kind TEXT NOT NULL,
148
+ completed_at_ms INTEGER NOT NULL,
149
+ log_path TEXT,
150
+ PRIMARY KEY (task_id, slot_ms)
151
+ );
152
+ CREATE INDEX IF NOT EXISTS idx_cowork_completion_task ON cowork_slot_completion(task_id);
153
+ `);let t=e.prepare("SELECT value FROM cowork_meta WHERE key = 'schema_version'").get();return(t?Number(t.value):0)<Ys&&e.prepare("INSERT OR REPLACE INTO cowork_meta (key, value) VALUES ('schema_version', ?)").run(String(Ys)),e}function Ft(e){Ge||(Ge=Vs()),fl(e)}function Xs(){if(Ge){try{Ge.close()}catch{}Ge=null}}function Tr(){return Ge||(Ge=Vs()),Ge}function ml(e){let n=Tr().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 Cn(e){let t=ml(e.id);return t??e.lastCompletedSlotMs}function Rn(e,t){if(t.length===0)return;let n=Tr(),r=n.prepare(`
154
+ INSERT INTO cowork_slot_completion (task_id, slot_ms, kind, completed_at_ms, log_path)
155
+ VALUES (?, ?, ?, ?, ?)
156
+ `);n.transaction(()=>{for(let s of t)r.run(e,s.slotMs,s.kind,s.completedAtMs,s.logPath)})()}function fl(e){let n=Tr().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{Rn(o.id,[{slotMs:o.lastCompletedSlotMs,kind:"migrated",logPath:null,completedAtMs:r}]),R.info({taskId:o.id,slotMs:o.lastCompletedSlotMs},"cowork seeded completion from tasks.json")}catch(i){R.warn({err:String(i),taskId:o.id},"cowork seed from tasks.json failed")}}import Qs from"node:crypto";import Nt from"node:fs";import Ir from"node:os";import ft from"node:path";var gl=/^[a-zA-Z0-9][a-zA-Z0-9_-]{0,31}$/;function Zs(e){let t=e.trim().toLowerCase();return gl.test(t)?{ok:!0,name:t}:{ok:!1,error:"Name must be alphanumeric with _ or -, max 32 chars."}}function gt(e,t){let n=e.trim();return n===""||n==="."?ft.resolve(t):n==="~"?Ir.homedir():n.startsWith("~/")||n.startsWith("~\\")?ft.join(Ir.homedir(),n.slice(2)):ft.isAbsolute(n)?n:ft.resolve(t,n)}function Er(e){return ft.join(Ir.homedir(),"Cowork",e)}function hl(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:Er(r),l=typeof t.enabled=="boolean"?t.enabled:!0,d=typeof t.notify=="string"?t.notify.toLowerCase():"self",c=d==="wa"||d==="whatsapp"?"wa":d==="tg"||d==="telegram"?"tg":d==="all"?"all":d==="none"?"none":"self",u={kind:"ondemand"};if(t.schedule&&typeof t.schedule=="object"){let S=t.schedule,F=typeof S.kind=="string"?S.kind:"";if(F==="ondemand")u={kind:"ondemand"};else if(F==="daily"){let I=Number(S.hour),L=Number(S.minute);Number.isFinite(I)&&Number.isFinite(L)&&(u={kind:"daily",hour:I,minute:L})}else if(F==="weekdays"){let I=Number(S.hour),L=Number(S.minute);Number.isFinite(I)&&Number.isFinite(L)&&(u={kind:"weekdays",hour:I,minute:L})}else if(F==="weekly"){let I=Number(S.hour),L=Number(S.minute),Y=Number(S.weekday);Number.isFinite(I)&&Number.isFinite(L)&&Number.isInteger(Y)&&(u={kind:"weekly",weekday:Y,hour:I,minute:L})}}let m=null;typeof t.lastCompletedSlotMs=="number"&&Number.isFinite(t.lastCompletedSlotMs)&&(m=t.lastCompletedSlotMs);let f=typeof t.createdAtMs=="number"&&Number.isFinite(t.createdAtMs)?t.createdAtMs:Date.now(),w=typeof t.attachLog=="boolean"?t.attachLog:!1,y=Array.isArray(t.attachFiles)?t.attachFiles.filter(S=>typeof S=="string"&&S.trim().length>0):[];return{id:n,name:r,ownerPeerKey:o,command:s,cwd:i,outputDir:a,schedule:u,enabled:l,notify:c,attachLog:w,attachFiles:y,lastCompletedSlotMs:m,createdAtMs:f}}function he(){try{let e=Nt.readFileSync(Dn,"utf8"),t=JSON.parse(e);if(!t||!Array.isArray(t.tasks))return[];let n=[];for(let r of t.tasks){let o=hl(r);o&&n.push(o)}return n}catch{return[]}}function Se(e){B(Ae);let t={tasks:e},n=ft.join(Ae,`.tasks.${process.pid}.${Qs.randomBytes(4).toString("hex")}.tmp`);Nt.writeFileSync(n,JSON.stringify(t,null,2)+`
157
+ `,{mode:384}),Nt.renameSync(n,Dn)}function ht(e,t,n){let r=t.trim().toLowerCase();return e.find(o=>o.name===r&&o.ownerPeerKey===n)}function ei(){return Qs.randomBytes(4).toString("hex")}function ti(e){B(Ae),Nt.writeFileSync(Hn,JSON.stringify(e,null,2)+`
158
+ `,{mode:384})}function ni(e){let t=Ar();t.push(e),ti(t)}function Ar(){try{let e=Nt.readFileSync(Hn,"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 ri(e){if(e<=0)return{batch:[],remainingAfter:Ar().length};let t=Ar();if(t.length===0)return{batch:[],remainingAfter:0};let n=t.slice(0,e),r=t.slice(e);return ti(r),{batch:n,remainingAfter:r.length}}function yl(){return p(["Cowork \u2014 scheduled shell tasks (gateway must be running).","","add <name> <schedule> -- <command\u2026>"," schedule: ondemand | 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(`
159
+ `))}function oi(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 wl(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 ii(e,t){let n=e.trim();if(!n||/^help$/i.test(n))return yl();let r=n.split(/\s+/)[0].toLowerCase();if(/^list$/i.test(r)){let d=he().filter(u=>u.ownerPeerKey===t);if(d.length===0)return p("(no cowork tasks for this chat)");let c=d.map(u=>{let m=u.enabled?"":" (disabled)";return`\u2022 ${u.name} ${Pt(u.schedule)} notify=${u.notify}${m}`});return p(c.join(`
160
+ `))}let o=n.match(/^show\s+(\S+)\s*$/i);if(o){let d=o[1],c=he();Ft(c);let u=ht(c,d,t);if(!u)return p(`Unknown task "${d}". /cowork list`);let m=Cn(u),f=[`name: ${u.name}`,`id: ${u.id}`,`schedule: ${Pt(u.schedule)}`,`enabled: ${u.enabled}`,`notify: ${u.notify}`,`attachLog: ${u.attachLog}`,`files: ${u.attachFiles.length?u.attachFiles.join(", "):"(none)"}`,`cwd: ${u.cwd||"(session cwd)"}`,`out: ${u.outputDir}`,`cmd: ${u.command}`,`last slot: ${m?new Date(m).toLocaleString():"(never)"}`];return p(f.join(`
161
+ `))}if(/^add$/i.test(r)){let d=n.slice(3).trim(),c=wl(d);if("error"in c)return p(c.error);let u=Zs(c.name);if(!u.ok)return p(u.error);let m=Mr(c.scheduleWords);if(!m.ok)return p(m.error);let f=he();if(ht(f,u.name,t))return p(`Task "${u.name}" already exists. Remove it first or pick another name.`);let w=j(t),y=Er(u.name),S={id:ei(),name:u.name,ownerPeerKey:t,command:c.command,cwd:"",outputDir:y,schedule:m.schedule,enabled:!0,notify:"self",attachLog:!1,attachFiles:[],lastCompletedSlotMs:null,createdAtMs:Date.now()};return f.push(S),Se(f),p([`Saved cowork task "${u.name}" (${Pt(m.schedule)}).`,`Output: ${y}`,`Notify: self \u2014 change with /cowork set ${u.name} notify wa|tg|all|none`].join(`
162
+ `))}let s=n.match(/^run\s+(\S+)\s*$/i);if(s){let d=s[1].toLowerCase(),c=ht(he(),d,t);return c?c.enabled?(ni({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 d=i[1],c=he(),u=c.findIndex(m=>m.name===d.toLowerCase()&&m.ownerPeerKey===t);return u===-1?p(`Unknown task "${d}".`):(c.splice(u,1),Se(c),p(`Removed cowork task "${d.toLowerCase()}".`))}let a=n.match(/^enable\s+(\S+)\s*$/i);if(a)return si(a[1],t,!0);let l=n.match(/^disable\s+(\S+)\s*$/i);return l?si(l[1],t,!1):/^set$/i.test(r)?Sl(n.slice(3).trim(),t):p("Unknown /cowork command. Try /cowork help")}function si(e,t,n){let r=he(),o=ht(r,e,t);return o?(o.enabled=n,Se(r),p(`Cowork "${o.name}" ${n?"enabled":"disabled"}.`)):p(`Unknown task "${e}".`)}function bl(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 kl(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 Sl(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=he(),i=ht(s,r,t);if(!i)return p(`Unknown task "${r}".`);if(o.toLowerCase().startsWith("cmd ")){let a=o.slice(4).trim(),l=a.indexOf(" -- ");if(l===-1)return p("Usage: /cowork set <name> cmd -- <command\u2026>");let d=a.slice(l+4).trim();return d?(i.command=d,Se(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=Mr(a);return l.ok?(i.schedule=l.schedule,Se(s),p(`Schedule for "${i.name}": ${Pt(l.schedule)}`)):p(l.error)}if(o.toLowerCase().startsWith("out ")){let a=o.slice(4).trim(),l=j(t);return i.outputDir=gt(a,l.cwd),Se(s),p(`outputDir: ${i.outputDir}`)}if(o.toLowerCase().startsWith("cwd ")){let a=o.slice(4).trim(),l=j(t);return i.cwd=a?gt(a,l.cwd):"",Se(s),p(`cwd: ${i.cwd||"(session cwd at run time)"}`)}if(o.toLowerCase().startsWith("notify ")){let a=o.slice(7).trim(),l=bl(a);return l?(i.notify=l,Se(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=oi(a);if(l.length!==1)return p("Usage: /cowork set <name> attach on|off");let d=kl(l[0]);return d===null?p("attach must be on or off"):(i.attachLog=d,Se(s),p(`attachLog: ${d}`))}if(o.toLowerCase().startsWith("files ")){let a=o.slice(6).trim(),l=oi(a);return l.length===1&&l[0].toLowerCase()==="clear"?(i.attachFiles=[],Se(s),p("files: (cleared)")):l.length===0?p("Usage: /cowork set <name> files clear | <pattern \u2026> \u2014 quote paths with spaces"):(i.attachFiles=l,Se(s),p(`files: ${i.attachFiles.join(", ")}`))}return p("Unknown set field. Try: cmd, schedule, out, cwd, notify, attach, files")}function Or(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 x(e){return{kind:"text",body:e}}function vl(e,t){return`${e}:${t}`}function $l(e,t){return`${e}:apps:${t}`}async function Cl(e,t){let n=e.trim();if(n===""||/^status$/i.test(n)||/^show$/i.test(n)||/^get$/i.test(n)){let a=C();return Io({gatewayMode:a.gatewayMode,authPresent:ot(),tokenSet:!!de(a),allowN:a.allowFrom.length,tgAllowN:a.telegramAllowFrom.length,updateBrief:xn(Et())})}if(/^help$/i.test(n))return P(Vn());let r=Vt(n);if(!r)return zo();Xt(r);let o=C(),s,i=!1;if(t?.reload){let a=await t.reload();s=a.ok?a.summary:`Reload failed: ${a.error}`}else i=!0;return Fo(o.gatewayMode,s,i)}function Rl(e){let t=e.trim();if(!nt(t))return Po();let n=rt(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.'),Lo(r)}async function ai(e,t,n){let r=j(t),o=ho(n);if(o!==null){let a=yo(r.cwd,o),l=wo(a);return l.ok?(en(t,a),p(`cwd: ${a}`)):p(`cd: ${l.error}`)}let s=await fn(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(`
163
+ `))}async function Ve(e,t,n,r,o,s,i,a,l=null,d=!1){let c=s.text.trim(),u=s.peerKey,m=c.match(/^!!\s*(start|stop)\s*$/i);if(m)return m[1].toLowerCase()==="start"?(o.set(u,!0),x(Wo())):(o.set(u,!1),x(p("Free shell mode off.")));if(c.startsWith(e.commandPrefix)){let w=c.slice(e.commandPrefix.length).trim();if(!w)return x(p(`Send ${e.commandPrefix}<command> or /help`));if(!d&&ls(w)){let y=ct(u,w);if(y!==void 0)return await Ve(e,t,n,r,o,{...s,text:y},i,a,l,!0)}return x(await ai(e,u,w))}if(/^\/help\s+files$/i.test(c.trim()))return x(Zn());if(c==="/help"||c==="help")return x(P(lt(e)));if(c.startsWith("/")){if(/^\/files(?:\s+help)?$/i.test(c.trim()))return x(Zn());let w=c.match(/^\/receive\b(?:\s+(\S+))?$/i);if(w){let g=(w[1]??"status").toLowerCase(),b=new Set(["here","cwd","session","dir"]),v=new Set(["default","global","reset"]);if(b.has(g)){Gn(u,"sessionCwd");let N=j(u).cwd;return x(p(`Inbound files will save under this chat\u2019s session folder:
164
+ ${N}
148
165
  (layout: \u2026/<peer>/<date>/<file> under that root).
149
166
 
150
- Change folder with ${e.commandPrefix}cd \u2026 Send /receive default to use the server config again.`))}return x.has(f)?(Ln(d,"default"),S(p("Per-chat inbound folder cleared. Uploads now follow fileReceiveRootMode in config.json (/files)."))):S(f==="help"?Gn():f==="status"?Ro(e,d):Gn())}if(c==="/reload"||c==="/restart"){if(!a?.reload)return S(p("Reload is only available while the omnish gateway (omnish run) is running."));let f=await a.reload();return S(p(f.ok?f.summary:`Reload failed: ${f.error}`))}if(c==="/updates"||c.startsWith("/updates ")){let f=c.slice(8).trim().toLowerCase();if(f==="cached"||f==="last"){let x=Ct();return S(x?bt(x):p("No update snapshot yet. Send /updates (live check) once, or enable updateCheckEnabled and wait for the scheduled check."))}let k=await Rt(Be(),$());return S(bt(k))}let w=c.match(/^\/security(?:\s+(\S+))?\s*$/i);if(w){let f=(w[1]??"").toLowerCase(),k=$(),x=nt(k);return S(f==="help"||f==="?"?O(jo()):f==="summary"||f==="brief"?p(po(x,"Send /security for the full report.")):f==="tips"?O(Ho()):f===""||f==="full"||f==="report"?Do(x):p("Unknown /security subcommand. Try /security, /security summary, /security tips, or /security help"))}let v=c.match(/^\/(gateway|gw|mode)\b(?:\s+(.*))?$/i);if(v){let f=(v[2]??"").trim();return S(await el(f,a))}if(c==="/config"||c.startsWith("/config ")){let f=c.slice(7).trim();return S(await xs(f,a))}let E=Rs(c);if(E!==null)return S(await Ms(e,E));let U=kr(c);if(U!==null){let f=ys(e,U,l);return f===null?null:S(f)}let se=c.match(/^\/(send|file)\b(?:\s+([\s\S]+))?$/i);if(se){let f=(se[2]??"").trim();if(!f)return S(Wn());let k=f.indexOf(" -- "),x,J;if(k!==-1?(x=f.slice(0,k).trim(),J=f.slice(k+4).trim()||void 0):x=f,(x.startsWith('"')&&x.endsWith('"')||x.startsWith("'")&&x.endsWith("'"))&&(x=x.slice(1,-1)),!x)return S(Wn());let X=Qa.resolve(B(d).cwd,x),Me=Xe(X,e.fileSendMaxBytes);return"error"in Me?S(p(Me.error)):{kind:"file",spec:{absPath:Me.absPath,category:Me.category,mimetype:Me.mimetype,displayName:Me.displayName,caption:J}}}if(c==="/allowlist"){let f=$();return S(bo([{label:"allowFrom (WhatsApp)",items:f.allowFrom},{label:"telegramAllowFrom",items:f.telegramAllowFrom}]))}let G=c.match(/^\/allow\b\s+(.+)$/i);if(G)try{let f=Ht(G[1].trim());return S(jn(f))}catch(f){return S(p(String(f)))}if(/^\/allow\b\s*$/i.test(c))return S(vo());let ce=c.match(/^\/deny\b\s+(.+)$/i);if(ce)try{let f=jt(ce[1].trim());return S(jn(f))}catch(f){return S(p(String(f)))}if(/^\/deny\b\s*$/i.test(c))return S($o());let F=c.match(/^\/(whatsapp|wa|telegram|tg)\b(?:\s+(.*))?$/i);if(F){let f=F[1].toLowerCase(),k=(F[2]??"").trim(),x=f==="whatsapp"||f==="wa";if(f==="telegram"||f==="tg"){let X=k.match(/^token\s+(\S+)\s*$/i);return X?S(tl(X[1]??"")):k===""||/^help$/i.test(k)?S(O(wo($()))):S(To())}if(x)return k===""||/^help$/i.test(k)?S(O(yo(e))):S(Mo())}let $e=c.match(/^\/(cowork|cw)\b(?:\s+([\s\S]*))?$/i);if($e){let f=($e[2]??"").trim();return S(Us(f,d))}if(c==="/apps"||c.startsWith("/apps "))return S(await sl(c,d,e,i,r));let ie=nl(c);if(ie!==null)return S(rl(ie,d,e,i));if(c.startsWith("/bg")){let f=c.slice(3).trim();if(!f)return S(Co());let k=B(d).cwd,{id:x}=t.spawnJob(e.shell,f,{cwd:k});return S(p(`Job ${x} started.
151
- [cwd: ${k}]
152
- /log ${x}
153
- /tail ${x}`))}if(c==="/jobs"){let f=t.list().slice(0,20);return f.length===0?S(p("(no jobs yet)")):S(p(f.map(k=>{let x=k.finishedAt&&k.startedAt?`${((Date.parse(k.finishedAt)-Date.parse(k.startedAt))/1e3).toFixed(1)}s`:"\u2026";return`${k.id} ${k.status} exit=${k.exitCode??"?"} ${x}
154
- ${k.cmd.slice(0,120)}${k.cmd.length>120?"\u2026":""}`}).join(`
167
+ Change folder with ${e.commandPrefix}cd \u2026 Send /receive default to use the server config again.`))}return v.has(g)?(Gn(u,"default"),x(p("Per-chat inbound folder cleared. Uploads now follow fileReceiveRootMode in config.json (/files)."))):x(g==="help"?er():g==="status"?Do(e,u):er())}if(c==="/reload"||c==="/restart"){if(!a?.reload)return x(p("Reload is only available while the omnish gateway (omnish run) is running."));let g=await a.reload();return x(p(g.ok?g.summary:`Reload failed: ${g.error}`))}if(c==="/updates"||c.startsWith("/updates ")){let g=c.slice(8).trim().toLowerCase();if(g==="cached"||g==="last"){let v=Et();return x(v?Ct(v):p("No update snapshot yet. Send /updates (live check) once, or enable updateCheckEnabled and wait for the scheduled check."))}let b=await Ot(Ue(),C());return x(Ct(b))}let y=c.match(/^\/security(?:\s+(\S+))?\s*$/i);if(y){let g=(y[1]??"").toLowerCase(),b=C(),v=at(b);return x(g==="help"||g==="?"?P(es()):g==="summary"||g==="brief"?p(Co(v,"Send /security for the full report.")):g==="tips"?P(Zo()):g===""||g==="full"||g==="report"?Qo(v):p("Unknown /security subcommand. Try /security, /security summary, /security tips, or /security help"))}let S=c.match(/^\/(gateway|gw|mode)\b(?:\s+(.*))?$/i);if(S){let g=(S[2]??"").trim();return x(await Cl(g,a))}if(c==="/config"||c.startsWith("/config ")){let g=c.slice(7).trim();return x(await Fs(g,a))}let F=Ds(c);if(F!==null)return x(await Hs(e,F));let I=Or(c);if(I!==null){let g=As(e,I,l);return g===null?null:x(g)}let L=c.match(/^\/(send|file)\b(?:\s+([\s\S]+))?$/i);if(L){let g=(L[2]??"").trim();if(!g)return x(Qn());let b=g.indexOf(" -- "),v,N;if(b!==-1?(v=g.slice(0,b).trim(),N=g.slice(b+4).trim()||void 0):v=g,(v.startsWith('"')&&v.endsWith('"')||v.startsWith("'")&&v.endsWith("'"))&&(v=v.slice(1,-1)),!v)return x(Qn());let G=xl.resolve(j(u).cwd,v),ee=_e(G,e.fileSendMaxBytes);return"error"in ee?x(p(ee.error)):{kind:"file",spec:{absPath:ee.absPath,category:ee.category,mimetype:ee.mimetype,displayName:ee.displayName,caption:N}}}if(c==="/allowlist"){let g=C();return x(Oo([{label:"allowFrom (WhatsApp)",items:g.allowFrom},{label:"telegramAllowFrom",items:g.telegramAllowFrom}]))}let Y=c.match(/^\/allow\b\s+(.+)$/i);if(Y)try{let g=Kt(Y[1].trim());return x(Xn(g))}catch(g){return x(p(String(g)))}if(/^\/allow\b\s*$/i.test(c))return x(No());let W=c.match(/^\/deny\b\s+(.+)$/i);if(W)try{let g=Yt(W[1].trim());return x(Xn(g))}catch(g){return x(p(String(g)))}if(/^\/deny\b\s*$/i.test(c))return x(_o());let _=c.match(/^\/(whatsapp|wa|telegram|tg)\b(?:\s+(.*))?$/i);if(_){let g=_[1].toLowerCase(),b=(_[2]??"").trim(),v=g==="whatsapp"||g==="wa";if(g==="telegram"||g==="tg"){let G=b.match(/^token\s+(\S+)\s*$/i);return G?x(Rl(G[1]??"")):b===""||/^help$/i.test(b)?x(P(Eo(C()))):x(jo())}if(v)return b===""||/^help$/i.test(b)?x(P(Ao(e))):x(Ho())}let ge=c.match(/^\/(cowork|cw)\b(?:\s+([\s\S]*))?$/i);if(ge){let g=(ge[2]??"").trim();return x(ii(g,u))}if(c==="/apps"||c.startsWith("/apps "))return x(await Al(c,u,e,i,r));let we=Ml(c);if(we!==null)return x(Tl(we,u,e,i));if(c.startsWith("/bg")){let g=c.slice(3).trim();if(!g)return x(Bo());let b=j(u).cwd,{id:v}=t.spawnJob(e.shell,g,{cwd:b});return x(p(`Job ${v} started.
168
+ [cwd: ${b}]
169
+ /log ${v}
170
+ /tail ${v}`))}if(c==="/jobs"){let g=t.list().slice(0,20);return g.length===0?x(p("(no jobs yet)")):x(p(g.map(b=>{let v=b.finishedAt&&b.startedAt?`${((Date.parse(b.finishedAt)-Date.parse(b.startedAt))/1e3).toFixed(1)}s`:"\u2026";return`${b.id} ${b.status} exit=${b.exitCode??"?"} ${v}
171
+ ${b.cmd.slice(0,120)}${b.cmd.length>120?"\u2026":""}`}).join(`
155
172
 
156
- `)))}let Re=c.match(/^\/log\s+([a-f0-9]{8})(?:\s+(\d+))?\s*$/i);if(Re){let f=Re[1].toLowerCase(),k=Re[2]?Number.parseInt(Re[2],10):e.jobLogTailLines,x=Number.isFinite(k)&&k>0?Math.min(k,500):e.jobLogTailLines;return S(p(t.tailLog(f,x)))}let M=c.match(/^\/tail\s+([a-f0-9]{8})\s*$/i);if(M){let f=M[1].toLowerCase(),k=Za(d,f),x=n.get(k)??0,{text:J,nextOffset:X}=t.readSince(f,x);return n.set(k,X),S(J?p(J.trimEnd()||"(no new output)"):p(`(no new output; offset ${X})`))}let L=c.match(/^\/kill\s+([a-f0-9]{8})\s*$/i);if(L){let f=L[1].toLowerCase();return S(p(t.kill(f)))}let D=c.match(/^\/(shortcut|shortcuts|alias|aliases)\b(?:\s+([\s\S]*))?$/i);if(D){let f=D[1].toLowerCase(),k=(D[2]??"").trim();return f==="shortcuts"&&!k?k="list":((f==="alias"||f==="aliases")&&!k||f==="shortcut"&&!k)&&(k="help"),S(ol(k,d))}if(!u){let f=c.trim().match(/^\/([a-zA-Z0-9][a-zA-Z0-9_-]{0,31})\s*$/);if(f){let k=f[1],x=ot(d,k);if(x!==void 0)return await ze(e,t,n,r,o,{...s,text:x},i,a,l,!0)}}return S(Ao(e))}let g=c.match(/^>(\S+)\s*(.*)$/s);if(g){let y=g[1],w=g[2]??"",v=await i.writeNamedLine(d,y,w);return v?S(p(v)):null}return o.get(d)&&c?S(await Gs(e,d,c)):await i.writeFocusedLine(d,c)?null:S(Eo(e))}function nl(e){return e==="/run"||e.startsWith("/run ")?e.slice(4).trim():e==="/r"||e.startsWith("/r ")?e.slice(2).trim():null}function rl(e,t,n,r){let o=e.trim();if(!o||/^help$/i.test(o))return O(No());if(/^list$/i.test(o))return _o(ts(t,n));if(/^show$/i.test(o))return p("Usage: /run show <name>");let s=o.match(/^show\s+(\S+)\s*$/i);if(s){let d=s[1],m=st(t,n,d);return m?Bo(m):Xt(d)}if(/^add$/i.test(o))return p('Usage: /run add <name> <command\u2026> \u2014 command must include "$OMNISH_TASK"');if(/^set$/i.test(o))return p('Usage: /run set <name> <command\u2026> \u2014 command must include "$OMNISH_TASK"');let i=o.match(/^add\s+(\S+)\s+([\s\S]+)$/i);if(i)try{or(t,i[1],{command:i[2].trim()});let d=on(i[1]),m=d.ok?d.normalized:i[1].toLowerCase(),g=st(t,n,m);return g?Kn(m,g):p("Recipe save failed.")}catch(d){return p(String(d))}let a=o.match(/^set\s+(\S+)\s+([\s\S]+)$/i);if(a)try{or(t,a[1],{command:a[2].trim()});let d=on(a[1]),m=d.ok?d.normalized:a[1].toLowerCase(),g=st(t,n,m);return g?Kn(m,g):p("Recipe save failed.")}catch(d){return p(String(d))}let l=o.match(/^(?:remove|rm|del)\s+(\S+)\s*$/i);if(l){let d=l[1];return es(t,d)?p(`Recipe removed (this chat): ${d}`):Xt(d)}if(/^(?:remove|rm|del)$/i.test(o))return p("Usage: /run remove <name>");let u=o.match(/^(\S+)\s+([\s\S]+)$/);if(u){let d=u[1],m=u[2],g=st(t,n,d);if(!g)return Xt(d);let y=g.taskEnv??"OMNISH_TASK";if(!rr(g.command,y))return p(`Recipe "${d}" command must reference "$${y}".`);let w=Xo(m,n.recipesMaxTaskChars);if(!w.ok)return p(w.error);let v=ns(d),E={[y]:w.task};return p(r.start(t,v,g.command,n,E))}let c=o.match(/^(\S+)$/);if(c){let d=c[1],m=d.toLowerCase();return m==="add"||m==="set"?p('Usage: /run add <name> <command\u2026> \u2014 include "$OMNISH_TASK"'):m==="show"?p("Usage: /run show <name>"):m==="remove"||m==="rm"||m==="del"?p("Usage: /run remove <name>"):st(t,n,m)?p(`Usage: /run ${d} <task\u2026>`):p(`Unknown recipe "${d}". /run list`)}return p("/run: could not parse. /run help")}function ol(e,t){let n=e.trim();if(!n||/^help$/i.test(n))return O(zn());if(/^list$/i.test(n))return Po(qo(t));let r=n.match(/^show\s+(\S+)\s*$/i);if(r){let a=r[1],l=ot(t,a);return l===void 0?qn(a):Fo(a,l)}let o=n.match(/^add\s+(\S+)\s+([\s\S]+)$/i);if(o)try{Vn(t,o[1],o[2]);let a=tn(o[1]),l=a.ok?a.normalized:o[1].trim().toLowerCase(),u=ot(t,l);return Jn(l,u)}catch(a){return p(String(a))}let s=n.match(/^set\s+(\S+)\s+([\s\S]+)$/i);if(s)try{Vn(t,s[1],s[2]);let a=tn(s[1]),l=a.ok?a.normalized:s[1].trim().toLowerCase(),u=ot(t,l);return Jn(l,u)}catch(a){return p(String(a))}let i=n.match(/^(?:remove|rm|del)\s+(\S+)\s*$/i);if(i){let a=i[1];return Ko(t,a)?Lo(a):qn(a)}return O(zn())}async function sl(e,t,n,r,o){let s=e.slice(5).trim(),i=s.toLowerCase();if(!i||i==="help")return O(Yn());let a=s.match(/^(\S+)\s*(.*)$/s);if(!a)return O(Yn());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=Xa(t,c),m=o.get(d)??0,{text:g,nextOffset:y}=r.readSince(t,c,m);return o.set(d,y),p(g.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]),g=Number(c[2]);return!Number.isFinite(m)||!Number.isFinite(g)?p("cols and rows must be numbers."):p(r.resize(t,d,m,g))}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 Sr(e,t,n){return!e.clusterEnabled||kr(t.trim())!==null?!0:n?hs(n,e):!1}import il from"pino";function hn(){return process.env.OMNISH_VERBOSE==="1"||process.env.WHATSVERBOSE==="1"}var al=hn()?"info":"silent",T=il({level:al,base:{app:"omnish"}});function zs(){return T.child({module:"baileys"})}function ll(e){return`wa:${e}`}function xr(e){return`tg:${e}`}function Js(e){return{peerKey:ll(e.fromJid),text:e.text,waMessageId:e.messageId,mediaSavedPath:e.mediaSavedPath,mediaError:e.mediaError}}import De from"node:fs";import hl from"node:path";var qs={enter:"\r",cr:"\r",lf:`
157
- `,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 cl(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+=`
158
- `,n++;continue}if(o==="t"){t+=" ",n++;continue}if(o==="\\"){t+="\\",n++;continue}}t+=r}return t}function ul(e){let t=e.trim();if(!t)return"";if(t.startsWith("\\"))return cl(t);let n=t.toLowerCase();if(qs[n])return qs[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 vr(e){let t=e.split(",").map(n=>n.trim()).filter(Boolean);return t.length===0?"":t.map(n=>ul(n)).join("")}var Et="\x1B";function Ks(e){let t=e;return t=t.replace(new RegExp(`${Et}\\[[\\d;?]*[ -/]*[@-~]`,"g"),""),t=t.replace(new RegExp(`${Et}\\][^${Et}\\u0007]*(?:\\u0007|${Et}\\\\)`,"g"),""),t=t.replace(new RegExp(`${Et}[@-Z\\\\-_]`,"g"),""),t=t.replace(/\u0007/g,""),t}function dl(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 pl(e,t){return`${e}${t}`}var yn=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=pl(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(g=>setTimeout(g,a)),this.closed)return;let l=this.pending.get(t)??"";if(this.pending.delete(t),!l||(this.opts.isRaw(n,r)||(l=Ks(l)),!l.trim()))return;let u=o.appsMaxFlushBytes;if(l.length>u){let g=l.slice(u);l=l.slice(0,u)+`
159
- [\u2026+${g.length} chars; /apps since ${r} to read more]`,this.pending.set(t,(this.pending.get(t)??"")+g)}let d=(this.opts.getRunningCount(n)>1?`[${r}] `:"")+l,m=dl(d,o.appsMaxWaChars);for(let g of m){if(this.closed)break;try{await this.opts.send(n,g)}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 ml from"node:fs";import fl from"node:path";import*as Ys from"node-pty";var gl=1024*1024,wn=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>gl&&this.ringChunks.length>0;){let n=this.ringChunks.shift();this.ringBytes-=n.length}}static start(t){P(yt(t.peerKey));let n=fl.join(yt(t.peerKey),`${t.name}.log`),r=ml.createWriteStream(n,{flags:"a",mode:384}),o={...process.env,TERM:"xterm-256color",COLORTERM:"truecolor",...t.cwd?{PWD:t.cwd}:{},...t.extraEnv??{}},s=Ys.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 yl=/^[a-zA-Z0-9_-]{1,32}$/;function me(e){return yl.test(e)?null:"Session name must be 1\u201332 chars: letters, digits, _ or -."}function le(e,t){return`${e}:${t}`}function wl(e){let t=e.lastIndexOf(":");return t<=0?null:{peerKey:e.slice(0,t),name:e.slice(t+1)}}function $r(e){return new Promise(t=>setTimeout(t,e))}async function Cr(e,t,n){let r=n.appsSubmitDelayMs,o=n.appsClearInputDelayMs,i=n.appsClearInput!==!1?vr(n.appsClearInputSequence||"^A,^K"):"",l=t.replace(/\r\n/g,`
173
+ `)))}let Re=c.match(/^\/log\s+([a-f0-9]{8})(?:\s+(\d+))?\s*$/i);if(Re){let g=Re[1].toLowerCase(),b=Re[2]?Number.parseInt(Re[2],10):e.jobLogTailLines,v=Number.isFinite(b)&&b>0?Math.min(b,500):e.jobLogTailLines;return x(p(t.tailLog(g,v)))}let Me=c.match(/^\/tail\s+([a-f0-9]{8})\s*$/i);if(Me){let g=Me[1].toLowerCase(),b=vl(u,g),v=n.get(b)??0,{text:N,nextOffset:G}=t.readSince(g,v);return n.set(b,G),x(N?p(N.trimEnd()||"(no new output)"):p(`(no new output; offset ${G})`))}let $=c.match(/^\/kill\s+([a-f0-9]{8})\s*$/i);if($){let g=$[1].toLowerCase();return x(p(t.kill(g)))}let E=c.match(/^\/(shortcut|shortcuts|alias|aliases)\b(?:\s+([\s\S]*))?$/i);if(E){let g=E[1].toLowerCase(),b=(E[2]??"").trim();return g==="shortcuts"&&!b?b="list":((g==="alias"||g==="aliases")&&!b||g==="shortcut"&&!b)&&(b="help"),x(Il(b,u))}if(!d){let g=c.trim().match(/^\/([a-zA-Z0-9][a-zA-Z0-9_-]{0,31})\s*$/);if(g){let b=g[1],v=ct(u,b);if(v!==void 0)return await Ve(e,t,n,r,o,{...s,text:v},i,a,l,!0)}}return x(Uo(e))}let f=c.match(/^>(\S+)\s*(.*)$/s);if(f){let w=f[1],y=f[2]??"",S=await i.writeNamedLine(u,w,y);return S?x(p(S)):null}return o.get(u)&&c?x(await ai(e,u,c)):await i.writeFocusedLine(u,c)?null:x(Go(e))}function Ml(e){return e==="/run"||e.startsWith("/run ")?e.slice(4).trim():e==="/r"||e.startsWith("/r ")?e.slice(2).trim():null}function Tl(e,t,n,r){let o=e.trim();if(!o||/^help$/i.test(o))return P(Yo());if(/^list$/i.test(o))return Vo(fs(t,n));if(/^show$/i.test(o))return p("Usage: /run show <name>");let s=o.match(/^show\s+(\S+)\s*$/i);if(s){let u=s[1],m=ut(t,n,u);return m?Xo(m):ln(u)}if(/^add$/i.test(o))return p('Usage: /run add <name> <command\u2026> \u2014 command must include "$OMNISH_TASK"');if(/^set$/i.test(o))return p('Usage: /run set <name> <command\u2026> \u2014 command must include "$OMNISH_TASK"');let i=o.match(/^add\s+(\S+)\s+([\s\S]+)$/i);if(i)try{fr(t,i[1],{command:i[2].trim()});let u=mn(i[1]),m=u.ok?u.normalized:i[1].toLowerCase(),f=ut(t,n,m);return f?or(m,f):p("Recipe save failed.")}catch(u){return p(String(u))}let a=o.match(/^set\s+(\S+)\s+([\s\S]+)$/i);if(a)try{fr(t,a[1],{command:a[2].trim()});let u=mn(a[1]),m=u.ok?u.normalized:a[1].toLowerCase(),f=ut(t,n,m);return f?or(m,f):p("Recipe save failed.")}catch(u){return p(String(u))}let l=o.match(/^(?:remove|rm|del)\s+(\S+)\s*$/i);if(l){let u=l[1];return ms(t,u)?p(`Recipe removed (this chat): ${u}`):ln(u)}if(/^(?:remove|rm|del)$/i.test(o))return p("Usage: /run remove <name>");let d=o.match(/^(\S+)\s+([\s\S]+)$/);if(d){let u=d[1],m=d[2],f=ut(t,n,u);if(!f)return ln(u);let w=f.taskEnv??"OMNISH_TASK";if(!mr(f.command,w))return p(`Recipe "${u}" command must reference "$${w}".`);let y=ps(m,n.recipesMaxTaskChars);if(!y.ok)return p(y.error);let S=gs(u),F={[w]:y.task};return p(r.start(t,S,f.command,n,F))}let c=o.match(/^(\S+)$/);if(c){let u=c[1],m=u.toLowerCase();return m==="add"||m==="set"?p('Usage: /run add <name> <command\u2026> \u2014 include "$OMNISH_TASK"'):m==="show"?p("Usage: /run show <name>"):m==="remove"||m==="rm"||m==="del"?p("Usage: /run remove <name>"):ut(t,n,m)?p(`Usage: /run ${u} <task\u2026>`):p(`Unknown recipe "${u}". /run list`)}return p("/run: could not parse. /run help")}function Il(e,t){let n=e.trim();if(!n||/^help$/i.test(n))return P(tr());if(/^list$/i.test(n))return Jo(is(t));let r=n.match(/^show\s+(\S+)\s*$/i);if(r){let a=r[1],l=ct(t,a);return l===void 0?rr(a):Ko(a,l)}let o=n.match(/^add\s+(\S+)\s+([\s\S]+)$/i);if(o)try{ir(t,o[1],o[2]);let a=un(o[1]),l=a.ok?a.normalized:o[1].trim().toLowerCase(),d=ct(t,l);return nr(l,d)}catch(a){return p(String(a))}let s=n.match(/^set\s+(\S+)\s+([\s\S]+)$/i);if(s)try{ir(t,s[1],s[2]);let a=un(s[1]),l=a.ok?a.normalized:s[1].trim().toLowerCase(),d=ct(t,l);return nr(l,d)}catch(a){return p(String(a))}let i=n.match(/^(?:remove|rm|del)\s+(\S+)\s*$/i);if(i){let a=i[1];return as(t,a)?qo(a):rr(a)}return P(tr())}async function Al(e,t,n,r,o){let s=e.slice(5).trim(),i=s.toLowerCase();if(!i||i==="help")return P(sr());let a=s.match(/^(\S+)\s*(.*)$/s);if(!a)return P(sr());let l=a[1].toLowerCase(),d=(a[2]??"").trim();switch(l){case"start":{let c=d.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=d.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=d.split(/\s+/)[0];return p(r.info(t,c||void 0))}case"send":{let c=d.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=d.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=d.match(/^(\S+)(?:\s+(\d+))?\s*$/);if(!c)return p("Usage: /apps tail <name> [lines]");let u=c[2]?Number.parseInt(c[2],10):n.appsLogTailLines;return p(r.tail(t,c[1],u))}case"since":{let c=d.split(/\s+/)[0];if(!c)return p("Usage: /apps since <name>");let u=$l(t,c),m=o.get(u)??0,{text:f,nextOffset:w}=r.readSince(t,c,m);return o.set(u,w),p(f.trimEnd()||"(no new log bytes)")}case"mute":{let c=d.split(/\s+/)[0];return c?p(r.mute(t,c)):p("Usage: /apps mute <name>")}case"unmute":{let c=d.split(/\s+/)[0];return c?p(r.unmute(t,c)):p("Usage: /apps unmute <name>")}case"raw":{let c=d.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=d.trim().split(/\s+/).filter(Boolean);if(c.length<3)return p("Usage: /apps resize <name> <cols> <rows>");let u=c[0],m=Number(c[1]),f=Number(c[2]);return!Number.isFinite(m)||!Number.isFinite(f)?p("cols and rows must be numbers."):p(r.resize(t,u,m,f))}case"stop":{let c=d.split(/\s+/)[0];return c?p(r.stop(t,c)):p("Usage: /apps stop <name>")}case"kill":{let c=d.split(/\s+/)[0];return c?p(r.kill(t,c)):p("Usage: /apps kill <name>")}case"rm":{let c=d.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 Lr(e,t,n){return!e.clusterEnabled||Or(t.trim())!==null?!0:n?Is(n,e):!1}function El(e){return`wa:${e}`}function Pr(e){return`tg:${e}`}function li(e){return{peerKey:El(e.fromJid),text:e.text,waMessageId:e.messageId,mediaSavedPath:e.mediaSavedPath,mediaError:e.mediaError}}import ze from"node:fs";import Dl from"node:path";var ci={enter:"\r",cr:"\r",lf:`
174
+ `,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 Ol(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+=`
175
+ `,n++;continue}if(o==="t"){t+=" ",n++;continue}if(o==="\\"){t+="\\",n++;continue}}t+=r}return t}function Ll(e){let t=e.trim();if(!t)return"";if(t.startsWith("\\"))return Ol(t);let n=t.toLowerCase();if(ci[n])return ci[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 Fr(e){let t=e.split(",").map(n=>n.trim()).filter(Boolean);return t.length===0?"":t.map(n=>Ll(n)).join("")}var _t="\x1B";function ui(e){let t=e;return t=t.replace(new RegExp(`${_t}\\[[\\d;?]*[ -/]*[@-~]`,"g"),""),t=t.replace(new RegExp(`${_t}\\][^${_t}\\u0007]*(?:\\u0007|${_t}\\\\)`,"g"),""),t=t.replace(new RegExp(`${_t}[@-Z\\\\-_]`,"g"),""),t=t.replace(/\u0007/g,""),t}function Pl(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 Fl(e,t){return`${e}${t}`}var Mn=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=Fl(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=ui(l)),!l.trim()))return;let d=o.appsMaxFlushBytes;if(l.length>d){let f=l.slice(d);l=l.slice(0,d)+`
176
+ [\u2026+${f.length} chars; /apps since ${r} to read more]`,this.pending.set(t,(this.pending.get(t)??"")+f)}let u=(this.opts.getRunningCount(n)>1?`[${r}] `:"")+l,m=Pl(u,o.appsMaxWaChars);for(let f of m){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))}}}};import Nl from"node:fs";import _l from"node:path";import*as di from"node-pty";var Bl=1024*1024,Tn=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>Bl&&this.ringChunks.length>0;){let n=this.ringChunks.shift();this.ringBytes-=n.length}}static start(t){B(vt(t.peerKey));let n=_l.join(vt(t.peerKey),`${t.name}.log`),r=Nl.createWriteStream(n,{flags:"a",mode:384}),o={...process.env,TERM:"xterm-256color",COLORTERM:"truecolor",...t.cwd?{PWD:t.cwd}:{},...t.extraEnv??{}},s=di.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 Hl=/^[a-zA-Z0-9_-]{1,32}$/;function me(e){return Hl.test(e)?null:"Session name must be 1\u201332 chars: letters, digits, _ or -."}function ce(e,t){return`${e}:${t}`}function jl(e){let t=e.lastIndexOf(":");return t<=0?null:{peerKey:e.slice(0,t),name:e.slice(t+1)}}function Nr(e){return new Promise(t=>setTimeout(t,e))}async function _r(e,t,n){let r=n.appsSubmitDelayMs,o=n.appsClearInputDelayMs,i=n.appsClearInput!==!1?Fr(n.appsClearInputSequence||"^A,^K"):"",l=t.replace(/\r\n/g,`
160
177
  `).replace(/\r/g,"").split(`
161
- `);i&&(o>0&&await $r(o),e.write(i));for(let u of l)u.length>0&&e.write(u),r>0&&await $r(r),e.write("\r"),i&&(o>0&&await $r(o),e.write(i))}function bl(e,t){try{let r=De.readFileSync(e,"utf8").split(/\r?\n/);return r.slice(Math.max(0,r.length-t)).join(`
162
- `).trimEnd()}catch{return""}}var dt=class{constructor(t,n){this.getCfg=t;this.send=n,this.router=new yn({getCfg:()=>this.getCfg(),send:n,isMuted:(r,o)=>this.muted.has(le(r,o)),isRaw:(r,o)=>this.rawMode.has(le(r,o)),getRunningCount:r=>this.countPeerRunning(r)})}sessions=new Map;focus=new Map;muted=new Set;rawMode=new Set;router;killTimers=new Set;send;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()}getFocus(t){return this.focus.get(t)??null}logPath(t,n){return hl.join(yt(t),`${n}.log`)}getSession(t,n){return this.sessions.get(le(t,n))}removeSessionRecord(t,n){this.sessions.delete(le(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 Cr(o,n,this.getCfg()),!0):!1}async writeNamedLine(t,n,r){let o=me(n);if(o)return o;let s=this.getSession(t,n);return s?.alive?(await Cr(s,r,this.getCfg()),null):`No app session "${n}". /apps list`}start(t,n,r,o,s){let i=me(n);if(i)return i;if(!r.trim())return`Usage: /apps start <name> <command\u2026>
163
- Example: /apps start sh bash`;if(this.sessions.has(le(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.`;Y();let a=B(t).cwd,l={...process.env,TERM:"xterm-256color",COLORTERM:"truecolor",...a?{PWD:a}:{},...s??{}},u;try{u=wn.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)}
164
- If install skipped native builds: pnpm approve-builds && pnpm install (needs @whiskeysockets/baileys, sharp, protobufjs, esbuild, node-pty).`}return this.sessions.set(le(t,n),u),this.focus.set(t,n),`App "${n}" started and attached.
178
+ `);i&&(o>0&&await Nr(o),e.write(i));for(let d of l)d.length>0&&e.write(d),r>0&&await Nr(r),e.write("\r"),i&&(o>0&&await Nr(o),e.write(i))}function Wl(e,t){try{let r=ze.readFileSync(e,"utf8").split(/\r?\n/);return r.slice(Math.max(0,r.length-t)).join(`
179
+ `).trimEnd()}catch{return""}}var yt=class{constructor(t,n){this.getCfg=t;this.send=n,this.router=new Mn({getCfg:()=>this.getCfg(),send:n,isMuted:(r,o)=>this.muted.has(ce(r,o)),isRaw:(r,o)=>this.rawMode.has(ce(r,o)),getRunningCount:r=>this.countPeerRunning(r)})}sessions=new Map;focus=new Map;muted=new Set;rawMode=new Set;router;killTimers=new Set;send;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()}getFocus(t){return this.focus.get(t)??null}logPath(t,n){return Dl.join(vt(t),`${n}.log`)}getSession(t,n){return this.sessions.get(ce(t,n))}removeSessionRecord(t,n){this.sessions.delete(ce(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 _r(o,n,this.getCfg()),!0):!1}async writeNamedLine(t,n,r){let o=me(n);if(o)return o;let s=this.getSession(t,n);return s?.alive?(await _r(s,r,this.getCfg()),null):`No app session "${n}". /apps list`}start(t,n,r,o,s){let i=me(n);if(i)return i;if(!r.trim())return`Usage: /apps start <name> <command\u2026>
180
+ Example: /apps start sh bash`;if(this.sessions.has(ce(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.`;Q();let a=j(t).cwd,l={...process.env,TERM:"xterm-256color",COLORTERM:"truecolor",...a?{PWD:a}:{},...s??{}},d;try{d=Tn.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)}
181
+ If install skipped native builds: pnpm approve-builds && pnpm install (needs @whiskeysockets/baileys, sharp, protobufjs, esbuild, node-pty).`}return this.sessions.set(ce(t,n),d),this.focus.set(t,n),`App "${n}" started and attached.
165
182
  [cwd: ${a}]
166
- Plain DMs go to this session until /apps detach. Use >othername text for another session.`}async handleSessionExit(t,n,r){let o=le(t,n);if(!this.sessions.has(o))return;let s=this.focus.get(t)===n;this.sessions.delete(o),s&&this.focus.set(t,null),this.muted.delete(o),this.rawMode.delete(o);let i=r.signal!=null?` signal=${r.signal}`:"",a=s?" (detached)":"",l=`[${n}] exited code=${r.exitCode}${i}${a}`;try{await this.send(t,l)}catch{}}attach(t,n){let r=me(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=wl(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)"}
183
+ Plain DMs go to this session until /apps detach. Use >othername text for another session.`}async handleSessionExit(t,n,r){let o=ce(t,n);if(!this.sessions.has(o))return;let s=this.focus.get(t)===n;this.sessions.delete(o),s&&this.focus.set(t,null),this.muted.delete(o),this.rawMode.delete(o);let i=r.signal!=null?` signal=${r.signal}`:"",a=s?" (detached)":"",l=`[${n}] exited code=${r.exitCode}${i}${a}`;try{await this.send(t,l)}catch{}}attach(t,n){let r=me(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=jl(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)"}
167
184
  App sessions:
168
185
  ${n.join(`
169
- `)}`}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=me(o);if(s)return s;let i=this.getSession(t,o),a=this.logPath(t,o),l=0;try{l=De.statSync(a).size}catch{}let u=this.muted.has(le(t,o)),c=this.rawMode.has(le(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(`
186
+ `)}`}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=me(o);if(s)return s;let i=this.getSession(t,o),a=this.logPath(t,o),l=0;try{l=ze.statSync(a).size}catch{}let d=this.muted.has(ce(t,o)),c=this.rawMode.has(ce(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: ${d}`,`raw outbound: ${c}`].join(`
170
187
  `)}async sendText(t,n,r){let o=me(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,`
171
- `);return await Cr(s,i,this.getCfg()),`Sent to "${n}" (${i.length} chars + Enter per line).`}sendKey(t,n,r){let o=me(n);if(o)return o;let s=this.getSession(t,n);if(!s?.alive)return`No session "${n}".`;let i=vr(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=me(n);if(o)return o;let s=this.logPath(t,n);if(!De.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 bl(s,a)||"(empty log)"}readSince(t,n,r){let o=this.logPath(t,n);try{let i=De.statSync(o).size,a=De.openSync(o,"r");try{let l=Math.min(r,i),u=i-l,c=Buffer.alloc(u);return u>0&&De.readSync(a,c,0,u,l),{text:c.toString("utf8"),nextOffset:i}}finally{De.closeSync(a)}}catch{return{text:"",nextOffset:r}}}mute(t,n){let r=me(n);return r||(this.muted.add(le(t,n)),`Muted chat output for "${n}" (log still grows). /apps unmute ${n}`)}unmute(t,n){let r=me(n);return r||(this.muted.delete(le(t,n)),`Unmuted "${n}".`)}setRaw(t,n,r){let o=me(n);if(o)return o;let s=le(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=me(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=me(n);if(r)return r;let o=this.getSession(t,n);if(!o?.alive)return`No running session "${n}".`;o.kill("SIGTERM");let s=le(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=me(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=me(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(le(t,n)),this.rawMode.delete(le(t,n));let s=this.logPath(t,n);try{De.rmSync(s,{force:!0})}catch{}return`Removed "${n}" metadata and log.`}};import Qs from"node:fs";import kl from"node:path";function Vs(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=N(String(o));s&&r.add(`wa:${_t(s)}`)}if(e==="tg"||e==="all")for(let o of n.telegramAllowFrom){let s=ue(String(o));s&&r.add(`tg:${s}`)}return[...r]}function Sl(e){return e.toISOString().replace(/[:.]/g,"-")}async function Zs(e,t,n,r){let o=B(t.ownerPeerKey),s=t.cwd.trim()?t.cwd:o.cwd,i=At(t.outputDir,o.cwd);try{Qs.mkdirSync(i,{recursive:!0,mode:448})}catch(ie){T.warn({err:String(ie),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 sn(e.shell,t.command,{timeoutMs:e.syncTimeoutMs,maxBytes:e.syncMaxBytes,cwd:s}),d=Date.now()-u,m=`${Sl(new Date)}-${t.id}-${l}.log`,g=kl.join(i,m),w=[`cowork task=${t.name} id=${t.id}`,`slot=${a} kind=${l}`,`cwd=${s}`,`exit=${c.code} timedOut=${c.timedOut} durationMs=${d}`,"---",""].join(`
188
+ `);return await _r(s,i,this.getCfg()),`Sent to "${n}" (${i.length} chars + Enter per line).`}sendKey(t,n,r){let o=me(n);if(o)return o;let s=this.getSession(t,n);if(!s?.alive)return`No session "${n}".`;let i=Fr(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=me(n);if(o)return o;let s=this.logPath(t,n);if(!ze.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 Wl(s,a)||"(empty log)"}readSince(t,n,r){let o=this.logPath(t,n);try{let i=ze.statSync(o).size,a=ze.openSync(o,"r");try{let l=Math.min(r,i),d=i-l,c=Buffer.alloc(d);return d>0&&ze.readSync(a,c,0,d,l),{text:c.toString("utf8"),nextOffset:i}}finally{ze.closeSync(a)}}catch{return{text:"",nextOffset:r}}}mute(t,n){let r=me(n);return r||(this.muted.add(ce(t,n)),`Muted chat output for "${n}" (log still grows). /apps unmute ${n}`)}unmute(t,n){let r=me(n);return r||(this.muted.delete(ce(t,n)),`Unmuted "${n}".`)}setRaw(t,n,r){let o=me(n);if(o)return o;let s=ce(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=me(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=me(n);if(r)return r;let o=this.getSession(t,n);if(!o?.alive)return`No running session "${n}".`;o.kill("SIGTERM");let s=ce(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=me(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=me(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(ce(t,n)),this.rawMode.delete(ce(t,n));let s=this.logPath(t,n);try{ze.rmSync(s,{force:!0})}catch{}return`Removed "${n}" metadata and log.`}};import Bt from"node:fs";import Dt from"node:path";function pi(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=D(String(o));s&&r.add(`wa:${zt(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 Ul=8,mi=12e4,fi=10,Gl=1e3;function zl(e){return e.toISOString().replace(/[:.]/g,"-")}function Jl(e){let t="";for(let n of e)n==="*"?t+="[^/\\\\]*":n==="?"?t+="[^/\\\\]":/[.+^${}()|[\]\\]/.test(n)?t+=`\\${n}`:t+=n;return new RegExp(`^${t}$`)}function ql(e,t,n){let r=gt(e,t),o=Dt.dirname(r),s=Dt.basename(r);if(o.includes("*")||o.includes("?"))return[];if(!s.includes("*")&&!s.includes("?")){try{let d=Bt.statSync(r);if(d.isFile()&&d.mtimeMs>=n)return[r]}catch{}return[]}let i;try{i=Bt.readdirSync(o)}catch{return[]}let a=Jl(s),l=[];for(let d of i){if(!a.test(d))continue;let c=Dt.join(o,d);try{let u=Bt.statSync(c);u.isFile()&&u.mtimeMs>=n&&l.push(c)}catch{}}return l}function gi(e,t,n){let r=_e(e,t.fileSendMaxBytes);return"error"in r?{ok:!1,displayName:n??Dt.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 hi(e,t,n,r){let o=j(t.ownerPeerKey),s=t.cwd.trim()?t.cwd:o.cwd,i=gt(t.outputDir,o.cwd);try{Bt.mkdirSync(i,{recursive:!0,mode:448})}catch(U){R.warn({err:String(U),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",d=Date.now(),c=await fn(e.shell,t.command,{timeoutMs:e.syncTimeoutMs,maxBytes:e.syncMaxBytes,cwd:s}),u=Date.now()-d,m=`${zl(new Date)}-${t.id}-${l}.log`,f=Dt.join(i,m),y=[`cowork task=${t.name} id=${t.id}`,`slot=${a} kind=${l}`,`cwd=${s}`,`exit=${c.code} timedOut=${c.timedOut} durationMs=${u}`,"---",""].join(`
172
189
  `)+(c.stdout||"")+(c.stderr?`
173
190
  --- stderr ---
174
- ${c.stderr}`:"");try{Qs.writeFileSync(g,w,{mode:384})}catch(ie){T.warn({err:String(ie),logPath:g},"cowork write log")}let v=we(),E=v.find(ie=>ie.id===t.id&&ie.ownerPeerKey===t.ownerPeerKey),U=n.slotMs!==null&&c.code===0&&!c.timedOut&&c.signal===null;E&&U&&(E.lastCompletedSlotMs=n.slotMs,ve(v));let se=E?.notify??t.notify,G=[`Cowork: ${t.name} (${l})`,`Slot: ${a}`,`Exit: ${c.code}${c.timedOut?" (timeout)":""}`,`Log: ${g}`];c.signal&&G.push(`Signal: ${c.signal}`);let ce=G.join(`
175
- `),F=p(ce),$e=Vs(se,t.ownerPeerKey,e);for(let ie of $e){let Re=Ce(F,ie.startsWith("tg:")?"telegram":"whatsapp").text;try{await r.sendToPeer(ie,Re)}catch(M){T.warn({err:String(M),pk:ie},"cowork notify failed")}}}function Xs(e){let t=!1,n=async()=>{if(!t){t=!0;try{let s=e.getConfig();for(;;){let l=js();if(!l)break;try{let c=we().find(d=>d.name===l.name.toLowerCase()&&d.ownerPeerKey===l.ownerPeerKey);c&&c.enabled&&await Zs(s,c,{slotMs:null,catchUp:!1,onDemand:!0},e)}catch(u){T.warn({err:String(u),pending:l.name},"cowork on-demand run failed")}}let i=we(),a=Date.now();for(let l of i){if(!l.enabled||l.schedule.kind==="ondemand")continue;let u=Ls(l.schedule,l.lastCompletedSlotMs,l.createdAtMs,a);if(u.length===0)continue;let c=u[0],d=a-c>12e4;try{await Zs(s,l,{slotMs:c,catchUp:d,onDemand:!1},e)}catch(m){T.warn({err:String(m),task:l.name},"cowork scheduled run failed")}}}finally{t=!1}}},r=setInterval(()=>void n(),3e4),o=setTimeout(()=>void n(),5e3);return()=>{clearInterval(r),clearTimeout(o)}}import{spawn as xl}from"node:child_process";import vl from"node:crypto";import ge from"node:fs";import Rr from"node:path";function pt(e,t){ge.writeFileSync(e,JSON.stringify(t,null,2)+`
176
- `,{mode:384})}function Ot(e){try{return JSON.parse(ge.readFileSync(e,"utf8"))}catch{return null}}var Je=class{running=new Map;metaPath(t){return Rr.join(be,`${t}.meta.json`)}logPath(t){return Rr.join(be,`${t}.log`)}spawnJob(t,n,r={}){Y();let o=vl.randomBytes(4).toString("hex"),s=this.logPath(o),i=this.metaPath(o);ge.writeFileSync(s,"",{flag:"w",mode:384});let a=ge.createWriteStream(s,{flags:"a"}),l=new Date().toISOString(),u=r.cwd,c=xl(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,pid:c.pid??null,startedAt:l,status:"running",exitCode:null,signal:null};return pt(i,d),c.stdout?.on("data",m=>{a.write(m)}),c.stderr?.on("data",m=>{a.write(m)}),c.on("close",(m,g)=>{this.running.delete(o),a.end();let y=Ot(i)??d,w={...y,status:y.status==="killed"?"killed":"done",exitCode:m,signal:g??null,finishedAt:new Date().toISOString()};pt(i,w)}),c.on("error",m=>{this.running.delete(o),a.end(()=>{try{ge.appendFileSync(s,`
191
+ ${c.stderr}`:"");try{Bt.writeFileSync(f,y,{mode:384})}catch(U){R.warn({err:String(U),logPath:f},"cowork write log")}let S=c.code===0&&!c.timedOut&&c.signal===null,I=he().find(U=>U.id===t.id&&U.ownerPeerKey===t.ownerPeerKey),L=I?.notify??t.notify,Y=I?.attachLog??t.attachLog,W=I?.attachFiles??t.attachFiles,_=c.timedOut?"timeout":c.signal?`signal ${c.signal}`:c.code!==0&&c.code!==null?`exit ${c.code}`:null,ge=`slot: ${a} \xB7 ${l}${_?` \xB7 ${_}`:""}`,we=(c.stdout||"").replace(/\s+$/,""),Re=(c.stderr||"").trim(),Me=we||(Re?`(stderr) ${Re}`:"(no output)"),$=[ge,`output: ${Me}`],E=[],g=[],b=d-Gl,v=[],N=new Set;if(Array.isArray(W))for(let U of W){let Te;try{Te=ql(U,s,b)}catch(Ie){R.warn({err:String(Ie),pat:U},"cowork attach glob"),E.push(`attach: ${U} skipped (glob error)`);continue}if(Te.length!==0)for(let Ie of Te)N.has(Ie)||(N.add(Ie),v.push(Ie))}if(Y){let U=gi(f,e,`${t.name}-${l}.log`);U.ok?g.push(U.spec):E.push(`attach: ${U.displayName} skipped (${U.reason})`)}let G=0;for(let U of v){if(g.length>=fi){G+=1;continue}let Te=gi(U,e);Te.ok?g.push(Te.spec):E.push(`attach: ${Te.displayName} skipped (${Te.reason})`)}G>0&&E.push(`attached: skipped ${G} file(s) over cap ${fi}`),E.length>0&&$.push(...E);let ee=$.join(`
192
+ `),xt=p(ee),et=pi(L,t.ownerPeerKey,e);for(let U of et){let Te=Ee(xt,U.startsWith("tg:")?"telegram":"whatsapp").text;try{await r.sendToPeer(U,Te)}catch(Ie){R.warn({err:String(Ie),pk:U},"cowork notify failed")}for(let Ie of g)try{await r.sendMediaToPeer(U,Ie)}catch(qi){R.warn({err:String(qi),pk:U,file:Ie.displayName},"cowork media notify failed")}}return{commandOk:S,logPath:f}}function yi(e){let t=!1;Ft(he());let n=async()=>{if(t)return;t=!0;let s=Date.now(),i=0,a=0,l=0,d=0,c=0;try{let u=e.getConfig(),{batch:m,remainingAfter:f}=ri(Ul);a=m.length,l=f,i=m.length+f;for(let S of m)try{let I=he().find(L=>L.name===S.name.toLowerCase()&&L.ownerPeerKey===S.ownerPeerKey);I&&I.enabled&&(d+=1,await hi(u,I,{slotMs:null,catchUp:!1,onDemand:!0},e))}catch(F){R.warn({err:String(F),pending:S.name},"cowork on-demand run failed")}let w=he();Ft(w);let y=Date.now();for(let S of w){if(!S.enabled||S.schedule.kind==="ondemand")continue;let F=Cn(S),I=qs(S.schedule,F,S.createdAtMs,y);if(I.length===0)continue;let L=I[I.length-1],Y=y-L>mi;try{c+=1;let{commandOk:W,logPath:_}=await hi(u,S,{slotMs:L,catchUp:Y,onDemand:!1},e);if(W){let ge=Date.now(),we=y-L<=mi?"on_time":"catch_up";if(I.length===1)Rn(S.id,[{slotMs:L,kind:we,logPath:_,completedAtMs:ge}]);else{let Me=I.slice(0,-1).map($=>({slotMs:$,kind:"coalesced",logPath:null,completedAtMs:ge}));Me.push({slotMs:L,kind:we,logPath:_,completedAtMs:ge}),Rn(S.id,Me)}}}catch(W){R.warn({err:String(W),task:S.name},"cowork scheduled run failed")}}}finally{let u=Date.now()-s;R.info({tickMs:u,pendingDepthStart:i,pendingDequeued:a,pendingRemainingAfter:l,pendingRunsStarted:d,scheduledRan:c},"cowork tick"),t=!1}},r=setInterval(()=>void n(),3e4),o=setTimeout(()=>void n(),5e3);return()=>{clearInterval(r),clearTimeout(o),Xs()}}import{spawn as Kl}from"node:child_process";import Yl from"node:crypto";import ye from"node:fs";import Br from"node:path";function wt(e,t){ye.writeFileSync(e,JSON.stringify(t,null,2)+`
193
+ `,{mode:384})}function Ht(e){try{return JSON.parse(ye.readFileSync(e,"utf8"))}catch{return null}}var Xe=class{running=new Map;metaPath(t){return Br.join(xe,`${t}.meta.json`)}logPath(t){return Br.join(xe,`${t}.log`)}spawnJob(t,n,r={}){Q();let o=Yl.randomBytes(4).toString("hex"),s=this.logPath(o),i=this.metaPath(o);ye.writeFileSync(s,"",{flag:"w",mode:384});let a=ye.createWriteStream(s,{flags:"a"}),l=new Date().toISOString(),d=r.cwd,c=Kl(t,["-c",n],{stdio:["ignore","pipe","pipe"],env:{...process.env,TERM:"dumb",...d?{PWD:d}:{}},...d?{cwd:d}:{}});this.running.set(o,c);let u={id:o,cmd:n,pid:c.pid??null,startedAt:l,status:"running",exitCode:null,signal:null};return wt(i,u),c.stdout?.on("data",m=>{a.write(m)}),c.stderr?.on("data",m=>{a.write(m)}),c.on("close",(m,f)=>{this.running.delete(o),a.end();let w=Ht(i)??u,y={...w,status:w.status==="killed"?"killed":"done",exitCode:m,signal:f??null,finishedAt:new Date().toISOString()};wt(i,y)}),c.on("error",m=>{this.running.delete(o),a.end(()=>{try{ye.appendFileSync(s,`
177
194
  [spawn error] ${String(m)}
178
- `)}catch{}});let g=Ot(i)??d;pt(i,{...g,status:"done",exitCode:null,signal:null,finishedAt:new Date().toISOString()})}),{id:o,meta:d}}list(){Y();let t=[];try{t=ge.readdirSync(be)}catch{return[]}let n=[];for(let r of t){if(!r.endsWith(".meta.json"))continue;let o=Ot(Rr.join(be,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(!ge.existsSync(r))return"(no log file)";let s=ge.readFileSync(r,"utf8").split(`
195
+ `)}catch{}});let f=Ht(i)??u;wt(i,{...f,status:"done",exitCode:null,signal:null,finishedAt:new Date().toISOString()})}),{id:o,meta:u}}list(){Q();let t=[];try{t=ye.readdirSync(xe)}catch{return[]}let n=[];for(let r of t){if(!r.endsWith(".meta.json"))continue;let o=Ht(Br.join(xe,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(!ye.existsSync(r))return"(no log file)";let s=ye.readFileSync(r,"utf8").split(`
179
196
  `);return s.slice(Math.max(0,s.length-n)).join(`
180
- `).trimEnd()||"(empty log)"}readSince(t,n){let r=this.logPath(t);if(!ge.existsSync(r))return{text:"",nextOffset:n};let s=ge.statSync(r).size;if(n>=s)return{text:"",nextOffset:s};let i=Buffer.alloc(s-n),a=ge.openSync(r,"r");try{ge.readSync(a,i,0,i.length,n)}finally{ge.closeSync(a)}return{text:i.toString("utf8"),nextOffset:s}}kill(t){let n=this.metaPath(t),r=Ot(n);if(!r)return`Unknown job id: ${t}`;let o=this.running.get(t);if(o&&!o.killed)return o.kill("SIGTERM"),pt(n,{...r,status:"killed",signal:"SIGTERM"}),`Sent SIGTERM to job ${t} (pid ${r.pid??"?"})`;if(r.pid)try{return process.kill(r.pid,"SIGTERM"),pt(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=Ot(this.metaPath(t));r&&pt(this.metaPath(t),{...r,status:"killed",signal:"SIGTERM"})}this.running.clear()}};import ec from"node:fs";import{Bot as tc,InputFile as nc}from"grammy";import ei from"node:fs";import mt from"node:path";function $l(e){return e.replace(/[^a-zA-Z0-9._-]+/g,"_").slice(0,120)||"unknown"}function Cl(e){let r=mt.basename(e.trim()||"file").replace(/[^a-zA-Z0-9._-]+/g,"_").replace(/^\.+/,"").slice(0,180);return r.length>0?r:"file"}function Rl(e,t){let n=new Date().toISOString().slice(0,10),r=$l(t);return mt.join(e,r,n)}function bn(e,t,n){let r=Rl(e,t);P(r);let o=Cl(n),s=mt.join(r,o);if(!ei.existsSync(s))return s;let i=mt.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=mt.join(r,u),!ei.existsSync(s))return s}return mt.join(r,`${a}-${Date.now()}${i}`)}import Ml from"node:dns";import Tl from"node:https";import{URL as Il}from"node:url";function ft(e){if(e instanceof Error){let t=e.cause;return t!=null?`${e.message} (${String(t)})`:e.message}return String(e)}function ti(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 Al(e,t){let r=t.split("/").filter(o=>o.length>0).map(encodeURIComponent).join("/");return`https://api.telegram.org/file/bot${e}/${r}`}var ni="omnish (Telegram bot; https://github.com/labKnowledge/whatsLive)";function El(e){let t=new Il(e);return new Promise((n,r)=>{let o=Tl.request({hostname:t.hostname,port:t.port||443,path:t.pathname+t.search,method:"GET",headers:{"User-Agent":ni,Accept:"*/*"},lookup(s,i,a){Ml.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 ri(e,t,n){let r;try{r=await e.api.getFile(t)}catch(i){return T.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=Al(e.token,r.file_path),s;try{let i=await fetch(o,{headers:{"User-Agent":ni,Accept:"*/*"}});if(!i.ok)return T.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 T.warn({err:String(a),fileId:t},"telegram file read body failed"),{error:`Could not read file from Telegram (${String(a)}).`}}}catch(i){T.warn({err:String(i),fileId:t},"telegram file fetch failed; retrying via HTTPS IPv4");try{let a=await El(o);if(a.status<200||a.status>=300)return T.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 T.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: ${ft(i)}; IPv4 fallback: ${ft(a)}).`}}}return n>0&&s.byteLength>n?{error:`Media too large (max ${n} bytes).`}:{buffer:s}}import{downloadMediaMessage as Ol,extensionForMediaMessage as Pl,getContentType as kn,isJidGroup as ii,isLidUser as Ll}from"@whiskeysockets/baileys";import Fl from"node:fs";function Nl(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 _l(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 ai(e){return[Nl(e),_l(e)].filter(n=>n.trim().length>0).join(`
181
- `).trim()}function Mr(e){return e.replace(/[\u0000-\u0008\u000B\u000C\u000E-\u001F\u007F]/g,"").replace(/\uFEFF/g,"").trim()}function li(e){let t=kn(e??void 0);return t==="imageMessage"||t==="videoMessage"||t==="audioMessage"||t==="documentMessage"||t==="stickerMessage"}function Bl(e){let t=kn(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 oi(e){try{if(!e)return"file.bin";let t=Pl(e);return`file${t&&t.length>0?t.startsWith(".")?t:`.${t}`:".bin"}`}catch{return`${(kn(e??void 0)??"file").replace("Message","")||"file"}.bin`}}function Dl(e){let t=kn(e??void 0);if(!t)return oi(e);let n=e?.[t];return n?.fileName&&typeof n.fileName=="string"&&n.fileName.trim()?n.fileName.trim():oi(e)}async function si(e,t,n,r){let o=t.message??void 0;if(!li(o))return{};let s=r.fileReceiveMaxBytes,i=Bl(o);if(s>0&&i!==void 0&&i>s)return{error:`Media too large (max ${s} bytes).`};let a;try{a=await Ol(t,"buffer",{},{logger:e.logger,reuploadRequest:e.updateMediaMessage})}catch(d){return T.warn({err:String(d),detail:ft(d)},"whatsapp downloadMediaMessage failed"),{error:`Could not download media from WhatsApp (${ft(d)}).`}}if(s>0&&a.length>s)return{error:`Media too large (max ${s} bytes).`};let l;try{l=tt(r,n)}catch(d){return{error:String(d)}}let u=Dl(o),c=bn(l,n,u);try{Fl.writeFileSync(c,a,{mode:384})}catch{return{error:"Could not write media to inbox."}}return{path:c}}async function Hl(e,t){let n=t.remoteJid??"";if(!n)return{fromJid:"",fromE164:""};if(t.remoteJidAlt){let r=N(t.remoteJidAlt)??N(n)??"";return{fromJid:n,fromE164:r}}if(Ll(n))try{let r=await e.signalRepository?.lidMapping?.getPNForLID?.(n);if(r){let o=N(r)??"";if(o)return{fromJid:n,fromE164:o}}}catch{}return{fromJid:n,fromE164:N(n)??""}}function jl(e){try{if(!$().clusterEnabled)return;let n=ai(e.message??void 0);if(!n)return;let r=is(n);if(!r)return;let o=e.key?.remoteJid??"",s=null;if(o&&!ii(o)){let i=N(o);i&&(s=`wa:${i}`)}gs(r,s)}catch(t){T.warn({err:String(t)},"cluster footer observation failed")}}function ci(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){jl(o);continue}let i=s.remoteJid;if(!i||ii(i)||i.toLowerCase().endsWith("@status")||i==="status@broadcast")continue;let l=Mr(ai(o.message??void 0)),{fromJid:u,fromE164:c}=await Hl(e,s),d=`wa:${u}`,m=li(o.message??void 0);if(m&&!l){(async()=>{try{let w=$(),v=await si(e,o,d,w);await t({fromJid:u,fromE164:c,text:"",messageId:s.id??void 0,mediaSavedPath:v.path,mediaError:v.error})}catch(w){T.error({err:String(w),fromJid:u},"whatsapp media-only background task failed")}})();continue}let g,y;if(m){let w=$(),v=await si(e,o,d,w);g=v.path,y=v.error}!l&&!g&&!y||await t({fromJid:u,fromE164:c,text:l,messageId:s.id??void 0,mediaSavedPath:g,mediaError:y})}})()};return e.ev.on("messages.upsert",n),()=>{e.ev.off("messages.upsert",n)}}import Yl from"node:fs";import Wl from"node:process";import Ul,{DisconnectReason as qe,fetchLatestBaileysVersion as Gl,makeCacheableSignalKeyStore as zl,useMultiFileAuthState as Jl}from"@whiskeysockets/baileys";import ql from"qrcode-terminal";var Kl="0.1.0";function Tr(e){return e?.error?.output?.statusCode}async function Sn(e={}){Y();let t=e.authDir??Q,n=e.verbose===!0,r=zs(),{state:o,saveCreds:s}=await Jl(t),{version:i}=await Gl(),a=Ul({version:i,logger:r,printQRInTerminal:!1,browser:["omnish","cli",Kl],auth:{creds:o.creds,keys:zl(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=Wl.stdout,g=h(m,"\xB7".repeat(42));console.log(ee(m,"Scan with WhatsApp \u2192 Linked devices")),console.log(g),ql.generate(d,{small:!0}),console.log(g)}if(u==="close"){let m=Tr(c);n&&m===qe.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 xn(e){try{e.end(new Error("omnish: socket closed"))}catch{e.ws?.close()}}function Ir(e){return e?.output?.statusCode??e?.status??e?.error?.output?.statusCode}function vn(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 Ke(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 fi=3500,Vl=400,ui=18e4,di=9e4,pi=3e5;function Ar(e,t=fi){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(`
197
+ `).trimEnd()||"(empty log)"}readSince(t,n){let r=this.logPath(t);if(!ye.existsSync(r))return{text:"",nextOffset:n};let s=ye.statSync(r).size;if(n>=s)return{text:"",nextOffset:s};let i=Buffer.alloc(s-n),a=ye.openSync(r,"r");try{ye.readSync(a,i,0,i.length,n)}finally{ye.closeSync(a)}return{text:i.toString("utf8"),nextOffset:s}}kill(t){let n=this.metaPath(t),r=Ht(n);if(!r)return`Unknown job id: ${t}`;let o=this.running.get(t);if(o&&!o.killed)return o.kill("SIGTERM"),wt(n,{...r,status:"killed",signal:"SIGTERM"}),`Sent SIGTERM to job ${t} (pid ${r.pid??"?"})`;if(r.pid)try{return process.kill(r.pid,"SIGTERM"),wt(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=Ht(this.metaPath(t));r&&wt(this.metaPath(t),{...r,status:"killed",signal:"SIGTERM"})}this.running.clear()}};import Rc from"node:fs";import{Bot as Mc,InputFile as Tc}from"grammy";import wi from"node:fs";import bt from"node:path";function Vl(e){return e.replace(/[^a-zA-Z0-9._-]+/g,"_").slice(0,120)||"unknown"}function Xl(e){let r=bt.basename(e.trim()||"file").replace(/[^a-zA-Z0-9._-]+/g,"_").replace(/^\.+/,"").slice(0,180);return r.length>0?r:"file"}function Ql(e,t){let n=new Date().toISOString().slice(0,10),r=Vl(t);return bt.join(e,r,n)}function In(e,t,n){let r=Ql(e,t);B(r);let o=Xl(n),s=bt.join(r,o);if(!wi.existsSync(s))return s;let i=bt.extname(o),a=i?o.slice(0,-i.length):o;for(let l=1;l<1e4;l+=1){let d=`${a}-${l}${i}`;if(s=bt.join(r,d),!wi.existsSync(s))return s}return bt.join(r,`${a}-${Date.now()}${i}`)}import Zl from"node:dns";import ec from"node:https";import{URL as tc}from"node:url";function kt(e){if(e instanceof Error){let t=e.cause;return t!=null?`${e.message} (${String(t)})`:e.message}return String(e)}function bi(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 nc(e,t){let r=t.split("/").filter(o=>o.length>0).map(encodeURIComponent).join("/");return`https://api.telegram.org/file/bot${e}/${r}`}var ki="omnish (Telegram bot; https://github.com/labKnowledge/whatsLive)";function rc(e){let t=new tc(e);return new Promise((n,r)=>{let o=ec.request({hostname:t.hostname,port:t.port||443,path:t.pathname+t.search,method:"GET",headers:{"User-Agent":ki,Accept:"*/*"},lookup(s,i,a){Zl.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 Si(e,t,n){let r;try{r=await e.api.getFile(t)}catch(i){return R.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=nc(e.token,r.file_path),s;try{let i=await fetch(o,{headers:{"User-Agent":ki,Accept:"*/*"}});if(!i.ok)return R.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 R.warn({err:String(a),fileId:t},"telegram file read body failed"),{error:`Could not read file from Telegram (${String(a)}).`}}}catch(i){R.warn({err:String(i),fileId:t},"telegram file fetch failed; retrying via HTTPS IPv4");try{let a=await rc(o);if(a.status<200||a.status>=300)return R.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 R.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: ${kt(i)}; IPv4 fallback: ${kt(a)}).`}}}return n>0&&s.byteLength>n?{error:`Media too large (max ${n} bytes).`}:{buffer:s}}import{downloadMediaMessage as oc,extensionForMediaMessage as sc,getContentType as An,isJidGroup as $i,isLidUser as ic}from"@whiskeysockets/baileys";import ac from"node:fs";function lc(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 cc(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 Ci(e){return[lc(e),cc(e)].filter(n=>n.trim().length>0).join(`
198
+ `).trim()}function Dr(e){return e.replace(/[\u0000-\u0008\u000B\u000C\u000E-\u001F\u007F]/g,"").replace(/\uFEFF/g,"").trim()}function Ri(e){let t=An(e??void 0);return t==="imageMessage"||t==="videoMessage"||t==="audioMessage"||t==="documentMessage"||t==="stickerMessage"}function uc(e){let t=An(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 xi(e){try{if(!e)return"file.bin";let t=sc(e);return`file${t&&t.length>0?t.startsWith(".")?t:`.${t}`:".bin"}`}catch{return`${(An(e??void 0)??"file").replace("Message","")||"file"}.bin`}}function dc(e){let t=An(e??void 0);if(!t)return xi(e);let n=e?.[t];return n?.fileName&&typeof n.fileName=="string"&&n.fileName.trim()?n.fileName.trim():xi(e)}async function vi(e,t,n,r){let o=t.message??void 0;if(!Ri(o))return{};let s=r.fileReceiveMaxBytes,i=uc(o);if(s>0&&i!==void 0&&i>s)return{error:`Media too large (max ${s} bytes).`};let a;try{a=await oc(t,"buffer",{},{logger:e.logger,reuploadRequest:e.updateMediaMessage})}catch(u){return R.warn({err:String(u),detail:kt(u)},"whatsapp downloadMediaMessage failed"),{error:`Could not download media from WhatsApp (${kt(u)}).`}}if(s>0&&a.length>s)return{error:`Media too large (max ${s} bytes).`};let l;try{l=it(r,n)}catch(u){return{error:String(u)}}let d=dc(o),c=In(l,n,d);try{ac.writeFileSync(c,a,{mode:384})}catch{return{error:"Could not write media to inbox."}}return{path:c}}async function pc(e,t){let n=t.remoteJid??"";if(!n)return{fromJid:"",fromE164:""};if(t.remoteJidAlt){let r=D(t.remoteJidAlt)??D(n)??"";return{fromJid:n,fromE164:r}}if(ic(n))try{let r=await e.signalRepository?.lidMapping?.getPNForLID?.(n);if(r){let o=D(r)??"";if(o)return{fromJid:n,fromE164:o}}}catch{}return{fromJid:n,fromE164:D(n)??""}}function mc(e){try{if(!C().clusterEnabled)return;let n=Ci(e.message??void 0);if(!n)return;let r=bs(n);if(!r)return;let o=e.key?.remoteJid??"",s=null;if(o&&!$i(o)){let i=D(o);i&&(s=`wa:${i}`)}Ts(r,s)}catch(t){R.warn({err:String(t)},"cluster footer observation failed")}}function Mi(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){mc(o);continue}let i=s.remoteJid;if(!i||$i(i)||i.toLowerCase().endsWith("@status")||i==="status@broadcast")continue;let l=Dr(Ci(o.message??void 0)),{fromJid:d,fromE164:c}=await pc(e,s),u=`wa:${d}`,m=Ri(o.message??void 0);if(m&&!l){(async()=>{try{let y=C(),S=await vi(e,o,u,y);await t({fromJid:d,fromE164:c,text:"",messageId:s.id??void 0,mediaSavedPath:S.path,mediaError:S.error})}catch(y){R.error({err:String(y),fromJid:d},"whatsapp media-only background task failed")}})();continue}let f,w;if(m){let y=C(),S=await vi(e,o,u,y);f=S.path,w=S.error}!l&&!f&&!w||await t({fromJid:d,fromE164:c,text:l,messageId:s.id??void 0,mediaSavedPath:f,mediaError:w})}})()};return e.ev.on("messages.upsert",n),()=>{e.ev.off("messages.upsert",n)}}import Sc from"node:fs";import fc from"node:process";import gc,{DisconnectReason as Qe,fetchLatestBaileysVersion as hc,makeCacheableSignalKeyStore as yc,useMultiFileAuthState as wc}from"@whiskeysockets/baileys";import bc from"qrcode-terminal";var kc="0.1.0";function Hr(e){return e?.error?.output?.statusCode}async function En(e={}){Q();let t=e.authDir??te,n=e.verbose===!0,r=Ks(),{state:o,saveCreds:s}=await wc(t),{version:i}=await hc(),a=gc({version:i,logger:r,printQRInTerminal:!1,browser:["omnish","cli",kc],auth:{creds:o.creds,keys:yc(o.keys,r)},syncFullHistory:!1,markOnlineOnConnect:!1});return a.ev.on("creds.update",s),a.ev.on("connection.update",l=>{let{connection:d,lastDisconnect:c,qr:u}=l;if(u&&(e.onQr?.(u),e.printQr)){let m=fc.stdout,f=h(m,"\xB7".repeat(42));console.log(re(m,"Scan with WhatsApp \u2192 Linked devices")),console.log(f),bc.generate(u,{small:!0}),console.log(f)}if(d==="close"){let m=Hr(c);n&&m===Qe.loggedOut&&r.warn("WhatsApp session logged out (401).")}d==="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 On(e){try{e.end(new Error("omnish: socket closed"))}catch{e.ws?.close()}}function jr(e){return e?.output?.statusCode??e?.status??e?.error?.output?.statusCode}function Ln(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 Ze(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 Oi=3500,xc=400,Ti=18e4,Ii=9e4,Ai=3e5;function Wr(e,t=Oi){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(`
182
199
 
183
- `);i>Math.floor(t*.35)&&(o=r+i+2)}n.push(e.slice(r,o)),r=o}return n}function $n(e){return new Promise(t=>setTimeout(t,e))}async function mi(e,t){await Promise.race([e,$n(ui).then(()=>{T.warn({jid:t,ms:ui},"whatsapp outbound self-heal: prior send chain waited too long; continuing")})])}async function Ql(e,t,n,r=3){let o;for(let s=1;s<=r;s+=1)try{await Ke(e.sendMessage(t,{text:n}),di,`whatsapp sendMessage timed out after ${di}ms`);return}catch(i){o=i;let a=String(i);if(/not connected|closed|timed out|timeout/i.test(a)&&s<r){await $n(800*s);continue}throw i}throw o}function Zl(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 Xl(e,t,n,r=3){let o;for(let s=1;s<=r;s+=1)try{await Ke(e.sendMessage(t,n),pi,`whatsapp sendMedia timed out after ${pi}ms`);return}catch(i){o=i;let a=String(i);if(/not connected|closed|timed out|timeout/i.test(a)&&s<r){await $n(800*s);continue}throw i}throw o}function gi(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=mi(a,o).then(async()=>{let u=Ar(i,fi);for(let c=0;c<u.length;c+=1)await Ql(e,o,u[c]??""),c<u.length-1&&await $n(Vl)}).catch(u=>{T.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=Yl.readFileSync(s.absPath),a=Zl(s,i),l=n.get(o)??Promise.resolve(),u=mi(l,o).then(async()=>{await Xl(e,o,a)}).catch(c=>{T.error({err:String(c),jid:o},"sendMedia failed")});n.set(o,u),await u.finally(()=>{n.get(o)===u&&n.delete(o)})}}}var rc=400;async function hi(e,t,n,r){let o=t(),s=await ri(e,r.fileId,o.fileReceiveMaxBytes);if("error"in s)return{mediaError:s.error};let i;try{i=tt(o,n)}catch(l){return{mediaError:String(l)}}let a=bn(i,n,r.baseName);try{return ec.writeFileSync(a,s.buffer,{mode:384}),{mediaSavedPath:a}}catch{return{mediaError:"Could not write media to inbox."}}}function oc(e){return new Promise(t=>setTimeout(t,e))}async function Er(e,t,n,r={}){let o=new tc(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),g=Ce(d,"telegram"),y=i(g.text,xr(c)),w=g.parseModeHtml,E=(s.get(c)??Promise.resolve()).then(async()=>{let U=Ar(y,m);for(let se=0;se<U.length;se+=1){let G=U[se]??"",ce=w?{parse_mode:"HTML"}:void 0;try{await o.api.sendMessage(c,G,ce)}catch(F){if(w){T.warn({err:String(F),chatId:c},"telegram HTML send failed; retrying plain");let $e=G.replace(/<[^>]+>/g,"");await o.api.sendMessage(c,$e)}else throw F}se<U.length-1&&await oc(rc)}}).catch(U=>{T.error({err:String(U),chatId:c},"telegram sendText failed")});s.set(c,E),await E.finally(()=>{s.get(c)===E&&s.delete(c)})}async function l(c,d){let m=new nc(d.absPath,d.displayName),g=d.caption,w=(s.get(c)??Promise.resolve()).then(async()=>{switch(d.category){case"image":await o.api.sendPhoto(c,m,g?{caption:g}:void 0);break;case"video":await o.api.sendVideo(c,m,g?{caption:g}:void 0);break;case"audio":await o.api.sendAudio(c,m,g?{caption:g}:void 0);break;case"document":await o.api.sendDocument(c,m,g?{caption:g}:void 0);break}}).catch(v=>{T.error({err:String(v),chatId:c},"telegram sendMedia failed")});s.set(c,w),await w.finally(()=>{s.get(c)===w&&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 g="text"in m&&m.text?m.text:"",y="caption"in m&&m.caption?m.caption:"",w=Mr([g,y].filter(Boolean).join(`
184
- `)),v=ti(m),E=xr(d.id),U=d.id,se=async F=>{F.kind==="file"?await l(U,F.spec):await a(U,F.body)};if(v&&!w){(async()=>{try{let F=await hi(o,t,E,v);if(!F.mediaSavedPath&&!F.mediaError)return;await n({peerKey:E,text:"",tgChatId:d.id,tgReplyToMessageId:m.message_id,mediaSavedPath:F.mediaSavedPath,mediaError:F.mediaError},se)}catch(F){T.error({err:String(F),chatId:U},"telegram media-only background task failed")}})();return}let G,ce;if(v){let F=await hi(o,t,E,v);G=F.mediaSavedPath,ce=F.mediaError}!w&&!G&&!ce||await n({peerKey:E,text:w,tgChatId:d.id,tgReplyToMessageId:m.message_id,mediaSavedPath:G,mediaError:ce},se)}),o.catch(c=>{T.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 sc from"node:fs";import ic from"node:path";function ac(e){let t=ic.join(e,"creds.json");try{let n=sc.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 yi(e){let t=ac(e);if(!t)return null;let n=t.phoneNumber?.trim();if(n){let s=N(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=N(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 Cn from"node:fs";import He from"node:process";function wi(e){let t=String(e).toLowerCase();return t.includes("401")||t.includes("logged out")?!0:Ir(e)===qe.loggedOut}async function lc(e,t){let n=He.stdout;console.log(`
185
- ${te(n,"omnish link")} ${h(n,"\u2014 QR appears below; scan from WhatsApp \u2192 Linked devices.")}
186
- `);let r=!1;for(let o=0;o<3;o++){let s=await Sn({authDir:e,printQr:!0,verbose:t,onQr:()=>{}});try{await Ke(vn(s),6e5,"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."),console.log(`
187
- ${H(n,"Linked.")} ${b(n,"Session saved. You can run")} ${H(n,"omnish run")} ${b(n,"now.")}
188
- `);return}catch(i){if(Ir(i)===qe.restartRequired&&!r){r=!0,console.warn(`
189
- ${ne(He.stderr,"WhatsApp requested a restart after pairing (code 515). This is normal. Opening a new connection\u2026")}
190
- `),await new Promise(l=>setTimeout(l,1500));continue}throw i}finally{xn(s)}}throw new Error("Pairing failed after restart (515) retries.")}async function bi(e={}){let t=e.authDir??Q,n=e.verbose===!0;Y(),e.force&&(Cn.rmSync(t,{recursive:!0,force:!0}),Cn.mkdirSync(t,{recursive:!0,mode:448}),console.log(`${ke(He.stdout,"Cleared saved session (--force).")} ${h(He.stdout,"Requesting a new QR\u2026")}
191
- `));for(let r=1;r<=2;r++)try{await lc(t,n);return}catch(o){if(r===1&&wi(o)){console.warn(`
192
- ${ne(He.stderr,"WhatsApp returned logged-out (401). This often happens after Ctrl+C during link or corrupt auth files.")}
193
- ${ne(He.stderr,"Clearing auth dir and retrying once with a fresh QR\u2026")}
194
- `),Cn.rmSync(t,{recursive:!0,force:!0}),Cn.mkdirSync(t,{recursive:!0,mode:448});continue}throw wi(o)&&console.error(`
195
- ${C(He.stderr,"Still failing after a clean auth directory. Try:")}
196
- ${h(He.stderr,` pnpm approve-builds && pnpm install
200
+ `);i>Math.floor(t*.35)&&(o=r+i+2)}n.push(e.slice(r,o)),r=o}return n}function Pn(e){return new Promise(t=>setTimeout(t,e))}async function Ei(e,t){await Promise.race([e,Pn(Ti).then(()=>{R.warn({jid:t,ms:Ti},"whatsapp outbound self-heal: prior send chain waited too long; continuing")})])}async function vc(e,t,n,r=3){let o;for(let s=1;s<=r;s+=1)try{await Ze(e.sendMessage(t,{text:n}),Ii,`whatsapp sendMessage timed out after ${Ii}ms`);return}catch(i){o=i;let a=String(i);if(/not connected|closed|timed out|timeout/i.test(a)&&s<r){await Pn(800*s);continue}throw i}throw o}function $c(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 Cc(e,t,n,r=3){let o;for(let s=1;s<=r;s+=1)try{await Ze(e.sendMessage(t,n),Ai,`whatsapp sendMedia timed out after ${Ai}ms`);return}catch(i){o=i;let a=String(i);if(/not connected|closed|timed out|timeout/i.test(a)&&s<r){await Pn(800*s);continue}throw i}throw o}function Li(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=Ei(a,o).then(async()=>{let d=Wr(i,Oi);for(let c=0;c<d.length;c+=1)await vc(e,o,d[c]??""),c<d.length-1&&await Pn(xc)}).catch(d=>{R.error({err:String(d),jid:o},"sendText failed")});n.set(o,l),await l.finally(()=>{n.get(o)===l&&n.delete(o)})},async sendMedia(o,s){let i=Sc.readFileSync(s.absPath),a=$c(s,i),l=n.get(o)??Promise.resolve(),d=Ei(l,o).then(async()=>{await Cc(e,o,a)}).catch(c=>{R.error({err:String(c),jid:o},"sendMedia failed")});n.set(o,d),await d.finally(()=>{n.get(o)===d&&n.delete(o)})}}}var Ic=400;async function Pi(e,t,n,r){let o=t(),s=await Si(e,r.fileId,o.fileReceiveMaxBytes);if("error"in s)return{mediaError:s.error};let i;try{i=it(o,n)}catch(l){return{mediaError:String(l)}}let a=In(i,n,r.baseName);try{return Rc.writeFileSync(a,s.buffer,{mode:384}),{mediaSavedPath:a}}catch{return{mediaError:"Could not write media to inbox."}}}function Ac(e){return new Promise(t=>setTimeout(t,e))}async function Ur(e,t,n,r={}){let o=new Mc(e);await o.api.deleteWebhook({drop_pending_updates:!1});let s=new Map,i=r.decorate??(c=>c);async function a(c,u){let m=Math.min(t().appsMaxWaChars,4096),f=Ee(u,"telegram"),w=i(f.text,Pr(c)),y=f.parseModeHtml,F=(s.get(c)??Promise.resolve()).then(async()=>{let I=Wr(w,m);for(let L=0;L<I.length;L+=1){let Y=I[L]??"",W=y?{parse_mode:"HTML"}:void 0;try{await o.api.sendMessage(c,Y,W)}catch(_){if(y){R.warn({err:String(_),chatId:c},"telegram HTML send failed; retrying plain");let ge=Y.replace(/<[^>]+>/g,"");await o.api.sendMessage(c,ge)}else throw _}L<I.length-1&&await Ac(Ic)}}).catch(I=>{R.error({err:String(I),chatId:c},"telegram sendText failed")});s.set(c,F),await F.finally(()=>{s.get(c)===F&&s.delete(c)})}async function l(c,u){let m=new Tc(u.absPath,u.displayName),f=u.caption,y=(s.get(c)??Promise.resolve()).then(async()=>{switch(u.category){case"image":await o.api.sendPhoto(c,m,f?{caption:f}:void 0);break;case"video":await o.api.sendVideo(c,m,f?{caption:f}:void 0);break;case"audio":await o.api.sendAudio(c,m,f?{caption:f}:void 0);break;case"document":await o.api.sendDocument(c,m,f?{caption:f}:void 0);break}}).catch(S=>{R.error({err:String(S),chatId:c},"telegram sendMedia failed")});s.set(c,y),await y.finally(()=>{s.get(c)===y&&s.delete(c)})}o.on("message",async c=>{let u=c.chat,m=c.message;if(!u||u.type!=="private"||!c.from||!m)return;let f="text"in m&&m.text?m.text:"",w="caption"in m&&m.caption?m.caption:"",y=Dr([f,w].filter(Boolean).join(`
201
+ `)),S=bi(m),F=Pr(u.id),I=u.id,L=async _=>{_.kind==="file"?await l(I,_.spec):await a(I,_.body)};if(S&&!y){(async()=>{try{let _=await Pi(o,t,F,S);if(!_.mediaSavedPath&&!_.mediaError)return;await n({peerKey:F,text:"",tgChatId:u.id,tgReplyToMessageId:m.message_id,mediaSavedPath:_.mediaSavedPath,mediaError:_.mediaError},L)}catch(_){R.error({err:String(_),chatId:I},"telegram media-only background task failed")}})();return}let Y,W;if(S){let _=await Pi(o,t,F,S);Y=_.mediaSavedPath,W=_.mediaError}!y&&!Y&&!W||await n({peerKey:F,text:y,tgChatId:u.id,tgReplyToMessageId:m.message_id,mediaSavedPath:Y,mediaError:W},L)}),o.catch(c=>{R.error({err:String(c)},"telegram bot error")});let d=o.start();return{bot:o,sendText:a,sendMedia:l,stop:async()=>{await o.stop(),await d.catch(()=>{})}}}import Ec from"node:fs";import Oc from"node:path";function Lc(e){let t=Oc.join(e,"creds.json");try{let n=Ec.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 Fi(e){let t=Lc(e);if(!t)return null;let n=t.phoneNumber?.trim();if(n){let s=D(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=D(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 Fn from"node:fs";import Je from"node:process";function Ni(e){let t=String(e).toLowerCase();return t.includes("401")||t.includes("logged out")?!0:jr(e)===Qe.loggedOut}async function Pc(e,t){let n=Je.stdout;console.log(`
202
+ ${oe(n,"omnish link")} ${h(n,"\u2014 QR appears below; scan from WhatsApp \u2192 Linked devices.")}
203
+ `);let r=!1;for(let o=0;o<3;o++){let s=await En({authDir:e,printQr:!0,verbose:t,onQr:()=>{}});try{await Ze(Ln(s),6e5,"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."),console.log(`
204
+ ${z(n,"Linked.")} ${k(n,"Session saved. You can run")} ${z(n,"omnish run")} ${k(n,"now.")}
205
+ `);return}catch(i){if(jr(i)===Qe.restartRequired&&!r){r=!0,console.warn(`
206
+ ${se(Je.stderr,"WhatsApp requested a restart after pairing (code 515). This is normal. Opening a new connection\u2026")}
207
+ `),await new Promise(l=>setTimeout(l,1500));continue}throw i}finally{On(s)}}throw new Error("Pairing failed after restart (515) retries.")}async function _i(e={}){let t=e.authDir??te,n=e.verbose===!0;Q(),e.force&&(Fn.rmSync(t,{recursive:!0,force:!0}),Fn.mkdirSync(t,{recursive:!0,mode:448}),console.log(`${ve(Je.stdout,"Cleared saved session (--force).")} ${h(Je.stdout,"Requesting a new QR\u2026")}
208
+ `));for(let r=1;r<=2;r++)try{await Pc(t,n);return}catch(o){if(r===1&&Ni(o)){console.warn(`
209
+ ${se(Je.stderr,"WhatsApp returned logged-out (401). This often happens after Ctrl+C during link or corrupt auth files.")}
210
+ ${se(Je.stderr,"Clearing auth dir and retrying once with a fresh QR\u2026")}
211
+ `),Fn.rmSync(t,{recursive:!0,force:!0}),Fn.mkdirSync(t,{recursive:!0,mode:448});continue}throw Ni(o)&&console.error(`
212
+ ${M(Je.stderr,"Still failing after a clean auth directory. Try:")}
213
+ ${h(Je.stderr,` pnpm approve-builds && pnpm install
197
214
  (pnpm may have skipped Baileys/sharp/protobuf build scripts.)
198
215
  Then: omnish link --force
199
- `)}`),o}}import cc from"node:crypto";import Or from"node:fs";import uc from"node:net";var Pt=null;function dc(){try{let e=Or.readFileSync(Ye,"utf8"),t=JSON.parse(e);return typeof t.token=="string"?t.token:""}catch{return""}}function pc(e){Or.writeFileSync(Ye,JSON.stringify(e,null,2)+`
200
- `,{mode:384})}function mc(){try{Or.unlinkSync(Ye)}catch{}}async function fc(e,t,n){let r;try{r=JSON.parse(e)}catch{return{ok:!1,error:"Invalid JSON."}}if(!r||typeof r!="object"||r.op!=="sendMedia")return{ok:!1,error:"Unsupported operation."};if(typeof r.token!="string"||!n||r.token!==n)return{ok:!1,error:"Unauthorized."};let o=t.getCfg(),s=typeof r.absPath=="string"?r.absPath.trim():"";if(!s)return{ok:!1,error:"Missing absPath."};let i=typeof r.caption=="string"&&r.caption.length>0?r.caption:void 0,a=Xe(s,o.fileSendMaxBytes);if("error"in a)return{ok:!1,error:a.error};let l={absPath:a.absPath,category:a.category,mimetype:a.mimetype,displayName:a.displayName,caption:i};if(r.channel==="whatsapp"){let u=t.getWaOutbound();if(!u)return{ok:!1,error:"WhatsApp outbound is not connected."};let c=o.gatewayMode;if(c!=="whatsapp"&&c!=="both")return{ok:!1,error:"gatewayMode does not include WhatsApp."};let d=typeof r.e164=="string"?r.e164.trim():"";if(!d.startsWith("+"))return{ok:!1,error:"WhatsApp destination must be E.164 (e.g. +15551234567)."};let m=_t(d);try{return await u.sendMedia(m,l),{ok:!0}}catch(g){return{ok:!1,error:String(g)}}}if(r.channel==="telegram"){let u=t.getTgSendMedia();if(!u)return{ok:!1,error:"Telegram outbound is not connected."};let c=o.gatewayMode;if(c!=="telegram"&&c!=="both")return{ok:!1,error:"gatewayMode does not include Telegram."};if(!Number.isFinite(r.chatId))return{ok:!1,error:"Invalid Telegram chat id."};try{return await u(r.chatId,l),{ok:!0}}catch(d){return{ok:!1,error:String(d)}}}return{ok:!1,error:"Unknown channel."}}function ki(e){if(Pt)return;let t=cc.randomBytes(32).toString("hex"),n=uc.createServer(r=>{let o="";r.setTimeout(12e4),r.on("data",s=>{o+=s.toString("utf8");let i=o.indexOf(`
201
- `);if(i===-1)return;let a=o.slice(0,i).trim();o=o.slice(i+1);let l=dc();fc(a,e,l).then(u=>{r.write(`${JSON.stringify(u)}
202
- `),r.end()})}),r.on("error",()=>{})});n.listen(0,"127.0.0.1",()=>{let r=n.address();if(!r||typeof r=="string"){T.error("gateway control: could not read listen address");return}let o={token:t,host:r.address,port:r.port};pc(o),T.info({port:r.port},"gateway control listening")}),n.on("error",r=>{T.error({err:String(r)},"gateway control server error")}),Pt=n}function Rn(){if(Pt){try{Pt.close()}catch{}Pt=null,mc()}}import wc from"node:readline";import Fr from"node:path";import oe from"node:process";import gc from"node:net";import hc from"node:fs";function yc(){try{let e=hc.readFileSync(Ye,"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 Pr(e){let t=yc();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)}
203
- `;return new Promise(o=>{let s=!1,i="";function a(u){s||(s=!0,o(u))}let l=gc.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(`
204
- `);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 Si(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=s.indexOf(" -- "),a,l;if(i>=0?(a=s.slice(0,i).trim(),l=s.slice(i+4).trim()||void 0):a=s,(a.startsWith('"')&&a.endsWith('"')||a.startsWith("'")&&a.endsWith("'"))&&(a=a.slice(1,-1)),!a)return null;let u=o.toLowerCase();if(u.startsWith("tg:")||u.startsWith("telegram:")){let c=ue(o);if(!c)return null;let d=Number(c);return Number.isFinite(d)?{channel:"telegram",chatId:d,filePart:a,caption:l}:null}if(u.startsWith("wa:")){let c=N(o.slice(3));return c?{channel:"whatsapp",e164:c,filePart:a,caption:l}:null}if(o.startsWith("+")){let c=N(o);return c?{channel:"whatsapp",e164:c,filePart:a,caption:l}:null}return null}function Lr(){return["/sendto wa:+E164 <file> [-- caption]","/sendto tg:<chat_id> <file> [-- caption]","Also: /sendto +E164 <file> (WhatsApp). Requires `omnish run` on this machine."].join(`
205
- `)}var gt="wa:cli:interactive";function bc(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=N(r);return o?`wa:${o}`:null}function kc(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=bc(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 Nr(e){let t=oe.cwd(),n=[`${H(e,"omnish i")} ${h(e,"[options]")}`,ee(e,"Interactive shell \u2014 same commands as WhatsApp/Telegram chat."),"",q(e,"Usage:"),` ${b(e,"omnish i [options]")}`,` ${b(e,"omnish interactive [options]")}`,"",q(e,"Options:"),...Le(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=>h(e,r)),"",`${h(e,"Trust:")} ${b(e,"Full local access like your shell; not gated by inbox allowlist.")}`,`${h(e,"Jobs:")} ${b(e,"/bg and /jobs apply only to this REPL session (not the gateway process).")}`,`${h(e,"Files:")} ${b(e,"Use /sendto to push a host file through the gateway; plain /send needs a chat peer.")}`,`${h(e,"Gateway:")} ${b(e,"/reload and /updates require omnish run; /sendto requires omnish run for WA/TG delivery.")}`,"",Lr(),"",`${h(e,"cwd:")} ${b(e,`session starts at ${t} (change with !cd or ${$().commandPrefix}cd).`)}`];console.log(n.join(`
206
- `))}async function Sc(e){let t=$(),n=B(gt).cwd,r=Fr.resolve(n,e.filePart),o=Xe(r,t.fileSendMaxBytes);return"error"in o?o.error:e.channel==="whatsapp"?await Pr({op:"sendMedia",channel:"whatsapp",e164:e.e164,absPath:o.absPath,caption:e.caption}):await Pr({op:"sendMedia",channel:"telegram",chatId:e.chatId,absPath:o.absPath,caption:e.caption})}async function xc(e,t,n,r,o,s,i){let a=e.trim();if(!a)return;let l=Si(a);if(l!==null||/^\/sendto(\s|$)/i.test(a)){if(l===null){console.log(C(oe.stderr,`Invalid /sendto.
207
- `+Lr()));return}let d=await Sc(l);console.log(d?C(oe.stderr,d):ne(oe.stdout,"Sent."));return}let u={peerKey:gt,text:e},c=await ze($(),t,n,r,o,u,s,void 0,i);c!==null&&await vc(c)}async function vc(e){if(e===null)return;if(e.kind==="file"){console.log(ne(oe.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(`
208
- `)));return}let t=Ce(e.body,"whatsapp").text;t.trim()&&console.log(t)}async function xi(e){let t=kc(e);if(t.error==="help"){Nr(oe.stdout);return}if(t.error&&t.error!==null){console.error(C(oe.stderr,t.error)),console.error(h(oe.stderr,"Try: omnish i --help")),oe.exitCode=1;return}let{senderKey:n,oneShot:r}=t.opts,o=n??gt;Y(),Jt(gt,oe.cwd());let s=new Je,i=new Map,a=new Map,l=new Map,u=new dt(()=>$(),async(g,y)=>{oe.stdout.write(y),y.endsWith(`
209
- `)||oe.stdout.write(`
210
- `)}),c=async g=>{try{await xc(g,s,i,a,l,u,o)}catch(y){console.error(C(oe.stderr,String(y)))}};if(r!==null){await c(r),u.dispose(),s.killAllRunning();return}let d=wc.createInterface({input:oe.stdin,output:oe.stdout}),m=Fr.basename(B(gt).cwd);d.setPrompt(`${m}> `),d.on("line",g=>{c(g).then(()=>{let y=Fr.basename(B(gt).cwd);d.setPrompt(`${y}> `),d.prompt()})}),d.on("close",()=>{u.dispose(),s.killAllRunning(),oe.stdout.write(`
211
- `)}),d.prompt()}$c.setDefaultResultOrder("ipv4first");function Ci(){let e=process.stdout,t=[`${te(e,"omnish run")} ${h(e,"[options]")}`,ee(e,"Listen for DMs and run shell commands from allowlisted chats."),"",q(e,"Usage:"),` ${b(e,"omnish run [options]")}`,"",q(e,"Options:"),...Le(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: ${he}).`},{left:"-h, --help",right:"Show this help."}],n=>h(e,n)),"",`${b(e,"Config reload:")} ${h(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(`
212
- `))}function Ri(){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=>h(e,i.left)),r=Math.max(...n.map(Kt)),o=t.map((i,a)=>Fn(" ",r,n[a],b(e,i.right))),s=[`${te(e,"omnish link")} ${h(e,"[options]")}`,ee(e,"Connect WhatsApp (QR) or save a Telegram bot token."),"",q(e,"Usage:"),` ${b(e,"omnish link [--force]")}`,` ${b(e,"omnish link --tg <bot_token>")}`,"",q(e,"Modes:"),...o,` ${ee(e,"Do not combine --tg with --force.")}`,"",q(e,"Options:"),...Le(e," ",[{left:"-f, --force",right:"WhatsApp only: delete saved session before pairing."},{left:"-h, --help",right:"Show this help."}],i=>h(e,i)),"",`${b(e,"Next:")} ${H(e,"omnish allow tg:<your_user_id>")} ${h(e,"then")} ${H(e,"omnish run")}`,`${h(e,"Config:")} ${b(e,R)}`,""];console.log(s.join(`
213
- `))}function Mc(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}
214
- 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 _r(){let e=process.stdout,t=`${te(e,"omnish")} ${h(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: ${Z})`},{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)"}],r=[{left:"-v, --version",right:"Print version and exit."},{left:"-h, --help",right:"Show this help (same as omnish help)."}],o=[t,ee(e,"Allowlisted inbox \u2192 your real shell. No AI."),"",q(e,"Usage:"),` ${b(e,"omnish [options] <command> [args...]")}`,"",q(e,"Options:"),...Le(e," ",r,s=>h(e,s)),"",q(e,"Commands:"),...Le(e," ",n,s=>h(e,s)),"",`${h(e,"Config:")} ${b(e,`${R} \u2014 gatewayMode: "whatsapp" | "telegram" | "both"`)}`,`${h(e,"Env:")} ${b(e,"OMNISH_VERBOSE=1 (Baileys; legacy WHATSVERBOSE=1 still works), TELEGRAM_BOT_TOKEN (optional)")}`,`${h(e,"Data:")} ${b(e,"~/.omnish by default; ~/.whatslive reused if it already exists. OMNISH_HOME overrides.")}`,`${h(e,"See also:")} ${b(e,"https://omnish.dev")}`,""];console.log(o.join(`
215
- `))}function Tc(e){let t=(e??"").trim().toLowerCase(),n=process.stdout;if(!t){_r();return}switch(t){case"link":Ri();return;case"run":Ci();return;case"service":Ti();return;case"i":case"interactive":Nr(n);return;default:console.error(C(process.stderr,`No detailed help for "${e}". Try: omnish help`)),process.exitCode=1}}function Ic(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()!==""?Mn.isAbsolute(n)?n:Mn.resolve(process.cwd(),n):he;return{background:t,logFile:o,help:r}}function Ac(e){Y(),P(Mn.dirname(e));let t=process.argv[1];t||(console.error(C(process.stderr,"cannot resolve entry script; invoke via node path/to/dist/index.js.")),process.exit(1));let n=fe.openSync(e,"a"),r=Cc(process.execPath,[t,"run"],{detached:!0,stdio:["ignore",n,n],env:{...process.env,OMNISH_BACKGROUND_GATEWAY:"1"}});fe.closeSync(n),r.unref(),r.pid||(console.error(C(process.stderr,"failed to start background gateway.")),process.exit(1));let o=process.stdout;console.log(`${ne(o,`gateway started in background (pid ${r.pid}).`)} ${h(o,`Log: ${e}`)}`)}function Ec(){if(Y(),!fe.existsSync(Z)){console.error(C(process.stderr,`no pidfile at ${Z} \u2014 is a background gateway running?`)),process.exitCode=1;return}let e=fe.readFileSync(Z,"utf8").trim(),t=Number(e);if(!Number.isFinite(t)||t<=0){console.error(C(process.stderr,"invalid pidfile."));try{fe.unlinkSync(Z)}catch{}process.exitCode=1;return}try{process.kill(t,0)}catch{console.log(ne(process.stdout,`process ${t} is not running; removing stale pidfile.`));try{fe.unlinkSync(Z)}catch{}return}try{process.kill(t,"SIGTERM"),console.log(ne(process.stdout,`sent SIGTERM to gateway (pid ${t}).`))}catch(n){if(process.platform==="win32"&&Rc("taskkill",["/PID",String(t),"/T"],{windowsHide:!0}).status===0){console.log(ne(process.stdout,`stopped gateway (pid ${t}) using taskkill.`));return}console.error(C(process.stderr,`could not signal process: ${String(n)}`)),process.exitCode=1}}function vi(){if(process.env.OMNISH_BACKGROUND_GATEWAY==="1")try{fe.readFileSync(Z,"utf8").trim()===String(process.pid)&&fe.unlinkSync(Z)}catch{}}function Oc(e){return e.length<=8?"(set)":`${e.slice(0,4)}\u2026${e.slice(-4)}`}function Pc(){if(!fe.existsSync(Z))return"gateway process: not running (no pid file)";let e=fe.readFileSync(Z,"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 Mi=120;function Ti(){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 ${Mi}).`},{left:"install",right:"Write user systemd / LaunchAgent unit (needs serviceInstallFromChat=true)."},{left:"uninstall",right:"Remove that unit (same gate as install)."}],n=[`${te(e,"omnish service")} ${h(e,"<subcommand>")}`,ee(e,"Boot integration, crash restart policy, and logs (same ideas as /service in chat)."),"",q(e,"Usage:"),` ${b(e,"omnish service <subcommand>")}`,"",q(e,"Subcommands:"),...Le(e," ",t,r=>h(e,r)),"",q(e,"Restart and reload:"),` ${b(e,"Process")} ${h(e,"\u2014 Linux user unit: Restart=on-failure, RestartSec=5. macOS: KeepAlive. Full restart loads config from disk.")}`,` ${b(e,"Config live")} ${h(e,"\u2014 allowlisted chat while gateway runs: /reload or /restart")}`,"",`${h(e,"Docs:")} ${b(e,"docs/guides/background-and-boot.md")} ${ee(e,"\xB7")} https://omnish.dev`,""];console.log(n.join(`
216
- `))}function Lc(e){let t=process.stdout,n=process.stderr,r=(e[0]??"help").toLowerCase();if(r==="help"||r==="--help"||r==="-h"){Ti();return}if(r==="instructions"){let o=Ge();if(o.error){console.error(C(n,o.error)),process.exitCode=1;return}console.log(cn(o));return}if(r==="status"){let o=Ge(),s=(()=>{try{return fe.existsSync(Z)?`gateway.pid: ${fe.readFileSync(Z,"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}
217
- Script: ${o.scriptPath}`,c=$().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(te(t,"omnish service status")),console.log(""),console.log(`${h(t,"platform:")} ${b(t,process.platform)}`),console.log(`${h(t,"session:")} ${b(t,i)}`),console.log(`${h(t,"env:")} ${b(t,a)}`),console.log(`${h(t,"data dir:")} ${b(t,A)}`),console.log(`${h(t,"pidfile:")} ${b(t,s)}`),console.log(`${h(t,"default log:")} ${b(t,he)}`),console.log(""),console.log(l),console.log(""),console.log(b(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,Mi):80,i=mn(he,s);console.log(`${h(t,"file:")} ${b(t,he)}`),console.log(`${h(t,"lines:")} ${b(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=un();s.ok?console.log(ne(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=dn();s.ok?console.log(ne(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 Fc(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=N(r);return o?`wa:${o}`:null}async function Nc(){let e=null,t=$(),n=t.gatewayMode,r=n==="whatsapp"||n==="both",o=n==="telegram"||n==="both",s=de(t);o&&!s&&(console.error(C(process.stderr,`Telegram enabled (gatewayMode) but no bot token. Set telegramBotToken in config or TELEGRAM_BOT_TOKEN.
218
- config: ${R}`)),process.exit(1)),r&&!Ze()&&(console.error(C(process.stderr,"WhatsApp enabled but no session. Run `omnish link` first.")),process.exit(1));let i=nt(t),a=process.stderr;Bn(i)&&(console.error(Zt(i,a)),console.error(C(a,"Fix security errors above before starting the gateway (or change gatewayMode / token).")),process.exit(1));let l=co(i,"warn");if(l.length>0&&(console.warn(`${ne(a,`Security (${l.length} finding(s)):`)}
219
- `),console.warn(Zt(l,a))),process.env.OMNISH_BACKGROUND_GATEWAY==="1")try{fe.writeFileSync(Z,`${process.pid}
220
- `,{mode:384})}catch(M){T.warn({err:String(M)},"could not write gateway pidfile")}let u=(M,L)=>{let D=$();if(!D.clusterEnabled)return M;let f=ye(),k=(D.clusterLabel??"").trim()||$i.hostname(),x=null;if(L.startsWith("tg:"))x=L;else if(L){let X=N(L);X&&(x=`wa:${X}`)}let J=x?Ae(D,x):null;return ss(M,{nodeId:f,label:k,role:D.clusterRole,activeNodeId:J?.nodeId??""})},c=new Je,d=new Map,m=new Map,g=new Map,y=null,w={stop:null,sendText:null,sendMedia:null},v=null,E=!1,U=async(M,L)=>{if(M.startsWith("wa:")){let D=M.slice(3);y&&await y.sendText(D,L)}else if(M.startsWith("tg:")){let D=Number(M.slice(3));w.sendText&&Number.isFinite(D)&&await w.sendText(D,p(L))}},se=()=>new dt(()=>$(),U),G=se();v=G;let ce,F={async reload(){try{T.info("gateway reload requested from chat"),await w.stop?.().catch(()=>{}),w.stop=null,w.sendText=null,w.sendMedia=null;let M=$(),L=M.gatewayMode==="telegram"||M.gatewayMode==="both",D=de(M);if(L&&D){let x=await Er(D,()=>$(),ce,{decorate:u});w.sendText=x.sendText,w.sendMedia=x.sendMedia,w.stop=x.stop}let f=["Reload complete.",`gatewayMode: ${M.gatewayMode}`,L&&D?"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(`
221
- `),k=fn(Ct());return{ok:!0,summary:k?`${f}
216
+ `)}`),o}}import Fc from"node:crypto";import Gr from"node:fs";import Nc from"node:net";var jt=null;function _c(){try{let e=Gr.readFileSync(tt,"utf8"),t=JSON.parse(e);return typeof t.token=="string"?t.token:""}catch{return""}}function Bc(e){Gr.writeFileSync(tt,JSON.stringify(e,null,2)+`
217
+ `,{mode:384})}function Dc(){try{Gr.unlinkSync(tt)}catch{}}async function Hc(e,t,n){let r;try{r=JSON.parse(e)}catch{return{ok:!1,error:"Invalid JSON."}}if(!r||typeof r!="object"||r.op!=="sendMedia")return{ok:!1,error:"Unsupported operation."};if(typeof r.token!="string"||!n||r.token!==n)return{ok:!1,error:"Unauthorized."};let o=t.getCfg(),s=typeof r.absPath=="string"?r.absPath.trim():"";if(!s)return{ok:!1,error:"Missing absPath."};let i=typeof r.caption=="string"&&r.caption.length>0?r.caption:void 0,a=_e(s,o.fileSendMaxBytes);if("error"in a)return{ok:!1,error:a.error};let l={absPath:a.absPath,category:a.category,mimetype:a.mimetype,displayName:a.displayName,caption:i};if(r.channel==="whatsapp"){let d=t.getWaOutbound();if(!d)return{ok:!1,error:"WhatsApp outbound is not connected."};let c=o.gatewayMode;if(c!=="whatsapp"&&c!=="both")return{ok:!1,error:"gatewayMode does not include WhatsApp."};let u=typeof r.e164=="string"?r.e164.trim():"";if(!u.startsWith("+"))return{ok:!1,error:"WhatsApp destination must be E.164 (e.g. +15551234567)."};let m=zt(u);try{return await d.sendMedia(m,l),{ok:!0}}catch(f){return{ok:!1,error:String(f)}}}if(r.channel==="telegram"){let d=t.getTgSendMedia();if(!d)return{ok:!1,error:"Telegram outbound is not connected."};let c=o.gatewayMode;if(c!=="telegram"&&c!=="both")return{ok:!1,error:"gatewayMode does not include Telegram."};if(!Number.isFinite(r.chatId))return{ok:!1,error:"Invalid Telegram chat id."};try{return await d(r.chatId,l),{ok:!0}}catch(u){return{ok:!1,error:String(u)}}}return{ok:!1,error:"Unknown channel."}}function Bi(e){if(jt)return;let t=Fc.randomBytes(32).toString("hex"),n=Nc.createServer(r=>{let o="";r.setTimeout(12e4),r.on("data",s=>{o+=s.toString("utf8");let i=o.indexOf(`
218
+ `);if(i===-1)return;let a=o.slice(0,i).trim();o=o.slice(i+1);let l=_c();Hc(a,e,l).then(d=>{r.write(`${JSON.stringify(d)}
219
+ `),r.end()})}),r.on("error",()=>{})});n.listen(0,"127.0.0.1",()=>{let r=n.address();if(!r||typeof r=="string"){R.error("gateway control: could not read listen address");return}let o={token:t,host:r.address,port:r.port};Bc(o),R.info({port:r.port},"gateway control listening")}),n.on("error",r=>{R.error({err:String(r)},"gateway control server error")}),jt=n}function Nn(){if(jt){try{jt.close()}catch{}jt=null,Dc()}}import Gc from"node:readline";import qr from"node:path";import ae from"node:process";import jc from"node:net";import Wc from"node:fs";function Uc(){try{let e=Wc.readFileSync(tt,"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 zr(e){let t=Uc();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)}
220
+ `;return new Promise(o=>{let s=!1,i="";function a(d){s||(s=!0,o(d))}let l=jc.connect({host:t.host,port:t.port},()=>{l.write(r)});l.setTimeout(6e5),l.on("data",d=>{i+=d.toString("utf8");let c=i.indexOf(`
221
+ `);if(c>=0){let u=i.slice(0,c).trim();try{let m=JSON.parse(u);m.ok?a(null):a(m.error||"Unknown error from gateway control.")}catch{a("Invalid response from gateway control.")}l.destroy()}}),l.on("error",d=>{a(`Control connection failed: ${String(d)}`)}),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 Di(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=s.indexOf(" -- "),a,l;if(i>=0?(a=s.slice(0,i).trim(),l=s.slice(i+4).trim()||void 0):a=s,(a.startsWith('"')&&a.endsWith('"')||a.startsWith("'")&&a.endsWith("'"))&&(a=a.slice(1,-1)),!a)return null;let d=o.toLowerCase();if(d.startsWith("tg:")||d.startsWith("telegram:")){let c=ue(o);if(!c)return null;let u=Number(c);return Number.isFinite(u)?{channel:"telegram",chatId:u,filePart:a,caption:l}:null}if(d.startsWith("wa:")){let c=D(o.slice(3));return c?{channel:"whatsapp",e164:c,filePart:a,caption:l}:null}if(o.startsWith("+")){let c=D(o);return c?{channel:"whatsapp",e164:c,filePart:a,caption:l}:null}return null}function Jr(){return["/sendto wa:+E164 <file> [-- caption]","/sendto tg:<chat_id> <file> [-- caption]","Also: /sendto +E164 <file> (WhatsApp). Requires `omnish run` on this machine."].join(`
222
+ `)}var St="wa:cli:interactive";function zc(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=D(r);return o?`wa:${o}`:null}function Jc(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=zc(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 Kr(e){let t=ae.cwd(),n=[`${z(e,"omnish i")} ${h(e,"[options]")}`,re(e,"Interactive shell \u2014 same commands as WhatsApp/Telegram chat."),"",X(e,"Usage:"),` ${k(e,"omnish i [options]")}`,` ${k(e,"omnish interactive [options]")}`,"",X(e,"Options:"),...De(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=>h(e,r)),"",`${h(e,"Trust:")} ${k(e,"Full local access like your shell; not gated by inbox allowlist.")}`,`${h(e,"Jobs:")} ${k(e,"/bg and /jobs apply only to this REPL session (not the gateway process).")}`,`${h(e,"Files:")} ${k(e,"Use /sendto to push a host file through the gateway; plain /send needs a chat peer.")}`,`${h(e,"Gateway:")} ${k(e,"/reload and /updates require omnish run; /sendto requires omnish run for WA/TG delivery.")}`,"",Jr(),"",`${h(e,"cwd:")} ${k(e,`session starts at ${t} (change with !cd or ${C().commandPrefix}cd).`)}`];console.log(n.join(`
223
+ `))}async function qc(e){let t=C(),n=j(St).cwd,r=qr.resolve(n,e.filePart),o=_e(r,t.fileSendMaxBytes);return"error"in o?o.error:e.channel==="whatsapp"?await zr({op:"sendMedia",channel:"whatsapp",e164:e.e164,absPath:o.absPath,caption:e.caption}):await zr({op:"sendMedia",channel:"telegram",chatId:e.chatId,absPath:o.absPath,caption:e.caption})}async function Kc(e,t,n,r,o,s,i){let a=e.trim();if(!a)return;let l=Di(a);if(l!==null||/^\/sendto(\s|$)/i.test(a)){if(l===null){console.log(M(ae.stderr,`Invalid /sendto.
224
+ `+Jr()));return}let u=await qc(l);console.log(u?M(ae.stderr,u):se(ae.stdout,"Sent."));return}let d={peerKey:St,text:e},c=await Ve(C(),t,n,r,o,d,s,void 0,i);c!==null&&await Yc(c)}async function Yc(e){if(e===null)return;if(e.kind==="file"){console.log(se(ae.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(`
225
+ `)));return}let t=Ee(e.body,"whatsapp").text;t.trim()&&console.log(t)}async function Hi(e){let t=Jc(e);if(t.error==="help"){Kr(ae.stdout);return}if(t.error&&t.error!==null){console.error(M(ae.stderr,t.error)),console.error(h(ae.stderr,"Try: omnish i --help")),ae.exitCode=1;return}let{senderKey:n,oneShot:r}=t.opts,o=n??St;Q(),en(St,ae.cwd());let s=new Xe,i=new Map,a=new Map,l=new Map,d=new yt(()=>C(),async(f,w)=>{ae.stdout.write(w),w.endsWith(`
226
+ `)||ae.stdout.write(`
227
+ `)}),c=async f=>{try{await Kc(f,s,i,a,l,d,o)}catch(w){console.error(M(ae.stderr,String(w)))}};if(r!==null){await c(r),d.dispose(),s.killAllRunning();return}let u=Gc.createInterface({input:ae.stdin,output:ae.stdout}),m=qr.basename(j(St).cwd);u.setPrompt(`${m}> `),u.on("line",f=>{c(f).then(()=>{let w=qr.basename(j(St).cwd);u.setPrompt(`${w}> `),u.prompt()})}),u.on("close",()=>{d.dispose(),s.killAllRunning(),ae.stdout.write(`
228
+ `)}),u.prompt()}Vc.setDefaultResultOrder("ipv4first");function Ui(){let e=process.stdout,t=[`${oe(e,"omnish run")} ${h(e,"[options]")}`,re(e,"Listen for DMs and run shell commands from allowlisted chats."),"",X(e,"Usage:"),` ${k(e,"omnish run [options]")}`,"",X(e,"Options:"),...De(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: ${be}).`},{left:"-h, --help",right:"Show this help."}],n=>h(e,n)),"",`${k(e,"Config reload:")} ${h(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(`
229
+ `))}function Gi(){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=>h(e,i.left)),r=Math.max(...n.map(nn)),o=t.map((i,a)=>zn(" ",r,n[a],k(e,i.right))),s=[`${oe(e,"omnish link")} ${h(e,"[options]")}`,re(e,"Connect WhatsApp (QR) or save a Telegram bot token."),"",X(e,"Usage:"),` ${k(e,"omnish link [--force]")}`,` ${k(e,"omnish link --tg <bot_token>")}`,"",X(e,"Modes:"),...o,` ${re(e,"Do not combine --tg with --force.")}`,"",X(e,"Options:"),...De(e," ",[{left:"-f, --force",right:"WhatsApp only: delete saved session before pairing."},{left:"-h, --help",right:"Show this help."}],i=>h(e,i)),"",`${k(e,"Next:")} ${z(e,"omnish allow tg:<your_user_id>")} ${h(e,"then")} ${z(e,"omnish run")}`,`${h(e,"Config:")} ${k(e,T)}`,""];console.log(s.join(`
230
+ `))}function Zc(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}
231
+ 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 Yr(){let e=process.stdout,t=`${oe(e,"omnish")} ${h(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)"}],r=[{left:"-v, --version",right:"Print version and exit."},{left:"-h, --help",right:"Show this help (same as omnish help)."}],o=[t,re(e,"Allowlisted inbox \u2192 your real shell. No AI."),"",X(e,"Usage:"),` ${k(e,"omnish [options] <command> [args...]")}`,"",X(e,"Options:"),...De(e," ",r,s=>h(e,s)),"",X(e,"Commands:"),...De(e," ",n,s=>h(e,s)),"",`${h(e,"Config:")} ${k(e,`${T} \u2014 gatewayMode: "whatsapp" | "telegram" | "both"`)}`,`${h(e,"Env:")} ${k(e,"OMNISH_VERBOSE=1 (Baileys; legacy WHATSVERBOSE=1 still works), TELEGRAM_BOT_TOKEN (optional)")}`,`${h(e,"Data:")} ${k(e,"~/.omnish by default; ~/.whatslive reused if it already exists. OMNISH_HOME overrides.")}`,`${h(e,"See also:")} ${k(e,"https://omnish.dev")}`,""];console.log(o.join(`
232
+ `))}function eu(e){let t=(e??"").trim().toLowerCase(),n=process.stdout;if(!t){Yr();return}switch(t){case"link":Gi();return;case"run":Ui();return;case"service":Ji();return;case"i":case"interactive":Kr(n);return;default:console.error(M(process.stderr,`No detailed help for "${e}". Try: omnish help`)),process.exitCode=1}}function tu(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(M(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(M(a,`unknown run option: ${i}`)),console.error(M(a,"Try: omnish run --help")),process.exit(1)}}let o=n.trim()!==""?_n.isAbsolute(n)?n:_n.resolve(process.cwd(),n):be;return{background:t,logFile:o,help:r}}function nu(e){Q(),B(_n.dirname(e));let t=process.argv[1];t||(console.error(M(process.stderr,"cannot resolve entry script; invoke via node path/to/dist/index.js.")),process.exit(1));let n=fe.openSync(e,"a"),r=Xc(process.execPath,[t,"run"],{detached:!0,stdio:["ignore",n,n],env:{...process.env,OMNISH_BACKGROUND_GATEWAY:"1"}});fe.closeSync(n),r.unref(),r.pid||(console.error(M(process.stderr,"failed to start background gateway.")),process.exit(1));let o=process.stdout;console.log(`${se(o,`gateway started in background (pid ${r.pid}).`)} ${h(o,`Log: ${e}`)}`)}function ru(){if(Q(),!fe.existsSync(ne)){console.error(M(process.stderr,`no pidfile at ${ne} \u2014 is a background gateway running?`)),process.exitCode=1;return}let e=fe.readFileSync(ne,"utf8").trim(),t=Number(e);if(!Number.isFinite(t)||t<=0){console.error(M(process.stderr,"invalid pidfile."));try{fe.unlinkSync(ne)}catch{}process.exitCode=1;return}try{process.kill(t,0)}catch{console.log(se(process.stdout,`process ${t} is not running; removing stale pidfile.`));try{fe.unlinkSync(ne)}catch{}return}try{process.kill(t,"SIGTERM"),console.log(se(process.stdout,`sent SIGTERM to gateway (pid ${t}).`))}catch(n){if(process.platform==="win32"&&Qc("taskkill",["/PID",String(t),"/T"],{windowsHide:!0}).status===0){console.log(se(process.stdout,`stopped gateway (pid ${t}) using taskkill.`));return}console.error(M(process.stderr,`could not signal process: ${String(n)}`)),process.exitCode=1}}function ji(){if(process.env.OMNISH_BACKGROUND_GATEWAY==="1")try{fe.readFileSync(ne,"utf8").trim()===String(process.pid)&&fe.unlinkSync(ne)}catch{}}function ou(e){return e.length<=8?"(set)":`${e.slice(0,4)}\u2026${e.slice(-4)}`}function su(){if(!fe.existsSync(ne))return"gateway process: not running (no pid file)";let e=fe.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 zi=120;function Ji(){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 ${zi}).`},{left:"install",right:"Write user systemd / LaunchAgent unit (needs serviceInstallFromChat=true)."},{left:"uninstall",right:"Remove that unit (same gate as install)."}],n=[`${oe(e,"omnish service")} ${h(e,"<subcommand>")}`,re(e,"Boot integration, crash restart policy, and logs (same ideas as /service in chat)."),"",X(e,"Usage:"),` ${k(e,"omnish service <subcommand>")}`,"",X(e,"Subcommands:"),...De(e," ",t,r=>h(e,r)),"",X(e,"Restart and reload:"),` ${k(e,"Process")} ${h(e,"\u2014 Linux user unit: Restart=on-failure, RestartSec=5. macOS: KeepAlive. Full restart loads config from disk.")}`,` ${k(e,"Config live")} ${h(e,"\u2014 allowlisted chat while gateway runs: /reload or /restart")}`,"",`${h(e,"Docs:")} ${k(e,"docs/guides/background-and-boot.md")} ${re(e,"\xB7")} https://omnish.dev`,""];console.log(n.join(`
233
+ `))}function iu(e){let t=process.stdout,n=process.stderr,r=(e[0]??"help").toLowerCase();if(r==="help"||r==="--help"||r==="-h"){Ji();return}if(r==="instructions"){let o=Ye();if(o.error){console.error(M(n,o.error)),process.exitCode=1;return}console.log(yn(o));return}if(r==="status"){let o=Ye(),s=(()=>{try{return fe.existsSync(ne)?`gateway.pid: ${fe.readFileSync(ne,"utf8").trim()}`:"gateway.pid: (missing)"}catch(u){return`gateway.pid: (read error: ${String(u)})`}})(),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}
234
+ Script: ${o.scriptPath}`,c=C().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(oe(t,"omnish service status")),console.log(""),console.log(`${h(t,"platform:")} ${k(t,process.platform)}`),console.log(`${h(t,"session:")} ${k(t,i)}`),console.log(`${h(t,"env:")} ${k(t,a)}`),console.log(`${h(t,"data dir:")} ${k(t,O)}`),console.log(`${h(t,"pidfile:")} ${k(t,s)}`),console.log(`${h(t,"default log:")} ${k(t,be)}`),console.log(""),console.log(l),console.log(""),console.log(k(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,zi):80,i=Sn(be,s);console.log(`${h(t,"file:")} ${k(t,be)}`),console.log(`${h(t,"lines:")} ${k(t,String(s))}`),console.log(""),console.log(i);return}if(r==="install"){if(!C().serviceInstallFromChat){console.error(M(n,"Install is disabled. Set serviceInstallFromChat to true in config (same trust as shell), then run again.")),process.exitCode=1;return}let s=wn();s.ok?console.log(se(t,s.detail)):(console.error(M(n,s.detail)),process.exitCode=1);return}if(r==="uninstall"){if(!C().serviceInstallFromChat){console.error(M(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=bn();s.ok?console.log(se(t,s.detail)):(console.error(M(n,s.detail)),process.exitCode=1);return}console.error(M(n,`Unknown subcommand "${r}". Try: omnish service help`)),process.exitCode=1}function au(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=D(r);return o?`wa:${o}`:null}async function lu(){let e=null,t=C(),n=t.gatewayMode,r=n==="whatsapp"||n==="both",o=n==="telegram"||n==="both",s=de(t);o&&!s&&(console.error(M(process.stderr,`Telegram enabled (gatewayMode) but no bot token. Set telegramBotToken in config or TELEGRAM_BOT_TOKEN.
235
+ config: ${T}`)),process.exit(1)),r&&!ot()&&(console.error(M(process.stderr,"WhatsApp enabled but no session. Run `omnish link` first.")),process.exit(1));let i=at(t),a=process.stderr;Kn(i)&&(console.error(an(i,a)),console.error(M(a,"Fix security errors above before starting the gateway (or change gatewayMode / token).")),process.exit(1));let l=vo(i,"warn");if(l.length>0&&(console.warn(`${se(a,`Security (${l.length} finding(s)):`)}
236
+ `),console.warn(an(l,a))),process.env.OMNISH_BACKGROUND_GATEWAY==="1")try{fe.writeFileSync(ne,`${process.pid}
237
+ `,{mode:384})}catch($){R.warn({err:String($)},"could not write gateway pidfile")}let d=($,E)=>{let g=C();if(!g.clusterEnabled)return $;let b=ke(),v=(g.clusterLabel??"").trim()||Wi.hostname(),N=null;if(E.startsWith("tg:"))N=E;else if(E){let ee=D(E);ee&&(N=`wa:${ee}`)}let G=N?Pe(g,N):null;return ws($,{nodeId:b,label:v,role:g.clusterRole,activeNodeId:G?.nodeId??""})},c=new Xe,u=new Map,m=new Map,f=new Map,w=null,y={stop:null,sendText:null,sendMedia:null},S=null,F=!1,I=async($,E)=>{if($.startsWith("wa:")){let g=$.slice(3);w&&await w.sendText(g,E)}else if($.startsWith("tg:")){let g=Number($.slice(3));y.sendText&&Number.isFinite(g)&&await y.sendText(g,p(E))}},L=async($,E)=>{if($.startsWith("wa:")){let g=$.slice(3);w&&await w.sendMedia(g,E)}else if($.startsWith("tg:")){let g=Number($.slice(3));y.sendMedia&&Number.isFinite(g)&&await y.sendMedia(g,E)}},Y=()=>new yt(()=>C(),I),W=Y();S=W;let _,ge={async reload(){try{R.info("gateway reload requested from chat"),await y.stop?.().catch(()=>{}),y.stop=null,y.sendText=null,y.sendMedia=null;let $=C(),E=$.gatewayMode==="telegram"||$.gatewayMode==="both",g=de($);if(E&&g){let N=await Ur(g,()=>C(),_,{decorate:d});y.sendText=N.sendText,y.sendMedia=N.sendMedia,y.stop=N.stop}let b=["Reload complete.",`gatewayMode: ${$.gatewayMode}`,E&&g?"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(`
238
+ `),v=xn(Et());return{ok:!0,summary:v?`${b}
222
239
 
223
- Updates (last check): ${k}`:f}}catch(M){return{ok:!1,error:String(M)}}}};if(ce=async(M,L)=>{let D=$(),f=qr(D.telegramAllowFrom),k=M.peerKey.startsWith("tg:")?M.peerKey.slice(3):"";if(!k||!f.has(k)){T.warn({denied:M.peerKey,uid:k},"telegram denied");return}try{let x=M.peerKey;if(!Sr(D,M.text,x))return;if(M.mediaError&&await L({kind:"text",body:p(M.mediaError)}),M.mediaSavedPath&&await L({kind:"text",body:p(`Saved: ${M.mediaSavedPath}`)}),M.text.trim()){let J=await ze(D,c,d,m,g,M,G,F,x);J!==null&&await L(J)}}catch(x){T.error({err:String(x)},"telegram handler error"),await L({kind:"text",body:p(`Error: ${String(x)}`)}).catch(()=>{})}},o){let M=await Er(s,()=>$(),ce,{decorate:u});w.sendText=M.sendText,w.sendMedia=M.sendMedia,w.stop=M.stop}ki({getCfg:()=>$(),getWaOutbound:()=>y,getTgSendMedia:()=>w.sendMedia}),e=Os({getRunningVersion:Be,getConfig:$,log:T});let $e=Xs({getConfig:$,sendToPeer:U}),ie=!o,Re=()=>{E=!0,$e(),e?.(),e=null,vi(),Rn(),w.stop?.().catch(()=>{}),v?.dispose(),c.killAllRunning(),console.error(`
224
- ${C(process.stderr,"shutting down\u2026")}`),process.exit(0)};if(process.on("SIGINT",Re),process.on("SIGTERM",Re),r)for(;!E;){let M=!1,L;try{L=await Sn({printQr:!1,verbose:hn()}),await Ke(vn(L),3e5,"Gateway: timed out waiting for WhatsApp connection (5 min).")}catch(f){console.error(C(process.stderr,`connect failed: ${String(f)}`)),await new Promise(k=>setTimeout(k,5e3));continue}y=gi(L,{decorate:u});let D=ci(L,async f=>{let k=$(),x=Jr(k.allowFrom),J=f.fromE164||N(f.fromJid)||"";if(!J||!x.has(J)){T.warn({denied:f.fromJid,phone:J},"denied");return}try{let X=Js(f),Me=`wa:${J}`;if(!Sr(k,X.text,Me))return;if(f.mediaError&&await y.sendText(f.fromJid,f.mediaError),f.mediaSavedPath&&await y.sendText(f.fromJid,`Saved: ${f.mediaSavedPath}`),f.text.trim()){let ht=await ze(k,c,d,m,g,X,G,F,Me);ht!==null&&(ht.kind==="file"?await y.sendMedia(f.fromJid,ht.spec):await y.sendText(f.fromJid,Ce(ht.body,"whatsapp").text))}}catch(X){T.error({err:String(X)},"handler error"),await y.sendText(f.fromJid,Ce(p(`Error: ${String(X)}`),"whatsapp").text).catch(()=>{})}});if(await new Promise(f=>{let k=x=>{x.connection==="close"&&(Tr(x.lastDisconnect)===qe.loggedOut&&(M=!0),L.ev.off("connection.update",k),f())};L.ev.on("connection.update",k)}),D(),ie&&(G.dispose(),G=se(),v=G),xn(L),y=null,M&&(console.error(C(process.stderr,"session logged out. Run `omnish link` again.")),$e(),e?.(),e=null,vi(),Rn(),w.stop?.().catch(()=>{}),process.exit(1)),E)break;await new Promise(f=>setTimeout(f,3e3))}else for(;!E;)await new Promise(M=>setTimeout(M,500));$e(),e?.(),Rn(),w.stop?.().catch(()=>{}),G.dispose()}async function _c(){let[,,e,...t]=process.argv;if(e==="--version"||e==="-v"||e==="-V"){let n=process.stdout;console.log(`${te(n,"omnish")} ${h(n,Be())}`);return}if(e==="--help"||e==="-h"){_r();return}if(e==="help"){Tc(t[0]);return}switch(Y(),e){case"link":{let n=Mc(t);if(n.kind==="help"){Ri();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(!Ve(r)){console.error(C(o,"That does not look like a Telegram bot token (expect digits:secret from @BotFather).")),process.exitCode=1;return}Qe(r);let i=Ze()?"both":"telegram";Ut(i),console.log([`${H(s,"Telegram")} ${b(s,"bot token saved to")} ${h(s,R)}`,`${h(s,"gatewayMode:")} ${H(s,i)}`,"",b(s,"Next:"),` ${h(s,"1.")} ${b(s,"Find your numeric user id (e.g. t.me/userinfobot), then:")} ${H(s,"omnish allow tg:<id>")}`,` ${h(s,"2.")} ${H(s,"omnish run")}`,""].join(`
225
- `));return}await bi({verbose:hn(),force:n.force});return}case"run":{let n=Ic(t);if(n.help){Ci();return}if(n.background){Ac(n.logFile);return}await Nc();return}case"stop":Ec();return;case"logout":{try{fe.rmSync(Q,{recursive:!0,force:!0}),console.log(ne(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=Ht(n);console.log(`${h(r,"allowFrom:")} ${b(r,s.allowFrom.join(", ")||"(empty)")}`),console.log(`${h(r,"telegramAllowFrom:")} ${b(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=jt(n);console.log(`${h(r,"allowFrom:")} ${b(r,s.allowFrom.join(", ")||"(empty)")}`),console.log(`${h(r,"telegramAllowFrom:")} ${b(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 xi(t);return}case"status":{let n=process.stdout,r=$(),o=t.includes("--check-updates"),s=new Je().list(),i=s.filter(y=>y.status==="running").length,a=de(r),l=nt(r),u=r.gatewayMode==="whatsapp"||r.gatewayMode==="both",c=r.gatewayMode==="telegram"||r.gatewayMode==="both",d=Ze(),m=d?yi(Q):null,g=[];if(g.push(`${te(n,"omnish")} ${h(n,Be())}`,`${h(n,"gatewayMode:")} ${b(n,r.gatewayMode)}`,`${h(n,"data dir:")} ${b(n,Mn.dirname(Q))}`,"",`${h(n,"gateway process:")} ${b(n,Pc().replace(/^gateway process: /,""))}`,"",Ue(n),H(n,"whatsapp"),` ${h(n,"in use:")} ${u?b(n,"yes"):ke(n,"no (gatewayMode is telegram-only)")}`),u){let y=d?b(n,`linked (${Q})`):ke(n,"missing \u2014 run omnish link");g.push(` ${h(n,"session:")} ${y}`),d&&m&&g.push(` ${h(n,"linked as:")} ${b(n,m)}`),d&&!m&&g.push(` ${h(n,"linked as:")} ${ee(n,"(not in creds yet \u2014 try again after omnish link completes)")}`)}if(g.push(` ${q(n,"Allowed")}`),r.allowFrom.length===0)g.push(` ${ee(n,"(none)")}`);else for(let y of r.allowFrom)g.push(` ${h(n,"whatsapp:")} ${b(n,y)}`);if(g.push("",Ue(n),H(n,"telegram")),g.push(` ${h(n,"in use:")} ${c?b(n,"yes"):ke(n,"no (gatewayMode is whatsapp-only)")}`),c){let y=a?b(n,Oc(a)):ke(n,"(none) \u2014 omnish link --tg <token> or TELEGRAM_BOT_TOKEN");g.push(` ${h(n,"bot token:")} ${y}`)}if(g.push(` ${q(n,"Allowed")}`),r.telegramAllowFrom.length===0)g.push(` ${ee(n,"(none)")}`);else for(let y of r.telegramAllowFrom)g.push(` ${h(n,"telegram:")} ${b(n,y)}`);if(g.push("",Ue(n),`${H(n,"jobs")} ${h(n,`(recent): ${s.length} total, ${i} running`)}`,mo(l,n)),console.log(g.join(`
226
- `)),o){let y=await Rt(Be(),r),w=bt(y);console.log(""),console.log(Ue(n)),console.log(Ce(w,"whatsapp").text)}if(r.clusterEnabled){let y=ye(),w=j(),v=Object.keys(w.senderBindings).length,E=Object.keys(r.clusterSenderBindings??{}).length;console.log(""),console.log(Ue(n)),console.log(H(n,"cluster")),console.log(` ${h(n,"\xB7")} ${b(n,`enabled \xB7 label ${r.clusterLabel||$i.hostname()} \xB7 bindings ${v} chat / ${E} default`)}`),console.log(` ${h(n,"node:")} ${b(n,`${y.slice(0,8)}\u2026`)}`)}return}case"commands":{let n=process.stdout,r=$();console.log(io(rt(r),n)),console.log(""),console.log(Ue(n)),console.log(b(n,"Keys editable from chat via /config set (same trust as shell):")),console.log(h(n,ln.join(", "))),console.log(`${h(n,"See also:")} ${b(n,R)}`);return}case"cluster":{let n=process.stdout,r=process.stderr,o=(t[0]??"status").toLowerCase();if(o==="status"){let s=$(),i=ye();if(console.log(`${h(n,"clusterEnabled:")} ${b(n,String(s.clusterEnabled))}`),console.log(`${h(n,"clusterLabel:")} ${b(n,s.clusterLabel||"(hostname)")}`),console.log(`${h(n,"clusterRole:")} ${b(n,`${s.clusterRole} (informational; no longer used to gate traffic)`)}`),console.log(`${h(n,"node id:")} ${H(n,i)}`),s.clusterEnabled){let a=j(),l=mr(s,null);console.log(""),console.log(b(n,l.wa.replace(/\*([^*]+)\*/g,"$1").replace(/`([^`]+)`/g,"$1"))),console.log(""),console.log(pr(a,s,null));let u=Object.keys(a.senderBindings).length,c=Object.entries(s.clusterSenderBindings??{});if(u>0){console.log(""),console.log(H(n,"Chat bindings (cluster-local.json)"));for(let[d,m]of Object.entries(a.senderBindings))console.log(` ${h(n,d)} ${ee(n,"->")} ${b(n,`${m.nodeId} (${m.source}, since ${m.sinceIso})`)}`)}if(c.length>0){console.log(""),console.log(H(n,"Config defaults (clusterSenderBindings)"));for(let[d,m]of c)console.log(` ${h(n,d)} ${ee(n,"->")} ${b(n,m)}`)}}else console.log(""),console.log(ke(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=Fc(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:u}=ws(a,i);if(!u.ok){if(u.reason==="ambiguous-label"){let d=(u.matches??[]).map(m=>`${m.nodeId}(${m.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(`${H(n,"cluster:")} ${b(n,`${a} -> ${u.peer.nodeId} (${u.peer.label}).`)}`);let c=$();console.log(""),console.log(pr(l,c,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=nt(n),o=t.includes("--json");console.log(o?uo(r):Zt(r,process.stdout)),Bn(r)&&(process.exitCode=1);return}case"service":{Lc(t);return}default:_r(),e&&(process.exitCode=1)}}_c().catch(e=>{console.error(C(process.stderr,String(e))),process.exit(1)});
240
+ Updates (last check): ${v}`:b}}catch($){return{ok:!1,error:String($)}}}};if(_=async($,E)=>{let g=C(),b=io(g.telegramAllowFrom),v=$.peerKey.startsWith("tg:")?$.peerKey.slice(3):"";if(!v||!b.has(v)){R.warn({denied:$.peerKey,uid:v},"telegram denied");return}try{let N=$.peerKey;if(!Lr(g,$.text,N))return;if($.mediaError&&await E({kind:"text",body:p($.mediaError)}),$.mediaSavedPath&&await E({kind:"text",body:p(`Saved: ${$.mediaSavedPath}`)}),$.text.trim()){let G=await Ve(g,c,u,m,f,$,W,ge,N);G!==null&&await E(G)}}catch(N){R.error({err:String(N)},"telegram handler error"),await E({kind:"text",body:p(`Error: ${String(N)}`)}).catch(()=>{})}},o){let $=await Ur(s,()=>C(),_,{decorate:d});y.sendText=$.sendText,y.sendMedia=$.sendMedia,y.stop=$.stop}Bi({getCfg:()=>C(),getWaOutbound:()=>w,getTgSendMedia:()=>y.sendMedia}),e=zs({getRunningVersion:Ue,getConfig:C,log:R});let we=yi({getConfig:C,sendToPeer:I,sendMediaToPeer:L}),Re=!o,Me=()=>{F=!0,we(),e?.(),e=null,ji(),Nn(),y.stop?.().catch(()=>{}),S?.dispose(),c.killAllRunning(),console.error(`
241
+ ${M(process.stderr,"shutting down\u2026")}`),process.exit(0)};if(process.on("SIGINT",Me),process.on("SIGTERM",Me),r)for(;!F;){let $=!1,E;try{E=await En({printQr:!1,verbose:$n()}),await Ze(Ln(E),3e5,"Gateway: timed out waiting for WhatsApp connection (5 min).")}catch(b){console.error(M(process.stderr,`connect failed: ${String(b)}`)),await new Promise(v=>setTimeout(v,5e3));continue}w=Li(E,{decorate:d});let g=Mi(E,async b=>{let v=C(),N=so(v.allowFrom),G=b.fromE164||D(b.fromJid)||"";if(!G||!N.has(G)){R.warn({denied:b.fromJid,phone:G},"denied");return}try{let ee=li(b),xt=`wa:${G}`;if(!Lr(v,ee.text,xt))return;if(b.mediaError&&await w.sendText(b.fromJid,b.mediaError),b.mediaSavedPath&&await w.sendText(b.fromJid,`Saved: ${b.mediaSavedPath}`),b.text.trim()){let et=await Ve(v,c,u,m,f,ee,W,ge,xt);et!==null&&(et.kind==="file"?await w.sendMedia(b.fromJid,et.spec):await w.sendText(b.fromJid,Ee(et.body,"whatsapp").text))}}catch(ee){R.error({err:String(ee)},"handler error"),await w.sendText(b.fromJid,Ee(p(`Error: ${String(ee)}`),"whatsapp").text).catch(()=>{})}});if(await new Promise(b=>{let v=N=>{N.connection==="close"&&(Hr(N.lastDisconnect)===Qe.loggedOut&&($=!0),E.ev.off("connection.update",v),b())};E.ev.on("connection.update",v)}),g(),Re&&(W.dispose(),W=Y(),S=W),On(E),w=null,$&&(console.error(M(process.stderr,"session logged out. Run `omnish link` again.")),we(),e?.(),e=null,ji(),Nn(),y.stop?.().catch(()=>{}),process.exit(1)),F)break;await new Promise(b=>setTimeout(b,3e3))}else for(;!F;)await new Promise($=>setTimeout($,500));we(),e?.(),Nn(),y.stop?.().catch(()=>{}),W.dispose()}async function cu(){let[,,e,...t]=process.argv;if(e==="--version"||e==="-v"||e==="-V"){let n=process.stdout;console.log(`${oe(n,"omnish")} ${h(n,Ue())}`);return}if(e==="--help"||e==="-h"){Yr();return}if(e==="help"){eu(t[0]);return}switch(Q(),e){case"link":{let n=Zc(t);if(n.kind==="help"){Gi();return}if(n.kind==="error"){let r=process.stderr,o=n.message.replace(/^\[omnish\]\s*/,"");console.error(M(r,o)),process.exitCode=1;return}if(n.kind==="tg"){let r=n.token.trim(),o=process.stderr,s=process.stdout;if(!nt(r)){console.error(M(o,"That does not look like a Telegram bot token (expect digits:secret from @BotFather).")),process.exitCode=1;return}rt(r);let i=ot()?"both":"telegram";Xt(i),console.log([`${z(s,"Telegram")} ${k(s,"bot token saved to")} ${h(s,T)}`,`${h(s,"gatewayMode:")} ${z(s,i)}`,"",k(s,"Next:"),` ${h(s,"1.")} ${k(s,"Find your numeric user id (e.g. t.me/userinfobot), then:")} ${z(s,"omnish allow tg:<id>")}`,` ${h(s,"2.")} ${z(s,"omnish run")}`,""].join(`
242
+ `));return}await _i({verbose:$n(),force:n.force});return}case"run":{let n=tu(t);if(n.help){Ui();return}if(n.background){nu(n.logFile);return}await lu();return}case"stop":ru();return;case"logout":{try{fe.rmSync(te,{recursive:!0,force:!0}),console.log(se(process.stdout,"Session removed. Run `omnish link` to pair again."))}catch(n){console.error(M(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(M(o,"Usage: omnish allow +<E164> or omnish allow tg:<user_id>")),process.exitCode=1;return}try{let s=Kt(n);console.log(`${h(r,"allowFrom:")} ${k(r,s.allowFrom.join(", ")||"(empty)")}`),console.log(`${h(r,"telegramAllowFrom:")} ${k(r,s.telegramAllowFrom.join(", ")||"(empty)")}`)}catch(s){console.error(M(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(M(o,"Usage: omnish deny +<E164> or omnish deny tg:<user_id>")),process.exitCode=1;return}try{let s=Yt(n);console.log(`${h(r,"allowFrom:")} ${k(r,s.allowFrom.join(", ")||"(empty)")}`),console.log(`${h(r,"telegramAllowFrom:")} ${k(r,s.telegramAllowFrom.join(", ")||"(empty)")}`)}catch(s){console.error(M(o,String(s).replace(/^\[omnish\]\s*/,""))),process.exitCode=1}return}case"i":case"interactive":{await Hi(t);return}case"status":{let n=process.stdout,r=C(),o=t.includes("--check-updates"),s=new Xe().list(),i=s.filter(w=>w.status==="running").length,a=de(r),l=at(r),d=r.gatewayMode==="whatsapp"||r.gatewayMode==="both",c=r.gatewayMode==="telegram"||r.gatewayMode==="both",u=ot(),m=u?Fi(te):null,f=[];if(f.push(`${oe(n,"omnish")} ${h(n,Ue())}`,`${h(n,"gatewayMode:")} ${k(n,r.gatewayMode)}`,`${h(n,"data dir:")} ${k(n,_n.dirname(te))}`,"",`${h(n,"gateway process:")} ${k(n,su().replace(/^gateway process: /,""))}`,"",Ke(n),z(n,"whatsapp"),` ${h(n,"in use:")} ${d?k(n,"yes"):ve(n,"no (gatewayMode is telegram-only)")}`),d){let w=u?k(n,`linked (${te})`):ve(n,"missing \u2014 run omnish link");f.push(` ${h(n,"session:")} ${w}`),u&&m&&f.push(` ${h(n,"linked as:")} ${k(n,m)}`),u&&!m&&f.push(` ${h(n,"linked as:")} ${re(n,"(not in creds yet \u2014 try again after omnish link completes)")}`)}if(f.push(` ${X(n,"Allowed")}`),r.allowFrom.length===0)f.push(` ${re(n,"(none)")}`);else for(let w of r.allowFrom)f.push(` ${h(n,"whatsapp:")} ${k(n,w)}`);if(f.push("",Ke(n),z(n,"telegram")),f.push(` ${h(n,"in use:")} ${c?k(n,"yes"):ve(n,"no (gatewayMode is whatsapp-only)")}`),c){let w=a?k(n,ou(a)):ve(n,"(none) \u2014 omnish link --tg <token> or TELEGRAM_BOT_TOKEN");f.push(` ${h(n,"bot token:")} ${w}`)}if(f.push(` ${X(n,"Allowed")}`),r.telegramAllowFrom.length===0)f.push(` ${re(n,"(none)")}`);else for(let w of r.telegramAllowFrom)f.push(` ${h(n,"telegram:")} ${k(n,w)}`);if(f.push("",Ke(n),`${z(n,"jobs")} ${h(n,`(recent): ${s.length} total, ${i} running`)}`,Ro(l,n)),console.log(f.join(`
243
+ `)),o){let w=await Ot(Ue(),r),y=Ct(w);console.log(""),console.log(Ke(n)),console.log(Ee(y,"whatsapp").text)}if(r.clusterEnabled){let w=ke(),y=J(),S=Object.keys(y.senderBindings).length,F=Object.keys(r.clusterSenderBindings??{}).length;console.log(""),console.log(Ke(n)),console.log(z(n,"cluster")),console.log(` ${h(n,"\xB7")} ${k(n,`enabled \xB7 label ${r.clusterLabel||Wi.hostname()} \xB7 bindings ${S} chat / ${F} default`)}`),console.log(` ${h(n,"node:")} ${k(n,`${w.slice(0,8)}\u2026`)}`)}return}case"commands":{let n=process.stdout,r=C();console.log(ko(lt(r),n)),console.log(""),console.log(Ke(n)),console.log(k(n,"Keys editable from chat via /config set (same trust as shell):")),console.log(h(n,hn.join(", "))),console.log(`${h(n,"See also:")} ${k(n,T)}`);return}case"cluster":{let n=process.stdout,r=process.stderr,o=(t[0]??"status").toLowerCase();if(o==="status"){let s=C(),i=ke();if(console.log(`${h(n,"clusterEnabled:")} ${k(n,String(s.clusterEnabled))}`),console.log(`${h(n,"clusterLabel:")} ${k(n,s.clusterLabel||"(hostname)")}`),console.log(`${h(n,"clusterRole:")} ${k(n,`${s.clusterRole} (informational; no longer used to gate traffic)`)}`),console.log(`${h(n,"node id:")} ${z(n,i)}`),s.clusterEnabled){let a=J(),l=vr(s,null);console.log(""),console.log(k(n,l.wa.replace(/\*([^*]+)\*/g,"$1").replace(/`([^`]+)`/g,"$1"))),console.log(""),console.log(xr(a,s,null));let d=Object.keys(a.senderBindings).length,c=Object.entries(s.clusterSenderBindings??{});if(d>0){console.log(""),console.log(z(n,"Chat bindings (cluster-local.json)"));for(let[u,m]of Object.entries(a.senderBindings))console.log(` ${h(n,u)} ${re(n,"->")} ${k(n,`${m.nodeId} (${m.source}, since ${m.sinceIso})`)}`)}if(c.length>0){console.log(""),console.log(z(n,"Config defaults (clusterSenderBindings)"));for(let[u,m]of c)console.log(` ${h(n,u)} ${re(n,"->")} ${k(n,m)}`)}}else console.log(""),console.log(ve(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(M(r,"Usage: omnish cluster use <senderE164|tg:id> <label-or-id>")),process.exitCode=1;return}let a=au(s);if(!a){console.error(M(r,`Could not parse sender "${s}". Use +E164 (WhatsApp) or tg:<user_id> (Telegram).`)),process.exitCode=1;return}let{state:l,resolved:d}=Es(a,i);if(!d.ok){if(d.reason==="ambiguous-label"){let u=(d.matches??[]).map(m=>`${m.nodeId}(${m.label})`).join(", ");console.error(M(r,`Label "${i}" matches multiple machines: ${u}. Use the 8-character id.`))}else console.error(M(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:")} ${k(n,`${a} -> ${d.peer.nodeId} (${d.peer.label}).`)}`);let c=C();console.log(""),console.log(xr(l,c,a));return}if(o==="here"){console.error(M(r,"omnish cluster here is no longer available. Use: omnish cluster use <senderE164|tg:id> <label-or-id>")),console.error(M(r,"Or send /c here from the controller's chat on the machine you want to bind.")),process.exitCode=1;return}console.error(M(r,"Usage: omnish cluster [status | use <sender> <label-or-id>]")),process.exitCode=1;return}case"security":{let n=C(),r=at(n),o=t.includes("--json");console.log(o?$o(r):an(r,process.stdout)),Kn(r)&&(process.exitCode=1);return}case"service":{iu(t);return}default:Yr(),e&&(process.exitCode=1)}}cu().catch(e=>{console.error(M(process.stderr,String(e))),process.exit(1)});