omnish 1.2.1 → 1.2.2-beta.1

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.
Files changed (3) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/dist/index.js +150 -146
  3. package/package.json +19 -10
package/dist/index.js CHANGED
@@ -1,24 +1,24 @@
1
1
  #!/usr/bin/env node
2
- import xd from"node:dns";import rt from"node:fs";import Eo from"node:path";import ja from"node:os";import fn from"node:fs";import Ya from"node:crypto";import cr from"node:fs";import qa from"node:os";import z from"node:path";function Va(){let e=process.env.OMNISH_HOME?.trim();if(e)return z.resolve(e);let t=qa.homedir(),n=z.join(t,".omnish"),r=z.join(t,".whatslive");try{if(cr.existsSync(n))return n;if(cr.existsSync(r))return r}catch{}return n}var A=Va(),H=z.join(A,"auth"),Me=z.join(A,"jobs"),Lo=z.join(A,"apps"),Po=z.join(A,"logs"),de=z.join(Po,"gateway.log"),Z=z.join(A,"gateway.pid"),ft=z.join(A,"gateway-control.json"),Ft=z.join(A,"config-ui.json"),Ne=z.join(A,"ui.json"),an=z.join(A,"ui-server.json"),T=z.join(A,"config.json"),ln=z.join(A,"shortcuts.json"),cn=z.join(A,"recipes.json"),un=z.join(A,"recipes-user.json"),Fe=z.join(A,"cowork"),ur=z.join(Fe,"tasks.json"),dr=z.join(Fe,"pending-runs.json"),No=z.join(Fe,"completions.sqlite");function _t(e){let t=Ya.createHash("sha1").update(e,"utf8").digest("hex").slice(0,8);return z.join(Lo,t)}function N(e){cr.mkdirSync(e,{recursive:!0,mode:448})}function J(){N(A),N(H),N(Me),N(Lo),N(Po),N(Fe)}var _o=/^(\d+)(?::\d+)?@s\.whatsapp\.net$/i,Bo=/^(\d+)@c\.us$/i,Do=/^(\d+)@lid$/i;function Ho(e){let t=e.trim();for(;;){let n=t;if(t=t.replace(/^whatsapp:/i,"").trim(),t===n)return t}}function Xa(e){let t=Ho(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 Qa(e){let t=e.match(_o);if(t)return t[1]??null;let n=e.match(Bo);if(n)return n[1]??null;let r=e.match(Do);return r?r[1]??null:null}function Fo(e){let t=e.replace(/\D/g,"");return t?`+${t}`:""}function dn(e){return`${e.replace(/\D/g,"")}@s.whatsapp.net`}function j(e){let t=Ho(e);if(!t||Xa(t))return null;if(_o.test(t)||Bo.test(t)||Do.test(t)){let r=Qa(t);if(!r)return null;let o=Fo(r);return o.length>1?o:null}if(t.includes("@"))return null;let n=Fo(t);return n.length>1?n:null}function pn(e){return e.map(t=>String(t).trim()).filter(t=>!!t).map(t=>t==="*"?t:j(t)).filter(t=>!!t)}function jo(e){let t=new Set;for(let n of e)n!=="*"&&t.add(n);return t}function ae(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 Uo(e){let t=new Set;for(let n of e){let r=ae(String(n));r&&t.add(r)}return t}function pr(e){let t=e.trim();if(!t)return null;let n=t.toLowerCase();if(n.startsWith("tg:")||n.startsWith("telegram:")){let o=ae(t);return o?{kind:"tg",id:o}:null}let r=j(t);return r?{kind:"wa",normalized:r}:null}var O={gatewayMode:"whatsapp",telegramBotToken:"",telegramAllowFrom:[],allowFrom:[],commandPrefix:"!",syncTimeoutMs:3e4,syncMaxBytes:32768,jobLogTailLines:80,shell:"/bin/bash",appsCols:120,appsRows:40,appsFlushMs:300,appsMinIntervalMs:800,appsMaxFlushBytes:8192,appsMaxSessions:5,appsMaxSessionsTotal:20,appsMaxWaChars:3500,appsLogTailLines:80,appsSubmitDelayMs:50,appsClearInput:!0,appsClearInputDelayMs:20,appsClearInputSequence:"^A,^K",fileSendMaxBytes:0,fileReceiveMaxBytes:0,fileInboxSubdir:"inbox",fileReceiveRootMode:"downloads",fileReceiveRootPath:"",recipesAllowDangerousBuiltins:!1,recipesMaxTaskChars:0,recipesMacroDefaultCommand:'claude -p "$OMNISH_TASK"',clusterEnabled:!1,clusterLabel:"",clusterRole:"secondary",clusterSenderBindings:{},serviceInstallFromChat:!1,updateCheckEnabled:!1,updateCheckIntervalMs:864e5,updateCheckPackageName:"omnish",updateInfoUrl:""};function Za(e){return e==="downloads"||e==="omnishData"||e==="sessionCwd"||e==="processCwd"||e==="fixed"?e:O.fileReceiveRootMode}function el(e){return e==="primary"?"primary":"secondary"}function tl(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=j(s.slice(3));l&&(t[`wa:${l}`]=o.slice(0,64));continue}if(i.startsWith("tg:")||i.startsWith("telegram:")){let l=ae(s);l&&(t[`tg:${l}`]=o.slice(0,64));continue}let a=j(s);a&&(t[`wa:${a}`]=o.slice(0,64))}return t}function mn(e){let t=typeof e.appsFlushMs=="number"&&e.appsFlushMs>=0?e.appsFlushMs:O.appsFlushMs,n=e.gatewayMode==="telegram"||e.gatewayMode==="both"||e.gatewayMode==="whatsapp"?e.gatewayMode:O.gatewayMode,r=typeof e.telegramBotToken=="string"?e.telegramBotToken:O.telegramBotToken,o=Array.isArray(e.telegramAllowFrom)?[...new Set(e.telegramAllowFrom.map(s=>ae(String(s))).filter(s=>!!s))].sort():O.telegramAllowFrom;return{...O,...e,gatewayMode:n,telegramBotToken:r,telegramAllowFrom:o,allowFrom:Array.isArray(e.allowFrom)?pn(e.allowFrom.map(String)).filter(s=>s!=="*"):O.allowFrom,commandPrefix:(()=>{let s=typeof e.commandPrefix=="string"&&e.commandPrefix.length>0?e.commandPrefix:O.commandPrefix;return s==="! "?"!":s})(),syncTimeoutMs:typeof e.syncTimeoutMs=="number"&&e.syncTimeoutMs>0?e.syncTimeoutMs:O.syncTimeoutMs,syncMaxBytes:typeof e.syncMaxBytes=="number"&&e.syncMaxBytes>0?e.syncMaxBytes:O.syncMaxBytes,jobLogTailLines:typeof e.jobLogTailLines=="number"&&e.jobLogTailLines>0?e.jobLogTailLines:O.jobLogTailLines,shell:typeof e.shell=="string"&&e.shell.length>0?e.shell:O.shell,appsCols:typeof e.appsCols=="number"&&e.appsCols>0&&e.appsCols<=500?Math.floor(e.appsCols):O.appsCols,appsRows:typeof e.appsRows=="number"&&e.appsRows>0&&e.appsRows<=200?Math.floor(e.appsRows):O.appsRows,appsFlushMs:t,appsMinIntervalMs:typeof e.appsMinIntervalMs=="number"&&e.appsMinIntervalMs>=0?e.appsMinIntervalMs:O.appsMinIntervalMs,appsMaxFlushBytes:typeof e.appsMaxFlushBytes=="number"&&e.appsMaxFlushBytes>256?Math.floor(e.appsMaxFlushBytes):O.appsMaxFlushBytes,appsMaxSessions:typeof e.appsMaxSessions=="number"&&e.appsMaxSessions>0?Math.min(50,Math.floor(e.appsMaxSessions)):O.appsMaxSessions,appsMaxSessionsTotal:typeof e.appsMaxSessionsTotal=="number"&&e.appsMaxSessionsTotal>0?Math.min(200,Math.floor(e.appsMaxSessionsTotal)):O.appsMaxSessionsTotal,appsMaxWaChars:typeof e.appsMaxWaChars=="number"&&e.appsMaxWaChars>256?Math.floor(e.appsMaxWaChars):O.appsMaxWaChars,appsLogTailLines:typeof e.appsLogTailLines=="number"&&e.appsLogTailLines>0?Math.min(500,Math.floor(e.appsLogTailLines)):O.appsLogTailLines,appsSubmitDelayMs:typeof e.appsSubmitDelayMs=="number"&&e.appsSubmitDelayMs>=0?Math.min(500,Math.floor(e.appsSubmitDelayMs)):O.appsSubmitDelayMs,appsClearInput:typeof e.appsClearInput=="boolean"?e.appsClearInput:O.appsClearInput,appsClearInputDelayMs:typeof e.appsClearInputDelayMs=="number"&&e.appsClearInputDelayMs>=0?Math.min(200,Math.floor(e.appsClearInputDelayMs)):O.appsClearInputDelayMs,appsClearInputSequence:typeof e.appsClearInputSequence=="string"&&e.appsClearInputSequence.length>0?e.appsClearInputSequence.slice(0,200):O.appsClearInputSequence,fileSendMaxBytes:typeof e.fileSendMaxBytes=="number"&&e.fileSendMaxBytes>=0?e.fileSendMaxBytes===0?0:Math.min(2e9,Math.floor(e.fileSendMaxBytes)):O.fileSendMaxBytes,fileReceiveMaxBytes:typeof e.fileReceiveMaxBytes=="number"&&e.fileReceiveMaxBytes>=0?e.fileReceiveMaxBytes===0?0:Math.min(2e9,Math.floor(e.fileReceiveMaxBytes)):O.fileReceiveMaxBytes,fileInboxSubdir:typeof e.fileInboxSubdir=="string"&&e.fileInboxSubdir.length>0&&e.fileInboxSubdir.replace(/[/\\]/g,"").slice(0,80)||O.fileInboxSubdir,fileReceiveRootMode:Za(e.fileReceiveRootMode),fileReceiveRootPath:typeof e.fileReceiveRootPath=="string"?e.fileReceiveRootPath.trim().slice(0,4096):O.fileReceiveRootPath,recipesAllowDangerousBuiltins:typeof e.recipesAllowDangerousBuiltins=="boolean"?e.recipesAllowDangerousBuiltins:O.recipesAllowDangerousBuiltins,recipesMaxTaskChars:typeof e.recipesMaxTaskChars=="number"&&e.recipesMaxTaskChars>=0?e.recipesMaxTaskChars===0?0:Math.min(Number.MAX_SAFE_INTEGER,Math.floor(e.recipesMaxTaskChars)):O.recipesMaxTaskChars,recipesMacroDefaultCommand:typeof e.recipesMacroDefaultCommand=="string"&&e.recipesMacroDefaultCommand.trim().length>0?e.recipesMacroDefaultCommand.trim().slice(0,4096):O.recipesMacroDefaultCommand,clusterEnabled:typeof e.clusterEnabled=="boolean"?e.clusterEnabled:O.clusterEnabled,clusterLabel:typeof e.clusterLabel=="string"?e.clusterLabel.trim().slice(0,128):O.clusterLabel,clusterRole:el(e.clusterRole),clusterSenderBindings:tl(e.clusterSenderBindings),serviceInstallFromChat:typeof e.serviceInstallFromChat=="boolean"?e.serviceInstallFromChat:O.serviceInstallFromChat,updateCheckEnabled:typeof e.updateCheckEnabled=="boolean"?e.updateCheckEnabled:O.updateCheckEnabled,updateCheckIntervalMs:typeof e.updateCheckIntervalMs=="number"&&e.updateCheckIntervalMs>0?Math.min(6048e5,Math.max(36e5,Math.floor(e.updateCheckIntervalMs))):O.updateCheckIntervalMs,updateCheckPackageName:typeof e.updateCheckPackageName=="string"&&e.updateCheckPackageName.trim().length>0?e.updateCheckPackageName.trim().slice(0,214):O.updateCheckPackageName,updateInfoUrl:typeof e.updateInfoUrl=="string"?e.updateInfoUrl.trim().slice(0,2048):O.updateInfoUrl}}function v(){if(J(),!fn.existsSync(T)){let e=mn({});return _e(e),e}try{let e=fn.readFileSync(T,"utf8"),t=JSON.parse(e);return mn(t)}catch{return mn({})}}function _e(e){J();let t=mn(e);fn.writeFileSync(T,JSON.stringify(t,null,2)+`
3
- `,{mode:384})}function gn(e){let t=pr(e);if(!t)throw new Error("Invalid entry. Use +E164 (WhatsApp) or tg:<user_id> (Telegram).");let n=v();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 _e(n),n}function hn(e){let t=pr(e);if(!t)throw new Error("Invalid entry. Use +E164 (WhatsApp) or tg:<user_id> (Telegram).");let n=v();return t.kind==="wa"?n.allowFrom=n.allowFrom.filter(r=>r!==t.normalized):n.telegramAllowFrom=n.telegramAllowFrom.filter(r=>ae(r)!==t.id),_e(n),n}function ee(e){let t=typeof process.env.TELEGRAM_BOT_TOKEN=="string"?process.env.TELEGRAM_BOT_TOKEN.trim():"";return t||(e.telegramBotToken??"").trim()}function ze(e){let t=e.trim();return/^\d{6,}:[A-Za-z0-9_-]{20,}$/.test(t)}function gt(e){let t=v();return t.telegramBotToken=e.trim(),_e(t),v()}function yn(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 wn(e){let t=v();return t.gatewayMode=e,_e(t),v()}function bn(e){let t=v();return t.clusterEnabled=e,_e(t),v()}function Te(){try{return fn.readdirSync(H).length>0}catch{return!1}}import $c from"node:path";import Wo from"node:fs";import Go from"node:path";var nl=new Set(["jpg","jpeg","png","gif","webp","bmp","tif","tiff","heic","avif"]),rl=new Set(["mp4","mov","webm","mkv","avi","m4v","3gp"]),ol=new Set(["mp3","ogg","opus","wav","m4a","flac","aac","wma"]),kn={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 sl(e){let t=e.replace(/^\./,"").toLowerCase();return nl.has(t)?{category:"image",mimetype:kn[t]??"image/jpeg"}:rl.has(t)?{category:"video",mimetype:kn[t]??"video/mp4"}:ol.has(t)?{category:"audio",mimetype:kn[t]??"audio/mpeg"}:{category:"document",mimetype:kn[t]??"application/octet-stream"}}function Ke(e,t){let n;try{n=Wo.realpathSync(e)}catch{return{error:"File not found or unreadable."}}let r;try{r=Wo.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=Go.basename(n),s=Go.extname(n).slice(1),{category:i,mimetype:a}=sl(s);return{absPath:n,category:i,mimetype:a,displayName:o}}import ll from"node:os";import xn from"node:path";import mr from"node:fs";import zo from"node:os";import Ye from"node:path";var Ko=Ye.join(A,"sessions.json"),ht=new Map;function Yo(){return{cwd:Ye.resolve(zo.homedir())}}function il(e){return e.startsWith("wa:")||e.startsWith("tg:")?e:`wa:${e}`}function al(){try{let e=mr.readFileSync(Ko,"utf8"),t=JSON.parse(e);for(let[n,r]of Object.entries(t)){if(!r||typeof r!="object")continue;let o=il(n),i={cwd:typeof r.cwd=="string"&&r.cwd.length>0?Ye.resolve(r.cwd):Yo().cwd};r.fileReceiveRoot==="sessionCwd"&&(i.fileReceiveRoot="sessionCwd"),ht.set(o,i)}}catch{}}function qo(){N(A);let e={};for(let[t,n]of ht){let r={cwd:n.cwd};n.fileReceiveRoot==="sessionCwd"&&(r.fileReceiveRoot="sessionCwd"),e[t]=r}mr.writeFileSync(Ko,JSON.stringify(e,null,2)+`
4
- `,{mode:384})}var Jo=!1;function fr(){Jo||(al(),Jo=!0)}function W(e){fr();let t=ht.get(e);return t||(t=Yo(),ht.set(e,t)),t}function Sn(e,t){fr();let n=Ye.resolve(t),r=W(e);r.cwd=n,ht.set(e,r),qo()}function Vo(e){return W(e).fileReceiveRoot==="sessionCwd"?"sessionCwd":"default"}function gr(e,t){fr();let n=W(e);t==="default"?delete n.fileReceiveRoot:n.fileReceiveRoot="sessionCwd",ht.set(e,n),qo()}function Xo(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 Qo(e,t){if(t.kind==="home")return Ye.resolve(zo.homedir());let n=t.value.replace(/^['"]|['"]$/g,"");return Ye.isAbsolute(n)?Ye.normalize(n):Ye.resolve(e,n)}function Zo(e){try{return mr.statSync(e).isDirectory()?{ok:!0}:{ok:!1,error:`Not a directory: ${e}`}}catch(t){return{ok:!1,error:String(t)}}}var cl="Omnish";function es(){return xn.join(ll.homedir(),"Downloads",cl)}function yt(e,t){let n=W(t);if(n.fileReceiveRoot==="sessionCwd")return n.cwd;switch(e.fileReceiveRootMode){case"downloads":return es();case"omnishData":return xn.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(!xn.isAbsolute(r))throw new Error('fileReceiveRootPath must be an absolute path when fileReceiveRootMode is "fixed".');return xn.resolve(r)}default:return es()}}import We from"node:fs";import ns from"node:path";import Bt from"node:process";var ot="\x1B";function ul(e){return e.replace(/\u001B\[[\d;]*m/g,"")}function vn(e){return ul(e).length}function hr(e,t,n,r,o=2){let s=Math.max(0,t-vn(n));return`${e}${n}${" ".repeat(s)}${" ".repeat(o)}${r}`}function je(e,t,n,r){if(n.length===0)return[];let o=n.map(i=>r(i.left)),s=Math.max(...o.map(vn));return n.map((i,a)=>hr(t,s,o[a],k(e,i.right)))}var qe={primary:"#b4ff24",foreground:"#eef2f4",muted:"#8a9199",border:"#2a3139",error:"#ef4444",warn:"#a0e614",warnAlt:"#8cce04"};function dl(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 Ve(e){let t=dl(e);return t?`${ot}[38;2;${t.r};${t.g};${t.b}m`:""}function Cn(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 Ue(e,t,n){return!t||!Cn(e)?n:`${t}${n}${ot}[0m`}function q(e,t){return Ue(e,`${ot}[1m`,t)}function pl(e,t){return Cn(e)?`${Ve(qe.foreground)}${ot}[1m${t}${ot}[0m`:t}function re(e,t){return Ue(e,`${ot}[2m`,t)}function oe(e,t){return Ue(e,`${Ve(qe.primary)}${ot}[1m`,t)}function V(e,t){return Ue(e,Ve(qe.primary),t)}function k(e,t){return Ue(e,Ve(qe.foreground),t)}function y(e,t){return Ue(e,Ve(qe.muted),t)}function ml(e,t){return Ue(e,Ve(qe.border),t)}function $n(e,t){return Ue(e,Ve(qe.error),t)}function we(e,t){return Ue(e,Ve(qe.warn),t)}function st(e,t=60,n="\u2500"){let r=n.repeat(Math.max(1,t));return ml(e,r)}function le(e,t){return Cn(e)?`${`${y(e,"[")}${V(e,"omnish")}${y(e,"]")}`} ${t}`:`[omnish] ${t}`}function M(e,t){return Cn(e)?`${`${$n(e,"[omnish]")}`} ${t}`:`[omnish] ${t}`}function ts(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("",pl(t,r.text),"");break;case"gap":n.push("");break;case"p":n.push(k(t,r.text));break;case"bullet":n.push(`${y(t,"\u2022")} ${k(t,r.text)}`);break}return n.join(`
5
- `).replace(/^\n+/,"").trimEnd()}function Rn(e){return(e&4)!==0}function yr(e){return(e&2)!==0}var rs={error:3,warn:2,info:1};function os(e,t){let n=rs[t];return e.filter(r=>rs[r.severity]>=n)}function Xe(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.`}),!ns.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{We.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?ns.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(Bt.platform!=="win32"){try{if(We.existsSync(T)){let u=We.statSync(T);(Rn(u.mode)||yr(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 ${T}`,fixHint:`chmod 600 ${T}`})}}catch{}(typeof Bt.getuid=="function"?Bt.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(We.existsSync(A)){let u=We.statSync(A);(Rn(u.mode)||yr(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&&We.existsSync(Me)){let u=We.statSync(Me);(Rn(u.mode)||yr(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 ${Me}`,fixHint:`chmod 700 ${Me}`})}}catch{}try{if(We.existsSync(H)){let u=We.statSync(H);Rn(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 ${H}`,fixHint:`chmod 700 ${H}`})}}catch{}}return(typeof Bt.env.TELEGRAM_BOT_TOKEN=="string"?Bt.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&&!ee(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 Mn(e){return e.some(t=>t.severity==="error")}function fl(e,t){switch(t){case"error":return $n(e,"[ERROR]");case"warn":return we(e,"[WARN]");case"info":return y(e,"[INFO]")}}function wr(e,t,n,r){if(r.length!==0){t.push(q(e,`${n} (${r.length}):`));for(let o of r)t.push(` ${fl(e,o.severity)} ${y(e,`${o.code}:`)} ${k(e,o.message)}`),o.detail&&t.push(` ${y(e,o.detail)}`),o.fixHint&&t.push(` ${y(e,`Fix: ${o.fixHint}`)}`);t.push("")}}function br(e,t){if(e.length===0)return[`${oe(t,"Security check:")} ${k(t,"no issues reported by automated rules.")}`,"",y(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 wr(t,i,"Errors",n),wr(t,i,"Warnings",r),wr(t,i,"Notes",o),i.push(y(t,"Allowlisted identities can run commands as this user; treat them like passwords.")),i.join(`
7
- `).trimEnd()}function kr(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 ss(e){return`${JSON.stringify({findings:e,summary:kr(e)},null,2)}
8
- `}function is(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 as(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($n(t,`${r} error(s)`)),o&&i.push(we(t,`${o} warning(s)`)),s&&i.push(y(t,`${s} note(s)`));let a=n??"run `omnish security` for details";return`${oe(t,"security:")} ${i.join(", ")} ${y(t,`\u2014 ${a}`)}`}var ge="- ";function p(e){return{wa:e,tg:e}}function K(e,t){return{wa:e,tg:t,tgHtml:!0}}function Be(e,t){return t==="whatsapp"?{text:e.wa}:e.tgHtml?{text:e.tg,parseModeHtml:!0}:{text:e.tg}}function F(e){return e.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;")}function se(e){return e.replace(/[*_~`]/g,t=>({"*":"\u2217",_:"\uFF3F","~":"\u02DC","`":"\u2032"})[t]??t)}function gl(e){let t=[];for(let n of e)switch(n.kind){case"title":t.push("",`*${n.text}*`,"");break;case"sub":t.push("",`_${n.text}_`,"");break;case"gap":t.push("");break;case"p":t.push(n.text);break;case"bullet":t.push(`${ge}${n.text}`);break}return t.join(`
2
+ import $d from"node:dns";import rt from"node:fs";import Oo from"node:path";import ja from"node:os";import fn from"node:fs";import Qa from"node:crypto";import ur from"node:fs";import Ya from"node:os";import z from"node:path";function Ka(){let e=process.env.OMNISH_HOME?.trim();if(e)return z.resolve(e);let t=Ya.homedir(),n=z.join(t,".omnish"),r=z.join(t,".whatslive");try{if(ur.existsSync(n))return n;if(ur.existsSync(r))return r}catch{}return n}var A=Ka(),D=z.join(A,"auth"),Me=z.join(A,"jobs"),Po=z.join(A,"apps"),No=z.join(A,"logs"),de=z.join(No,"gateway.log"),Z=z.join(A,"gateway.pid"),ft=z.join(A,"gateway-control.json"),Ft=z.join(A,"config-ui.json"),Ne=z.join(A,"ui.json"),an=z.join(A,"ui-server.json"),T=z.join(A,"config.json"),ln=z.join(A,"shortcuts.json"),cn=z.join(A,"recipes.json"),un=z.join(A,"recipes-user.json"),Fe=z.join(A,"cowork"),dr=z.join(Fe,"tasks.json"),pr=z.join(Fe,"pending-runs.json"),Fo=z.join(Fe,"completions.sqlite");function _t(e){let t=Qa.createHash("sha1").update(e,"utf8").digest("hex").slice(0,8);return z.join(Po,t)}function N(e){ur.mkdirSync(e,{recursive:!0,mode:448})}function J(){N(A),N(D),N(Me),N(Po),N(No),N(Fe)}var Bo=/^(\d+)(?::\d+)?@s\.whatsapp\.net$/i,Ho=/^(\d+)@c\.us$/i,Do=/^(\d+)@lid$/i;function jo(e){let t=e.trim();for(;;){let n=t;if(t=t.replace(/^whatsapp:/i,"").trim(),t===n)return t}}function Va(e){let t=jo(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 Xa(e){let t=e.match(Bo);if(t)return t[1]??null;let n=e.match(Ho);if(n)return n[1]??null;let r=e.match(Do);return r?r[1]??null:null}function _o(e){let t=e.replace(/\D/g,"");return t?`+${t}`:""}function dn(e){return`${e.replace(/\D/g,"")}@s.whatsapp.net`}function j(e){let t=jo(e);if(!t||Va(t))return null;if(Bo.test(t)||Ho.test(t)||Do.test(t)){let r=Xa(t);if(!r)return null;let o=_o(r);return o.length>1?o:null}if(t.includes("@"))return null;let n=_o(t);return n.length>1?n:null}function pn(e){return e.map(t=>String(t).trim()).filter(t=>!!t).map(t=>t==="*"?t:j(t)).filter(t=>!!t)}function Uo(e){let t=new Set;for(let n of e)n!=="*"&&t.add(n);return t}function ae(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 Wo(e){let t=new Set;for(let n of e){let r=ae(String(n));r&&t.add(r)}return t}function mr(e){let t=e.trim();if(!t)return null;let n=t.toLowerCase();if(n.startsWith("tg:")||n.startsWith("telegram:")){let o=ae(t);return o?{kind:"tg",id:o}:null}let r=j(t);return r?{kind:"wa",normalized:r}:null}var O={gatewayMode:"whatsapp",telegramBotToken:"",telegramAllowFrom:[],allowFrom:[],commandPrefix:"!",syncTimeoutMs:3e4,syncMaxBytes:32768,jobLogTailLines:80,shell:"/bin/bash",appsCols:120,appsRows:40,appsFlushMs:300,appsMinIntervalMs:800,appsMaxFlushBytes:8192,appsMaxSessions:5,appsMaxSessionsTotal:20,appsMaxWaChars:3500,appsLogTailLines:80,appsSubmitDelayMs:50,appsClearInput:!0,appsClearInputDelayMs:20,appsClearInputSequence:"^A,^K",fileSendMaxBytes:0,fileReceiveMaxBytes:0,fileInboxSubdir:"inbox",fileReceiveRootMode:"downloads",fileReceiveRootPath:"",recipesAllowDangerousBuiltins:!1,recipesMaxTaskChars:0,recipesMacroDefaultCommand:'claude -p "$OMNISH_TASK"',clusterEnabled:!1,clusterLabel:"",clusterRole:"secondary",clusterSenderBindings:{},serviceInstallFromChat:!1,updateCheckEnabled:!1,updateCheckIntervalMs:864e5,updateCheckPackageName:"omnish",updateInfoUrl:""};function Za(e){return e==="downloads"||e==="omnishData"||e==="sessionCwd"||e==="processCwd"||e==="fixed"?e:O.fileReceiveRootMode}function el(e){return e==="primary"?"primary":"secondary"}function tl(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(),a=s.toLowerCase();if(a.startsWith("wa:")){let l=j(s.slice(3));l&&(t[`wa:${l}`]=o.slice(0,64));continue}if(a.startsWith("tg:")||a.startsWith("telegram:")){let l=ae(s);l&&(t[`tg:${l}`]=o.slice(0,64));continue}let i=j(s);i&&(t[`wa:${i}`]=o.slice(0,64))}return t}function mn(e){let t=typeof e.appsFlushMs=="number"&&e.appsFlushMs>=0?e.appsFlushMs:O.appsFlushMs,n=e.gatewayMode==="telegram"||e.gatewayMode==="both"||e.gatewayMode==="whatsapp"?e.gatewayMode:O.gatewayMode,r=typeof e.telegramBotToken=="string"?e.telegramBotToken:O.telegramBotToken,o=Array.isArray(e.telegramAllowFrom)?[...new Set(e.telegramAllowFrom.map(s=>ae(String(s))).filter(s=>!!s))].sort():O.telegramAllowFrom;return{...O,...e,gatewayMode:n,telegramBotToken:r,telegramAllowFrom:o,allowFrom:Array.isArray(e.allowFrom)?pn(e.allowFrom.map(String)).filter(s=>s!=="*"):O.allowFrom,commandPrefix:(()=>{let s=typeof e.commandPrefix=="string"&&e.commandPrefix.length>0?e.commandPrefix:O.commandPrefix;return s==="! "?"!":s})(),syncTimeoutMs:typeof e.syncTimeoutMs=="number"&&e.syncTimeoutMs>0?e.syncTimeoutMs:O.syncTimeoutMs,syncMaxBytes:typeof e.syncMaxBytes=="number"&&e.syncMaxBytes>0?e.syncMaxBytes:O.syncMaxBytes,jobLogTailLines:typeof e.jobLogTailLines=="number"&&e.jobLogTailLines>0?e.jobLogTailLines:O.jobLogTailLines,shell:typeof e.shell=="string"&&e.shell.length>0?e.shell:O.shell,appsCols:typeof e.appsCols=="number"&&e.appsCols>0&&e.appsCols<=500?Math.floor(e.appsCols):O.appsCols,appsRows:typeof e.appsRows=="number"&&e.appsRows>0&&e.appsRows<=200?Math.floor(e.appsRows):O.appsRows,appsFlushMs:t,appsMinIntervalMs:typeof e.appsMinIntervalMs=="number"&&e.appsMinIntervalMs>=0?e.appsMinIntervalMs:O.appsMinIntervalMs,appsMaxFlushBytes:typeof e.appsMaxFlushBytes=="number"&&e.appsMaxFlushBytes>256?Math.floor(e.appsMaxFlushBytes):O.appsMaxFlushBytes,appsMaxSessions:typeof e.appsMaxSessions=="number"&&e.appsMaxSessions>0?Math.min(50,Math.floor(e.appsMaxSessions)):O.appsMaxSessions,appsMaxSessionsTotal:typeof e.appsMaxSessionsTotal=="number"&&e.appsMaxSessionsTotal>0?Math.min(200,Math.floor(e.appsMaxSessionsTotal)):O.appsMaxSessionsTotal,appsMaxWaChars:typeof e.appsMaxWaChars=="number"&&e.appsMaxWaChars>256?Math.floor(e.appsMaxWaChars):O.appsMaxWaChars,appsLogTailLines:typeof e.appsLogTailLines=="number"&&e.appsLogTailLines>0?Math.min(500,Math.floor(e.appsLogTailLines)):O.appsLogTailLines,appsSubmitDelayMs:typeof e.appsSubmitDelayMs=="number"&&e.appsSubmitDelayMs>=0?Math.min(500,Math.floor(e.appsSubmitDelayMs)):O.appsSubmitDelayMs,appsClearInput:typeof e.appsClearInput=="boolean"?e.appsClearInput:O.appsClearInput,appsClearInputDelayMs:typeof e.appsClearInputDelayMs=="number"&&e.appsClearInputDelayMs>=0?Math.min(200,Math.floor(e.appsClearInputDelayMs)):O.appsClearInputDelayMs,appsClearInputSequence:typeof e.appsClearInputSequence=="string"&&e.appsClearInputSequence.length>0?e.appsClearInputSequence.slice(0,200):O.appsClearInputSequence,fileSendMaxBytes:typeof e.fileSendMaxBytes=="number"&&e.fileSendMaxBytes>=0?e.fileSendMaxBytes===0?0:Math.min(2e9,Math.floor(e.fileSendMaxBytes)):O.fileSendMaxBytes,fileReceiveMaxBytes:typeof e.fileReceiveMaxBytes=="number"&&e.fileReceiveMaxBytes>=0?e.fileReceiveMaxBytes===0?0:Math.min(2e9,Math.floor(e.fileReceiveMaxBytes)):O.fileReceiveMaxBytes,fileInboxSubdir:typeof e.fileInboxSubdir=="string"&&e.fileInboxSubdir.length>0&&e.fileInboxSubdir.replace(/[/\\]/g,"").slice(0,80)||O.fileInboxSubdir,fileReceiveRootMode:Za(e.fileReceiveRootMode),fileReceiveRootPath:typeof e.fileReceiveRootPath=="string"?e.fileReceiveRootPath.trim().slice(0,4096):O.fileReceiveRootPath,recipesAllowDangerousBuiltins:typeof e.recipesAllowDangerousBuiltins=="boolean"?e.recipesAllowDangerousBuiltins:O.recipesAllowDangerousBuiltins,recipesMaxTaskChars:typeof e.recipesMaxTaskChars=="number"&&e.recipesMaxTaskChars>=0?e.recipesMaxTaskChars===0?0:Math.min(Number.MAX_SAFE_INTEGER,Math.floor(e.recipesMaxTaskChars)):O.recipesMaxTaskChars,recipesMacroDefaultCommand:typeof e.recipesMacroDefaultCommand=="string"&&e.recipesMacroDefaultCommand.trim().length>0?e.recipesMacroDefaultCommand.trim().slice(0,4096):O.recipesMacroDefaultCommand,clusterEnabled:typeof e.clusterEnabled=="boolean"?e.clusterEnabled:O.clusterEnabled,clusterLabel:typeof e.clusterLabel=="string"?e.clusterLabel.trim().slice(0,128):O.clusterLabel,clusterRole:el(e.clusterRole),clusterSenderBindings:tl(e.clusterSenderBindings),serviceInstallFromChat:typeof e.serviceInstallFromChat=="boolean"?e.serviceInstallFromChat:O.serviceInstallFromChat,updateCheckEnabled:typeof e.updateCheckEnabled=="boolean"?e.updateCheckEnabled:O.updateCheckEnabled,updateCheckIntervalMs:typeof e.updateCheckIntervalMs=="number"&&e.updateCheckIntervalMs>0?Math.min(6048e5,Math.max(36e5,Math.floor(e.updateCheckIntervalMs))):O.updateCheckIntervalMs,updateCheckPackageName:typeof e.updateCheckPackageName=="string"&&e.updateCheckPackageName.trim().length>0?e.updateCheckPackageName.trim().slice(0,214):O.updateCheckPackageName,updateInfoUrl:typeof e.updateInfoUrl=="string"?e.updateInfoUrl.trim().slice(0,2048):O.updateInfoUrl}}function x(){if(J(),!fn.existsSync(T)){let e=mn({});return _e(e),e}try{let e=fn.readFileSync(T,"utf8"),t=JSON.parse(e);return mn(t)}catch{return mn({})}}function _e(e){J();let t=mn(e);fn.writeFileSync(T,JSON.stringify(t,null,2)+`
3
+ `,{mode:384})}function gn(e){let t=mr(e);if(!t)throw new Error("Invalid entry. Use +E164 (WhatsApp) or tg:<user_id> (Telegram).");let n=x();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 _e(n),n}function hn(e){let t=mr(e);if(!t)throw new Error("Invalid entry. Use +E164 (WhatsApp) or tg:<user_id> (Telegram).");let n=x();return t.kind==="wa"?n.allowFrom=n.allowFrom.filter(r=>r!==t.normalized):n.telegramAllowFrom=n.telegramAllowFrom.filter(r=>ae(r)!==t.id),_e(n),n}function ee(e){let t=typeof process.env.TELEGRAM_BOT_TOKEN=="string"?process.env.TELEGRAM_BOT_TOKEN.trim():"";return t||(e.telegramBotToken??"").trim()}function ze(e){let t=e.trim();return/^\d{6,}:[A-Za-z0-9_-]{20,}$/.test(t)}function gt(e){let t=x();return t.telegramBotToken=e.trim(),_e(t),x()}function yn(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 wn(e){let t=x();return t.gatewayMode=e,_e(t),x()}function bn(e){let t=x();return t.clusterEnabled=e,_e(t),x()}function Te(){try{return fn.readdirSync(D).length>0}catch{return!1}}import Cc from"node:path";import Go from"node:fs";import Jo from"node:path";var nl=new Set(["jpg","jpeg","png","gif","webp","bmp","tif","tiff","heic","avif"]),rl=new Set(["mp4","mov","webm","mkv","avi","m4v","3gp"]),ol=new Set(["mp3","ogg","opus","wav","m4a","flac","aac","wma"]),kn={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 sl(e){let t=e.replace(/^\./,"").toLowerCase();return nl.has(t)?{category:"image",mimetype:kn[t]??"image/jpeg"}:rl.has(t)?{category:"video",mimetype:kn[t]??"video/mp4"}:ol.has(t)?{category:"audio",mimetype:kn[t]??"audio/mpeg"}:{category:"document",mimetype:kn[t]??"application/octet-stream"}}function qe(e,t){let n;try{n=Go.realpathSync(e)}catch{return{error:"File not found or unreadable."}}let r;try{r=Go.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=Jo.basename(n),s=Jo.extname(n).slice(1),{category:a,mimetype:i}=sl(s);return{absPath:n,category:a,mimetype:i,displayName:o}}import ll from"node:os";import vn from"node:path";import fr from"node:fs";import qo from"node:os";import Qe from"node:path";var Qo=Qe.join(A,"sessions.json"),ht=new Map;function Yo(){return{cwd:Qe.resolve(qo.homedir())}}function il(e){return e.startsWith("wa:")||e.startsWith("tg:")?e:`wa:${e}`}function al(){try{let e=fr.readFileSync(Qo,"utf8"),t=JSON.parse(e);for(let[n,r]of Object.entries(t)){if(!r||typeof r!="object")continue;let o=il(n),a={cwd:typeof r.cwd=="string"&&r.cwd.length>0?Qe.resolve(r.cwd):Yo().cwd};r.fileReceiveRoot==="sessionCwd"&&(a.fileReceiveRoot="sessionCwd"),ht.set(o,a)}}catch{}}function Ko(){N(A);let e={};for(let[t,n]of ht){let r={cwd:n.cwd};n.fileReceiveRoot==="sessionCwd"&&(r.fileReceiveRoot="sessionCwd"),e[t]=r}fr.writeFileSync(Qo,JSON.stringify(e,null,2)+`
4
+ `,{mode:384})}var zo=!1;function gr(){zo||(al(),zo=!0)}function W(e){gr();let t=ht.get(e);return t||(t=Yo(),ht.set(e,t)),t}function Sn(e,t){gr();let n=Qe.resolve(t),r=W(e);r.cwd=n,ht.set(e,r),Ko()}function Vo(e){return W(e).fileReceiveRoot==="sessionCwd"?"sessionCwd":"default"}function hr(e,t){gr();let n=W(e);t==="default"?delete n.fileReceiveRoot:n.fileReceiveRoot="sessionCwd",ht.set(e,n),Ko()}function Xo(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 Zo(e,t){if(t.kind==="home")return Qe.resolve(qo.homedir());let n=t.value.replace(/^['"]|['"]$/g,"");return Qe.isAbsolute(n)?Qe.normalize(n):Qe.resolve(e,n)}function es(e){try{return fr.statSync(e).isDirectory()?{ok:!0}:{ok:!1,error:`Not a directory: ${e}`}}catch(t){return{ok:!1,error:String(t)}}}var cl="Omnish";function ts(){return vn.join(ll.homedir(),"Downloads",cl)}function yt(e,t){let n=W(t);if(n.fileReceiveRoot==="sessionCwd")return n.cwd;switch(e.fileReceiveRootMode){case"downloads":return ts();case"omnishData":return vn.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(!vn.isAbsolute(r))throw new Error('fileReceiveRootPath must be an absolute path when fileReceiveRootMode is "fixed".');return vn.resolve(r)}default:return ts()}}import We from"node:fs";import rs from"node:path";import Bt from"node:process";var ot="\x1B";function ul(e){return e.replace(/\u001B\[[\d;]*m/g,"")}function xn(e){return ul(e).length}function yr(e,t,n,r,o=2){let s=Math.max(0,t-xn(n));return`${e}${n}${" ".repeat(s)}${" ".repeat(o)}${r}`}function je(e,t,n,r){if(n.length===0)return[];let o=n.map(a=>r(a.left)),s=Math.max(...o.map(xn));return n.map((a,i)=>yr(t,s,o[i],k(e,a.right)))}var Ye={primary:"#b4ff24",foreground:"#eef2f4",muted:"#8a9199",border:"#2a3139",error:"#ef4444",warn:"#a0e614",warnAlt:"#8cce04"};function dl(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 Ke(e){let t=dl(e);return t?`${ot}[38;2;${t.r};${t.g};${t.b}m`:""}function $n(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 Ue(e,t,n){return!t||!$n(e)?n:`${t}${n}${ot}[0m`}function Y(e,t){return Ue(e,`${ot}[1m`,t)}function pl(e,t){return $n(e)?`${Ke(Ye.foreground)}${ot}[1m${t}${ot}[0m`:t}function re(e,t){return Ue(e,`${ot}[2m`,t)}function oe(e,t){return Ue(e,`${Ke(Ye.primary)}${ot}[1m`,t)}function K(e,t){return Ue(e,Ke(Ye.primary),t)}function k(e,t){return Ue(e,Ke(Ye.foreground),t)}function w(e,t){return Ue(e,Ke(Ye.muted),t)}function ml(e,t){return Ue(e,Ke(Ye.border),t)}function Cn(e,t){return Ue(e,Ke(Ye.error),t)}function we(e,t){return Ue(e,Ke(Ye.warn),t)}function st(e,t=60,n="\u2500"){let r=n.repeat(Math.max(1,t));return ml(e,r)}function le(e,t){return $n(e)?`${`${w(e,"[")}${K(e,"omnish")}${w(e,"]")}`} ${t}`:`[omnish] ${t}`}function M(e,t){return $n(e)?`${`${Cn(e,"[omnish]")}`} ${t}`:`[omnish] ${t}`}function ns(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("",pl(t,r.text),"");break;case"gap":n.push("");break;case"p":n.push(k(t,r.text));break;case"bullet":n.push(`${w(t,"\u2022")} ${k(t,r.text)}`);break}return n.join(`
5
+ `).replace(/^\n+/,"").trimEnd()}function Rn(e){return(e&4)!==0}function wr(e){return(e&2)!==0}var os={error:3,warn:2,info:1};function ss(e,t){let n=os[t];return e.filter(r=>os[r.severity]>=n)}function Ve(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.`}),!rs.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{We.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 i=e.fileReceiveRootPath.trim();i?rs.isAbsolute(i)||n.push({severity:"error",code:"receive-fixed-not-absolute",message:`fileReceiveRootPath must be absolute when fileReceiveRootMode is "fixed": ${i}`,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(Bt.platform!=="win32"){try{if(We.existsSync(T)){let u=We.statSync(T);(Rn(u.mode)||wr(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 ${T}`,fixHint:`chmod 600 ${T}`})}}catch{}(typeof Bt.getuid=="function"?Bt.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(We.existsSync(A)){let u=We.statSync(A);(Rn(u.mode)||wr(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&&We.existsSync(Me)){let u=We.statSync(Me);(Rn(u.mode)||wr(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 ${Me}`,fixHint:`chmod 700 ${Me}`})}}catch{}try{if(We.existsSync(D)){let u=We.statSync(D);Rn(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 ${D}`,fixHint:`chmod 700 ${D}`})}}catch{}}return(typeof Bt.env.TELEGRAM_BOT_TOKEN=="string"?Bt.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&&!ee(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 Mn(e){return e.some(t=>t.severity==="error")}function fl(e,t){switch(t){case"error":return Cn(e,"[ERROR]");case"warn":return we(e,"[WARN]");case"info":return w(e,"[INFO]")}}function br(e,t,n,r){if(r.length!==0){t.push(Y(e,`${n} (${r.length}):`));for(let o of r)t.push(` ${fl(e,o.severity)} ${w(e,`${o.code}:`)} ${k(e,o.message)}`),o.detail&&t.push(` ${w(e,o.detail)}`),o.fixHint&&t.push(` ${w(e,`Fix: ${o.fixHint}`)}`);t.push("")}}function kr(e,t){if(e.length===0)return[`${oe(t,"Security check:")} ${k(t,"no issues reported by automated rules.")}`,"",w(t,"Note: allowlisted remote shell access is still equivalent to sharing credentials with those identities.")].join(`
6
+ `);let n=e.filter(i=>i.severity==="error"),r=e.filter(i=>i.severity==="warn"),o=e.filter(i=>i.severity==="info"),a=[`${oe(t,"Security check:")} `+k(t,`${n.length} error(s), ${r.length} warning(s), ${o.length} note(s).`),""];return br(t,a,"Errors",n),br(t,a,"Warnings",r),br(t,a,"Notes",o),a.push(w(t,"Allowlisted identities can run commands as this user; treat them like passwords.")),a.join(`
7
+ `).trimEnd()}function Sr(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 is(e){return`${JSON.stringify({findings:e,summary:Sr(e)},null,2)}
8
+ `}function as(e,t){let n=e.filter(i=>i.severity==="error").length,r=e.filter(i=>i.severity==="warn").length,o=e.filter(i=>i.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 a=t??"run `omnish security` for details";return`security: ${s.join(", ")} \u2014 ${a}`}function ls(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 a=[];r&&a.push(Cn(t,`${r} error(s)`)),o&&a.push(we(t,`${o} warning(s)`)),s&&a.push(w(t,`${s} note(s)`));let i=n??"run `omnish security` for details";return`${oe(t,"security:")} ${a.join(", ")} ${w(t,`\u2014 ${i}`)}`}var ge="- ";function p(e){return{wa:e,tg:e}}function q(e,t){return{wa:e,tg:t,tgHtml:!0}}function Be(e,t){return t==="whatsapp"?{text:e.wa}:e.tgHtml?{text:e.tg,parseModeHtml:!0}:{text:e.tg}}function F(e){return e.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;")}function se(e){return e.replace(/[*_~`]/g,t=>({"*":"\u2217",_:"\uFF3F","~":"\u02DC","`":"\u2032"})[t]??t)}function gl(e){let t=[];for(let n of e)switch(n.kind){case"title":t.push("",`*${n.text}*`,"");break;case"sub":t.push("",`_${n.text}_`,"");break;case"gap":t.push("");break;case"p":t.push(n.text);break;case"bullet":t.push(`${ge}${n.text}`);break}return t.join(`
9
9
  `).replace(/^\n+/,"").trimEnd()}function hl(e){let t=[];for(let n of e)switch(n.kind){case"title":t.push("",`<b>${F(n.text)}</b>`,"");break;case"sub":t.push("",`<i>${F(n.text)}</i>`,"");break;case"gap":t.push("");break;case"p":t.push(F(n.text));break;case"bullet":t.push(`\u2022 ${F(n.text)}`);break}return t.join(`
10
- `).replace(/^\n+/,"").trimEnd()}function _(e){return{wa:gl(e),tg:hl(e),tgHtml:!0}}function wt(e){return[{kind:"title",text:"Omnish \u2014 quick help"},{kind:"p",text:"Per-chat shell cwd is stored under your data dir (see /wa help)."},{kind:"gap"},{kind:"sub",text:"Run commands"},{kind:"bullet",text:`${e.commandPrefix}<command> \u2014 sync shell in session cwd (timeout ${e.syncTimeoutMs} ms)`},{kind:"bullet",text:`${e.commandPrefix}cd <dir> \u2014 change session cwd (${e.commandPrefix}cd alone \u2192 home)`},{kind:"bullet",text:"!!start | !!stop \u2014 free shell (plain \u2192 sync shell only when no focused /apps session); space optional"},{kind:"bullet",text:"/bg <cmd> \u2014 background job; /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 one-line aliases (/shortcut help); !name or /name expands once (no task injection)"},{kind:"bullet",text:"/run \u2014 recipes: inject task into system agent CLIs ($OMNISH_TASK) + attached PTY (/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 ls(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 cs(){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 Sr(){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 us(e){let t=["*Gateway status*","",`Mode: *${e.gatewayMode}*`,"- whatsapp \u2014 WhatsApp only","- telegram \u2014 Telegram only","- both \u2014 both channels","",`WhatsApp auth: ${e.authPresent?"present":"missing (omnish link)"}`,`Telegram token: ${e.tokenSet?"set":"not set"}`,`Allowlists: ${e.allowN} WA \xB7 ${e.tgAllowN} Telegram`,""];e.updateBrief&&t.push(`Updates: ${se(e.updateBrief)}`,""),t.push("Change: /gateway both \xB7 /gw tg","/updates \u2014 npm + optional notice URL",`Config: ${se(T)}`);let n=["<b>Gateway status</b>","",`<b>${F(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> ${F(e.updateBrief)}`,""),n.push("<i>Change:</i> /gateway both \xB7 /gw tg","<i>/updates</i> \u2014 npm + optional notice URL",F(`Config: ${T}`)),K(t.join(`
10
+ `).replace(/^\n+/,"").trimEnd()}function _(e){return{wa:gl(e),tg:hl(e),tgHtml:!0}}function wt(e){return[{kind:"title",text:"Omnish \u2014 quick help"},{kind:"p",text:"Per-chat shell cwd is stored under your data dir (see /wa help)."},{kind:"gap"},{kind:"sub",text:"Run commands"},{kind:"bullet",text:`${e.commandPrefix}<command> \u2014 sync shell in session cwd (timeout ${e.syncTimeoutMs} ms)`},{kind:"bullet",text:`${e.commandPrefix}cd <dir> \u2014 change session cwd (${e.commandPrefix}cd alone \u2192 home)`},{kind:"bullet",text:"!!start | !!stop \u2014 free shell (plain \u2192 sync shell only when no focused /apps session); space optional"},{kind:"bullet",text:"/bg <cmd> \u2014 background job; /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 one-line aliases (/shortcut help); !name or /name expands once (no task injection)"},{kind:"bullet",text:"/run \u2014 recipe-based task runs in app sessions (/r); list with /run list, details in /run help"},{kind:"bullet",text:"/cowork | /cw \u2014 scheduled & on-demand shell tasks; logs to ~/Cowork/\u2026 (/cowork help)"},{kind:"gap"},{kind:"sub",text:"Setup & gateway"},{kind:"bullet",text:"/wa help \u2014 WhatsApp link, allowlist"},{kind:"bullet",text:"/tg help \u2014 Telegram; /tg token <paste>"},{kind:"bullet",text:"/reload | /restart \u2014 apply config"},{kind:"bullet",text:"/updates \u2014 npm latest + optional notice URL; /updates cached \u2014 last snapshot"},{kind:"bullet",text:"/security \u2014 posture report; /security summary; /security tips; /security help"},{kind:"bullet",text:"/gateway | /gw | /mode \u2014 show or set gatewayMode"},{kind:"bullet",text:"/allow +E164 | /allow tg:id \u2014 allowlist; /deny \u2026; /allowlist"},{kind:"bullet",text:"/help \u2014 this message"}]}function cs(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 us(){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 vr(){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 ds(e){let t=["*Gateway status*","",`Mode: *${e.gatewayMode}*`,"- whatsapp \u2014 WhatsApp only","- telegram \u2014 Telegram only","- both \u2014 both channels","",`WhatsApp auth: ${e.authPresent?"present":"missing (omnish link)"}`,`Telegram token: ${e.tokenSet?"set":"not set"}`,`Allowlists: ${e.allowN} WA \xB7 ${e.tgAllowN} Telegram`,""];e.updateBrief&&t.push(`Updates: ${se(e.updateBrief)}`,""),t.push("Change: /gateway both \xB7 /gw tg","/updates \u2014 npm + optional notice URL",`Config: ${se(T)}`);let n=["<b>Gateway status</b>","",`<b>${F(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> ${F(e.updateBrief)}`,""),n.push("<i>Change:</i> /gateway both \xB7 /gw tg","<i>/updates</i> \u2014 npm + optional notice URL",F(`Config: ${T}`)),q(t.join(`
11
11
  `),n.join(`
12
- `))}function Dt(e){let t=["*Update check*","",`Running: *${se(e.runningVersion)}*`,`Checked (UTC): ${e.checkedAtIso}`,`npm package: ${se(e.registryPackage)}`];e.registryError?t.push("",`Registry: ${se(e.registryError)}`):e.registryLatest&&t.push("",e.updateAvailable?`*Newer version on npm:* ${se(e.registryLatest)} (upgrade when ready).`:`npm latest: ${se(e.registryLatest)} (this install is current or newer).`),e.infoError?t.push("",`Notice URL: ${se(e.infoError)}`):e.infoMessage&&(t.push("",`Notice: ${se(e.infoMessage)}`),e.infoLink&&t.push(se(e.infoLink))),t.push("","Config: updateCheckEnabled, updateCheckIntervalMs, updateCheckPackageName, updateInfoUrl");let n=["<b>Update check</b>","",`<b>Running</b> <code>${F(e.runningVersion)}</code>`,`<b>Checked (UTC)</b> ${F(e.checkedAtIso)}`,`<b>npm package</b> <code>${F(e.registryPackage)}</code>`];return e.registryError?n.push("",`<b>Registry</b> ${F(e.registryError)}`):e.registryLatest&&n.push("",e.updateAvailable?`<b>Newer on npm</b> <code>${F(e.registryLatest)}</code>`:`<b>npm latest</b> <code>${F(e.registryLatest)}</code> (current or newer here)`),e.infoError?n.push("",`<b>Notice URL</b> ${F(e.infoError)}`):e.infoMessage&&(n.push("",`<b>Notice</b> ${F(e.infoMessage)}`),e.infoLink&&n.push(F(e.infoLink))),n.push("","<i>Config keys:</i> updateCheckEnabled, updateCheckIntervalMs, updateCheckPackageName, updateInfoUrl"),K(t.join(`
12
+ `))}function Ht(e){let t=["*Update check*","",`Running: *${se(e.runningVersion)}*`,`Checked (UTC): ${e.checkedAtIso}`,`npm package: ${se(e.registryPackage)}`];e.registryError?t.push("",`Registry: ${se(e.registryError)}`):e.registryLatest&&t.push("",e.updateAvailable?`*Newer version on npm:* ${se(e.registryLatest)} (upgrade when ready).`:`npm latest: ${se(e.registryLatest)} (this install is current or newer).`),e.infoError?t.push("",`Notice URL: ${se(e.infoError)}`):e.infoMessage&&(t.push("",`Notice: ${se(e.infoMessage)}`),e.infoLink&&t.push(se(e.infoLink))),t.push("","Config: updateCheckEnabled, updateCheckIntervalMs, updateCheckPackageName, updateInfoUrl");let n=["<b>Update check</b>","",`<b>Running</b> <code>${F(e.runningVersion)}</code>`,`<b>Checked (UTC)</b> ${F(e.checkedAtIso)}`,`<b>npm package</b> <code>${F(e.registryPackage)}</code>`];return e.registryError?n.push("",`<b>Registry</b> ${F(e.registryError)}`):e.registryLatest&&n.push("",e.updateAvailable?`<b>Newer on npm</b> <code>${F(e.registryLatest)}</code>`:`<b>npm latest</b> <code>${F(e.registryLatest)}</code> (current or newer here)`),e.infoError?n.push("",`<b>Notice URL</b> ${F(e.infoError)}`):e.infoMessage&&(n.push("",`<b>Notice</b> ${F(e.infoMessage)}`),e.infoLink&&n.push(F(e.infoLink))),n.push("","<i>Config keys:</i> updateCheckEnabled, updateCheckIntervalMs, updateCheckPackageName, updateInfoUrl"),q(t.join(`
13
13
  `),n.join(`
14
- `))}function ds(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 ${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 ps(e){let t=!!ee(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 ms(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})`,se(o),""),n.push(`<b>${F(r.label)}</b> (${r.items.length})`,F(o),"")}return K(t.join(`
14
+ `))}function ps(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 ${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 ms(e){let t=!!ee(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 fs(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})`,se(o),""),n.push(`<b>${F(r.label)}</b> (${r.items.length})`,F(o),"")}return q(t.join(`
15
15
  `).trimEnd(),n.join(`
16
- `).trimEnd())}function xr(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})`,se(s),""),r.push(`<b>${F(o.label)}</b> (${o.items.length})`,F(s),"")}return K(n.join(`
16
+ `).trimEnd())}function xr(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})`,se(s),""),r.push(`<b>${F(o.label)}</b> (${o.items.length})`,F(s),"")}return q(n.join(`
17
17
  `).trimEnd(),r.join(`
18
- `).trimEnd())}function fs(e){let t=["*Token saved*","","telegramBotToken written to config (not echoed).",`Config: ${se(T)}`,...e.flatMap(r=>["",r]),"","Send /reload so the gateway picks it up."].join(`
18
+ `).trimEnd())}function gs(e){let t=["*Token saved*","","telegramBotToken written to config (not echoed).",`Config: ${se(T)}`,...e.flatMap(r=>["",r]),"","Send /reload so the gateway picks it up."].join(`
19
19
  `),n=["<b>Token saved</b>","","telegramBotToken written to config (not echoed).",F(`Config: ${T}`),...e.map(r=>`<i>${F(r)}</i>`),"","Send /reload so the gateway picks it up."].join(`
20
20
  `)+`
21
- `;return K(t.trimEnd(),n.trimEnd())}function gs(){return _([{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 hs(e,t,n){let r=t?`
21
+ `;return q(t.trimEnd(),n.trimEnd())}function hs(){return _([{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 ys(e,t,n){let r=t?`
22
22
 
23
23
  ${t}`:n?`
24
24
 
@@ -29,63 +29,63 @@ ${F(t)}`:n?`
29
29
  Start omnish run on the host to apply (or /reload from a running gateway).`:"",s=`*gatewayMode saved*
30
30
 
31
31
  "${se(e)}"
32
- ${se(T)}${r}`,i=`<b>gatewayMode saved</b>
32
+ ${se(T)}${r}`,a=`<b>gatewayMode saved</b>
33
33
 
34
34
  <code>${F(e)}</code>
35
- ${F(T)}${o}`;return K(s,i)}function ys(){return _([{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 ws(){return _([{kind:"title",text:"/deny \u2014 remove from allowlist"},{kind:"p",text:"Same forms as /allow: +E164 or tg:id"}])}function bs(){return _([{kind:"title",text:"/bg \u2014 background job"},{kind:"p",text:"Usage: /bg <shell command>"},{kind:"bullet",text:"Example: /bg sleep 30 && echo done"}])}function vr(){return _([{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 Cr(){return _([{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 $r(){return _([{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 ks(e,t){let n=Vo(t),r=W(t),o="";try{o=yt(e,t)}catch(s){o=`(${String(s)})`}return _(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 Ss(){return _([{kind:"title",text:"Unknown /wa command"},{kind:"p",text:"Send /wa help for setup."}])}function xs(){return _([{kind:"title",text:"Unknown /tg command"},{kind:"p",text:"Send /tg help or /tg token <botfather_token>."}])}function vs(){return _([{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 Cs(e){return _([{kind:"title",text:"Unknown command"},{kind:"p",text:`Try /help or ${e.commandPrefix}<command>.`},{kind:"gap"},...wt(e)])}function $s(e){return _([{kind:"title",text:"No command matched"},{kind:"p",text:`Use ${JSON.stringify(e.commandPrefix)}, a slash command, or /apps help.`},{kind:"gap"},...wt(e)])}function Rs(){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"},...Sr()];return _(e)}function Rr(){return[{kind:"title",text:"User shortcuts (this chat)"},{kind:"p",text:"One-line aliases stored per chat. Invoke with a bare token only (no extra words). For task-injected CLIs use /run recipes, not shortcuts."},{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 Ms(e){if(e.length===0)return p("(no shortcuts yet \u2014 /shortcut add <name> <command\u2026>)");let t=["*Shortcuts*","_This chat_","",...e.map(r=>`${ge}\`${r.name}\` \u2192 ${r.body}`)].join(`
35
+ ${F(T)}${o}`;return q(s,a)}function ws(){return _([{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 bs(){return _([{kind:"title",text:"/deny \u2014 remove from allowlist"},{kind:"p",text:"Same forms as /allow: +E164 or tg:id"}])}function ks(){return _([{kind:"title",text:"/bg \u2014 background job"},{kind:"p",text:"Usage: /bg <shell command>"},{kind:"bullet",text:"Example: /bg sleep 30 && echo done"}])}function $r(){return _([{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 Cr(){return _([{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 Rr(){return _([{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 Ss(e,t){let n=Vo(t),r=W(t),o="";try{o=yt(e,t)}catch(s){o=`(${String(s)})`}return _(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 vs(){return _([{kind:"title",text:"Unknown /wa command"},{kind:"p",text:"Send /wa help for setup."}])}function xs(){return _([{kind:"title",text:"Unknown /tg command"},{kind:"p",text:"Send /tg help or /tg token <botfather_token>."}])}function $s(){return _([{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 Cs(e){return _([{kind:"title",text:"Unknown command"},{kind:"p",text:`Try /help or ${e.commandPrefix}<command>.`},{kind:"gap"},...wt(e)])}function Rs(e){return _([{kind:"title",text:"No command matched"},{kind:"p",text:`Use ${JSON.stringify(e.commandPrefix)}, a slash command, or /apps help.`},{kind:"gap"},...wt(e)])}function Ms(){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"},...vr()];return _(e)}function Mr(){return[{kind:"title",text:"User shortcuts (this chat)"},{kind:"p",text:"One-line aliases stored per chat. Invoke with a bare token only (no extra words). For task-injected CLIs use /run recipes, not shortcuts."},{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 Ts(e){if(e.length===0)return p("(no shortcuts yet \u2014 /shortcut add <name> <command\u2026>)");let t=["*Shortcuts*","_This chat_","",...e.map(r=>`${ge}\`${r.name}\` \u2192 ${r.body}`)].join(`
36
36
  `),n=["<b>Shortcuts</b>","<i>This chat</i>","",...e.map(r=>`\u2022 <code>${F(r.name)}</code> \u2192 ${F(r.body)}`)].join(`
37
- `);return K(t,n)}function Mr(e,t){return p(`Shortcut saved: ${e}
38
- \u2192 ${t}`)}function Ts(e){return p(`Shortcut removed: ${e}`)}function Tr(e){return p(`Unknown shortcut: ${e}`)}function Is(e,t){return p(`${e}
39
- \u2192 ${t}`)}function As(){return[{kind:"title",text:"/run \u2014 recipe templates"},{kind:"p",text:"Not the same as /shortcut: recipes run a system agent (or other CLI) with your task in $OMNISH_TASK (default) and attach a PTY session. Shortcuts only expand a fixed line once."},{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:"Placeholders: `<<<OMNISH_TASK>>>`, `<<<```$OMNISH_TASK```>>>`, and `$OMNISH_TASK` in a template are replaced by your `/run` task text."},{kind:"bullet",text:`/run add|set <name> <command> --template "\u2026" \u2014 canonical: /run add demo 'agent -p "$OMNISH_TASK"' --template 'template with $OMNISH_TASK'; alias: --tamplate.`},{kind:"bullet",text:"/run set <name> \u2026 \u2014 same formats"},{kind:"bullet",text:"/run remove <name> \u2014 aliases: rm, del"},{kind:"gap"},{kind:"p",text:`Host overrides: ${cn}`},{kind:"p",text:"Built-in Claude templates omit --dangerously-skip-permissions unless recipesAllowDangerousBuiltins is true in config."}]}function Es(e){let t=["*Recipes*","`/run <name> <task>`",""],n=["<b>Recipes</b>","<code>/run &lt;name&gt; &lt;task&gt;</code>",""],r=(o,s)=>{let i=o.description?` \u2014 ${o.description}`:"",a=s&&o.dangerous?" [dangerous flags]":"";t.push(`${ge}${o.name} \u2014 ${o.label??o.name}${i}${a}`);let l=o.description?` \u2014 ${F(o.description)}`:"",u=s&&o.dangerous?F(" [dangerous flags]"):"";n.push(`\u2022 ${F(o.name)} \u2014 ${F(o.label??o.name)}${l}${u}`)};if(e.featured.length>0){t.push("_Featured:_"),n.push("<i>Featured:</i>");for(let o of e.featured)r(o,!0);t.push(""),n.push("")}if(e.yours.length>0){t.push("_This chat:_"),n.push("<i>This chat:</i>");for(let o of e.yours)r(o,!1);t.push(""),n.push("")}if(e.more.length>0){t.push("_More:_"),n.push("<i>More:</i>");for(let o of e.more)r(o,!1)}return e.featured.length===0&&e.yours.length===0&&e.more.length===0&&(t.push("(no recipes \u2014 add host file or /run add)"),n.push(F("(no recipes \u2014 add host file or /run add)"))),K(t.join(`
37
+ `);return q(t,n)}function Tr(e,t){return p(`Shortcut saved: ${e}
38
+ \u2192 ${t}`)}function Is(e){return p(`Shortcut removed: ${e}`)}function Ir(e){return p(`Unknown shortcut: ${e}`)}function As(e,t){return p(`${e}
39
+ \u2192 ${t}`)}function Es(){return[{kind:"title",text:"/run \u2014 recipe templates"},{kind:"sub",text:"Invoke"},{kind:"bullet",text:"/run <name> <task\u2026> \u2014 start attached session (plain DMs go to it until /apps detach)"},{kind:"bullet",text:"/r <name> <task\u2026> \u2014 short alias for /run"},{kind:"gap"},{kind:"sub",text:"Queue"},{kind:"bullet",text:"/run <name> -q <task\u2026> or /run <name> --queue <task\u2026> \u2014 FIFO per chat: head starts immediately; others wait in memory until the head exits cleanly (code=0, signal=0)"},{kind:"bullet",text:"Pending counts only jobs not yet started \u2014 the first queued item becomes Active right away, so /run queue can show Pending: 0 while a queued run is still executing"},{kind:"bullet",text:"/run queue \u2014 active session + recipe, numbered waiting list, paused flag"},{kind:"bullet",text:"/run queue resume \u2014 after a pause or non-clean exit, clear pause and start the next waiting item"},{kind:"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:"Manage (this chat)"},{kind:"bullet",text:'/run add <name> <command\u2026> [--template "\u2026"] \u2014 create a recipe'},{kind:"bullet",text:'/run set <name> <command\u2026> [--template "\u2026"] \u2014 overwrite existing'},{kind:"bullet",text:"/run remove <name> \u2014 aliases: rm, del"},{kind:"gap"},{kind:"sub",text:"Notes"},{kind:"bullet",text:"Commands must reference the task env variable (default `$OMNISH_TASK`)."},{kind:"bullet",text:"Template placeholders `<<<OMNISH_TASK>>>`, `<<<```$OMNISH_TASK```>>>`, and `$OMNISH_TASK` are replaced with task text."},{kind:"bullet",text:"Host overrides file: "+cn},{kind:"bullet",text:"Built-in Claude recipes omit dangerous flags unless recipesAllowDangerousBuiltins=true."}]}function Os(e){let t=["*Recipes*","`/run <name> <task>`",""],n=["<b>Recipes</b>","<code>/run &lt;name&gt; &lt;task&gt;</code>",""],r=(o,s)=>{let a=o.description?` \u2014 ${o.description}`:"",i=s&&o.dangerous?" [dangerous flags]":"";t.push(`${ge}${o.name} \u2014 ${o.label??o.name}${a}${i}`);let l=o.description?` \u2014 ${F(o.description)}`:"",u=s&&o.dangerous?F(" [dangerous flags]"):"";n.push(`\u2022 ${F(o.name)} \u2014 ${F(o.label??o.name)}${l}${u}`)};if(e.featured.length>0){t.push("_Featured:_"),n.push("<i>Featured:</i>");for(let o of e.featured)r(o,!0);t.push(""),n.push("")}if(e.yours.length>0){t.push("_This chat:_"),n.push("<i>This chat:</i>");for(let o of e.yours)r(o,!1);t.push(""),n.push("")}if(e.more.length>0){t.push("_More:_"),n.push("<i>More:</i>");for(let o of e.more)r(o,!1)}return e.featured.length===0&&e.yours.length===0&&e.more.length===0&&(t.push("(no recipes \u2014 add host file or /run add)"),n.push(F("(no recipes \u2014 add host file or /run add)"))),q(t.join(`
40
40
  `).trimEnd(),n.join(`
41
- `).trimEnd())}function Os(e){let t=e.taskEnv??"OMNISH_TASK",n=[`Recipe: ${e.name}`,`Source: ${e.source}`,`Label: ${e.label??"(none)"}`,`Task env: ${t}`,`Command: ${e.command}`];if(e.promptTemplate){n.push(`Template: ${e.promptTemplate.length} chars`);let o=e.promptTemplate.replace(/\s+/g," ").trim().slice(0,200);n.push(`Preview: ${o}${e.promptTemplate.length>200?"\u2026":""}`)}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(`
42
- `))}function Ir(e,t){let n=[`Recipe saved: ${e}`,`\u2192 ${t.command}`,`(task env: ${t.taskEnv??"OMNISH_TASK"})`];return t.promptTemplate&&n.push(`Template stored: ${t.promptTemplate.length} chars`),p(n.join(`
41
+ `).trimEnd())}function Ls(e){let t=e.taskEnv??"OMNISH_TASK",n=[`Recipe: ${e.name}`,`Source: ${e.source}`,`Label: ${e.label??"(none)"}`,`Task env: ${t}`,`Command: ${e.command}`];if(e.promptTemplate){n.push(`Template: ${e.promptTemplate.length} chars`);let o=e.promptTemplate.replace(/\s+/g," ").trim().slice(0,200);n.push(`Preview: ${o}${e.promptTemplate.length>200?"\u2026":""}`)}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(`
42
+ `))}function Ar(e,t){let n=[`Recipe saved: ${e}`,`\u2192 ${t.command}`,`(task env: ${t.taskEnv??"OMNISH_TASK"})`];return t.promptTemplate&&n.push(`Template stored: ${t.promptTemplate.length} chars`),p(n.join(`
43
43
  `))}function Tn(e){return p(`Unknown recipe: ${e}
44
- /run list`)}function Ar(){return[{kind:"title",text:"Apps (interactive CLI)"},{kind:"sub",text:"Sessions"},{kind:"bullet",text:"/apps start <name> <command\u2026> \u2014 spawn in session cwd; auto-attaches"},{kind:"bullet",text:"/apps attach <name> | /apps detach"},{kind:"bullet",text:"/apps list | /apps info <name>"},{kind:"gap"},{kind:"sub",text:"Input"},{kind:"bullet",text:"/apps send <name> <text> \u2014 text + newline"},{kind:"bullet",text:">name text \u2014 shorthand for send"},{kind:"bullet",text:"/apps key <name> <KEY[,KEY\u2026]> \u2014 Enter, ^C, Up, Esc, \\x1b, \u2026"},{kind:"gap"},{kind:"sub",text:"Output & control"},{kind:"bullet",text:"/apps tail <name> [lines] | /apps since <name>"},{kind:"bullet",text:"/apps mute|unmute <name> | /apps raw <name> on|off"},{kind:"bullet",text:"/apps resize <name> <cols> <rows>"},{kind:"bullet",text:"/apps stop <name> | /apps kill <name> | /apps rm <name>"},{kind:"gap"},{kind:"p",text:"Attached: plain DMs go to the focused app. Escaped by ! prefix, /commands, or >other."},{kind:"p",text:"Free shell: !!start \u2014 plain DMs run as sync shell when no focused PTY; !!stop \u2014 off. Attached session wins over free shell for plain text."}]}function yl(e){let{errors:t,warns:n,infos:r}=kr(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:se(`[${u}] ${l.code}: ${l.message}`)}),l.detail&&o.push({kind:"bullet",text:se(l.detail)}),l.fixHint&&o.push({kind:"bullet",text:se(`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 Ls(e){return _(yl(e))}function Ps(){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 Ns(){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 Bs from"node:fs";import wl from"node:path";var Fs=500,Ds=/^[a-zA-Z0-9][a-zA-Z0-9_-]{0,31}$/,bl=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"]),In=new Map,_s=!1;function kl(){try{let e=Bs.readFileSync(ln,"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())}In.set(n,o)}}catch{}}function Hs(){N(wl.dirname(ln));let e={};for(let[t,n]of In)Object.entries(n).length>0&&(e[t]={...n});Bs.writeFileSync(ln,JSON.stringify(e,null,2)+`
45
- `,{mode:384})}function Sl(){_s||(kl(),_s=!0)}function xl(e){return bl.has(e.trim().toLowerCase())}function An(e){let t=e.trim();if(!t)return{ok:!1,error:"Name is empty."};let n=t.toLowerCase();return Ds.test(n)?xl(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 vl(e){let t=e.replace(/\r\n/g,`
46
- `).replace(/\n/g," ").trim();return t?t.length>Fs?{ok:!1,error:`Body too long (max ${Fs} characters).`}:{ok:!0,body:t}:{ok:!1,error:"Body is empty."}}function En(e){Sl();let t=In.get(e);return t||(t={},In.set(e,t)),t}function js(e){let t=En(e);return Object.entries(t).map(([n,r])=>({name:n,body:r})).sort((n,r)=>n.name.localeCompare(r.name))}function bt(e,t){let n=t.trim().toLowerCase();return En(e)[n]}function Er(e,t,n){let r=An(t);if(!r.ok)throw new Error(r.error);let o=vl(n);if(!o.ok)throw new Error(o.error);let s=En(e);s[r.normalized]=o.body,Hs()}function Us(e,t){let n=t.trim().toLowerCase(),r=En(e);return n in r?(delete r[n],Hs(),!0):!1}function Ws(e){let t=e.trim();return t.length>0&&!/\s/.test(t)&&Ds.test(t.toLowerCase())}import Cl from"node:crypto";import Lr from"node:fs";import $l from"node:path";var Pr=/^[a-zA-Z0-9][a-zA-Z0-9_-]{0,31}$/,Nr=new Set(["help","list","show","add","set","remove","rm","del","run","r"]),On=new Map,Gs=!1;function Rl(){try{let e=Lr.readFileSync(un,"utf8"),t=JSON.parse(e);if(t&&typeof t=="object")for(let[n,r]of Object.entries(t)){if(!r||typeof r!="object")continue;let o={};for(let[s,i]of Object.entries(r)){let a=String(s).toLowerCase();i&&typeof i=="object"&&typeof i.command=="string"&&(o[a]=Qe(i))}On.set(n,o)}}catch{}}function Qe(e){let t=typeof e.taskEnv=="string"&&/^[A-Za-z_][A-Za-z0-9_]*$/.test(e.taskEnv)?e.taskEnv:"OMNISH_TASK",n=typeof e.promptTemplate=="string"&&e.promptTemplate.trim().length>0?e.promptTemplate.trim():void 0,r={command:String(e.command).trim(),taskEnv:t,label:typeof e.label=="string"?e.label.trim().slice(0,80):void 0,description:typeof e.description=="string"?e.description.trim().slice(0,200):void 0,category:typeof e.category=="string"?e.category.trim().slice(0,40):void 0,featured:typeof e.featured=="boolean"?e.featured:void 0,dangerous:typeof e.dangerous=="boolean"?e.dangerous:void 0};return n!==void 0&&(r.promptTemplate=n),r}function Js(){N($l.dirname(un));let e={};for(let[t,n]of On)Object.entries(n).length>0&&(e[t]={...n});Lr.writeFileSync(un,JSON.stringify(e,null,2)+`
47
- `,{mode:384})}function Ml(){Gs||(Rl(),Gs=!0)}function Fr(e){Ml();let t=On.get(e);return t||(t={},On.set(e,t)),t}function Tl(e,t){let n=e.taskEnv??"OMNISH_TASK",r=t.recipesMacroDefaultCommand.trim();return it(r,n).ok?Qe({...e,command:r,promptTemplate:e.command}):e}function Il(e,t){if(e.promptTemplate||it(e.command,t).ok||!Pn(e.command,t))return!1;let n=e.command;return n.includes("```")||n.length>2e3||/^"Create\b/i.test(n)||n.includes("<<<")||n.includes(`
48
- ### `)}function zs(e,t){let n=Qe(e),r=n.taskEnv??"OMNISH_TASK";return!n.promptTemplate&&Il(n,r)&&(n=Tl(n,t)),n}function Al(e){try{let t=Lr.readFileSync(cn,"utf8"),n=JSON.parse(t);if(!n||typeof n!="object")return{};let r={};for(let[o,s]of Object.entries(n)){let i=o.trim().toLowerCase();if(!(!Pr.test(i)||Nr.has(i))&&s&&typeof s=="object"&&typeof s.command=="string"){let l=zs(s,e),u=_r(l);if(!u.ok){console.warn(`[omnish] recipes.json: skipping "${i}": ${u.error}`);continue}r[i]=l}}return r}catch{return{}}}function El(e){return{claude:{command:'claude -p "$OMNISH_TASK" --allowedTools all --dangerously-skip-permissions',taskEnv:"OMNISH_TASK",label:"Claude Code",description:"Anthropic Claude Code CLI (requires `claude` on PATH)",category:"agents",featured:!0,dangerous:e.recipesAllowDangerousBuiltins},codex:{command:'codex "$OMNISH_TASK"',taskEnv:"OMNISH_TASK",label:"Codex CLI",description:"OpenAI Codex CLI if installed; override in recipes.json",category:"agents",featured:!1},gemini:{command:'gemini -p "$OMNISH_TASK"',taskEnv:"OMNISH_TASK",label:"Gemini CLI",description:"Google Gemini CLI if installed; override in recipes.json",category:"agents",featured:!1},cursor:{command:"agent --yolo --force -p ```Before writing any code:\n1. Analyze the codebase and state your full implementation plan\n2. List every file you will touch\n3. Identify any risks or ambiguities\n4. Then execute the plan step by step, <task> $OMNISH_TASK </task>```",taskEnv:"OMNISH_TASK",label:"Cursor Agent",description:"Cursor Agent if installed; override in recipes.json",category:"agents",featured:!0}}}function Or(e,t,n){for(let[r,o]of Object.entries(t)){let s=r.toLowerCase();!Pr.test(s)||Nr.has(s)||o.command.trim()&&e.set(s,{...o,name:s,source:n})}}function Ol(e,t){let n=Fr(e),r={};for(let[o,s]of Object.entries(n)){let i=zs(s,t);_r(i).ok&&(r[o]=i)}return r}function Ks(e,t){let n=new Map;return Or(n,El(t),"builtin"),Or(n,Al(t),"global"),Or(n,Ol(e,t),"peer"),n}function kt(e,t,n){let r=n.trim().toLowerCase();return Ks(e,t).get(r)}function Ln(e){let t=e.trim();if(!t)return{ok:!1,error:"Name is empty."};let n=t.toLowerCase();return Pr.test(n)?Nr.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 Ys(e,t){let n=e.replace(/\r\n/g,`
49
- `).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 Pn(e,t){return e.includes("$")?e.includes(t):!1}function it(e,t){let n=e.trim();if(!n)return{ok:!1,error:"Command is empty."};if(!/^[A-Za-z_][A-Za-z0-9_]*$/.test(t))return{ok:!1,error:"Invalid taskEnv (use letters, digits, _)."};if(!Pn(n,t))return{ok:!1,error:`Command must reference "$${t}" so the task is passed via the environment.`};let r=`"$${t}"`;return n.includes(r)?n.includes("```")?{ok:!1,error:"Recipe command must not contain markdown code fences (```). Use promptTemplate or split with a --- line (see /run help)."}:{ok:!0}:{ok:!1,error:`Command must include ${r} (quoted).`}}function _r(e){let t=e.taskEnv??"OMNISH_TASK",n=it(e.command,t);return n.ok?e.promptTemplate!==void 0&&e.promptTemplate.trim().length===0?{ok:!1,error:"promptTemplate cannot be empty."}:{ok:!0}:n}function Ll(e){let t=e,n=null,r=!1,o=-1,s=-1;for(let l=0;l<t.length;l+=1){let u=t[l];if(r){r=!1;continue}if(u==="\\"){r=!0;continue}if(n){u===n&&(n=null);continue}if(u==='"'||u==="'"){n=u;continue}if(u!=="-"||t[l+1]!=="-")continue;let c=l===0?"":t[l-1];if(c&&!/\s/.test(c))continue;let d=t.slice(l+2),m=/^(template|tamplate)\b/i.exec(d);if(!m)continue;let f=l+2+m[0].length,w=t[f]??"";if(!w||!/\s/.test(w))continue;o=l;let b=f;for(;b<t.length&&/\s/.test(t[b]);)b+=1;s=b;break}if(o<0||s<0)return{command:t.trim()};let i=t.slice(0,o).trim(),a=t.slice(s).trim();return(a.startsWith('"')&&a.endsWith('"')&&a.length>=2||a.startsWith("'")&&a.endsWith("'")&&a.length>=2)&&(a=a.slice(1,-1)),{command:i,template:a}}function Br(e,t){let n=e.replace(/\r\n/g,`
50
- `).trim();if(!n)throw new Error("Recipe body is empty.");let r="OMNISH_TASK",{command:o,template:s}=Ll(n);if(s!==void 0){if(!o)throw new Error("Command part (before --template) is empty.");if(!s.trim())throw new Error("Template part (after --template) is empty.");let c=it(o,r);if(!c.ok)throw new Error(c.error);return fetch("http://127.0.0.1:7557/ingest/2fbb6f7c-e2c0-4ae7-a5e8-a47dfe33e2bd",{method:"POST",headers:{"Content-Type":"application/json","X-Debug-Session-Id":"790643"},body:JSON.stringify({sessionId:"790643",hypothesisId:"H1",location:"recipes.ts:parseRecipeAddBody",message:"branch --template flag",data:{cmdLen:o.length,tmplLen:s.length},timestamp:Date.now()})}).catch(()=>{}),Qe({command:o,promptTemplate:s})}let i=/\n---\n/,a=n.search(i);if(a>=0){let c=n.slice(0,a).trim(),d=n.slice(a).replace(/^\n---\n/,"").trim();if(!c)throw new Error("Command part (before ---) is empty.");if(!d)throw new Error("Template part (after ---) is empty.");let m=it(c,r);if(!m.ok)throw new Error(m.error);return Qe({command:c,promptTemplate:d})}if(it(n,r).ok)return fetch("http://127.0.0.1:7557/ingest/2fbb6f7c-e2c0-4ae7-a5e8-a47dfe33e2bd",{method:"POST",headers:{"Content-Type":"application/json","X-Debug-Session-Id":"790643"},body:JSON.stringify({sessionId:"790643",hypothesisId:"H3",location:"recipes.ts:parseRecipeAddBody",message:"branch bare safe command",data:{cmdLen:n.length},timestamp:Date.now()})}).catch(()=>{}),Qe({command:n});let l=t.trim(),u=it(l,r);if(!u.ok)throw new Error(`recipesMacroDefaultCommand invalid (${u.error}). Fix config or paste runner then --- then template.`);return fetch("http://127.0.0.1:7557/ingest/2fbb6f7c-e2c0-4ae7-a5e8-a47dfe33e2bd",{method:"POST",headers:{"Content-Type":"application/json","X-Debug-Session-Id":"790643"},body:JSON.stringify({sessionId:"790643",hypothesisId:"H1",location:"recipes.ts:parseRecipeAddBody",message:"branch macro fallback",data:{bodyLen:n.length},timestamp:Date.now()})}).catch(()=>{}),Qe({command:l,promptTemplate:n})}function qs(e,t,n){let r="`".repeat(3),o=`<<<${r}$${t}${r}>>>`,s=e,i=0;s.includes(o)&&(i=s.split(o).length-1,s=s.split(o).join(n));let a=`<<<${t}>>>`,l=0;s.includes(a)&&(l=s.split(a).length-1,s=s.split(a).join(n));let u=new RegExp(`\\$${t}\\b`,"g"),c=s;s=s.replace(u,n);let d=(c.match(u)||[]).length;return fetch("http://127.0.0.1:7557/ingest/2fbb6f7c-e2c0-4ae7-a5e8-a47dfe33e2bd",{method:"POST",headers:{"Content-Type":"application/json","X-Debug-Session-Id":"790643"},body:JSON.stringify({sessionId:"790643",hypothesisId:"H2",location:"recipes.ts:applyPromptTemplate",message:"substitution counts",data:{nFence:i,nTri:l,nDollar:d,tmplLen:e.length,taskLen:n.length},timestamp:Date.now()})}).catch(()=>{}),s}function Dr(e,t,n){let r=Ln(t);if(!r.ok)throw new Error(r.error);let o=Qe({...n,command:n.command}),s=_r(o);if(!s.ok)throw new Error(s.error);let i=Fr(e);i[r.normalized]=o,Js()}function Vs(e,t){let n=t.trim().toLowerCase(),r=Fr(e);return n in r?(delete r[n],Js(),!0):!1}function Xs(e,t){let n=[...Ks(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 Qs(e){let t=Cl.randomBytes(4).toString("hex");return`r-${e.replace(/[^a-zA-Z0-9_-]/g,"").slice(0,12)}-${t}`.slice(0,32)}import Pl from"node:os";import*as ei from"node-pty";function Nl(e,t){return e.length<=t?e:`${e.slice(0,t)}
51
- [...truncated]`}function Fl(e){if(e===void 0||e===0)return null;let t=Pl.constants.signals;for(let[n,r]of Object.entries(t))if(r===e)return n;return null}function Zs(e){try{process.platform==="win32"?e.kill():e.kill("SIGTERM")}catch{e.kill()}}function Nn(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,f=b=>{if(u)return;u=!0,m!==void 0&&clearTimeout(m),c?.dispose(),c=null,r(b);let h=d;d=null,queueMicrotask(()=>h?.dispose())},w={...process.env,TERM:"xterm-256color",COLORTERM:"truecolor",...a?{PWD:a}:{}};try{l=ei.spawn(e,["-c",t],{name:"xterm-256color",cols:120,rows:40,cwd:a,env:w})}catch(b){f({code:null,stdout:"",stderr:String(b),durationMs:Date.now()-o,timedOut:!1,signal:null});return}m=setTimeout(()=>{s=!0,l&&Zs(l)},n.timeoutMs),c=l.onData(b=>{i+=b,i.length>n.maxBytes&&(i=Nl(i,n.maxBytes),l&&Zs(l))}),d=l.onExit(b=>{f({code:b.exitCode,stdout:i,stderr:"",durationMs:Date.now()-o,timedOut:s,signal:Fl(b.signal)})})})}import St from"node:fs";import Wr from"node:os";import Ht from"node:path";import oi from"node:crypto";var jr="\u2063omnish/c v1",_l=/([nlra])=([^\s\]]*)/g;function Ie(e){return e.replace(/-/g,"").slice(0,8)}function Hr(e){return e.replace(/[\s\]\r\n]/g,"_").slice(0,64)}function Bl(e){let t=Hr(Ie(e.nodeId)),n=Hr(e.label||""),r=e.role==="primary"?"p":"s",o=Hr(e.activeNodeId?Ie(e.activeNodeId):"");return`${jr} [n=${t} l=${n} r=${r} a=${o}]`}function ti(e,t){let n=Bl(t);if(e.includes(n))return e;let r=e.replace(/\s+$/,"");return r.length===0?n:`${r}
44
+ /run list`)}function Er(){return[{kind:"title",text:"Apps (interactive CLI)"},{kind:"sub",text:"Sessions"},{kind:"bullet",text:"/apps start <name> <command\u2026> \u2014 spawn in session cwd; auto-attaches"},{kind:"bullet",text:"/apps attach <name> | /apps detach"},{kind:"bullet",text:"/apps list | /apps info <name>"},{kind:"gap"},{kind:"sub",text:"Input"},{kind:"bullet",text:"/apps send <name> <text> \u2014 text + newline"},{kind:"bullet",text:">name text \u2014 shorthand for send"},{kind:"bullet",text:"/apps key <name> <KEY[,KEY\u2026]> \u2014 Enter, ^C, Up, Esc, \\x1b, \u2026"},{kind:"gap"},{kind:"sub",text:"Output & control"},{kind:"bullet",text:"/apps tail <name> [lines] | /apps since <name>"},{kind:"bullet",text:"/apps mute|unmute <name> | /apps raw <name> on|off"},{kind:"bullet",text:"/apps resize <name> <cols> <rows>"},{kind:"bullet",text:"/apps stop <name> | /apps kill <name> | /apps rm <name>"},{kind:"gap"},{kind:"p",text:"Attached: plain DMs go to the focused app. Escaped by ! prefix, /commands, or >other."},{kind:"p",text:"Free shell: !!start \u2014 plain DMs run as sync shell when no focused PTY; !!stop \u2014 off. Attached session wins over free shell for plain text."}]}function yl(e){let{errors:t,warns:n,infos:r}=Sr(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=(a,i)=>{if(i.length!==0){o.push({kind:"sub",text:`${a} (${i.length})`});for(let l of i){let u=l.severity.toUpperCase();o.push({kind:"bullet",text:se(`[${u}] ${l.code}: ${l.message}`)}),l.detail&&o.push({kind:"bullet",text:se(l.detail)}),l.fixHint&&o.push({kind:"bullet",text:se(`Fix: ${l.fixHint}`)})}o.push({kind:"gap"})}};return s("Errors",e.filter(a=>a.severity==="error")),s("Warnings",e.filter(a=>a.severity==="warn")),s("Notes",e.filter(a=>a.severity==="info")),o.push({kind:"p",text:"Allowlisted identities can run commands as this user; treat them like passwords."}),o}function Ps(e){return _(yl(e))}function Ns(){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 Fs(){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 Hs from"node:fs";import wl from"node:path";var _s=500,Ds=/^[a-zA-Z0-9][a-zA-Z0-9_-]{0,31}$/,bl=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"]),In=new Map,Bs=!1;function kl(){try{let e=Hs.readFileSync(ln,"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,a]of Object.entries(r)){let i=String(s).toLowerCase();typeof a=="string"&&a.length>0&&(o[i]=a.trim())}In.set(n,o)}}catch{}}function js(){N(wl.dirname(ln));let e={};for(let[t,n]of In)Object.entries(n).length>0&&(e[t]={...n});Hs.writeFileSync(ln,JSON.stringify(e,null,2)+`
45
+ `,{mode:384})}function Sl(){Bs||(kl(),Bs=!0)}function vl(e){return bl.has(e.trim().toLowerCase())}function An(e){let t=e.trim();if(!t)return{ok:!1,error:"Name is empty."};let n=t.toLowerCase();return Ds.test(n)?vl(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 xl(e){let t=e.replace(/\r\n/g,`
46
+ `).replace(/\n/g," ").trim();return t?t.length>_s?{ok:!1,error:`Body too long (max ${_s} characters).`}:{ok:!0,body:t}:{ok:!1,error:"Body is empty."}}function En(e){Sl();let t=In.get(e);return t||(t={},In.set(e,t)),t}function Us(e){let t=En(e);return Object.entries(t).map(([n,r])=>({name:n,body:r})).sort((n,r)=>n.name.localeCompare(r.name))}function bt(e,t){let n=t.trim().toLowerCase();return En(e)[n]}function Or(e,t,n){let r=An(t);if(!r.ok)throw new Error(r.error);let o=xl(n);if(!o.ok)throw new Error(o.error);let s=En(e);s[r.normalized]=o.body,js()}function Ws(e,t){let n=t.trim().toLowerCase(),r=En(e);return n in r?(delete r[n],js(),!0):!1}function Gs(e){let t=e.trim();return t.length>0&&!/\s/.test(t)&&Ds.test(t.toLowerCase())}import $l from"node:crypto";import Pr from"node:fs";import Cl from"node:path";var Nr=/^[a-zA-Z0-9][a-zA-Z0-9_-]{0,31}$/,Fr=new Set(["help","list","show","add","set","remove","rm","del","run","r"]),On=new Map,Js=!1;function Rl(){try{let e=Pr.readFileSync(un,"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,a]of Object.entries(r)){let i=String(s).toLowerCase();a&&typeof a=="object"&&typeof a.command=="string"&&(o[i]=Xe(a))}On.set(n,o)}}catch{}}function Xe(e){let t=typeof e.taskEnv=="string"&&/^[A-Za-z_][A-Za-z0-9_]*$/.test(e.taskEnv)?e.taskEnv:"OMNISH_TASK",n=typeof e.promptTemplate=="string"&&e.promptTemplate.trim().length>0?e.promptTemplate.trim():void 0,r={command:String(e.command).trim(),taskEnv:t,label:typeof e.label=="string"?e.label.trim().slice(0,80):void 0,description:typeof e.description=="string"?e.description.trim().slice(0,200):void 0,category:typeof e.category=="string"?e.category.trim().slice(0,40):void 0,featured:typeof e.featured=="boolean"?e.featured:void 0,dangerous:typeof e.dangerous=="boolean"?e.dangerous:void 0};return n!==void 0&&(r.promptTemplate=n),r}function zs(){N(Cl.dirname(un));let e={};for(let[t,n]of On)Object.entries(n).length>0&&(e[t]={...n});Pr.writeFileSync(un,JSON.stringify(e,null,2)+`
47
+ `,{mode:384})}function Ml(){Js||(Rl(),Js=!0)}function _r(e){Ml();let t=On.get(e);return t||(t={},On.set(e,t)),t}function Tl(e,t){let n=e.taskEnv??"OMNISH_TASK",r=t.recipesMacroDefaultCommand.trim();return it(r,n).ok?Xe({...e,command:r,promptTemplate:e.command}):e}function Il(e,t){if(e.promptTemplate||it(e.command,t).ok||!Pn(e.command,t))return!1;let n=e.command;return n.includes("```")||n.length>2e3||/^"Create\b/i.test(n)||n.includes("<<<")||n.includes(`
48
+ ### `)}function qs(e,t){let n=Xe(e),r=n.taskEnv??"OMNISH_TASK";return!n.promptTemplate&&Il(n,r)&&(n=Tl(n,t)),n}function Al(e){try{let t=Pr.readFileSync(cn,"utf8"),n=JSON.parse(t);if(!n||typeof n!="object")return{};let r={};for(let[o,s]of Object.entries(n)){let a=o.trim().toLowerCase();if(!(!Nr.test(a)||Fr.has(a))&&s&&typeof s=="object"&&typeof s.command=="string"){let l=qs(s,e),u=Br(l);if(!u.ok){console.warn(`[omnish] recipes.json: skipping "${a}": ${u.error}`);continue}r[a]=l}}return r}catch{return{}}}function El(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 Lr(e,t,n){for(let[r,o]of Object.entries(t)){let s=r.toLowerCase();!Nr.test(s)||Fr.has(s)||o.command.trim()&&e.set(s,{...o,name:s,source:n})}}function Ol(e,t){let n=_r(e),r={};for(let[o,s]of Object.entries(n)){let a=qs(s,t);Br(a).ok&&(r[o]=a)}return r}function Qs(e,t){let n=new Map;return Lr(n,El(t),"builtin"),Lr(n,Al(t),"global"),Lr(n,Ol(e,t),"peer"),n}function kt(e,t,n){let r=n.trim().toLowerCase();return Qs(e,t).get(r)}function Ln(e){let t=e.trim();if(!t)return{ok:!1,error:"Name is empty."};let n=t.toLowerCase();return Nr.test(n)?Fr.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 Ys(e,t){let n=e.replace(/\r\n/g,`
49
+ `).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 Pn(e,t){return e.includes("$")?e.includes(t):!1}function it(e,t){let n=e.trim();if(!n)return{ok:!1,error:"Command is empty."};if(!/^[A-Za-z_][A-Za-z0-9_]*$/.test(t))return{ok:!1,error:"Invalid taskEnv (use letters, digits, _)."};if(!Pn(n,t))return{ok:!1,error:`Command must reference "$${t}" so the task is passed via the environment.`};let r=`"$${t}"`;return n.includes(r)?n.includes("```")?{ok:!1,error:"Recipe command must not contain markdown code fences (```). Use promptTemplate or split with a --- line (see /run help)."}:{ok:!0}:{ok:!1,error:`Command must include ${r} (quoted).`}}function Br(e){let t=e.taskEnv??"OMNISH_TASK",n=it(e.command,t);return n.ok?e.promptTemplate!==void 0&&e.promptTemplate.trim().length===0?{ok:!1,error:"promptTemplate cannot be empty."}:{ok:!0}:n}function Ll(e){let t=e,n=null,r=!1,o=-1,s=-1;for(let l=0;l<t.length;l+=1){let u=t[l];if(r){r=!1;continue}if(u==="\\"){r=!0;continue}if(n){u===n&&(n=null);continue}if(u==='"'||u==="'"){n=u;continue}if(u!=="-"||t[l+1]!=="-")continue;let c=l===0?"":t[l-1];if(c&&!/\s/.test(c))continue;let d=t.slice(l+2),m=/^(template|tamplate)\b/i.exec(d);if(!m)continue;let f=l+2+m[0].length,y=t[f]??"";if(!y||!/\s/.test(y))continue;o=l;let b=f;for(;b<t.length&&/\s/.test(t[b]);)b+=1;s=b;break}if(o<0||s<0)return{command:t.trim()};let a=t.slice(0,o).trim(),i=t.slice(s).trim();return(i.startsWith('"')&&i.endsWith('"')&&i.length>=2||i.startsWith("'")&&i.endsWith("'")&&i.length>=2)&&(i=i.slice(1,-1)),{command:a,template:i}}function Hr(e,t){let n=e.replace(/\r\n/g,`
50
+ `).trim();if(!n)throw new Error("Recipe body is empty.");let r="OMNISH_TASK",{command:o,template:s}=Ll(n);if(s!==void 0){if(!o)throw new Error("Command part (before --template) is empty.");if(!s.trim())throw new Error("Template part (after --template) is empty.");let c=it(o,r);if(!c.ok)throw new Error(c.error);return fetch("http://127.0.0.1:7557/ingest/2fbb6f7c-e2c0-4ae7-a5e8-a47dfe33e2bd",{method:"POST",headers:{"Content-Type":"application/json","X-Debug-Session-Id":"790643"},body:JSON.stringify({sessionId:"790643",hypothesisId:"H1",location:"recipes.ts:parseRecipeAddBody",message:"branch --template flag",data:{cmdLen:o.length,tmplLen:s.length},timestamp:Date.now()})}).catch(()=>{}),Xe({command:o,promptTemplate:s})}let a=/\n---\n/,i=n.search(a);if(i>=0){let c=n.slice(0,i).trim(),d=n.slice(i).replace(/^\n---\n/,"").trim();if(!c)throw new Error("Command part (before ---) is empty.");if(!d)throw new Error("Template part (after ---) is empty.");let m=it(c,r);if(!m.ok)throw new Error(m.error);return Xe({command:c,promptTemplate:d})}if(it(n,r).ok)return fetch("http://127.0.0.1:7557/ingest/2fbb6f7c-e2c0-4ae7-a5e8-a47dfe33e2bd",{method:"POST",headers:{"Content-Type":"application/json","X-Debug-Session-Id":"790643"},body:JSON.stringify({sessionId:"790643",hypothesisId:"H3",location:"recipes.ts:parseRecipeAddBody",message:"branch bare safe command",data:{cmdLen:n.length},timestamp:Date.now()})}).catch(()=>{}),Xe({command:n});let l=t.trim(),u=it(l,r);if(!u.ok)throw new Error(`recipesMacroDefaultCommand invalid (${u.error}). Fix config or paste runner then --- then template.`);return fetch("http://127.0.0.1:7557/ingest/2fbb6f7c-e2c0-4ae7-a5e8-a47dfe33e2bd",{method:"POST",headers:{"Content-Type":"application/json","X-Debug-Session-Id":"790643"},body:JSON.stringify({sessionId:"790643",hypothesisId:"H1",location:"recipes.ts:parseRecipeAddBody",message:"branch macro fallback",data:{bodyLen:n.length},timestamp:Date.now()})}).catch(()=>{}),Xe({command:l,promptTemplate:n})}function Ks(e,t,n){let r="`".repeat(3),o=`<<<${r}$${t}${r}>>>`,s=e,a=0;s.includes(o)&&(a=s.split(o).length-1,s=s.split(o).join(n));let i=`<<<${t}>>>`,l=0;s.includes(i)&&(l=s.split(i).length-1,s=s.split(i).join(n));let u=new RegExp(`\\$${t}\\b`,"g"),c=s;s=s.replace(u,n);let d=(c.match(u)||[]).length;return fetch("http://127.0.0.1:7557/ingest/2fbb6f7c-e2c0-4ae7-a5e8-a47dfe33e2bd",{method:"POST",headers:{"Content-Type":"application/json","X-Debug-Session-Id":"790643"},body:JSON.stringify({sessionId:"790643",hypothesisId:"H2",location:"recipes.ts:applyPromptTemplate",message:"substitution counts",data:{nFence:a,nTri:l,nDollar:d,tmplLen:e.length,taskLen:n.length},timestamp:Date.now()})}).catch(()=>{}),s}function Dr(e,t,n){let r=Ln(t);if(!r.ok)throw new Error(r.error);let o=Xe({...n,command:n.command}),s=Br(o);if(!s.ok)throw new Error(s.error);let a=_r(e);a[r.normalized]=o,zs()}function Vs(e,t){let n=t.trim().toLowerCase(),r=_r(e);return n in r?(delete r[n],zs(),!0):!1}function Xs(e,t){let n=[...Qs(e,t).values()],r=n.filter(a=>a.featured).sort((a,i)=>a.name.localeCompare(i.name)),o=n.filter(a=>a.source==="peer").sort((a,i)=>a.name.localeCompare(i.name)),s=n.filter(a=>!a.featured&&a.source!=="peer").sort((a,i)=>a.name.localeCompare(i.name));return{featured:r,yours:o,more:s}}function Nn(e){let t=$l.randomBytes(4).toString("hex");return`r-${e.replace(/[^a-zA-Z0-9_-]/g,"").slice(0,12)}-${t}`.slice(0,32)}import Pl from"node:os";import*as ei from"node-pty";function Nl(e,t){return e.length<=t?e:`${e.slice(0,t)}
51
+ [...truncated]`}function Fl(e){if(e===void 0||e===0)return null;let t=Pl.constants.signals;for(let[n,r]of Object.entries(t))if(r===e)return n;return null}function Zs(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,a="",i=n.cwd,l=null,u=!1,c=null,d=null,m,f=b=>{if(u)return;u=!0,m!==void 0&&clearTimeout(m),c?.dispose(),c=null,r(b);let h=d;d=null,queueMicrotask(()=>h?.dispose())},y={...process.env,TERM:"xterm-256color",COLORTERM:"truecolor",...i?{PWD:i}:{}};try{l=ei.spawn(e,["-c",t],{name:"xterm-256color",cols:120,rows:40,cwd:i,env:y})}catch(b){f({code:null,stdout:"",stderr:String(b),durationMs:Date.now()-o,timedOut:!1,signal:null});return}m=setTimeout(()=>{s=!0,l&&Zs(l)},n.timeoutMs),c=l.onData(b=>{a+=b,a.length>n.maxBytes&&(a=Nl(a,n.maxBytes),l&&Zs(l))}),d=l.onExit(b=>{f({code:b.exitCode,stdout:a,stderr:"",durationMs:Date.now()-o,timedOut:s,signal:Fl(b.signal)})})})}import St from"node:fs";import Gr from"node:os";import Dt from"node:path";import oi from"node:crypto";var Ur="\u2063omnish/c v1",_l=/([nlra])=([^\s\]]*)/g;function Ie(e){return e.replace(/-/g,"").slice(0,8)}function jr(e){return e.replace(/[\s\]\r\n]/g,"_").slice(0,64)}function Bl(e){let t=jr(Ie(e.nodeId)),n=jr(e.label||""),r=e.role==="primary"?"p":"s",o=jr(e.activeNodeId?Ie(e.activeNodeId):"");return`${Ur} [n=${t} l=${n} r=${r} a=${o}]`}function ti(e,t){let n=Bl(t);if(e.includes(n))return e;let r=e.replace(/\s+$/,"");return r.length===0?n:`${r}
52
52
 
53
- ${n}`}function ni(e){if(!e||!e.includes(jr))return null;let t=e.indexOf(jr),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(_l))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 si=3,Dl="node-id",Hl="cluster-local.json",ri=8;function ii(){return Ht.join(A,Hl)}function Gr(){return Ht.join(A,Dl)}function xe(){N(A);let e=Gr();try{if(St.existsSync(e)){let n=St.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=oi.randomUUID();return St.writeFileSync(e,`${t}
54
- `,{mode:384}),t}function ai(){return{schemaVersion:si,updatedAt:new Date().toISOString(),peers:[],senderBindings:{}}}function jl(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 X(){let e=ii();try{let t=St.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=jl(n.senderBindings);return{schemaVersion:si,updatedAt:typeof n.updatedAt=="string"?n.updatedAt:new Date().toISOString(),peers:r,senderBindings:o}}catch{return ai()}}function Ul(e,t){let n=Ht.dirname(e);N(n);let r=`${JSON.stringify(t,null,2)}
55
- `,o=Ht.join(n,`.${Ht.basename(e)}.tmp.${process.pid}.${oi.randomBytes(4).toString("hex")}`);St.writeFileSync(o,r,{mode:384}),St.renameSync(o,e)}function zr(e){let t=ii(),n=ai();for(let r=0;r<ri;r++){n=X(),e(n),n.updatedAt=new Date().toISOString();try{return Ul(t,n),n}catch{}}throw new Error(`Could not write cluster local state after ${ri} attempts: ${t}`)}function li(e){return(e.clusterLabel??"").trim()||Wr.hostname()}function Q(e){return e.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;")}function ci(e,t){let n=e.peers.findIndex(r=>r.nodeId===t.nodeId);n>=0?e.peers[n]=t:e.peers.push(t)}function jt(e){return{nodeId:Ie(xe()),label:li(e),role:e.clusterRole,lastSeenIso:new Date().toISOString()}}function ui(e,t,n){let r=n.trim();if(!r)return{ok:!1,reason:"not-found"};let o=jt(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 Ge(e,t){let n=X(),r=n.senderBindings[t];if(r&&r.nodeId)return r;let o=e.clusterSenderBindings?.[t];if(typeof o=="string"&&o.trim()){let s=ui(n,e,o);if(s.ok)return{senderKey:t,nodeId:s.peer.nodeId,sinceIso:new Date(0).toISOString(),source:"config"}}return null}function Jr(e,t,n="chat"){let r=v(),o=X(),s=ui(o,r,t);if(!s.ok)return{state:o,resolved:s};let i=new Date().toISOString();return{state:zr(l=>{l.senderBindings[e]={senderKey:e,nodeId:s.peer.nodeId,sinceIso:i,source:n},ci(l,{...s.peer,lastSeenIso:i})}),resolved:s}}function Wl(e){let t=null;return{state:zr(r=>{r.senderBindings[e]&&(t=r.senderBindings[e]??null,delete r.senderBindings[e])}),removed:t}}function di(e,t){let n=Ie(xe()),r=new Date().toISOString();return zr(o=>{if(ci(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 pi(e,t){let n=Ge(t,e);return n?n.nodeId===Ie(xe()):!1}function Gl(e,t){let n=jt(t).nodeId,r=new Set([n]);for(let s of e.peers)r.add(s.nodeId);return[...r].sort()[0]??n}function he(e,t){return Gl(e,t)===Ie(xe())}function Fn(e,t,n){let r=Ie(xe()),o=["*Computers*",""],s=n?Ge(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,jt(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(`
56
- `);for(let l of a){let u=l.nodeId===r,d=[(s?l.nodeId===s.nodeId:!1)?"your binding":null,u?"you":null].filter(Boolean).join(", "),m=d?` (${d})`:"";o.push(`${ge}*${l.label}*${m}
53
+ ${n}`}function ni(e){if(!e||!e.includes(Ur))return null;let t=e.indexOf(Ur),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),a={};for(let l of s.matchAll(_l))a[l[1]]=l[2]??"";if(!a.n)return null;let i=a.r==="p"?"primary":"secondary";return{nodeId:a.n,label:a.l??"",role:i,activeNodeId:a.a??""}}var si=3,Hl="node-id",Dl="cluster-local.json",ri=8;function ii(){return Dt.join(A,Dl)}function Jr(){return Dt.join(A,Hl)}function ve(){N(A);let e=Jr();try{if(St.existsSync(e)){let n=St.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=oi.randomUUID();return St.writeFileSync(e,`${t}
54
+ `,{mode:384}),t}function ai(){return{schemaVersion:si,updatedAt:new Date().toISOString(),peers:[],senderBindings:{}}}function jl(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 V(){let e=ii();try{let t=St.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=jl(n.senderBindings);return{schemaVersion:si,updatedAt:typeof n.updatedAt=="string"?n.updatedAt:new Date().toISOString(),peers:r,senderBindings:o}}catch{return ai()}}function Ul(e,t){let n=Dt.dirname(e);N(n);let r=`${JSON.stringify(t,null,2)}
55
+ `,o=Dt.join(n,`.${Dt.basename(e)}.tmp.${process.pid}.${oi.randomBytes(4).toString("hex")}`);St.writeFileSync(o,r,{mode:384}),St.renameSync(o,e)}function qr(e){let t=ii(),n=ai();for(let r=0;r<ri;r++){n=V(),e(n),n.updatedAt=new Date().toISOString();try{return Ul(t,n),n}catch{}}throw new Error(`Could not write cluster local state after ${ri} attempts: ${t}`)}function li(e){return(e.clusterLabel??"").trim()||Gr.hostname()}function X(e){return e.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;")}function ci(e,t){let n=e.peers.findIndex(r=>r.nodeId===t.nodeId);n>=0?e.peers[n]=t:e.peers.push(t)}function jt(e){return{nodeId:Ie(ve()),label:li(e),role:e.clusterRole,lastSeenIso:new Date().toISOString()}}function ui(e,t,n){let r=n.trim();if(!r)return{ok:!1,reason:"not-found"};let o=jt(t),s=[o];for(let l of e.peers)l.nodeId!==o.nodeId&&s.push(l);let a=r.toLowerCase();if(/^[0-9a-f]{8}$/i.test(r)){let l=s.find(u=>u.nodeId.toLowerCase()===a);if(l)return{ok:!0,peer:l}}let i=s.filter(l=>l.label.toLowerCase()===a);return i.length===1?{ok:!0,peer:i[0]}:i.length>1?{ok:!1,reason:"ambiguous-label",matches:i}:{ok:!1,reason:"not-found"}}function Ge(e,t){let n=V(),r=n.senderBindings[t];if(r&&r.nodeId)return r;let o=e.clusterSenderBindings?.[t];if(typeof o=="string"&&o.trim()){let s=ui(n,e,o);if(s.ok)return{senderKey:t,nodeId:s.peer.nodeId,sinceIso:new Date(0).toISOString(),source:"config"}}return null}function zr(e,t,n="chat"){let r=x(),o=V(),s=ui(o,r,t);if(!s.ok)return{state:o,resolved:s};let a=new Date().toISOString();return{state:qr(l=>{l.senderBindings[e]={senderKey:e,nodeId:s.peer.nodeId,sinceIso:a,source:n},ci(l,{...s.peer,lastSeenIso:a})}),resolved:s}}function Wl(e){let t=null;return{state:qr(r=>{r.senderBindings[e]&&(t=r.senderBindings[e]??null,delete r.senderBindings[e])}),removed:t}}function di(e,t){let n=Ie(ve()),r=new Date().toISOString();return qr(o=>{if(ci(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 pi(e,t){let n=Ge(t,e);return n?n.nodeId===Ie(ve()):!1}function Gl(e,t){let n=jt(t).nodeId,r=new Set([n]);for(let s of e.peers)r.add(s.nodeId);return[...r].sort()[0]??n}function he(e,t){return Gl(e,t)===Ie(ve())}function _n(e,t,n){let r=Ie(ve()),o=["*Computers*",""],s=n?Ge(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 a=new Map;a.set(r,jt(t));for(let l of e.peers)a.set(l.nodeId,l);let i=[...a.values()].sort((l,u)=>l.label.localeCompare(u.label));if(i.length===0)return o.push("(no peers observed yet \u2014 run /c status from this chat)"),o.join(`
56
+ `);for(let l of i){let u=l.nodeId===r,d=[(s?l.nodeId===s.nodeId:!1)?"your binding":null,u?"you":null].filter(Boolean).join(", "),m=d?` (${d})`:"";o.push(`${ge}*${l.label}*${m}
57
57
  id \`${l.nodeId}\` \xB7 role ${l.role}
58
58
  seen ${l.lastSeenIso}`)}return o.push(""),o.push(`Updated: ${e.updatedAt}`),o.join(`
59
- `)}function Ur(e,t,n){let r=Ie(xe()),o=["<b>Computers</b>",""],s=n?Ge(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,jt(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(`
60
- `);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?` (${Q(d)})`:"";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(`
61
- `)}function Kr(e,t,n){return Fn(e,t,n??null).replace(/\*([^*]+)\*/g,"$1").replace(/`([^`]+)`/g,"$1")}function Yr(e,t){let n=xe(),r=Ie(n),o=X(),s=e.clusterRole,i=li(e),a=t?Ge(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 ${Gr()})`,`host: ${Wr.hostname()}`,`clusterRole: ${s}`,u,`peers known: ${o.peers.length}`,`senderBindings (chat): ${Object.keys(o.senderBindings).length}`].filter(Boolean).join(`
62
- `),d=["<b>This computer</b>","",`label: ${Q(i)}`,`node id: <code>${Q(r)}</code> (full id in ${Q(Gr())})`,`host: ${Q(Wr.hostname())}`,`clusterRole: ${Q(s)}`,t?Q(u):"",`peers known: ${o.peers.length}`,`senderBindings (chat): ${Object.keys(o.senderBindings).length}`].filter(Boolean).join(`
59
+ `)}function Wr(e,t,n){let r=Ie(ve()),o=["<b>Computers</b>",""],s=n?Ge(t,n):null;n&&(s?o.push(`Your binding: <code>${X(s.nodeId)}</code> (${X(s.source)})`):o.push("Your binding: (none \u2014 send /c use &lt;label-or-id&gt;)"),o.push(""));let a=new Map;a.set(r,jt(t));for(let l of e.peers)a.set(l.nodeId,l);let i=[...a.values()].sort((l,u)=>l.label.localeCompare(u.label));if(i.length===0)return o.push("(no peers observed yet \u2014 run /c status from this chat)"),o.join(`
60
+ `);for(let l of i){let u=l.nodeId===r,d=[(s?l.nodeId===s.nodeId:!1)?"your binding":null,u?"you":null].filter(Boolean).join(", "),m=d?` (${X(d)})`:"";o.push(`\u2022 <b>${X(l.label)}</b>${m}<br/> id <code>${X(l.nodeId)}</code> \xB7 role ${X(l.role)}<br/> seen ${X(l.lastSeenIso)}`)}return o.push(""),o.push(`Updated: ${X(e.updatedAt)}`),o.join(`
61
+ `)}function Qr(e,t,n){return _n(e,t,n??null).replace(/\*([^*]+)\*/g,"$1").replace(/`([^`]+)`/g,"$1")}function Yr(e,t){let n=ve(),r=Ie(n),o=V(),s=e.clusterRole,a=li(e),i=t?Ge(e,t):null,l=i?i.nodeId===r:!1,u=t?i?`your binding: ${i.nodeId} (${i.source}${l?", here":""})`:"your binding: (none)":"",c=["*This computer*","",`label: ${a}`,`node id: \`${r}\` (full id in ${Jr()})`,`host: ${Gr.hostname()}`,`clusterRole: ${s}`,u,`peers known: ${o.peers.length}`,`senderBindings (chat): ${Object.keys(o.senderBindings).length}`].filter(Boolean).join(`
62
+ `),d=["<b>This computer</b>","",`label: ${X(a)}`,`node id: <code>${X(r)}</code> (full id in ${X(Jr())})`,`host: ${X(Gr.hostname())}`,`clusterRole: ${X(s)}`,t?X(u):"",`peers known: ${o.peers.length}`,`senderBindings (chat): ${Object.keys(o.senderBindings).length}`].filter(Boolean).join(`
63
63
  `);return{wa:c,tg:d}}function Jl(e){let t=e.clusterRole,n=["*Computers* (per-sender bindings)","","Each allowlisted phone picks one machine to talk to. Other senders are not affected.","Selection persists; defaults can be set in config (clusterSenderBindings).","","Shorthand: /pcs \u2026 \xB7 /c \u2026","",`${ge}/c use <label-or-id> \u2014 bind your messages to that machine`,`${ge}/c here \u2014 bind your messages to THIS machine`,`${ge}/c using \u2014 show your current binding`,`${ge}/c unuse \u2014 clear your chat-set binding (config default still applies, if any)`,`${ge}/c status \u2014 every online host replies with its own paragraph`,`${ge}/c list \u2014 locally known roster (single responder)`,`${ge}/c help \u2014 this help`,"",`clusterRole on this host: ${t}`].join(`
64
- `),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(`
65
- `);return K(n,r)}function zl(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 mi(e,t,n){let r=t.trim().split(/\s+/),o=r[0]?.toLowerCase()??"";if(!o||o==="help"){if(!e.clusterEnabled&&o!=="help"){let l=X();return he(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=X();return he(a,e)?Jl(e):null}let s=Ie(xe());if(o==="use"||o==="bind"){let a=r.slice(1).join(" ").trim();if(!n){let h=X();return he(h,e)?p("/c use is only available on chats with a known sender (allowlisted WhatsApp/Telegram)."):null}if(!a){let h=X();return he(h,e)?p("Usage: /c use <label-or-id>"):null}e.clusterEnabled||bn(!0);let u=X().senderBindings[n]??null,{state:c,resolved:d}=Jr(n,a,"chat");if(!d.ok)return he(c,e)?zl(a,d):null;let m=d.peer.nodeId===s,f=u?u.nodeId===s:!1;if(!m)return null;let w=[`*Bound to ${d.peer.label}*`,`id \`${d.peer.nodeId}\``,"","Your messages now route to this machine. Other senders are not affected.","",Fn(c,e,n)].join(`
66
- `),b=[`<b>Bound to ${Q(d.peer.label)}</b>`,`id <code>${Q(d.peer.nodeId)}</code>`,"","Your messages now route to this machine. Other senders are not affected.","",Ur(c,e,n)].join(`
67
- `);return K(w,b)}if(o==="here"||o==="take"){if(!n){let d=X();return he(d,e)?p("/c here is only available on chats with a known sender (allowlisted WhatsApp/Telegram)."):null}e.clusterEnabled||bn(!0);let{state:a,resolved:l}=Jr(n,s,"chat");if(!l.ok)return he(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.","",Fn(a,e,n)].join(`
68
- `),c=["<b>Bound to this machine.</b>","","Your messages now route here. Other senders are not affected.","",Ur(a,e,n)].join(`
69
- `);return K(u,c)}if(o==="using"){if(!n){let u=X();return he(u,e)?p("/c using is only available on chats with a known sender (allowlisted WhatsApp/Telegram)."):null}let a=Ge(e,n);if(a){if(a.nodeId!==s)return null;let d=[...X().peers,jt(e)].find(w=>w.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(`
70
- `),f=[`<b>Bound to ${Q(d)}</b>`,`id <code>${Q(a.nodeId)}</code> \xB7 source ${Q(a.source)}`,a.source==="chat"?`since ${Q(a.sinceIso)}`:"(from config)"].join(`
71
- `);return K(m,f)}let l=X();return he(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=X();return he(m,e)?p("/c unuse is only available on chats with a known sender (allowlisted WhatsApp/Telegram)."):null}let l=X().senderBindings[n]??null,{state:u}=Wl(n);if(l){if(l.nodeId!==s)return null}else if(!he(u,e))return null;let c=Ge(e,n),d=c?`
64
+ `),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: ${X(t)}`].join(`
65
+ `);return q(n,r)}function zl(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 mi(e,t,n){let r=t.trim().split(/\s+/),o=r[0]?.toLowerCase()??"";if(!o||o==="help"){if(!e.clusterEnabled&&o!=="help"){let l=V();return he(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 i=V();return he(i,e)?Jl(e):null}let s=Ie(ve());if(o==="use"||o==="bind"){let i=r.slice(1).join(" ").trim();if(!n){let h=V();return he(h,e)?p("/c use is only available on chats with a known sender (allowlisted WhatsApp/Telegram)."):null}if(!i){let h=V();return he(h,e)?p("Usage: /c use <label-or-id>"):null}e.clusterEnabled||bn(!0);let u=V().senderBindings[n]??null,{state:c,resolved:d}=zr(n,i,"chat");if(!d.ok)return he(c,e)?zl(i,d):null;let m=d.peer.nodeId===s,f=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.","",_n(c,e,n)].join(`
66
+ `),b=[`<b>Bound to ${X(d.peer.label)}</b>`,`id <code>${X(d.peer.nodeId)}</code>`,"","Your messages now route to this machine. Other senders are not affected.","",Wr(c,e,n)].join(`
67
+ `);return q(y,b)}if(o==="here"||o==="take"){if(!n){let d=V();return he(d,e)?p("/c here is only available on chats with a known sender (allowlisted WhatsApp/Telegram)."):null}e.clusterEnabled||bn(!0);let{state:i,resolved:l}=zr(n,s,"chat");if(!l.ok)return he(i,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.","",_n(i,e,n)].join(`
68
+ `),c=["<b>Bound to this machine.</b>","","Your messages now route here. Other senders are not affected.","",Wr(i,e,n)].join(`
69
+ `);return q(u,c)}if(o==="using"){if(!n){let u=V();return he(u,e)?p("/c using is only available on chats with a known sender (allowlisted WhatsApp/Telegram)."):null}let i=Ge(e,n);if(i){if(i.nodeId!==s)return null;let d=[...V().peers,jt(e)].find(y=>y.nodeId===i.nodeId)?.label??"(unknown label)",m=[`*Bound to ${d}*`,`id \`${i.nodeId}\` \xB7 source ${i.source}`,i.source==="chat"?`since ${i.sinceIso}`:"(from config)"].join(`
70
+ `),f=[`<b>Bound to ${X(d)}</b>`,`id <code>${X(i.nodeId)}</code> \xB7 source ${X(i.source)}`,i.source==="chat"?`since ${X(i.sinceIso)}`:"(from config)"].join(`
71
+ `);return q(m,f)}let l=V();return he(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=V();return he(m,e)?p("/c unuse is only available on chats with a known sender (allowlisted WhatsApp/Telegram)."):null}let l=V().senderBindings[n]??null,{state:u}=Wl(n);if(l){if(l.nodeId!==s)return null}else if(!he(u,e))return null;let c=Ge(e,n),d=c?`
72
72
  Config default still applies: \`${c.nodeId}\`.`:`
73
- 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=X();return he(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=Yr(e,n);return K(a.wa,a.tg)}if(o==="list"){let a=X(),l=n?Ge(e,n):null;if(l){if(l.nodeId!==s)return null}else if(!he(a,e))return null;return K(Fn(a,e,n??null),Ur(a,e,n??null))}let i=X();return he(i,e)?p(`Unknown /c subcommand "${o}". Try /c help`):null}function fi(e,t){return bn(!0),Jr(e,t,"chat")}function ce(e){return e.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;")}function qr(e){let t=e.trim();return t?t.length<=8?"(set)":`${t.slice(0,4)}\u2026${t.slice(-4)}`:"(empty)"}function gi(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 Ut(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 hi=new Set(["downloads","omnishData","sessionCwd","processCwd","fixed"]),xt=["gatewayMode","clusterEnabled","clusterRole","clusterLabel","clusterSenderBindings","commandPrefix","syncTimeoutMs","syncMaxBytes","jobLogTailLines","shell","appsCols","appsRows","appsFlushMs","appsMinIntervalMs","appsMaxFlushBytes","appsMaxSessions","appsMaxSessionsTotal","appsMaxWaChars","appsLogTailLines","appsSubmitDelayMs","appsClearInput","appsClearInputDelayMs","appsClearInputSequence","fileSendMaxBytes","fileReceiveMaxBytes","fileInboxSubdir","fileReceiveRootMode","fileReceiveRootPath","recipesAllowDangerousBuiltins","recipesMaxTaskChars","recipesMacroDefaultCommand","telegramBotToken","serviceInstallFromChat","updateCheckEnabled","updateCheckIntervalMs","updateCheckPackageName","updateInfoUrl"];function yi(e){return xt.includes(e)}function Kl(e){let t=ee(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: ${qr(t)}`,`telegramAllowFrom: ${e.telegramAllowFrom.length} entries`,"","*WhatsApp allowFrom*",`${e.allowFrom.length} entries`,"","*Apps*",`appsCols \xD7 appsRows: ${e.appsCols}\xD7${e.appsRows}`,`appsFlushMs / appsMinIntervalMs: ${e.appsFlushMs} / ${e.appsMinIntervalMs}`,`appsMaxFlushBytes: ${e.appsMaxFlushBytes}`,`appsMaxSessions / total: ${e.appsMaxSessions} / ${e.appsMaxSessionsTotal}`,`appsMaxWaChars: ${e.appsMaxWaChars}`,`appsLogTailLines: ${e.appsLogTailLines}`,`appsSubmitDelayMs: ${e.appsSubmitDelayMs}`,`appsClearInput: ${e.appsClearInput}`,`appsClearInputDelayMs: ${e.appsClearInputDelayMs}`,`appsClearInputSequence: ${e.appsClearInputSequence}`,"","*Files*",`fileSendMaxBytes / fileReceiveMaxBytes: ${e.fileSendMaxBytes} / ${e.fileReceiveMaxBytes}`,`fileInboxSubdir: ${e.fileInboxSubdir}`,`fileReceiveRootMode: ${e.fileReceiveRootMode}`,`fileReceiveRootPath: ${e.fileReceiveRootPath||"(empty)"}`,"","*Recipes*",`recipesAllowDangerousBuiltins: ${e.recipesAllowDangerousBuiltins}`,`recipesMaxTaskChars: ${e.recipesMaxTaskChars}`,`recipesMacroDefaultCommand: ${e.recipesMacroDefaultCommand}`,"","*Service (chat)*",`serviceInstallFromChat: ${e.serviceInstallFromChat}`,"","*Updates (optional)*",`updateCheckEnabled: ${e.updateCheckEnabled}`,`updateCheckIntervalMs: ${e.updateCheckIntervalMs}`,`updateCheckPackageName: ${e.updateCheckPackageName}`,`updateInfoUrl: ${e.updateInfoUrl?"(set)":"(empty)"}`,"",`File: ${T}`].join(`
74
- `)}function Yl(e){let t=ee(e);return["<b>Config</b> (secrets masked)","",`<b>gatewayMode</b> ${ce(e.gatewayMode)}`,`<b>commandPrefix</b> ${ce(e.commandPrefix)}`,`<b>shell</b> ${ce(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: ${ce(e.clusterRole)}`,`clusterLabel: ${ce(e.clusterLabel||"(hostname)")}`,`clusterSenderBindings: ${Object.keys(e.clusterSenderBindings??{}).length} entries`,"","<b>Telegram</b>",`telegramBotToken: ${ce(qr(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>",`${ce(e.fileReceiveRootMode)} \xB7 inbox ${ce(e.fileInboxSubdir)}`,"","<b>Service (chat)</b>",`serviceInstallFromChat: ${e.serviceInstallFromChat}`,"","<b>Updates (optional)</b>",`updateCheckEnabled: ${e.updateCheckEnabled} \xB7 interval ms: ${e.updateCheckIntervalMs}`,`package: <code>${ce(e.updateCheckPackageName)}</code> \xB7 info URL: ${e.updateInfoUrl?"set":"empty"}`,"",`<code>${ce(T)}</code>`].join(`
75
- `)}function wi(e,t){if(!yi(t))return null;let n=t;if(n==="telegramBotToken")return`telegramBotToken: ${qr(ee(e))}`;let r=e[n];return`${t}: ${typeof r=="object"?JSON.stringify(r):String(r)}`}function ql(e,t){let n=wi(e,t);if(!n)return null;let r=n.split(": ");return r.length<2?ce(n):`<b>${ce(r[0])}</b> ${ce(r.slice(1).join(": "))}`}function _n(e,t){let n=gi(t),r=!1,o=!1,s=!1;if(e==="telegramBotToken"){if(!ze(n))throw new Error("Invalid bot token format (expect digits:secret from BotFather).");return gt(n),r=!0,s=!0,{reloadSuggested:r,shellWarning:o,tokenSaved:s}}let i=v();switch(e){case"gatewayMode":{let a=yn(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=Ut(n);if(a===null)throw new Error("clusterEnabled: true or false");i.clusterEnabled=a;break}case"clusterRole":{let a=n.trim().toLowerCase();if(a!=="primary"&&a!=="secondary")throw new Error("clusterRole: primary | secondary");i.clusterRole=a;break}case"clusterLabel":i.clusterLabel=n.trim().slice(0,128);break;case"clusterSenderBindings":{let a=n.trim();if(a===""||a==="{}"||a.toLowerCase()==="clear"){i.clusterSenderBindings={};break}let l;try{l=JSON.parse(a)}catch{throw new Error('clusterSenderBindings: pass a JSON object, e.g. {"wa:+15551234567":"workshop-box"}')}if(!l||typeof l!="object"||Array.isArray(l))throw new Error("clusterSenderBindings must be a JSON object of sender \u2192 label/id");let u={};for(let[c,d]of Object.entries(l)){if(typeof d!="string"||!d.trim())throw new Error(`clusterSenderBindings.${c}: value must be a non-empty string`);u[c]=d.trim()}i.clusterSenderBindings=u;break}case"commandPrefix":if(!n.trim())throw new Error("commandPrefix cannot be empty");i.commandPrefix=n.trim().slice(0,32);break;case"syncTimeoutMs":{let a=Number.parseInt(n,10);if(!Number.isFinite(a)||a<=0)throw new Error("syncTimeoutMs: positive integer (ms)");i.syncTimeoutMs=a;break}case"syncMaxBytes":{let a=Number.parseInt(n,10);if(!Number.isFinite(a)||a<=0)throw new Error("syncMaxBytes: positive integer");i.syncMaxBytes=a;break}case"jobLogTailLines":{let a=Number.parseInt(n,10);if(!Number.isFinite(a)||a<=0)throw new Error("jobLogTailLines: positive integer");i.jobLogTailLines=a;break}case"shell":if(!n.trim())throw new Error("shell: non-empty path");i.shell=n.trim().slice(0,4096),o=!0;break;case"recipesMacroDefaultCommand":if(!n.trim())throw new Error('recipesMacroDefaultCommand: non-empty shell snippet with "$OMNISH_TASK"');i.recipesMacroDefaultCommand=gi(n).trim().slice(0,4096);break;case"appsCols":case"appsRows":case"appsFlushMs":case"appsMinIntervalMs":case"appsMaxFlushBytes":case"appsMaxSessions":case"appsMaxSessionsTotal":case"appsMaxWaChars":case"appsLogTailLines":case"appsSubmitDelayMs":case"appsClearInputDelayMs":case"recipesMaxTaskChars":case"fileSendMaxBytes":case"fileReceiveMaxBytes":{let a=Number.parseInt(n,10);if(!Number.isFinite(a)||a<0)throw new Error(`${e}: non-negative integer`);i[e]=a;break}case"appsClearInput":{let a=Ut(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(!hi.has(a))throw new Error(`fileReceiveRootMode: ${[...hi].join(" | ")}`);i.fileReceiveRootMode=a;break}case"fileReceiveRootPath":i.fileReceiveRootPath=n.trim().slice(0,4096);break;case"recipesAllowDangerousBuiltins":{let a=Ut(n);if(a===null)throw new Error("recipesAllowDangerousBuiltins: true or false");i.recipesAllowDangerousBuiltins=a;break}case"serviceInstallFromChat":{let a=Ut(n);if(a===null)throw new Error("serviceInstallFromChat: true or false");i.serviceInstallFromChat=a;break}case"updateCheckEnabled":{let a=Ut(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 _e(i),{reloadSuggested:r,shellWarning:o,tokenSaved:s}}async function bi(e,t){let n=e.trim(),r=n.toLowerCase();if(!n||r==="help")return _(cs());if(r==="keys"||r==="help keys")return p(["*Configurable keys* (/config set)","",xt.join(", "),"","Examples:","/config set clusterLabel work-laptop",'/config set fileReceiveRootPath "/path/with spaces"',"/config set gatewayMode both"].join(`
76
- `));if(r==="show"){let i=v();return K(Kl(i),Yl(i))}let o=n.match(/^get\s+(\S+)\s*$/i);if(o){let i=o[1],a=v(),l=wi(a,i);if(!l)return p(`Unknown key "${i}". /config keys`);let u=ql(a,i);return K(`${l}
73
+ 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 i=V();return he(i,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 i=Yr(e,n);return q(i.wa,i.tg)}if(o==="list"){let i=V(),l=n?Ge(e,n):null;if(l){if(l.nodeId!==s)return null}else if(!he(i,e))return null;return q(_n(i,e,n??null),Wr(i,e,n??null))}let a=V();return he(a,e)?p(`Unknown /c subcommand "${o}". Try /c help`):null}function fi(e,t){return bn(!0),zr(e,t,"chat")}function ce(e){return e.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;")}function Kr(e){let t=e.trim();return t?t.length<=8?"(set)":`${t.slice(0,4)}\u2026${t.slice(-4)}`:"(empty)"}function gi(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 Ut(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 hi=new Set(["downloads","omnishData","sessionCwd","processCwd","fixed"]),vt=["gatewayMode","clusterEnabled","clusterRole","clusterLabel","clusterSenderBindings","commandPrefix","syncTimeoutMs","syncMaxBytes","jobLogTailLines","shell","appsCols","appsRows","appsFlushMs","appsMinIntervalMs","appsMaxFlushBytes","appsMaxSessions","appsMaxSessionsTotal","appsMaxWaChars","appsLogTailLines","appsSubmitDelayMs","appsClearInput","appsClearInputDelayMs","appsClearInputSequence","fileSendMaxBytes","fileReceiveMaxBytes","fileInboxSubdir","fileReceiveRootMode","fileReceiveRootPath","recipesAllowDangerousBuiltins","recipesMaxTaskChars","recipesMacroDefaultCommand","telegramBotToken","serviceInstallFromChat","updateCheckEnabled","updateCheckIntervalMs","updateCheckPackageName","updateInfoUrl"];function yi(e){return vt.includes(e)}function ql(e){let t=ee(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: ${Kr(t)}`,`telegramAllowFrom: ${e.telegramAllowFrom.length} entries`,"","*WhatsApp allowFrom*",`${e.allowFrom.length} entries`,"","*Apps*",`appsCols \xD7 appsRows: ${e.appsCols}\xD7${e.appsRows}`,`appsFlushMs / appsMinIntervalMs: ${e.appsFlushMs} / ${e.appsMinIntervalMs}`,`appsMaxFlushBytes: ${e.appsMaxFlushBytes}`,`appsMaxSessions / total: ${e.appsMaxSessions} / ${e.appsMaxSessionsTotal}`,`appsMaxWaChars: ${e.appsMaxWaChars}`,`appsLogTailLines: ${e.appsLogTailLines}`,`appsSubmitDelayMs: ${e.appsSubmitDelayMs}`,`appsClearInput: ${e.appsClearInput}`,`appsClearInputDelayMs: ${e.appsClearInputDelayMs}`,`appsClearInputSequence: ${e.appsClearInputSequence}`,"","*Files*",`fileSendMaxBytes / fileReceiveMaxBytes: ${e.fileSendMaxBytes} / ${e.fileReceiveMaxBytes}`,`fileInboxSubdir: ${e.fileInboxSubdir}`,`fileReceiveRootMode: ${e.fileReceiveRootMode}`,`fileReceiveRootPath: ${e.fileReceiveRootPath||"(empty)"}`,"","*Recipes*",`recipesAllowDangerousBuiltins: ${e.recipesAllowDangerousBuiltins}`,`recipesMaxTaskChars: ${e.recipesMaxTaskChars}`,`recipesMacroDefaultCommand: ${e.recipesMacroDefaultCommand}`,"","*Service (chat)*",`serviceInstallFromChat: ${e.serviceInstallFromChat}`,"","*Updates (optional)*",`updateCheckEnabled: ${e.updateCheckEnabled}`,`updateCheckIntervalMs: ${e.updateCheckIntervalMs}`,`updateCheckPackageName: ${e.updateCheckPackageName}`,`updateInfoUrl: ${e.updateInfoUrl?"(set)":"(empty)"}`,"",`File: ${T}`].join(`
74
+ `)}function Ql(e){let t=ee(e);return["<b>Config</b> (secrets masked)","",`<b>gatewayMode</b> ${ce(e.gatewayMode)}`,`<b>commandPrefix</b> ${ce(e.commandPrefix)}`,`<b>shell</b> ${ce(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: ${ce(e.clusterRole)}`,`clusterLabel: ${ce(e.clusterLabel||"(hostname)")}`,`clusterSenderBindings: ${Object.keys(e.clusterSenderBindings??{}).length} entries`,"","<b>Telegram</b>",`telegramBotToken: ${ce(Kr(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>",`${ce(e.fileReceiveRootMode)} \xB7 inbox ${ce(e.fileInboxSubdir)}`,"","<b>Service (chat)</b>",`serviceInstallFromChat: ${e.serviceInstallFromChat}`,"","<b>Updates (optional)</b>",`updateCheckEnabled: ${e.updateCheckEnabled} \xB7 interval ms: ${e.updateCheckIntervalMs}`,`package: <code>${ce(e.updateCheckPackageName)}</code> \xB7 info URL: ${e.updateInfoUrl?"set":"empty"}`,"",`<code>${ce(T)}</code>`].join(`
75
+ `)}function wi(e,t){if(!yi(t))return null;let n=t;if(n==="telegramBotToken")return`telegramBotToken: ${Kr(ee(e))}`;let r=e[n];return`${t}: ${typeof r=="object"?JSON.stringify(r):String(r)}`}function Yl(e,t){let n=wi(e,t);if(!n)return null;let r=n.split(": ");return r.length<2?ce(n):`<b>${ce(r[0])}</b> ${ce(r.slice(1).join(": "))}`}function Bn(e,t){let n=gi(t),r=!1,o=!1,s=!1;if(e==="telegramBotToken"){if(!ze(n))throw new Error("Invalid bot token format (expect digits:secret from BotFather).");return gt(n),r=!0,s=!0,{reloadSuggested:r,shellWarning:o,tokenSaved:s}}let a=x();switch(e){case"gatewayMode":{let i=yn(n);if(!i)throw new Error("gatewayMode: use whatsapp | telegram | both (or wa, tg, b)");a.gatewayMode=i,r=!0;break}case"clusterEnabled":{let i=Ut(n);if(i===null)throw new Error("clusterEnabled: true or false");a.clusterEnabled=i;break}case"clusterRole":{let i=n.trim().toLowerCase();if(i!=="primary"&&i!=="secondary")throw new Error("clusterRole: primary | secondary");a.clusterRole=i;break}case"clusterLabel":a.clusterLabel=n.trim().slice(0,128);break;case"clusterSenderBindings":{let i=n.trim();if(i===""||i==="{}"||i.toLowerCase()==="clear"){a.clusterSenderBindings={};break}let l;try{l=JSON.parse(i)}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()}a.clusterSenderBindings=u;break}case"commandPrefix":if(!n.trim())throw new Error("commandPrefix cannot be empty");a.commandPrefix=n.trim().slice(0,32);break;case"syncTimeoutMs":{let i=Number.parseInt(n,10);if(!Number.isFinite(i)||i<=0)throw new Error("syncTimeoutMs: positive integer (ms)");a.syncTimeoutMs=i;break}case"syncMaxBytes":{let i=Number.parseInt(n,10);if(!Number.isFinite(i)||i<=0)throw new Error("syncMaxBytes: positive integer");a.syncMaxBytes=i;break}case"jobLogTailLines":{let i=Number.parseInt(n,10);if(!Number.isFinite(i)||i<=0)throw new Error("jobLogTailLines: positive integer");a.jobLogTailLines=i;break}case"shell":if(!n.trim())throw new Error("shell: non-empty path");a.shell=n.trim().slice(0,4096),o=!0;break;case"recipesMacroDefaultCommand":if(!n.trim())throw new Error('recipesMacroDefaultCommand: non-empty shell snippet with "$OMNISH_TASK"');a.recipesMacroDefaultCommand=gi(n).trim().slice(0,4096);break;case"appsCols":case"appsRows":case"appsFlushMs":case"appsMinIntervalMs":case"appsMaxFlushBytes":case"appsMaxSessions":case"appsMaxSessionsTotal":case"appsMaxWaChars":case"appsLogTailLines":case"appsSubmitDelayMs":case"appsClearInputDelayMs":case"recipesMaxTaskChars":case"fileSendMaxBytes":case"fileReceiveMaxBytes":{let i=Number.parseInt(n,10);if(!Number.isFinite(i)||i<0)throw new Error(`${e}: non-negative integer`);a[e]=i;break}case"appsClearInput":{let i=Ut(n);if(i===null)throw new Error("appsClearInput: true or false");a.appsClearInput=i;break}case"appsClearInputSequence":a.appsClearInputSequence=n.trim().slice(0,200);break;case"fileInboxSubdir":a.fileInboxSubdir=n.trim().slice(0,80);break;case"fileReceiveRootMode":{let i=n.trim();if(!hi.has(i))throw new Error(`fileReceiveRootMode: ${[...hi].join(" | ")}`);a.fileReceiveRootMode=i;break}case"fileReceiveRootPath":a.fileReceiveRootPath=n.trim().slice(0,4096);break;case"recipesAllowDangerousBuiltins":{let i=Ut(n);if(i===null)throw new Error("recipesAllowDangerousBuiltins: true or false");a.recipesAllowDangerousBuiltins=i;break}case"serviceInstallFromChat":{let i=Ut(n);if(i===null)throw new Error("serviceInstallFromChat: true or false");a.serviceInstallFromChat=i;break}case"updateCheckEnabled":{let i=Ut(n);if(i===null)throw new Error("updateCheckEnabled: true or false");a.updateCheckEnabled=i;break}case"updateCheckIntervalMs":{let i=Number.parseInt(n,10);if(!Number.isFinite(i)||i<36e5)throw new Error("updateCheckIntervalMs: integer ms, minimum 3600000 (1 hour)");a.updateCheckIntervalMs=Math.min(6048e5,i);break}case"updateCheckPackageName":if(!n.trim())throw new Error("updateCheckPackageName: non-empty npm package name");a.updateCheckPackageName=n.trim().slice(0,214);break;case"updateInfoUrl":a.updateInfoUrl=n.trim().slice(0,2048);break}return _e(a),{reloadSuggested:r,shellWarning:o,tokenSaved:s}}async function bi(e,t){let n=e.trim(),r=n.toLowerCase();if(!n||r==="help")return _(us());if(r==="keys"||r==="help keys")return p(["*Configurable keys* (/config set)","",vt.join(", "),"","Examples:","/config set clusterLabel work-laptop",'/config set fileReceiveRootPath "/path/with spaces"',"/config set gatewayMode both"].join(`
76
+ `));if(r==="show"){let a=x();return q(ql(a),Ql(a))}let o=n.match(/^get\s+(\S+)\s*$/i);if(o){let a=o[1],i=x(),l=wi(i,a);if(!l)return p(`Unknown key "${a}". /config keys`);let u=Yl(i,a);return q(`${l}
77
77
 
78
- ${T}`,`${u}<br/><br/><code>${ce(T)}</code>`)}let s=n.match(/^set\s+(\S+)\s+([\s\S]+)$/i);if(s){let i=s[1],a=s[2]??"";if(!yi(i))return p(`Unknown key "${i}". /config keys`);try{let{reloadSuggested:l,shellWarning:u,tokenSaved:c}=_n(i,a),d="";if(t?.reload&&l){let h=await t.reload();d=h.ok?`
78
+ ${T}`,`${u}<br/><br/><code>${ce(T)}</code>`)}let s=n.match(/^set\s+(\S+)\s+([\s\S]+)$/i);if(s){let a=s[1],i=s[2]??"";if(!yi(a))return p(`Unknown key "${a}". /config keys`);try{let{reloadSuggested:l,shellWarning:u,tokenSaved:c}=Bn(a,i),d="";if(t?.reload&&l){let h=await t.reload();d=h.ok?`
79
79
  Reload: ${h.summary}`:`
80
80
  Reload failed: ${h.error}`}else l?d=`
81
81
  Send /reload while omnish run is active (gatewayMode / Telegram).`:d=`
82
82
  Send /reload to pick up changes where applicable.`;let m=u?`
83
83
  \u26A0 shell changed \u2014 affects all commands run via omnish.`:"",f=c?`
84
- telegramBotToken saved (not echoed).`:"",w=`Set *${i}* (saved).${f}${m}${d}`,b=`<b>Set ${ce(i)}</b> (saved).${f?"<br/>token saved (not echoed).":""}${u?"<br/>\u26A0 shell path changed.":""}${ce(d)}`;return K(w,b)}catch(l){return p(`Error: ${String(l)}`)}}return p("Unknown /config command. Try /config help or /config show")}import xi from"node:fs";import Ct from"node:process";function Bn(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(`
84
+ telegramBotToken saved (not echoed).`:"",y=`Set *${a}* (saved).${f}${m}${d}`,b=`<b>Set ${ce(a)}</b> (saved).${f?"<br/>token saved (not echoed).":""}${u?"<br/>\u26A0 shell path changed.":""}${ce(d)}`;return q(y,b)}catch(l){return p(`Error: ${String(l)}`)}}return p("Unknown /config command. Try /config help or /config show")}import vi from"node:fs";import $t from"node:process";function Hn(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(`
85
85
  `),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(`
86
- `),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(`
87
- `);return process.platform==="linux"?o:process.platform==="darwin"?s:process.platform==="win32"?i:[o,"","---","",s].join(`
88
- `)}import{execFileSync as Ze}from"node:child_process";import vt from"node:fs";import Gt from"node:os";import Ae from"node:path";import pe from"node:process";function at(){let e=pe.execPath,t=pe.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=Ae.resolve(t),r=pe.env.OMNISH_HOME?.trim(),o=r?Ae.resolve(r):A;return{nodePath:e,scriptPath:n,omnishHome:o}}function ki(e){return/[ "'\\\s]/.test(e)?`"${e.replace(/\\/g,"\\\\").replace(/"/g,'\\"')}"`:e}function Vl(e){return`Environment="OMNISH_HOME=${e.replace(/\\/g,"\\\\").replace(/"/g,'\\"')}"`}function Xl(e){return`[Unit]
86
+ `),a=["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(`
87
+ `);return process.platform==="linux"?o:process.platform==="darwin"?s:process.platform==="win32"?a:[o,"","---","",s].join(`
88
+ `)}import{execFileSync as Ze}from"node:child_process";import xt from"node:fs";import Gt from"node:os";import Ae from"node:path";import pe from"node:process";function at(){let e=pe.execPath,t=pe.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=Ae.resolve(t),r=pe.env.OMNISH_HOME?.trim(),o=r?Ae.resolve(r):A;return{nodePath:e,scriptPath:n,omnishHome:o}}function ki(e){return/[ "'\\\s]/.test(e)?`"${e.replace(/\\/g,"\\\\").replace(/"/g,'\\"')}"`:e}function Kl(e){return`Environment="OMNISH_HOME=${e.replace(/\\/g,"\\\\").replace(/"/g,'\\"')}"`}function Vl(e){return`[Unit]
89
89
  Description=omnish gateway (WhatsApp/Telegram)
90
90
  After=network-online.target
91
91
  Wants=network-online.target
@@ -93,13 +93,13 @@ Wants=network-online.target
93
93
  [Service]
94
94
  Type=simple
95
95
  ExecStart=${`${ki(e.nodePath)} ${ki(e.scriptPath)} run`}
96
- ${Vl(e.omnishHome)}
96
+ ${Kl(e.omnishHome)}
97
97
  Restart=on-failure
98
98
  RestartSec=5
99
99
 
100
100
  [Install]
101
101
  WantedBy=default.target
102
- `}function Wt(e){return e.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;")}function Ql(e){let t=Gt.homedir(),n=Ae.join(e.omnishHome,"logs","launchd-stdout.log"),r=Ae.join(e.omnishHome,"logs","launchd-stderr.log");N(Ae.dirname(n));let o=Wt(e.nodePath),s=Wt(e.scriptPath),i=Wt(e.omnishHome),a=Wt(n),l=Wt(r);return`<?xml version="1.0" encoding="UTF-8"?>
102
+ `}function Wt(e){return e.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;")}function Xl(e){let t=Gt.homedir(),n=Ae.join(e.omnishHome,"logs","launchd-stdout.log"),r=Ae.join(e.omnishHome,"logs","launchd-stderr.log");N(Ae.dirname(n));let o=Wt(e.nodePath),s=Wt(e.scriptPath),a=Wt(e.omnishHome),i=Wt(n),l=Wt(r);return`<?xml version="1.0" encoding="UTF-8"?>
103
103
  <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
104
104
  <plist version="1.0">
105
105
  <dict>
@@ -114,32 +114,32 @@ WantedBy=default.target
114
114
  <key>EnvironmentVariables</key>
115
115
  <dict>
116
116
  <key>OMNISH_HOME</key>
117
- <string>${i}</string>
117
+ <string>${a}</string>
118
118
  </dict>
119
119
  <key>RunAtLoad</key>
120
120
  <true/>
121
121
  <key>KeepAlive</key>
122
122
  <true/>
123
123
  <key>StandardOutPath</key>
124
- <string>${a}</string>
124
+ <string>${i}</string>
125
125
  <key>StandardErrorPath</key>
126
126
  <string>${l}</string>
127
127
  </dict>
128
128
  </plist>
129
- `}function Dn(){let e=at();if(e.error)return{ok:!1,detail:e.error};if(pe.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(pe.platform==="darwin")try{let t=Ae.join(Gt.homedir(),"Library/LaunchAgents/dev.omnish.gateway.plist");N(Ae.dirname(t));let n=Ql(e);vt.writeFileSync(t,n,{mode:384});let r=typeof pe.getuid=="function"?pe.getuid():null;if(r===null||r<0)return{ok:!1,detail:"Could not read user id for launchctl."};let o=`gui/${r}`;try{Ze("launchctl",["bootout",o,t],{stdio:"pipe"})}catch{}return Ze("launchctl",["bootstrap",o,t],{stdio:"inherit"}),Ze("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(pe.platform==="linux")try{let t=Ae.join(Gt.homedir(),".config","systemd","user");N(t);let n=Ae.join(t,"omnish.service"),r=Xl(e);return vt.writeFileSync(n,r,{mode:420}),Ze("systemctl",["--user","daemon-reload"],{stdio:"inherit"}),Ze("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: ${pe.platform}`}}function Hn(){if(pe.platform==="win32")return{ok:!0,detail:"Windows: remove the omnish task from Task Scheduler manually on this PC. See /service instructions."};if(pe.platform==="darwin")try{let e=Ae.join(Gt.homedir(),"Library/LaunchAgents/dev.omnish.gateway.plist"),n=`gui/${typeof pe.getuid=="function"?pe.getuid():0}`;if(vt.existsSync(e)){try{Ze("launchctl",["bootout",n,e],{stdio:"pipe"})}catch{}vt.unlinkSync(e)}return{ok:!0,detail:"LaunchAgent dev.omnish.gateway removed (if it was present)."}}catch(e){return{ok:!1,detail:String(e)}}if(pe.platform==="linux")try{let e=Ae.join(Gt.homedir(),".config","systemd","user","omnish.service");try{Ze("systemctl",["--user","disable","--now","omnish.service"],{stdio:"pipe"})}catch{}vt.existsSync(e)&&vt.unlinkSync(e);try{Ze("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: ${pe.platform}`}}import Si from"node:fs";var jn=48e3;function Un(e,t){try{if(!Si.existsSync(e))return"(no log file yet \u2014 start with `omnish run -d` or the systemd/LaunchAgent service.)";let n=Si.readFileSync(e),o=(n.length>jn?n.subarray(n.length-jn):n).toString("utf8");return n.length>jn&&(o=`\u2026(truncated to last ${jn} bytes)
129
+ `}function Dn(){let e=at();if(e.error)return{ok:!1,detail:e.error};if(pe.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(pe.platform==="darwin")try{let t=Ae.join(Gt.homedir(),"Library/LaunchAgents/dev.omnish.gateway.plist");N(Ae.dirname(t));let n=Xl(e);xt.writeFileSync(t,n,{mode:384});let r=typeof pe.getuid=="function"?pe.getuid():null;if(r===null||r<0)return{ok:!1,detail:"Could not read user id for launchctl."};let o=`gui/${r}`;try{Ze("launchctl",["bootout",o,t],{stdio:"pipe"})}catch{}return Ze("launchctl",["bootstrap",o,t],{stdio:"inherit"}),Ze("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(pe.platform==="linux")try{let t=Ae.join(Gt.homedir(),".config","systemd","user");N(t);let n=Ae.join(t,"omnish.service"),r=Vl(e);return xt.writeFileSync(n,r,{mode:420}),Ze("systemctl",["--user","daemon-reload"],{stdio:"inherit"}),Ze("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: ${pe.platform}`}}function jn(){if(pe.platform==="win32")return{ok:!0,detail:"Windows: remove the omnish task from Task Scheduler manually on this PC. See /service instructions."};if(pe.platform==="darwin")try{let e=Ae.join(Gt.homedir(),"Library/LaunchAgents/dev.omnish.gateway.plist"),n=`gui/${typeof pe.getuid=="function"?pe.getuid():0}`;if(xt.existsSync(e)){try{Ze("launchctl",["bootout",n,e],{stdio:"pipe"})}catch{}xt.unlinkSync(e)}return{ok:!0,detail:"LaunchAgent dev.omnish.gateway removed (if it was present)."}}catch(e){return{ok:!1,detail:String(e)}}if(pe.platform==="linux")try{let e=Ae.join(Gt.homedir(),".config","systemd","user","omnish.service");try{Ze("systemctl",["--user","disable","--now","omnish.service"],{stdio:"pipe"})}catch{}xt.existsSync(e)&&xt.unlinkSync(e);try{Ze("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: ${pe.platform}`}}import Si from"node:fs";var Un=48e3;function Wn(e,t){try{if(!Si.existsSync(e))return"(no log file yet \u2014 start with `omnish run -d` or the systemd/LaunchAgent service.)";let n=Si.readFileSync(e),o=(n.length>Un?n.subarray(n.length-Un):n).toString("utf8");return n.length>Un&&(o=`\u2026(truncated to last ${Un} bytes)
130
130
  ${o}`),o.split(/\r?\n/).slice(-t).join(`
131
- `).trimEnd()||"(empty)"}catch(n){return`Could not read log: ${String(n)}`}}var Zl=120;function Je(e){return e.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;")}function vi(e){let t=e.trim();return t==="/service"||t.startsWith("/service ")?t.slice(8).trim():null}async function Ci(e,t){let n=t.trim().split(/\s+/),r=(n[0]??"").toLowerCase();if(!t.trim()||r==="help")return _(ls(e));if(r==="status"){let s=at(),i=(()=>{try{return xi.existsSync(Z)?`gateway.pid: ${xi.readFileSync(Z,"utf8").trim()}`:"gateway.pid: (missing)"}catch(m){return`gateway.pid: (read error: ${String(m)})`}})(),a=Ct.env.OMNISH_BACKGROUND_GATEWAY==="1"?"This process: background gateway (OMNISH_BACKGROUND_GATEWAY=1).":"This process: foreground gateway session.",l=typeof Ct.env.OMNISH_HOME=="string"&&Ct.env.OMNISH_HOME.trim()?`OMNISH_HOME env: ${Ct.env.OMNISH_HOME.trim()}`:"OMNISH_HOME env: (not set \u2014 using default data dir)",u=s.error?s.error:`Node: ${s.nodePath}
132
- Script: ${s.scriptPath}`,c=["*Service status*","",`platform: ${Ct.platform}`,a,l,`data dir: ${A}`,i,`default log: ${de}`,"",u,"",e.serviceInstallFromChat?"Install from chat: enabled (/service install).":"Install from chat: off \u2014 `/config set serviceInstallFromChat true` to allow /service install."].join(`
133
- `),d=["<b>Service status</b>","",`<code>${Je(Ct.platform)}</code>`,`<br/><code>${Je(a)}</code>`,`<br/><code>${Je(l)}</code>`,`<br/>data dir: <code>${Je(A)}</code>`,`<br/><code>${Je(i)}</code>`,`<br/>default log: <code>${Je(de)}</code>`,"",`<pre>${Je(u)}</pre>`,"",e.serviceInstallFromChat?"Install from chat: enabled.":"Install from chat: off \u2014 <code>/config set serviceInstallFromChat true</code>."].join(`
134
- `);return K(c,d)}if(r==="instructions"){let s=at();if(s.error)return p(s.error);let i=Bn(s);return p(`*Install hints*
131
+ `).trimEnd()||"(empty)"}catch(n){return`Could not read log: ${String(n)}`}}var Zl=120;function Je(e){return e.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;")}function xi(e){let t=e.trim();return t==="/service"||t.startsWith("/service ")?t.slice(8).trim():null}async function $i(e,t){let n=t.trim().split(/\s+/),r=(n[0]??"").toLowerCase();if(!t.trim()||r==="help")return _(cs(e));if(r==="status"){let s=at(),a=(()=>{try{return vi.existsSync(Z)?`gateway.pid: ${vi.readFileSync(Z,"utf8").trim()}`:"gateway.pid: (missing)"}catch(m){return`gateway.pid: (read error: ${String(m)})`}})(),i=$t.env.OMNISH_BACKGROUND_GATEWAY==="1"?"This process: background gateway (OMNISH_BACKGROUND_GATEWAY=1).":"This process: foreground gateway session.",l=typeof $t.env.OMNISH_HOME=="string"&&$t.env.OMNISH_HOME.trim()?`OMNISH_HOME env: ${$t.env.OMNISH_HOME.trim()}`:"OMNISH_HOME env: (not set \u2014 using default data dir)",u=s.error?s.error:`Node: ${s.nodePath}
132
+ Script: ${s.scriptPath}`,c=["*Service status*","",`platform: ${$t.platform}`,i,l,`data dir: ${A}`,a,`default log: ${de}`,"",u,"",e.serviceInstallFromChat?"Install from chat: enabled (/service install).":"Install from chat: off \u2014 `/config set serviceInstallFromChat true` to allow /service install."].join(`
133
+ `),d=["<b>Service status</b>","",`<code>${Je($t.platform)}</code>`,`<br/><code>${Je(i)}</code>`,`<br/><code>${Je(l)}</code>`,`<br/>data dir: <code>${Je(A)}</code>`,`<br/><code>${Je(a)}</code>`,`<br/>default log: <code>${Je(de)}</code>`,"",`<pre>${Je(u)}</pre>`,"",e.serviceInstallFromChat?"Install from chat: enabled.":"Install from chat: off \u2014 <code>/config set serviceInstallFromChat true</code>."].join(`
134
+ `);return q(c,d)}if(r==="instructions"){let s=at();if(s.error)return p(s.error);let a=Hn(s);return p(`*Install hints*
135
135
 
136
- ${i}`)}if(r==="logs"){let s=n.length>=2?Number.parseInt(n[1],10):80,i=Number.isFinite(s)&&s>0?Math.min(s,Zl):80,a=Un(de,i),l=[`*Gateway log* (last ${i} lines)
136
+ ${a}`)}if(r==="logs"){let s=n.length>=2?Number.parseInt(n[1],10):80,a=Number.isFinite(s)&&s>0?Math.min(s,Zl):80,i=Wn(de,a),l=[`*Gateway log* (last ${a} lines)
137
137
  ${de}
138
- `,"```",a,"```"].join(`
139
- `),u=`<b>Gateway log</b> (last ${i} lines)<br/><code>${Je(de)}</code><pre>${Je(a)}</pre>`;return K(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=Dn();return p(s.ok?`*Installed*
138
+ `,"```",i,"```"].join(`
139
+ `),u=`<b>Gateway log</b> (last ${a} lines)<br/><code>${Je(de)}</code><pre>${Je(i)}</pre>`;return q(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=Dn();return p(s.ok?`*Installed*
140
140
  ${s.detail}`:`*Install failed*
141
- ${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=Hn();return p(`*Uninstall*
142
- ${s.detail}`)}return p("Unknown /service command. Try /service help")}function $i(e){let t=e.trim().match(/^(\d+)\.(\d+)\.(\d+)/);return t?[Number(t[1]),Number(t[2]),Number(t[3])]:null}function Ri(e,t){let n=$i(e),r=$i(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 ec="https://registry.npmjs.org",Mi=null,Vr=0;function Jt(){return Mi}function tc(e){let t=e.trim();return t.length<1||t.length>214?!1:!/\s/.test(t)}function Ti(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 nc(e){let t=e.trim(),n=`${ec}/${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 rc(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=Ti(u)),{message:a,link:l}}catch(t){return{error:`updateInfoUrl: ${String(t)}`}}}function oc(e){return!Number.isFinite(e)||e<36e5?36e5:e>6048e5?6048e5:Math.floor(e)}async function zt(e,t){let n=tc(t.updateCheckPackageName)?t.updateCheckPackageName.trim():"omnish",r=await nc(n),o="version"in r?r.version:null,s="error"in r?r.error:null,i=!1;o&&!s&&(i=Ri(e,o)<0);let a=null,l=null,u=null,c=Ti(t.updateInfoUrl);if(c){let m=await rc(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 Mi=d,d}function Wn(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 Ii(e){let t=!1,n=async()=>{if(t)return;let s=e.getConfig();if(!s.updateCheckEnabled)return;let i=oc(s.updateCheckIntervalMs),a=Date.now();if(!(Vr!==0&&a-Vr<i)){Vr=a;try{let l=await zt(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 sc from"node:fs";import Ai from"node:path";import{fileURLToPath as ic}from"node:url";var Gn=null;function De(){if(Gn!==null)return Gn;let e=Ai.dirname(ic(import.meta.url)),t=Ai.join(e,"..","package.json"),n=sc.readFileSync(t,"utf8"),r=JSON.parse(n);return Gn=typeof r.version=="string"&&r.version.trim()?r.version.trim():"0.0.0",Gn}function Kt(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 ac(e,t,n){let r=e;for(let o=0;o<14;o++){let s=Kt(r,t,n),i=new Date(s).getDay();if(i>=1&&i<=5)return s;r=s}return Kt(r,t,n)}function lc(e,t,n,r){let o=e;for(let s=0;s<370;s++){let i=Kt(o,n,r);if(new Date(i).getDay()===t)return i;o=i}return Kt(o,n,r)}function cc(e,t){let n=new Date(e),r=n.getFullYear(),o=n.getMonth(),s=n.getDate(),i=n.getHours(),a=new Date(r,o,s,i,t,0,0).getTime();return a<=e&&(a=new Date(r,o,s,i+1,t,0,0).getTime()),a}function uc(e,t){return e.kind==="ondemand"?Number.POSITIVE_INFINITY:e.kind==="daily"?Kt(t,e.hour,e.minute):e.kind==="weekdays"?ac(t,e.hour,e.minute):e.kind==="hourly"?cc(t,e.minute):lc(t,e.weekday,e.hour,e.minute)}function Ei(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=uc(e,a);if(l>r)break;i.push(l),a=l}return i}var dc={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 Xr(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 pc(e){let t=e.trim(),n=/^:(\d{1,2})$/.exec(t),r=/^(\d{1,2})$/.exec(t),o=n?n[1]:r?r[1]:null;if(o===null)return null;let s=Number(o);return!Number.isInteger(s)||s<0||s>59?null:s}function Qr(e){if(e.length===0)return{ok:!1,error:"Missing schedule (try: ondemand, hourly [:MM], daily HH:MM, weekdays HH:MM, weekly <dow> HH:MM)."};let t=e[0].toLowerCase();if(t==="ondemand"||t==="manual")return e.length!==1?{ok:!1,error:"ondemand takes no extra tokens."}:{ok:!0,schedule:{kind:"ondemand"}};if(t==="daily"){if(e.length!==2)return{ok:!1,error:"Usage: daily HH:MM"};let n=Xr(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=Xr(e[1]);return n?{ok:!0,schedule:{kind:"weekdays",hour:n.hour,minute:n.minute}}:{ok:!1,error:"weekdays needs HH:MM (24h)."}}if(t==="hourly"){if(e.length===1)return{ok:!0,schedule:{kind:"hourly",minute:0}};if(e.length!==2)return{ok:!1,error:"Usage: hourly | hourly :MM | hourly MM (minute 0\u201359)."};let n=pc(e[1]);return n===null?{ok:!1,error:"hourly needs :MM or MM (minute 0\u201359), e.g. hourly :30 or hourly 15."}:{ok:!0,schedule:{kind:"hourly",minute:n}}}if(t==="weekly"){if(e.length!==3)return{ok:!1,error:"Usage: weekly <mon|tue|\u2026|sun> HH:MM"};let n=e[1].toLowerCase(),r=dc[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=Xr(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 Yt(e){let t=r=>r<10?`0${r}`:String(r),n=(r,o)=>`${t(r)}:${t(o)}`;switch(e.kind){case"ondemand":return"ondemand";case"daily":return`daily ${n(e.hour,e.minute)}`;case"weekdays":return`weekdays ${n(e.hour,e.minute)}`;case"hourly":return e.minute===0?"hourly":`hourly :${t(e.minute)}`;case"weekly":return`weekly ${["sun","mon","tue","wed","thu","fri","sat"][e.weekday]??e.weekday} ${n(e.hour,e.minute)}`}}import gc from"better-sqlite3";import mc from"pino";function Jn(){return process.env.OMNISH_VERBOSE==="1"||process.env.WHATSVERBOSE==="1"}var fc=Jn()?"info":"silent",I=mc({level:fc,base:{app:"omnish"}});function Oi(){return I.child({module:"baileys"})}var Li=1,et=null;function Pi(){N(Fe);let e=new gc(No);e.pragma("journal_mode = WAL"),e.exec(`
141
+ ${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=jn();return p(`*Uninstall*
142
+ ${s.detail}`)}return p("Unknown /service command. Try /service help")}function Ci(e){let t=e.trim().match(/^(\d+)\.(\d+)\.(\d+)/);return t?[Number(t[1]),Number(t[2]),Number(t[3])]:null}function Ri(e,t){let n=Ci(e),r=Ci(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 ec="https://registry.npmjs.org",Mi=null,Vr=0;function Jt(){return Mi}function tc(e){let t=e.trim();return t.length<1||t.length>214?!1:!/\s/.test(t)}function Ti(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 nc(e){let t=e.trim(),n=`${ec}/${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 rc(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 a=s,i=null;typeof a.message=="string"&&a.message.trim()&&(i=a.message.trim().slice(0,1500));let l=null,u=a.link??a.url;return typeof u=="string"&&u.trim()&&(l=Ti(u)),{message:i,link:l}}catch(t){return{error:`updateInfoUrl: ${String(t)}`}}}function oc(e){return!Number.isFinite(e)||e<36e5?36e5:e>6048e5?6048e5:Math.floor(e)}async function zt(e,t){let n=tc(t.updateCheckPackageName)?t.updateCheckPackageName.trim():"omnish",r=await nc(n),o="version"in r?r.version:null,s="error"in r?r.error:null,a=!1;o&&!s&&(a=Ri(e,o)<0);let i=null,l=null,u=null,c=Ti(t.updateInfoUrl);if(c){let m=await rc(c);"error"in m?u=m.error:(i=m.message,l=m.link)}let d={runningVersion:e,checkedAtIso:new Date().toISOString(),registryPackage:n,registryLatest:o,registryError:s,updateAvailable:a,infoMessage:i,infoLink:l,infoError:u};return Mi=d,d}function Gn(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 Ii(e){let t=!1,n=async()=>{if(t)return;let s=e.getConfig();if(!s.updateCheckEnabled)return;let a=oc(s.updateCheckIntervalMs),i=Date.now();if(!(Vr!==0&&i-Vr<a)){Vr=i;try{let l=await zt(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 sc from"node:fs";import Ai from"node:path";import{fileURLToPath as ic}from"node:url";var Jn=null;function He(){if(Jn!==null)return Jn;let e=Ai.dirname(ic(import.meta.url)),t=Ai.join(e,"..","package.json"),n=sc.readFileSync(t,"utf8"),r=JSON.parse(n);return Jn=typeof r.version=="string"&&r.version.trim()?r.version.trim():"0.0.0",Jn}function qt(e,t,n){let r=new Date(e),o=r.getFullYear(),s=r.getMonth(),a=r.getDate(),i=new Date(o,s,a,t,n,0,0).getTime();return i<=e&&(i=new Date(o,s,a+1,t,n,0,0).getTime()),i}function ac(e,t,n){let r=e;for(let o=0;o<14;o++){let s=qt(r,t,n),a=new Date(s).getDay();if(a>=1&&a<=5)return s;r=s}return qt(r,t,n)}function lc(e,t,n,r){let o=e;for(let s=0;s<370;s++){let a=qt(o,n,r);if(new Date(a).getDay()===t)return a;o=a}return qt(o,n,r)}function cc(e,t){let n=new Date(e),r=n.getFullYear(),o=n.getMonth(),s=n.getDate(),a=n.getHours(),i=new Date(r,o,s,a,t,0,0).getTime();return i<=e&&(i=new Date(r,o,s,a+1,t,0,0).getTime()),i}function uc(e,t){return e.kind==="ondemand"?Number.POSITIVE_INFINITY:e.kind==="daily"?qt(t,e.hour,e.minute):e.kind==="weekdays"?ac(t,e.hour,e.minute):e.kind==="hourly"?cc(t,e.minute):lc(t,e.weekday,e.hour,e.minute)}function Ei(e,t,n,r,o=32){if(e.kind==="ondemand")return[];let s=t??n-1,a=[],i=s;for(;a.length<o;){let l=uc(e,i);if(l>r)break;a.push(l),i=l}return a}var dc={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 Xr(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 pc(e){let t=e.trim(),n=/^:(\d{1,2})$/.exec(t),r=/^(\d{1,2})$/.exec(t),o=n?n[1]:r?r[1]:null;if(o===null)return null;let s=Number(o);return!Number.isInteger(s)||s<0||s>59?null:s}function Zr(e){if(e.length===0)return{ok:!1,error:"Missing schedule (try: ondemand, hourly [:MM], daily HH:MM, weekdays HH:MM, weekly <dow> HH:MM)."};let t=e[0].toLowerCase();if(t==="ondemand"||t==="manual")return e.length!==1?{ok:!1,error:"ondemand takes no extra tokens."}:{ok:!0,schedule:{kind:"ondemand"}};if(t==="daily"){if(e.length!==2)return{ok:!1,error:"Usage: daily HH:MM"};let n=Xr(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=Xr(e[1]);return n?{ok:!0,schedule:{kind:"weekdays",hour:n.hour,minute:n.minute}}:{ok:!1,error:"weekdays needs HH:MM (24h)."}}if(t==="hourly"){if(e.length===1)return{ok:!0,schedule:{kind:"hourly",minute:0}};if(e.length!==2)return{ok:!1,error:"Usage: hourly | hourly :MM | hourly MM (minute 0\u201359)."};let n=pc(e[1]);return n===null?{ok:!1,error:"hourly needs :MM or MM (minute 0\u201359), e.g. hourly :30 or hourly 15."}:{ok:!0,schedule:{kind:"hourly",minute:n}}}if(t==="weekly"){if(e.length!==3)return{ok:!1,error:"Usage: weekly <mon|tue|\u2026|sun> HH:MM"};let n=e[1].toLowerCase(),r=dc[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=Xr(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 Qt(e){let t=r=>r<10?`0${r}`:String(r),n=(r,o)=>`${t(r)}:${t(o)}`;switch(e.kind){case"ondemand":return"ondemand";case"daily":return`daily ${n(e.hour,e.minute)}`;case"weekdays":return`weekdays ${n(e.hour,e.minute)}`;case"hourly":return e.minute===0?"hourly":`hourly :${t(e.minute)}`;case"weekly":return`weekly ${["sun","mon","tue","wed","thu","fri","sat"][e.weekday]??e.weekday} ${n(e.hour,e.minute)}`}}import gc from"better-sqlite3";import mc from"pino";function zn(){return process.env.OMNISH_VERBOSE==="1"||process.env.WHATSVERBOSE==="1"}var fc=zn()?"info":"silent",I=mc({level:fc,base:{app:"omnish"}});function Oi(){return I.child({module:"baileys"})}var Li=1,et=null;function Pi(){N(Fe);let e=new gc(Fo);e.pragma("journal_mode = WAL"),e.exec(`
143
143
  CREATE TABLE IF NOT EXISTS cowork_meta (
144
144
  key TEXT PRIMARY KEY,
145
145
  value TEXT NOT NULL
@@ -153,108 +153,112 @@ ${s.detail}`)}return p("Unknown /service command. Try /service help")}function $
153
153
  PRIMARY KEY (task_id, slot_ms)
154
154
  );
155
155
  CREATE INDEX IF NOT EXISTS idx_cowork_completion_task ON cowork_slot_completion(task_id);
156
- `);let t=e.prepare("SELECT value FROM cowork_meta WHERE key = 'schema_version'").get();return(t?Number(t.value):0)<Li&&e.prepare("INSERT OR REPLACE INTO cowork_meta (key, value) VALUES ('schema_version', ?)").run(String(Li)),e}function qt(e){et||(et=Pi()),yc(e)}function Ni(){if(et){try{et.close()}catch{}et=null}}function Zr(){return et||(et=Pi()),et}function hc(e){let n=Zr().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 zn(e){let t=hc(e.id);return t??e.lastCompletedSlotMs}function Kn(e,t){if(t.length===0)return;let n=Zr(),r=n.prepare(`
156
+ `);let t=e.prepare("SELECT value FROM cowork_meta WHERE key = 'schema_version'").get();return(t?Number(t.value):0)<Li&&e.prepare("INSERT OR REPLACE INTO cowork_meta (key, value) VALUES ('schema_version', ?)").run(String(Li)),e}function Yt(e){et||(et=Pi()),yc(e)}function Ni(){if(et){try{et.close()}catch{}et=null}}function eo(){return et||(et=Pi()),et}function hc(e){let n=eo().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 qn(e){let t=hc(e.id);return t??e.lastCompletedSlotMs}function Qn(e,t){if(t.length===0)return;let n=eo(),r=n.prepare(`
157
157
  INSERT INTO cowork_slot_completion (task_id, slot_ms, kind, completed_at_ms, log_path)
158
158
  VALUES (?, ?, ?, ?, ?)
159
- `);n.transaction(()=>{for(let s of t)r.run(e,s.slotMs,s.kind,s.completedAtMs,s.logPath)})()}function yc(e){let n=Zr().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{Kn(o.id,[{slotMs:o.lastCompletedSlotMs,kind:"migrated",logPath:null,completedAtMs:r}]),I.info({taskId:o.id,slotMs:o.lastCompletedSlotMs},"cowork seeded completion from tasks.json")}catch(i){I.warn({err:String(i),taskId:o.id},"cowork seed from tasks.json failed")}}import Fi from"node:crypto";import Vt from"node:fs";import eo from"node:os";import $t from"node:path";var wc=/^[a-zA-Z0-9][a-zA-Z0-9_-]{0,31}$/;function _i(e){let t=e.trim().toLowerCase();return wc.test(t)?{ok:!0,name:t}:{ok:!1,error:"Name must be alphanumeric with _ or -, max 32 chars."}}function Rt(e,t){let n=e.trim();return n===""||n==="."?$t.resolve(t):n==="~"?eo.homedir():n.startsWith("~/")||n.startsWith("~\\")?$t.join(eo.homedir(),n.slice(2)):$t.isAbsolute(n)?n:$t.resolve(t,n)}function no(e){return $t.join(eo.homedir(),"Cowork",e)}function bc(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:no(r),l=typeof t.enabled=="boolean"?t.enabled:!0,u=typeof t.notify=="string"?t.notify.toLowerCase():"self",c=u==="wa"||u==="whatsapp"?"wa":u==="tg"||u==="telegram"?"tg":u==="all"?"all":u==="none"?"none":"self",d={kind:"ondemand"};if(t.schedule&&typeof t.schedule=="object"){let h=t.schedule,E=typeof h.kind=="string"?h.kind:"";if(E==="ondemand")d={kind:"ondemand"};else if(E==="daily"){let $=Number(h.hour),P=Number(h.minute);Number.isFinite($)&&Number.isFinite(P)&&(d={kind:"daily",hour:$,minute:P})}else if(E==="weekdays"){let $=Number(h.hour),P=Number(h.minute);Number.isFinite($)&&Number.isFinite(P)&&(d={kind:"weekdays",hour:$,minute:P})}else if(E==="hourly"){let $=Number(h.minute);Number.isFinite($)&&Number.isInteger($)&&$>=0&&$<=59&&(d={kind:"hourly",minute:$})}else if(E==="weekly"){let $=Number(h.hour),P=Number(h.minute),te=Number(h.weekday);Number.isFinite($)&&Number.isFinite(P)&&Number.isInteger(te)&&(d={kind:"weekly",weekday:te,hour:$,minute:P})}}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,b=Array.isArray(t.attachFiles)?t.attachFiles.filter(h=>typeof h=="string"&&h.trim().length>0):[];return{id:n,name:r,ownerPeerKey:o,command:s,cwd:i,outputDir:a,schedule:d,enabled:l,notify:c,attachLog:w,attachFiles:b,lastCompletedSlotMs:m,createdAtMs:f}}function be(){try{let e=Vt.readFileSync(ur,"utf8"),t=JSON.parse(e);if(!t||!Array.isArray(t.tasks))return[];let n=[];for(let r of t.tasks){let o=bc(r);o&&n.push(o)}return n}catch{return[]}}function ve(e){N(Fe);let t={tasks:e},n=$t.join(Fe,`.tasks.${process.pid}.${Fi.randomBytes(4).toString("hex")}.tmp`);Vt.writeFileSync(n,JSON.stringify(t,null,2)+`
160
- `,{mode:384}),Vt.renameSync(n,ur)}function Mt(e,t,n){let r=t.trim().toLowerCase();return e.find(o=>o.name===r&&o.ownerPeerKey===n)}function Bi(){return Fi.randomBytes(4).toString("hex")}function Di(e){N(Fe),Vt.writeFileSync(dr,JSON.stringify(e,null,2)+`
161
- `,{mode:384})}function Hi(e){let t=to();t.push(e),Di(t)}function to(){try{let e=Vt.readFileSync(dr,"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 ji(e){if(e<=0)return{batch:[],remainingAfter:to().length};let t=to();if(t.length===0)return{batch:[],remainingAfter:0};let n=t.slice(0,e),r=t.slice(e);return Di(r),{batch:n,remainingAfter:r.length}}function kc(){return p(["Cowork \u2014 scheduled shell tasks (gateway must be running).","","add <name> <schedule> -- <command\u2026>"," schedule: ondemand | hourly [:MM] | daily HH:MM | weekdays HH:MM | weekly <mon\u2026sun> HH:MM","set <name> cmd -- <command\u2026> | schedule <\u2026> | out <path> | cwd <path>"," notify self|wa|tg|all|none"," attach on|off \u2014 send run log as a file with notify messages"," files clear | <glob/path \u2026> \u2014 optional artifacts (basename * ?); quote paths with spaces","list | show <name> | run <name> | enable <name> | disable <name> | remove <name>","","Output logs: task outputDir (default ~/Cowork/<name>). Missed times catch up when the gateway is back."].join(`
162
- `))}function Ui(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 Sc(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 Gi(e,t){let n=e.trim();if(!n||/^help$/i.test(n))return kc();let r=n.split(/\s+/)[0].toLowerCase();if(/^list$/i.test(r)){let u=be().filter(d=>d.ownerPeerKey===t);if(u.length===0)return p("(no cowork tasks for this chat)");let c=u.map(d=>{let m=d.enabled?"":" (disabled)";return`${ge}${d.name} ${Yt(d.schedule)} notify=${d.notify}${m}`});return p(c.join(`
163
- `))}let o=n.match(/^show\s+(\S+)\s*$/i);if(o){let u=o[1],c=be();qt(c);let d=Mt(c,u,t);if(!d)return p(`Unknown task "${u}". /cowork list`);let m=zn(d),f=[`name: ${d.name}`,`id: ${d.id}`,`schedule: ${Yt(d.schedule)}`,`enabled: ${d.enabled}`,`notify: ${d.notify}`,`attachLog: ${d.attachLog}`,`files: ${d.attachFiles.length?d.attachFiles.join(", "):"(none)"}`,`cwd: ${d.cwd||"(session cwd)"}`,`out: ${d.outputDir}`,`cmd: ${d.command}`,`last slot: ${m?new Date(m).toLocaleString():"(never)"}`];return p(f.join(`
164
- `))}if(/^add$/i.test(r)){let u=n.slice(3).trim(),c=Sc(u);if("error"in c)return p(c.error);let d=_i(c.name);if(!d.ok)return p(d.error);let m=Qr(c.scheduleWords);if(!m.ok)return p(m.error);let f=be();if(Mt(f,d.name,t))return p(`Task "${d.name}" already exists. Remove it first or pick another name.`);let w=W(t),b=no(d.name),h={id:Bi(),name:d.name,ownerPeerKey:t,command:c.command,cwd:"",outputDir:b,schedule:m.schedule,enabled:!0,notify:"self",attachLog:!1,attachFiles:[],lastCompletedSlotMs:null,createdAtMs:Date.now()};return f.push(h),ve(f),p([`Saved cowork task "${d.name}" (${Yt(m.schedule)}).`,`Output: ${b}`,`Notify: self \u2014 change with /cowork set ${d.name} notify wa|tg|all|none`].join(`
165
- `))}let s=n.match(/^run\s+(\S+)\s*$/i);if(s){let u=s[1].toLowerCase(),c=Mt(be(),u,t);return c?c.enabled?(Hi({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=be(),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 Wi(a[1],t,!0);let l=n.match(/^disable\s+(\S+)\s*$/i);return l?Wi(l[1],t,!1):/^set$/i.test(r)?Cc(n.slice(3).trim(),t):p("Unknown /cowork command. Try /cowork help")}function Wi(e,t,n){let r=be(),o=Mt(r,e,t);return o?(o.enabled=n,ve(r),p(`Cowork "${o.name}" ${n?"enabled":"disabled"}.`)):p(`Unknown task "${e}".`)}function xc(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 vc(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 Cc(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=be(),i=Mt(s,r,t);if(!i)return p(`Unknown task "${r}".`);if(o.toLowerCase().startsWith("cmd ")){let a=o.slice(4).trim(),l=" -- ",u=a.indexOf(l),c;if(u!==-1)c=a.slice(u+l.length).trim();else if(a.startsWith("--"))c=a.slice(2).trim();else return p("Usage: /cowork set <name> cmd -- <command\u2026>");return c?(i.command=c,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=Qr(a);return l.ok?(i.schedule=l.schedule,ve(s),p(`Schedule for "${i.name}": ${Yt(l.schedule)}`)):p(l.error)}if(o.toLowerCase().startsWith("out ")){let a=o.slice(4).trim(),l=W(t);return i.outputDir=Rt(a,l.cwd),ve(s),p(`outputDir: ${i.outputDir}`)}if(o.toLowerCase().startsWith("cwd ")){let a=o.slice(4).trim(),l=W(t);return i.cwd=a?Rt(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=xc(a);return l?(i.notify=l,ve(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=Ui(a);if(l.length!==1)return p("Usage: /cowork set <name> attach on|off");let u=vc(l[0]);return u===null?p("attach must be on or off"):(i.attachLog=u,ve(s),p(`attachLog: ${u}`))}if(o.toLowerCase().startsWith("files ")){let a=o.slice(6).trim(),l=Ui(a);return l.length===1&&l[0].toLowerCase()==="clear"?(i.attachFiles=[],ve(s),p("files: (cleared)")):l.length===0?p("Usage: /cowork set <name> files clear | <pattern \u2026> \u2014 quote paths with spaces"):(i.attachFiles=l,ve(s),p(`files: ${i.attachFiles.join(", ")}`))}return p("Unknown set field. Try: cmd, schedule, out, cwd, notify, attach, files")}function ro(e){let t=e.trim();if(t==="/computers"||t.startsWith("/computers "))return t.slice(10).trim();if(t==="/pcs"||t.startsWith("/pcs "))return t.slice(4).trim();let n=t.match(/^\/c(?:$|\s+(.*))/);return n?(n[1]??"").trim():null}function C(e){return{kind:"text",body:e}}function Rc(e,t){return`${e}:${t}`}function Mc(e,t){return`${e}:apps:${t}`}async function Tc(e,t){let n=e.trim();if(n===""||/^status$/i.test(n)||/^show$/i.test(n)||/^get$/i.test(n)){let a=v();return us({gatewayMode:a.gatewayMode,authPresent:Te(),tokenSet:!!ee(a),allowN:a.allowFrom.length,tgAllowN:a.telegramAllowFrom.length,updateBrief:Wn(Jt())})}if(/^help$/i.test(n))return _(Sr());let r=yn(n);if(!r)return Rs();wn(r);let o=v(),s,i=!1;if(t?.reload){let a=await t.reload();s=a.ok?a.summary:`Reload failed: ${a.error}`}else i=!0;return hs(o.gatewayMode,s,i)}function Ic(e){let t=e.trim();if(!ze(t))return gs();let n=gt(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.'),fs(r)}async function Ji(e,t,n){let r=W(t),o=Xo(n);if(o!==null){let a=Qo(r.cwd,o),l=Zo(a);return l.ok?(Sn(t,a),p(`cwd: ${a}`)):p(`cd: ${l.error}`)}let s=await Nn(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(`
166
- `))}async function lt(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),C(vs())):(o.set(d,!1),C(p("Free shell mode off.")));if(c.startsWith(e.commandPrefix)){let w=c.slice(e.commandPrefix.length).trim();if(!w)return C(p(`Send ${e.commandPrefix}<command> or /help`));if(!u&&Ws(w)){let b=bt(d,w);if(b!==void 0)return await lt(e,t,n,r,o,{...s,text:b},i,a,l,!0)}return C(await Ji(e,d,w))}if(/^\/help\s+files$/i.test(c.trim()))return C(Cr());if(c==="/help"||c==="help")return C(_(wt(e)));if(c.startsWith("/")){if(/^\/files(?:\s+help)?$/i.test(c.trim()))return C(Cr());let w=c.match(/^\/receive\b(?:\s+(\S+))?$/i);if(w){let g=(w[1]??"status").toLowerCase(),S=new Set(["here","cwd","session","dir"]),x=new Set(["default","global","reset"]);if(S.has(g)){gr(d,"sessionCwd");let D=W(d).cwd;return C(p(`Inbound files will save under this chat\u2019s session folder:
167
- ${D}
159
+ `);n.transaction(()=>{for(let s of t)r.run(e,s.slotMs,s.kind,s.completedAtMs,s.logPath)})()}function yc(e){let n=eo().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{Qn(o.id,[{slotMs:o.lastCompletedSlotMs,kind:"migrated",logPath:null,completedAtMs:r}]),I.info({taskId:o.id,slotMs:o.lastCompletedSlotMs},"cowork seeded completion from tasks.json")}catch(a){I.warn({err:String(a),taskId:o.id},"cowork seed from tasks.json failed")}}import Fi from"node:crypto";import Kt from"node:fs";import to from"node:os";import Ct from"node:path";var wc=/^[a-zA-Z0-9][a-zA-Z0-9_-]{0,31}$/;function _i(e){let t=e.trim().toLowerCase();return wc.test(t)?{ok:!0,name:t}:{ok:!1,error:"Name must be alphanumeric with _ or -, max 32 chars."}}function Rt(e,t){let n=e.trim();return n===""||n==="."?Ct.resolve(t):n==="~"?to.homedir():n.startsWith("~/")||n.startsWith("~\\")?Ct.join(to.homedir(),n.slice(2)):Ct.isAbsolute(n)?n:Ct.resolve(t,n)}function ro(e){return Ct.join(to.homedir(),"Cowork",e)}function bc(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 a=typeof t.cwd=="string"?t.cwd:"",i=typeof t.outputDir=="string"&&t.outputDir.trim()?t.outputDir:ro(r),l=typeof t.enabled=="boolean"?t.enabled:!0,u=typeof t.notify=="string"?t.notify.toLowerCase():"self",c=u==="wa"||u==="whatsapp"?"wa":u==="tg"||u==="telegram"?"tg":u==="all"?"all":u==="none"?"none":"self",d={kind:"ondemand"};if(t.schedule&&typeof t.schedule=="object"){let h=t.schedule,E=typeof h.kind=="string"?h.kind:"";if(E==="ondemand")d={kind:"ondemand"};else if(E==="daily"){let $=Number(h.hour),P=Number(h.minute);Number.isFinite($)&&Number.isFinite(P)&&(d={kind:"daily",hour:$,minute:P})}else if(E==="weekdays"){let $=Number(h.hour),P=Number(h.minute);Number.isFinite($)&&Number.isFinite(P)&&(d={kind:"weekdays",hour:$,minute:P})}else if(E==="hourly"){let $=Number(h.minute);Number.isFinite($)&&Number.isInteger($)&&$>=0&&$<=59&&(d={kind:"hourly",minute:$})}else if(E==="weekly"){let $=Number(h.hour),P=Number(h.minute),te=Number(h.weekday);Number.isFinite($)&&Number.isFinite(P)&&Number.isInteger(te)&&(d={kind:"weekly",weekday:te,hour:$,minute:P})}}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(),y=typeof t.attachLog=="boolean"?t.attachLog:!1,b=Array.isArray(t.attachFiles)?t.attachFiles.filter(h=>typeof h=="string"&&h.trim().length>0):[];return{id:n,name:r,ownerPeerKey:o,command:s,cwd:a,outputDir:i,schedule:d,enabled:l,notify:c,attachLog:y,attachFiles:b,lastCompletedSlotMs:m,createdAtMs:f}}function be(){try{let e=Kt.readFileSync(dr,"utf8"),t=JSON.parse(e);if(!t||!Array.isArray(t.tasks))return[];let n=[];for(let r of t.tasks){let o=bc(r);o&&n.push(o)}return n}catch{return[]}}function xe(e){N(Fe);let t={tasks:e},n=Ct.join(Fe,`.tasks.${process.pid}.${Fi.randomBytes(4).toString("hex")}.tmp`);Kt.writeFileSync(n,JSON.stringify(t,null,2)+`
160
+ `,{mode:384}),Kt.renameSync(n,dr)}function Mt(e,t,n){let r=t.trim().toLowerCase();return e.find(o=>o.name===r&&o.ownerPeerKey===n)}function Bi(){return Fi.randomBytes(4).toString("hex")}function Hi(e){N(Fe),Kt.writeFileSync(pr,JSON.stringify(e,null,2)+`
161
+ `,{mode:384})}function Di(e){let t=no();t.push(e),Hi(t)}function no(){try{let e=Kt.readFileSync(pr,"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 ji(e){if(e<=0)return{batch:[],remainingAfter:no().length};let t=no();if(t.length===0)return{batch:[],remainingAfter:0};let n=t.slice(0,e),r=t.slice(e);return Hi(r),{batch:n,remainingAfter:r.length}}function kc(){return p(["Cowork \u2014 scheduled shell tasks (gateway must be running).","","add <name> <schedule> -- <command\u2026>"," schedule: ondemand | hourly [:MM] | daily HH:MM | weekdays HH:MM | weekly <mon\u2026sun> HH:MM","set <name> cmd -- <command\u2026> | schedule <\u2026> | out <path> | cwd <path>"," notify self|wa|tg|all|none"," attach on|off \u2014 send run log as a file with notify messages"," files clear | <glob/path \u2026> \u2014 optional artifacts (basename * ?); quote paths with spaces","list | show <name> | run <name> | enable <name> | disable <name> | remove <name>","","Output logs: task outputDir (default ~/Cowork/<name>). Missed times catch up when the gateway is back."].join(`
162
+ `))}function Ui(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 Sc(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 a=s[0],i=s.slice(1);return{name:a,scheduleWords:i,command:o}}function Gi(e,t){let n=e.trim();if(!n||/^help$/i.test(n))return kc();let r=n.split(/\s+/)[0].toLowerCase();if(/^list$/i.test(r)){let u=be().filter(d=>d.ownerPeerKey===t);if(u.length===0)return p("(no cowork tasks for this chat)");let c=u.map(d=>{let m=d.enabled?"":" (disabled)";return`${ge}${d.name} ${Qt(d.schedule)} notify=${d.notify}${m}`});return p(c.join(`
163
+ `))}let o=n.match(/^show\s+(\S+)\s*$/i);if(o){let u=o[1],c=be();Yt(c);let d=Mt(c,u,t);if(!d)return p(`Unknown task "${u}". /cowork list`);let m=qn(d),f=[`name: ${d.name}`,`id: ${d.id}`,`schedule: ${Qt(d.schedule)}`,`enabled: ${d.enabled}`,`notify: ${d.notify}`,`attachLog: ${d.attachLog}`,`files: ${d.attachFiles.length?d.attachFiles.join(", "):"(none)"}`,`cwd: ${d.cwd||"(session cwd)"}`,`out: ${d.outputDir}`,`cmd: ${d.command}`,`last slot: ${m?new Date(m).toLocaleString():"(never)"}`];return p(f.join(`
164
+ `))}if(/^add$/i.test(r)){let u=n.slice(3).trim(),c=Sc(u);if("error"in c)return p(c.error);let d=_i(c.name);if(!d.ok)return p(d.error);let m=Zr(c.scheduleWords);if(!m.ok)return p(m.error);let f=be();if(Mt(f,d.name,t))return p(`Task "${d.name}" already exists. Remove it first or pick another name.`);let y=W(t),b=ro(d.name),h={id:Bi(),name:d.name,ownerPeerKey:t,command:c.command,cwd:"",outputDir:b,schedule:m.schedule,enabled:!0,notify:"self",attachLog:!1,attachFiles:[],lastCompletedSlotMs:null,createdAtMs:Date.now()};return f.push(h),xe(f),p([`Saved cowork task "${d.name}" (${Qt(m.schedule)}).`,`Output: ${b}`,`Notify: self \u2014 change with /cowork set ${d.name} notify wa|tg|all|none`].join(`
165
+ `))}let s=n.match(/^run\s+(\S+)\s*$/i);if(s){let u=s[1].toLowerCase(),c=Mt(be(),u,t);return c?c.enabled?(Di({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 a=n.match(/^(?:remove|rm|del)\s+(\S+)\s*$/i);if(a){let u=a[1],c=be(),d=c.findIndex(m=>m.name===u.toLowerCase()&&m.ownerPeerKey===t);return d===-1?p(`Unknown task "${u}".`):(c.splice(d,1),xe(c),p(`Removed cowork task "${u.toLowerCase()}".`))}let i=n.match(/^enable\s+(\S+)\s*$/i);if(i)return Wi(i[1],t,!0);let l=n.match(/^disable\s+(\S+)\s*$/i);return l?Wi(l[1],t,!1):/^set$/i.test(r)?$c(n.slice(3).trim(),t):p("Unknown /cowork command. Try /cowork help")}function Wi(e,t,n){let r=be(),o=Mt(r,e,t);return o?(o.enabled=n,xe(r),p(`Cowork "${o.name}" ${n?"enabled":"disabled"}.`)):p(`Unknown task "${e}".`)}function vc(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 xc(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 $c(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=be(),a=Mt(s,r,t);if(!a)return p(`Unknown task "${r}".`);if(o.toLowerCase().startsWith("cmd ")){let i=o.slice(4).trim(),l=" -- ",u=i.indexOf(l),c;if(u!==-1)c=i.slice(u+l.length).trim();else if(i.startsWith("--"))c=i.slice(2).trim();else return p("Usage: /cowork set <name> cmd -- <command\u2026>");return c?(a.command=c,xe(s),p(`Updated command for "${a.name}".`)):p("Command is empty.")}if(o.toLowerCase().startsWith("schedule ")){let i=o.slice(9).trim().split(/\s+/).filter(Boolean),l=Zr(i);return l.ok?(a.schedule=l.schedule,xe(s),p(`Schedule for "${a.name}": ${Qt(l.schedule)}`)):p(l.error)}if(o.toLowerCase().startsWith("out ")){let i=o.slice(4).trim(),l=W(t);return a.outputDir=Rt(i,l.cwd),xe(s),p(`outputDir: ${a.outputDir}`)}if(o.toLowerCase().startsWith("cwd ")){let i=o.slice(4).trim(),l=W(t);return a.cwd=i?Rt(i,l.cwd):"",xe(s),p(`cwd: ${a.cwd||"(session cwd at run time)"}`)}if(o.toLowerCase().startsWith("notify ")){let i=o.slice(7).trim(),l=vc(i);return l?(a.notify=l,xe(s),p(`notify: ${l}`)):p("notify must be self, wa, tg, all, or none.")}if(o.toLowerCase().startsWith("attach ")){let i=o.slice(7).trim(),l=Ui(i);if(l.length!==1)return p("Usage: /cowork set <name> attach on|off");let u=xc(l[0]);return u===null?p("attach must be on or off"):(a.attachLog=u,xe(s),p(`attachLog: ${u}`))}if(o.toLowerCase().startsWith("files ")){let i=o.slice(6).trim(),l=Ui(i);return l.length===1&&l[0].toLowerCase()==="clear"?(a.attachFiles=[],xe(s),p("files: (cleared)")):l.length===0?p("Usage: /cowork set <name> files clear | <pattern \u2026> \u2014 quote paths with spaces"):(a.attachFiles=l,xe(s),p(`files: ${a.attachFiles.join(", ")}`))}return p("Unknown set field. Try: cmd, schedule, out, cwd, notify, attach, files")}function oo(e){let t=e.trim();if(t==="/computers"||t.startsWith("/computers "))return t.slice(10).trim();if(t==="/pcs"||t.startsWith("/pcs "))return t.slice(4).trim();let n=t.match(/^\/c(?:$|\s+(.*))/);return n?(n[1]??"").trim():null}function C(e){return{kind:"text",body:e}}function Rc(e,t){return`${e}:${t}`}function Mc(e,t){return`${e}:apps:${t}`}async function Tc(e,t){let n=e.trim();if(n===""||/^status$/i.test(n)||/^show$/i.test(n)||/^get$/i.test(n)){let i=x();return ds({gatewayMode:i.gatewayMode,authPresent:Te(),tokenSet:!!ee(i),allowN:i.allowFrom.length,tgAllowN:i.telegramAllowFrom.length,updateBrief:Gn(Jt())})}if(/^help$/i.test(n))return _(vr());let r=yn(n);if(!r)return Ms();wn(r);let o=x(),s,a=!1;if(t?.reload){let i=await t.reload();s=i.ok?i.summary:`Reload failed: ${i.error}`}else a=!0;return ys(o.gatewayMode,s,a)}function Ic(e){let t=e.trim();if(!ze(t))return hs();let n=gt(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.'),gs(r)}async function Ji(e,t,n){let r=W(t),o=Xo(n);if(o!==null){let i=Zo(r.cwd,o),l=es(i);return l.ok?(Sn(t,i),p(`cwd: ${i}`)):p(`cd: ${l.error}`)}let s=await Fn(e.shell,n,{timeoutMs:e.syncTimeoutMs,maxBytes:e.syncMaxBytes,cwd:r.cwd}),a=[];return a.push(`$ ${n}`),s.stdout.trim()&&a.push(s.stdout.trimEnd()),s.stderr.trim()&&(a.push("\u2014 stderr \u2014"),a.push(s.stderr.trimEnd())),s.timedOut?a.push(`Timed out (${Math.round(e.syncTimeoutMs/1e3)}s limit).`):s.code!==0&&s.code!==null&&a.push(`Exit ${s.code}`),p(a.join(`
166
+ `))}async function lt(e,t,n,r,o,s,a,i,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),C($s())):(o.set(d,!1),C(p("Free shell mode off.")));if(c.startsWith(e.commandPrefix)){let y=c.slice(e.commandPrefix.length).trim();if(!y)return C(p(`Send ${e.commandPrefix}<command> or /help`));if(!u&&Gs(y)){let b=bt(d,y);if(b!==void 0)return await lt(e,t,n,r,o,{...s,text:b},a,i,l,!0)}return C(await Ji(e,d,y))}if(/^\/help\s+files$/i.test(c.trim()))return C(Cr());if(c==="/help"||c==="help")return C(_(wt(e)));if(c.startsWith("/")){if(/^\/files(?:\s+help)?$/i.test(c.trim()))return C(Cr());let y=c.match(/^\/receive\b(?:\s+(\S+))?$/i);if(y){let g=(y[1]??"status").toLowerCase(),S=new Set(["here","cwd","session","dir"]),v=new Set(["default","global","reset"]);if(S.has(g)){hr(d,"sessionCwd");let H=W(d).cwd;return C(p(`Inbound files will save under this chat\u2019s session folder:
167
+ ${H}
168
168
  (layout: \u2026/<peer>/<date>/<file> under that root).
169
169
 
170
- Change folder with ${e.commandPrefix}cd \u2026 Send /receive default to use the server config again.`))}return x.has(g)?(gr(d,"default"),C(p("Per-chat inbound folder cleared. Uploads now follow fileReceiveRootMode in config.json (/files)."))):C(g==="help"?$r():g==="status"?ks(e,d):$r())}if(c==="/reload"||c==="/restart"){if(!a?.reload)return C(p("Reload is only available while the omnish gateway (omnish run) is running."));let g=await a.reload();return C(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 x=Jt();return C(x?Dt(x):p("No update snapshot yet. Send /updates (live check) once, or enable updateCheckEnabled and wait for the scheduled check."))}let S=await zt(De(),v());return C(Dt(S))}let b=c.match(/^\/security(?:\s+(\S+))?\s*$/i);if(b){let g=(b[1]??"").toLowerCase(),S=v(),x=Xe(S);return C(g==="help"||g==="?"?_(Ns()):g==="summary"||g==="brief"?p(is(x,"Send /security for the full report.")):g==="tips"?_(Ps()):g===""||g==="full"||g==="report"?Ls(x):p("Unknown /security subcommand. Try /security, /security summary, /security tips, or /security help"))}let h=c.match(/^\/(gateway|gw|mode)\b(?:\s+(.*))?$/i);if(h){let g=(h[2]??"").trim();return C(await Tc(g,a))}if(c==="/config"||c.startsWith("/config ")){let g=c.slice(7).trim();return C(await bi(g,a))}let E=vi(c);if(E!==null)return C(await Ci(e,E));let $=ro(c);if($!==null){let g=mi(e,$,l);return g===null?null:C(g)}let P=c.match(/^\/(send|file)\b(?:\s+([\s\S]+))?$/i);if(P){let g=(P[2]??"").trim();if(!g)return C(vr());let S=g.indexOf(" -- "),x,D;if(S!==-1?(x=g.slice(0,S).trim(),D=g.slice(S+4).trim()||void 0):x=g,(x.startsWith('"')&&x.endsWith('"')||x.startsWith("'")&&x.endsWith("'"))&&(x=x.slice(1,-1)),!x)return C(vr());let B=$c.resolve(W(d).cwd,x),Y=Ke(B,e.fileSendMaxBytes);return"error"in Y?C(p(Y.error)):{kind:"file",spec:{absPath:Y.absPath,category:Y.category,mimetype:Y.mimetype,displayName:Y.displayName,caption:D}}}if(c==="/allowlist"){let g=v();return C(ms([{label:"allowFrom (WhatsApp)",items:g.allowFrom},{label:"telegramAllowFrom",items:g.telegramAllowFrom}]))}let te=c.match(/^\/allow\b\s+(.+)$/i);if(te)try{let g=gn(te[1].trim());return C(xr(g))}catch(g){return C(p(String(g)))}if(/^\/allow\b\s*$/i.test(c))return C(ys());let ne=c.match(/^\/deny\b\s+(.+)$/i);if(ne)try{let g=hn(ne[1].trim());return C(xr(g))}catch(g){return C(p(String(g)))}if(/^\/deny\b\s*$/i.test(c))return C(ws());let L=c.match(/^\/(whatsapp|wa|telegram|tg)\b(?:\s+(.*))?$/i);if(L){let g=L[1].toLowerCase(),S=(L[2]??"").trim(),x=g==="whatsapp"||g==="wa";if(g==="telegram"||g==="tg"){let B=S.match(/^token\s+(\S+)\s*$/i);return B?C(Ic(B[1]??"")):S===""||/^help$/i.test(S)?C(_(ps(v()))):C(xs())}if(x)return S===""||/^help$/i.test(S)?C(_(ds(e))):C(Ss())}let fe=c.match(/^\/(cowork|cw)\b(?:\s+([\s\S]*))?$/i);if(fe){let g=(fe[2]??"").trim();return C(Gi(g,d))}if(c==="/apps"||c.startsWith("/apps "))return C(await Lc(c,d,e,i,r));let $e=Ac(c);if($e!==null)return C(Ec($e,d,e,i));if(c.startsWith("/bg")){let g=c.slice(3).trim();if(!g)return C(bs());let S=W(d).cwd,{id:x}=t.spawnJob(e.shell,g,{cwd:S});return C(p(`Job ${x} started.
170
+ Change folder with ${e.commandPrefix}cd \u2026 Send /receive default to use the server config again.`))}return v.has(g)?(hr(d,"default"),C(p("Per-chat inbound folder cleared. Uploads now follow fileReceiveRootMode in config.json (/files)."))):C(g==="help"?Rr():g==="status"?Ss(e,d):Rr())}if(c==="/reload"||c==="/restart"){if(!i?.reload)return C(p("Reload is only available while the omnish gateway (omnish run) is running."));let g=await i.reload();return C(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=Jt();return C(v?Ht(v):p("No update snapshot yet. Send /updates (live check) once, or enable updateCheckEnabled and wait for the scheduled check."))}let S=await zt(He(),x());return C(Ht(S))}let b=c.match(/^\/security(?:\s+(\S+))?\s*$/i);if(b){let g=(b[1]??"").toLowerCase(),S=x(),v=Ve(S);return C(g==="help"||g==="?"?_(Fs()):g==="summary"||g==="brief"?p(as(v,"Send /security for the full report.")):g==="tips"?_(Ns()):g===""||g==="full"||g==="report"?Ps(v):p("Unknown /security subcommand. Try /security, /security summary, /security tips, or /security help"))}let h=c.match(/^\/(gateway|gw|mode)\b(?:\s+(.*))?$/i);if(h){let g=(h[2]??"").trim();return C(await Tc(g,i))}if(c==="/config"||c.startsWith("/config ")){let g=c.slice(7).trim();return C(await bi(g,i))}let E=xi(c);if(E!==null)return C(await $i(e,E));let $=oo(c);if($!==null){let g=mi(e,$,l);return g===null?null:C(g)}let P=c.match(/^\/(send|file)\b(?:\s+([\s\S]+))?$/i);if(P){let g=(P[2]??"").trim();if(!g)return C($r());let S=g.indexOf(" -- "),v,H;if(S!==-1?(v=g.slice(0,S).trim(),H=g.slice(S+4).trim()||void 0):v=g,(v.startsWith('"')&&v.endsWith('"')||v.startsWith("'")&&v.endsWith("'"))&&(v=v.slice(1,-1)),!v)return C($r());let B=Cc.resolve(W(d).cwd,v),Q=qe(B,e.fileSendMaxBytes);return"error"in Q?C(p(Q.error)):{kind:"file",spec:{absPath:Q.absPath,category:Q.category,mimetype:Q.mimetype,displayName:Q.displayName,caption:H}}}if(c==="/allowlist"){let g=x();return C(fs([{label:"allowFrom (WhatsApp)",items:g.allowFrom},{label:"telegramAllowFrom",items:g.telegramAllowFrom}]))}let te=c.match(/^\/allow\b\s+(.+)$/i);if(te)try{let g=gn(te[1].trim());return C(xr(g))}catch(g){return C(p(String(g)))}if(/^\/allow\b\s*$/i.test(c))return C(ws());let ne=c.match(/^\/deny\b\s+(.+)$/i);if(ne)try{let g=hn(ne[1].trim());return C(xr(g))}catch(g){return C(p(String(g)))}if(/^\/deny\b\s*$/i.test(c))return C(bs());let L=c.match(/^\/(whatsapp|wa|telegram|tg)\b(?:\s+(.*))?$/i);if(L){let g=L[1].toLowerCase(),S=(L[2]??"").trim(),v=g==="whatsapp"||g==="wa";if(g==="telegram"||g==="tg"){let B=S.match(/^token\s+(\S+)\s*$/i);return B?C(Ic(B[1]??"")):S===""||/^help$/i.test(S)?C(_(ms(x()))):C(xs())}if(v)return S===""||/^help$/i.test(S)?C(_(ps(e))):C(vs())}let fe=c.match(/^\/(cowork|cw)\b(?:\s+([\s\S]*))?$/i);if(fe){let g=(fe[2]??"").trim();return C(Gi(g,d))}if(c==="/apps"||c.startsWith("/apps "))return C(await Pc(c,d,e,a,r));let Ce=Ac(c);if(Ce!==null)return C(Oc(Ce,d,e,a));if(c.startsWith("/bg")){let g=c.slice(3).trim();if(!g)return C(ks());let S=W(d).cwd,{id:v}=t.spawnJob(e.shell,g,{cwd:S});return C(p(`Job ${v} started.
171
171
  [cwd: ${S}]
172
- /log ${x}
173
- /tail ${x}`))}if(c==="/jobs"){let g=t.list().slice(0,20);return g.length===0?C(p("(no jobs yet)")):C(p(g.map(S=>{let x=S.finishedAt&&S.startedAt?`${((Date.parse(S.finishedAt)-Date.parse(S.startedAt))/1e3).toFixed(1)}s`:"\u2026";return`${S.id} ${S.status} exit=${S.exitCode??"?"} ${x}
172
+ /log ${v}
173
+ /tail ${v}`))}if(c==="/jobs"){let g=t.list().slice(0,20);return g.length===0?C(p("(no jobs yet)")):C(p(g.map(S=>{let v=S.finishedAt&&S.startedAt?`${((Date.parse(S.finishedAt)-Date.parse(S.startedAt))/1e3).toFixed(1)}s`:"\u2026";return`${S.id} ${S.status} exit=${S.exitCode??"?"} ${v}
174
174
  ${S.cmd.slice(0,120)}${S.cmd.length>120?"\u2026":""}`}).join(`
175
175
 
176
- `)))}let Se=c.match(/^\/log\s+([a-f0-9]{8})(?:\s+(\d+))?\s*$/i);if(Se){let g=Se[1].toLowerCase(),S=Se[2]?Number.parseInt(Se[2],10):e.jobLogTailLines,x=Number.isFinite(S)&&S>0?Math.min(S,500):e.jobLogTailLines;return C(p(t.tailLog(g,x)))}let Oe=c.match(/^\/tail\s+([a-f0-9]{8})\s*$/i);if(Oe){let g=Oe[1].toLowerCase(),S=Rc(d,g),x=n.get(S)??0,{text:D,nextOffset:B}=t.readSince(g,x);return n.set(S,B),C(D?p(D.trimEnd()||"(no new output)"):p(`(no new output; offset ${B})`))}let He=c.match(/^\/kill\s+([a-f0-9]{8})\s*$/i);if(He){let g=He[1].toLowerCase();return C(p(t.kill(g)))}let R=c.match(/^\/(shortcut|shortcuts|alias|aliases)\b(?:\s+([\s\S]*))?$/i);if(R){let g=R[1].toLowerCase(),S=(R[2]??"").trim();return g==="shortcuts"&&!S?S="list":((g==="alias"||g==="aliases")&&!S||g==="shortcut"&&!S)&&(S="help"),C(Oc(S,d))}if(!u){let g=c.trim().match(/^\/([a-zA-Z0-9][a-zA-Z0-9_-]{0,31})\s*$/);if(g){let S=g[1],x=bt(d,S);if(x!==void 0)return await lt(e,t,n,r,o,{...s,text:x},i,a,l,!0)}}return C(Cs(e))}let f=c.match(/^>(\S+)\s*(.*)$/s);if(f){let w=f[1],b=f[2]??"",h=await i.writeNamedLine(d,w,b);return h?C(p(h)):null}return await i.writeFocusedLine(d,c)?null:o.get(d)&&c?C(await Ji(e,d,c)):C($s(e))}function Ac(e){return e==="/run"||e.startsWith("/run ")?e.slice(4).trim():e==="/r"||e.startsWith("/r ")?e.slice(2).trim():null}function Ec(e,t,n,r){let o=e.trim();if(!o||/^help$/i.test(o))return _(As());if(/^list$/i.test(o))return Es(Xs(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=kt(t,n,d);return m?Os(m):Tn(d)}if(/^add$/i.test(o))return p('Usage: /run add <name> <command> [--template "\u2026"] \u2014 command e.g. agent -p "$OMNISH_TASK"; see /run help');if(/^set$/i.test(o))return p('Usage: /run set <name> <command> [--template "\u2026"] \u2014 command e.g. agent -p "$OMNISH_TASK"; see /run help');let i=o.match(/^add\s+(\S+)\s+([\s\S]+)$/i);if(i)try{let d=Br(i[2],n.recipesMacroDefaultCommand);Dr(t,i[1],d);let m=Ln(i[1]),f=m.ok?m.normalized:i[1].toLowerCase(),w=kt(t,n,f);return w?Ir(f,w):p("Recipe save failed.")}catch(d){return p(String(d))}let a=o.match(/^set\s+(\S+)\s+([\s\S]+)$/i);if(a)try{let d=Br(a[2],n.recipesMacroDefaultCommand);Dr(t,a[1],d);let m=Ln(a[1]),f=m.ok?m.normalized:a[1].toLowerCase(),w=kt(t,n,f);return w?Ir(f,w):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 Vs(t,d)?p(`Recipe removed (this chat): ${d}`):Tn(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],f=kt(t,n,d);if(!f)return Tn(d);let w=f.taskEnv??"OMNISH_TASK";if(!Pn(f.command,w))return p(`Recipe "${d}" command must reference "$${w}".`);let b=Ys(m,n.recipesMaxTaskChars);if(!b.ok)return p(b.error);let h=f.promptTemplate?qs(f.promptTemplate,w,b.task):b.task,E=Qs(d),$={[w]:h};return p(r.start(t,E,f.command,n,$))}let c=o.match(/^(\S+)$/);if(c){let d=c[1],m=d.toLowerCase();return m==="add"||m==="set"?p('Usage: /run add <name> cmd [--template "\u2026"] \u2014 cmd must include "$OMNISH_TASK"'):m==="show"?p("Usage: /run show <name>"):m==="remove"||m==="rm"||m==="del"?p("Usage: /run remove <name>"):kt(t,n,m)?p(`Usage: /run ${d} <task text\u2026> \u2014 replaces <<<OMNISH_TASK>>> / $OMNISH_TASK in stored templates`):p(`Unknown recipe "${d}". /run list`)}return p("/run: could not parse. /run help")}function Oc(e,t){let n=e.trim();if(!n||/^help$/i.test(n))return _(Rr());if(/^list$/i.test(n))return Ms(js(t));let r=n.match(/^show\s+(\S+)\s*$/i);if(r){let a=r[1],l=bt(t,a);return l===void 0?Tr(a):Is(a,l)}let o=n.match(/^add\s+(\S+)\s+([\s\S]+)$/i);if(o)try{Er(t,o[1],o[2]);let a=An(o[1]),l=a.ok?a.normalized:o[1].trim().toLowerCase(),u=bt(t,l);return Mr(l,u)}catch(a){return p(String(a))}let s=n.match(/^set\s+(\S+)\s+([\s\S]+)$/i);if(s)try{Er(t,s[1],s[2]);let a=An(s[1]),l=a.ok?a.normalized:s[1].trim().toLowerCase(),u=bt(t,l);return Mr(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 Us(t,a)?Ts(a):Tr(a)}return _(Rr())}async function Lc(e,t,n,r,o){let s=e.slice(5).trim(),i=s.toLowerCase();if(!i||i==="help")return _(Ar());let a=s.match(/^(\S+)\s*(.*)$/s);if(!a)return _(Ar());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=Mc(t,c),m=o.get(d)??0,{text:f,nextOffset:w}=r.readSince(t,c,m);return o.set(d,w),p(f.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]),f=Number(c[2]);return!Number.isFinite(m)||!Number.isFinite(f)?p("cols and rows must be numbers."):p(r.resize(t,d,m,f))}case"stop":{let c=u.split(/\s+/)[0];return c?p(r.stop(t,c)):p("Usage: /apps stop <name>")}case"kill":{let c=u.split(/\s+/)[0];return c?p(r.kill(t,c)):p("Usage: /apps kill <name>")}case"rm":{let c=u.split(/\s+/)[0];return c?p(r.rm(t,c)):p("Usage: /apps rm <name>")}default:return p(`Unknown /apps subcommand "${l}". /apps help`)}}function oo(e,t,n){return!e.clusterEnabled||ro(t.trim())!==null?!0:n?pi(n,e):!1}function Pc(e){return`wa:${e}`}function so(e){return`tg:${e}`}function zi(e){return{peerKey:Pc(e.fromJid),text:e.text,waMessageId:e.messageId,mediaSavedPath:e.mediaSavedPath,mediaError:e.mediaError}}import tt from"node:fs";import Uc from"node:path";var Ki={enter:"\r",cr:"\r",lf:`
177
- `,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 Nc(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+=`
178
- `,n++;continue}if(o==="t"){t+=" ",n++;continue}if(o==="\\"){t+="\\",n++;continue}}t+=r}return t}function Fc(e){let t=e.trim();if(!t)return"";if(t.startsWith("\\"))return Nc(t);let n=t.toLowerCase();if(Ki[n])return Ki[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 io(e){let t=e.split(",").map(n=>n.trim()).filter(Boolean);return t.length===0?"":t.map(n=>Fc(n)).join("")}var Xt="\x1B";function Yi(e){let t=e;return t=t.replace(new RegExp(`${Xt}\\[[\\d;?]*[ -/]*[@-~]`,"g"),""),t=t.replace(new RegExp(`${Xt}\\][^${Xt}\\u0007]*(?:\\u0007|${Xt}\\\\)`,"g"),""),t=t.replace(new RegExp(`${Xt}[@-Z\\\\-_]`,"g"),""),t=t.replace(/\u0007/g,""),t}function _c(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 Bc(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=Bc(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=Yi(l)),!l.trim()))return;let u=o.appsMaxFlushBytes;if(l.length>u){let f=l.slice(u);l=l.slice(0,u)+`
179
- [\u2026+${f.length} chars; /apps since ${r} to read more]`,this.pending.set(t,(this.pending.get(t)??"")+f)}let d=(this.opts.getRunningCount(n)>1?`[${r}] `:"")+l,m=_c(d,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 Dc from"node:fs";import Hc from"node:path";import*as qi from"node-pty";var jc=1024*1024,qn=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>jc&&this.ringChunks.length>0;){let n=this.ringChunks.shift();this.ringBytes-=n.length}}static start(t){N(_t(t.peerKey));let n=Hc.join(_t(t.peerKey),`${t.name}.log`),r=Dc.createWriteStream(n,{flags:"a",mode:384}),o={...process.env,TERM:"xterm-256color",COLORTERM:"truecolor",...t.cwd?{PWD:t.cwd}:{},...t.extraEnv??{}},s=qi.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 Wc=/^[a-zA-Z0-9_-]{1,32}$/;function ye(e){return Wc.test(e)?null:"Session name must be 1\u201332 chars: letters, digits, _ or -."}function me(e,t){return`${e}:${t}`}function Gc(e){let t=e.lastIndexOf(":");return t<=0?null:{peerKey:e.slice(0,t),name:e.slice(t+1)}}function ao(e){return new Promise(t=>setTimeout(t,e))}async function lo(e,t,n){let r=n.appsSubmitDelayMs,o=n.appsClearInputDelayMs,i=n.appsClearInput!==!1?io(n.appsClearInputSequence||"^A,^K"):"",l=t.replace(/\r\n/g,`
176
+ `)))}let Se=c.match(/^\/log\s+([a-f0-9]{8})(?:\s+(\d+))?\s*$/i);if(Se){let g=Se[1].toLowerCase(),S=Se[2]?Number.parseInt(Se[2],10):e.jobLogTailLines,v=Number.isFinite(S)&&S>0?Math.min(S,500):e.jobLogTailLines;return C(p(t.tailLog(g,v)))}let Oe=c.match(/^\/tail\s+([a-f0-9]{8})\s*$/i);if(Oe){let g=Oe[1].toLowerCase(),S=Rc(d,g),v=n.get(S)??0,{text:H,nextOffset:B}=t.readSince(g,v);return n.set(S,B),C(H?p(H.trimEnd()||"(no new output)"):p(`(no new output; offset ${B})`))}let De=c.match(/^\/kill\s+([a-f0-9]{8})\s*$/i);if(De){let g=De[1].toLowerCase();return C(p(t.kill(g)))}let R=c.match(/^\/(shortcut|shortcuts|alias|aliases)\b(?:\s+([\s\S]*))?$/i);if(R){let g=R[1].toLowerCase(),S=(R[2]??"").trim();return g==="shortcuts"&&!S?S="list":((g==="alias"||g==="aliases")&&!S||g==="shortcut"&&!S)&&(S="help"),C(Lc(S,d))}if(!u){let g=c.trim().match(/^\/([a-zA-Z0-9][a-zA-Z0-9_-]{0,31})\s*$/);if(g){let S=g[1],v=bt(d,S);if(v!==void 0)return await lt(e,t,n,r,o,{...s,text:v},a,i,l,!0)}}return C(Cs(e))}let f=c.match(/^>(\S+)\s*(.*)$/s);if(f){let y=f[1],b=f[2]??"",h=await a.writeNamedLine(d,y,b);return h?C(p(h)):null}return await a.writeFocusedLine(d,c)?null:o.get(d)&&c?C(await Ji(e,d,c)):C(Rs(e))}function Ac(e){return e==="/run"||e.startsWith("/run ")?e.slice(4).trim():e==="/r"||e.startsWith("/r ")?e.slice(2).trim():null}function Ec(e){let t=e.trim(),n=t.match(/^(\S+)\s+(?:--queue|-q)\s+([\s\S]+)$/i);if(n)return{recipe:n[1],task:n[2],queued:!0};let r=t.match(/^(\S+)\s+([\s\S]+)$/);return r?{recipe:r[1],task:r[2],queued:!1}:null}function Oc(e,t,n,r){let o=e.trim();if(!o||/^help$/i.test(o))return _(Es());if(/^list$/i.test(o))return Os(Xs(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=kt(t,n,d);return m?Ls(m):Tn(d)}if(/^add$/i.test(o))return p('Usage: /run add <name> <command> [--template "\u2026"] \u2014 command e.g. agent -p "$OMNISH_TASK"; see /run help');if(/^set$/i.test(o))return p('Usage: /run set <name> <command> [--template "\u2026"] \u2014 command e.g. agent -p "$OMNISH_TASK"; see /run help');let a=o.match(/^add\s+(\S+)\s+([\s\S]+)$/i);if(a)try{let d=Hr(a[2],n.recipesMacroDefaultCommand);Dr(t,a[1],d);let m=Ln(a[1]),f=m.ok?m.normalized:a[1].toLowerCase(),y=kt(t,n,f);return y?Ar(f,y):p("Recipe save failed.")}catch(d){return p(String(d))}let i=o.match(/^set\s+(\S+)\s+([\s\S]+)$/i);if(i)try{let d=Hr(i[2],n.recipesMacroDefaultCommand);Dr(t,i[1],d);let m=Ln(i[1]),f=m.ok?m.normalized:i[1].toLowerCase(),y=kt(t,n,f);return y?Ar(f,y):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 Vs(t,d)?p(`Recipe removed (this chat): ${d}`):Tn(d)}if(/^(?:remove|rm|del)$/i.test(o))return p("Usage: /run remove <name>");if(/^queue$/i.test(o))return p(r.runQueueStatus(t));if(/^queue\s+resume\s*$/i.test(o))return p(r.resumeRunQueue(t,n));let u=Ec(o);if(u){let{recipe:d,task:m,queued:f}=u,y=kt(t,n,d);if(!y)return Tn(d);let b=y.taskEnv??"OMNISH_TASK";if(!Pn(y.command,b))return p(`Recipe "${d}" command must reference "$${b}".`);let h=Ys(m,n.recipesMaxTaskChars);if(!h.ok)return p(h.error);let E=y.promptTemplate?Ks(y.promptTemplate,b,h.task):h.task,$={[b]:E};if(f)return p(r.enqueueQueuedRun(t,{command:y.command,extraEnv:$,recipeLabel:d},n));let P=Nn(d);return p(r.start(t,P,y.command,n,$))}let c=o.match(/^(\S+)$/);if(c){let d=c[1],m=d.toLowerCase();return m==="add"||m==="set"?p('Usage: /run add <name> cmd [--template "\u2026"] \u2014 cmd must include "$OMNISH_TASK"'):m==="show"?p("Usage: /run show <name>"):m==="remove"||m==="rm"||m==="del"?p("Usage: /run remove <name>"):kt(t,n,m)?p(`Usage: /run ${d} <task text\u2026> \u2014 replaces <<<OMNISH_TASK>>> / $OMNISH_TASK in stored templates`):p(`Unknown recipe "${d}". /run list`)}return p("/run: could not parse. /run help")}function Lc(e,t){let n=e.trim();if(!n||/^help$/i.test(n))return _(Mr());if(/^list$/i.test(n))return Ts(Us(t));let r=n.match(/^show\s+(\S+)\s*$/i);if(r){let i=r[1],l=bt(t,i);return l===void 0?Ir(i):As(i,l)}let o=n.match(/^add\s+(\S+)\s+([\s\S]+)$/i);if(o)try{Or(t,o[1],o[2]);let i=An(o[1]),l=i.ok?i.normalized:o[1].trim().toLowerCase(),u=bt(t,l);return Tr(l,u)}catch(i){return p(String(i))}let s=n.match(/^set\s+(\S+)\s+([\s\S]+)$/i);if(s)try{Or(t,s[1],s[2]);let i=An(s[1]),l=i.ok?i.normalized:s[1].trim().toLowerCase(),u=bt(t,l);return Tr(l,u)}catch(i){return p(String(i))}let a=n.match(/^(?:remove|rm|del)\s+(\S+)\s*$/i);if(a){let i=a[1];return Ws(t,i)?Is(i):Ir(i)}return _(Mr())}async function Pc(e,t,n,r,o){let s=e.slice(5).trim(),a=s.toLowerCase();if(!a||a==="help")return _(Er());let i=s.match(/^(\S+)\s*(.*)$/s);if(!i)return _(Er());let l=i[1].toLowerCase(),u=(i[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=Mc(t,c),m=o.get(d)??0,{text:f,nextOffset:y}=r.readSince(t,c,m);return o.set(d,y),p(f.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]),f=Number(c[2]);return!Number.isFinite(m)||!Number.isFinite(f)?p("cols and rows must be numbers."):p(r.resize(t,d,m,f))}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 so(e,t,n){return!e.clusterEnabled||oo(t.trim())!==null?!0:n?pi(n,e):!1}function Nc(e){return`wa:${e}`}function io(e){return`tg:${e}`}function zi(e){return{peerKey:Nc(e.fromJid),text:e.text,waMessageId:e.messageId,mediaSavedPath:e.mediaSavedPath,mediaError:e.mediaError}}import tt from"node:fs";import Wc from"node:path";var qi={enter:"\r",cr:"\r",lf:`
177
+ `,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 Fc(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+=`
178
+ `,n++;continue}if(o==="t"){t+=" ",n++;continue}if(o==="\\"){t+="\\",n++;continue}}t+=r}return t}function _c(e){let t=e.trim();if(!t)return"";if(t.startsWith("\\"))return Fc(t);let n=t.toLowerCase();if(qi[n])return qi[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 a=s.toUpperCase().charCodeAt(0);if(a>=64&&a<=95)return String.fromCharCode(a-64)}}return t}function ao(e){let t=e.split(",").map(n=>n.trim()).filter(Boolean);return t.length===0?"":t.map(n=>_c(n)).join("")}var Vt="\x1B";function Qi(e){let t=e;return t=t.replace(new RegExp(`${Vt}\\[[\\d;?]*[ -/]*[@-~]`,"g"),""),t=t.replace(new RegExp(`${Vt}\\][^${Vt}\\u0007]*(?:\\u0007|${Vt}\\\\)`,"g"),""),t=t.replace(new RegExp(`${Vt}[@-Z\\\\-_]`,"g"),""),t=t.replace(/\u0007/g,""),t}function Bc(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 Hc(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=Hc(t,n);this.pending.set(o,(this.pending.get(o)??"")+r);let s=this.timers.get(o);s&&clearTimeout(s);let a=this.opts.getCfg().appsFlushMs,i=setTimeout(()=>{this.timers.delete(o),this.flushNow(o,t,n)},a);this.timers.set(o,i)}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,a=this.lastFlushEnd.get(t)??0,i=Math.max(0,a+s-Date.now());if(i>0&&await new Promise(f=>setTimeout(f,i)),this.closed)return;let l=this.pending.get(t)??"";if(this.pending.delete(t),!l||(this.opts.isRaw(n,r)||(l=Qi(l)),!l.trim()))return;let u=o.appsMaxFlushBytes;if(l.length>u){let f=l.slice(u);l=l.slice(0,u)+`
179
+ [\u2026+${f.length} chars; /apps since ${r} to read more]`,this.pending.set(t,(this.pending.get(t)??"")+f)}let d=(this.opts.getRunningCount(n)>1?`[${r}] `:"")+l,m=Bc(d,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 Dc from"node:fs";import jc from"node:path";import*as Yi from"node-pty";var Uc=1024*1024,Kn=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>Uc&&this.ringChunks.length>0;){let n=this.ringChunks.shift();this.ringBytes-=n.length}}static start(t){N(_t(t.peerKey));let n=jc.join(_t(t.peerKey),`${t.name}.log`),r=Dc.createWriteStream(n,{flags:"a",mode:384}),o={...process.env,TERM:"xterm-256color",COLORTERM:"truecolor",...t.cwd?{PWD:t.cwd}:{},...t.extraEnv??{}},s=Yi.spawn(t.shell,["-lc",t.command],{name:"xterm-256color",cols:t.cols,rows:t.rows,cwd:t.cwd,env:o}),a=new e({...t,logPath:n});return a.term=s,a.logStream=r,a.disposeData=s.onData(i=>{let l=Buffer.from(i,"utf8");a.appendRing(l),r.write(i),t.router.push(t.peerKey,t.name,i)}),a.disposeExit=s.onExit(i=>{a.exited||(a.exited=!0,a.cleanupTerm(),t.onExit({exitCode:i.exitCode,signal:i.signal}))}),a}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 Gc=/^[a-zA-Z0-9_-]{1,32}$/;function ye(e){return Gc.test(e)?null:"Session name must be 1\u201332 chars: letters, digits, _ or -."}function me(e,t){return`${e}:${t}`}function Jc(e){let t=e.lastIndexOf(":");return t<=0?null:{peerKey:e.slice(0,t),name:e.slice(t+1)}}function lo(e){return new Promise(t=>setTimeout(t,e))}function zc(e,t){return e===0&&(t===0||t==null)}async function co(e,t,n){let r=n.appsSubmitDelayMs,o=n.appsClearInputDelayMs,a=n.appsClearInput!==!1?ao(n.appsClearInputSequence||"^A,^K"):"",l=t.replace(/\r\n/g,`
180
180
  `).replace(/\r/g,"").split(`
181
- `);i&&(o>0&&await ao(o),e.write(i));for(let u of l)u.length>0&&e.write(u),r>0&&await ao(r),e.write("\r"),i&&(o>0&&await ao(o),e.write(i))}function Jc(e,t){try{let r=tt.readFileSync(e,"utf8").split(/\r?\n/);return r.slice(Math.max(0,r.length-t)).join(`
182
- `).trimEnd()}catch{return""}}var Tt=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(me(r,o)),isRaw:(r,o)=>this.rawMode.has(me(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 Uc.join(_t(t),`${n}.log`)}getSession(t,n){return this.sessions.get(me(t,n))}removeSessionRecord(t,n){this.sessions.delete(me(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 lo(o,n,this.getCfg()),!0):!1}async writeNamedLine(t,n,r){let o=ye(n);if(o)return o;let s=this.getSession(t,n);return s?.alive?(await lo(s,r,this.getCfg()),null):`No app session "${n}". /apps list`}start(t,n,r,o,s){let i=ye(n);if(i)return i;if(!r.trim())return`Usage: /apps start <name> <command\u2026>
183
- Example: /apps start sh bash`;if(this.sessions.has(me(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.`;J();let a=W(t).cwd,l={...process.env,TERM:"xterm-256color",COLORTERM:"truecolor",...a?{PWD:a}:{},...s??{}},u;try{u=qn.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
+ `);a&&(o>0&&await lo(o),e.write(a));for(let u of l)u.length>0&&e.write(u),r>0&&await lo(r),e.write("\r"),a&&(o>0&&await lo(o),e.write(a))}function qc(e,t){try{let r=tt.readFileSync(e,"utf8").split(/\r?\n/);return r.slice(Math.max(0,r.length-t)).join(`
182
+ `).trimEnd()}catch{return""}}var Tt=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(me(r,o)),isRaw:(r,o)=>this.rawMode.has(me(r,o)),getRunningCount:r=>this.countPeerRunning(r)})}sessions=new Map;focus=new Map;muted=new Set;rawMode=new Set;router;killTimers=new Set;send;runQueue=new Map;activeQueuedRunHead=new Map;runQueuePaused=new Map;countPeerRunning(t){let n=`${t}:`,r=0;for(let[o,s]of this.sessions)o.startsWith(n)&&s.alive&&r++;return r}countTotalRunning(){let t=0;for(let n of this.sessions.values())n.alive&&t++;return t}dispose(){for(let t of this.killTimers)clearTimeout(t);this.killTimers.clear(),this.stopAll(),this.router.dispose()}stopAll(){for(let t of this.sessions.values())t.destroy();this.sessions.clear(),this.focus.clear(),this.runQueue.clear(),this.activeQueuedRunHead.clear(),this.runQueuePaused.clear()}getFocus(t){return this.focus.get(t)??null}logPath(t,n){return Wc.join(_t(t),`${n}.log`)}getSession(t,n){return this.sessions.get(me(t,n))}removeSessionRecord(t,n){this.sessions.delete(me(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 co(o,n,this.getCfg()),!0):!1}async writeNamedLine(t,n,r){let o=ye(n);if(o)return o;let s=this.getSession(t,n);return s?.alive?(await co(s,r,this.getCfg()),null):`No app session "${n}". /apps list`}enqueueQueuedRun(t,n,r){let o=this.runQueue.get(t)??[];o.push(n),this.runQueue.set(t,o);let s=o.length,a=this.runQueuePaused.get(t)??!1,i=this.activeQueuedRunHead.get(t)??null,l=i?.sessionName??null;if(l?this.getSession(t,l)?.alive??!1:!1){let c=i?.recipeLabel?` (recipe: ${i.recipeLabel})`:"",d=s-1,m=d>0?`${d} other job(s) in line before this one.`:"Next in line after the active run finishes.";return`Queued "${n.recipeLabel}" (wait slot ${s} behind active ${l}${c}). ${m}`}return a?`Queued "${n.recipeLabel}" (position ${s}). Run queue is paused \u2014 /run queue resume to continue.`:this.drainNextQueuedRun(t,r)}resumeRunQueue(t,n){this.runQueuePaused.set(t,!1);let r=this.activeQueuedRunHead.get(t)??null,o=r?.sessionName??null;if(o?this.getSession(t,o)?.alive??!1:!1){let i=r?.recipeLabel?` (recipe: ${r.recipeLabel})`:"";return`Run queue: session "${o}"${i} is still running. Wait for exit code=0 and signal=0 before the next starts.`}return(this.runQueue.get(t)??[]).length===0?"Run queue is empty.":(this.activeQueuedRunHead.set(t,null),this.drainNextQueuedRun(t,n))}runQueueStatus(t){let n=this.runQueue.get(t)??[],r=this.activeQueuedRunHead.get(t)??null,o=r?.sessionName??null,s=this.runQueuePaused.get(t)??!1,a=o?this.getSession(t,o)?.alive??!1:!1,i=["Run queue (this chat)"];if(o&&a){let l=r?.recipeLabel?` \xB7 recipe: ${r.recipeLabel}`:"";i.push(`Active: ${o}${l}`)}else o?i.push(`Active: ${o} (exiting or stale)`):i.push("Active: (none)");if(i.push(`Pending: ${n.length} (waiting only \u2014 not counting the active run above)`),i.push(`Paused: ${s}`),n.length>0){i.push("Waiting (FIFO order):");for(let u=0;u<Math.min(n.length,20);u++)i.push(`${u+1}) ${n[u].recipeLabel}`);n.length>20&&i.push(`\u2026 and ${n.length-20} more`)}return i.push("Next auto-starts only after exit code=0 and signal=0."),i.push("Commands: /run queue resume"),i.join(`
183
+ `)}drainNextQueuedRun(t,n){let r=this.runQueue.get(t);if(!r||r.length===0)return"";let o=r.shift();r.length===0?this.runQueue.delete(t):this.runQueue.set(t,r);let s=Nn(o.recipeLabel),a=this.runQueue.get(t)?.length??0,i=this.start(t,s,o.command,n,o.extraEnv);if(!i.includes("started and attached"))return r.unshift(o),this.runQueue.set(t,r),this.runQueuePaused.set(t,!0),this.activeQueuedRunHead.set(t,null),`${i}
184
+ Run queue paused; fix the issue, then /run queue resume.`;this.activeQueuedRunHead.set(t,{sessionName:s,recipeLabel:o.recipeLabel});let u=a>0?`
185
+ Run queue: started head above; ${a} job(s) still waiting in FIFO.`:`
186
+ Run queue: started head above; no further queued jobs.`;return`${i}${u}`}start(t,n,r,o,s){let a=ye(n);if(a)return a;if(!r.trim())return`Usage: /apps start <name> <command\u2026>
187
+ Example: /apps start sh bash`;if(this.sessions.has(me(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.`;J();let i=W(t).cwd,l={...process.env,TERM:"xterm-256color",COLORTERM:"truecolor",...i?{PWD:i}:{},...s??{}},u;try{u=Kn.start({peerKey:t,name:n,shell:o.shell,command:r.trim(),cwd:i,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)}
184
188
  If install skipped native builds: pnpm approve-builds && pnpm install (needs @whiskeysockets/baileys, sharp, protobufjs, esbuild, node-pty).`}return this.sessions.set(me(t,n),u),this.focus.set(t,n),`App "${n}" started and attached.
185
- [cwd: ${a}]
186
- Plain DMs go to this session until /apps detach. Use >othername text for another session.`}async handleSessionExit(t,n,r){let o=me(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=ye(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=Gc(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)"}
189
+ [cwd: ${i}]
190
+ Plain DMs go to this session until /apps detach. Use >othername text for another session.`}async handleSessionExit(t,n,r){let o=me(t,n);if(!this.sessions.has(o))return;let s=this.activeQueuedRunHead.get(t)?.sessionName===n,a=this.focus.get(t)===n;this.sessions.delete(o),a&&this.focus.set(t,null),this.muted.delete(o),this.rawMode.delete(o);let i=r.signal!=null?` signal=${r.signal}`:"",l=a?" (detached)":"",u=`[${n}] exited code=${r.exitCode}${i}${l}`;try{await this.send(t,u)}catch{}if(!s)return;this.activeQueuedRunHead.set(t,null);let c=this.getCfg(),d=zc(r.exitCode,r.signal),m=this.runQueue.get(t)?.length??0;if(d){if(this.runQueuePaused.set(t,!1),m>0){let f=this.drainNextQueuedRun(t,c);if(f)try{await this.send(t,f)}catch{}}return}if(this.runQueuePaused.set(t,!0),m>0){let f=`Run queue paused (exit code=${r.exitCode}${r.signal!=null?` signal=${r.signal}`:""}). ${m} run(s) waiting. /run queue resume to continue.`;try{await this.send(t,f)}catch{}}}attach(t,n){let r=ye(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,a]of this.sessions){let i=Jc(s);if(!i||i.peerKey!==t||!a.alive)continue;let l=this.focus.get(t)===i.name?" *":"";n.push(`${i.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)"}
187
191
  App sessions:
188
192
  ${n.join(`
189
- `)}`}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=ye(o);if(s)return s;let i=this.getSession(t,o),a=this.logPath(t,o),l=0;try{l=tt.statSync(a).size}catch{}let u=this.muted.has(me(t,o)),c=this.rawMode.has(me(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(`
190
- `)}async sendText(t,n,r){let o=ye(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,`
191
- `);return await lo(s,i,this.getCfg()),`Sent to "${n}" (${i.length} chars + Enter per line).`}sendKey(t,n,r){let o=ye(n);if(o)return o;let s=this.getSession(t,n);if(!s?.alive)return`No session "${n}".`;let i=io(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=ye(n);if(o)return o;let s=this.logPath(t,n);if(!tt.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 Jc(s,a)||"(empty log)"}readSince(t,n,r){let o=this.logPath(t,n);try{let i=tt.statSync(o).size,a=tt.openSync(o,"r");try{let l=Math.min(r,i),u=i-l,c=Buffer.alloc(u);return u>0&&tt.readSync(a,c,0,u,l),{text:c.toString("utf8"),nextOffset:i}}finally{tt.closeSync(a)}}catch{return{text:"",nextOffset:r}}}mute(t,n){let r=ye(n);return r||(this.muted.add(me(t,n)),`Muted chat output for "${n}" (log still grows). /apps unmute ${n}`)}unmute(t,n){let r=ye(n);return r||(this.muted.delete(me(t,n)),`Unmuted "${n}".`)}setRaw(t,n,r){let o=ye(n);if(o)return o;let s=me(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=ye(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=ye(n);if(r)return r;let o=this.getSession(t,n);if(!o?.alive)return`No running session "${n}".`;o.kill("SIGTERM");let s=me(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=ye(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=ye(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(me(t,n)),this.rawMode.delete(me(t,n));let s=this.logPath(t,n);try{tt.rmSync(s,{force:!0})}catch{}return`Removed "${n}" metadata and log.`}};import Qt from"node:fs";import Zt from"node:path";function Vi(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=j(String(o));s&&r.add(`wa:${dn(s)}`)}if(e==="tg"||e==="all")for(let o of n.telegramAllowFrom){let s=ae(String(o));s&&r.add(`tg:${s}`)}return[...r]}var zc=8,Xi=12e4,Qi=10,Kc=1e3;function Yc(e){return e.toISOString().replace(/[:.]/g,"-")}function qc(e){let t="";for(let n of e)n==="*"?t+="[^/\\\\]*":n==="?"?t+="[^/\\\\]":/[.+^${}()|[\]\\]/.test(n)?t+=`\\${n}`:t+=n;return new RegExp(`^${t}$`)}function Vc(e,t,n){let r=Rt(e,t),o=Zt.dirname(r),s=Zt.basename(r);if(o.includes("*")||o.includes("?"))return[];if(!s.includes("*")&&!s.includes("?")){try{let u=Qt.statSync(r);if(u.isFile()&&u.mtimeMs>=n)return[r]}catch{}return[]}let i;try{i=Qt.readdirSync(o)}catch{return[]}let a=qc(s),l=[];for(let u of i){if(!a.test(u))continue;let c=Zt.join(o,u);try{let d=Qt.statSync(c);d.isFile()&&d.mtimeMs>=n&&l.push(c)}catch{}}return l}function Zi(e,t,n){let r=Ke(e,t.fileSendMaxBytes);return"error"in r?{ok:!1,displayName:n??Zt.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 ea(e,t,n,r){let o=W(t.ownerPeerKey),s=t.cwd.trim()?t.cwd:o.cwd,i=Rt(t.outputDir,o.cwd);try{Qt.mkdirSync(i,{recursive:!0,mode:448})}catch(G){I.warn({err:String(G),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 Nn(e.shell,t.command,{timeoutMs:e.syncTimeoutMs,maxBytes:e.syncMaxBytes,cwd:s}),d=Date.now()-u,m=`${Yc(new Date)}-${t.id}-${l}.log`,f=Zt.join(i,m),b=[`cowork task=${t.name} id=${t.id}`,`slot=${a} kind=${l}`,`cwd=${s}`,`exit=${c.code} timedOut=${c.timedOut} durationMs=${d}`,"---",""].join(`
193
+ `)}`}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=ye(o);if(s)return s;let a=this.getSession(t,o),i=this.logPath(t,o),l=0;try{l=tt.statSync(i).size}catch{}let u=this.muted.has(me(t,o)),c=this.rawMode.has(me(t,o));return[`session: ${o}`,`alive: ${a?.alive??!1}`,`cmd: ${a?.command??"(unknown)"}`,`cwd: ${a?.cwd??"(unknown)"}`,`env keys: ${a?.envKeysCount??"?"}`,`terminal size: ${r.appsCols}x${r.appsRows}`,`ring bytes (approx): ${a?.ringByteCount??0}`,`log: ${i} (${l} bytes)`,`mute outbound: ${u}`,`raw outbound: ${c}`].join(`
194
+ `)}async sendText(t,n,r){let o=ye(n);if(o)return o;let s=this.getSession(t,n);if(!s?.alive)return`No session "${n}".`;let a=r.replace(/\r\n/g,`
195
+ `);return await co(s,a,this.getCfg()),`Sent to "${n}" (${a.length} chars + Enter per line).`}sendKey(t,n,r){let o=ye(n);if(o)return o;let s=this.getSession(t,n);if(!s?.alive)return`No session "${n}".`;let a=ao(r);return a?(s.write(a),`Sent keys to "${n}".`):"Empty key sequence. Example: /apps key sh Enter or ^C,Up,Enter"}tail(t,n,r){let o=ye(n);if(o)return o;let s=this.logPath(t,n);if(!tt.existsSync(s))return`(no log file for ${n})`;let a=this.getCfg(),i=Number.isFinite(r)&&r>0?Math.min(500,r):a.appsLogTailLines;return qc(s,i)||"(empty log)"}readSince(t,n,r){let o=this.logPath(t,n);try{let a=tt.statSync(o).size,i=tt.openSync(o,"r");try{let l=Math.min(r,a),u=a-l,c=Buffer.alloc(u);return u>0&&tt.readSync(i,c,0,u,l),{text:c.toString("utf8"),nextOffset:a}}finally{tt.closeSync(i)}}catch{return{text:"",nextOffset:r}}}mute(t,n){let r=ye(n);return r||(this.muted.add(me(t,n)),`Muted chat output for "${n}" (log still grows). /apps unmute ${n}`)}unmute(t,n){let r=ye(n);return r||(this.muted.delete(me(t,n)),`Unmuted "${n}".`)}setRaw(t,n,r){let o=ye(n);if(o)return o;let s=me(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=ye(n);if(s)return s;let a=this.getSession(t,n);if(!a?.alive)return`No session "${n}".`;let i=Math.min(500,Math.max(20,Math.floor(r))),l=Math.min(200,Math.max(5,Math.floor(o)));return a.resize(i,l),`Resized "${n}" to ${i}x${l}.`}stop(t,n){let r=ye(n);if(r)return r;let o=this.getSession(t,n);if(!o?.alive)return`No running session "${n}".`;o.kill("SIGTERM");let s=me(t,n),a=setTimeout(()=>{this.killTimers.delete(a);let i=this.getSession(t,n);i?.alive&&i.kill("SIGKILL")},5e3);return this.killTimers.add(a),`SIGTERM sent to "${n}". SIGKILL in 5s if still running.`}kill(t,n){let r=ye(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=ye(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(me(t,n)),this.rawMode.delete(me(t,n));let s=this.logPath(t,n);try{tt.rmSync(s,{force:!0})}catch{}return`Removed "${n}" metadata and log.`}};import Xt from"node:fs";import Zt from"node:path";function Ki(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=j(String(o));s&&r.add(`wa:${dn(s)}`)}if(e==="tg"||e==="all")for(let o of n.telegramAllowFrom){let s=ae(String(o));s&&r.add(`tg:${s}`)}return[...r]}var Qc=8,Vi=12e4,Xi=10,Yc=1e3;function Kc(e){return e.toISOString().replace(/[:.]/g,"-")}function Vc(e){let t="";for(let n of e)n==="*"?t+="[^/\\\\]*":n==="?"?t+="[^/\\\\]":/[.+^${}()|[\]\\]/.test(n)?t+=`\\${n}`:t+=n;return new RegExp(`^${t}$`)}function Xc(e,t,n){let r=Rt(e,t),o=Zt.dirname(r),s=Zt.basename(r);if(o.includes("*")||o.includes("?"))return[];if(!s.includes("*")&&!s.includes("?")){try{let u=Xt.statSync(r);if(u.isFile()&&u.mtimeMs>=n)return[r]}catch{}return[]}let a;try{a=Xt.readdirSync(o)}catch{return[]}let i=Vc(s),l=[];for(let u of a){if(!i.test(u))continue;let c=Zt.join(o,u);try{let d=Xt.statSync(c);d.isFile()&&d.mtimeMs>=n&&l.push(c)}catch{}}return l}function Zi(e,t,n){let r=qe(e,t.fileSendMaxBytes);return"error"in r?{ok:!1,displayName:n??Zt.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 ea(e,t,n,r){let o=W(t.ownerPeerKey),s=t.cwd.trim()?t.cwd:o.cwd,a=Rt(t.outputDir,o.cwd);try{Xt.mkdirSync(a,{recursive:!0,mode:448})}catch(G){I.warn({err:String(G),outDir:a},"cowork mkdir outputDir")}let i=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 Fn(e.shell,t.command,{timeoutMs:e.syncTimeoutMs,maxBytes:e.syncMaxBytes,cwd:s}),d=Date.now()-u,m=`${Kc(new Date)}-${t.id}-${l}.log`,f=Zt.join(a,m),b=[`cowork task=${t.name} id=${t.id}`,`slot=${i} kind=${l}`,`cwd=${s}`,`exit=${c.code} timedOut=${c.timedOut} durationMs=${d}`,"---",""].join(`
192
196
  `)+(c.stdout||"")+(c.stderr?`
193
197
  --- stderr ---
194
- ${c.stderr}`:"");try{Qt.writeFileSync(f,b,{mode:384})}catch(G){I.warn({err:String(G),logPath:f},"cowork write log")}let h=c.code===0&&!c.timedOut&&c.signal===null,$=be().find(G=>G.id===t.id&&G.ownerPeerKey===t.ownerPeerKey),P=$?.notify??t.notify,te=$?.attachLog??t.attachLog,ne=$?.attachFiles??t.attachFiles,L=c.timedOut?"timeout":c.signal?`signal ${c.signal}`:c.code!==0&&c.code!==null?`exit ${c.code}`:null,fe=`slot: ${a} \xB7 ${l}${L?` \xB7 ${L}`:""}`,$e=(c.stdout||"").replace(/\s+$/,""),Se=(c.stderr||"").trim(),Oe=$e||(Se?`(stderr) ${Se}`:"(no output)"),He=`${t.name} : ${L?`[${L}] `:""}${Oe}`,R=n.onDemand?[fe,`output: ${Oe}`]:[He],g=[],S=[],x=u-Kc,D=[],B=new Set;if(Array.isArray(ne))for(let G of ne){let Le;try{Le=Vc(G,s,x)}catch(Pe){I.warn({err:String(Pe),pat:G},"cowork attach glob"),g.push(`attach: ${G} skipped (glob error)`);continue}if(Le.length!==0)for(let Pe of Le)B.has(Pe)||(B.add(Pe),D.push(Pe))}if(te){let G=Zi(f,e,`${t.name}-${l}.log`);G.ok?S.push(G.spec):g.push(`attach: ${G.displayName} skipped (${G.reason})`)}let Y=0;for(let G of D){if(S.length>=Qi){Y+=1;continue}let Le=Zi(G,e);Le.ok?S.push(Le.spec):g.push(`attach: ${Le.displayName} skipped (${Le.reason})`)}Y>0&&g.push(`attached: skipped ${Y} file(s) over cap ${Qi}`),g.length>0&&R.push(...g);let Re=R.join(`
195
- `),sn=p(Re),mt=Vi(P,t.ownerPeerKey,e);for(let G of mt){let Le=Be(sn,G.startsWith("tg:")?"telegram":"whatsapp").text;try{await r.sendToPeer(G,Le)}catch(Pe){I.warn({err:String(Pe),pk:G},"cowork notify failed")}for(let Pe of S)try{await r.sendMediaToPeer(G,Pe)}catch(Ka){I.warn({err:String(Ka),pk:G,file:Pe.displayName},"cowork media notify failed")}}return{commandOk:h,logPath:f}}function ta(e){let t=!1;qt(be());let n=async()=>{if(t)return;t=!0;let s=Date.now(),i=0,a=0,l=0,u=0,c=0;try{let d=e.getConfig(),{batch:m,remainingAfter:f}=ji(zc);a=m.length,l=f,i=m.length+f;for(let h of m)try{let $=be().find(P=>P.name===h.name.toLowerCase()&&P.ownerPeerKey===h.ownerPeerKey);$&&$.enabled&&(u+=1,await ea(d,$,{slotMs:null,catchUp:!1,onDemand:!0},e))}catch(E){I.warn({err:String(E),pending:h.name},"cowork on-demand run failed")}let w=be();qt(w);let b=Date.now();for(let h of w){if(!h.enabled||h.schedule.kind==="ondemand")continue;let E=zn(h),$=Ei(h.schedule,E,h.createdAtMs,b);if($.length===0)continue;let P=$[$.length-1],te=b-P>Xi;try{c+=1;let{commandOk:ne,logPath:L}=await ea(d,h,{slotMs:P,catchUp:te,onDemand:!1},e);if(ne){let fe=Date.now(),$e=b-P<=Xi?"on_time":"catch_up";if($.length===1)Kn(h.id,[{slotMs:P,kind:$e,logPath:L,completedAtMs:fe}]);else{let Oe=$.slice(0,-1).map(He=>({slotMs:He,kind:"coalesced",logPath:null,completedAtMs:fe}));Oe.push({slotMs:P,kind:$e,logPath:L,completedAtMs:fe}),Kn(h.id,Oe)}}}catch(ne){I.warn({err:String(ne),task:h.name},"cowork scheduled run failed")}}}finally{let d=Date.now()-s;I.info({tickMs:d,pendingDepthStart:i,pendingDequeued:a,pendingRemainingAfter:l,pendingRunsStarted:u,scheduledRan:c},"cowork tick"),t=!1}},r=setInterval(()=>void n(),3e4),o=setTimeout(()=>void n(),5e3);return()=>{clearInterval(r),clearTimeout(o),Ni()}}import{spawn as Xc}from"node:child_process";import Qc from"node:crypto";import ke from"node:fs";import co from"node:path";function It(e,t){ke.writeFileSync(e,JSON.stringify(t,null,2)+`
196
- `,{mode:384})}function en(e){try{return JSON.parse(ke.readFileSync(e,"utf8"))}catch{return null}}var ct=class{running=new Map;metaPath(t){return co.join(Me,`${t}.meta.json`)}logPath(t){return co.join(Me,`${t}.log`)}spawnJob(t,n,r={}){J();let o=Qc.randomBytes(4).toString("hex"),s=this.logPath(o),i=this.metaPath(o);ke.writeFileSync(s,"",{flag:"w",mode:384});let a=ke.createWriteStream(s,{flags:"a"}),l=new Date().toISOString(),u=r.cwd,c=Xc(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 It(i,d),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=en(i)??d,b={...w,status:w.status==="killed"?"killed":"done",exitCode:m,signal:f??null,finishedAt:new Date().toISOString()};It(i,b)}),c.on("error",m=>{this.running.delete(o),a.end(()=>{try{ke.appendFileSync(s,`
198
+ ${c.stderr}`:"");try{Xt.writeFileSync(f,b,{mode:384})}catch(G){I.warn({err:String(G),logPath:f},"cowork write log")}let h=c.code===0&&!c.timedOut&&c.signal===null,$=be().find(G=>G.id===t.id&&G.ownerPeerKey===t.ownerPeerKey),P=$?.notify??t.notify,te=$?.attachLog??t.attachLog,ne=$?.attachFiles??t.attachFiles,L=c.timedOut?"timeout":c.signal?`signal ${c.signal}`:c.code!==0&&c.code!==null?`exit ${c.code}`:null,fe=`slot: ${i} \xB7 ${l}${L?` \xB7 ${L}`:""}`,Ce=(c.stdout||"").replace(/\s+$/,""),Se=(c.stderr||"").trim(),Oe=Ce||(Se?`(stderr) ${Se}`:"(no output)"),De=`${t.name} : ${L?`[${L}] `:""}${Oe}`,R=n.onDemand?[fe,`output: ${Oe}`]:[De],g=[],S=[],v=u-Yc,H=[],B=new Set;if(Array.isArray(ne))for(let G of ne){let Le;try{Le=Xc(G,s,v)}catch(Pe){I.warn({err:String(Pe),pat:G},"cowork attach glob"),g.push(`attach: ${G} skipped (glob error)`);continue}if(Le.length!==0)for(let Pe of Le)B.has(Pe)||(B.add(Pe),H.push(Pe))}if(te){let G=Zi(f,e,`${t.name}-${l}.log`);G.ok?S.push(G.spec):g.push(`attach: ${G.displayName} skipped (${G.reason})`)}let Q=0;for(let G of H){if(S.length>=Xi){Q+=1;continue}let Le=Zi(G,e);Le.ok?S.push(Le.spec):g.push(`attach: ${Le.displayName} skipped (${Le.reason})`)}Q>0&&g.push(`attached: skipped ${Q} file(s) over cap ${Xi}`),g.length>0&&R.push(...g);let Re=R.join(`
199
+ `),sn=p(Re),mt=Ki(P,t.ownerPeerKey,e);for(let G of mt){let Le=Be(sn,G.startsWith("tg:")?"telegram":"whatsapp").text;try{await r.sendToPeer(G,Le)}catch(Pe){I.warn({err:String(Pe),pk:G},"cowork notify failed")}for(let Pe of S)try{await r.sendMediaToPeer(G,Pe)}catch(qa){I.warn({err:String(qa),pk:G,file:Pe.displayName},"cowork media notify failed")}}return{commandOk:h,logPath:f}}function ta(e){let t=!1;Yt(be());let n=async()=>{if(t)return;t=!0;let s=Date.now(),a=0,i=0,l=0,u=0,c=0;try{let d=e.getConfig(),{batch:m,remainingAfter:f}=ji(Qc);i=m.length,l=f,a=m.length+f;for(let h of m)try{let $=be().find(P=>P.name===h.name.toLowerCase()&&P.ownerPeerKey===h.ownerPeerKey);$&&$.enabled&&(u+=1,await ea(d,$,{slotMs:null,catchUp:!1,onDemand:!0},e))}catch(E){I.warn({err:String(E),pending:h.name},"cowork on-demand run failed")}let y=be();Yt(y);let b=Date.now();for(let h of y){if(!h.enabled||h.schedule.kind==="ondemand")continue;let E=qn(h),$=Ei(h.schedule,E,h.createdAtMs,b);if($.length===0)continue;let P=$[$.length-1],te=b-P>Vi;try{c+=1;let{commandOk:ne,logPath:L}=await ea(d,h,{slotMs:P,catchUp:te,onDemand:!1},e);if(ne){let fe=Date.now(),Ce=b-P<=Vi?"on_time":"catch_up";if($.length===1)Qn(h.id,[{slotMs:P,kind:Ce,logPath:L,completedAtMs:fe}]);else{let Oe=$.slice(0,-1).map(De=>({slotMs:De,kind:"coalesced",logPath:null,completedAtMs:fe}));Oe.push({slotMs:P,kind:Ce,logPath:L,completedAtMs:fe}),Qn(h.id,Oe)}}}catch(ne){I.warn({err:String(ne),task:h.name},"cowork scheduled run failed")}}}finally{let d=Date.now()-s;I.info({tickMs:d,pendingDepthStart:a,pendingDequeued:i,pendingRemainingAfter:l,pendingRunsStarted:u,scheduledRan:c},"cowork tick"),t=!1}},r=setInterval(()=>void n(),3e4),o=setTimeout(()=>void n(),5e3);return()=>{clearInterval(r),clearTimeout(o),Ni()}}import{spawn as Zc}from"node:child_process";import eu from"node:crypto";import ke from"node:fs";import uo from"node:path";function It(e,t){ke.writeFileSync(e,JSON.stringify(t,null,2)+`
200
+ `,{mode:384})}function en(e){try{return JSON.parse(ke.readFileSync(e,"utf8"))}catch{return null}}var ct=class{running=new Map;metaPath(t){return uo.join(Me,`${t}.meta.json`)}logPath(t){return uo.join(Me,`${t}.log`)}spawnJob(t,n,r={}){J();let o=eu.randomBytes(4).toString("hex"),s=this.logPath(o),a=this.metaPath(o);ke.writeFileSync(s,"",{flag:"w",mode:384});let i=ke.createWriteStream(s,{flags:"a"}),l=new Date().toISOString(),u=r.cwd,c=Zc(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 It(a,d),c.stdout?.on("data",m=>{i.write(m)}),c.stderr?.on("data",m=>{i.write(m)}),c.on("close",(m,f)=>{this.running.delete(o),i.end();let y=en(a)??d,b={...y,status:y.status==="killed"?"killed":"done",exitCode:m,signal:f??null,finishedAt:new Date().toISOString()};It(a,b)}),c.on("error",m=>{this.running.delete(o),i.end(()=>{try{ke.appendFileSync(s,`
197
201
  [spawn error] ${String(m)}
198
- `)}catch{}});let f=en(i)??d;It(i,{...f,status:"done",exitCode:null,signal:null,finishedAt:new Date().toISOString()})}),{id:o,meta:d}}list(){J();let t=[];try{t=ke.readdirSync(Me)}catch{return[]}let n=[];for(let r of t){if(!r.endsWith(".meta.json"))continue;let o=en(co.join(Me,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(!ke.existsSync(r))return"(no log file)";let s=ke.readFileSync(r,"utf8").split(`
202
+ `)}catch{}});let f=en(a)??d;It(a,{...f,status:"done",exitCode:null,signal:null,finishedAt:new Date().toISOString()})}),{id:o,meta:d}}list(){J();let t=[];try{t=ke.readdirSync(Me)}catch{return[]}let n=[];for(let r of t){if(!r.endsWith(".meta.json"))continue;let o=en(uo.join(Me,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(!ke.existsSync(r))return"(no log file)";let s=ke.readFileSync(r,"utf8").split(`
199
203
  `);return s.slice(Math.max(0,s.length-n)).join(`
200
- `).trimEnd()||"(empty log)"}readSince(t,n){let r=this.logPath(t);if(!ke.existsSync(r))return{text:"",nextOffset:n};let s=ke.statSync(r).size;if(n>=s)return{text:"",nextOffset:s};let i=Buffer.alloc(s-n),a=ke.openSync(r,"r");try{ke.readSync(a,i,0,i.length,n)}finally{ke.closeSync(a)}return{text:i.toString("utf8"),nextOffset:s}}kill(t){let n=this.metaPath(t),r=en(n);if(!r)return`Unknown job id: ${t}`;let o=this.running.get(t);if(o&&!o.killed)return o.kill("SIGTERM"),It(n,{...r,status:"killed",signal:"SIGTERM"}),`Sent SIGTERM to job ${t} (pid ${r.pid??"?"})`;if(r.pid)try{return process.kill(r.pid,"SIGTERM"),It(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=en(this.metaPath(t));r&&It(this.metaPath(t),{...r,status:"killed",signal:"SIGTERM"})}this.running.clear()}};import Iu from"node:fs";import{Bot as Au,InputFile as Eu}from"grammy";import na from"node:fs";import At from"node:path";function Zc(e){return e.replace(/[^a-zA-Z0-9._-]+/g,"_").slice(0,120)||"unknown"}function eu(e){let r=At.basename(e.trim()||"file").replace(/[^a-zA-Z0-9._-]+/g,"_").replace(/^\.+/,"").slice(0,180);return r.length>0?r:"file"}function tu(e,t){let n=new Date().toISOString().slice(0,10),r=Zc(t);return At.join(e,r,n)}function Vn(e,t,n){let r=tu(e,t);N(r);let o=eu(n),s=At.join(r,o);if(!na.existsSync(s))return s;let i=At.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=At.join(r,u),!na.existsSync(s))return s}return At.join(r,`${a}-${Date.now()}${i}`)}import nu from"node:dns";import ru from"node:https";import{URL as ou}from"node:url";function Et(e){if(e instanceof Error){let t=e.cause;return t!=null?`${e.message} (${String(t)})`:e.message}return String(e)}function ra(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 su(e,t){let r=t.split("/").filter(o=>o.length>0).map(encodeURIComponent).join("/");return`https://api.telegram.org/file/bot${e}/${r}`}var oa="omnish (Telegram bot; https://github.com/labKnowledge/whatsLive)";function iu(e){let t=new ou(e);return new Promise((n,r)=>{let o=ru.request({hostname:t.hostname,port:t.port||443,path:t.pathname+t.search,method:"GET",headers:{"User-Agent":oa,Accept:"*/*"},lookup(s,i,a){nu.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 sa(e,t,n){let r;try{r=await e.api.getFile(t)}catch(i){return I.warn({err:String(i),fileId:t},"telegram getFile failed"),{error:"Could not resolve file on Telegram."}}if(!r.file_path)return{error:"Telegram did not return a file path."};let o=su(e.token,r.file_path),s;try{let i=await fetch(o,{headers:{"User-Agent":oa,Accept:"*/*"}});if(!i.ok)return I.warn({status:i.status,statusText:i.statusText,fileId:t},"telegram file fetch HTTP error"),{error:`Could not download file from Telegram (HTTP ${i.status}${i.statusText?` ${i.statusText}`:""}).`};try{s=Buffer.from(await i.arrayBuffer())}catch(a){return I.warn({err:String(a),fileId:t},"telegram file read body failed"),{error:`Could not read file from Telegram (${String(a)}).`}}}catch(i){I.warn({err:String(i),fileId:t},"telegram file fetch failed; retrying via HTTPS IPv4");try{let a=await iu(o);if(a.status<200||a.status>=300)return I.warn({status:a.status,fileId:t},"telegram HTTPS IPv4 fallback HTTP error"),{error:`Could not download file from Telegram (HTTP ${a.status}).`};s=a.buffer}catch(a){return I.warn({err:String(i),err2:String(a),fileId:t},"telegram file download failed (fetch and IPv4 fallback)"),{error:`Could not download file from Telegram (network: ${Et(i)}; IPv4 fallback: ${Et(a)}).`}}}return n>0&&s.byteLength>n?{error:`Media too large (max ${n} bytes).`}:{buffer:s}}import{downloadMediaMessage as au,extensionForMediaMessage as lu,getContentType as Xn,isJidGroup as la,isLidUser as cu}from"@whiskeysockets/baileys";import uu from"node:fs";function du(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 pu(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 ca(e){return[du(e),pu(e)].filter(n=>n.trim().length>0).join(`
201
- `).trim()}function uo(e){return e.replace(/[\u0000-\u0008\u000B\u000C\u000E-\u001F\u007F]/g,"").replace(/\uFEFF/g,"").trim()}function ua(e){let t=Xn(e??void 0);return t==="imageMessage"||t==="videoMessage"||t==="audioMessage"||t==="documentMessage"||t==="stickerMessage"}function mu(e){let t=Xn(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 ia(e){try{if(!e)return"file.bin";let t=lu(e);return`file${t&&t.length>0?t.startsWith(".")?t:`.${t}`:".bin"}`}catch{return`${(Xn(e??void 0)??"file").replace("Message","")||"file"}.bin`}}function fu(e){let t=Xn(e??void 0);if(!t)return ia(e);let n=e?.[t];return n?.fileName&&typeof n.fileName=="string"&&n.fileName.trim()?n.fileName.trim():ia(e)}async function aa(e,t,n,r){let o=t.message??void 0;if(!ua(o))return{};let s=r.fileReceiveMaxBytes,i=mu(o);if(s>0&&i!==void 0&&i>s)return{error:`Media too large (max ${s} bytes).`};let a;try{a=await au(t,"buffer",{},{logger:e.logger,reuploadRequest:e.updateMediaMessage})}catch(d){return I.warn({err:String(d),detail:Et(d)},"whatsapp downloadMediaMessage failed"),{error:`Could not download media from WhatsApp (${Et(d)}).`}}if(s>0&&a.length>s)return{error:`Media too large (max ${s} bytes).`};let l;try{l=yt(r,n)}catch(d){return{error:String(d)}}let u=fu(o),c=Vn(l,n,u);try{uu.writeFileSync(c,a,{mode:384})}catch{return{error:"Could not write media to inbox."}}return{path:c}}async function gu(e,t){let n=t.remoteJid??"";if(!n)return{fromJid:"",fromE164:""};if(t.remoteJidAlt){let r=j(t.remoteJidAlt)??j(n)??"";return{fromJid:n,fromE164:r}}if(cu(n))try{let r=await e.signalRepository?.lidMapping?.getPNForLID?.(n);if(r){let o=j(r)??"";if(o)return{fromJid:n,fromE164:o}}}catch{}return{fromJid:n,fromE164:j(n)??""}}function hu(e){try{if(!v().clusterEnabled)return;let n=ca(e.message??void 0);if(!n)return;let r=ni(n);if(!r)return;let o=e.key?.remoteJid??"",s=null;if(o&&!la(o)){let i=j(o);i&&(s=`wa:${i}`)}di(r,s)}catch(t){I.warn({err:String(t)},"cluster footer observation failed")}}function da(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){hu(o);continue}let i=s.remoteJid;if(!i||la(i)||i.toLowerCase().endsWith("@status")||i==="status@broadcast")continue;let l=uo(ca(o.message??void 0)),{fromJid:u,fromE164:c}=await gu(e,s),d=`wa:${u}`,m=ua(o.message??void 0);if(m&&!l){(async()=>{try{let b=v(),h=await aa(e,o,d,b);await t({fromJid:u,fromE164:c,text:"",messageId:s.id??void 0,mediaSavedPath:h.path,mediaError:h.error})}catch(b){I.error({err:String(b),fromJid:u},"whatsapp media-only background task failed")}})();continue}let f,w;if(m){let b=v(),h=await aa(e,o,d,b);f=h.path,w=h.error}!l&&!f&&!w||await t({fromJid:u,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 Cu from"node:fs";import yu from"node:process";import wu,{DisconnectReason as ut,fetchLatestBaileysVersion as bu,makeCacheableSignalKeyStore as ku,useMultiFileAuthState as Su}from"@whiskeysockets/baileys";import xu from"qrcode-terminal";var vu="0.1.0";function po(e){return e?.error?.output?.statusCode}async function Qn(e={}){J();let t=e.authDir??H,n=e.verbose===!0,r=Oi(),{state:o,saveCreds:s}=await Su(t),{version:i}=await bu(),a=wu({version:i,logger:r,printQRInTerminal:!1,browser:["omnish","cli",vu],auth:{creds:o.creds,keys:ku(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=yu.stdout,f=y(m,"\xB7".repeat(42));console.log(re(m,"Scan with WhatsApp \u2192 Linked devices")),console.log(f),xu.generate(d,{small:!0}),console.log(f)}if(u==="close"){let m=po(c);n&&m===ut.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 Ot(e){try{e.end(new Error("omnish: socket closed"))}catch{e.ws?.close()}}function mo(e){return e?.output?.statusCode??e?.status??e?.error?.output?.statusCode}function Zn(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 dt(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 ha=3500,$u=400,pa=18e4,ma=9e4,fa=3e5;function fo(e,t=ha){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(`
204
+ `).trimEnd()||"(empty log)"}readSince(t,n){let r=this.logPath(t);if(!ke.existsSync(r))return{text:"",nextOffset:n};let s=ke.statSync(r).size;if(n>=s)return{text:"",nextOffset:s};let a=Buffer.alloc(s-n),i=ke.openSync(r,"r");try{ke.readSync(i,a,0,a.length,n)}finally{ke.closeSync(i)}return{text:a.toString("utf8"),nextOffset:s}}kill(t){let n=this.metaPath(t),r=en(n);if(!r)return`Unknown job id: ${t}`;let o=this.running.get(t);if(o&&!o.killed)return o.kill("SIGTERM"),It(n,{...r,status:"killed",signal:"SIGTERM"}),`Sent SIGTERM to job ${t} (pid ${r.pid??"?"})`;if(r.pid)try{return process.kill(r.pid,"SIGTERM"),It(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=en(this.metaPath(t));r&&It(this.metaPath(t),{...r,status:"killed",signal:"SIGTERM"})}this.running.clear()}};import Eu from"node:fs";import{Bot as Ou,InputFile as Lu}from"grammy";import na from"node:fs";import At from"node:path";function tu(e){return e.replace(/[^a-zA-Z0-9._-]+/g,"_").slice(0,120)||"unknown"}function nu(e){let r=At.basename(e.trim()||"file").replace(/[^a-zA-Z0-9._-]+/g,"_").replace(/^\.+/,"").slice(0,180);return r.length>0?r:"file"}function ru(e,t){let n=new Date().toISOString().slice(0,10),r=tu(t);return At.join(e,r,n)}function Vn(e,t,n){let r=ru(e,t);N(r);let o=nu(n),s=At.join(r,o);if(!na.existsSync(s))return s;let a=At.extname(o),i=a?o.slice(0,-a.length):o;for(let l=1;l<1e4;l+=1){let u=`${i}-${l}${a}`;if(s=At.join(r,u),!na.existsSync(s))return s}return At.join(r,`${i}-${Date.now()}${a}`)}import ou from"node:dns";import su from"node:https";import{URL as iu}from"node:url";function Et(e){if(e instanceof Error){let t=e.cause;return t!=null?`${e.message} (${String(t)})`:e.message}return String(e)}function ra(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 au(e,t){let r=t.split("/").filter(o=>o.length>0).map(encodeURIComponent).join("/");return`https://api.telegram.org/file/bot${e}/${r}`}var oa="omnish (Telegram bot; https://github.com/labKnowledge/whatsLive)";function lu(e){let t=new iu(e);return new Promise((n,r)=>{let o=su.request({hostname:t.hostname,port:t.port||443,path:t.pathname+t.search,method:"GET",headers:{"User-Agent":oa,Accept:"*/*"},lookup(s,a,i){ou.lookup(s,{family:4},i)}},s=>{let a=s.statusCode??0,i=[];s.on("data",l=>i.push(l)),s.on("end",()=>n({status:a,buffer:Buffer.concat(i)})),s.on("error",r)});o.on("error",r),o.end()})}async function sa(e,t,n){let r;try{r=await e.api.getFile(t)}catch(a){return I.warn({err:String(a),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=au(e.token,r.file_path),s;try{let a=await fetch(o,{headers:{"User-Agent":oa,Accept:"*/*"}});if(!a.ok)return I.warn({status:a.status,statusText:a.statusText,fileId:t},"telegram file fetch HTTP error"),{error:`Could not download file from Telegram (HTTP ${a.status}${a.statusText?` ${a.statusText}`:""}).`};try{s=Buffer.from(await a.arrayBuffer())}catch(i){return I.warn({err:String(i),fileId:t},"telegram file read body failed"),{error:`Could not read file from Telegram (${String(i)}).`}}}catch(a){I.warn({err:String(a),fileId:t},"telegram file fetch failed; retrying via HTTPS IPv4");try{let i=await lu(o);if(i.status<200||i.status>=300)return I.warn({status:i.status,fileId:t},"telegram HTTPS IPv4 fallback HTTP error"),{error:`Could not download file from Telegram (HTTP ${i.status}).`};s=i.buffer}catch(i){return I.warn({err:String(a),err2:String(i),fileId:t},"telegram file download failed (fetch and IPv4 fallback)"),{error:`Could not download file from Telegram (network: ${Et(a)}; IPv4 fallback: ${Et(i)}).`}}}return n>0&&s.byteLength>n?{error:`Media too large (max ${n} bytes).`}:{buffer:s}}import{downloadMediaMessage as cu,extensionForMediaMessage as uu,getContentType as Xn,isJidGroup as la,isLidUser as du}from"@whiskeysockets/baileys";import pu from"node:fs";function mu(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 fu(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 ca(e){return[mu(e),fu(e)].filter(n=>n.trim().length>0).join(`
205
+ `).trim()}function po(e){return e.replace(/[\u0000-\u0008\u000B\u000C\u000E-\u001F\u007F]/g,"").replace(/\uFEFF/g,"").trim()}function ua(e){let t=Xn(e??void 0);return t==="imageMessage"||t==="videoMessage"||t==="audioMessage"||t==="documentMessage"||t==="stickerMessage"}function gu(e){let t=Xn(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 ia(e){try{if(!e)return"file.bin";let t=uu(e);return`file${t&&t.length>0?t.startsWith(".")?t:`.${t}`:".bin"}`}catch{return`${(Xn(e??void 0)??"file").replace("Message","")||"file"}.bin`}}function hu(e){let t=Xn(e??void 0);if(!t)return ia(e);let n=e?.[t];return n?.fileName&&typeof n.fileName=="string"&&n.fileName.trim()?n.fileName.trim():ia(e)}async function aa(e,t,n,r){let o=t.message??void 0;if(!ua(o))return{};let s=r.fileReceiveMaxBytes,a=gu(o);if(s>0&&a!==void 0&&a>s)return{error:`Media too large (max ${s} bytes).`};let i;try{i=await cu(t,"buffer",{},{logger:e.logger,reuploadRequest:e.updateMediaMessage})}catch(d){return I.warn({err:String(d),detail:Et(d)},"whatsapp downloadMediaMessage failed"),{error:`Could not download media from WhatsApp (${Et(d)}).`}}if(s>0&&i.length>s)return{error:`Media too large (max ${s} bytes).`};let l;try{l=yt(r,n)}catch(d){return{error:String(d)}}let u=hu(o),c=Vn(l,n,u);try{pu.writeFileSync(c,i,{mode:384})}catch{return{error:"Could not write media to inbox."}}return{path:c}}async function yu(e,t){let n=t.remoteJid??"";if(!n)return{fromJid:"",fromE164:""};if(t.remoteJidAlt){let r=j(t.remoteJidAlt)??j(n)??"";return{fromJid:n,fromE164:r}}if(du(n))try{let r=await e.signalRepository?.lidMapping?.getPNForLID?.(n);if(r){let o=j(r)??"";if(o)return{fromJid:n,fromE164:o}}}catch{}return{fromJid:n,fromE164:j(n)??""}}function wu(e){try{if(!x().clusterEnabled)return;let n=ca(e.message??void 0);if(!n)return;let r=ni(n);if(!r)return;let o=e.key?.remoteJid??"",s=null;if(o&&!la(o)){let a=j(o);a&&(s=`wa:${a}`)}di(r,s)}catch(t){I.warn({err:String(t)},"cluster footer observation failed")}}function da(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){wu(o);continue}let a=s.remoteJid;if(!a||la(a)||a.toLowerCase().endsWith("@status")||a==="status@broadcast")continue;let l=po(ca(o.message??void 0)),{fromJid:u,fromE164:c}=await yu(e,s),d=`wa:${u}`,m=ua(o.message??void 0);if(m&&!l){(async()=>{try{let b=x(),h=await aa(e,o,d,b);await t({fromJid:u,fromE164:c,text:"",messageId:s.id??void 0,mediaSavedPath:h.path,mediaError:h.error})}catch(b){I.error({err:String(b),fromJid:u},"whatsapp media-only background task failed")}})();continue}let f,y;if(m){let b=x(),h=await aa(e,o,d,b);f=h.path,y=h.error}!l&&!f&&!y||await t({fromJid:u,fromE164:c,text:l,messageId:s.id??void 0,mediaSavedPath:f,mediaError:y})}})()};return e.ev.on("messages.upsert",n),()=>{e.ev.off("messages.upsert",n)}}import Ru from"node:fs";import bu from"node:process";import ku,{DisconnectReason as ut,fetchLatestBaileysVersion as Su,makeCacheableSignalKeyStore as vu,useMultiFileAuthState as xu}from"@whiskeysockets/baileys";import $u from"qrcode-terminal";var Cu="0.1.0";function mo(e){return e?.error?.output?.statusCode}async function Zn(e={}){J();let t=e.authDir??D,n=e.verbose===!0,r=Oi(),{state:o,saveCreds:s}=await xu(t),{version:a}=await Su(),i=ku({version:a,logger:r,printQRInTerminal:!1,browser:["omnish","cli",Cu],auth:{creds:o.creds,keys:vu(o.keys,r)},syncFullHistory:!1,markOnlineOnConnect:!1});return i.ev.on("creds.update",s),i.ev.on("connection.update",l=>{let{connection:u,lastDisconnect:c,qr:d}=l;if(d&&(e.onQr?.(d),e.printQr)){let m=bu.stdout,f=w(m,"\xB7".repeat(42));console.log(re(m,"Scan with WhatsApp \u2192 Linked devices")),console.log(f),$u.generate(d,{small:!0}),console.log(f)}if(u==="close"){let m=mo(c);n&&m===ut.loggedOut&&r.warn("WhatsApp session logged out (401).")}u==="open"&&n&&r.info("WhatsApp Web connected.")}),i.ws&&typeof i.ws.on=="function"&&i.ws.on("error",l=>{r.error({err:String(l)},"WebSocket error")}),i}function Ot(e){try{e.end(new Error("omnish: socket closed"))}catch{e.ws?.close()}}function fo(e){return e?.output?.statusCode??e?.status??e?.error?.output?.statusCode}function er(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 a=o.lastDisconnect?.error??o.lastDisconnect??new Error("Connection closed");n(a)}};e.ev.on("connection.update",r)})}function dt(e,t,n){return new Promise((r,o)=>{let s=setTimeout(()=>o(new Error(n)),t);e.then(a=>{clearTimeout(s),r(a)},a=>{clearTimeout(s),o(a)})})}var ha=3500,Mu=400,pa=18e4,ma=9e4,fa=3e5;function go(e,t=ha){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 a=e.slice(r,o).lastIndexOf(`
202
206
 
203
- `);i>Math.floor(t*.35)&&(o=r+i+2)}n.push(e.slice(r,o)),r=o}return n}function er(e){return new Promise(t=>setTimeout(t,e))}async function ga(e,t){await Promise.race([e,er(pa).then(()=>{I.warn({jid:t,ms:pa},"whatsapp outbound self-heal: prior send chain waited too long; continuing")})])}async function Ru(e,t,n,r=3){let o;for(let s=1;s<=r;s+=1)try{await dt(e.sendMessage(t,{text:n}),ma,`whatsapp sendMessage timed out after ${ma}ms`);return}catch(i){o=i;let a=String(i);if(/not connected|closed|timed out|timeout/i.test(a)&&s<r){await er(800*s);continue}throw i}throw o}function Mu(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 Tu(e,t,n,r=3){let o;for(let s=1;s<=r;s+=1)try{await dt(e.sendMessage(t,n),fa,`whatsapp sendMedia timed out after ${fa}ms`);return}catch(i){o=i;let a=String(i);if(/not connected|closed|timed out|timeout/i.test(a)&&s<r){await er(800*s);continue}throw i}throw o}function ya(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=ga(a,o).then(async()=>{let u=fo(i,ha);for(let c=0;c<u.length;c+=1)await Ru(e,o,u[c]??""),c<u.length-1&&await er($u)}).catch(u=>{I.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=Cu.readFileSync(s.absPath),a=Mu(s,i),l=n.get(o)??Promise.resolve(),u=ga(l,o).then(async()=>{await Tu(e,o,a)}).catch(c=>{I.error({err:String(c),jid:o},"sendMedia failed")});n.set(o,u),await u.finally(()=>{n.get(o)===u&&n.delete(o)})}}}var Ou=400;async function wa(e,t,n,r){let o=t(),s=await sa(e,r.fileId,o.fileReceiveMaxBytes);if("error"in s)return{mediaError:s.error};let i;try{i=yt(o,n)}catch(l){return{mediaError:String(l)}}let a=Vn(i,n,r.baseName);try{return Iu.writeFileSync(a,s.buffer,{mode:384}),{mediaSavedPath:a}}catch{return{mediaError:"Could not write media to inbox."}}}function Lu(e){return new Promise(t=>setTimeout(t,e))}async function go(e,t,n,r={}){let o=new Au(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),f=Be(d,"telegram"),w=i(f.text,so(c)),b=f.parseModeHtml,E=(s.get(c)??Promise.resolve()).then(async()=>{let $=fo(w,m);for(let P=0;P<$.length;P+=1){let te=$[P]??"",ne=b?{parse_mode:"HTML"}:void 0;try{await o.api.sendMessage(c,te,ne)}catch(L){if(b){I.warn({err:String(L),chatId:c},"telegram HTML send failed; retrying plain");let fe=te.replace(/<[^>]+>/g,"");await o.api.sendMessage(c,fe)}else throw L}P<$.length-1&&await Lu(Ou)}}).catch($=>{I.error({err:String($),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 Eu(d.absPath,d.displayName),f=d.caption,b=(s.get(c)??Promise.resolve()).then(async()=>{switch(d.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(h=>{I.error({err:String(h),chatId:c},"telegram sendMedia failed")});s.set(c,b),await b.finally(()=>{s.get(c)===b&&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 f="text"in m&&m.text?m.text:"",w="caption"in m&&m.caption?m.caption:"",b=uo([f,w].filter(Boolean).join(`
204
- `)),h=ra(m),E=so(d.id),$=d.id,P=async L=>{L.kind==="file"?await l($,L.spec):await a($,L.body)};if(h&&!b){(async()=>{try{let L=await wa(o,t,E,h);if(!L.mediaSavedPath&&!L.mediaError)return;await n({peerKey:E,text:"",tgChatId:d.id,tgReplyToMessageId:m.message_id,mediaSavedPath:L.mediaSavedPath,mediaError:L.mediaError},P)}catch(L){I.error({err:String(L),chatId:$},"telegram media-only background task failed")}})();return}let te,ne;if(h){let L=await wa(o,t,E,h);te=L.mediaSavedPath,ne=L.mediaError}!b&&!te&&!ne||await n({peerKey:E,text:b,tgChatId:d.id,tgReplyToMessageId:m.message_id,mediaSavedPath:te,mediaError:ne},P)}),o.catch(c=>{I.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 Pu from"node:fs";import Nu from"node:path";function Fu(e){let t=Nu.join(e,"creds.json");try{let n=Pu.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 ba(e){let t=Fu(e);if(!t)return null;let n=t.phoneNumber?.trim();if(n){let s=j(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=j(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 nr from"node:fs";import Ce from"node:process";import ka from"node:fs";var _u="Timed out after 10 minutes. Phone stuck on \u201CLogging in\u201D usually means: run `pnpm approve-builds && pnpm install`, check network/firewall, then try `omnish link --force` again.";function tr(e){let t=String(e).toLowerCase();return t.includes("401")||t.includes("logged out")?!0:mo(e)===ut.loggedOut}function Bu(e){return new Promise((t,n)=>{let r=()=>{n(new Error("Pairing cancelled."))};if(e.aborted){r();return}e.addEventListener("abort",r,{once:!0})})}async function ho(e){let t=!1;for(let n=0;n<3;n++){if(e.signal?.aborted)throw new Error("Pairing cancelled.");let r=await Qn({authDir:e.authDir,printQr:e.printQr===!0,verbose:e.verbose===!0,onQr:e.onQr});e.onSocketReady?.(r);try{let o=dt(Zn(r),6e5,_u);e.signal?await Promise.race([o,Bu(e.signal)]):await o;return}catch(o){if(o instanceof Error&&o.message==="Pairing cancelled.")throw o;if(mo(o)===ut.restartRequired&&!t){t=!0,e.onRestart515?.(),await new Promise(i=>setTimeout(i,1500));continue}throw o}finally{e.onSocketClosed?.(),Ot(r)}}throw new Error("Pairing failed after restart (515) retries.")}async function Sa(e){let t=e.authDir??H;J();for(let n=1;n<=2;n++)try{await ho({...e,authDir:t});return}catch(r){if(r instanceof Error&&r.message==="Pairing cancelled.")throw r;if(n===1&&tr(r)){ka.rmSync(t,{recursive:!0,force:!0}),ka.mkdirSync(t,{recursive:!0,mode:448});continue}throw r}}async function Du(e,t){let n=Ce.stdout;console.log(`
205
- ${oe(n,"omnish link")} ${y(n,"\u2014 QR appears below; scan from WhatsApp \u2192 Linked devices.")}
206
- `),await ho({authDir:e,verbose:t,printQr:!0,onRestart515:()=>{console.warn(`
207
- ${le(Ce.stderr,"WhatsApp requested a restart after pairing (code 515). This is normal. Opening a new connection\u2026")}
208
- `)}})}async function xa(e={}){let t=e.authDir??H,n=e.verbose===!0;J(),e.force&&(nr.rmSync(t,{recursive:!0,force:!0}),nr.mkdirSync(t,{recursive:!0,mode:448}),console.log(`${we(Ce.stdout,"Cleared saved session (--force).")} ${y(Ce.stdout,"Requesting a new QR\u2026")}
209
- `));for(let r=1;r<=2;r++)try{await Du(t,n),console.log(`
210
- ${V(Ce.stdout,"Linked.")} ${k(Ce.stdout,"Session saved. You can run")} ${V(Ce.stdout,"omnish run")} ${k(Ce.stdout,"now.")}
211
- `);return}catch(o){if(r===1&&tr(o)){console.warn(`
212
- ${le(Ce.stderr,"WhatsApp returned logged-out (401). This often happens after Ctrl+C during link or corrupt auth files.")}
213
- ${le(Ce.stderr,"Clearing auth dir and retrying once with a fresh QR\u2026")}
214
- `),nr.rmSync(t,{recursive:!0,force:!0}),nr.mkdirSync(t,{recursive:!0,mode:448});continue}throw tr(o)&&console.error(`
215
- ${M(Ce.stderr,"Still failing after a clean auth directory. Try:")}
216
- ${y(Ce.stderr,` pnpm approve-builds && pnpm install
207
+ `);a>Math.floor(t*.35)&&(o=r+a+2)}n.push(e.slice(r,o)),r=o}return n}function tr(e){return new Promise(t=>setTimeout(t,e))}async function ga(e,t){await Promise.race([e,tr(pa).then(()=>{I.warn({jid:t,ms:pa},"whatsapp outbound self-heal: prior send chain waited too long; continuing")})])}async function Tu(e,t,n,r=3){let o;for(let s=1;s<=r;s+=1)try{await dt(e.sendMessage(t,{text:n}),ma,`whatsapp sendMessage timed out after ${ma}ms`);return}catch(a){o=a;let i=String(a);if(/not connected|closed|timed out|timeout/i.test(i)&&s<r){await tr(800*s);continue}throw a}throw o}function Iu(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 Au(e,t,n,r=3){let o;for(let s=1;s<=r;s+=1)try{await dt(e.sendMessage(t,n),fa,`whatsapp sendMedia timed out after ${fa}ms`);return}catch(a){o=a;let i=String(a);if(/not connected|closed|timed out|timeout/i.test(i)&&s<r){await tr(800*s);continue}throw a}throw o}function ya(e,t={}){let n=new Map,r=t.decorate??(o=>o);return{async sendText(o,s){let a=r(s,o),i=n.get(o)??Promise.resolve(),l=ga(i,o).then(async()=>{let u=go(a,ha);for(let c=0;c<u.length;c+=1)await Tu(e,o,u[c]??""),c<u.length-1&&await tr(Mu)}).catch(u=>{I.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 a=Ru.readFileSync(s.absPath),i=Iu(s,a),l=n.get(o)??Promise.resolve(),u=ga(l,o).then(async()=>{await Au(e,o,i)}).catch(c=>{I.error({err:String(c),jid:o},"sendMedia failed")});n.set(o,u),await u.finally(()=>{n.get(o)===u&&n.delete(o)})}}}var Pu=400;async function wa(e,t,n,r){let o=t(),s=await sa(e,r.fileId,o.fileReceiveMaxBytes);if("error"in s)return{mediaError:s.error};let a;try{a=yt(o,n)}catch(l){return{mediaError:String(l)}}let i=Vn(a,n,r.baseName);try{return Eu.writeFileSync(i,s.buffer,{mode:384}),{mediaSavedPath:i}}catch{return{mediaError:"Could not write media to inbox."}}}function Nu(e){return new Promise(t=>setTimeout(t,e))}async function ho(e,t,n,r={}){let o=new Ou(e);await o.api.deleteWebhook({drop_pending_updates:!1});let s=new Map,a=r.decorate??(c=>c);async function i(c,d){let m=Math.min(t().appsMaxWaChars,4096),f=Be(d,"telegram"),y=a(f.text,io(c)),b=f.parseModeHtml,E=(s.get(c)??Promise.resolve()).then(async()=>{let $=go(y,m);for(let P=0;P<$.length;P+=1){let te=$[P]??"",ne=b?{parse_mode:"HTML"}:void 0;try{await o.api.sendMessage(c,te,ne)}catch(L){if(b){I.warn({err:String(L),chatId:c},"telegram HTML send failed; retrying plain");let fe=te.replace(/<[^>]+>/g,"");await o.api.sendMessage(c,fe)}else throw L}P<$.length-1&&await Nu(Pu)}}).catch($=>{I.error({err:String($),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 Lu(d.absPath,d.displayName),f=d.caption,b=(s.get(c)??Promise.resolve()).then(async()=>{switch(d.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(h=>{I.error({err:String(h),chatId:c},"telegram sendMedia failed")});s.set(c,b),await b.finally(()=>{s.get(c)===b&&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 f="text"in m&&m.text?m.text:"",y="caption"in m&&m.caption?m.caption:"",b=po([f,y].filter(Boolean).join(`
208
+ `)),h=ra(m),E=io(d.id),$=d.id,P=async L=>{L.kind==="file"?await l($,L.spec):await i($,L.body)};if(h&&!b){(async()=>{try{let L=await wa(o,t,E,h);if(!L.mediaSavedPath&&!L.mediaError)return;await n({peerKey:E,text:"",tgChatId:d.id,tgReplyToMessageId:m.message_id,mediaSavedPath:L.mediaSavedPath,mediaError:L.mediaError},P)}catch(L){I.error({err:String(L),chatId:$},"telegram media-only background task failed")}})();return}let te,ne;if(h){let L=await wa(o,t,E,h);te=L.mediaSavedPath,ne=L.mediaError}!b&&!te&&!ne||await n({peerKey:E,text:b,tgChatId:d.id,tgReplyToMessageId:m.message_id,mediaSavedPath:te,mediaError:ne},P)}),o.catch(c=>{I.error({err:String(c)},"telegram bot error")});let u=o.start();return{bot:o,sendText:i,sendMedia:l,stop:async()=>{await o.stop(),await u.catch(()=>{})}}}import Fu from"node:fs";import _u from"node:path";function Bu(e){let t=_u.join(e,"creds.json");try{let n=Fu.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,a=typeof o.phoneNumber=="string"?o.phoneNumber:void 0;return!s&&!a?null:{id:s,phoneNumber:a}}catch{return null}}function ba(e){let t=Bu(e);if(!t)return null;let n=t.phoneNumber?.trim();if(n){let s=j(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=j(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 rr from"node:fs";import $e from"node:process";import ka from"node:fs";var Hu="Timed out after 10 minutes. Phone stuck on \u201CLogging in\u201D usually means: run `pnpm approve-builds && pnpm install`, check network/firewall, then try `omnish link --force` again.";function nr(e){let t=String(e).toLowerCase();return t.includes("401")||t.includes("logged out")?!0:fo(e)===ut.loggedOut}function Du(e){return new Promise((t,n)=>{let r=()=>{n(new Error("Pairing cancelled."))};if(e.aborted){r();return}e.addEventListener("abort",r,{once:!0})})}async function yo(e){let t=!1;for(let n=0;n<3;n++){if(e.signal?.aborted)throw new Error("Pairing cancelled.");let r=await Zn({authDir:e.authDir,printQr:e.printQr===!0,verbose:e.verbose===!0,onQr:e.onQr});e.onSocketReady?.(r);try{let o=dt(er(r),6e5,Hu);e.signal?await Promise.race([o,Du(e.signal)]):await o;return}catch(o){if(o instanceof Error&&o.message==="Pairing cancelled.")throw o;if(fo(o)===ut.restartRequired&&!t){t=!0,e.onRestart515?.(),await new Promise(a=>setTimeout(a,1500));continue}throw o}finally{e.onSocketClosed?.(),Ot(r)}}throw new Error("Pairing failed after restart (515) retries.")}async function Sa(e){let t=e.authDir??D;J();for(let n=1;n<=2;n++)try{await yo({...e,authDir:t});return}catch(r){if(r instanceof Error&&r.message==="Pairing cancelled.")throw r;if(n===1&&nr(r)){ka.rmSync(t,{recursive:!0,force:!0}),ka.mkdirSync(t,{recursive:!0,mode:448});continue}throw r}}async function ju(e,t){let n=$e.stdout;console.log(`
209
+ ${oe(n,"omnish link")} ${w(n,"\u2014 QR appears below; scan from WhatsApp \u2192 Linked devices.")}
210
+ `),await yo({authDir:e,verbose:t,printQr:!0,onRestart515:()=>{console.warn(`
211
+ ${le($e.stderr,"WhatsApp requested a restart after pairing (code 515). This is normal. Opening a new connection\u2026")}
212
+ `)}})}async function va(e={}){let t=e.authDir??D,n=e.verbose===!0;J(),e.force&&(rr.rmSync(t,{recursive:!0,force:!0}),rr.mkdirSync(t,{recursive:!0,mode:448}),console.log(`${we($e.stdout,"Cleared saved session (--force).")} ${w($e.stdout,"Requesting a new QR\u2026")}
213
+ `));for(let r=1;r<=2;r++)try{await ju(t,n),console.log(`
214
+ ${K($e.stdout,"Linked.")} ${k($e.stdout,"Session saved. You can run")} ${K($e.stdout,"omnish run")} ${k($e.stdout,"now.")}
215
+ `);return}catch(o){if(r===1&&nr(o)){console.warn(`
216
+ ${le($e.stderr,"WhatsApp returned logged-out (401). This often happens after Ctrl+C during link or corrupt auth files.")}
217
+ ${le($e.stderr,"Clearing auth dir and retrying once with a fresh QR\u2026")}
218
+ `),rr.rmSync(t,{recursive:!0,force:!0}),rr.mkdirSync(t,{recursive:!0,mode:448});continue}throw nr(o)&&console.error(`
219
+ ${M($e.stderr,"Still failing after a clean auth directory. Try:")}
220
+ ${w($e.stderr,` pnpm approve-builds && pnpm install
217
221
  (pnpm may have skipped Baileys/sharp/protobuf build scripts.)
218
222
  Then: omnish link --force
219
- `)}`),o}}import{spawn as Hu,spawnSync as ju}from"node:child_process";import Lt from"node:fs";import Uu from"node:path";import Pt from"node:process";function rr(e){J(),N(Uu.dirname(e));let t=Pt.argv[1];if(!t)return{ok:!1,message:"Cannot resolve entry script; invoke via node path/to/dist/index.js."};let n=Lt.openSync(e,"a"),r=Hu(Pt.execPath,[t,"run"],{detached:!0,stdio:["ignore",n,n],env:{...Pt.env,OMNISH_BACKGROUND_GATEWAY:"1"}});return Lt.closeSync(n),r.unref(),r.pid?{ok:!0,pid:r.pid}:{ok:!1,message:"Failed to start background gateway."}}function or(){if(J(),!Lt.existsSync(Z))return{outcome:"no_pidfile"};let e=Lt.readFileSync(Z,"utf8").trim(),t=Number(e);if(!Number.isFinite(t)||t<=0){try{Lt.unlinkSync(Z)}catch{}return{outcome:"invalid_pidfile"}}try{Pt.kill(t,0)}catch{try{Lt.unlinkSync(Z)}catch{}return{outcome:"stale_cleaned",pid:t}}try{return Pt.kill(t,"SIGTERM"),{outcome:"sent_signal",pid:t}}catch(n){return Pt.platform==="win32"&&ju("taskkill",["/PID",String(t),"/T"],{windowsHide:!0}).status===0?{outcome:"taskkill_ok",pid:t}:{outcome:"failed",message:`could not signal process: ${String(n)}`}}}import Wu from"node:crypto";import yo from"node:fs";import Gu from"node:net";var tn=null;function Ju(){try{let e=yo.readFileSync(ft,"utf8"),t=JSON.parse(e);return typeof t.token=="string"?t.token:""}catch{return""}}function zu(e){yo.writeFileSync(ft,JSON.stringify(e,null,2)+`
220
- `,{mode:384})}function Ku(){try{yo.unlinkSync(ft)}catch{}}async function Yu(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=Ke(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=dn(d);try{return await u.sendMedia(m,l),{ok:!0}}catch(f){return{ok:!1,error:String(f)}}}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 va(e){if(tn)return;let t=Wu.randomBytes(32).toString("hex"),n=Gu.createServer(r=>{let o="";r.setTimeout(12e4),r.on("data",s=>{o+=s.toString("utf8");let i=o.indexOf(`
221
- `);if(i===-1)return;let a=o.slice(0,i).trim();o=o.slice(i+1);let l=Ju();Yu(a,e,l).then(u=>{r.write(`${JSON.stringify(u)}
222
- `),r.end()})}),r.on("error",()=>{})});n.listen(0,"127.0.0.1",()=>{let r=n.address();if(!r||typeof r=="string"){I.error("gateway control: could not read listen address");return}let o={token:t,host:r.address,port:r.port};zu(o),I.info({port:r.port},"gateway control listening")}),n.on("error",r=>{I.error({err:String(r)},"gateway control server error")}),tn=n}function sr(){if(tn){try{tn.close()}catch{}tn=null,Ku()}}function ir(){let e=v(),t=e.gatewayMode,n=t==="whatsapp"||t==="both",r=t==="telegram"||t==="both",o=ee(e);if(r&&!o)return{ok:!1,message:`Telegram enabled (gatewayMode) but no bot token. Set telegramBotToken in config or TELEGRAM_BOT_TOKEN.
223
- config: ${T}`};if(n&&!Te())return{ok:!1,message:"WhatsApp enabled but no session. Run `omnish link` first."};let s=Xe(e);return Mn(s)?{ok:!1,message:`Fix security errors before starting the gateway (or change gatewayMode / token).
223
+ `)}`),o}}import{spawn as Uu,spawnSync as Wu}from"node:child_process";import Lt from"node:fs";import Gu from"node:path";import Pt from"node:process";function or(e){J(),N(Gu.dirname(e));let t=Pt.argv[1];if(!t)return{ok:!1,message:"Cannot resolve entry script; invoke via node path/to/dist/index.js."};let n=Lt.openSync(e,"a"),r=Uu(Pt.execPath,[t,"run"],{detached:!0,stdio:["ignore",n,n],env:{...Pt.env,OMNISH_BACKGROUND_GATEWAY:"1"}});return Lt.closeSync(n),r.unref(),r.pid?{ok:!0,pid:r.pid}:{ok:!1,message:"Failed to start background gateway."}}function sr(){if(J(),!Lt.existsSync(Z))return{outcome:"no_pidfile"};let e=Lt.readFileSync(Z,"utf8").trim(),t=Number(e);if(!Number.isFinite(t)||t<=0){try{Lt.unlinkSync(Z)}catch{}return{outcome:"invalid_pidfile"}}try{Pt.kill(t,0)}catch{try{Lt.unlinkSync(Z)}catch{}return{outcome:"stale_cleaned",pid:t}}try{return Pt.kill(t,"SIGTERM"),{outcome:"sent_signal",pid:t}}catch(n){return Pt.platform==="win32"&&Wu("taskkill",["/PID",String(t),"/T"],{windowsHide:!0}).status===0?{outcome:"taskkill_ok",pid:t}:{outcome:"failed",message:`could not signal process: ${String(n)}`}}}import Ju from"node:crypto";import wo from"node:fs";import zu from"node:net";var tn=null;function qu(){try{let e=wo.readFileSync(ft,"utf8"),t=JSON.parse(e);return typeof t.token=="string"?t.token:""}catch{return""}}function Qu(e){wo.writeFileSync(ft,JSON.stringify(e,null,2)+`
224
+ `,{mode:384})}function Yu(){try{wo.unlinkSync(ft)}catch{}}async function Ku(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 a=typeof r.caption=="string"&&r.caption.length>0?r.caption:void 0,i=qe(s,o.fileSendMaxBytes);if("error"in i)return{ok:!1,error:i.error};let l={absPath:i.absPath,category:i.category,mimetype:i.mimetype,displayName:i.displayName,caption:a};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=dn(d);try{return await u.sendMedia(m,l),{ok:!0}}catch(f){return{ok:!1,error:String(f)}}}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 xa(e){if(tn)return;let t=Ju.randomBytes(32).toString("hex"),n=zu.createServer(r=>{let o="";r.setTimeout(12e4),r.on("data",s=>{o+=s.toString("utf8");let a=o.indexOf(`
225
+ `);if(a===-1)return;let i=o.slice(0,a).trim();o=o.slice(a+1);let l=qu();Ku(i,e,l).then(u=>{r.write(`${JSON.stringify(u)}
226
+ `),r.end()})}),r.on("error",()=>{})});n.listen(0,"127.0.0.1",()=>{let r=n.address();if(!r||typeof r=="string"){I.error("gateway control: could not read listen address");return}let o={token:t,host:r.address,port:r.port};Qu(o),I.info({port:r.port},"gateway control listening")}),n.on("error",r=>{I.error({err:String(r)},"gateway control server error")}),tn=n}function ir(){if(tn){try{tn.close()}catch{}tn=null,Yu()}}function ar(){let e=x(),t=e.gatewayMode,n=t==="whatsapp"||t==="both",r=t==="telegram"||t==="both",o=ee(e);if(r&&!o)return{ok:!1,message:`Telegram enabled (gatewayMode) but no bot token. Set telegramBotToken in config or TELEGRAM_BOT_TOKEN.
227
+ config: ${T}`};if(n&&!Te())return{ok:!1,message:"WhatsApp enabled but no session. Run `omnish link` first."};let s=Ve(e);return Mn(s)?{ok:!1,message:`Fix security errors before starting the gateway (or change gatewayMode / token).
224
228
 
225
229
  ${s.filter(l=>l.severity==="error").map(l=>{let u=[l.message];return l.detail&&u.push(l.detail),l.fixHint&&u.push(`Fix: ${l.fixHint}`),u.join(`
226
230
  `)}).join(`
227
231
 
228
- `)}`}:{ok:!0}}import Ra from"node:crypto";import pt from"node:fs";import Ma from"node:path";function qu(){return Ra.randomBytes(24).toString("hex")}function Ca(){return Ra.randomBytes(32).toString("hex")}function $a(e){try{let t=pt.readFileSync(e,"utf8"),n=JSON.parse(t);if(typeof n.token=="string"&&n.token.length>=16&&typeof n.secret=="string"&&n.secret.length>=16)return{token:n.token,secret:n.secret}}catch{}return null}function Vu(){let e=$a(Ne);if(e)return e;if(e=$a(Ft),e){N(Ma.dirname(Ne)),pt.writeFileSync(Ne,JSON.stringify(e,null,2)+`
229
- `,{mode:384});try{pt.unlinkSync(Ft)}catch{}return e}return null}function Ta(e){N(Ma.dirname(Ne));let t=Vu(),n=(e??"").trim();if(n){let o={token:n,secret:t?.secret??Ca()};pt.writeFileSync(Ne,JSON.stringify(o,null,2)+`
230
- `,{mode:384});try{pt.existsSync(Ft)&&pt.unlinkSync(Ft)}catch{}return o}if(t)return t;let r={token:qu(),secret:Ca()};return pt.writeFileSync(Ne,JSON.stringify(r,null,2)+`
231
- `,{mode:384}),r}import nt from"node:fs";import nd from"node:http";import ie from"node:path";import Ee from"node:process";import{fileURLToPath as rd}from"node:url";import od from"node:os";import wo from"node:crypto";var bo="omnish_cfg_sess",Ia=7*24*60*60*1e3;function ko(e){let t=Date.now()+Ia,n=Buffer.from(JSON.stringify({exp:t}),"utf8").toString("base64url"),r=wo.createHmac("sha256",e).update(n).digest("hex");return`${n}.${r}`}function Aa(e,t){let n=Xu(t??"")[bo];if(!n||!n.includes("."))return!1;let r=n.lastIndexOf("."),o=n.slice(0,r),s=n.slice(r+1),i=wo.createHmac("sha256",e).update(o).digest("hex");try{let a=Buffer.from(s,"hex"),l=Buffer.from(i,"hex");if(a.length!==l.length||!wo.timingSafeEqual(a,l))return!1}catch{return!1}try{let a=JSON.parse(Buffer.from(o,"base64url").toString("utf8"));return!(typeof a.exp!="number"||a.exp<Date.now())}catch{return!1}}function So(e){let t=Math.floor(Ia/1e3);return`${bo}=${e}; HttpOnly; Path=/; SameSite=Lax; Max-Age=${t}`}function Ea(){return`${bo}=; HttpOnly; Path=/; SameSite=Lax; Max-Age=0`}function Xu(e){let t={};for(let n of e.split(";")){let r=n.indexOf("=");if(r===-1)continue;let o=n.slice(0,r).trim(),s=n.slice(r+1).trim();o&&(t[o]=decodeURIComponent(s))}return t}import xo from"node:fs";import ar from"node:process";function Oa(e){try{return ar.kill(e,0),!0}catch{return!1}}function La(e){let t=Date.now()+e;for(;Date.now()<t;);}function Qu(){try{let e=xo.readFileSync(an,"utf8"),t=JSON.parse(e);if(!t||typeof t!="object")return null;let n=t,r=typeof n.pid=="number"?n.pid:Number(n.pid),o=typeof n.port=="number"?n.port:Number(n.port),s=typeof n.host=="string"?n.host:"",i=typeof n.startedAt=="string"?n.startedAt:"";return!Number.isFinite(r)||r<=0||!Number.isFinite(o)||o<=0||o>65535?null:{pid:r,port:o,host:s,startedAt:i}}catch{return null}}function Pa(e){xo.writeFileSync(an,`${JSON.stringify(e)}
232
- `,{mode:384})}function nn(){try{xo.unlinkSync(an)}catch{}}function Na(e){let t=Qu();if(t&&t.port===e&&t.pid!==ar.pid){if(!Oa(t.pid)){nn();return}try{ar.kill(t.pid,"SIGTERM")}catch{}if(La(350),Oa(t.pid)){try{ar.kill(t.pid,"SIGKILL")}catch{}La(100)}nn()}}import vo from"node:fs";import Zu from"node:process";function ed(e){try{return Zu.kill(e,0),!0}catch{return!1}}function td(){try{let e=vo.readFileSync(Z,"utf8").trim(),t=Number.parseInt(e,10);return Number.isFinite(t)&&t>0?t:null}catch{return null}}function lr(){let e=td();return e===null?!1:ed(e)}var Co=class{busy=!1;subscribers=new Set;abort=null;currentSock=null;subscribe(t){return this.subscribers.add(t),()=>{this.subscribers.delete(t)}}emit(t){for(let n of this.subscribers)try{n(t)}catch{}}requestCancel(){this.abort?.abort(),this.currentSock&&(Ot(this.currentSock),this.currentSock=null)}beginPairing(t){if(this.busy)throw new Error("Pairing already in progress.");if(lr())throw new Error("Gateway appears to be running (gateway.pid). Stop `omnish run` or your service before pairing from the browser.");if(!t.force&&Te())throw new Error("WhatsApp session already linked. Use \u201CReplace session\u201D or run `omnish link --force` from the CLI.");J(),t.force&&(vo.rmSync(H,{recursive:!0,force:!0}),vo.mkdirSync(H,{recursive:!0,mode:448})),this.busy=!0,this.abort=new AbortController;let n=this.abort.signal;(async()=>{try{await Sa({authDir:H,verbose:!1,printQr:!1,signal:n,onQr:r=>this.emit({type:"qr",payload:r}),onRestart515:()=>this.emit({type:"restart"}),onSocketReady:r=>{this.currentSock=r},onSocketClosed:()=>{this.currentSock=null}}),this.emit({type:"open"}),this.emit({type:"done",ok:!0})}catch(r){let o=r instanceof Error?r.message:String(r);o==="Pairing cancelled."?this.emit({type:"error",message:"Cancelled."}):this.emit({type:"error",message:o}),this.emit({type:"done",ok:!1})}finally{this.busy=!1,this.abort=null,this.currentSock=null}})()}},rn=new Co;var U="application/json; charset=utf-8",on=null;function sd(){nn();let e=on;on=null,e?e.close(()=>{Ee.exit(0)}):Ee.exit(0),setTimeout(()=>Ee.exit(0),4e3).unref()}function id(){let e=Ee.env.OMNISH_CONFIG_UI_STATIC?.trim(),t=Ee.env.OMNISH_UI_STATIC?.trim()||e;if(t&&nt.existsSync(ie.join(t,"index.html")))return t;let n=ie.dirname(rd(import.meta.url)),r=ie.join(n,"ui");if(nt.existsSync(ie.join(r,"index.html")))return r;let o=ie.join(n,"..","..","dist","ui");if(nt.existsSync(ie.join(o,"index.html")))return o;let s=ie.join(Ee.cwd(),"dist","ui");if(nt.existsSync(ie.join(s,"index.html")))return s;throw new Error("omnish ui static files not found (expected dist/ui/index.html). Run `pnpm build`.")}function ad(e){let t=ie.extname(e).toLowerCase();return{".html":"text/html; charset=utf-8",".js":"text/javascript; charset=utf-8",".css":"text/css; charset=utf-8",".svg":"image/svg+xml",".png":"image/png",".jpg":"image/jpeg",".jpeg":"image/jpeg",".webp":"image/webp",".ico":"image/x-icon",".woff2":"font/woff2",".json":"application/json; charset=utf-8"}[t]??"application/octet-stream"}function ld(e,t){let r=decodeURIComponent(t.split("?")[0]??"").replace(/^\/+/,""),o=ie.normalize(ie.join(e,r));return!o.startsWith(ie.normalize(e+ie.sep))&&o!==ie.normalize(e)?null:o}async function $o(e,t=512e3){let n=[],r=0;for await(let s of e){if(r+=s.length,r>t)throw new Error("Body too large.");n.push(s)}if(n.length===0)return;let o=Buffer.concat(n).toString("utf8");return JSON.parse(o)}function cd(e){return xt.includes(e)}function Ro(e){let t=(e.telegramBotToken??"").trim(),n=t.length===0?"":t.length<=8?"(set)":`${t.slice(0,4)}\u2026${t.slice(-4)}`;return{...e,telegramBotToken:n,telegramBotTokenConfigured:!!ee(e),telegramBotTokenEnvOverride:typeof Ee.env.TELEGRAM_BOT_TOKEN=="string"&&Ee.env.TELEGRAM_BOT_TOKEN.trim().length>0}}function ud(){let e=v();return{version:De(),dataDir:A,configPath:T,waAuthDir:H,whatsappLinked:Te(),gatewayPidHint:nt.existsSync(ie.join(A,"gateway.pid")),gatewayRunning:lr(),gatewayLogFile:de,gatewayMode:e.gatewayMode,telegramBotTokenMasked:ee(e).length===0?"":Ro(e).telegramBotToken,telegramBotTokenEnvOverride:typeof Ee.env.TELEGRAM_BOT_TOKEN=="string"&&Ee.env.TELEGRAM_BOT_TOKEN.trim().length>0}}function dd(e,t){if(!Array.isArray(e)||!Array.isArray(t))throw new Error("allowFrom and telegramAllowFrom must be arrays of strings.");let n=e.map(a=>String(a).trim()).filter(Boolean),r=t.map(a=>String(a).trim()).filter(Boolean),o=pn(n).filter(a=>a!=="*"),s=[];for(let a of r){let l=ae(a);if(!l)throw new Error(`Invalid Telegram allow entry: ${a}`);s.push(l)}let i=v();i.allowFrom=o.sort(),i.telegramAllowFrom=[...new Set(s)].sort(),_e(i)}function pd(e){return typeof e=="string"?e:typeof e=="boolean"||typeof e=="number"?String(e):JSON.stringify(e)}async function Fa(e){let t=id(),{meta:n}=e;Na(e.port);let r=nd.createServer(async(o,s)=>{try{let i=new URL(o.url??"/",`http://${o.headers.host??"localhost"}`),a=i.pathname;if(o.method==="GET"&&a==="/"){let m=i.searchParams.get("token")?.trim();if(m&&m===n.token){let f=ko(n.secret);s.statusCode=302,s.setHeader("Location","/"),s.setHeader("Set-Cookie",So(f)),s.end();return}}if(a.startsWith("/api/")){let m=o.headers.cookie,f=Aa(n.secret,m);if(o.method==="POST"&&a==="/api/session"){let w=await $o(o),b=typeof w?.token=="string"?w.token.trim():"";if(!b||b!==n.token){s.statusCode=401,s.setHeader("Content-Type",U),s.end(JSON.stringify({ok:!1,error:"Invalid token."}));return}let h=ko(n.secret);s.statusCode=200,s.setHeader("Content-Type",U),s.setHeader("Set-Cookie",So(h)),s.end(JSON.stringify({ok:!0}));return}if(o.method==="GET"&&a==="/api/me"){s.statusCode=f?200:401,s.setHeader("Content-Type",U),s.end(JSON.stringify({ok:f}));return}if(!f){s.statusCode=401,s.setHeader("Content-Type",U),s.end(JSON.stringify({ok:!1,error:"Unauthorized."}));return}if(o.method==="GET"&&a==="/api/status"){s.statusCode=200,s.setHeader("Content-Type",U),s.end(JSON.stringify({ok:!0,...ud()}));return}if(o.method==="GET"&&a==="/api/config"){s.statusCode=200,s.setHeader("Content-Type",U),s.end(JSON.stringify({ok:!0,config:Ro(v())}));return}if(o.method==="PUT"&&a==="/api/config"){let w=await $o(o);if(!w||typeof w!="object"){s.statusCode=400,s.setHeader("Content-Type",U),s.end(JSON.stringify({ok:!1,error:"Expected JSON object."}));return}let b=w;for(let[h,E]of Object.entries(b))if(E!==void 0&&!(h==="allowFrom"||h==="telegramAllowFrom")){if(h==="telegramBotToken"){let $=typeof E=="string"?E.trim():"";if(!$||$==="(set)"||$.includes("\u2026"))continue;if(!ze($))throw new Error("Invalid Telegram bot token format.");_n("telegramBotToken",$);continue}if(!cd(h))throw new Error(`Unknown or unsupported config key: ${h}`);_n(h,pd(E))}if("allowFrom"in b||"telegramAllowFrom"in b){let h=v();dd("allowFrom"in b?b.allowFrom:h.allowFrom,"telegramAllowFrom"in b?b.telegramAllowFrom:h.telegramAllowFrom)}s.statusCode=200,s.setHeader("Content-Type",U),s.end(JSON.stringify({ok:!0,config:Ro(v())}));return}if(o.method==="POST"&&a==="/api/logout"){s.statusCode=200,s.setHeader("Content-Type",U),s.setHeader("Set-Cookie",Ea()),s.end(JSON.stringify({ok:!0}));return}if(o.method==="POST"&&a==="/api/shutdown"){s.statusCode=200,s.setHeader("Content-Type",U),s.end(JSON.stringify({ok:!0})),setImmediate(()=>{rn.requestCancel(),sd()});return}if(o.method==="POST"&&a==="/api/gateway/start"){if(lr()){s.statusCode=409,s.setHeader("Content-Type",U),s.end(JSON.stringify({ok:!1,error:"Gateway already appears to be running (pidfile + live process). Stop it first if you need to restart."}));return}let w=ir();if(!w.ok){s.statusCode=400,s.setHeader("Content-Type",U),s.end(JSON.stringify({ok:!1,error:w.message}));return}let b=de,h=rr(b);if(!h.ok){s.statusCode=500,s.setHeader("Content-Type",U),s.end(JSON.stringify({ok:!1,error:h.message}));return}s.statusCode=200,s.setHeader("Content-Type",U),s.end(JSON.stringify({ok:!0,pid:h.pid,logFile:b}));return}if(o.method==="POST"&&a==="/api/gateway/stop"){let w=or();switch(w.outcome){case"no_pidfile":s.statusCode=400,s.setHeader("Content-Type",U),s.end(JSON.stringify({ok:!1,error:"No gateway pidfile \u2014 background gateway does not appear to be running."}));return;case"invalid_pidfile":s.statusCode=400,s.setHeader("Content-Type",U),s.end(JSON.stringify({ok:!1,error:"Invalid pidfile (removed)."}));return;case"stale_cleaned":s.statusCode=200,s.setHeader("Content-Type",U),s.end(JSON.stringify({ok:!0,stale:!0,pid:w.pid,message:"Process was not running; stale pidfile removed."}));return;case"sent_signal":s.statusCode=200,s.setHeader("Content-Type",U),s.end(JSON.stringify({ok:!0,pid:w.pid}));return;case"taskkill_ok":s.statusCode=200,s.setHeader("Content-Type",U),s.end(JSON.stringify({ok:!0,pid:w.pid,taskkill:!0}));return;case"failed":s.statusCode=500,s.setHeader("Content-Type",U),s.end(JSON.stringify({ok:!1,error:w.message}));return}}if(o.method==="GET"&&a==="/api/wa/link/events"){s.writeHead(200,{"Content-Type":"text/event-stream; charset=utf-8","Cache-Control":"no-store",Connection:"keep-alive"}),s.write(`: connected
232
+ `)}`}:{ok:!0}}import Ra from"node:crypto";import pt from"node:fs";import Ma from"node:path";function Vu(){return Ra.randomBytes(24).toString("hex")}function $a(){return Ra.randomBytes(32).toString("hex")}function Ca(e){try{let t=pt.readFileSync(e,"utf8"),n=JSON.parse(t);if(typeof n.token=="string"&&n.token.length>=16&&typeof n.secret=="string"&&n.secret.length>=16)return{token:n.token,secret:n.secret}}catch{}return null}function Xu(){let e=Ca(Ne);if(e)return e;if(e=Ca(Ft),e){N(Ma.dirname(Ne)),pt.writeFileSync(Ne,JSON.stringify(e,null,2)+`
233
+ `,{mode:384});try{pt.unlinkSync(Ft)}catch{}return e}return null}function Ta(e){N(Ma.dirname(Ne));let t=Xu(),n=(e??"").trim();if(n){let o={token:n,secret:t?.secret??$a()};pt.writeFileSync(Ne,JSON.stringify(o,null,2)+`
234
+ `,{mode:384});try{pt.existsSync(Ft)&&pt.unlinkSync(Ft)}catch{}return o}if(t)return t;let r={token:Vu(),secret:$a()};return pt.writeFileSync(Ne,JSON.stringify(r,null,2)+`
235
+ `,{mode:384}),r}import nt from"node:fs";import od from"node:http";import ie from"node:path";import Ee from"node:process";import{fileURLToPath as sd}from"node:url";import id from"node:os";import bo from"node:crypto";var ko="omnish_cfg_sess",Ia=7*24*60*60*1e3;function So(e){let t=Date.now()+Ia,n=Buffer.from(JSON.stringify({exp:t}),"utf8").toString("base64url"),r=bo.createHmac("sha256",e).update(n).digest("hex");return`${n}.${r}`}function Aa(e,t){let n=Zu(t??"")[ko];if(!n||!n.includes("."))return!1;let r=n.lastIndexOf("."),o=n.slice(0,r),s=n.slice(r+1),a=bo.createHmac("sha256",e).update(o).digest("hex");try{let i=Buffer.from(s,"hex"),l=Buffer.from(a,"hex");if(i.length!==l.length||!bo.timingSafeEqual(i,l))return!1}catch{return!1}try{let i=JSON.parse(Buffer.from(o,"base64url").toString("utf8"));return!(typeof i.exp!="number"||i.exp<Date.now())}catch{return!1}}function vo(e){let t=Math.floor(Ia/1e3);return`${ko}=${e}; HttpOnly; Path=/; SameSite=Lax; Max-Age=${t}`}function Ea(){return`${ko}=; HttpOnly; Path=/; SameSite=Lax; Max-Age=0`}function Zu(e){let t={};for(let n of e.split(";")){let r=n.indexOf("=");if(r===-1)continue;let o=n.slice(0,r).trim(),s=n.slice(r+1).trim();o&&(t[o]=decodeURIComponent(s))}return t}import xo from"node:fs";import lr from"node:process";function Oa(e){try{return lr.kill(e,0),!0}catch{return!1}}function La(e){let t=Date.now()+e;for(;Date.now()<t;);}function ed(){try{let e=xo.readFileSync(an,"utf8"),t=JSON.parse(e);if(!t||typeof t!="object")return null;let n=t,r=typeof n.pid=="number"?n.pid:Number(n.pid),o=typeof n.port=="number"?n.port:Number(n.port),s=typeof n.host=="string"?n.host:"",a=typeof n.startedAt=="string"?n.startedAt:"";return!Number.isFinite(r)||r<=0||!Number.isFinite(o)||o<=0||o>65535?null:{pid:r,port:o,host:s,startedAt:a}}catch{return null}}function Pa(e){xo.writeFileSync(an,`${JSON.stringify(e)}
236
+ `,{mode:384})}function nn(){try{xo.unlinkSync(an)}catch{}}function Na(e){let t=ed();if(t&&t.port===e&&t.pid!==lr.pid){if(!Oa(t.pid)){nn();return}try{lr.kill(t.pid,"SIGTERM")}catch{}if(La(350),Oa(t.pid)){try{lr.kill(t.pid,"SIGKILL")}catch{}La(100)}nn()}}import $o from"node:fs";import td from"node:process";function nd(e){try{return td.kill(e,0),!0}catch{return!1}}function rd(){try{let e=$o.readFileSync(Z,"utf8").trim(),t=Number.parseInt(e,10);return Number.isFinite(t)&&t>0?t:null}catch{return null}}function cr(){let e=rd();return e===null?!1:nd(e)}var Co=class{busy=!1;subscribers=new Set;abort=null;currentSock=null;subscribe(t){return this.subscribers.add(t),()=>{this.subscribers.delete(t)}}emit(t){for(let n of this.subscribers)try{n(t)}catch{}}requestCancel(){this.abort?.abort(),this.currentSock&&(Ot(this.currentSock),this.currentSock=null)}beginPairing(t){if(this.busy)throw new Error("Pairing already in progress.");if(cr())throw new Error("Gateway appears to be running (gateway.pid). Stop `omnish run` or your service before pairing from the browser.");if(!t.force&&Te())throw new Error("WhatsApp session already linked. Use \u201CReplace session\u201D or run `omnish link --force` from the CLI.");J(),t.force&&($o.rmSync(D,{recursive:!0,force:!0}),$o.mkdirSync(D,{recursive:!0,mode:448})),this.busy=!0,this.abort=new AbortController;let n=this.abort.signal;(async()=>{try{await Sa({authDir:D,verbose:!1,printQr:!1,signal:n,onQr:r=>this.emit({type:"qr",payload:r}),onRestart515:()=>this.emit({type:"restart"}),onSocketReady:r=>{this.currentSock=r},onSocketClosed:()=>{this.currentSock=null}}),this.emit({type:"open"}),this.emit({type:"done",ok:!0})}catch(r){let o=r instanceof Error?r.message:String(r);o==="Pairing cancelled."?this.emit({type:"error",message:"Cancelled."}):this.emit({type:"error",message:o}),this.emit({type:"done",ok:!1})}finally{this.busy=!1,this.abort=null,this.currentSock=null}})()}},rn=new Co;var U="application/json; charset=utf-8",on=null;function ad(){nn();let e=on;on=null,e?e.close(()=>{Ee.exit(0)}):Ee.exit(0),setTimeout(()=>Ee.exit(0),4e3).unref()}function ld(){let e=Ee.env.OMNISH_CONFIG_UI_STATIC?.trim(),t=Ee.env.OMNISH_UI_STATIC?.trim()||e;if(t&&nt.existsSync(ie.join(t,"index.html")))return t;let n=ie.dirname(sd(import.meta.url)),r=ie.join(n,"ui");if(nt.existsSync(ie.join(r,"index.html")))return r;let o=ie.join(n,"..","..","dist","ui");if(nt.existsSync(ie.join(o,"index.html")))return o;let s=ie.join(Ee.cwd(),"dist","ui");if(nt.existsSync(ie.join(s,"index.html")))return s;throw new Error("omnish ui static files not found (expected dist/ui/index.html). Run `pnpm build`.")}function cd(e){let t=ie.extname(e).toLowerCase();return{".html":"text/html; charset=utf-8",".js":"text/javascript; charset=utf-8",".css":"text/css; charset=utf-8",".svg":"image/svg+xml",".png":"image/png",".jpg":"image/jpeg",".jpeg":"image/jpeg",".webp":"image/webp",".ico":"image/x-icon",".woff2":"font/woff2",".json":"application/json; charset=utf-8"}[t]??"application/octet-stream"}function ud(e,t){let r=decodeURIComponent(t.split("?")[0]??"").replace(/^\/+/,""),o=ie.normalize(ie.join(e,r));return!o.startsWith(ie.normalize(e+ie.sep))&&o!==ie.normalize(e)?null:o}async function Ro(e,t=512e3){let n=[],r=0;for await(let s of e){if(r+=s.length,r>t)throw new Error("Body too large.");n.push(s)}if(n.length===0)return;let o=Buffer.concat(n).toString("utf8");return JSON.parse(o)}function dd(e){return vt.includes(e)}function Mo(e){let t=(e.telegramBotToken??"").trim(),n=t.length===0?"":t.length<=8?"(set)":`${t.slice(0,4)}\u2026${t.slice(-4)}`;return{...e,telegramBotToken:n,telegramBotTokenConfigured:!!ee(e),telegramBotTokenEnvOverride:typeof Ee.env.TELEGRAM_BOT_TOKEN=="string"&&Ee.env.TELEGRAM_BOT_TOKEN.trim().length>0}}function pd(){let e=x();return{version:He(),dataDir:A,configPath:T,waAuthDir:D,whatsappLinked:Te(),gatewayPidHint:nt.existsSync(ie.join(A,"gateway.pid")),gatewayRunning:cr(),gatewayLogFile:de,gatewayMode:e.gatewayMode,telegramBotTokenMasked:ee(e).length===0?"":Mo(e).telegramBotToken,telegramBotTokenEnvOverride:typeof Ee.env.TELEGRAM_BOT_TOKEN=="string"&&Ee.env.TELEGRAM_BOT_TOKEN.trim().length>0}}function md(e,t){if(!Array.isArray(e)||!Array.isArray(t))throw new Error("allowFrom and telegramAllowFrom must be arrays of strings.");let n=e.map(i=>String(i).trim()).filter(Boolean),r=t.map(i=>String(i).trim()).filter(Boolean),o=pn(n).filter(i=>i!=="*"),s=[];for(let i of r){let l=ae(i);if(!l)throw new Error(`Invalid Telegram allow entry: ${i}`);s.push(l)}let a=x();a.allowFrom=o.sort(),a.telegramAllowFrom=[...new Set(s)].sort(),_e(a)}function fd(e){return typeof e=="string"?e:typeof e=="boolean"||typeof e=="number"?String(e):JSON.stringify(e)}async function Fa(e){let t=ld(),{meta:n}=e;Na(e.port);let r=od.createServer(async(o,s)=>{try{let a=new URL(o.url??"/",`http://${o.headers.host??"localhost"}`),i=a.pathname;if(o.method==="GET"&&i==="/"){let m=a.searchParams.get("token")?.trim();if(m&&m===n.token){let f=So(n.secret);s.statusCode=302,s.setHeader("Location","/"),s.setHeader("Set-Cookie",vo(f)),s.end();return}}if(i.startsWith("/api/")){let m=o.headers.cookie,f=Aa(n.secret,m);if(o.method==="POST"&&i==="/api/session"){let y=await Ro(o),b=typeof y?.token=="string"?y.token.trim():"";if(!b||b!==n.token){s.statusCode=401,s.setHeader("Content-Type",U),s.end(JSON.stringify({ok:!1,error:"Invalid token."}));return}let h=So(n.secret);s.statusCode=200,s.setHeader("Content-Type",U),s.setHeader("Set-Cookie",vo(h)),s.end(JSON.stringify({ok:!0}));return}if(o.method==="GET"&&i==="/api/me"){s.statusCode=f?200:401,s.setHeader("Content-Type",U),s.end(JSON.stringify({ok:f}));return}if(!f){s.statusCode=401,s.setHeader("Content-Type",U),s.end(JSON.stringify({ok:!1,error:"Unauthorized."}));return}if(o.method==="GET"&&i==="/api/status"){s.statusCode=200,s.setHeader("Content-Type",U),s.end(JSON.stringify({ok:!0,...pd()}));return}if(o.method==="GET"&&i==="/api/config"){s.statusCode=200,s.setHeader("Content-Type",U),s.end(JSON.stringify({ok:!0,config:Mo(x())}));return}if(o.method==="PUT"&&i==="/api/config"){let y=await Ro(o);if(!y||typeof y!="object"){s.statusCode=400,s.setHeader("Content-Type",U),s.end(JSON.stringify({ok:!1,error:"Expected JSON object."}));return}let b=y;for(let[h,E]of Object.entries(b))if(E!==void 0&&!(h==="allowFrom"||h==="telegramAllowFrom")){if(h==="telegramBotToken"){let $=typeof E=="string"?E.trim():"";if(!$||$==="(set)"||$.includes("\u2026"))continue;if(!ze($))throw new Error("Invalid Telegram bot token format.");Bn("telegramBotToken",$);continue}if(!dd(h))throw new Error(`Unknown or unsupported config key: ${h}`);Bn(h,fd(E))}if("allowFrom"in b||"telegramAllowFrom"in b){let h=x();md("allowFrom"in b?b.allowFrom:h.allowFrom,"telegramAllowFrom"in b?b.telegramAllowFrom:h.telegramAllowFrom)}s.statusCode=200,s.setHeader("Content-Type",U),s.end(JSON.stringify({ok:!0,config:Mo(x())}));return}if(o.method==="POST"&&i==="/api/logout"){s.statusCode=200,s.setHeader("Content-Type",U),s.setHeader("Set-Cookie",Ea()),s.end(JSON.stringify({ok:!0}));return}if(o.method==="POST"&&i==="/api/shutdown"){s.statusCode=200,s.setHeader("Content-Type",U),s.end(JSON.stringify({ok:!0})),setImmediate(()=>{rn.requestCancel(),ad()});return}if(o.method==="POST"&&i==="/api/gateway/start"){if(cr()){s.statusCode=409,s.setHeader("Content-Type",U),s.end(JSON.stringify({ok:!1,error:"Gateway already appears to be running (pidfile + live process). Stop it first if you need to restart."}));return}let y=ar();if(!y.ok){s.statusCode=400,s.setHeader("Content-Type",U),s.end(JSON.stringify({ok:!1,error:y.message}));return}let b=de,h=or(b);if(!h.ok){s.statusCode=500,s.setHeader("Content-Type",U),s.end(JSON.stringify({ok:!1,error:h.message}));return}s.statusCode=200,s.setHeader("Content-Type",U),s.end(JSON.stringify({ok:!0,pid:h.pid,logFile:b}));return}if(o.method==="POST"&&i==="/api/gateway/stop"){let y=sr();switch(y.outcome){case"no_pidfile":s.statusCode=400,s.setHeader("Content-Type",U),s.end(JSON.stringify({ok:!1,error:"No gateway pidfile \u2014 background gateway does not appear to be running."}));return;case"invalid_pidfile":s.statusCode=400,s.setHeader("Content-Type",U),s.end(JSON.stringify({ok:!1,error:"Invalid pidfile (removed)."}));return;case"stale_cleaned":s.statusCode=200,s.setHeader("Content-Type",U),s.end(JSON.stringify({ok:!0,stale:!0,pid:y.pid,message:"Process was not running; stale pidfile removed."}));return;case"sent_signal":s.statusCode=200,s.setHeader("Content-Type",U),s.end(JSON.stringify({ok:!0,pid:y.pid}));return;case"taskkill_ok":s.statusCode=200,s.setHeader("Content-Type",U),s.end(JSON.stringify({ok:!0,pid:y.pid,taskkill:!0}));return;case"failed":s.statusCode=500,s.setHeader("Content-Type",U),s.end(JSON.stringify({ok:!1,error:y.message}));return}}if(o.method==="GET"&&i==="/api/wa/link/events"){s.writeHead(200,{"Content-Type":"text/event-stream; charset=utf-8","Cache-Control":"no-store",Connection:"keep-alive"}),s.write(`: connected
233
237
 
234
- `);let w=rn.subscribe(b=>{s.write(`data: ${JSON.stringify(b)}
238
+ `);let y=rn.subscribe(b=>{s.write(`data: ${JSON.stringify(b)}
235
239
 
236
- `)});o.on("close",()=>{w()});return}if(o.method==="POST"&&a==="/api/wa/link/start"){let b=(await $o(o))?.force===!0;try{rn.beginPairing({force:b})}catch(h){let E=String(h),$=E.includes("already in progress")||E.includes("Gateway appears")?409:400;s.statusCode=$,s.setHeader("Content-Type",U),s.end(JSON.stringify({ok:!1,error:E}));return}s.statusCode=202,s.setHeader("Content-Type",U),s.end(JSON.stringify({ok:!0}));return}if(o.method==="POST"&&a==="/api/wa/link/cancel"){rn.requestCancel(),s.statusCode=200,s.setHeader("Content-Type",U),s.end(JSON.stringify({ok:!0}));return}s.statusCode=404,s.setHeader("Content-Type",U),s.end(JSON.stringify({ok:!1,error:"Not found."}));return}let l=a==="/"?"index.html":a.replace(/^\/+/,""),u=ld(t,l),c=ie.join(t,"index.html");u&&nt.existsSync(u)&&nt.statSync(u).isFile()&&(c=u);let d=nt.readFileSync(c);s.statusCode=200,s.setHeader("Content-Type",ad(c)),s.setHeader("Cache-Control",ie.basename(c)==="index.html"?"no-store":"public, max-age=3600"),s.end(d)}catch(i){s.statusCode=500,s.setHeader("Content-Type",U),s.end(JSON.stringify({ok:!1,error:String(i)}))}});await new Promise((o,s)=>{r.once("error",s),r.listen(e.port,e.host,()=>o())}),on=r,Pa({pid:Ee.pid,port:e.port,host:e.host,startedAt:new Date().toISOString()}),r.on("close",()=>{nn(),on===r&&(on=null)})}function _a(e){let t=od.networkInterfaces(),n=[];for(let r of Object.values(t))if(r)for(let o of r)o.family==="IPv4"&&!o.internal&&n.push(`http://${o.address}:${e}/`);return[...new Set(n)].sort()}import hd from"node:readline";import Io from"node:path";import ue from"node:process";import md from"node:net";import fd from"node:fs";function gd(){try{let e=fd.readFileSync(ft,"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 Mo(e){let t=gd();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)}
237
- `;return new Promise(o=>{let s=!1,i="";function a(u){s||(s=!0,o(u))}let l=md.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(`
238
- `);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 Ba(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=ae(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=j(o.slice(3));return c?{channel:"whatsapp",e164:c,filePart:a,caption:l}:null}if(o.startsWith("+")){let c=j(o);return c?{channel:"whatsapp",e164:c,filePart:a,caption:l}:null}return null}function To(){return["/sendto wa:+E164 <file> [-- caption]","/sendto tg:<chat_id> <file> [-- caption]","Also: /sendto +E164 <file> (WhatsApp). Requires `omnish run` on this machine."].join(`
239
- `)}var Nt="wa:cli:interactive";function yd(e){let t=e.trim();if(!t)return null;let n=t.toLowerCase();if(n.startsWith("tg:")||n.startsWith("telegram:")){let s=ae(t);return s?`tg:${s}`:null}let r=n.startsWith("wa:")?t.slice(3):t,o=j(r);return o?`wa:${o}`:null}function wd(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=yd(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 Ao(e){let t=ue.cwd(),n=[`${V(e,"omnish i")} ${y(e,"[options]")}`,re(e,"Interactive shell \u2014 same commands as WhatsApp/Telegram chat."),"",q(e,"Usage:"),` ${k(e,"omnish i [options]")}`,` ${k(e,"omnish interactive [options]")}`,"",q(e,"Options:"),...je(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=>y(e,r)),"",`${y(e,"Trust:")} ${k(e,"Full local access like your shell; not gated by inbox allowlist.")}`,`${y(e,"Jobs:")} ${k(e,"/bg and /jobs apply only to this REPL session (not the gateway process).")}`,`${y(e,"Files:")} ${k(e,"Use /sendto to push a host file through the gateway; plain /send needs a chat peer.")}`,`${y(e,"Gateway:")} ${k(e,"/reload and /updates require omnish run; /sendto requires omnish run for WA/TG delivery.")}`,"",To(),"",`${y(e,"cwd:")} ${k(e,`session starts at ${t} (change with !cd or ${v().commandPrefix}cd).`)}`];console.log(n.join(`
240
- `))}async function bd(e){let t=v(),n=W(Nt).cwd,r=Io.resolve(n,e.filePart),o=Ke(r,t.fileSendMaxBytes);return"error"in o?o.error:e.channel==="whatsapp"?await Mo({op:"sendMedia",channel:"whatsapp",e164:e.e164,absPath:o.absPath,caption:e.caption}):await Mo({op:"sendMedia",channel:"telegram",chatId:e.chatId,absPath:o.absPath,caption:e.caption})}async function kd(e,t,n,r,o,s,i){let a=e.trim();if(!a)return;let l=Ba(a);if(l!==null||/^\/sendto(\s|$)/i.test(a)){if(l===null){console.log(M(ue.stderr,`Invalid /sendto.
241
- `+To()));return}let d=await bd(l);console.log(d?M(ue.stderr,d):le(ue.stdout,"Sent."));return}let u={peerKey:Nt,text:e},c=await lt(v(),t,n,r,o,u,s,void 0,i);c!==null&&await Sd(c)}async function Sd(e){if(e===null)return;if(e.kind==="file"){console.log(le(ue.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(`
242
- `)));return}let t=Be(e.body,"whatsapp").text;t.trim()&&console.log(t)}async function Da(e){let t=wd(e);if(t.error==="help"){Ao(ue.stdout);return}if(t.error&&t.error!==null){console.error(M(ue.stderr,t.error)),console.error(y(ue.stderr,"Try: omnish i --help")),ue.exitCode=1;return}let{senderKey:n,oneShot:r}=t.opts,o=n??Nt;J(),Sn(Nt,ue.cwd());let s=new ct,i=new Map,a=new Map,l=new Map,u=new Tt(()=>v(),async(f,w)=>{ue.stdout.write(w),w.endsWith(`
240
+ `)});o.on("close",()=>{y()});return}if(o.method==="POST"&&i==="/api/wa/link/start"){let b=(await Ro(o))?.force===!0;try{rn.beginPairing({force:b})}catch(h){let E=String(h),$=E.includes("already in progress")||E.includes("Gateway appears")?409:400;s.statusCode=$,s.setHeader("Content-Type",U),s.end(JSON.stringify({ok:!1,error:E}));return}s.statusCode=202,s.setHeader("Content-Type",U),s.end(JSON.stringify({ok:!0}));return}if(o.method==="POST"&&i==="/api/wa/link/cancel"){rn.requestCancel(),s.statusCode=200,s.setHeader("Content-Type",U),s.end(JSON.stringify({ok:!0}));return}s.statusCode=404,s.setHeader("Content-Type",U),s.end(JSON.stringify({ok:!1,error:"Not found."}));return}let l=i==="/"?"index.html":i.replace(/^\/+/,""),u=ud(t,l),c=ie.join(t,"index.html");u&&nt.existsSync(u)&&nt.statSync(u).isFile()&&(c=u);let d=nt.readFileSync(c);s.statusCode=200,s.setHeader("Content-Type",cd(c)),s.setHeader("Cache-Control",ie.basename(c)==="index.html"?"no-store":"public, max-age=3600"),s.end(d)}catch(a){s.statusCode=500,s.setHeader("Content-Type",U),s.end(JSON.stringify({ok:!1,error:String(a)}))}});await new Promise((o,s)=>{r.once("error",s),r.listen(e.port,e.host,()=>o())}),on=r,Pa({pid:Ee.pid,port:e.port,host:e.host,startedAt:new Date().toISOString()}),r.on("close",()=>{nn(),on===r&&(on=null)})}function _a(e){let t=id.networkInterfaces(),n=[];for(let r of Object.values(t))if(r)for(let o of r)o.family==="IPv4"&&!o.internal&&n.push(`http://${o.address}:${e}/`);return[...new Set(n)].sort()}import wd from"node:readline";import Ao from"node:path";import ue from"node:process";import gd from"node:net";import hd from"node:fs";function yd(){try{let e=hd.readFileSync(ft,"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 To(e){let t=yd();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)}
241
+ `;return new Promise(o=>{let s=!1,a="";function i(u){s||(s=!0,o(u))}let l=gd.connect({host:t.host,port:t.port},()=>{l.write(r)});l.setTimeout(6e5),l.on("data",u=>{a+=u.toString("utf8");let c=a.indexOf(`
242
+ `);if(c>=0){let d=a.slice(0,c).trim();try{let m=JSON.parse(d);m.ok?i(null):i(m.error||"Unknown error from gateway control.")}catch{i("Invalid response from gateway control.")}l.destroy()}}),l.on("error",u=>{i(`Control connection failed: ${String(u)}`)}),l.on("timeout",()=>{l.destroy(),i("Gateway control timed out.")}),l.on("close",()=>{!s&&a.trim()===""&&i("Gateway closed the control connection without a response.")})})}function Ba(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 a=s.indexOf(" -- "),i,l;if(a>=0?(i=s.slice(0,a).trim(),l=s.slice(a+4).trim()||void 0):i=s,(i.startsWith('"')&&i.endsWith('"')||i.startsWith("'")&&i.endsWith("'"))&&(i=i.slice(1,-1)),!i)return null;let u=o.toLowerCase();if(u.startsWith("tg:")||u.startsWith("telegram:")){let c=ae(o);if(!c)return null;let d=Number(c);return Number.isFinite(d)?{channel:"telegram",chatId:d,filePart:i,caption:l}:null}if(u.startsWith("wa:")){let c=j(o.slice(3));return c?{channel:"whatsapp",e164:c,filePart:i,caption:l}:null}if(o.startsWith("+")){let c=j(o);return c?{channel:"whatsapp",e164:c,filePart:i,caption:l}:null}return null}function Io(){return["/sendto wa:+E164 <file> [-- caption]","/sendto tg:<chat_id> <file> [-- caption]","Also: /sendto +E164 <file> (WhatsApp). Requires `omnish run` on this machine."].join(`
243
+ `)}var Nt="wa:cli:interactive";function bd(e){let t=e.trim();if(!t)return null;let n=t.toLowerCase();if(n.startsWith("tg:")||n.startsWith("telegram:")){let s=ae(t);return s?`tg:${s}`:null}let r=n.startsWith("wa:")?t.slice(3):t,o=j(r);return o?`wa:${o}`:null}function kd(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 a=bd(s);if(!a)return{opts:{senderKey:null,oneShot:null},error:`Could not parse sender "${s}". Use +E164, wa:+E164, or tg:<user_id>.`};t=a,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 Eo(e){let t=ue.cwd(),n=[`${K(e,"omnish i")} ${w(e,"[options]")}`,re(e,"Interactive shell \u2014 same commands as WhatsApp/Telegram chat."),"",Y(e,"Usage:"),` ${k(e,"omnish i [options]")}`,` ${k(e,"omnish interactive [options]")}`,"",Y(e,"Options:"),...je(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=>w(e,r)),"",`${w(e,"Trust:")} ${k(e,"Full local access like your shell; not gated by inbox allowlist.")}`,`${w(e,"Jobs:")} ${k(e,"/bg and /jobs apply only to this REPL session (not the gateway process).")}`,`${w(e,"Files:")} ${k(e,"Use /sendto to push a host file through the gateway; plain /send needs a chat peer.")}`,`${w(e,"Gateway:")} ${k(e,"/reload and /updates require omnish run; /sendto requires omnish run for WA/TG delivery.")}`,"",Io(),"",`${w(e,"cwd:")} ${k(e,`session starts at ${t} (change with !cd or ${x().commandPrefix}cd).`)}`];console.log(n.join(`
244
+ `))}async function Sd(e){let t=x(),n=W(Nt).cwd,r=Ao.resolve(n,e.filePart),o=qe(r,t.fileSendMaxBytes);return"error"in o?o.error:e.channel==="whatsapp"?await To({op:"sendMedia",channel:"whatsapp",e164:e.e164,absPath:o.absPath,caption:e.caption}):await To({op:"sendMedia",channel:"telegram",chatId:e.chatId,absPath:o.absPath,caption:e.caption})}async function vd(e,t,n,r,o,s,a){let i=e.trim();if(!i)return;let l=Ba(i);if(l!==null||/^\/sendto(\s|$)/i.test(i)){if(l===null){console.log(M(ue.stderr,`Invalid /sendto.
245
+ `+Io()));return}let d=await Sd(l);console.log(d?M(ue.stderr,d):le(ue.stdout,"Sent."));return}let u={peerKey:Nt,text:e},c=await lt(x(),t,n,r,o,u,s,void 0,a);c!==null&&await xd(c)}async function xd(e){if(e===null)return;if(e.kind==="file"){console.log(le(ue.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(`
246
+ `)));return}let t=Be(e.body,"whatsapp").text;t.trim()&&console.log(t)}async function Ha(e){let t=kd(e);if(t.error==="help"){Eo(ue.stdout);return}if(t.error&&t.error!==null){console.error(M(ue.stderr,t.error)),console.error(w(ue.stderr,"Try: omnish i --help")),ue.exitCode=1;return}let{senderKey:n,oneShot:r}=t.opts,o=n??Nt;J(),Sn(Nt,ue.cwd());let s=new ct,a=new Map,i=new Map,l=new Map,u=new Tt(()=>x(),async(f,y)=>{ue.stdout.write(y),y.endsWith(`
243
247
  `)||ue.stdout.write(`
244
- `)}),c=async f=>{try{await kd(f,s,i,a,l,u,o)}catch(w){console.error(M(ue.stderr,String(w)))}};if(r!==null){await c(r),u.dispose(),s.killAllRunning();return}let d=hd.createInterface({input:ue.stdin,output:ue.stdout}),m=Io.basename(W(Nt).cwd);d.setPrompt(`${m}> `),d.on("line",f=>{c(f).then(()=>{let w=Io.basename(W(Nt).cwd);d.setPrompt(`${w}> `),d.prompt()})}),d.on("close",()=>{u.dispose(),s.killAllRunning(),ue.stdout.write(`
245
- `)}),d.prompt()}xd.setDefaultResultOrder("ipv4first");function Ua(){let e=process.stdout,t=[`${oe(e,"omnish run")} ${y(e,"[options]")}`,re(e,"Listen for DMs and run shell commands from allowlisted chats."),"",q(e,"Usage:"),` ${k(e,"omnish run [options]")}`,"",q(e,"Options:"),...je(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: ${de}).`},{left:"-h, --help",right:"Show this help."}],n=>y(e,n)),"",`${k(e,"Config reload:")} ${y(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(`
246
- `))}function Wa(){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=>y(e,i.left)),r=Math.max(...n.map(vn)),o=t.map((i,a)=>hr(" ",r,n[a],k(e,i.right))),s=[`${oe(e,"omnish link")} ${y(e,"[options]")}`,re(e,"Connect WhatsApp (QR) or save a Telegram bot token."),"",q(e,"Usage:"),` ${k(e,"omnish link [--force]")}`,` ${k(e,"omnish link --tg <bot_token>")}`,"",q(e,"Modes:"),...o,` ${re(e,"Do not combine --tg with --force.")}`,"",q(e,"Options:"),...je(e," ",[{left:"-f, --force",right:"WhatsApp only: delete saved session before pairing."},{left:"-h, --help",right:"Show this help."}],i=>y(e,i)),"",`${k(e,"Next:")} ${V(e,"omnish allow tg:<your_user_id>")} ${y(e,"then")} ${V(e,"omnish run")}`,`${y(e,"Config:")} ${k(e,T)}`,""];console.log(s.join(`
247
- `))}function vd(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}
248
- 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 Oo(){let e=process.stdout,t=`${oe(e,"omnish")} ${y(e,`v${De()}`)}`,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)"},{left:"ui [options]",right:"Browser setup UI on LAN (token-gated) \u2014 omnish ui --help"}],r=[{left:"-v, --version",right:"Print version and exit."},{left:"-h, --help",right:"Show this help (same as omnish help)."}],o=[t,re(e,"Allowlisted inbox \u2192 your real shell. No AI."),"",q(e,"Usage:"),` ${k(e,"omnish [options] <command> [args...]")}`,"",q(e,"Options:"),...je(e," ",r,s=>y(e,s)),"",q(e,"Commands:"),...je(e," ",n,s=>y(e,s)),"",`${y(e,"Config:")} ${k(e,`${T} \u2014 gatewayMode: "whatsapp" | "telegram" | "both"`)}`,`${y(e,"Env:")} ${k(e,"OMNISH_VERBOSE=1 (Baileys; legacy WHATSVERBOSE=1 still works), TELEGRAM_BOT_TOKEN (optional)")}`,`${y(e,"Data:")} ${k(e,"~/.omnish by default; ~/.whatslive reused if it already exists. OMNISH_HOME overrides.")}`,`${y(e,"See also:")} ${k(e,"https://omnish.dev")}`,""];console.log(o.join(`
249
- `))}function Cd(e){let t=(e??"").trim().toLowerCase(),n=process.stdout;if(!t){Oo();return}switch(t){case"link":Wa();return;case"run":Ua();return;case"service":Ja();return;case"i":case"interactive":Ao(n);return;case"ui":za();return;default:console.error(M(process.stderr,`No detailed help for "${e}". Try: omnish help`)),process.exitCode=1}}function $d(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()!==""?Eo.isAbsolute(n)?n:Eo.resolve(process.cwd(),n):de;return{background:t,logFile:o,help:r}}function Rd(e){let t=rr(e);t.ok||(console.error(M(process.stderr,t.message)),process.exit(1));let n=process.stdout;console.log(`${le(n,`gateway started in background (pid ${t.pid}).`)} ${y(n,`Log: ${e}`)}`)}function Md(){let e=or();switch(e.outcome){case"no_pidfile":console.error(M(process.stderr,`no pidfile at ${Z} \u2014 is a background gateway running?`)),process.exitCode=1;return;case"invalid_pidfile":console.error(M(process.stderr,"invalid pidfile.")),process.exitCode=1;return;case"stale_cleaned":console.log(le(process.stdout,`process ${e.pid} is not running; removing stale pidfile.`));return;case"sent_signal":console.log(le(process.stdout,`sent SIGTERM to gateway (pid ${e.pid}).`));return;case"taskkill_ok":console.log(le(process.stdout,`stopped gateway (pid ${e.pid}) using taskkill.`));return;case"failed":console.error(M(process.stderr,e.message)),process.exitCode=1;return}}function Ha(){if(process.env.OMNISH_BACKGROUND_GATEWAY==="1")try{rt.readFileSync(Z,"utf8").trim()===String(process.pid)&&rt.unlinkSync(Z)}catch{}}function Td(e){return e.length<=8?"(set)":`${e.slice(0,4)}\u2026${e.slice(-4)}`}function Id(){if(!rt.existsSync(Z))return"gateway process: not running (no pid file)";let e=rt.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 Ga=120;function Ja(){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 ${Ga}).`},{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")} ${y(e,"<subcommand>")}`,re(e,"Boot integration, crash restart policy, and logs (same ideas as /service in chat)."),"",q(e,"Usage:"),` ${k(e,"omnish service <subcommand>")}`,"",q(e,"Subcommands:"),...je(e," ",t,r=>y(e,r)),"",q(e,"Restart and reload:"),` ${k(e,"Process")} ${y(e,"\u2014 Linux user unit: Restart=on-failure, RestartSec=5. macOS: KeepAlive. Full restart loads config from disk.")}`,` ${k(e,"Config live")} ${y(e,"\u2014 allowlisted chat while gateway runs: /reload or /restart")}`,"",`${y(e,"Docs:")} ${k(e,"docs/guides/background-and-boot.md")} ${re(e,"\xB7")} https://omnish.dev`,""];console.log(n.join(`
250
- `))}function Ad(e){let t=process.stdout,n=process.stderr,r=(e[0]??"help").toLowerCase();if(r==="help"||r==="--help"||r==="-h"){Ja();return}if(r==="instructions"){let o=at();if(o.error){console.error(M(n,o.error)),process.exitCode=1;return}console.log(Bn(o));return}if(r==="status"){let o=at(),s=(()=>{try{return rt.existsSync(Z)?`gateway.pid: ${rt.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}
251
- Script: ${o.scriptPath}`,c=v().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(`${y(t,"platform:")} ${k(t,process.platform)}`),console.log(`${y(t,"session:")} ${k(t,i)}`),console.log(`${y(t,"env:")} ${k(t,a)}`),console.log(`${y(t,"data dir:")} ${k(t,A)}`),console.log(`${y(t,"pidfile:")} ${k(t,s)}`),console.log(`${y(t,"default log:")} ${k(t,de)}`),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,Ga):80,i=Un(de,s);console.log(`${y(t,"file:")} ${k(t,de)}`),console.log(`${y(t,"lines:")} ${k(t,String(s))}`),console.log(""),console.log(i);return}if(r==="install"){if(!v().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=Dn();s.ok?console.log(le(t,s.detail)):(console.error(M(n,s.detail)),process.exitCode=1);return}if(r==="uninstall"){if(!v().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=Hn();s.ok?console.log(le(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 Ed(e){let t=e.trim();if(!t)return null;let n=t.toLowerCase();if(n.startsWith("tg:")||n.startsWith("telegram:")){let s=ae(t);return s?`tg:${s}`:null}let r=n.startsWith("wa:")?t.slice(3):t,o=j(r);return o?`wa:${o}`:null}async function Od(){let e=null,t=ir();t.ok||(console.error(M(process.stderr,t.message)),process.exit(1));let n=v(),r=n.gatewayMode,o=r==="whatsapp"||r==="both",s=r==="telegram"||r==="both",i=ee(n),a=Xe(n),l=process.stderr,u=os(a,"warn");if(u.length>0&&(console.warn(`${le(l,`Security (${u.length} finding(s)):`)}
252
- `),console.warn(br(u,l))),process.env.OMNISH_BACKGROUND_GATEWAY==="1")try{rt.writeFileSync(Z,`${process.pid}
253
- `,{mode:384})}catch(R){I.warn({err:String(R)},"could not write gateway pidfile")}let c=(R,g)=>{let S=v();if(!S.clusterEnabled)return R;let x=xe(),D=(S.clusterLabel??"").trim()||ja.hostname(),B=null;if(g.startsWith("tg:"))B=g;else if(g){let Re=j(g);Re&&(B=`wa:${Re}`)}let Y=B?Ge(S,B):null;return ti(R,{nodeId:x,label:D,role:S.clusterRole,activeNodeId:Y?.nodeId??""})},d=new ct,m=new Map,f=new Map,w=new Map,b=null,h={stop:null,sendText:null,sendMedia:null},E=null,$=!1,P=async(R,g)=>{if(R.startsWith("wa:")){let S=R.slice(3);b&&await b.sendText(S,g)}else if(R.startsWith("tg:")){let S=Number(R.slice(3));h.sendText&&Number.isFinite(S)&&await h.sendText(S,p(g))}},te=async(R,g)=>{if(R.startsWith("wa:")){let S=R.slice(3);b&&await b.sendMedia(S,g)}else if(R.startsWith("tg:")){let S=Number(R.slice(3));h.sendMedia&&Number.isFinite(S)&&await h.sendMedia(S,g)}},ne=()=>new Tt(()=>v(),P),L=ne();E=L;let fe,$e={async reload(){try{I.info("gateway reload requested from chat"),await h.stop?.().catch(()=>{}),h.stop=null,h.sendText=null,h.sendMedia=null;let R=v(),g=R.gatewayMode==="telegram"||R.gatewayMode==="both",S=ee(R);if(g&&S){let B=await go(S,()=>v(),fe,{decorate:c});h.sendText=B.sendText,h.sendMedia=B.sendMedia,h.stop=B.stop}let x=["Reload complete.",`gatewayMode: ${R.gatewayMode}`,g&&S?"Telegram bot is running with the current token.":"Telegram bot is stopped (enable telegram/both + token if you want it).","Allowlists, shell, app session limits, and timeouts are read from disk on each command."].join(`
254
- `),D=Wn(Jt());return{ok:!0,summary:D?`${x}
248
+ `)}),c=async f=>{try{await vd(f,s,a,i,l,u,o)}catch(y){console.error(M(ue.stderr,String(y)))}};if(r!==null){await c(r),u.dispose(),s.killAllRunning();return}let d=wd.createInterface({input:ue.stdin,output:ue.stdout}),m=Ao.basename(W(Nt).cwd);d.setPrompt(`${m}> `),d.on("line",f=>{c(f).then(()=>{let y=Ao.basename(W(Nt).cwd);d.setPrompt(`${y}> `),d.prompt()})}),d.on("close",()=>{u.dispose(),s.killAllRunning(),ue.stdout.write(`
249
+ `)}),d.prompt()}$d.setDefaultResultOrder("ipv4first");function Ua(){let e=process.stdout,t=[`${oe(e,"omnish run")} ${w(e,"[options]")}`,re(e,"Listen for DMs and run shell commands from allowlisted chats."),"",Y(e,"Usage:"),` ${k(e,"omnish run [options]")}`,"",Y(e,"Options:"),...je(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: ${de}).`},{left:"-h, --help",right:"Show this help."}],n=>w(e,n)),"",`${k(e,"Config reload:")} ${w(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(`
250
+ `))}function Wa(){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(a=>w(e,a.left)),r=Math.max(...n.map(xn)),o=t.map((a,i)=>yr(" ",r,n[i],k(e,a.right))),s=[`${oe(e,"omnish link")} ${w(e,"[options]")}`,re(e,"Connect WhatsApp (QR) or save a Telegram bot token."),"",Y(e,"Usage:"),` ${k(e,"omnish link [--force]")}`,` ${k(e,"omnish link --tg <bot_token>")}`,"",Y(e,"Modes:"),...o,` ${re(e,"Do not combine --tg with --force.")}`,"",Y(e,"Options:"),...je(e," ",[{left:"-f, --force",right:"WhatsApp only: delete saved session before pairing."},{left:"-h, --help",right:"Show this help."}],a=>w(e,a)),"",`${k(e,"Next:")} ${K(e,"omnish allow tg:<your_user_id>")} ${w(e,"then")} ${K(e,"omnish run")}`,`${w(e,"Config:")} ${k(e,T)}`,""];console.log(s.join(`
251
+ `))}function Cd(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("="),a=o.slice(s+1).trim();if(!a)return{kind:"error",message:"[omnish] --tg= requires a non-empty bot token."};n=a;continue}return{kind:"error",message:`[omnish] unknown link argument: ${o}
252
+ 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 Lo(){let e=process.stdout,t=`${oe(e,"omnish")} ${w(e,`v${He()}`)}`,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)"},{left:"ui [options]",right:"Browser setup UI on LAN (token-gated) \u2014 omnish ui --help"}],r=[{left:"-v, --version",right:"Print version and exit."},{left:"-h, --help",right:"Show this help (same as omnish help)."}],o=[t,re(e,"Allowlisted inbox \u2192 your real shell. No AI."),"",Y(e,"Usage:"),` ${k(e,"omnish [options] <command> [args...]")}`,"",Y(e,"Options:"),...je(e," ",r,s=>w(e,s)),"",Y(e,"Commands:"),...je(e," ",n,s=>w(e,s)),"",`${w(e,"Config:")} ${k(e,`${T} \u2014 gatewayMode: "whatsapp" | "telegram" | "both"`)}`,`${w(e,"Env:")} ${k(e,"OMNISH_VERBOSE=1 (Baileys; legacy WHATSVERBOSE=1 still works), TELEGRAM_BOT_TOKEN (optional)")}`,`${w(e,"Data:")} ${k(e,"~/.omnish by default; ~/.whatslive reused if it already exists. OMNISH_HOME overrides.")}`,`${w(e,"See also:")} ${k(e,"https://omnish.dev")}`,""];console.log(o.join(`
253
+ `))}function Rd(e){let t=(e??"").trim().toLowerCase(),n=process.stdout;if(!t){Lo();return}switch(t){case"link":Wa();return;case"run":Ua();return;case"service":Ja();return;case"i":case"interactive":Eo(n);return;case"ui":za();return;default:console.error(M(process.stderr,`No detailed help for "${e}". Try: omnish help`)),process.exitCode=1}}function Md(e){let t=!1,n="",r=!1;for(let s=0;s<e.length;s++){let a=e[s]??"";if(a==="-d"||a==="--background")t=!0;else if(a==="--log-file"||a==="--log"){let i=e[++s];i||(console.error(M(process.stderr,"--log-file requires a path.")),process.exit(1)),n=i}else if(a==="--help"||a==="-h")r=!0;else{let i=process.stderr;console.error(M(i,`unknown run option: ${a}`)),console.error(M(i,"Try: omnish run --help")),process.exit(1)}}let o=n.trim()!==""?Oo.isAbsolute(n)?n:Oo.resolve(process.cwd(),n):de;return{background:t,logFile:o,help:r}}function Td(e){let t=or(e);t.ok||(console.error(M(process.stderr,t.message)),process.exit(1));let n=process.stdout;console.log(`${le(n,`gateway started in background (pid ${t.pid}).`)} ${w(n,`Log: ${e}`)}`)}function Id(){let e=sr();switch(e.outcome){case"no_pidfile":console.error(M(process.stderr,`no pidfile at ${Z} \u2014 is a background gateway running?`)),process.exitCode=1;return;case"invalid_pidfile":console.error(M(process.stderr,"invalid pidfile.")),process.exitCode=1;return;case"stale_cleaned":console.log(le(process.stdout,`process ${e.pid} is not running; removing stale pidfile.`));return;case"sent_signal":console.log(le(process.stdout,`sent SIGTERM to gateway (pid ${e.pid}).`));return;case"taskkill_ok":console.log(le(process.stdout,`stopped gateway (pid ${e.pid}) using taskkill.`));return;case"failed":console.error(M(process.stderr,e.message)),process.exitCode=1;return}}function Da(){if(process.env.OMNISH_BACKGROUND_GATEWAY==="1")try{rt.readFileSync(Z,"utf8").trim()===String(process.pid)&&rt.unlinkSync(Z)}catch{}}function Ad(e){return e.length<=8?"(set)":`${e.slice(0,4)}\u2026${e.slice(-4)}`}function Ed(){if(!rt.existsSync(Z))return"gateway process: not running (no pid file)";let e=rt.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 Ga=120;function Ja(){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 ${Ga}).`},{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")} ${w(e,"<subcommand>")}`,re(e,"Boot integration, crash restart policy, and logs (same ideas as /service in chat)."),"",Y(e,"Usage:"),` ${k(e,"omnish service <subcommand>")}`,"",Y(e,"Subcommands:"),...je(e," ",t,r=>w(e,r)),"",Y(e,"Restart and reload:"),` ${k(e,"Process")} ${w(e,"\u2014 Linux user unit: Restart=on-failure, RestartSec=5. macOS: KeepAlive. Full restart loads config from disk.")}`,` ${k(e,"Config live")} ${w(e,"\u2014 allowlisted chat while gateway runs: /reload or /restart")}`,"",`${w(e,"Docs:")} ${k(e,"docs/guides/background-and-boot.md")} ${re(e,"\xB7")} https://omnish.dev`,""];console.log(n.join(`
254
+ `))}function Od(e){let t=process.stdout,n=process.stderr,r=(e[0]??"help").toLowerCase();if(r==="help"||r==="--help"||r==="-h"){Ja();return}if(r==="instructions"){let o=at();if(o.error){console.error(M(n,o.error)),process.exitCode=1;return}console.log(Hn(o));return}if(r==="status"){let o=at(),s=(()=>{try{return rt.existsSync(Z)?`gateway.pid: ${rt.readFileSync(Z,"utf8").trim()}`:"gateway.pid: (missing)"}catch(d){return`gateway.pid: (read error: ${String(d)})`}})(),a=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).",i=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}
255
+ Script: ${o.scriptPath}`,c=x().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(`${w(t,"platform:")} ${k(t,process.platform)}`),console.log(`${w(t,"session:")} ${k(t,a)}`),console.log(`${w(t,"env:")} ${k(t,i)}`),console.log(`${w(t,"data dir:")} ${k(t,A)}`),console.log(`${w(t,"pidfile:")} ${k(t,s)}`),console.log(`${w(t,"default log:")} ${k(t,de)}`),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,Ga):80,a=Wn(de,s);console.log(`${w(t,"file:")} ${k(t,de)}`),console.log(`${w(t,"lines:")} ${k(t,String(s))}`),console.log(""),console.log(a);return}if(r==="install"){if(!x().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=Dn();s.ok?console.log(le(t,s.detail)):(console.error(M(n,s.detail)),process.exitCode=1);return}if(r==="uninstall"){if(!x().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=jn();s.ok?console.log(le(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 Ld(e){let t=e.trim();if(!t)return null;let n=t.toLowerCase();if(n.startsWith("tg:")||n.startsWith("telegram:")){let s=ae(t);return s?`tg:${s}`:null}let r=n.startsWith("wa:")?t.slice(3):t,o=j(r);return o?`wa:${o}`:null}async function Pd(){let e=null,t=ar();t.ok||(console.error(M(process.stderr,t.message)),process.exit(1));let n=x(),r=n.gatewayMode,o=r==="whatsapp"||r==="both",s=r==="telegram"||r==="both",a=ee(n),i=Ve(n),l=process.stderr,u=ss(i,"warn");if(u.length>0&&(console.warn(`${le(l,`Security (${u.length} finding(s)):`)}
256
+ `),console.warn(kr(u,l))),process.env.OMNISH_BACKGROUND_GATEWAY==="1")try{rt.writeFileSync(Z,`${process.pid}
257
+ `,{mode:384})}catch(R){I.warn({err:String(R)},"could not write gateway pidfile")}let c=(R,g)=>{let S=x();if(!S.clusterEnabled)return R;let v=ve(),H=(S.clusterLabel??"").trim()||ja.hostname(),B=null;if(g.startsWith("tg:"))B=g;else if(g){let Re=j(g);Re&&(B=`wa:${Re}`)}let Q=B?Ge(S,B):null;return ti(R,{nodeId:v,label:H,role:S.clusterRole,activeNodeId:Q?.nodeId??""})},d=new ct,m=new Map,f=new Map,y=new Map,b=null,h={stop:null,sendText:null,sendMedia:null},E=null,$=!1,P=async(R,g)=>{if(R.startsWith("wa:")){let S=R.slice(3);b&&await b.sendText(S,g)}else if(R.startsWith("tg:")){let S=Number(R.slice(3));h.sendText&&Number.isFinite(S)&&await h.sendText(S,p(g))}},te=async(R,g)=>{if(R.startsWith("wa:")){let S=R.slice(3);b&&await b.sendMedia(S,g)}else if(R.startsWith("tg:")){let S=Number(R.slice(3));h.sendMedia&&Number.isFinite(S)&&await h.sendMedia(S,g)}},ne=()=>new Tt(()=>x(),P),L=ne();E=L;let fe,Ce={async reload(){try{I.info("gateway reload requested from chat"),await h.stop?.().catch(()=>{}),h.stop=null,h.sendText=null,h.sendMedia=null;let R=x(),g=R.gatewayMode==="telegram"||R.gatewayMode==="both",S=ee(R);if(g&&S){let B=await ho(S,()=>x(),fe,{decorate:c});h.sendText=B.sendText,h.sendMedia=B.sendMedia,h.stop=B.stop}let v=["Reload complete.",`gatewayMode: ${R.gatewayMode}`,g&&S?"Telegram bot is running with the current token.":"Telegram bot is stopped (enable telegram/both + token if you want it).","Allowlists, shell, app session limits, and timeouts are read from disk on each command."].join(`
258
+ `),H=Gn(Jt());return{ok:!0,summary:H?`${v}
255
259
 
256
- Updates (last check): ${D}`:x}}catch(R){return{ok:!1,error:String(R)}}}};if(fe=async(R,g)=>{let S=v(),x=Uo(S.telegramAllowFrom),D=R.peerKey.startsWith("tg:")?R.peerKey.slice(3):"";if(!D||!x.has(D)){I.warn({denied:R.peerKey,uid:D},"telegram denied");return}try{let B=R.peerKey;if(!oo(S,R.text,B))return;if(R.mediaError&&await g({kind:"text",body:p(R.mediaError)}),R.mediaSavedPath&&await g({kind:"text",body:p(`Saved: ${R.mediaSavedPath}`)}),R.text.trim()){let Y=await lt(S,d,m,f,w,R,L,$e,B);Y!==null&&await g(Y)}}catch(B){I.error({err:String(B)},"telegram handler error"),await g({kind:"text",body:p(`Error: ${String(B)}`)}).catch(()=>{})}},s){let R=await go(i,()=>v(),fe,{decorate:c});h.sendText=R.sendText,h.sendMedia=R.sendMedia,h.stop=R.stop}va({getCfg:()=>v(),getWaOutbound:()=>b,getTgSendMedia:()=>h.sendMedia}),e=Ii({getRunningVersion:De,getConfig:v,log:I});let Se=ta({getConfig:v,sendToPeer:P,sendMediaToPeer:te}),Oe=!s,He=()=>{$=!0,Se(),e?.(),e=null,Ha(),sr(),h.stop?.().catch(()=>{}),E?.dispose(),d.killAllRunning(),console.error(`
257
- ${M(process.stderr,"shutting down\u2026")}`),process.exit(0)};if(process.on("SIGINT",He),process.on("SIGTERM",He),o)for(;!$;){let R=!1,g;try{g=await Qn({printQr:!1,verbose:Jn()}),await dt(Zn(g),3e5,"Gateway: timed out waiting for WhatsApp connection (5 min).")}catch(x){console.error(M(process.stderr,`connect failed: ${String(x)}`)),await new Promise(D=>setTimeout(D,5e3));continue}b=ya(g,{decorate:c});let S=da(g,async x=>{let D=v(),B=jo(D.allowFrom),Y=x.fromE164||j(x.fromJid)||"";if(!Y||!B.has(Y)){I.warn({denied:x.fromJid,phone:Y},"denied");return}try{let Re=zi(x),sn=`wa:${Y}`;if(!oo(D,Re.text,sn))return;if(x.mediaError&&await b.sendText(x.fromJid,x.mediaError),x.mediaSavedPath&&await b.sendText(x.fromJid,`Saved: ${x.mediaSavedPath}`),x.text.trim()){let mt=await lt(D,d,m,f,w,Re,L,$e,sn);mt!==null&&(mt.kind==="file"?await b.sendMedia(x.fromJid,mt.spec):await b.sendText(x.fromJid,Be(mt.body,"whatsapp").text))}}catch(Re){I.error({err:String(Re)},"handler error"),await b.sendText(x.fromJid,Be(p(`Error: ${String(Re)}`),"whatsapp").text).catch(()=>{})}});if(await new Promise(x=>{let D=B=>{B.connection==="close"&&(po(B.lastDisconnect)===ut.loggedOut&&(R=!0),g.ev.off("connection.update",D),x())};g.ev.on("connection.update",D)}),S(),Oe&&(L.dispose(),L=ne(),E=L),Ot(g),b=null,R&&(console.error(M(process.stderr,"session logged out. Run `omnish link` again.")),Se(),e?.(),e=null,Ha(),sr(),h.stop?.().catch(()=>{}),process.exit(1)),$)break;await new Promise(x=>setTimeout(x,3e3))}else for(;!$;)await new Promise(R=>setTimeout(R,500));Se(),e?.(),sr(),h.stop?.().catch(()=>{}),L.dispose()}function Ld(e){let t=!1,n="0.0.0.0",r=3789,o;for(let s=0;s<e.length;s++){let i=e[s];if(i==="--help"||i==="-h"){t=!0;continue}if(i==="--host"||i==="-H"){let l=e[++s];l||(console.error(M(process.stderr,"--host requires an address (e.g. 127.0.0.1).")),process.exit(1)),n=l;continue}if(i==="--port"||i==="-p"){let l=e[++s],u=Number.parseInt(l??"",10);Number.isFinite(u)||(console.error(M(process.stderr,"--port requires a number.")),process.exit(1)),r=u;continue}if(i==="--token"||i==="-t"){let l=e[++s];(!l||l.startsWith("-"))&&(console.error(M(process.stderr,"--token requires a secret string.")),process.exit(1)),o=l;continue}let a=process.stderr;console.error(M(a,`unknown ui argument: ${i}`)),console.error(M(a,"Try: omnish ui --help")),process.exit(1)}return{help:t,host:n,port:r,token:o}}function za(){let e=process.stdout,t=[`${oe(e,"omnish ui")} ${y(e,"[options]")}`,re(e,"Serve the browser setup panel on your LAN (token-gated). Same trust as editing config on disk."),"",q(e,"Usage:"),` ${k(e,"omnish ui [options]")}`,"",q(e,"Options:"),...je(e," ",[{left:"--host <addr>",right:"Bind address (default 0.0.0.0 \u2014 reachable on LAN). Use 127.0.0.1 for loopback only."},{left:"--port <n>",right:"TCP port (default 3789)."},{left:"--token <secret>",right:`Set or rotate setup token (saved to ${Ne}).`},{left:"-h, --help",right:"This help."}],n=>y(e,n)),"",`${y(e,"Warning:")} ${we(e,"Listening on all interfaces exposes a remote control panel on your network \u2014 use a trusted LAN.")}`,""];console.log(t.join(`
258
- `))}async function Pd(){let[,,e,...t]=process.argv;if(e==="--version"||e==="-v"||e==="-V"){let n=process.stdout;console.log(`${oe(n,"omnish")} ${y(n,De())}`);return}if(e==="--help"||e==="-h"){Oo();return}if(e==="help"){Cd(t[0]);return}switch(J(),e){case"link":{let n=vd(t);if(n.kind==="help"){Wa();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(!ze(r)){console.error(M(o,"That does not look like a Telegram bot token (expect digits:secret from @BotFather).")),process.exitCode=1;return}gt(r);let i=Te()?"both":"telegram";wn(i),console.log([`${V(s,"Telegram")} ${k(s,"bot token saved to")} ${y(s,T)}`,`${y(s,"gatewayMode:")} ${V(s,i)}`,"",k(s,"Next:"),` ${y(s,"1.")} ${k(s,"Find your numeric user id (e.g. t.me/userinfobot), then:")} ${V(s,"omnish allow tg:<id>")}`,` ${y(s,"2.")} ${V(s,"omnish run")}`,""].join(`
259
- `));return}await xa({verbose:Jn(),force:n.force});return}case"run":{let n=$d(t);if(n.help){Ua();return}if(n.background){Rd(n.logFile);return}await Od();return}case"stop":Md();return;case"logout":{try{rt.rmSync(H,{recursive:!0,force:!0}),console.log(le(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=gn(n);console.log(`${y(r,"allowFrom:")} ${k(r,s.allowFrom.join(", ")||"(empty)")}`),console.log(`${y(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=hn(n);console.log(`${y(r,"allowFrom:")} ${k(r,s.allowFrom.join(", ")||"(empty)")}`),console.log(`${y(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 Da(t);return}case"status":{let n=process.stdout,r=v(),o=t.includes("--check-updates"),s=new ct().list(),i=s.filter(w=>w.status==="running").length,a=ee(r),l=Xe(r),u=r.gatewayMode==="whatsapp"||r.gatewayMode==="both",c=r.gatewayMode==="telegram"||r.gatewayMode==="both",d=Te(),m=d?ba(H):null,f=[];if(f.push(`${oe(n,"omnish")} ${y(n,De())}`,`${y(n,"gatewayMode:")} ${k(n,r.gatewayMode)}`,`${y(n,"data dir:")} ${k(n,Eo.dirname(H))}`,"",`${y(n,"gateway process:")} ${k(n,Id().replace(/^gateway process: /,""))}`,"",st(n),V(n,"whatsapp"),` ${y(n,"in use:")} ${u?k(n,"yes"):we(n,"no (gatewayMode is telegram-only)")}`),u){let w=d?k(n,`linked (${H})`):we(n,"missing \u2014 run omnish link");f.push(` ${y(n,"session:")} ${w}`),d&&m&&f.push(` ${y(n,"linked as:")} ${k(n,m)}`),d&&!m&&f.push(` ${y(n,"linked as:")} ${re(n,"(not in creds yet \u2014 try again after omnish link completes)")}`)}if(f.push(` ${q(n,"Allowed")}`),r.allowFrom.length===0)f.push(` ${re(n,"(none)")}`);else for(let w of r.allowFrom)f.push(` ${y(n,"whatsapp:")} ${k(n,w)}`);if(f.push("",st(n),V(n,"telegram")),f.push(` ${y(n,"in use:")} ${c?k(n,"yes"):we(n,"no (gatewayMode is whatsapp-only)")}`),c){let w=a?k(n,Td(a)):we(n,"(none) \u2014 omnish link --tg <token> or TELEGRAM_BOT_TOKEN");f.push(` ${y(n,"bot token:")} ${w}`)}if(f.push(` ${q(n,"Allowed")}`),r.telegramAllowFrom.length===0)f.push(` ${re(n,"(none)")}`);else for(let w of r.telegramAllowFrom)f.push(` ${y(n,"telegram:")} ${k(n,w)}`);if(f.push("",st(n),`${V(n,"jobs")} ${y(n,`(recent): ${s.length} total, ${i} running`)}`,as(l,n)),console.log(f.join(`
260
- `)),o){let w=await zt(De(),r),b=Dt(w);console.log(""),console.log(st(n)),console.log(Be(b,"whatsapp").text)}if(r.clusterEnabled){let w=xe(),b=X(),h=Object.keys(b.senderBindings).length,E=Object.keys(r.clusterSenderBindings??{}).length;console.log(""),console.log(st(n)),console.log(V(n,"cluster")),console.log(` ${y(n,"\xB7")} ${k(n,`enabled \xB7 label ${r.clusterLabel||ja.hostname()} \xB7 bindings ${h} chat / ${E} default`)}`),console.log(` ${y(n,"node:")} ${k(n,`${w.slice(0,8)}\u2026`)}`)}return}case"commands":{let n=process.stdout,r=v();console.log(ts(wt(r),n)),console.log(""),console.log(st(n)),console.log(k(n,"Keys editable from chat via /config set (same trust as shell):")),console.log(y(n,xt.join(", "))),console.log(`${y(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=v(),i=xe();if(console.log(`${y(n,"clusterEnabled:")} ${k(n,String(s.clusterEnabled))}`),console.log(`${y(n,"clusterLabel:")} ${k(n,s.clusterLabel||"(hostname)")}`),console.log(`${y(n,"clusterRole:")} ${k(n,`${s.clusterRole} (informational; no longer used to gate traffic)`)}`),console.log(`${y(n,"node id:")} ${V(n,i)}`),s.clusterEnabled){let a=X(),l=Yr(s,null);console.log(""),console.log(k(n,l.wa.replace(/\*([^*]+)\*/g,"$1").replace(/`([^`]+)`/g,"$1"))),console.log(""),console.log(Kr(a,s,null));let u=Object.keys(a.senderBindings).length,c=Object.entries(s.clusterSenderBindings??{});if(u>0){console.log(""),console.log(V(n,"Chat bindings (cluster-local.json)"));for(let[d,m]of Object.entries(a.senderBindings))console.log(` ${y(n,d)} ${re(n,"->")} ${k(n,`${m.nodeId} (${m.source}, since ${m.sinceIso})`)}`)}if(c.length>0){console.log(""),console.log(V(n,"Config defaults (clusterSenderBindings)"));for(let[d,m]of c)console.log(` ${y(n,d)} ${re(n,"->")} ${k(n,m)}`)}}else console.log(""),console.log(we(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=Ed(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:u}=fi(a,i);if(!u.ok){if(u.reason==="ambiguous-label"){let d=(u.matches??[]).map(m=>`${m.nodeId}(${m.label})`).join(", ");console.error(M(r,`Label "${i}" matches multiple machines: ${d}. 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(`${V(n,"cluster:")} ${k(n,`${a} -> ${u.peer.nodeId} (${u.peer.label}).`)}`);let c=v();console.log(""),console.log(Kr(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=v(),r=Xe(n),o=t.includes("--json");console.log(o?ss(r):br(r,process.stdout)),Mn(r)&&(process.exitCode=1);return}case"service":{Ad(t);return}case"ui":{let n=Ld(t);if(n.help){za();return}if(!Number.isFinite(n.port)||n.port<1||n.port>65535){console.error(M(process.stderr,"port must be between 1 and 65535.")),process.exitCode=1;return}let r=Ta(n.token);await Fa({host:n.host,port:n.port,meta:r});let o=process.stdout,s=_a(n.port);console.log(""),console.log(`${oe(o,"ui")} ${y(o,"listening")}`),console.log(`${y(o,"bind:")} ${k(o,`${n.host}:${n.port}`)}`),console.log(`${y(o,"setup token:")} ${k(o,r.token)}`),console.log(`${y(o,"token file:")} ${re(o,Ne)}`),console.log(""),console.log(we(o,"Anyone on your network who can reach this port needs the token \u2014 do not expose to untrusted Wi\u2011Fi.")),console.log(""),console.log(`${y(o,"Open:")}`),console.log(` ${k(o,`http://127.0.0.1:${n.port}/`)}`);for(let i of s)console.log(` ${k(o,i)}`);console.log(""),console.log(`${y(o,"Quick link (same Wi\u2011Fi):")} ${k(o,`http://127.0.0.1:${n.port}/?token=${encodeURIComponent(r.token)}`)}`),console.log("");return}default:Oo(),e&&(process.exitCode=1)}}Pd().catch(e=>{console.error(M(process.stderr,String(e))),process.exit(1)});
260
+ Updates (last check): ${H}`:v}}catch(R){return{ok:!1,error:String(R)}}}};if(fe=async(R,g)=>{let S=x(),v=Wo(S.telegramAllowFrom),H=R.peerKey.startsWith("tg:")?R.peerKey.slice(3):"";if(!H||!v.has(H)){I.warn({denied:R.peerKey,uid:H},"telegram denied");return}try{let B=R.peerKey;if(!so(S,R.text,B))return;if(R.mediaError&&await g({kind:"text",body:p(R.mediaError)}),R.mediaSavedPath&&await g({kind:"text",body:p(`Saved: ${R.mediaSavedPath}`)}),R.text.trim()){let Q=await lt(S,d,m,f,y,R,L,Ce,B);Q!==null&&await g(Q)}}catch(B){I.error({err:String(B)},"telegram handler error"),await g({kind:"text",body:p(`Error: ${String(B)}`)}).catch(()=>{})}},s){let R=await ho(a,()=>x(),fe,{decorate:c});h.sendText=R.sendText,h.sendMedia=R.sendMedia,h.stop=R.stop}xa({getCfg:()=>x(),getWaOutbound:()=>b,getTgSendMedia:()=>h.sendMedia}),e=Ii({getRunningVersion:He,getConfig:x,log:I});let Se=ta({getConfig:x,sendToPeer:P,sendMediaToPeer:te}),Oe=!s,De=()=>{$=!0,Se(),e?.(),e=null,Da(),ir(),h.stop?.().catch(()=>{}),E?.dispose(),d.killAllRunning(),console.error(`
261
+ ${M(process.stderr,"shutting down\u2026")}`),process.exit(0)};if(process.on("SIGINT",De),process.on("SIGTERM",De),o)for(;!$;){let R=!1,g;try{g=await Zn({printQr:!1,verbose:zn()}),await dt(er(g),3e5,"Gateway: timed out waiting for WhatsApp connection (5 min).")}catch(v){console.error(M(process.stderr,`connect failed: ${String(v)}`)),await new Promise(H=>setTimeout(H,5e3));continue}b=ya(g,{decorate:c});let S=da(g,async v=>{let H=x(),B=Uo(H.allowFrom),Q=v.fromE164||j(v.fromJid)||"";if(!Q||!B.has(Q)){I.warn({denied:v.fromJid,phone:Q},"denied");return}try{let Re=zi(v),sn=`wa:${Q}`;if(!so(H,Re.text,sn))return;if(v.mediaError&&await b.sendText(v.fromJid,v.mediaError),v.mediaSavedPath&&await b.sendText(v.fromJid,`Saved: ${v.mediaSavedPath}`),v.text.trim()){let mt=await lt(H,d,m,f,y,Re,L,Ce,sn);mt!==null&&(mt.kind==="file"?await b.sendMedia(v.fromJid,mt.spec):await b.sendText(v.fromJid,Be(mt.body,"whatsapp").text))}}catch(Re){I.error({err:String(Re)},"handler error"),await b.sendText(v.fromJid,Be(p(`Error: ${String(Re)}`),"whatsapp").text).catch(()=>{})}});if(await new Promise(v=>{let H=B=>{B.connection==="close"&&(mo(B.lastDisconnect)===ut.loggedOut&&(R=!0),g.ev.off("connection.update",H),v())};g.ev.on("connection.update",H)}),S(),Oe&&(L.dispose(),L=ne(),E=L),Ot(g),b=null,R&&(console.error(M(process.stderr,"session logged out. Run `omnish link` again.")),Se(),e?.(),e=null,Da(),ir(),h.stop?.().catch(()=>{}),process.exit(1)),$)break;await new Promise(v=>setTimeout(v,3e3))}else for(;!$;)await new Promise(R=>setTimeout(R,500));Se(),e?.(),ir(),h.stop?.().catch(()=>{}),L.dispose()}function Nd(e){let t=!1,n="0.0.0.0",r=3789,o;for(let s=0;s<e.length;s++){let a=e[s];if(a==="--help"||a==="-h"){t=!0;continue}if(a==="--host"||a==="-H"){let l=e[++s];l||(console.error(M(process.stderr,"--host requires an address (e.g. 127.0.0.1).")),process.exit(1)),n=l;continue}if(a==="--port"||a==="-p"){let l=e[++s],u=Number.parseInt(l??"",10);Number.isFinite(u)||(console.error(M(process.stderr,"--port requires a number.")),process.exit(1)),r=u;continue}if(a==="--token"||a==="-t"){let l=e[++s];(!l||l.startsWith("-"))&&(console.error(M(process.stderr,"--token requires a secret string.")),process.exit(1)),o=l;continue}let i=process.stderr;console.error(M(i,`unknown ui argument: ${a}`)),console.error(M(i,"Try: omnish ui --help")),process.exit(1)}return{help:t,host:n,port:r,token:o}}function za(){let e=process.stdout,t=[`${oe(e,"omnish ui")} ${w(e,"[options]")}`,re(e,"Serve the browser setup panel on your LAN (token-gated). Same trust as editing config on disk."),"",Y(e,"Usage:"),` ${k(e,"omnish ui [options]")}`,"",Y(e,"Options:"),...je(e," ",[{left:"--host <addr>",right:"Bind address (default 0.0.0.0 \u2014 reachable on LAN). Use 127.0.0.1 for loopback only."},{left:"--port <n>",right:"TCP port (default 3789)."},{left:"--token <secret>",right:`Set or rotate setup token (saved to ${Ne}).`},{left:"-h, --help",right:"This help."}],n=>w(e,n)),"",`${w(e,"Warning:")} ${we(e,"Listening on all interfaces exposes a remote control panel on your network \u2014 use a trusted LAN.")}`,""];console.log(t.join(`
262
+ `))}async function Fd(){let[,,e,...t]=process.argv;if(e==="--version"||e==="-v"||e==="-V"){let n=process.stdout;console.log(`${oe(n,"omnish")} ${w(n,He())}`);return}if(e==="--help"||e==="-h"){Lo();return}if(e==="help"){Rd(t[0]);return}switch(J(),e){case"link":{let n=Cd(t);if(n.kind==="help"){Wa();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(!ze(r)){console.error(M(o,"That does not look like a Telegram bot token (expect digits:secret from @BotFather).")),process.exitCode=1;return}gt(r);let a=Te()?"both":"telegram";wn(a),console.log([`${K(s,"Telegram")} ${k(s,"bot token saved to")} ${w(s,T)}`,`${w(s,"gatewayMode:")} ${K(s,a)}`,"",k(s,"Next:"),` ${w(s,"1.")} ${k(s,"Find your numeric user id (e.g. t.me/userinfobot), then:")} ${K(s,"omnish allow tg:<id>")}`,` ${w(s,"2.")} ${K(s,"omnish run")}`,""].join(`
263
+ `));return}await va({verbose:zn(),force:n.force});return}case"run":{let n=Md(t);if(n.help){Ua();return}if(n.background){Td(n.logFile);return}await Pd();return}case"stop":Id();return;case"logout":{try{rt.rmSync(D,{recursive:!0,force:!0}),console.log(le(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=gn(n);console.log(`${w(r,"allowFrom:")} ${k(r,s.allowFrom.join(", ")||"(empty)")}`),console.log(`${w(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=hn(n);console.log(`${w(r,"allowFrom:")} ${k(r,s.allowFrom.join(", ")||"(empty)")}`),console.log(`${w(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 Ha(t);return}case"status":{let n=process.stdout,r=x(),o=t.includes("--check-updates"),s=new ct().list(),a=s.filter(y=>y.status==="running").length,i=ee(r),l=Ve(r),u=r.gatewayMode==="whatsapp"||r.gatewayMode==="both",c=r.gatewayMode==="telegram"||r.gatewayMode==="both",d=Te(),m=d?ba(D):null,f=[];if(f.push(`${oe(n,"omnish")} ${w(n,He())}`,`${w(n,"gatewayMode:")} ${k(n,r.gatewayMode)}`,`${w(n,"data dir:")} ${k(n,Oo.dirname(D))}`,"",`${w(n,"gateway process:")} ${k(n,Ed().replace(/^gateway process: /,""))}`,"",st(n),K(n,"whatsapp"),` ${w(n,"in use:")} ${u?k(n,"yes"):we(n,"no (gatewayMode is telegram-only)")}`),u){let y=d?k(n,`linked (${D})`):we(n,"missing \u2014 run omnish link");f.push(` ${w(n,"session:")} ${y}`),d&&m&&f.push(` ${w(n,"linked as:")} ${k(n,m)}`),d&&!m&&f.push(` ${w(n,"linked as:")} ${re(n,"(not in creds yet \u2014 try again after omnish link completes)")}`)}if(f.push(` ${Y(n,"Allowed")}`),r.allowFrom.length===0)f.push(` ${re(n,"(none)")}`);else for(let y of r.allowFrom)f.push(` ${w(n,"whatsapp:")} ${k(n,y)}`);if(f.push("",st(n),K(n,"telegram")),f.push(` ${w(n,"in use:")} ${c?k(n,"yes"):we(n,"no (gatewayMode is whatsapp-only)")}`),c){let y=i?k(n,Ad(i)):we(n,"(none) \u2014 omnish link --tg <token> or TELEGRAM_BOT_TOKEN");f.push(` ${w(n,"bot token:")} ${y}`)}if(f.push(` ${Y(n,"Allowed")}`),r.telegramAllowFrom.length===0)f.push(` ${re(n,"(none)")}`);else for(let y of r.telegramAllowFrom)f.push(` ${w(n,"telegram:")} ${k(n,y)}`);if(f.push("",st(n),`${K(n,"jobs")} ${w(n,`(recent): ${s.length} total, ${a} running`)}`,ls(l,n)),console.log(f.join(`
264
+ `)),o){let y=await zt(He(),r),b=Ht(y);console.log(""),console.log(st(n)),console.log(Be(b,"whatsapp").text)}if(r.clusterEnabled){let y=ve(),b=V(),h=Object.keys(b.senderBindings).length,E=Object.keys(r.clusterSenderBindings??{}).length;console.log(""),console.log(st(n)),console.log(K(n,"cluster")),console.log(` ${w(n,"\xB7")} ${k(n,`enabled \xB7 label ${r.clusterLabel||ja.hostname()} \xB7 bindings ${h} chat / ${E} default`)}`),console.log(` ${w(n,"node:")} ${k(n,`${y.slice(0,8)}\u2026`)}`)}return}case"commands":{let n=process.stdout,r=x();console.log(ns(wt(r),n)),console.log(""),console.log(st(n)),console.log(k(n,"Keys editable from chat via /config set (same trust as shell):")),console.log(w(n,vt.join(", "))),console.log(`${w(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=x(),a=ve();if(console.log(`${w(n,"clusterEnabled:")} ${k(n,String(s.clusterEnabled))}`),console.log(`${w(n,"clusterLabel:")} ${k(n,s.clusterLabel||"(hostname)")}`),console.log(`${w(n,"clusterRole:")} ${k(n,`${s.clusterRole} (informational; no longer used to gate traffic)`)}`),console.log(`${w(n,"node id:")} ${K(n,a)}`),s.clusterEnabled){let i=V(),l=Yr(s,null);console.log(""),console.log(k(n,l.wa.replace(/\*([^*]+)\*/g,"$1").replace(/`([^`]+)`/g,"$1"))),console.log(""),console.log(Qr(i,s,null));let u=Object.keys(i.senderBindings).length,c=Object.entries(s.clusterSenderBindings??{});if(u>0){console.log(""),console.log(K(n,"Chat bindings (cluster-local.json)"));for(let[d,m]of Object.entries(i.senderBindings))console.log(` ${w(n,d)} ${re(n,"->")} ${k(n,`${m.nodeId} (${m.source}, since ${m.sinceIso})`)}`)}if(c.length>0){console.log(""),console.log(K(n,"Config defaults (clusterSenderBindings)"));for(let[d,m]of c)console.log(` ${w(n,d)} ${re(n,"->")} ${k(n,m)}`)}}else console.log(""),console.log(we(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],a=t.slice(2).join(" ").trim();if(!s||!a){console.error(M(r,"Usage: omnish cluster use <senderE164|tg:id> <label-or-id>")),process.exitCode=1;return}let i=Ld(s);if(!i){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:u}=fi(i,a);if(!u.ok){if(u.reason==="ambiguous-label"){let d=(u.matches??[]).map(m=>`${m.nodeId}(${m.label})`).join(", ");console.error(M(r,`Label "${a}" matches multiple machines: ${d}. Use the 8-character id.`))}else console.error(M(r,`No machine matches "${a}". Run /c status from the chat first to populate the roster, or pass an 8-character node id.`));process.exitCode=1;return}console.log(`${K(n,"cluster:")} ${k(n,`${i} -> ${u.peer.nodeId} (${u.peer.label}).`)}`);let c=x();console.log(""),console.log(Qr(l,c,i));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=x(),r=Ve(n),o=t.includes("--json");console.log(o?is(r):kr(r,process.stdout)),Mn(r)&&(process.exitCode=1);return}case"service":{Od(t);return}case"ui":{let n=Nd(t);if(n.help){za();return}if(!Number.isFinite(n.port)||n.port<1||n.port>65535){console.error(M(process.stderr,"port must be between 1 and 65535.")),process.exitCode=1;return}let r=Ta(n.token);await Fa({host:n.host,port:n.port,meta:r});let o=process.stdout,s=_a(n.port);console.log(""),console.log(`${oe(o,"ui")} ${w(o,"listening")}`),console.log(`${w(o,"bind:")} ${k(o,`${n.host}:${n.port}`)}`),console.log(`${w(o,"setup token:")} ${k(o,r.token)}`),console.log(`${w(o,"token file:")} ${re(o,Ne)}`),console.log(""),console.log(we(o,"Anyone on your network who can reach this port needs the token \u2014 do not expose to untrusted Wi\u2011Fi.")),console.log(""),console.log(`${w(o,"Open:")}`),console.log(` ${k(o,`http://127.0.0.1:${n.port}/`)}`);for(let a of s)console.log(` ${k(o,a)}`);console.log(""),console.log(`${w(o,"Quick link (same Wi\u2011Fi):")} ${k(o,`http://127.0.0.1:${n.port}/?token=${encodeURIComponent(r.token)}`)}`),console.log("");return}default:Lo(),e&&(process.exitCode=1)}}Fd().catch(e=>{console.error(M(process.stderr,String(e))),process.exit(1)});