omnish 1.4.1 → 1.4.2

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 +8 -0
  2. package/dist/index.js +190 -190
  3. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -1,8 +1,9 @@
1
1
  #!/usr/bin/env node
2
- var Rm=Object.defineProperty;var Zn=(e,t)=>()=>(e&&(t=e(e=0)),t);var Cm=(e,t)=>{for(var n in t)Rm(e,n,{get:t[n],enumerable:!0})};import Mm from"node:crypto";import _s from"node:fs";import Tm from"node:os";import re from"node:path";function Em(){let e=process.env.OMNISH_HOME?.trim();if(e)return re.resolve(e);let t=Tm.homedir(),n=re.join(t,".omnish"),r=re.join(t,".whatslive");try{if(_s.existsSync(n))return n;if(_s.existsSync(r))return r}catch{}return n}function nr(e){let t=Mm.createHash("sha1").update(e,"utf8").digest("hex").slice(0,8);return re.join(Ya,t)}function j(e){_s.mkdirSync(e,{recursive:!0,mode:448})}function te(){j(H),j(ne),j(tt),j(Ya),j(Qa),j(ut),j(bt)}var H,ne,tt,Ya,Qa,Ne,le,hn,er,ct,_r,Fr,_,Wr,Ur,Dr,ut,Fs,Ws,Va,bt,tr,Hr,G=Zn(()=>{"use strict";H=Em(),ne=re.join(H,"auth"),tt=re.join(H,"jobs"),Ya=re.join(H,"apps"),Qa=re.join(H,"logs"),Ne=re.join(Qa,"gateway.log"),le=re.join(H,"gateway.pid"),hn=re.join(H,"gateway-control.json"),er=re.join(H,"config-ui.json"),ct=re.join(H,"ui.json"),_r=re.join(H,"tunnel-auth.json"),Fr=re.join(H,"ui-server.json"),_=re.join(H,"config.json"),Wr=re.join(H,"shortcuts.json"),Ur=re.join(H,"recipes.json"),Dr=re.join(H,"recipes-user.json"),ut=re.join(H,"cowork"),Fs=re.join(ut,"tasks.json"),Ws=re.join(ut,"pending-runs.json"),Va=re.join(ut,"completions.sqlite"),bt=re.join(H,"watch"),tr=re.join(bt,"rules.json"),Hr=re.join(bt,"events.sqlite")});function nl(e){let t=e.trim();for(;;){let n=t;if(t=t.replace(/^whatsapp:/i,"").trim(),t===n)return t}}function Pm(e){let t=nl(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 Am(e){let t=e.match(Za);if(t)return t[1]??null;let n=e.match(el);if(n)return n[1]??null;let r=e.match(tl);return r?r[1]??null:null}function Xa(e){let t=e.replace(/\D/g,"");return t?`+${t}`:""}function It(e){return`${e.replace(/\D/g,"")}@s.whatsapp.net`}function ee(e){let t=nl(e);if(!t||Pm(t))return null;if(Za.test(t)||el.test(t)||tl.test(t)){let r=Am(t);if(!r)return null;let o=Xa(r);return o.length>1?o:null}if(t.includes("@"))return null;let n=Xa(t);return n.length>1?n:null}function Br(e){return e.map(t=>String(t).trim()).filter(t=>!!t).map(t=>t==="*"?t:ee(t)).filter(t=>!!t)}function jr(e){let t=new Set;for(let n of e){if(n==="*")continue;let r=ee(String(n));r&&t.add(r)}return t}function Ee(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 Gr(e){let t=new Set;for(let n of e){let r=Ee(String(n));r&&t.add(r)}return t}function Us(e){let t=e.trim();if(!t)return null;let n=t.toLowerCase();if(n.startsWith("tg:")||n.startsWith("telegram:")){let o=Ee(t);return o?{kind:"tg",id:o}:null}let r=ee(t);return r?{kind:"wa",normalized:r}:null}function rl(e,t){let n=ee(t);return n?e.has(n):!1}var Za,el,tl,qe=Zn(()=>{"use strict";Za=/^(\d+)(?::\d+)?@s\.whatsapp\.net$/i,el=/^(\d+)@c\.us$/i,tl=/^(\d+)@lid$/i});import Jr from"node:fs";function Im(e){return e==="downloads"||e==="omnishData"||e==="sessionCwd"||e==="processCwd"||e==="fixed"?e:N.fileReceiveRootMode}function Om(e){return e==="primary"?"primary":"secondary"}function Lm(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=ee(s.slice(3));l&&(t[`wa:${l}`]=o.slice(0,64));continue}if(i.startsWith("tg:")||i.startsWith("telegram:")){let l=Ee(s);l&&(t[`tg:${l}`]=o.slice(0,64));continue}let a=ee(s);a&&(t[`wa:${a}`]=o.slice(0,64))}return t}function rr(e){let t=typeof e.appsFlushMs=="number"&&e.appsFlushMs>=0?e.appsFlushMs:N.appsFlushMs,n=e.gatewayMode==="telegram"||e.gatewayMode==="both"||e.gatewayMode==="whatsapp"?e.gatewayMode:N.gatewayMode,r=typeof e.telegramBotToken=="string"?e.telegramBotToken:N.telegramBotToken,o=Array.isArray(e.telegramAllowFrom)?[...new Set(e.telegramAllowFrom.map(s=>Ee(String(s))).filter(s=>!!s))].sort():N.telegramAllowFrom;return{...N,...e,gatewayMode:n,telegramBotToken:r,telegramAllowFrom:o,allowFrom:Array.isArray(e.allowFrom)?Br(e.allowFrom.map(String)).filter(s=>s!=="*"):N.allowFrom,commandPrefix:(()=>{let s=typeof e.commandPrefix=="string"&&e.commandPrefix.length>0?e.commandPrefix:N.commandPrefix;return s==="! "?"!":s})(),syncTimeoutMs:typeof e.syncTimeoutMs=="number"&&e.syncTimeoutMs>0?e.syncTimeoutMs:N.syncTimeoutMs,syncMaxBytes:typeof e.syncMaxBytes=="number"&&e.syncMaxBytes>0?e.syncMaxBytes:N.syncMaxBytes,jobLogTailLines:typeof e.jobLogTailLines=="number"&&e.jobLogTailLines>0?e.jobLogTailLines:N.jobLogTailLines,shell:typeof e.shell=="string"&&e.shell.length>0?e.shell:N.shell,appsCols:typeof e.appsCols=="number"&&e.appsCols>0&&e.appsCols<=500?Math.floor(e.appsCols):N.appsCols,appsRows:typeof e.appsRows=="number"&&e.appsRows>0&&e.appsRows<=200?Math.floor(e.appsRows):N.appsRows,appsFlushMs:t,appsMinIntervalMs:typeof e.appsMinIntervalMs=="number"&&e.appsMinIntervalMs>=0?e.appsMinIntervalMs:N.appsMinIntervalMs,appsMaxFlushBytes:typeof e.appsMaxFlushBytes=="number"&&e.appsMaxFlushBytes>256?Math.floor(e.appsMaxFlushBytes):N.appsMaxFlushBytes,appsMaxSessions:typeof e.appsMaxSessions=="number"&&e.appsMaxSessions>0?Math.min(50,Math.floor(e.appsMaxSessions)):N.appsMaxSessions,appsMaxSessionsTotal:typeof e.appsMaxSessionsTotal=="number"&&e.appsMaxSessionsTotal>0?Math.min(200,Math.floor(e.appsMaxSessionsTotal)):N.appsMaxSessionsTotal,appsMaxWaChars:typeof e.appsMaxWaChars=="number"&&e.appsMaxWaChars>256?Math.floor(e.appsMaxWaChars):N.appsMaxWaChars,appsLogTailLines:typeof e.appsLogTailLines=="number"&&e.appsLogTailLines>0?Math.min(500,Math.floor(e.appsLogTailLines)):N.appsLogTailLines,appsSubmitDelayMs:typeof e.appsSubmitDelayMs=="number"&&e.appsSubmitDelayMs>=0?Math.min(500,Math.floor(e.appsSubmitDelayMs)):N.appsSubmitDelayMs,appsClearInput:typeof e.appsClearInput=="boolean"?e.appsClearInput:N.appsClearInput,appsClearInputDelayMs:typeof e.appsClearInputDelayMs=="number"&&e.appsClearInputDelayMs>=0?Math.min(200,Math.floor(e.appsClearInputDelayMs)):N.appsClearInputDelayMs,appsClearInputSequence:typeof e.appsClearInputSequence=="string"&&e.appsClearInputSequence.length>0?e.appsClearInputSequence.slice(0,200):N.appsClearInputSequence,appsSkipClearOnPasswordPrompt:typeof e.appsSkipClearOnPasswordPrompt=="boolean"?e.appsSkipClearOnPasswordPrompt:N.appsSkipClearOnPasswordPrompt,appsPasswordPromptHint:typeof e.appsPasswordPromptHint=="boolean"?e.appsPasswordPromptHint:N.appsPasswordPromptHint,fileSendMaxBytes:typeof e.fileSendMaxBytes=="number"&&e.fileSendMaxBytes>=0?e.fileSendMaxBytes===0?0:Math.min(2e9,Math.floor(e.fileSendMaxBytes)):N.fileSendMaxBytes,fileReceiveMaxBytes:typeof e.fileReceiveMaxBytes=="number"&&e.fileReceiveMaxBytes>=0?e.fileReceiveMaxBytes===0?0:Math.min(2e9,Math.floor(e.fileReceiveMaxBytes)):N.fileReceiveMaxBytes,fileInboxSubdir:typeof e.fileInboxSubdir=="string"&&e.fileInboxSubdir.length>0&&e.fileInboxSubdir.replace(/[/\\]/g,"").slice(0,80)||N.fileInboxSubdir,fileReceiveRootMode:Im(e.fileReceiveRootMode),fileReceiveRootPath:typeof e.fileReceiveRootPath=="string"?e.fileReceiveRootPath.trim().slice(0,4096):N.fileReceiveRootPath,recipesAllowDangerousBuiltins:typeof e.recipesAllowDangerousBuiltins=="boolean"?e.recipesAllowDangerousBuiltins:N.recipesAllowDangerousBuiltins,recipesMaxTaskChars:typeof e.recipesMaxTaskChars=="number"&&e.recipesMaxTaskChars>=0?e.recipesMaxTaskChars===0?0:Math.min(Number.MAX_SAFE_INTEGER,Math.floor(e.recipesMaxTaskChars)):N.recipesMaxTaskChars,recipesMacroDefaultCommand:typeof e.recipesMacroDefaultCommand=="string"&&e.recipesMacroDefaultCommand.trim().length>0?e.recipesMacroDefaultCommand.trim().slice(0,4096):N.recipesMacroDefaultCommand,clusterEnabled:typeof e.clusterEnabled=="boolean"?e.clusterEnabled:N.clusterEnabled,clusterLabel:typeof e.clusterLabel=="string"?e.clusterLabel.trim().slice(0,128):N.clusterLabel,clusterRole:Om(e.clusterRole),clusterSenderBindings:Lm(e.clusterSenderBindings),serviceInstallFromChat:typeof e.serviceInstallFromChat=="boolean"?e.serviceInstallFromChat:N.serviceInstallFromChat,updateCheckEnabled:typeof e.updateCheckEnabled=="boolean"?e.updateCheckEnabled:N.updateCheckEnabled,updateCheckIntervalMs:typeof e.updateCheckIntervalMs=="number"&&e.updateCheckIntervalMs>0?Math.min(6048e5,Math.max(36e5,Math.floor(e.updateCheckIntervalMs))):N.updateCheckIntervalMs,updateCheckPackageName:typeof e.updateCheckPackageName=="string"&&e.updateCheckPackageName.trim().length>0?e.updateCheckPackageName.trim().slice(0,214):N.updateCheckPackageName,updateInfoUrl:typeof e.updateInfoUrl=="string"?e.updateInfoUrl.trim().slice(0,2048):N.updateInfoUrl,chatLlmFallbackEnabled:typeof e.chatLlmFallbackEnabled=="boolean"?e.chatLlmFallbackEnabled:N.chatLlmFallbackEnabled,chatLlmShellCommand:typeof e.chatLlmShellCommand=="string"?e.chatLlmShellCommand.trim().slice(0,8192):N.chatLlmShellCommand,chatLlmTimeoutMs:typeof e.chatLlmTimeoutMs=="number"&&e.chatLlmTimeoutMs>0?Math.min(9e5,Math.floor(e.chatLlmTimeoutMs)):N.chatLlmTimeoutMs,chatLlmMaxInputChars:typeof e.chatLlmMaxInputChars=="number"&&e.chatLlmMaxInputChars>0?Math.min(5e5,Math.floor(e.chatLlmMaxInputChars)):N.chatLlmMaxInputChars,chatLlmMaxOutputChars:typeof e.chatLlmMaxOutputChars=="number"&&e.chatLlmMaxOutputChars>0?Math.min(2e6,Math.floor(e.chatLlmMaxOutputChars)):N.chatLlmMaxOutputChars,chatLlmNeedsTty:typeof e.chatLlmNeedsTty=="boolean"?e.chatLlmNeedsTty:N.chatLlmNeedsTty,chatLlmWorkDir:typeof e.chatLlmWorkDir=="string"?e.chatLlmWorkDir.trim().slice(0,4096):N.chatLlmWorkDir,tunnelEnabled:typeof e.tunnelEnabled=="boolean"?e.tunnelEnabled:N.tunnelEnabled,tunnelRelayUrl:typeof e.tunnelRelayUrl=="string"&&e.tunnelRelayUrl.trim().length>0?e.tunnelRelayUrl.trim().slice(0,2048):N.tunnelRelayUrl,tunnelMaxActive:typeof e.tunnelMaxActive=="number"&&e.tunnelMaxActive>0?Math.min(50,Math.floor(e.tunnelMaxActive)):N.tunnelMaxActive,platformToken:typeof e.platformToken=="string"?e.platformToken.trim():N.platformToken,platformDeviceId:typeof e.platformDeviceId=="string"?e.platformDeviceId.trim().slice(0,128):N.platformDeviceId,webhookEnabled:typeof e.webhookEnabled=="boolean"?e.webhookEnabled:N.webhookEnabled,webhookPort:typeof e.webhookPort=="number"&&e.webhookPort>=0?Math.min(65535,Math.floor(e.webhookPort)):N.webhookPort,webhookHost:typeof e.webhookHost=="string"&&e.webhookHost.trim().length>0?e.webhookHost.trim():N.webhookHost,webhookToken:typeof e.webhookToken=="string"?e.webhookToken.trim():N.webhookToken,watchEnabled:typeof e.watchEnabled=="boolean"?e.watchEnabled:N.watchEnabled,watchDebounceMs:typeof e.watchDebounceMs=="number"&&Number.isFinite(e.watchDebounceMs)?Math.max(500,Math.min(6e4,Math.floor(e.watchDebounceMs))):N.watchDebounceMs,watchMaxEventsPerMinute:typeof e.watchMaxEventsPerMinute=="number"&&Number.isFinite(e.watchMaxEventsPerMinute)?Math.max(1,Math.min(120,Math.floor(e.watchMaxEventsPerMinute))):N.watchMaxEventsPerMinute,watchAutoRestore:typeof e.watchAutoRestore=="boolean"?e.watchAutoRestore:N.watchAutoRestore}}function W(e){let t=$(),n=rr({...t,...e});return _e(n),n}function $(){if(te(),!Jr.existsSync(_)){let e=rr({});return _e(e),e}try{let e=Jr.readFileSync(_,"utf8"),t=JSON.parse(e);return rr(t)}catch{return rr({})}}function _e(e){te();let t=rr(e);Jr.writeFileSync(_,JSON.stringify(t,null,2)+`
3
- `,{mode:384})}function Kr(e){let t=Us(e);if(!t)throw new Error("Invalid entry. Use +E164 (WhatsApp) or tg:<user_id> (Telegram).");let n=$();if(t.kind==="wa"){if(t.normalized==="*")throw new Error("Wildcards are not allowed.");let r=new Set(n.allowFrom);r.add(t.normalized),n.allowFrom=[...r].sort()}else{let r=new Set(n.telegramAllowFrom);r.add(t.id),n.telegramAllowFrom=[...r].sort()}return _e(n),n}function zr(e){let t=Us(e);if(!t)throw new Error("Invalid entry. Use +E164 (WhatsApp) or tg:<user_id> (Telegram).");let n=$();return t.kind==="wa"?n.allowFrom=n.allowFrom.filter(r=>r!==t.normalized):n.telegramAllowFrom=n.telegramAllowFrom.filter(r=>Ee(r)!==t.id),_e(n),n}function ve(e){let t=typeof process.env.TELEGRAM_BOT_TOKEN=="string"?process.env.TELEGRAM_BOT_TOKEN.trim():"";return t||(e.telegramBotToken??"").trim()}function dt(e){let t=e.trim();return/^\d{6,}:[A-Za-z0-9_-]{20,}$/.test(t)}function Ot(e){let t=$();return t.telegramBotToken=e.trim(),_e(t),$()}function gn(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 qr(e){let t=$();return t.gatewayMode=e,_e(t),$()}function Yr(e){let t=$();return t.clusterEnabled=e,_e(t),$()}function nt(){try{return Jr.readdirSync(ne).length>0}catch{return!1}}var N,pe=Zn(()=>{"use strict";G();qe();N={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",appsSkipClearOnPasswordPrompt:!0,appsPasswordPromptHint:!0,fileSendMaxBytes:0,fileReceiveMaxBytes:0,fileInboxSubdir:"inbox",fileReceiveRootMode:"downloads",fileReceiveRootPath:"",recipesAllowDangerousBuiltins:!1,recipesMaxTaskChars:0,recipesMacroDefaultCommand:'claude -p "$OMNISH_TASK"',clusterEnabled:!1,clusterLabel:"",clusterRole:"secondary",clusterSenderBindings:{},serviceInstallFromChat:!1,updateCheckEnabled:!1,updateCheckIntervalMs:864e5,updateCheckPackageName:"omnish",updateInfoUrl:"",chatLlmFallbackEnabled:!1,chatLlmShellCommand:"",chatLlmTimeoutMs:12e4,chatLlmMaxInputChars:16e3,chatLlmMaxOutputChars:24e3,chatLlmNeedsTty:!1,chatLlmWorkDir:"",tunnelEnabled:!1,tunnelRelayUrl:"https://tunnel.omnish.dev",platformToken:"",platformDeviceId:"",tunnelMaxActive:5,webhookEnabled:!1,webhookPort:0,webhookHost:"127.0.0.1",webhookToken:"",watchEnabled:!1,watchDebounceMs:2e3,watchMaxEventsPerMinute:30,watchAutoRestore:!0}});import Nm from"pino";function yn(){return process.env.OMNISH_VERBOSE==="1"||process.env.WHATSVERBOSE==="1"}function ol(){return yn()?"info":"silent"}function Ds(e){e?process.env.OMNISH_VERBOSE="1":delete process.env.OMNISH_VERBOSE,M.level=ol()}function sl(){return M.child({module:"baileys"})}var M,be=Zn(()=>{"use strict";M=Nm({level:ol(),base:{app:"omnish"}})});var yd={};Cm(yd,{fetchPlatformAccount:()=>Bn,getAttachedConfig:()=>ta,getAttachedPlatformSnapshot:()=>vy,mergeAttachedPlatformPolicy:()=>gd,parsePlatformMeResponse:()=>fd,setAttachedPlatformSnapshot:()=>Zi,setPlatformDefaultDevice:()=>na,snapshotFromRegisteredAccount:()=>hd,syncAttachedPlatformPolicy:()=>ns,updatePlatformAllowlists:()=>ts});function ea(e){let t=e.replace(/\/$/,"");return/^https?:\/\//i.test(t)?t:`http://${t}`}function ky(e){return e==="telegram"||e==="both"||e==="whatsapp"?e:"whatsapp"}function Sy(e){if(!e||typeof e!="object")return{};let t={};for(let[n,r]of Object.entries(e)){if(!r||typeof r!="object")continue;let o=r,s=typeof o.status=="string"?o.status:"idle";t[n]={status:s,linked:o.linked===!0||s==="linked",...typeof o.tokenConfigured=="boolean"?{tokenConfigured:o.tokenConfigured}:{}}}return t}function fd(e){let t=e.routing,n=t&&typeof t=="object"?t:null,r=Array.isArray(n?.onlineDeviceIds)?n.onlineDeviceIds.map(String):[],o=n?.defaultDeviceId!=null?String(n.defaultDeviceId):e.defaultDeviceId!=null?String(e.defaultDeviceId):null;return{allowFrom:Array.isArray(e.allowFrom)?e.allowFrom.map(String):[],telegramAllowFrom:Array.isArray(e.telegramAllowFrom)?e.telegramAllowFrom.map(String):[],gatewayMode:ky(e.gatewayMode),connectors:Sy(e.connectors),defaultDeviceId:o,routing:{defaultDeviceId:o,onlineDeviceIds:r,onlineCount:typeof n?.onlineCount=="number"?n.onlineCount:r.length}}}function hd(e,t=[],n=[]){let r=e.defaultDeviceId??null;return{allowFrom:t,telegramAllowFrom:n,gatewayMode:e.gatewayMode,connectors:e.connectors,defaultDeviceId:r,routing:{defaultDeviceId:r,onlineDeviceIds:[],onlineCount:0}}}function gd(e,t){return{...e,allowFrom:[...t.allowFrom],telegramAllowFrom:[...t.telegramAllowFrom],gatewayMode:t.gatewayMode}}function Zi(e){es=e}function vy(){return es}function ta(){let e=$();return es?gd(e,es):e}async function Bn(e){let t=await fetch(`${ea(e.platformUrl)}/v1/me`,{headers:{Authorization:`Bearer ${e.token}`}});if(!t.ok)throw new Error(`Platform GET /v1/me failed: HTTP ${t.status}`);let n=await t.json();return fd(n)}async function ts(e,t){let n=await fetch(`${ea(e.platformUrl)}/v1/me/allowlists`,{method:"PUT",headers:{"content-type":"application/json",Authorization:`Bearer ${e.token}`},body:JSON.stringify(t)}),r=await n.json().catch(()=>({}));if(!n.ok)throw new Error(r.error||`Platform PUT /v1/me/allowlists failed: HTTP ${n.status}`);return{allowFrom:Array.isArray(r.allowFrom)?r.allowFrom.map(String):[],telegramAllowFrom:Array.isArray(r.telegramAllowFrom)?r.telegramAllowFrom.map(String):[]}}async function na(e,t){let n=await fetch(`${ea(e.platformUrl)}/v1/me/default-device`,{method:"PUT",headers:{"content-type":"application/json",Authorization:`Bearer ${e.token}`},body:JSON.stringify({deviceId:t})});if(!n.ok){let r=await n.json().catch(()=>({}));throw new Error(r.error||`Platform PUT /v1/me/default-device failed: HTTP ${n.status}`)}}async function ns(e,t){try{let n=await Bn(e);return Zi(n),M.info({gatewayMode:n.gatewayMode,waLinked:n.connectors.whatsapp?.linked===!0,tgLinked:n.connectors.telegram?.linked===!0,allowFromCount:n.allowFrom.length,telegramAllowFromCount:n.telegramAllowFrom.length},"attached platform policy loaded"),n}catch(n){if(t){let r=hd(t);return Zi(r),M.warn({err:String(n)},"platform GET /v1/me failed; using register ack for gatewayMode/connectors only (allowlists from local config until /v1/me works)"),r}return M.warn({err:String(n)},"platform GET /v1/me failed; attached inbound uses local config.json allowlists"),null}}var es,rs=Zn(()=>{"use strict";pe();be();es=null});pe();import Kb from"node:dns";import zb from"node:crypto";import Xt from"node:fs";import za from"node:path";import wm from"node:os";pe();be();import vf from"node:os";import xf from"node:fs";G();import il from"node:crypto";import or from"node:fs";import Hs from"node:os";import wn from"node:path";var _m=/^[a-zA-Z0-9][a-zA-Z0-9_-]{0,31}$/;function js(e){let t=e.trim().toLowerCase();return _m.test(t)?{ok:!0,name:t}:{ok:!1,error:"Name must be alphanumeric with _ or -, max 32 chars."}}function Ge(e,t){let n=e.trim();return n===""||n==="."?wn.resolve(t):n==="~"?Hs.homedir():n.startsWith("~/")||n.startsWith("~\\")?wn.join(Hs.homedir(),n.slice(2)):wn.isAbsolute(n)?n:wn.resolve(t,n)}function Qr(e){return wn.join(Hs.homedir(),"Cowork",e)}function Fm(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:Qr(r),l=typeof t.enabled=="boolean"?t.enabled:!0,d=typeof t.notify=="string"?t.notify.toLowerCase():"self",u=d==="wa"||d==="whatsapp"?"wa":d==="tg"||d==="telegram"?"tg":d==="all"?"all":d==="none"?"none":"self",c={kind:"ondemand"};if(t.schedule&&typeof t.schedule=="object"){let v=t.schedule,E=typeof v.kind=="string"?v.kind:"";if(E==="ondemand")c={kind:"ondemand"};else if(E==="daily"){let R=Number(v.hour),P=Number(v.minute);Number.isFinite(R)&&Number.isFinite(P)&&(c={kind:"daily",hour:R,minute:P})}else if(E==="weekdays"){let R=Number(v.hour),P=Number(v.minute);Number.isFinite(R)&&Number.isFinite(P)&&(c={kind:"weekdays",hour:R,minute:P})}else if(E==="hourly"){let R=Number(v.minute);Number.isFinite(R)&&Number.isInteger(R)&&R>=0&&R<=59&&(c={kind:"hourly",minute:R})}else if(E==="weekly"){let R=Number(v.hour),P=Number(v.minute),T=Number(v.weekday);Number.isFinite(R)&&Number.isFinite(P)&&Number.isInteger(T)&&(c={kind:"weekly",weekday:T,hour:R,minute:P})}else if(E==="heartbeat"){let R=Number(v.intervalMs),P=Number(v.graceMs);Number.isFinite(R)&&R>0&&Number.isFinite(P)&&P>0&&(c={kind:"heartbeat",intervalMs:R,graceMs: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(),h=typeof t.attachLog=="boolean"?t.attachLog:!1,g=Array.isArray(t.attachFiles)?t.attachFiles.filter(v=>typeof v=="string"&&v.trim().length>0):[],y=typeof t.notifyWhen=="string"?t.notifyWhen.toLowerCase():"always";return{id:n,name:r,ownerPeerKey:o,command:s,cwd:i,outputDir:a,schedule:c,enabled:l,notify:u,notifyWhen:y==="failure"?"failure":y==="state-change"?"state-change":"always",attachLog:h,attachFiles:g,lastCompletedSlotMs:m,createdAtMs:f}}function xe(){try{let e=or.readFileSync(Fs,"utf8"),t=JSON.parse(e);if(!t||!Array.isArray(t.tasks))return[];let n=[];for(let r of t.tasks){let o=Fm(r);o&&n.push(o)}return n}catch{return[]}}function Oe(e){j(ut);let t={tasks:e},n=wn.join(ut,`.tasks.${process.pid}.${il.randomBytes(4).toString("hex")}.tmp`);or.writeFileSync(n,JSON.stringify(t,null,2)+`
4
- `,{mode:384}),or.renameSync(n,Fs)}function Ye(e,t,n){let r=t.trim().toLowerCase();return e.find(o=>o.name===r&&o.ownerPeerKey===n)}function sr(){return il.randomBytes(4).toString("hex")}function al(e){j(ut),or.writeFileSync(Ws,JSON.stringify(e,null,2)+`
5
- `,{mode:384})}function ll(e){let t=Bs();t.push(e),al(t)}function Bs(){try{let e=or.readFileSync(Ws,"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 cl(e){if(e<=0)return{batch:[],remainingAfter:Bs().length};let t=Bs();if(t.length===0)return{batch:[],remainingAfter:0};let n=t.slice(0,e),r=t.slice(e);return al(r),{batch:n,remainingAfter:r.length}}G();G();import Wm from"better-sqlite3";var ul=1,dl=200,bn=null;function Um(){j(bt);let e=new Wm(Hr);e.pragma("journal_mode = WAL"),e.exec(`
2
+ var Am=Object.defineProperty;var Ot=(e,t)=>()=>(e&&(t=e(e=0)),t);var Im=(e,t)=>{for(var n in t)Am(e,n,{get:t[n],enumerable:!0})};import Om from"node:crypto";import Us from"node:fs";import Lm from"node:os";import re from"node:path";function Nm(){let e=process.env.OMNISH_HOME?.trim();if(e)return re.resolve(e);let t=Lm.homedir(),n=re.join(t,".omnish"),r=re.join(t,".whatslive");try{if(Us.existsSync(n))return n;if(Us.existsSync(r))return r}catch{}return n}function sr(e){let t=Om.createHash("sha1").update(e,"utf8").digest("hex").slice(0,8);return re.join(tl,t)}function j(e){Us.mkdirSync(e,{recursive:!0,mode:448})}function te(){j(H),j(ne),j(tt),j(tl),j(nl),j(dt),j(kt)}var H,ne,tt,tl,nl,Ne,ce,kn,rr,ut,Dr,Hr,_,Br,jr,Gr,dt,Ds,Hs,rl,kt,or,Jr,G=Ot(()=>{"use strict";H=Nm(),ne=re.join(H,"auth"),tt=re.join(H,"jobs"),tl=re.join(H,"apps"),nl=re.join(H,"logs"),Ne=re.join(nl,"gateway.log"),ce=re.join(H,"gateway.pid"),kn=re.join(H,"gateway-control.json"),rr=re.join(H,"config-ui.json"),ut=re.join(H,"ui.json"),Dr=re.join(H,"tunnel-auth.json"),Hr=re.join(H,"ui-server.json"),_=re.join(H,"config.json"),Br=re.join(H,"shortcuts.json"),jr=re.join(H,"recipes.json"),Gr=re.join(H,"recipes-user.json"),dt=re.join(H,"cowork"),Ds=re.join(dt,"tasks.json"),Hs=re.join(dt,"pending-runs.json"),rl=re.join(dt,"completions.sqlite"),kt=re.join(H,"watch"),or=re.join(kt,"rules.json"),Jr=re.join(kt,"events.sqlite")});function ll(e){let t=e.trim();for(;;){let n=t;if(t=t.replace(/^whatsapp:/i,"").trim(),t===n)return t}}function _m(e){let t=ll(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 Fm(e){let t=e.match(sl);if(t)return t[1]??null;let n=e.match(il);if(n)return n[1]??null;let r=e.match(al);return r?r[1]??null:null}function ol(e){let t=e.replace(/\D/g,"");return t?`+${t}`:""}function Lt(e){return`${e.replace(/\D/g,"")}@s.whatsapp.net`}function ee(e){let t=ll(e);if(!t||_m(t))return null;if(sl.test(t)||il.test(t)||al.test(t)){let r=Fm(t);if(!r)return null;let o=ol(r);return o.length>1?o:null}if(t.includes("@"))return null;let n=ol(t);return n.length>1?n:null}function Kr(e){return e.map(t=>String(t).trim()).filter(t=>!!t).map(t=>t==="*"?t:ee(t)).filter(t=>!!t)}function zr(e){let t=new Set;for(let n of e){if(n==="*")continue;let r=ee(String(n));r&&t.add(r)}return t}function Ee(e){let t=e.trim();for(;;){let n=t.toLowerCase();if(n.startsWith("tg:")){t=t.slice(3).trimStart();continue}if(n.startsWith("telegram:")){t=t.slice(9).trimStart();continue}break}return/^\d+$/.test(t)?t:null}function qr(e){let t=new Set;for(let n of e){let r=Ee(String(n));r&&t.add(r)}return t}function Bs(e){let t=e.trim();if(!t)return null;let n=t.toLowerCase();if(n.startsWith("tg:")||n.startsWith("telegram:")){let o=Ee(t);return o?{kind:"tg",id:o}:null}let r=ee(t);return r?{kind:"wa",normalized:r}:null}function cl(e,t){let n=ee(t);return n?e.has(n):!1}var sl,il,al,qe=Ot(()=>{"use strict";sl=/^(\d+)(?::\d+)?@s\.whatsapp\.net$/i,il=/^(\d+)@c\.us$/i,al=/^(\d+)@lid$/i});import Yr from"node:fs";function Wm(e){return e==="downloads"||e==="omnishData"||e==="sessionCwd"||e==="processCwd"||e==="fixed"?e:N.fileReceiveRootMode}function Um(e){return e==="primary"?"primary":"secondary"}function Dm(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=ee(s.slice(3));l&&(t[`wa:${l}`]=o.slice(0,64));continue}if(i.startsWith("tg:")||i.startsWith("telegram:")){let l=Ee(s);l&&(t[`tg:${l}`]=o.slice(0,64));continue}let a=ee(s);a&&(t[`wa:${a}`]=o.slice(0,64))}return t}function ir(e){let t=typeof e.appsFlushMs=="number"&&e.appsFlushMs>=0?e.appsFlushMs:N.appsFlushMs,n=e.gatewayMode==="telegram"||e.gatewayMode==="both"||e.gatewayMode==="whatsapp"?e.gatewayMode:N.gatewayMode,r=typeof e.telegramBotToken=="string"?e.telegramBotToken:N.telegramBotToken,o=Array.isArray(e.telegramAllowFrom)?[...new Set(e.telegramAllowFrom.map(s=>Ee(String(s))).filter(s=>!!s))].sort():N.telegramAllowFrom;return{...N,...e,gatewayMode:n,telegramBotToken:r,telegramAllowFrom:o,allowFrom:Array.isArray(e.allowFrom)?Kr(e.allowFrom.map(String)).filter(s=>s!=="*"):N.allowFrom,commandPrefix:(()=>{let s=typeof e.commandPrefix=="string"&&e.commandPrefix.length>0?e.commandPrefix:N.commandPrefix;return s==="! "?"!":s})(),syncTimeoutMs:typeof e.syncTimeoutMs=="number"&&e.syncTimeoutMs>0?e.syncTimeoutMs:N.syncTimeoutMs,syncMaxBytes:typeof e.syncMaxBytes=="number"&&e.syncMaxBytes>0?e.syncMaxBytes:N.syncMaxBytes,jobLogTailLines:typeof e.jobLogTailLines=="number"&&e.jobLogTailLines>0?e.jobLogTailLines:N.jobLogTailLines,shell:typeof e.shell=="string"&&e.shell.length>0?e.shell:N.shell,appsCols:typeof e.appsCols=="number"&&e.appsCols>0&&e.appsCols<=500?Math.floor(e.appsCols):N.appsCols,appsRows:typeof e.appsRows=="number"&&e.appsRows>0&&e.appsRows<=200?Math.floor(e.appsRows):N.appsRows,appsFlushMs:t,appsMinIntervalMs:typeof e.appsMinIntervalMs=="number"&&e.appsMinIntervalMs>=0?e.appsMinIntervalMs:N.appsMinIntervalMs,appsMaxFlushBytes:typeof e.appsMaxFlushBytes=="number"&&e.appsMaxFlushBytes>256?Math.floor(e.appsMaxFlushBytes):N.appsMaxFlushBytes,appsMaxSessions:typeof e.appsMaxSessions=="number"&&e.appsMaxSessions>0?Math.min(50,Math.floor(e.appsMaxSessions)):N.appsMaxSessions,appsMaxSessionsTotal:typeof e.appsMaxSessionsTotal=="number"&&e.appsMaxSessionsTotal>0?Math.min(200,Math.floor(e.appsMaxSessionsTotal)):N.appsMaxSessionsTotal,appsMaxWaChars:typeof e.appsMaxWaChars=="number"&&e.appsMaxWaChars>256?Math.floor(e.appsMaxWaChars):N.appsMaxWaChars,appsLogTailLines:typeof e.appsLogTailLines=="number"&&e.appsLogTailLines>0?Math.min(500,Math.floor(e.appsLogTailLines)):N.appsLogTailLines,appsSubmitDelayMs:typeof e.appsSubmitDelayMs=="number"&&e.appsSubmitDelayMs>=0?Math.min(500,Math.floor(e.appsSubmitDelayMs)):N.appsSubmitDelayMs,appsClearInput:typeof e.appsClearInput=="boolean"?e.appsClearInput:N.appsClearInput,appsClearInputDelayMs:typeof e.appsClearInputDelayMs=="number"&&e.appsClearInputDelayMs>=0?Math.min(200,Math.floor(e.appsClearInputDelayMs)):N.appsClearInputDelayMs,appsClearInputSequence:typeof e.appsClearInputSequence=="string"&&e.appsClearInputSequence.length>0?e.appsClearInputSequence.slice(0,200):N.appsClearInputSequence,appsSkipClearOnPasswordPrompt:typeof e.appsSkipClearOnPasswordPrompt=="boolean"?e.appsSkipClearOnPasswordPrompt:N.appsSkipClearOnPasswordPrompt,appsPasswordPromptHint:typeof e.appsPasswordPromptHint=="boolean"?e.appsPasswordPromptHint:N.appsPasswordPromptHint,fileSendMaxBytes:typeof e.fileSendMaxBytes=="number"&&e.fileSendMaxBytes>=0?e.fileSendMaxBytes===0?0:Math.min(2e9,Math.floor(e.fileSendMaxBytes)):N.fileSendMaxBytes,fileReceiveMaxBytes:typeof e.fileReceiveMaxBytes=="number"&&e.fileReceiveMaxBytes>=0?e.fileReceiveMaxBytes===0?0:Math.min(2e9,Math.floor(e.fileReceiveMaxBytes)):N.fileReceiveMaxBytes,fileInboxSubdir:typeof e.fileInboxSubdir=="string"&&e.fileInboxSubdir.length>0&&e.fileInboxSubdir.replace(/[/\\]/g,"").slice(0,80)||N.fileInboxSubdir,fileReceiveRootMode:Wm(e.fileReceiveRootMode),fileReceiveRootPath:typeof e.fileReceiveRootPath=="string"?e.fileReceiveRootPath.trim().slice(0,4096):N.fileReceiveRootPath,recipesAllowDangerousBuiltins:typeof e.recipesAllowDangerousBuiltins=="boolean"?e.recipesAllowDangerousBuiltins:N.recipesAllowDangerousBuiltins,recipesMaxTaskChars:typeof e.recipesMaxTaskChars=="number"&&e.recipesMaxTaskChars>=0?e.recipesMaxTaskChars===0?0:Math.min(Number.MAX_SAFE_INTEGER,Math.floor(e.recipesMaxTaskChars)):N.recipesMaxTaskChars,recipesMacroDefaultCommand:typeof e.recipesMacroDefaultCommand=="string"&&e.recipesMacroDefaultCommand.trim().length>0?e.recipesMacroDefaultCommand.trim().slice(0,4096):N.recipesMacroDefaultCommand,clusterEnabled:typeof e.clusterEnabled=="boolean"?e.clusterEnabled:N.clusterEnabled,clusterLabel:typeof e.clusterLabel=="string"?e.clusterLabel.trim().slice(0,128):N.clusterLabel,clusterRole:Um(e.clusterRole),clusterSenderBindings:Dm(e.clusterSenderBindings),serviceInstallFromChat:typeof e.serviceInstallFromChat=="boolean"?e.serviceInstallFromChat:N.serviceInstallFromChat,updateCheckEnabled:typeof e.updateCheckEnabled=="boolean"?e.updateCheckEnabled:N.updateCheckEnabled,updateCheckIntervalMs:typeof e.updateCheckIntervalMs=="number"&&e.updateCheckIntervalMs>0?Math.min(6048e5,Math.max(36e5,Math.floor(e.updateCheckIntervalMs))):N.updateCheckIntervalMs,updateCheckPackageName:typeof e.updateCheckPackageName=="string"&&e.updateCheckPackageName.trim().length>0?e.updateCheckPackageName.trim().slice(0,214):N.updateCheckPackageName,updateInfoUrl:typeof e.updateInfoUrl=="string"?e.updateInfoUrl.trim().slice(0,2048):N.updateInfoUrl,chatLlmFallbackEnabled:typeof e.chatLlmFallbackEnabled=="boolean"?e.chatLlmFallbackEnabled:N.chatLlmFallbackEnabled,chatLlmShellCommand:typeof e.chatLlmShellCommand=="string"?e.chatLlmShellCommand.trim().slice(0,8192):N.chatLlmShellCommand,chatLlmTimeoutMs:typeof e.chatLlmTimeoutMs=="number"&&e.chatLlmTimeoutMs>0?Math.min(9e5,Math.floor(e.chatLlmTimeoutMs)):N.chatLlmTimeoutMs,chatLlmMaxInputChars:typeof e.chatLlmMaxInputChars=="number"&&e.chatLlmMaxInputChars>0?Math.min(5e5,Math.floor(e.chatLlmMaxInputChars)):N.chatLlmMaxInputChars,chatLlmMaxOutputChars:typeof e.chatLlmMaxOutputChars=="number"&&e.chatLlmMaxOutputChars>0?Math.min(2e6,Math.floor(e.chatLlmMaxOutputChars)):N.chatLlmMaxOutputChars,chatLlmNeedsTty:typeof e.chatLlmNeedsTty=="boolean"?e.chatLlmNeedsTty:N.chatLlmNeedsTty,chatLlmWorkDir:typeof e.chatLlmWorkDir=="string"?e.chatLlmWorkDir.trim().slice(0,4096):N.chatLlmWorkDir,tunnelEnabled:typeof e.tunnelEnabled=="boolean"?e.tunnelEnabled:N.tunnelEnabled,tunnelRelayUrl:typeof e.tunnelRelayUrl=="string"&&e.tunnelRelayUrl.trim().length>0?e.tunnelRelayUrl.trim().slice(0,2048):N.tunnelRelayUrl,tunnelMaxActive:typeof e.tunnelMaxActive=="number"&&e.tunnelMaxActive>0?Math.min(50,Math.floor(e.tunnelMaxActive)):N.tunnelMaxActive,platformToken:typeof e.platformToken=="string"?e.platformToken.trim():N.platformToken,platformDeviceId:typeof e.platformDeviceId=="string"?e.platformDeviceId.trim().slice(0,128):N.platformDeviceId,webhookEnabled:typeof e.webhookEnabled=="boolean"?e.webhookEnabled:N.webhookEnabled,webhookPort:typeof e.webhookPort=="number"&&e.webhookPort>=0?Math.min(65535,Math.floor(e.webhookPort)):N.webhookPort,webhookHost:typeof e.webhookHost=="string"&&e.webhookHost.trim().length>0?e.webhookHost.trim():N.webhookHost,webhookToken:typeof e.webhookToken=="string"?e.webhookToken.trim():N.webhookToken,watchEnabled:typeof e.watchEnabled=="boolean"?e.watchEnabled:N.watchEnabled,watchDebounceMs:typeof e.watchDebounceMs=="number"&&Number.isFinite(e.watchDebounceMs)?Math.max(500,Math.min(6e4,Math.floor(e.watchDebounceMs))):N.watchDebounceMs,watchMaxEventsPerMinute:typeof e.watchMaxEventsPerMinute=="number"&&Number.isFinite(e.watchMaxEventsPerMinute)?Math.max(1,Math.min(120,Math.floor(e.watchMaxEventsPerMinute))):N.watchMaxEventsPerMinute,watchAutoRestore:typeof e.watchAutoRestore=="boolean"?e.watchAutoRestore:N.watchAutoRestore}}function W(e){let t=$(),n=ir({...t,...e});return _e(n),n}function $(){if(te(),!Yr.existsSync(_)){let e=ir({});return _e(e),e}try{let e=Yr.readFileSync(_,"utf8"),t=JSON.parse(e);return ir(t)}catch{return ir({})}}function _e(e){te();let t=ir(e);Yr.writeFileSync(_,JSON.stringify(t,null,2)+`
3
+ `,{mode:384})}function Qr(e){let t=Bs(e);if(!t)throw new Error("Invalid entry. Use +E164 (WhatsApp) or tg:<user_id> (Telegram).");let n=$();if(t.kind==="wa"){if(t.normalized==="*")throw new Error("Wildcards are not allowed.");let r=new Set(n.allowFrom);r.add(t.normalized),n.allowFrom=[...r].sort()}else{let r=new Set(n.telegramAllowFrom);r.add(t.id),n.telegramAllowFrom=[...r].sort()}return _e(n),n}function Vr(e){let t=Bs(e);if(!t)throw new Error("Invalid entry. Use +E164 (WhatsApp) or tg:<user_id> (Telegram).");let n=$();return t.kind==="wa"?n.allowFrom=n.allowFrom.filter(r=>r!==t.normalized):n.telegramAllowFrom=n.telegramAllowFrom.filter(r=>Ee(r)!==t.id),_e(n),n}function ve(e){let t=typeof process.env.TELEGRAM_BOT_TOKEN=="string"?process.env.TELEGRAM_BOT_TOKEN.trim():"";return t||(e.telegramBotToken??"").trim()}function pt(e){let t=e.trim();return/^\d{6,}:[A-Za-z0-9_-]{20,}$/.test(t)}function Nt(e){let t=$();return t.telegramBotToken=e.trim(),_e(t),$()}function Sn(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 Xr(e){let t=$();return t.gatewayMode=e,_e(t),$()}function Zr(e){let t=$();return t.clusterEnabled=e,_e(t),$()}function nt(){try{return Yr.readdirSync(ne).length>0}catch{return!1}}var N,me=Ot(()=>{"use strict";G();qe();N={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",appsSkipClearOnPasswordPrompt:!0,appsPasswordPromptHint:!0,fileSendMaxBytes:0,fileReceiveMaxBytes:0,fileInboxSubdir:"inbox",fileReceiveRootMode:"downloads",fileReceiveRootPath:"",recipesAllowDangerousBuiltins:!1,recipesMaxTaskChars:0,recipesMacroDefaultCommand:'claude -p "$OMNISH_TASK"',clusterEnabled:!1,clusterLabel:"",clusterRole:"secondary",clusterSenderBindings:{},serviceInstallFromChat:!1,updateCheckEnabled:!1,updateCheckIntervalMs:864e5,updateCheckPackageName:"omnish",updateInfoUrl:"",chatLlmFallbackEnabled:!1,chatLlmShellCommand:"",chatLlmTimeoutMs:12e4,chatLlmMaxInputChars:16e3,chatLlmMaxOutputChars:24e3,chatLlmNeedsTty:!1,chatLlmWorkDir:"",tunnelEnabled:!1,tunnelRelayUrl:"https://tunnel.omnish.dev",platformToken:"",platformDeviceId:"",tunnelMaxActive:5,webhookEnabled:!1,webhookPort:0,webhookHost:"127.0.0.1",webhookToken:"",watchEnabled:!1,watchDebounceMs:2e3,watchMaxEventsPerMinute:30,watchAutoRestore:!0}});import Hm from"pino";function vn(){return process.env.OMNISH_VERBOSE==="1"||process.env.WHATSVERBOSE==="1"}function ul(){return vn()?"info":"silent"}function js(e){e?process.env.OMNISH_VERBOSE="1":delete process.env.OMNISH_VERBOSE,M.level=ul()}function dl(){return M.child({module:"baileys"})}var M,be=Ot(()=>{"use strict";M=Hm({level:ul(),base:{app:"omnish"}})});import ri from"node:fs";function $t(){try{let e=ri.readFileSync(Dr,"utf8"),t=JSON.parse(e);return!t||typeof t.token!="string"||!t.token.trim()?null:{token:t.token.trim(),relayUrl:typeof t.relayUrl=="string"?t.relayUrl.trim():void 0}}catch{return null}}function ht(e){te(),ri.writeFileSync(Dr,JSON.stringify({token:e.token.trim(),...e.relayUrl?{relayUrl:e.relayUrl.trim()}:{}},null,2)+`
4
+ `,{mode:384})}function mo(){try{ri.unlinkSync(Dr)}catch{}}function Df(){return process.env.OMNISH_TOKEN?.trim()||process.env.OMNISH_TUNNEL_TOKEN?.trim()||process.env.OMNISH_DEVICE_TOKEN?.trim()||""}function gt(){let e=Df();if(e)return e;let t=$().platformToken.trim();return t||($t()?.token??"")}function oi(e){let t=process.env.OMNISH_PLATFORM_URL?.trim()||process.env.OMNISH_COMM_LAYER_URL?.trim()||process.env.OMNISH_TUNNEL_RELAY?.trim();if(t)return t.replace(/\/$/,"");let n=$().tunnelRelayUrl.trim();if(n)return n.replace(/\/$/,"");let r=$t()?.relayUrl?.trim();return r?r.replace(/\/$/,""):e.replace(/\/$/,"")}function rt(e){return oi(e)}var rn=Ot(()=>{"use strict";me();G()});var $e,on=Ot(()=>{"use strict";$e="https://tunnel.omnish.dev"});function Vt(e){return(e??"").trim()}function Cy(){return Vt(process.env.OMNISH_PLATFORM_URL)||Vt(process.env.OMNISH_COMM_LAYER_URL)||Vt(process.env.OMNISH_TUNNEL_RELAY)}function My(){return Vt(process.env.OMNISH_TOKEN)||Vt(process.env.OMNISH_DEVICE_TOKEN)||Vt(process.env.OMNISH_TUNNEL_TOKEN)}function na(){return gt()}function zn(){return My()?"env":$().platformToken.trim()?"config":$t()?.token?"file":"default"}function Rr(){let e=$();return oi(e.tunnelRelayUrl||$e)}function qn(){return Cy()?"env":$().tunnelRelayUrl.trim()?"config":$t()?.relayUrl?.trim()?"file":"default"}function ra(){let e=Vt(process.env.OMNISH_DEVICE_ID);if(e)return e;let t=$().platformDeviceId.trim();if(t)return t}function oa(){return Vt(process.env.OMNISH_DEVICE_ID)?"env":$().platformDeviceId.trim()?"config":"default"}function se(){let e=Rr(),t=na();if(!e||!t)return null;let n=ra();return{platformUrl:e.replace(/\/$/,""),token:t,deviceId:n}}var it=Ot(()=>{"use strict";me();rn();on()});var vd={};Im(vd,{fetchPlatformAccount:()=>hn,getAttachedConfig:()=>la,getAttachedPlatformSnapshot:()=>Py,loadConfigForSendtoBroadcast:()=>ca,mergeAttachedPlatformPolicy:()=>aa,parsePlatformMeResponse:()=>kd,setAttachedPlatformSnapshot:()=>sa,setPlatformDefaultDevice:()=>ua,snapshotFromRegisteredAccount:()=>Sd,syncAttachedPlatformPolicy:()=>is,updatePlatformAllowlists:()=>ss});function ia(e){let t=e.replace(/\/$/,"");return/^https?:\/\//i.test(t)?t:`http://${t}`}function Ty(e){return e==="telegram"||e==="both"||e==="whatsapp"?e:"whatsapp"}function Ey(e){if(!e||typeof e!="object")return{};let t={};for(let[n,r]of Object.entries(e)){if(!r||typeof r!="object")continue;let o=r,s=typeof o.status=="string"?o.status:"idle";t[n]={status:s,linked:o.linked===!0||s==="linked",...typeof o.tokenConfigured=="boolean"?{tokenConfigured:o.tokenConfigured}:{}}}return t}function kd(e){let t=e.routing,n=t&&typeof t=="object"?t:null,r=Array.isArray(n?.onlineDeviceIds)?n.onlineDeviceIds.map(String):[],o=n?.defaultDeviceId!=null?String(n.defaultDeviceId):e.defaultDeviceId!=null?String(e.defaultDeviceId):null;return{allowFrom:Array.isArray(e.allowFrom)?e.allowFrom.map(String):[],telegramAllowFrom:Array.isArray(e.telegramAllowFrom)?e.telegramAllowFrom.map(String):[],gatewayMode:Ty(e.gatewayMode),connectors:Ey(e.connectors),defaultDeviceId:o,routing:{defaultDeviceId:o,onlineDeviceIds:r,onlineCount:typeof n?.onlineCount=="number"?n.onlineCount:r.length}}}function Sd(e,t=[],n=[]){let r=e.defaultDeviceId??null;return{allowFrom:t,telegramAllowFrom:n,gatewayMode:e.gatewayMode,connectors:e.connectors,defaultDeviceId:r,routing:{defaultDeviceId:r,onlineDeviceIds:[],onlineCount:0}}}function aa(e,t){return{...e,allowFrom:[...t.allowFrom],telegramAllowFrom:[...t.telegramAllowFrom],gatewayMode:t.gatewayMode}}function sa(e){os=e}function Py(){return os}function la(){let e=$();return os?aa(e,os):e}async function ca(){let e=$(),t=se();if(!t)return e;try{return aa(e,await hn(t))}catch(n){return M.warn({err:String(n)},"sendto: could not fetch platform allowlists; using local config.json"),e}}async function hn(e){let t=await fetch(`${ia(e.platformUrl)}/v1/me`,{headers:{Authorization:`Bearer ${e.token}`}});if(!t.ok)throw new Error(`Platform GET /v1/me failed: HTTP ${t.status}`);let n=await t.json();return kd(n)}async function ss(e,t){let n=await fetch(`${ia(e.platformUrl)}/v1/me/allowlists`,{method:"PUT",headers:{"content-type":"application/json",Authorization:`Bearer ${e.token}`},body:JSON.stringify(t)}),r=await n.json().catch(()=>({}));if(!n.ok)throw new Error(r.error||`Platform PUT /v1/me/allowlists failed: HTTP ${n.status}`);return{allowFrom:Array.isArray(r.allowFrom)?r.allowFrom.map(String):[],telegramAllowFrom:Array.isArray(r.telegramAllowFrom)?r.telegramAllowFrom.map(String):[]}}async function ua(e,t){let n=await fetch(`${ia(e.platformUrl)}/v1/me/default-device`,{method:"PUT",headers:{"content-type":"application/json",Authorization:`Bearer ${e.token}`},body:JSON.stringify({deviceId:t})});if(!n.ok){let r=await n.json().catch(()=>({}));throw new Error(r.error||`Platform PUT /v1/me/default-device failed: HTTP ${n.status}`)}}async function is(e,t){try{let n=await hn(e);return sa(n),M.info({gatewayMode:n.gatewayMode,waLinked:n.connectors.whatsapp?.linked===!0,tgLinked:n.connectors.telegram?.linked===!0,allowFromCount:n.allowFrom.length,telegramAllowFromCount:n.telegramAllowFrom.length},"attached platform policy loaded"),n}catch(n){if(t){let r=Sd(t);return sa(r),M.warn({err:String(n)},"platform GET /v1/me failed; using register ack for gatewayMode/connectors only (allowlists from local config until /v1/me works)"),r}return M.warn({err:String(n)},"platform GET /v1/me failed; attached inbound uses local config.json allowlists"),null}}var os,Cr=Ot(()=>{"use strict";me();be();it();os=null});me();import Xb from"node:dns";import Zb from"node:crypto";import en from"node:fs";import Za from"node:path";import $m from"node:os";me();be();import Tf from"node:os";import Ef from"node:fs";G();import pl from"node:crypto";import ar from"node:fs";import Gs from"node:os";import xn from"node:path";var Bm=/^[a-zA-Z0-9][a-zA-Z0-9_-]{0,31}$/;function Ks(e){let t=e.trim().toLowerCase();return Bm.test(t)?{ok:!0,name:t}:{ok:!1,error:"Name must be alphanumeric with _ or -, max 32 chars."}}function Ge(e,t){let n=e.trim();return n===""||n==="."?xn.resolve(t):n==="~"?Gs.homedir():n.startsWith("~/")||n.startsWith("~\\")?xn.join(Gs.homedir(),n.slice(2)):xn.isAbsolute(n)?n:xn.resolve(t,n)}function eo(e){return xn.join(Gs.homedir(),"Cowork",e)}function jm(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:eo(r),l=typeof t.enabled=="boolean"?t.enabled:!0,d=typeof t.notify=="string"?t.notify.toLowerCase():"self",u=d==="wa"||d==="whatsapp"?"wa":d==="tg"||d==="telegram"?"tg":d==="all"?"all":d==="none"?"none":"self",c={kind:"ondemand"};if(t.schedule&&typeof t.schedule=="object"){let v=t.schedule,E=typeof v.kind=="string"?v.kind:"";if(E==="ondemand")c={kind:"ondemand"};else if(E==="daily"){let R=Number(v.hour),P=Number(v.minute);Number.isFinite(R)&&Number.isFinite(P)&&(c={kind:"daily",hour:R,minute:P})}else if(E==="weekdays"){let R=Number(v.hour),P=Number(v.minute);Number.isFinite(R)&&Number.isFinite(P)&&(c={kind:"weekdays",hour:R,minute:P})}else if(E==="hourly"){let R=Number(v.minute);Number.isFinite(R)&&Number.isInteger(R)&&R>=0&&R<=59&&(c={kind:"hourly",minute:R})}else if(E==="weekly"){let R=Number(v.hour),P=Number(v.minute),T=Number(v.weekday);Number.isFinite(R)&&Number.isFinite(P)&&Number.isInteger(T)&&(c={kind:"weekly",weekday:T,hour:R,minute:P})}else if(E==="heartbeat"){let R=Number(v.intervalMs),P=Number(v.graceMs);Number.isFinite(R)&&R>0&&Number.isFinite(P)&&P>0&&(c={kind:"heartbeat",intervalMs:R,graceMs: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(),h=typeof t.attachLog=="boolean"?t.attachLog:!1,g=Array.isArray(t.attachFiles)?t.attachFiles.filter(v=>typeof v=="string"&&v.trim().length>0):[],y=typeof t.notifyWhen=="string"?t.notifyWhen.toLowerCase():"always";return{id:n,name:r,ownerPeerKey:o,command:s,cwd:i,outputDir:a,schedule:c,enabled:l,notify:u,notifyWhen:y==="failure"?"failure":y==="state-change"?"state-change":"always",attachLog:h,attachFiles:g,lastCompletedSlotMs:m,createdAtMs:f}}function xe(){try{let e=ar.readFileSync(Ds,"utf8"),t=JSON.parse(e);if(!t||!Array.isArray(t.tasks))return[];let n=[];for(let r of t.tasks){let o=jm(r);o&&n.push(o)}return n}catch{return[]}}function Oe(e){j(dt);let t={tasks:e},n=xn.join(dt,`.tasks.${process.pid}.${pl.randomBytes(4).toString("hex")}.tmp`);ar.writeFileSync(n,JSON.stringify(t,null,2)+`
5
+ `,{mode:384}),ar.renameSync(n,Ds)}function Ye(e,t,n){let r=t.trim().toLowerCase();return e.find(o=>o.name===r&&o.ownerPeerKey===n)}function lr(){return pl.randomBytes(4).toString("hex")}function ml(e){j(dt),ar.writeFileSync(Hs,JSON.stringify(e,null,2)+`
6
+ `,{mode:384})}function fl(e){let t=Js();t.push(e),ml(t)}function Js(){try{let e=ar.readFileSync(Hs,"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 hl(e){if(e<=0)return{batch:[],remainingAfter:Js().length};let t=Js();if(t.length===0)return{batch:[],remainingAfter:0};let n=t.slice(0,e),r=t.slice(e);return ml(r),{batch:n,remainingAfter:r.length}}G();G();import Gm from"better-sqlite3";var gl=1,yl=200,$n=null;function Jm(){j(kt);let e=new Gm(Jr);e.pragma("journal_mode = WAL"),e.exec(`
6
7
  CREATE TABLE IF NOT EXISTS watch_meta (
7
8
  key TEXT PRIMARY KEY,
8
9
  value TEXT NOT NULL
@@ -21,31 +22,30 @@ var Rm=Object.defineProperty;var Zn=(e,t)=>()=>(e&&(t=e(e=0)),t);var Cm=(e,t)=>{
21
22
  state_key TEXT NOT NULL,
22
23
  updated_at_ms INTEGER NOT NULL
23
24
  );
24
- `);let t=e.prepare("SELECT value FROM watch_meta WHERE key = 'schema_version'").get();return(t?Number(t.value):0)<ul&&e.prepare("INSERT OR REPLACE INTO watch_meta (key, value) VALUES ('schema_version', ?)").run(String(ul)),e}function ir(){return bn||(bn=Um()),bn}function Vr(){ir()}function pl(){if(bn){try{bn.close()}catch{}bn=null}}function ml(e){let t=ir();t.prepare("INSERT INTO watch_recent (rule_id, rule_name, kind, summary, ts_ms) VALUES (?, ?, ?, ?, ?)").run(e.ruleId,e.ruleName,e.kind,e.summary.slice(0,2e3),e.tsMs);let n=t.prepare("SELECT COUNT(*) AS c FROM watch_recent").get();n.c>dl&&t.prepare(`DELETE FROM watch_recent WHERE id IN (
25
+ `);let t=e.prepare("SELECT value FROM watch_meta WHERE key = 'schema_version'").get();return(t?Number(t.value):0)<gl&&e.prepare("INSERT OR REPLACE INTO watch_meta (key, value) VALUES ('schema_version', ?)").run(String(gl)),e}function cr(){return $n||($n=Jm()),$n}function to(){cr()}function wl(){if($n){try{$n.close()}catch{}$n=null}}function bl(e){let t=cr();t.prepare("INSERT INTO watch_recent (rule_id, rule_name, kind, summary, ts_ms) VALUES (?, ?, ?, ?, ?)").run(e.ruleId,e.ruleName,e.kind,e.summary.slice(0,2e3),e.tsMs);let n=t.prepare("SELECT COUNT(*) AS c FROM watch_recent").get();n.c>yl&&t.prepare(`DELETE FROM watch_recent WHERE id IN (
25
26
  SELECT id FROM watch_recent ORDER BY ts_ms ASC LIMIT ?
26
- )`).run(n.c-dl)}function fl(e,t){let n=ir(),r=Math.min(Math.max(1,e),50),o=n.prepare("SELECT rule_id, rule_name, kind, summary, ts_ms FROM watch_recent ORDER BY ts_ms DESC LIMIT ?").all(r*3),s=[];for(let i of o)if(!(t&&!t.has(i.rule_id))&&(s.push({ruleId:i.rule_id,ruleName:i.rule_name,kind:i.kind,stateKey:"",summary:i.summary,tsMs:i.ts_ms}),s.length>=r))break;return s}function hl(e){return ir().prepare("SELECT state_key FROM watch_rule_state WHERE rule_id = ?").get(e)?.state_key??null}function gl(e,t,n){ir().prepare("INSERT OR REPLACE INTO watch_rule_state (rule_id, state_key, updated_at_ms) VALUES (?, ?, ?)").run(e,t,n)}import Sl from"node:path";import kt from"node:path";function Dm(e,t){let n=kt.normalize(e);for(let r of t){let o=kt.normalize(r);if(n===o||n.startsWith(o+kt.sep))return!0}return!1}function Hm(e,t,n){let r=kt.relative(t,e);if(r.startsWith("..")||kt.isAbsolute(r))return!1;let o=r.split(kt.sep).join("/"),s=n.replace(/\\/g,"/").replace(/^\//,"");if(s.includes("**")){let i=s.replace(/[.+^${}()|[\]\\]/g,"\\$&").replace(/\*\*/g,"___GLOBSTAR___").replace(/\*/g,"[^/]*").replace(/___GLOBSTAR___/g,".*");try{if(new RegExp(`^${i}$`).test(o))return!0;if(s.startsWith("**/")){let l=s.slice(3);if(l&&!l.includes("*"))return o.split("/").includes(l)}return!1}catch{return!1}}if(s.includes("*")){let i=s.replace(/[.+^${}()|[\]\\]/g,"\\$&").replace(/\*/g,"[^/]*");try{return new RegExp(`^${i}$`).test(o)}catch{return!1}}return o===s||o.endsWith("/"+s)||o.includes("/"+s+"/")}function Xr(e,t){if(t.excludePaths.length&&Dm(e,t.excludePaths))return!0;let n=t.path;if(n&&t.excludeGlobs.length){for(let r of t.excludeGlobs)if(Hm(e,n,r))return!0}return!1}function yl(e){let t=[];for(let n of e.excludeGlobs){let r=n.replace(/\\/g,"/");r.startsWith("**/")?t.push(r):r.includes("*")?t.push(`**/${r}`):t.push(`**/${r}/**`)}for(let n of e.excludePaths)if(e.path)try{let r=kt.relative(e.path,n);!r.startsWith("..")&&!kt.isAbsolute(r)&&t.push(r.split(kt.sep).join("/")+"/**")}catch{}return t}qe();function kn(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=ee(String(o));s&&r.add(`wa:${It(s)}`)}if(e==="tg"||e==="all")for(let o of n.telegramAllowFrom){let s=Ee(String(o));s&&r.add(`tg:${s}`)}return[...r]}function wl(e,t,n){return kn(e,t,n)}be();G();import Bm from"node:crypto";import Gs from"node:fs";var jm=/^[a-zA-Z0-9][a-zA-Z0-9_-]{0,31}$/,Gm=1,Js=20;function Zr(e){let t=e.trim().toLowerCase();return jm.test(t)?{ok:!0,name:t}:{ok:!1,error:"Name must be alphanumeric with _ or -, max 32 chars."}}function eo(){return Bm.randomBytes(8).toString("hex")}function Jm(e){if(!Array.isArray(e)||e.length===0)return["create","delete","rename"];let t=new Set(["create","delete","rename","update"]),n=[];for(let r of e)typeof r=="string"&&t.has(r)&&n.push(r);return n.length?n:["create","delete","rename"]}function Km(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.kind=="string"?t.kind:"",i=s==="fs"||s==="pkg"||s==="svc"?s:null;if(!n||!r||!o||!i)return null;let a=typeof t.notify=="string"?t.notify.toLowerCase():"self",l=a==="wa"||a==="whatsapp"?"wa":a==="tg"||a==="telegram"?"tg":a==="all"?"all":a==="none"?"none":"self",d=typeof t.notifyWhen=="string"?t.notifyWhen:"always",u=d==="failure"||d==="state-change"?d:"always",c=Array.isArray(t.units)?t.units.filter(h=>typeof h=="string"&&h.trim().length>0).map(h=>h.trim()):[],m=Array.isArray(t.excludePaths)?t.excludePaths.filter(h=>typeof h=="string"&&h.trim().length>0):[],f=Array.isArray(t.excludeGlobs)?t.excludeGlobs.filter(h=>typeof h=="string"&&h.trim().length>0):[];return{id:n,name:r,ownerPeerKey:o,kind:i,enabled:typeof t.enabled=="boolean"?t.enabled:!0,paused:typeof t.paused=="boolean"?t.paused:!1,notify:l,notifyWhen:u,path:typeof t.path=="string"?t.path:"",events:Jm(t.events),units:c,excludePaths:m,excludeGlobs:f,adapterStatus:typeof t.adapterStatus=="string"?t.adapterStatus:"",createdAtMs:typeof t.createdAtMs=="number"&&Number.isFinite(t.createdAtMs)?t.createdAtMs:Date.now()}}function zm(e){let t=JSON.parse(e);return!t||!Array.isArray(t.rules)?[]:t.rules.map(Km).filter(n=>n!==null)}function qm(e){let t=[...e].sort((s,i)=>s.createdAtMs-i.createdAtMs),n=new Set,r=!1,o=[];for(let s of t){if(!n.has(s.name)){n.add(s.name),o.push(s);continue}let i=s.id.slice(0,8),a=`${s.name}-${i}`;a.length>32&&(a=`${s.name.slice(0,32-i.length-1)}-${i}`);let l=0;for(;n.has(a);){l+=1;let d=String(l);a=`${s.name.slice(0,Math.max(1,32-i.length-d.length-1))}-${i}${d}`}n.add(a),M.warn({oldName:s.name,newName:a,id:s.id},"watch: renamed duplicate rule for device-wide namespace"),o.push({...s,name:a}),r=!0}return{rules:o,changed:r}}function Le(){try{let e=Gs.readFileSync(tr,"utf8"),t=zm(e),{rules:n,changed:r}=qm(t);return r&&pt(n),n}catch{return[]}}function Ym(e){let t=0,n=0,r=0;for(let o of e)o.enabled?o.paused?n+=1:t+=1:r+=1;return{total:e.length,active:t,paused:n,disabled:r}}function ar(){let e=Le();return{rules:e,summary:Ym(e)}}function pt(e){j(bt);let n=`${JSON.stringify({version:Gm,rules:e},null,2)}
27
- `,r=`${tr}.tmp`;Gs.writeFileSync(r,n,{mode:384}),Gs.renameSync(r,tr)}function Ks(){return tr}function Zt(e,t){let n=t.trim().toLowerCase();return e.find(r=>r.name===n)}function bl(e,t){return e.find(n=>n.id===t)}function Sn(e,t){let n=e.findIndex(r=>r.id===t.id);if(n>=0){let r=[...e];return r[n]=t,r}return[...e,t]}function kl(e,t){let n=t.trim().toLowerCase();return e.filter(r=>r.name!==n)}import Qm from"node:os";import mt from"node:path";var Vm=new Set([".env","id_rsa","id_ed25519","id_ecdsa","known_hosts","credentials.json","secrets.json",".netrc","shadow","passwd"]);function Qe(e){let t=mt.normalize(e),n=t.toLowerCase(),r=Qm.homedir().toLowerCase();if(n.includes(`${mt.sep}.ssh${mt.sep}`)||n.endsWith(`${mt.sep}.ssh`)||n.includes(`${mt.sep}browser${mt.sep}`)&&n.includes("profile")||r&&(n===`${r}/.gnupg`||n.startsWith(`${r}/.gnupg${mt.sep}`))||n.includes(`${mt.sep}keychains${mt.sep}`))return!0;let o=mt.basename(t);return!!(Vm.has(o)||o.startsWith(".env.")||o.endsWith(".pem")||o.endsWith(".key"))}var Xm=["node_modules",".git",".svn",".hg","__pycache__",".cache",".next","dist","build"],Zm=[".tmp",".swp",".swx","~",".part"];function vl(e,t){let n=new Map,r=new Map;function o(u,c){let m=u.split(Sl.sep);for(let h of m)if(Xm.includes(h))return!0;let f=Sl.basename(u);for(let h of Zm)if(f.endsWith(h))return!0;return!!Xr(u,c)}function s(u){let c=bl(Le(),u);return!c||!c.enabled||c.paused?null:c}function i(u){let c=Date.now(),m=r.get(u);return!m||c-m.windowStart>=6e4?(r.set(u,{windowStart:c,count:1}),!1):m.count>=t.maxEventsPerMinute?!0:(m.count+=1,!1)}async function a(u,c){let m=e.getConfig();if(!m.watchEnabled)return;let f=s(u.id);if(!f||c.kind==="fs"&&c.meta?.path&&(Qe(c.meta.path)||o(c.meta.path,f)))return;if(ml(c),(f.notifyWhen??"always")==="state-change"){if(hl(f.id)===c.stateKey)return;gl(f.id,c.stateKey,c.tsMs)}let g=wl(f.notify,f.ownerPeerKey,m);if(g.length===0)return;let y=`[watch:${f.name}] ${c.summary}`;await Promise.all(g.map(b=>e.sendToPeer(b,y).catch(()=>{})))}function l(u){let c=n.get(u);if(!c)return;n.delete(u);let m=s(c.ruleId);m&&(i(c.ruleId)||a(m,c.event))}function d(u){for(let[c,m]of n)m.ruleId===u&&(clearTimeout(m.timer),n.delete(c))}return{ingest(u,c){if(!u.enabled||u.paused||c.meta?.path&&(Qe(c.meta.path)||o(c.meta.path,u)))return;let m=`${u.id}:${c.stateKey||c.summary}`,f=n.get(m);f&&clearTimeout(f.timer);let h=setTimeout(()=>l(m),t.debounceMs);h.unref?.(),n.set(m,{timer:h,event:c,ruleId:u.id})},cancelForRule:d,dispose(){for(let u of n.values())clearTimeout(u.timer);n.clear(),r.clear()}}}function xl(e,t){let n=e.toLowerCase();return!!(n==="create"&&t.has("create")||n==="delete"&&t.has("delete")||(n==="update"||n==="rename")&&(t.has("update")||t.has("rename")))}be();import $l from"node:fs";import ef from"node:path";import{subscribe as tf}from"@parcel/watcher";var nf=["**/node_modules/**","**/.git/**","**/.svn/**","**/.hg/**","**/__pycache__/**","**/.cache/**"];function rf(e){return e==="create"?"create":e==="delete"?"delete":e==="update"?"update":e}function of(e,t){try{let n=$l.statSync(t),r=n.isFile()&&n.size<1024*1024?` (${n.size<1024?`${n.size} B`:`${(n.size/1024).toFixed(1)} KB`})`:"";return`fs: ${e} ${t}${r}`}catch{return`fs: ${e} ${t}`}}async function Rl(e,t){let n=e.path;if(!n||!$l.existsSync(n))return{stop:async()=>{},status:()=>"error: path missing or not found"};if(Qe(n))return{stop:async()=>{},status:()=>"error: sensitive path denied"};let r=new Set(e.events.map(i=>i.toLowerCase())),o="ok",s=null;try{s=await tf(n,(i,a)=>{if(i){o=`error: ${i.message}`,M.warn({rule:e.name,err:i.message},"watch fs adapter");return}for(let l of a){let d=rf(l.type);if(!xl(d,r))continue;let u=ef.resolve(l.path);if(!u.startsWith(n)||Qe(u)||Xr(u,e))continue;let c=of(d,u);t({ruleId:e.id,ruleName:e.name,kind:"fs",stateKey:`${d}:${u}`,summary:c,tsMs:Date.now(),meta:{path:u,type:d}})}},{ignore:[...nf,...yl(e)]})}catch(i){o=`error: ${String(i)}`,M.warn({rule:e.name,err:String(i)},"watch fs subscribe failed")}return{stop:async()=>{s&&(await s.unsubscribe().catch(()=>{}),s=null)},status:()=>o}}import mf from"node:os";be();import Lt from"node:fs";function sf(e,t,n){let r=n?.pollMs??2e3,o=0,s=!1,i=null,a=null,l="starting";function d(){if(!s)try{let u=Lt.statSync(e);if(u.size<o&&(o=0),u.size<=o)return;let c=Lt.openSync(e,"r");try{let m=u.size-o,f=Buffer.alloc(m);Lt.readSync(c,f,0,m,o),o=u.size;let h=f.toString("utf8");for(let g of h.split(`
28
- `)){let y=g.trim();y&&t(y)}}finally{Lt.closeSync(c)}l="ok"}catch(u){l=`error: ${String(u)}`}}try{Lt.existsSync(e)&&(o=Lt.statSync(e).size),i=Lt.watch(e,()=>d()),i.on("error",()=>{i?.close(),i=null}),l="ok (watch)"}catch{l="ok (poll)",a=setInterval(d,r),a.unref?.(),d()}return{stop(){s=!0,i?.close(),a&&clearInterval(a)},status:()=>l}}function to(e,t){for(let n of e)try{if(Lt.existsSync(n))return sf(n,t)}catch(r){M.debug({path:n,err:String(r)},"watch log-tail skip path")}return null}var af=["/var/log/install.log"];function lf(e){let t=e.toLowerCase();return t.includes("installed")||t.includes("upgraded")||t.includes("removed")?e.length>240?`pkg: ${e.slice(0,240)}\u2026`:`pkg: ${e}`:null}function Cl(e,t){return to(af,n=>{let r=lf(n);r&&t({ruleId:e.id,ruleName:e.name,kind:"pkg",stateKey:r.slice(0,200),summary:r,tsMs:Date.now()})})}var cf=["/var/log/dpkg.log","/var/log/apt/history.log"];function uf(e){if(e.includes("status installed")){let t=e.match(/install\s+(\S+):/);if(t)return`pkg: installed ${t[1]} (dpkg)`}if(e.includes("status removed")||e.includes("remove ")){let t=e.match(/remove\s+(\S+):/);if(t)return`pkg: removed ${t[1]} (dpkg)`}return null}function df(e){return e.startsWith("Install:")||e.startsWith("Upgrade:")?`pkg: ${e.slice(0,120)} (apt)`:e.startsWith("Remove:")?`pkg: ${e.slice(0,120)} (apt)`:null}function Ml(e,t){return to(cf,n=>{let r=uf(n)??df(n);r&&t({ruleId:e.id,ruleName:e.name,kind:"pkg",stateKey:r,summary:r,tsMs:Date.now()})})}import{spawn as pf}from"node:child_process";function Tl(e,t){let n=!1,r="ok",o=0,s=()=>{if(n)return;let a=pf("powershell.exe",["-NoProfile","-NonInteractive","-Command","Get-WinEvent -FilterHashtable @{LogName='Application'; ProviderName=@('MsiInstaller','Windows Installer')} -MaxEvents 5 -ErrorAction SilentlyContinue | Select-Object -Property RecordId,Message | ConvertTo-Json -Compress"],{windowsHide:!0}),l="";a.stdout?.on("data",d=>{l+=String(d)}),a.on("close",d=>{if(n||d!==0||!l.trim()){d!==0&&(r="ok (no events or access denied)"),i();return}try{let u=JSON.parse(l),c=Array.isArray(u)?u:[u];for(let m of c.sort((f,h)=>f.RecordId-h.RecordId)){if(m.RecordId<=o)continue;o=m.RecordId;let f=(m.Message??"").replace(/\s+/g," ").trim().slice(0,300);if(!f)continue;let h=`pkg: ${f}`;t({ruleId:e.id,ruleName:e.name,kind:"pkg",stateKey:`win:${m.RecordId}`,summary:h,tsMs:Date.now()})}r="ok"}catch(u){r=`parse error: ${String(u)}`}i()}),a.on("error",d=>{r=`error: ${String(d)}`,i()})},i=()=>{n||setTimeout(s,3e4).unref?.()};return s(),{stop(){n=!0},status:()=>r}}function El(e,t){let n=mf.platform();if(n==="win32"){let o=Tl(e,t);return{stop:()=>o.stop(),status:o.status}}if(n==="darwin"){let o=Cl(e,t);return o?{stop:()=>o.stop(),status:o.status}:{stop:()=>{},status:()=>"error: cannot read /var/log/install.log (try sudo or adm group)"}}let r=Ml(e,t);return r?{stop:()=>r.stop(),status:r.status}:{stop:()=>{},status:()=>"error: cannot read dpkg/apt logs (add user to adm or run gateway with read access)"}}import Sf from"node:os";import{spawnSync as Pl}from"node:child_process";var ff=3e4;function hf(e){let t=new Map;for(let n of e){let r=Pl("launchctl",["print",`system/${n}`],{encoding:"utf8",timeout:1e4});if(r.status!==0){let s=process.getuid?.()??501,i=Pl("launchctl",["print",`gui/${s}/${n}`],{encoding:"utf8",timeout:1e4});if(i.status!==0){t.set(n,"not-found");continue}let a=(i.stdout??"").includes("state = running")?"running":"stopped";t.set(n,a);continue}let o=(r.stdout??"").includes("state = running")?"running":"stopped";t.set(n,o)}return t}function Al(e,t){let n=e.units,r=!1,o="ok",s=new Map,i=()=>{if(r)return;let a=hf(n);if(a.size===0)o="error: no launchd labels configured";else{o="ok";for(let[l,d]of a){let u=s.get(l);if(u!==void 0&&u!==d){let c=`svc: ${l} ${u} \u2192 ${d}`;t({ruleId:e.id,ruleName:e.name,kind:"svc",stateKey:`${l}:${d}`,summary:c,tsMs:Date.now(),meta:{unit:l,state:d}})}}s=a}r||setTimeout(i,ff).unref?.()};return i(),{stop(){r=!0},status:()=>o}}import{spawnSync as Il}from"node:child_process";var gf=3e4;function yf(e){let t=new Map;if(e.length===0)return t;let n=["show",...e,"--property=ActiveState,SubState,UnitFileState","--no-pager"],r=Il("systemctl",n,{encoding:"utf8",timeout:15e3});if(r.status!==0)return t;let o="";for(let s of(r.stdout??"").split(`
29
- `)){let i=s.match(/^Unit=(.+)$/);if(i){o=i[1];continue}let a=s.match(/^ActiveState=(.+)$/);a&&o&&t.set(o,a[1].trim())}if(t.size===0)for(let s of e){let i=s.endsWith(".service")?s:`${s}.service`,a=Il("systemctl",["is-active",i],{encoding:"utf8",timeout:5e3}),l=(a.stdout??a.stderr??"unknown").trim();t.set(i,l)}return t}function Ol(e,t){let n=e.units.map(a=>a.endsWith(".service")?a:`${a}.service`),r=!1,o="ok",s=new Map,i=()=>{if(r)return;let a=yf(n);if(a.size===0)o="error: systemctl unavailable or units not found";else{o="ok";for(let[l,d]of a){let u=s.get(l);if(u!==void 0&&u!==d){let c=`svc: ${l} ${u} \u2192 ${d}`;t({ruleId:e.id,ruleName:e.name,kind:"svc",stateKey:`${l}:${d}`,summary:c,tsMs:Date.now(),meta:{unit:l,state:d}})}}s=a}r||setTimeout(i,gf).unref?.()};return i(),{stop(){r=!0},status:()=>o}}import{spawnSync as wf}from"node:child_process";var bf=3e4;function kf(e){let t=new Map;for(let n of e){let r=wf("sc",["query",n],{encoding:"utf8",timeout:1e4,windowsHide:!0});if(r.status!==0){t.set(n,"not-found");continue}let o=r.stdout??"",s="unknown";o.includes("RUNNING")?s="running":o.includes("STOPPED")?s="stopped":o.includes("START_PENDING")?s="start-pending":o.includes("STOP_PENDING")&&(s="stop-pending"),t.set(n,s)}return t}function Ll(e,t){let n=e.units,r=!1,o="ok",s=new Map,i=()=>{if(r)return;let a=kf(n);if(a.size===0)o="error: no service names configured";else{o="ok";for(let[l,d]of a){let u=s.get(l);if(u!==void 0&&u!==d){let c=`svc: ${l} ${u} \u2192 ${d}`;t({ruleId:e.id,ruleName:e.name,kind:"svc",stateKey:`${l}:${d}`,summary:c,tsMs:Date.now(),meta:{unit:l,state:d}})}}s=a}r||setTimeout(i,bf).unref?.()};return i(),{stop(){r=!0},status:()=>o}}function Nl(e,t){let n=Sf.platform();return n==="win32"?Ll(e,t):n==="darwin"?Al(e,t):Ol(e,t)}var Ve=null,Nt=null;function _l(e){let t=e.getConfig();return vl(e,{debounceMs:Math.max(500,t.watchDebounceMs??2e3),maxEventsPerMinute:Math.max(1,t.watchMaxEventsPerMinute??30)})}function $f(e){return e.watchEnabled&&e.watchAutoRestore}var zs=class{deps;pipeline;runtimes=new Map;stopped=!1;constructor(t){this.deps=t,this.pipeline=_l(t)}onEvent=(t,n)=>{this.pipeline.ingest(t,n)};cancelPendingForRule(t){this.pipeline.cancelForRule(t)}async reload(){if(this.pipeline.dispose(),this.pipeline=_l(this.deps),await this.stopAdapters(),this.stopped||!this.deps.getConfig().watchEnabled)return 0;let n=Le().filter(r=>r.enabled&&!r.paused);for(let r of n)await this.startRule(r);return this.runtimes.size}async startRule(t){try{let n;if(t.kind==="fs"){let r=Ge(t.path,vf.homedir()),o={...t,path:r};if(Qe(r)){t.adapterStatus="denied: sensitive path",this.persistRuleStatus(t);return}if(!xf.existsSync(r)){t.adapterStatus="error: path not found",this.persistRuleStatus(t);return}let s=await Rl(o,i=>this.onEvent(o,i));n={stop:()=>s.stop(),status:s.status}}else if(t.kind==="pkg")n=El(t,o=>this.onEvent(t,o));else if(t.kind==="svc"){if(t.units.length===0){t.adapterStatus="error: no units",this.persistRuleStatus(t);return}n=Nl(t,o=>this.onEvent(t,o))}else return;t.adapterStatus=n.status(),this.persistRuleStatus(t),this.runtimes.set(t.id,{rule:t,adapter:n})}catch(n){t.adapterStatus=`error: ${String(n)}`,this.persistRuleStatus(t),M.warn({rule:t.name,err:String(n)},"watch rule start failed")}}persistRuleStatus(t){let n=Le(),r=n.findIndex(o=>o.id===t.id);r>=0&&(n[r]={...n[r],adapterStatus:t.adapterStatus},pt(n))}async stopAdapters(){for(let t of this.runtimes.values())try{await t.adapter.stop()}catch{}this.runtimes.clear()}async stop(){this.stopped=!0,await this.stopAdapters(),this.pipeline.dispose(),pl()}getStatusLines(){let t=Le();return t.length===0?["(no watch rules)"]:t.map(n=>{let o=this.runtimes.get(n.id)?.adapter.status()??n.adapterStatus,s=n.enabled?n.paused?"paused":"on":"disabled",i=n.kind==="fs"?n.path:n.kind==="svc"?n.units.join(","):"system",a=n.kind==="fs"&&(n.excludePaths.length||n.excludeGlobs.length)?` excludes:${n.excludePaths.length+n.excludeGlobs.length}`:"";return`${n.name} [${n.kind}] ${s} notify=${n.notify} when=${n.notifyWhen} \u2014 ${i}${a} \u2014 ${o}`})}getRunningCount(){return this.runtimes.size}isRuleRunning(t){return this.runtimes.has(t)}};function Fl(){return Ve}function qs(e){Ve?.cancelPendingForRule(e)}function Rf(){j(bt),Vr()}function Ys(){Nt&&(Ve||(Ve=new zs(Nt)),Ve.reload())}function no(e){Nt=e,Rf();let t=e.getConfig(),{summary:n}=ar();return n.total>0&&M.info({rules:n.total,active:n.active,paused:n.paused,disabled:n.disabled},"watch rules loaded from disk"),$f(t)&&Ys(),()=>{Ve?.stop(),Ve=null,Nt=null}}async function Wl(){return Nt?Nt.getConfig().watchEnabled?(Ys(),Ve?.getRunningCount()??0):(Ve?.stop(),Ve=null,-1):-1}function De(){if(!Nt)return;if(!Nt.getConfig().watchEnabled){Ve?.stop(),Ve=null;return}Ys()}G();pe();G();import Tf from"node:os";import oo from"node:path";G();import Qs from"node:fs";import Dl from"node:os";import _t from"node:path";var Hl=_t.join(H,"sessions.json"),vn=new Map;function Bl(){return{cwd:_t.resolve(Dl.homedir())}}function Cf(e){return e.startsWith("wa:")||e.startsWith("tg:")?e:`wa:${e}`}function Mf(){try{let e=Qs.readFileSync(Hl,"utf8"),t=JSON.parse(e);for(let[n,r]of Object.entries(t)){if(!r||typeof r!="object")continue;let o=Cf(n),i={cwd:typeof r.cwd=="string"&&r.cwd.length>0?_t.resolve(r.cwd):Bl().cwd};r.fileReceiveRoot==="sessionCwd"&&(i.fileReceiveRoot="sessionCwd"),vn.set(o,i)}}catch{}}function jl(){j(H);let e={};for(let[t,n]of vn){let r={cwd:n.cwd};n.fileReceiveRoot==="sessionCwd"&&(r.fileReceiveRoot="sessionCwd"),e[t]=r}Qs.writeFileSync(Hl,JSON.stringify(e,null,2)+`
30
- `,{mode:384})}var Ul=!1;function Vs(){Ul||(Mf(),Ul=!0)}function oe(e){Vs();let t=vn.get(e);return t||(t=Bl(),vn.set(e,t)),t}function ro(e,t){Vs();let n=_t.resolve(t),r=oe(e);r.cwd=n,vn.set(e,r),jl()}function Gl(e){return oe(e).fileReceiveRoot==="sessionCwd"?"sessionCwd":"default"}function Xs(e,t){Vs();let n=oe(e);t==="default"?delete n.fileReceiveRoot:n.fileReceiveRoot="sessionCwd",vn.set(e,n),jl()}function Jl(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 Kl(e,t){if(t.kind==="home")return _t.resolve(Dl.homedir());let n=t.value.replace(/^['"]|['"]$/g,"");return _t.isAbsolute(n)?_t.normalize(n):_t.resolve(e,n)}function zl(e){try{return Qs.statSync(e).isDirectory()?{ok:!0}:{ok:!1,error:`Not a directory: ${e}`}}catch(t){return{ok:!1,error:String(t)}}}var Ef="Omnish";function ql(){return oo.join(Tf.homedir(),"Downloads",Ef)}function Ft(e,t){let n=oe(t);if(n.fileReceiveRoot==="sessionCwd")return n.cwd;switch(e.fileReceiveRootMode){case"downloads":return ql();case"omnishData":return oo.join(H,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(!oo.isAbsolute(r))throw new Error('fileReceiveRootPath must be an absolute path when fileReceiveRootMode is "fixed".');return oo.resolve(r)}default:return ql()}}G();import $t from"node:fs";import Ql from"node:path";import Ht from"node:process";var en="\x1B";function Pf(e){return e.replace(/\u001B\[[\d;]*m/g,"")}function so(e){return Pf(e).length}function Zs(e,t,n,r,o=2){let s=Math.max(0,t-so(n));return`${e}${n}${" ".repeat(s)}${" ".repeat(o)}${r}`}function St(e,t,n,r){if(n.length===0)return[];let o=n.map(i=>r(i.left)),s=Math.max(...o.map(so));return n.map((i,a)=>Zs(t,s,o[a],S(e,i.right)))}var Wt={primary:"#b4ff24",foreground:"#eef2f4",muted:"#8a9199",border:"#2a3139",error:"#ef4444",warn:"#a0e614",warnAlt:"#8cce04"};function Af(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 Ut(e){let t=Af(e);return t?`${en}[38;2;${t.r};${t.g};${t.b}m`:""}function io(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 vt(e,t,n){return!t||!io(e)?n:`${t}${n}${en}[0m`}function J(e,t){return vt(e,`${en}[1m`,t)}function If(e,t){return io(e)?`${Ut(Wt.foreground)}${en}[1m${t}${en}[0m`:t}function V(e,t){return vt(e,`${en}[2m`,t)}function Ce(e,t){return vt(e,`${Ut(Wt.primary)}${en}[1m`,t)}function ce(e,t){return vt(e,Ut(Wt.primary),t)}function S(e,t){return vt(e,Ut(Wt.foreground),t)}function w(e,t){return vt(e,Ut(Wt.muted),t)}function Of(e,t){return vt(e,Ut(Wt.border),t)}function ao(e,t){return vt(e,Ut(Wt.error),t)}function me(e,t){return vt(e,Ut(Wt.warn),t)}function Dt(e,t=60,n="\u2500"){let r=n.repeat(Math.max(1,t));return Of(e,r)}function B(e,t){return io(e)?`${`${w(e,"[")}${ce(e,"omnish")}${w(e,"]")}`} ${t}`:`[omnish] ${t}`}function C(e,t){return io(e)?`${`${ao(e,"[omnish]")}`} ${t}`:`[omnish] ${t}`}function Yl(e,t){let n=[];for(let r of e)switch(r.kind){case"title":n.push("",Ce(t,r.text),"");break;case"sub":n.push("",If(t,r.text),"");break;case"gap":n.push("");break;case"p":n.push(S(t,r.text));break;case"bullet":n.push(`${w(t,"\u2022")} ${S(t,r.text)}`);break}return n.join(`
31
- `).replace(/^\n+/,"").trimEnd()}pe();G();pe();G();import ei from"node:fs";function xt(){try{let e=ei.readFileSync(_r,"utf8"),t=JSON.parse(e);return!t||typeof t.token!="string"||!t.token.trim()?null:{token:t.token.trim(),relayUrl:typeof t.relayUrl=="string"?t.relayUrl.trim():void 0}}catch{return null}}function ft(e){te(),ei.writeFileSync(_r,JSON.stringify({token:e.token.trim(),...e.relayUrl?{relayUrl:e.relayUrl.trim()}:{}},null,2)+`
32
- `,{mode:384})}function lo(){try{ei.unlinkSync(_r)}catch{}}function Lf(){return process.env.OMNISH_TOKEN?.trim()||process.env.OMNISH_TUNNEL_TOKEN?.trim()||process.env.OMNISH_DEVICE_TOKEN?.trim()||""}function ht(){let e=Lf();if(e)return e;let t=$().platformToken.trim();return t||(xt()?.token??"")}function ti(e){let t=process.env.OMNISH_PLATFORM_URL?.trim()||process.env.OMNISH_COMM_LAYER_URL?.trim()||process.env.OMNISH_TUNNEL_RELAY?.trim();if(t)return t.replace(/\/$/,"");let n=$().tunnelRelayUrl.trim();if(n)return n.replace(/\/$/,"");let r=xt()?.relayUrl?.trim();return r?r.replace(/\/$/,""):e.replace(/\/$/,"")}function rt(e){return ti(e)}var $e="https://tunnel.omnish.dev";function co(e){return(e&4)!==0}function ni(e){return(e&2)!==0}var Vl={error:3,warn:2,info:1};function Xl(e,t){let n=Vl[t];return e.filter(r=>Vl[r.severity]>=n)}function Rt(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 ${_} and delete wildcard entries.`,fixHint:`Edit ${_} 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 ${_} 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 ${_} unless you trust every allowlisted identity.`}),!Ql.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 ${_}.`});else try{$t.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 ${_} 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 ${_} points to a real file.`})}if(e.fileReceiveRootMode==="fixed"){let a=e.fileReceiveRootPath.trim();a?Ql.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 ${_}.`}):n.push({severity:"error",code:"receive-fixed-empty",message:'fileReceiveRootMode is "fixed" but fileReceiveRootPath is empty.',fixHint:`Set fileReceiveRootPath to an absolute directory in ${_}, or change fileReceiveRootMode.`})}if(Ht.platform!=="win32"){try{if($t.existsSync(_)){let d=$t.statSync(_);(co(d.mode)||ni(d.mode))&&n.push({severity:"warn",code:"config-permissions",message:"config.json is accessible to users other than the owner (group/world).",detail:`Recommended: chmod 600 ${_}`,fixHint:`chmod 600 ${_}`})}}catch{}(typeof Ht.getuid=="function"?Ht.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($t.existsSync(H)){let d=$t.statSync(H);(co(d.mode)||ni(d.mode))&&(l=!0,n.push({severity:"warn",code:"data-dir-permissive",message:"Omnish data directory is accessible to group or other users \u2014 auth, jobs, and logs may be exposed.",detail:`Recommended: chmod 700 ${H}`,fixHint:`chmod 700 ${H}`}))}}catch{}try{if(!l&&$t.existsSync(tt)){let d=$t.statSync(tt);(co(d.mode)||ni(d.mode))&&n.push({severity:"warn",code:"jobs-dir-permissive",message:"Jobs log directory is accessible to group or other users \u2014 command output may leak.",detail:`Recommended: chmod 700 ${tt}`,fixHint:`chmod 700 ${tt}`})}}catch{}try{if($t.existsSync(ne)){let d=$t.statSync(ne);co(d.mode)&&n.push({severity:"warn",code:"auth-dir-world-readable",message:"WhatsApp auth directory is readable by others \u2014 session material may be exposed.",detail:`Recommended: chmod 700 ${ne}`,fixHint:`chmod 700 ${ne}`})}}catch{}}return(typeof Ht.env.TELEGRAM_BOT_TOKEN=="string"?Ht.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&&!ve(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 ${_} or export TELEGRAM_BOT_TOKEN=<token>.`}),e.clusterEnabled&&s&&n.push({severity:"info",code:"cluster-telegram-tokens",message:"Cluster mode with Telegram: use a distinct BotFather bot/token per omnish host if several gateways run Telegram \u2014 the same token cannot be long-polled twice.",fixHint:"Create separate bots for each machine or run Telegram on fewer hosts."}),e.platformToken.trim()&&n.push({severity:"info",code:"platform-token-config",message:"platformToken is stored in config.json (same trust boundary as your shell account).",fixHint:"Use OMNISH_TOKEN in the environment for CI/ephemeral hosts; env overrides config."}),(Ht.env.OMNISH_TOKEN?.trim()||Ht.env.OMNISH_TUNNEL_TOKEN?.trim()||Ht.env.OMNISH_DEVICE_TOKEN?.trim())&&n.push({severity:"info",code:"platform-token-env",message:"OMNISH_TOKEN (or OMNISH_TUNNEL_TOKEN / OMNISH_DEVICE_TOKEN) is set in the environment.",fixHint:"Unset platform token env vars if you want config.json / tunnel-auth.json to apply."}),e.tunnelEnabled&&(n.push({severity:"warn",code:"tunnel-enabled",message:"Chat tunneling is enabled \u2014 allowlisted users can publish public URLs to local HTTP/TCP services.",fixHint:"Disable tunnelEnabled or restrict allowlists if you do not want remote tunnel control."}),ht()||n.push({severity:"warn",code:"tunnel-no-token",message:"Chat tunneling is enabled but no tunnel token is configured.",fixHint:"Run `omnish tunnel login` or set OMNISH_TUNNEL_TOKEN for the gateway process."})),e.tunnelRelayUrl.trim()&&e.tunnelRelayUrl.trim()!==$e&&n.push({severity:"info",code:"tunnel-relay-custom",message:`Custom tunnel relay configured: ${e.tunnelRelayUrl.trim()}`,fixHint:"Use the default relay only if you trust the operator of that endpoint."}),n}function lr(e){return e.some(t=>t.severity==="error")}function Nf(e,t){switch(t){case"error":return ao(e,"[ERROR]");case"warn":return me(e,"[WARN]");case"info":return w(e,"[INFO]")}}function ri(e,t,n,r){if(r.length!==0){t.push(J(e,`${n} (${r.length}):`));for(let o of r)t.push(` ${Nf(e,o.severity)} ${w(e,`${o.code}:`)} ${S(e,o.message)}`),o.detail&&t.push(` ${w(e,o.detail)}`),o.fixHint&&t.push(` ${w(e,`Fix: ${o.fixHint}`)}`);t.push("")}}function oi(e,t){if(e.length===0)return[`${Ce(t,"Security check:")} ${S(t,"no issues reported by automated rules.")}`,"",w(t,"Note: allowlisted remote shell access is still equivalent to sharing credentials with those identities.")].join(`
33
- `);let n=e.filter(a=>a.severity==="error"),r=e.filter(a=>a.severity==="warn"),o=e.filter(a=>a.severity==="info"),i=[`${Ce(t,"Security check:")} `+S(t,`${n.length} error(s), ${r.length} warning(s), ${o.length} note(s).`),""];return ri(t,i,"Errors",n),ri(t,i,"Warnings",r),ri(t,i,"Notes",o),i.push(w(t,"Allowlisted identities can run commands as this user; treat them like passwords.")),i.join(`
34
- `).trimEnd()}function si(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 Zl(e){return`${JSON.stringify({findings:e,summary:si(e)},null,2)}
35
- `}function ec(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 tc(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`${Ce(t,"security:")} ${S(t,"ok (no automated findings)")}`;let i=[];r&&i.push(ao(t,`${r} error(s)`)),o&&i.push(me(t,`${o} warning(s)`)),s&&i.push(w(t,`${s} note(s)`));let a=n??"run `omnish security` for details";return`${Ce(t,"security:")} ${i.join(", ")} ${w(t,`\u2014 ${a}`)}`}var Re="- ";function p(e){return{wa:e,tg:e}}function ue(e,t){return{wa:e,tg:t,tgHtml:!0}}function ke(e,t){return t==="whatsapp"?{text:e.wa}:e.tgHtml?{text:e.tg,parseModeHtml:!0}:{text:e.tg}}function q(e){return e.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;")}function Me(e){return e.replace(/[*_~`]/g,t=>({"*":"\u2217",_:"\uFF3F","~":"\u02DC","`":"\u2032"})[t]??t)}function _f(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(`${Re}${n.text}`);break}return t.join(`
36
- `).replace(/^\n+/,"").trimEnd()}function Ff(e){let t=[];for(let n of e)switch(n.kind){case"title":t.push("",`<b>${q(n.text)}</b>`,"");break;case"sub":t.push("",`<i>${q(n.text)}</i>`,"");break;case"gap":t.push("");break;case"p":t.push(q(n.text));break;case"bullet":t.push(`\u2022 ${q(n.text)}`);break}return t.join(`
37
- `).replace(/^\n+/,"").trimEnd()}function Z(e){return{wa:_f(e),tg:Ff(e),tgHtml:!0}}function xn(e){return[{kind:"title",text:"Omnish \u2014 quick help"},{kind:"p",text:"Per-chat shell cwd is stored under your data dir (see /wa help)."},{kind:"gap"},{kind:"sub",text:"Run commands"},{kind:"bullet",text:`${e.commandPrefix}<command> \u2014 sync shell in session cwd (timeout ${e.syncTimeoutMs} ms)`},{kind:"bullet",text:`${e.commandPrefix}cd <dir> \u2014 change session cwd (${e.commandPrefix}cd alone \u2192 home)`},{kind:"bullet",text:"!!start | !!stop \u2014 free shell (plain \u2192 sync shell only when no focused /apps session); space optional"},{kind:"bullet",text:"/bg <cmd> \u2014 background job; optional -n name; /jobs, /log, /tail, /kill (id or name)"},{kind:"bullet",text:e.tunnelEnabled?"/tunnel login|logout|status|http|tcp \u2014 login/status anytime; /tunnels needs tunnelEnabled":"/tunnel expose/list off until tunnelEnabled true (login/status still work)"},{kind:"bullet",text:"/apps \u2026 \u2014 interactive app sessions (/apps help)"},{kind:"bullet",text:"/send selectors \u2014 host files \u2192 chat (/file); caption: selectors -- note"},{kind:"bullet",text:"/files \u2014 full help for /send and saving inbound media"},{kind:"bullet",text:"/receive \u2014 set this chat\u2019s inbound folder (e.g. session cwd)"},{kind:"bullet",text:"/computers \xB7 /pcs \xB7 /c \u2014 chat-driven cluster (/c here to take over, /c help)"},{kind:"bullet",text:"/config \u2014 view or change server settings (/config help, /config keys)"},{kind:"bullet",text:"/service \u2014 background service status, install hints, logs (/service help)"},{kind:"bullet",text:"/shortcut \u2014 this chat & shared shortcuts (/shortcut help); !name or /name expands chat override first, then shared"},{kind:"bullet",text:"/run \u2014 recipe-based task runs (/r); per-chat or gateway-shared /run add \u2014 /run list, /run help"},{kind:"bullet",text:"/cowork | /cw \u2014 scheduled & on-demand shell tasks; logs to ~/Cowork/\u2026 (/cowork help)"},{kind:"bullet",text:"/watch \u2014 OS event eye: fs/pkg/svc alerts; pause/stop/rm, excludes (/watch help; docs/features/watch.md)"},{kind:"gap"},{kind:"sub",text:"Setup & gateway"},{kind:"bullet",text:"/wa help \u2014 WhatsApp link, allowlist"},{kind:"bullet",text:"/tg help \u2014 Telegram; /tg token <paste>"},{kind:"bullet",text:"/reload | /restart \u2014 apply config"},{kind:"bullet",text:"/updates \u2014 npm latest + optional notice URL; /updates cached \u2014 last snapshot"},{kind:"bullet",text:"/security \u2014 posture report; /security summary; /security tips; /security help"},{kind:"bullet",text:"/gateway | /gw | /mode \u2014 show or set gatewayMode"},{kind:"bullet",text:"/allow +E164 | /allow tg:id \u2014 allowlist; /deny \u2026; /allowlist"},{kind:"bullet",text:"/help \u2014 this message"},{kind:"gap"},{kind:"sub",text:"Terms"},{kind:"bullet",text:"Gateway = omnish on this host; shell runs here. Standalone = messengers on this host; attached = communication layer routes chat here."},{kind:"bullet",text:'Communication layer (when shipped) = link chats once, device token on this CLI; shell stays here. Standalone = no layer. /service "platform" = OS.'}]}function nc(e){let t=e.serviceInstallFromChat?[{kind:"bullet",text:"/service install \u2014 user-level systemd (Linux) or LaunchAgent (macOS)"},{kind:"bullet",text:"/service uninstall \u2014 remove that unit"}]:[{kind:"bullet",text:"/service install / uninstall \u2014 off by default. Enable: /config set serviceInstallFromChat true (same trust as shell)"}];return[{kind:"title",text:"Service and boot"},{kind:"p",text:"Inspect the gateway, get OS-specific install steps, or (if enabled) write the user service from chat. Same from the host terminal: omnish service help."},{kind:"p",text:'"platform" in status means your OS (Linux/macOS/Windows), not a hosted omnish account.'},{kind:"bullet",text:"/service status \u2014 platform, data dir, pidfile, node + entry script"},{kind:"bullet",text:"/service instructions \u2014 copy-paste commands for this machine"},{kind:"bullet",text:"/service logs [n] \u2014 tail default gateway log (default 80 lines)"},...t,{kind:"gap"},{kind:"p",text:"See https://omnish.dev and docs/guides/background-and-boot.md."}]}function rc(){return[{kind:"title",text:"Chat config"},{kind:"p",text:`Same trust as shell \u2014 allowlisted senders can change many keys saved to ${_}.`},{kind:"gap"},{kind:"bullet",text:"/config show \u2014 full snapshot (telegramBotToken masked)"},{kind:"bullet",text:"/config get <key> \u2014 single field"},{kind:"bullet",text:"/config set <key> <value> \u2014 values can be quoted for paths with spaces"},{kind:"bullet",text:"/config keys \u2014 allowed keys for /config set (includes tunnel*, webhook*, watch*, chatLlm*, cluster*, \u2026)"},{kind:"gap"},{kind:"p",text:"After edits: /reload while omnish run is active (needed for gatewayMode and telegramBotToken)."}]}function ii(){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: ${_}`}]}function oc(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: ${Me(e.updateBrief)}`,""),t.push("Change: /gateway both \xB7 /gw tg","/updates \u2014 npm + optional notice URL",`Config: ${Me(_)}`);let n=["<b>Gateway status</b>","",`<b>${q(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> ${q(e.updateBrief)}`,""),n.push("<i>Change:</i> /gateway both \xB7 /gw tg","<i>/updates</i> \u2014 npm + optional notice URL",q(`Config: ${_}`)),ue(t.join(`
27
+ )`).run(n.c-yl)}function kl(e,t){let n=cr(),r=Math.min(Math.max(1,e),50),o=n.prepare("SELECT rule_id, rule_name, kind, summary, ts_ms FROM watch_recent ORDER BY ts_ms DESC LIMIT ?").all(r*3),s=[];for(let i of o)if(!(t&&!t.has(i.rule_id))&&(s.push({ruleId:i.rule_id,ruleName:i.rule_name,kind:i.kind,stateKey:"",summary:i.summary,tsMs:i.ts_ms}),s.length>=r))break;return s}function Sl(e){return cr().prepare("SELECT state_key FROM watch_rule_state WHERE rule_id = ?").get(e)?.state_key??null}function vl(e,t,n){cr().prepare("INSERT OR REPLACE INTO watch_rule_state (rule_id, state_key, updated_at_ms) VALUES (?, ?, ?)").run(e,t,n)}import Ml from"node:path";import St from"node:path";function Km(e,t){let n=St.normalize(e);for(let r of t){let o=St.normalize(r);if(n===o||n.startsWith(o+St.sep))return!0}return!1}function zm(e,t,n){let r=St.relative(t,e);if(r.startsWith("..")||St.isAbsolute(r))return!1;let o=r.split(St.sep).join("/"),s=n.replace(/\\/g,"/").replace(/^\//,"");if(s.includes("**")){let i=s.replace(/[.+^${}()|[\]\\]/g,"\\$&").replace(/\*\*/g,"___GLOBSTAR___").replace(/\*/g,"[^/]*").replace(/___GLOBSTAR___/g,".*");try{if(new RegExp(`^${i}$`).test(o))return!0;if(s.startsWith("**/")){let l=s.slice(3);if(l&&!l.includes("*"))return o.split("/").includes(l)}return!1}catch{return!1}}if(s.includes("*")){let i=s.replace(/[.+^${}()|[\]\\]/g,"\\$&").replace(/\*/g,"[^/]*");try{return new RegExp(`^${i}$`).test(o)}catch{return!1}}return o===s||o.endsWith("/"+s)||o.includes("/"+s+"/")}function no(e,t){if(t.excludePaths.length&&Km(e,t.excludePaths))return!0;let n=t.path;if(n&&t.excludeGlobs.length){for(let r of t.excludeGlobs)if(zm(e,n,r))return!0}return!1}function xl(e){let t=[];for(let n of e.excludeGlobs){let r=n.replace(/\\/g,"/");r.startsWith("**/")?t.push(r):r.includes("*")?t.push(`**/${r}`):t.push(`**/${r}/**`)}for(let n of e.excludePaths)if(e.path)try{let r=St.relative(e.path,n);!r.startsWith("..")&&!St.isAbsolute(r)&&t.push(r.split(St.sep).join("/")+"/**")}catch{}return t}qe();function Rn(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=ee(String(o));s&&r.add(`wa:${Lt(s)}`)}if(e==="tg"||e==="all")for(let o of n.telegramAllowFrom){let s=Ee(String(o));s&&r.add(`tg:${s}`)}return[...r]}function $l(e,t,n){return Rn(e,t,n)}be();G();import qm from"node:crypto";import zs from"node:fs";var Ym=/^[a-zA-Z0-9][a-zA-Z0-9_-]{0,31}$/,Qm=1,qs=20;function ro(e){let t=e.trim().toLowerCase();return Ym.test(t)?{ok:!0,name:t}:{ok:!1,error:"Name must be alphanumeric with _ or -, max 32 chars."}}function oo(){return qm.randomBytes(8).toString("hex")}function Vm(e){if(!Array.isArray(e)||e.length===0)return["create","delete","rename"];let t=new Set(["create","delete","rename","update"]),n=[];for(let r of e)typeof r=="string"&&t.has(r)&&n.push(r);return n.length?n:["create","delete","rename"]}function Xm(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.kind=="string"?t.kind:"",i=s==="fs"||s==="pkg"||s==="svc"?s:null;if(!n||!r||!o||!i)return null;let a=typeof t.notify=="string"?t.notify.toLowerCase():"self",l=a==="wa"||a==="whatsapp"?"wa":a==="tg"||a==="telegram"?"tg":a==="all"?"all":a==="none"?"none":"self",d=typeof t.notifyWhen=="string"?t.notifyWhen:"always",u=d==="failure"||d==="state-change"?d:"always",c=Array.isArray(t.units)?t.units.filter(h=>typeof h=="string"&&h.trim().length>0).map(h=>h.trim()):[],m=Array.isArray(t.excludePaths)?t.excludePaths.filter(h=>typeof h=="string"&&h.trim().length>0):[],f=Array.isArray(t.excludeGlobs)?t.excludeGlobs.filter(h=>typeof h=="string"&&h.trim().length>0):[];return{id:n,name:r,ownerPeerKey:o,kind:i,enabled:typeof t.enabled=="boolean"?t.enabled:!0,paused:typeof t.paused=="boolean"?t.paused:!1,notify:l,notifyWhen:u,path:typeof t.path=="string"?t.path:"",events:Vm(t.events),units:c,excludePaths:m,excludeGlobs:f,adapterStatus:typeof t.adapterStatus=="string"?t.adapterStatus:"",createdAtMs:typeof t.createdAtMs=="number"&&Number.isFinite(t.createdAtMs)?t.createdAtMs:Date.now()}}function Zm(e){let t=JSON.parse(e);return!t||!Array.isArray(t.rules)?[]:t.rules.map(Xm).filter(n=>n!==null)}function ef(e){let t=[...e].sort((s,i)=>s.createdAtMs-i.createdAtMs),n=new Set,r=!1,o=[];for(let s of t){if(!n.has(s.name)){n.add(s.name),o.push(s);continue}let i=s.id.slice(0,8),a=`${s.name}-${i}`;a.length>32&&(a=`${s.name.slice(0,32-i.length-1)}-${i}`);let l=0;for(;n.has(a);){l+=1;let d=String(l);a=`${s.name.slice(0,Math.max(1,32-i.length-d.length-1))}-${i}${d}`}n.add(a),M.warn({oldName:s.name,newName:a,id:s.id},"watch: renamed duplicate rule for device-wide namespace"),o.push({...s,name:a}),r=!0}return{rules:o,changed:r}}function Le(){try{let e=zs.readFileSync(or,"utf8"),t=Zm(e),{rules:n,changed:r}=ef(t);return r&&mt(n),n}catch{return[]}}function tf(e){let t=0,n=0,r=0;for(let o of e)o.enabled?o.paused?n+=1:t+=1:r+=1;return{total:e.length,active:t,paused:n,disabled:r}}function ur(){let e=Le();return{rules:e,summary:tf(e)}}function mt(e){j(kt);let n=`${JSON.stringify({version:Qm,rules:e},null,2)}
28
+ `,r=`${or}.tmp`;zs.writeFileSync(r,n,{mode:384}),zs.renameSync(r,or)}function Ys(){return or}function tn(e,t){let n=t.trim().toLowerCase();return e.find(r=>r.name===n)}function Rl(e,t){return e.find(n=>n.id===t)}function Cn(e,t){let n=e.findIndex(r=>r.id===t.id);if(n>=0){let r=[...e];return r[n]=t,r}return[...e,t]}function Cl(e,t){let n=t.trim().toLowerCase();return e.filter(r=>r.name!==n)}import nf from"node:os";import ft from"node:path";var rf=new Set([".env","id_rsa","id_ed25519","id_ecdsa","known_hosts","credentials.json","secrets.json",".netrc","shadow","passwd"]);function Qe(e){let t=ft.normalize(e),n=t.toLowerCase(),r=nf.homedir().toLowerCase();if(n.includes(`${ft.sep}.ssh${ft.sep}`)||n.endsWith(`${ft.sep}.ssh`)||n.includes(`${ft.sep}browser${ft.sep}`)&&n.includes("profile")||r&&(n===`${r}/.gnupg`||n.startsWith(`${r}/.gnupg${ft.sep}`))||n.includes(`${ft.sep}keychains${ft.sep}`))return!0;let o=ft.basename(t);return!!(rf.has(o)||o.startsWith(".env.")||o.endsWith(".pem")||o.endsWith(".key"))}var of=["node_modules",".git",".svn",".hg","__pycache__",".cache",".next","dist","build"],sf=[".tmp",".swp",".swx","~",".part"];function Tl(e,t){let n=new Map,r=new Map;function o(u,c){let m=u.split(Ml.sep);for(let h of m)if(of.includes(h))return!0;let f=Ml.basename(u);for(let h of sf)if(f.endsWith(h))return!0;return!!no(u,c)}function s(u){let c=Rl(Le(),u);return!c||!c.enabled||c.paused?null:c}function i(u){let c=Date.now(),m=r.get(u);return!m||c-m.windowStart>=6e4?(r.set(u,{windowStart:c,count:1}),!1):m.count>=t.maxEventsPerMinute?!0:(m.count+=1,!1)}async function a(u,c){let m=e.getConfig();if(!m.watchEnabled)return;let f=s(u.id);if(!f||c.kind==="fs"&&c.meta?.path&&(Qe(c.meta.path)||o(c.meta.path,f)))return;if(bl(c),(f.notifyWhen??"always")==="state-change"){if(Sl(f.id)===c.stateKey)return;vl(f.id,c.stateKey,c.tsMs)}let g=$l(f.notify,f.ownerPeerKey,m);if(g.length===0)return;let y=`[watch:${f.name}] ${c.summary}`;await Promise.all(g.map(b=>e.sendToPeer(b,y).catch(()=>{})))}function l(u){let c=n.get(u);if(!c)return;n.delete(u);let m=s(c.ruleId);m&&(i(c.ruleId)||a(m,c.event))}function d(u){for(let[c,m]of n)m.ruleId===u&&(clearTimeout(m.timer),n.delete(c))}return{ingest(u,c){if(!u.enabled||u.paused||c.meta?.path&&(Qe(c.meta.path)||o(c.meta.path,u)))return;let m=`${u.id}:${c.stateKey||c.summary}`,f=n.get(m);f&&clearTimeout(f.timer);let h=setTimeout(()=>l(m),t.debounceMs);h.unref?.(),n.set(m,{timer:h,event:c,ruleId:u.id})},cancelForRule:d,dispose(){for(let u of n.values())clearTimeout(u.timer);n.clear(),r.clear()}}}function El(e,t){let n=e.toLowerCase();return!!(n==="create"&&t.has("create")||n==="delete"&&t.has("delete")||(n==="update"||n==="rename")&&(t.has("update")||t.has("rename")))}be();import Pl from"node:fs";import af from"node:path";import{subscribe as lf}from"@parcel/watcher";var cf=["**/node_modules/**","**/.git/**","**/.svn/**","**/.hg/**","**/__pycache__/**","**/.cache/**"];function uf(e){return e==="create"?"create":e==="delete"?"delete":e==="update"?"update":e}function df(e,t){try{let n=Pl.statSync(t),r=n.isFile()&&n.size<1024*1024?` (${n.size<1024?`${n.size} B`:`${(n.size/1024).toFixed(1)} KB`})`:"";return`fs: ${e} ${t}${r}`}catch{return`fs: ${e} ${t}`}}async function Al(e,t){let n=e.path;if(!n||!Pl.existsSync(n))return{stop:async()=>{},status:()=>"error: path missing or not found"};if(Qe(n))return{stop:async()=>{},status:()=>"error: sensitive path denied"};let r=new Set(e.events.map(i=>i.toLowerCase())),o="ok",s=null;try{s=await lf(n,(i,a)=>{if(i){o=`error: ${i.message}`,M.warn({rule:e.name,err:i.message},"watch fs adapter");return}for(let l of a){let d=uf(l.type);if(!El(d,r))continue;let u=af.resolve(l.path);if(!u.startsWith(n)||Qe(u)||no(u,e))continue;let c=df(d,u);t({ruleId:e.id,ruleName:e.name,kind:"fs",stateKey:`${d}:${u}`,summary:c,tsMs:Date.now(),meta:{path:u,type:d}})}},{ignore:[...cf,...xl(e)]})}catch(i){o=`error: ${String(i)}`,M.warn({rule:e.name,err:String(i)},"watch fs subscribe failed")}return{stop:async()=>{s&&(await s.unsubscribe().catch(()=>{}),s=null)},status:()=>o}}import bf from"node:os";be();import _t from"node:fs";function pf(e,t,n){let r=n?.pollMs??2e3,o=0,s=!1,i=null,a=null,l="starting";function d(){if(!s)try{let u=_t.statSync(e);if(u.size<o&&(o=0),u.size<=o)return;let c=_t.openSync(e,"r");try{let m=u.size-o,f=Buffer.alloc(m);_t.readSync(c,f,0,m,o),o=u.size;let h=f.toString("utf8");for(let g of h.split(`
29
+ `)){let y=g.trim();y&&t(y)}}finally{_t.closeSync(c)}l="ok"}catch(u){l=`error: ${String(u)}`}}try{_t.existsSync(e)&&(o=_t.statSync(e).size),i=_t.watch(e,()=>d()),i.on("error",()=>{i?.close(),i=null}),l="ok (watch)"}catch{l="ok (poll)",a=setInterval(d,r),a.unref?.(),d()}return{stop(){s=!0,i?.close(),a&&clearInterval(a)},status:()=>l}}function so(e,t){for(let n of e)try{if(_t.existsSync(n))return pf(n,t)}catch(r){M.debug({path:n,err:String(r)},"watch log-tail skip path")}return null}var mf=["/var/log/install.log"];function ff(e){let t=e.toLowerCase();return t.includes("installed")||t.includes("upgraded")||t.includes("removed")?e.length>240?`pkg: ${e.slice(0,240)}\u2026`:`pkg: ${e}`:null}function Il(e,t){return so(mf,n=>{let r=ff(n);r&&t({ruleId:e.id,ruleName:e.name,kind:"pkg",stateKey:r.slice(0,200),summary:r,tsMs:Date.now()})})}var hf=["/var/log/dpkg.log","/var/log/apt/history.log"];function gf(e){if(e.includes("status installed")){let t=e.match(/install\s+(\S+):/);if(t)return`pkg: installed ${t[1]} (dpkg)`}if(e.includes("status removed")||e.includes("remove ")){let t=e.match(/remove\s+(\S+):/);if(t)return`pkg: removed ${t[1]} (dpkg)`}return null}function yf(e){return e.startsWith("Install:")||e.startsWith("Upgrade:")?`pkg: ${e.slice(0,120)} (apt)`:e.startsWith("Remove:")?`pkg: ${e.slice(0,120)} (apt)`:null}function Ol(e,t){return so(hf,n=>{let r=gf(n)??yf(n);r&&t({ruleId:e.id,ruleName:e.name,kind:"pkg",stateKey:r,summary:r,tsMs:Date.now()})})}import{spawn as wf}from"node:child_process";function Ll(e,t){let n=!1,r="ok",o=0,s=()=>{if(n)return;let a=wf("powershell.exe",["-NoProfile","-NonInteractive","-Command","Get-WinEvent -FilterHashtable @{LogName='Application'; ProviderName=@('MsiInstaller','Windows Installer')} -MaxEvents 5 -ErrorAction SilentlyContinue | Select-Object -Property RecordId,Message | ConvertTo-Json -Compress"],{windowsHide:!0}),l="";a.stdout?.on("data",d=>{l+=String(d)}),a.on("close",d=>{if(n||d!==0||!l.trim()){d!==0&&(r="ok (no events or access denied)"),i();return}try{let u=JSON.parse(l),c=Array.isArray(u)?u:[u];for(let m of c.sort((f,h)=>f.RecordId-h.RecordId)){if(m.RecordId<=o)continue;o=m.RecordId;let f=(m.Message??"").replace(/\s+/g," ").trim().slice(0,300);if(!f)continue;let h=`pkg: ${f}`;t({ruleId:e.id,ruleName:e.name,kind:"pkg",stateKey:`win:${m.RecordId}`,summary:h,tsMs:Date.now()})}r="ok"}catch(u){r=`parse error: ${String(u)}`}i()}),a.on("error",d=>{r=`error: ${String(d)}`,i()})},i=()=>{n||setTimeout(s,3e4).unref?.()};return s(),{stop(){n=!0},status:()=>r}}function Nl(e,t){let n=bf.platform();if(n==="win32"){let o=Ll(e,t);return{stop:()=>o.stop(),status:o.status}}if(n==="darwin"){let o=Il(e,t);return o?{stop:()=>o.stop(),status:o.status}:{stop:()=>{},status:()=>"error: cannot read /var/log/install.log (try sudo or adm group)"}}let r=Ol(e,t);return r?{stop:()=>r.stop(),status:r.status}:{stop:()=>{},status:()=>"error: cannot read dpkg/apt logs (add user to adm or run gateway with read access)"}}import Mf from"node:os";import{spawnSync as _l}from"node:child_process";var kf=3e4;function Sf(e){let t=new Map;for(let n of e){let r=_l("launchctl",["print",`system/${n}`],{encoding:"utf8",timeout:1e4});if(r.status!==0){let s=process.getuid?.()??501,i=_l("launchctl",["print",`gui/${s}/${n}`],{encoding:"utf8",timeout:1e4});if(i.status!==0){t.set(n,"not-found");continue}let a=(i.stdout??"").includes("state = running")?"running":"stopped";t.set(n,a);continue}let o=(r.stdout??"").includes("state = running")?"running":"stopped";t.set(n,o)}return t}function Fl(e,t){let n=e.units,r=!1,o="ok",s=new Map,i=()=>{if(r)return;let a=Sf(n);if(a.size===0)o="error: no launchd labels configured";else{o="ok";for(let[l,d]of a){let u=s.get(l);if(u!==void 0&&u!==d){let c=`svc: ${l} ${u} \u2192 ${d}`;t({ruleId:e.id,ruleName:e.name,kind:"svc",stateKey:`${l}:${d}`,summary:c,tsMs:Date.now(),meta:{unit:l,state:d}})}}s=a}r||setTimeout(i,kf).unref?.()};return i(),{stop(){r=!0},status:()=>o}}import{spawnSync as Wl}from"node:child_process";var vf=3e4;function xf(e){let t=new Map;if(e.length===0)return t;let n=["show",...e,"--property=ActiveState,SubState,UnitFileState","--no-pager"],r=Wl("systemctl",n,{encoding:"utf8",timeout:15e3});if(r.status!==0)return t;let o="";for(let s of(r.stdout??"").split(`
30
+ `)){let i=s.match(/^Unit=(.+)$/);if(i){o=i[1];continue}let a=s.match(/^ActiveState=(.+)$/);a&&o&&t.set(o,a[1].trim())}if(t.size===0)for(let s of e){let i=s.endsWith(".service")?s:`${s}.service`,a=Wl("systemctl",["is-active",i],{encoding:"utf8",timeout:5e3}),l=(a.stdout??a.stderr??"unknown").trim();t.set(i,l)}return t}function Ul(e,t){let n=e.units.map(a=>a.endsWith(".service")?a:`${a}.service`),r=!1,o="ok",s=new Map,i=()=>{if(r)return;let a=xf(n);if(a.size===0)o="error: systemctl unavailable or units not found";else{o="ok";for(let[l,d]of a){let u=s.get(l);if(u!==void 0&&u!==d){let c=`svc: ${l} ${u} \u2192 ${d}`;t({ruleId:e.id,ruleName:e.name,kind:"svc",stateKey:`${l}:${d}`,summary:c,tsMs:Date.now(),meta:{unit:l,state:d}})}}s=a}r||setTimeout(i,vf).unref?.()};return i(),{stop(){r=!0},status:()=>o}}import{spawnSync as $f}from"node:child_process";var Rf=3e4;function Cf(e){let t=new Map;for(let n of e){let r=$f("sc",["query",n],{encoding:"utf8",timeout:1e4,windowsHide:!0});if(r.status!==0){t.set(n,"not-found");continue}let o=r.stdout??"",s="unknown";o.includes("RUNNING")?s="running":o.includes("STOPPED")?s="stopped":o.includes("START_PENDING")?s="start-pending":o.includes("STOP_PENDING")&&(s="stop-pending"),t.set(n,s)}return t}function Dl(e,t){let n=e.units,r=!1,o="ok",s=new Map,i=()=>{if(r)return;let a=Cf(n);if(a.size===0)o="error: no service names configured";else{o="ok";for(let[l,d]of a){let u=s.get(l);if(u!==void 0&&u!==d){let c=`svc: ${l} ${u} \u2192 ${d}`;t({ruleId:e.id,ruleName:e.name,kind:"svc",stateKey:`${l}:${d}`,summary:c,tsMs:Date.now(),meta:{unit:l,state:d}})}}s=a}r||setTimeout(i,Rf).unref?.()};return i(),{stop(){r=!0},status:()=>o}}function Hl(e,t){let n=Mf.platform();return n==="win32"?Dl(e,t):n==="darwin"?Fl(e,t):Ul(e,t)}var Ve=null,Ft=null;function Bl(e){let t=e.getConfig();return Tl(e,{debounceMs:Math.max(500,t.watchDebounceMs??2e3),maxEventsPerMinute:Math.max(1,t.watchMaxEventsPerMinute??30)})}function Pf(e){return e.watchEnabled&&e.watchAutoRestore}var Qs=class{deps;pipeline;runtimes=new Map;stopped=!1;constructor(t){this.deps=t,this.pipeline=Bl(t)}onEvent=(t,n)=>{this.pipeline.ingest(t,n)};cancelPendingForRule(t){this.pipeline.cancelForRule(t)}async reload(){if(this.pipeline.dispose(),this.pipeline=Bl(this.deps),await this.stopAdapters(),this.stopped||!this.deps.getConfig().watchEnabled)return 0;let n=Le().filter(r=>r.enabled&&!r.paused);for(let r of n)await this.startRule(r);return this.runtimes.size}async startRule(t){try{let n;if(t.kind==="fs"){let r=Ge(t.path,Tf.homedir()),o={...t,path:r};if(Qe(r)){t.adapterStatus="denied: sensitive path",this.persistRuleStatus(t);return}if(!Ef.existsSync(r)){t.adapterStatus="error: path not found",this.persistRuleStatus(t);return}let s=await Al(o,i=>this.onEvent(o,i));n={stop:()=>s.stop(),status:s.status}}else if(t.kind==="pkg")n=Nl(t,o=>this.onEvent(t,o));else if(t.kind==="svc"){if(t.units.length===0){t.adapterStatus="error: no units",this.persistRuleStatus(t);return}n=Hl(t,o=>this.onEvent(t,o))}else return;t.adapterStatus=n.status(),this.persistRuleStatus(t),this.runtimes.set(t.id,{rule:t,adapter:n})}catch(n){t.adapterStatus=`error: ${String(n)}`,this.persistRuleStatus(t),M.warn({rule:t.name,err:String(n)},"watch rule start failed")}}persistRuleStatus(t){let n=Le(),r=n.findIndex(o=>o.id===t.id);r>=0&&(n[r]={...n[r],adapterStatus:t.adapterStatus},mt(n))}async stopAdapters(){for(let t of this.runtimes.values())try{await t.adapter.stop()}catch{}this.runtimes.clear()}async stop(){this.stopped=!0,await this.stopAdapters(),this.pipeline.dispose(),wl()}getStatusLines(){let t=Le();return t.length===0?["(no watch rules)"]:t.map(n=>{let o=this.runtimes.get(n.id)?.adapter.status()??n.adapterStatus,s=n.enabled?n.paused?"paused":"on":"disabled",i=n.kind==="fs"?n.path:n.kind==="svc"?n.units.join(","):"system",a=n.kind==="fs"&&(n.excludePaths.length||n.excludeGlobs.length)?` excludes:${n.excludePaths.length+n.excludeGlobs.length}`:"";return`${n.name} [${n.kind}] ${s} notify=${n.notify} when=${n.notifyWhen} \u2014 ${i}${a} \u2014 ${o}`})}getRunningCount(){return this.runtimes.size}isRuleRunning(t){return this.runtimes.has(t)}};function jl(){return Ve}function Vs(e){Ve?.cancelPendingForRule(e)}function Af(){j(kt),to()}function Xs(){Ft&&(Ve||(Ve=new Qs(Ft)),Ve.reload())}function io(e){Ft=e,Af();let t=e.getConfig(),{summary:n}=ur();return n.total>0&&M.info({rules:n.total,active:n.active,paused:n.paused,disabled:n.disabled},"watch rules loaded from disk"),Pf(t)&&Xs(),()=>{Ve?.stop(),Ve=null,Ft=null}}async function Gl(){return Ft?Ft.getConfig().watchEnabled?(Xs(),Ve?.getRunningCount()??0):(Ve?.stop(),Ve=null,-1):-1}function De(){if(!Ft)return;if(!Ft.getConfig().watchEnabled){Ve?.stop(),Ve=null;return}Xs()}G();me();G();import Lf from"node:os";import lo from"node:path";G();import Zs from"node:fs";import Kl from"node:os";import Wt from"node:path";var zl=Wt.join(H,"sessions.json"),Mn=new Map;function ql(){return{cwd:Wt.resolve(Kl.homedir())}}function If(e){return e.startsWith("wa:")||e.startsWith("tg:")?e:`wa:${e}`}function Of(){try{let e=Zs.readFileSync(zl,"utf8"),t=JSON.parse(e);for(let[n,r]of Object.entries(t)){if(!r||typeof r!="object")continue;let o=If(n),i={cwd:typeof r.cwd=="string"&&r.cwd.length>0?Wt.resolve(r.cwd):ql().cwd};r.fileReceiveRoot==="sessionCwd"&&(i.fileReceiveRoot="sessionCwd"),Mn.set(o,i)}}catch{}}function Yl(){j(H);let e={};for(let[t,n]of Mn){let r={cwd:n.cwd};n.fileReceiveRoot==="sessionCwd"&&(r.fileReceiveRoot="sessionCwd"),e[t]=r}Zs.writeFileSync(zl,JSON.stringify(e,null,2)+`
31
+ `,{mode:384})}var Jl=!1;function ei(){Jl||(Of(),Jl=!0)}function oe(e){ei();let t=Mn.get(e);return t||(t=ql(),Mn.set(e,t)),t}function ao(e,t){ei();let n=Wt.resolve(t),r=oe(e);r.cwd=n,Mn.set(e,r),Yl()}function Ql(e){return oe(e).fileReceiveRoot==="sessionCwd"?"sessionCwd":"default"}function ti(e,t){ei();let n=oe(e);t==="default"?delete n.fileReceiveRoot:n.fileReceiveRoot="sessionCwd",Mn.set(e,n),Yl()}function Vl(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 Xl(e,t){if(t.kind==="home")return Wt.resolve(Kl.homedir());let n=t.value.replace(/^['"]|['"]$/g,"");return Wt.isAbsolute(n)?Wt.normalize(n):Wt.resolve(e,n)}function Zl(e){try{return Zs.statSync(e).isDirectory()?{ok:!0}:{ok:!1,error:`Not a directory: ${e}`}}catch(t){return{ok:!1,error:String(t)}}}var Nf="Omnish";function ec(){return lo.join(Lf.homedir(),"Downloads",Nf)}function Ut(e,t){let n=oe(t);if(n.fileReceiveRoot==="sessionCwd")return n.cwd;switch(e.fileReceiveRootMode){case"downloads":return ec();case"omnishData":return lo.join(H,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(!lo.isAbsolute(r))throw new Error('fileReceiveRootPath must be an absolute path when fileReceiveRootMode is "fixed".');return lo.resolve(r)}default:return ec()}}G();import Rt from"node:fs";import nc from"node:path";import jt from"node:process";var nn="\x1B";function _f(e){return e.replace(/\u001B\[[\d;]*m/g,"")}function co(e){return _f(e).length}function ni(e,t,n,r,o=2){let s=Math.max(0,t-co(n));return`${e}${n}${" ".repeat(s)}${" ".repeat(o)}${r}`}function vt(e,t,n,r){if(n.length===0)return[];let o=n.map(i=>r(i.left)),s=Math.max(...o.map(co));return n.map((i,a)=>ni(t,s,o[a],S(e,i.right)))}var Dt={primary:"#b4ff24",foreground:"#eef2f4",muted:"#8a9199",border:"#2a3139",error:"#ef4444",warn:"#a0e614",warnAlt:"#8cce04"};function Ff(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 Ht(e){let t=Ff(e);return t?`${nn}[38;2;${t.r};${t.g};${t.b}m`:""}function uo(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 xt(e,t,n){return!t||!uo(e)?n:`${t}${n}${nn}[0m`}function J(e,t){return xt(e,`${nn}[1m`,t)}function Wf(e,t){return uo(e)?`${Ht(Dt.foreground)}${nn}[1m${t}${nn}[0m`:t}function V(e,t){return xt(e,`${nn}[2m`,t)}function Ce(e,t){return xt(e,`${Ht(Dt.primary)}${nn}[1m`,t)}function ue(e,t){return xt(e,Ht(Dt.primary),t)}function S(e,t){return xt(e,Ht(Dt.foreground),t)}function w(e,t){return xt(e,Ht(Dt.muted),t)}function Uf(e,t){return xt(e,Ht(Dt.border),t)}function po(e,t){return xt(e,Ht(Dt.error),t)}function fe(e,t){return xt(e,Ht(Dt.warn),t)}function Bt(e,t=60,n="\u2500"){let r=n.repeat(Math.max(1,t));return Uf(e,r)}function B(e,t){return uo(e)?`${`${w(e,"[")}${ue(e,"omnish")}${w(e,"]")}`} ${t}`:`[omnish] ${t}`}function C(e,t){return uo(e)?`${`${po(e,"[omnish]")}`} ${t}`:`[omnish] ${t}`}function tc(e,t){let n=[];for(let r of e)switch(r.kind){case"title":n.push("",Ce(t,r.text),"");break;case"sub":n.push("",Wf(t,r.text),"");break;case"gap":n.push("");break;case"p":n.push(S(t,r.text));break;case"bullet":n.push(`${w(t,"\u2022")} ${S(t,r.text)}`);break}return n.join(`
32
+ `).replace(/^\n+/,"").trimEnd()}me();G();rn();on();function fo(e){return(e&4)!==0}function si(e){return(e&2)!==0}var rc={error:3,warn:2,info:1};function oc(e,t){let n=rc[t];return e.filter(r=>rc[r.severity]>=n)}function Ct(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 ${_} and delete wildcard entries.`,fixHint:`Edit ${_} 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 ${_} 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 ${_} unless you trust every allowlisted identity.`}),!nc.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 ${_}.`});else try{Rt.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 ${_} 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 ${_} points to a real file.`})}if(e.fileReceiveRootMode==="fixed"){let a=e.fileReceiveRootPath.trim();a?nc.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 ${_}.`}):n.push({severity:"error",code:"receive-fixed-empty",message:'fileReceiveRootMode is "fixed" but fileReceiveRootPath is empty.',fixHint:`Set fileReceiveRootPath to an absolute directory in ${_}, or change fileReceiveRootMode.`})}if(jt.platform!=="win32"){try{if(Rt.existsSync(_)){let d=Rt.statSync(_);(fo(d.mode)||si(d.mode))&&n.push({severity:"warn",code:"config-permissions",message:"config.json is accessible to users other than the owner (group/world).",detail:`Recommended: chmod 600 ${_}`,fixHint:`chmod 600 ${_}`})}}catch{}(typeof jt.getuid=="function"?jt.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(Rt.existsSync(H)){let d=Rt.statSync(H);(fo(d.mode)||si(d.mode))&&(l=!0,n.push({severity:"warn",code:"data-dir-permissive",message:"Omnish data directory is accessible to group or other users \u2014 auth, jobs, and logs may be exposed.",detail:`Recommended: chmod 700 ${H}`,fixHint:`chmod 700 ${H}`}))}}catch{}try{if(!l&&Rt.existsSync(tt)){let d=Rt.statSync(tt);(fo(d.mode)||si(d.mode))&&n.push({severity:"warn",code:"jobs-dir-permissive",message:"Jobs log directory is accessible to group or other users \u2014 command output may leak.",detail:`Recommended: chmod 700 ${tt}`,fixHint:`chmod 700 ${tt}`})}}catch{}try{if(Rt.existsSync(ne)){let d=Rt.statSync(ne);fo(d.mode)&&n.push({severity:"warn",code:"auth-dir-world-readable",message:"WhatsApp auth directory is readable by others \u2014 session material may be exposed.",detail:`Recommended: chmod 700 ${ne}`,fixHint:`chmod 700 ${ne}`})}}catch{}}return(typeof jt.env.TELEGRAM_BOT_TOKEN=="string"?jt.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&&!ve(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 ${_} or export TELEGRAM_BOT_TOKEN=<token>.`}),e.clusterEnabled&&s&&n.push({severity:"info",code:"cluster-telegram-tokens",message:"Cluster mode with Telegram: use a distinct BotFather bot/token per omnish host if several gateways run Telegram \u2014 the same token cannot be long-polled twice.",fixHint:"Create separate bots for each machine or run Telegram on fewer hosts."}),e.platformToken.trim()&&n.push({severity:"info",code:"platform-token-config",message:"platformToken is stored in config.json (same trust boundary as your shell account).",fixHint:"Use OMNISH_TOKEN in the environment for CI/ephemeral hosts; env overrides config."}),(jt.env.OMNISH_TOKEN?.trim()||jt.env.OMNISH_TUNNEL_TOKEN?.trim()||jt.env.OMNISH_DEVICE_TOKEN?.trim())&&n.push({severity:"info",code:"platform-token-env",message:"OMNISH_TOKEN (or OMNISH_TUNNEL_TOKEN / OMNISH_DEVICE_TOKEN) is set in the environment.",fixHint:"Unset platform token env vars if you want config.json / tunnel-auth.json to apply."}),e.tunnelEnabled&&(n.push({severity:"warn",code:"tunnel-enabled",message:"Chat tunneling is enabled \u2014 allowlisted users can publish public URLs to local HTTP/TCP services.",fixHint:"Disable tunnelEnabled or restrict allowlists if you do not want remote tunnel control."}),gt()||n.push({severity:"warn",code:"tunnel-no-token",message:"Chat tunneling is enabled but no tunnel token is configured.",fixHint:"Run `omnish tunnel login` or set OMNISH_TUNNEL_TOKEN for the gateway process."})),e.tunnelRelayUrl.trim()&&e.tunnelRelayUrl.trim()!==$e&&n.push({severity:"info",code:"tunnel-relay-custom",message:`Custom tunnel relay configured: ${e.tunnelRelayUrl.trim()}`,fixHint:"Use the default relay only if you trust the operator of that endpoint."}),n}function dr(e){return e.some(t=>t.severity==="error")}function Hf(e,t){switch(t){case"error":return po(e,"[ERROR]");case"warn":return fe(e,"[WARN]");case"info":return w(e,"[INFO]")}}function ii(e,t,n,r){if(r.length!==0){t.push(J(e,`${n} (${r.length}):`));for(let o of r)t.push(` ${Hf(e,o.severity)} ${w(e,`${o.code}:`)} ${S(e,o.message)}`),o.detail&&t.push(` ${w(e,o.detail)}`),o.fixHint&&t.push(` ${w(e,`Fix: ${o.fixHint}`)}`);t.push("")}}function ai(e,t){if(e.length===0)return[`${Ce(t,"Security check:")} ${S(t,"no issues reported by automated rules.")}`,"",w(t,"Note: allowlisted remote shell access is still equivalent to sharing credentials with those identities.")].join(`
33
+ `);let n=e.filter(a=>a.severity==="error"),r=e.filter(a=>a.severity==="warn"),o=e.filter(a=>a.severity==="info"),i=[`${Ce(t,"Security check:")} `+S(t,`${n.length} error(s), ${r.length} warning(s), ${o.length} note(s).`),""];return ii(t,i,"Errors",n),ii(t,i,"Warnings",r),ii(t,i,"Notes",o),i.push(w(t,"Allowlisted identities can run commands as this user; treat them like passwords.")),i.join(`
34
+ `).trimEnd()}function li(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 sc(e){return`${JSON.stringify({findings:e,summary:li(e)},null,2)}
35
+ `}function ic(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 ac(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`${Ce(t,"security:")} ${S(t,"ok (no automated findings)")}`;let i=[];r&&i.push(po(t,`${r} error(s)`)),o&&i.push(fe(t,`${o} warning(s)`)),s&&i.push(w(t,`${s} note(s)`));let a=n??"run `omnish security` for details";return`${Ce(t,"security:")} ${i.join(", ")} ${w(t,`\u2014 ${a}`)}`}var Re="- ";function p(e){return{wa:e,tg:e}}function de(e,t){return{wa:e,tg:t,tgHtml:!0}}function ke(e,t){return t==="whatsapp"?{text:e.wa}:e.tgHtml?{text:e.tg,parseModeHtml:!0}:{text:e.tg}}function q(e){return e.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;")}function Me(e){return e.replace(/[*_~`]/g,t=>({"*":"\u2217",_:"\uFF3F","~":"\u02DC","`":"\u2032"})[t]??t)}function Bf(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(`${Re}${n.text}`);break}return t.join(`
36
+ `).replace(/^\n+/,"").trimEnd()}function jf(e){let t=[];for(let n of e)switch(n.kind){case"title":t.push("",`<b>${q(n.text)}</b>`,"");break;case"sub":t.push("",`<i>${q(n.text)}</i>`,"");break;case"gap":t.push("");break;case"p":t.push(q(n.text));break;case"bullet":t.push(`\u2022 ${q(n.text)}`);break}return t.join(`
37
+ `).replace(/^\n+/,"").trimEnd()}function Z(e){return{wa:Bf(e),tg:jf(e),tgHtml:!0}}function Tn(e){return[{kind:"title",text:"Omnish \u2014 quick help"},{kind:"p",text:"Per-chat shell cwd is stored under your data dir (see /wa help)."},{kind:"gap"},{kind:"sub",text:"Run commands"},{kind:"bullet",text:`${e.commandPrefix}<command> \u2014 sync shell in session cwd (timeout ${e.syncTimeoutMs} ms)`},{kind:"bullet",text:`${e.commandPrefix}cd <dir> \u2014 change session cwd (${e.commandPrefix}cd alone \u2192 home)`},{kind:"bullet",text:"!!start | !!stop \u2014 free shell (plain \u2192 sync shell only when no focused /apps session); space optional"},{kind:"bullet",text:"/bg <cmd> \u2014 background job; optional -n name; /jobs, /log, /tail, /kill (id or name)"},{kind:"bullet",text:e.tunnelEnabled?"/tunnel login|logout|status|http|tcp \u2014 login/status anytime; /tunnels needs tunnelEnabled":"/tunnel expose/list off until tunnelEnabled true (login/status still work)"},{kind:"bullet",text:"/apps \u2026 \u2014 interactive app sessions (/apps help)"},{kind:"bullet",text:"/send selectors \u2014 host files \u2192 chat (/file); caption: selectors -- note"},{kind:"bullet",text:"/files \u2014 full help for /send and saving inbound media"},{kind:"bullet",text:"/receive \u2014 set this chat\u2019s inbound folder (e.g. session cwd)"},{kind:"bullet",text:"/computers \xB7 /pcs \xB7 /c \u2014 chat-driven cluster (/c here to take over, /c help)"},{kind:"bullet",text:"/config \u2014 view or change server settings (/config help, /config keys)"},{kind:"bullet",text:"/service \u2014 background service status, install hints, logs (/service help)"},{kind:"bullet",text:"/shortcut \u2014 this chat & shared shortcuts (/shortcut help); !name or /name expands chat override first, then shared"},{kind:"bullet",text:"/run \u2014 recipe-based task runs (/r); per-chat or gateway-shared /run add \u2014 /run list, /run help"},{kind:"bullet",text:"/cowork | /cw \u2014 scheduled & on-demand shell tasks; logs to ~/Cowork/\u2026 (/cowork help)"},{kind:"bullet",text:"/watch \u2014 OS event eye: fs/pkg/svc alerts; pause/stop/rm, excludes (/watch help; docs/features/watch.md)"},{kind:"gap"},{kind:"sub",text:"Setup & gateway"},{kind:"bullet",text:"/wa help \u2014 WhatsApp link, allowlist"},{kind:"bullet",text:"/tg help \u2014 Telegram; /tg token <paste>"},{kind:"bullet",text:"/reload | /restart \u2014 apply config"},{kind:"bullet",text:"/updates \u2014 npm latest + optional notice URL; /updates cached \u2014 last snapshot"},{kind:"bullet",text:"/security \u2014 posture report; /security summary; /security tips; /security help"},{kind:"bullet",text:"/gateway | /gw | /mode \u2014 show or set gatewayMode"},{kind:"bullet",text:"/allow +E164 | /allow tg:id \u2014 allowlist; /deny \u2026; /allowlist"},{kind:"bullet",text:"/help \u2014 this message"},{kind:"gap"},{kind:"sub",text:"Terms"},{kind:"bullet",text:"Gateway = omnish on this host; shell runs here. Standalone = messengers on this host; attached = communication layer routes chat here."},{kind:"bullet",text:'Communication layer (when shipped) = link chats once, device token on this CLI; shell stays here. Standalone = no layer. /service "platform" = OS.'}]}function lc(e){let t=e.serviceInstallFromChat?[{kind:"bullet",text:"/service install \u2014 user-level systemd (Linux) or LaunchAgent (macOS)"},{kind:"bullet",text:"/service uninstall \u2014 remove that unit"}]:[{kind:"bullet",text:"/service install / uninstall \u2014 off by default. Enable: /config set serviceInstallFromChat true (same trust as shell)"}];return[{kind:"title",text:"Service and boot"},{kind:"p",text:"Inspect the gateway, get OS-specific install steps, or (if enabled) write the user service from chat. Same from the host terminal: omnish service help."},{kind:"p",text:'"platform" in status means your OS (Linux/macOS/Windows), not a hosted omnish account.'},{kind:"bullet",text:"/service status \u2014 platform, data dir, pidfile, node + entry script"},{kind:"bullet",text:"/service instructions \u2014 copy-paste commands for this machine"},{kind:"bullet",text:"/service logs [n] \u2014 tail default gateway log (default 80 lines)"},...t,{kind:"gap"},{kind:"p",text:"See https://omnish.dev and docs/guides/background-and-boot.md."}]}function cc(){return[{kind:"title",text:"Chat config"},{kind:"p",text:`Same trust as shell \u2014 allowlisted senders can change many keys saved to ${_}.`},{kind:"gap"},{kind:"bullet",text:"/config show \u2014 full snapshot (telegramBotToken masked)"},{kind:"bullet",text:"/config get <key> \u2014 single field"},{kind:"bullet",text:"/config set <key> <value> \u2014 values can be quoted for paths with spaces"},{kind:"bullet",text:"/config keys \u2014 allowed keys for /config set (includes tunnel*, webhook*, watch*, chatLlm*, cluster*, \u2026)"},{kind:"gap"},{kind:"p",text:"After edits: /reload while omnish run is active (needed for gatewayMode and telegramBotToken)."}]}function ci(){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: ${_}`}]}function uc(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: ${Me(e.updateBrief)}`,""),t.push("Change: /gateway both \xB7 /gw tg","/updates \u2014 npm + optional notice URL",`Config: ${Me(_)}`);let n=["<b>Gateway status</b>","",`<b>${q(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> ${q(e.updateBrief)}`,""),n.push("<i>Change:</i> /gateway both \xB7 /gw tg","<i>/updates</i> \u2014 npm + optional notice URL",q(`Config: ${_}`)),de(t.join(`
38
38
  `),n.join(`
39
- `))}function cr(e){let t=["*Update check*","",`Running: *${Me(e.runningVersion)}*`,`Checked (UTC): ${e.checkedAtIso}`,`npm package: ${Me(e.registryPackage)}`];e.registryError?t.push("",`Registry: ${Me(e.registryError)}`):e.registryLatest&&t.push("",e.updateAvailable?`*Newer version on npm:* ${Me(e.registryLatest)} (upgrade when ready).`:`npm latest: ${Me(e.registryLatest)} (this install is current or newer).`),e.infoError?t.push("",`Notice URL: ${Me(e.infoError)}`):e.infoMessage&&(t.push("",`Notice: ${Me(e.infoMessage)}`),e.infoLink&&t.push(Me(e.infoLink))),t.push("","Config: updateCheckEnabled, updateCheckIntervalMs, updateCheckPackageName, updateInfoUrl");let n=["<b>Update check</b>","",`<b>Running</b> <code>${q(e.runningVersion)}</code>`,`<b>Checked (UTC)</b> ${q(e.checkedAtIso)}`,`<b>npm package</b> <code>${q(e.registryPackage)}</code>`];return e.registryError?n.push("",`<b>Registry</b> ${q(e.registryError)}`):e.registryLatest&&n.push("",e.updateAvailable?`<b>Newer on npm</b> <code>${q(e.registryLatest)}</code>`:`<b>npm latest</b> <code>${q(e.registryLatest)}</code> (current or newer here)`),e.infoError?n.push("",`<b>Notice URL</b> ${q(e.infoError)}`):e.infoMessage&&(n.push("",`<b>Notice</b> ${q(e.infoMessage)}`),e.infoLink&&n.push(q(e.infoLink))),n.push("","<i>Config keys:</i> updateCheckEnabled, updateCheckIntervalMs, updateCheckPackageName, updateInfoUrl"),ue(t.join(`
39
+ `))}function pr(e){let t=["*Update check*","",`Running: *${Me(e.runningVersion)}*`,`Checked (UTC): ${e.checkedAtIso}`,`npm package: ${Me(e.registryPackage)}`];e.registryError?t.push("",`Registry: ${Me(e.registryError)}`):e.registryLatest&&t.push("",e.updateAvailable?`*Newer version on npm:* ${Me(e.registryLatest)} (upgrade when ready).`:`npm latest: ${Me(e.registryLatest)} (this install is current or newer).`),e.infoError?t.push("",`Notice URL: ${Me(e.infoError)}`):e.infoMessage&&(t.push("",`Notice: ${Me(e.infoMessage)}`),e.infoLink&&t.push(Me(e.infoLink))),t.push("","Config: updateCheckEnabled, updateCheckIntervalMs, updateCheckPackageName, updateInfoUrl");let n=["<b>Update check</b>","",`<b>Running</b> <code>${q(e.runningVersion)}</code>`,`<b>Checked (UTC)</b> ${q(e.checkedAtIso)}`,`<b>npm package</b> <code>${q(e.registryPackage)}</code>`];return e.registryError?n.push("",`<b>Registry</b> ${q(e.registryError)}`):e.registryLatest&&n.push("",e.updateAvailable?`<b>Newer on npm</b> <code>${q(e.registryLatest)}</code>`:`<b>npm latest</b> <code>${q(e.registryLatest)}</code> (current or newer here)`),e.infoError?n.push("",`<b>Notice URL</b> ${q(e.infoError)}`):e.infoMessage&&(n.push("",`<b>Notice</b> ${q(e.infoMessage)}`),e.infoLink&&n.push(q(e.infoLink))),n.push("","<i>Config keys:</i> updateCheckEnabled, updateCheckIntervalMs, updateCheckPackageName, updateInfoUrl"),de(t.join(`
40
40
  `),n.join(`
41
- `))}function sc(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: ${H} (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 ${_}`},{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 run --verbose."},{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 ic(e){let t=!!ve(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 ${_}, 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 ${_}; 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 ac(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})`,Me(o),""),n.push(`<b>${q(r.label)}</b> (${r.items.length})`,q(o),"")}return ue(t.join(`
41
+ `))}function dc(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: ${H} (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 ${_}`},{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 run --verbose."},{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 pc(e){let t=!!ve(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 ${_}, 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 ${_}; 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 mc(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})`,Me(o),""),n.push(`<b>${q(r.label)}</b> (${r.items.length})`,q(o),"")}return de(t.join(`
42
42
  `).trimEnd(),n.join(`
43
- `).trimEnd())}function ai(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})`,Me(s),""),r.push(`<b>${q(o.label)}</b> (${o.items.length})`,q(s),"")}return ue(n.join(`
43
+ `).trimEnd())}function ui(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})`,Me(s),""),r.push(`<b>${q(o.label)}</b> (${o.items.length})`,q(s),"")}return de(n.join(`
44
44
  `).trimEnd(),r.join(`
45
- `).trimEnd())}function lc(e){let t=["*Token saved*","","telegramBotToken written to config (not echoed).",`Config: ${Me(_)}`,...e.flatMap(r=>["",r]),"","Send /reload so the gateway picks it up."].join(`
45
+ `).trimEnd())}function fc(e){let t=["*Token saved*","","telegramBotToken written to config (not echoed).",`Config: ${Me(_)}`,...e.flatMap(r=>["",r]),"","Send /reload so the gateway picks it up."].join(`
46
46
  `),n=["<b>Token saved</b>","","telegramBotToken written to config (not echoed).",q(`Config: ${_}`),...e.map(r=>`<i>${q(r)}</i>`),"","Send /reload so the gateway picks it up."].join(`
47
47
  `)+`
48
- `;return ue(t.trimEnd(),n.trimEnd())}function cc(){return Z([{kind:"title",text:"That does not look like a bot token"},{kind:"p",text:"Expected from @BotFather: one token, no spaces, like 123456789:AA_heG\u2026"},{kind:"bullet",text:"Example: /tg token 123456789:AAAbcd\u2026"}])}function uc(e,t,n){let r=t?`
48
+ `;return de(t.trimEnd(),n.trimEnd())}function hc(){return Z([{kind:"title",text:"That does not look like a bot token"},{kind:"p",text:"Expected from @BotFather: one token, no spaces, like 123456789:AA_heG\u2026"},{kind:"bullet",text:"Example: /tg token 123456789:AAAbcd\u2026"}])}function gc(e,t,n){let r=t?`
49
49
 
50
50
  ${t}`:n?`
51
51
 
@@ -59,72 +59,72 @@ Start omnish run on the host to apply (or /reload from a running gateway).`:"",s
59
59
  ${Me(_)}${r}`,i=`<b>gatewayMode saved</b>
60
60
 
61
61
  <code>${q(e)}</code>
62
- ${q(_)}${o}`;return ue(s,i)}function dc(){return Z([{kind:"title",text:"/allow \u2014 add to allowlist"},{kind:"bullet",text:"/allow +<E164> \u2014 WhatsApp (country code, no spaces)"},{kind:"bullet",text:"/allow tg:<user_id> \u2014 Telegram numeric id"},{kind:"gap"},{kind:"p",text:"Examples: /allow +15551234567 \xB7 /allow tg:987654321"}])}function pc(){return Z([{kind:"title",text:"/deny \u2014 remove from allowlist"},{kind:"p",text:"Same forms as /allow: +E164 or tg:id"}])}function mc(){return Z([{kind:"title",text:"/bg \u2014 background job"},{kind:"p",text:"Usage: /bg [flags] <shell command>"},{kind:"bullet",text:"Optional name: /bg -n mybuild npm run build \xB7 /bg --name mybuild \u2026 \xB7 /bg --name=mybuild \u2026"},{kind:"bullet",text:"Notify on completion: /bg --notify <cmd> or /bg -N <cmd> \u2014 sends a message when the job finishes (with exit status)."},{kind:"bullet",text:"Combine flags: /bg -N -n deploy git pull && docker compose up -d"},{kind:"bullet",text:"Then use /log mybuild, /tail mybuild, /kill mybuild (or the 8-char id). Names: letters, digits, . _ - up to 64 chars."},{kind:"bullet",text:"Example: /bg sleep 30 && echo done"}])}function li(){return Z([{kind:"title",text:"/send \u2014 push a host file to this chat"},{kind:"p",text:"Usage: /send <selectors> [-- caption] \u2014 selectors resolve from session cwd."},{kind:"bullet",text:"/send ./photo.png"},{kind:"bullet",text:"/send ./clip1.mp4,./clip2.mp4 -- Launch set"},{kind:"bullet",text:"/send **/*.mp4"},{kind:"bullet",text:"/send /abs/path/report.pdf -- Q4 draft"},{kind:"bullet",text:"Selectors: file1,file2 | *.ext | **/*.ext"},{kind:"bullet",text:"Alias: /file \u2026"},{kind:"gap"},{kind:"p",text:"Caps: fileSendMaxBytes / fileReceiveMaxBytes (0 = no omnish cap). Where inbound media is saved: fileReceiveRootMode + fileReceiveRootPath / fileInboxSubdir \u2014 send /files for details."}])}function ci(){return Z([{kind:"title",text:"Files \u2014 send & receive"},{kind:"sub",text:"Host \u2192 chat"},{kind:"bullet",text:"/send <selectors> or /file \u2026 \u2014 selectors support file1,file2, *.ext, **/*.ext from this chat\u2019s session cwd (same as ! shell); optional caption after -- "},{kind:"bullet",text:"fileSendMaxBytes in config caps outbound size (0 = no omnish cap)."},{kind:"gap"},{kind:"sub",text:"Chat \u2192 host"},{kind:"bullet",text:"Send a photo, video, document, audio, etc. in this DM; omnish saves it and replies with Saved: <path> (or an error)."},{kind:"bullet",text:"Folders: <root>/<peer>/<YYYY-MM-DD>/<filename> \u2014 root comes from config below."},{kind:"bullet",text:"fileReceiveRootMode: downloads (home/Downloads/Omnish) \xB7 omnishData (data dir + fileInboxSubdir) \xB7 sessionCwd \xB7 processCwd \xB7 fixed (needs absolute fileReceiveRootPath)."},{kind:"bullet",text:"fileReceiveMaxBytes \u2014 inbound size cap (0 = no omnish cap)."},{kind:"gap"},{kind:"p",text:`Edit config on the host (${_}) 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 ui(){return Z([{kind:"title",text:"/receive \u2014 inbound files for this chat"},{kind:"p",text:"Stored on the host with your session (sessions.json). Does not edit config.json."},{kind:"bullet",text:"/receive \u2014 show current setting and resolved save folder"},{kind:"bullet",text:"/receive here \u2014 save uploads under this chat\u2019s session cwd (same as !cd); subfolders: peer / date / file"},{kind:"bullet",text:"/receive default \u2014 clear per-chat rule; use global fileReceiveRootMode (/files, config.json)"},{kind:"gap"},{kind:"p",text:"Aliases: here = cwd = session = dir \xB7 default = global = reset"}])}function fc(e,t){let n=Gl(t),r=oe(t),o="";try{o=Ft(e,t)}catch(s){o=`(${String(s)})`}return Z(n==="sessionCwd"?[{kind:"title",text:"/receive \u2014 this chat"},{kind:"p",text:"Per-chat: inbound media saves under your session folder (updated when you !cd)."},{kind:"bullet",text:`Session cwd: ${r.cwd}`},{kind:"bullet",text:`Next file root: ${o}`},{kind:"p",text:"Send /receive default to follow server config instead."}]:[{kind:"title",text:"/receive \u2014 this chat"},{kind:"p",text:"Per-chat override: off \u2014 using global fileReceiveRootMode from config."},{kind:"bullet",text:`Global mode: ${e.fileReceiveRootMode}`},{kind:"bullet",text:`Next file root: ${o}`},{kind:"p",text:"Send /receive here to pin saves to your current session folder."}])}function hc(){return Z([{kind:"title",text:"Unknown /wa command"},{kind:"p",text:"Send /wa help for setup."}])}function gc(){return Z([{kind:"title",text:"Unknown /tg command"},{kind:"p",text:"Send /tg help or /tg token <botfather_token>."}])}function yc(){return Z([{kind:"title",text:"Free shell mode on"},{kind:"p",text:"Plain messages run as sync shell (no command prefix)."},{kind:"bullet",text:"Send !!stop to turn off."},{kind:"bullet",text:"Apps: plain DMs no longer go to the focused app \u2014 use >name text or /apps send."}])}function wc(e){return Z([{kind:"title",text:"Unknown command"},{kind:"p",text:`Try /help or ${e.commandPrefix}<command>.`},{kind:"gap"},...xn(e)])}function bc(e){return Z([{kind:"title",text:"No command matched"},{kind:"p",text:`Use ${JSON.stringify(e.commandPrefix)}, a slash command, or /apps help.`},{kind:"gap"},...xn(e)])}function kc(){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"},...ii()];return Z(e)}function di(){return[{kind:"title",text:"Shortcuts (this gateway)"},{kind:"p",text:"Stored per-chat or shared for every chat on this gateway. Expansion: this chat wins, then shared. Bare token only \u2014 for task injection use /run recipes."},{kind:"p",text:"Scope flags: -g or --global = shared on this gateway; -p or --chat = private to this chat."},{kind:"gap"},{kind:"sub",text:"Manage"},{kind:"bullet",text:"/shortcut add <name> <command\u2026> \u2014 private by default; add -g|--global to share on this gateway"},{kind:"bullet",text:"/shortcut add -p|--chat <name> <command\u2026> \u2014 explicit private (this chat)"},{kind:"bullet",text:"/shortcut set <name> <command\u2026> \u2014 overwrite in chosen bucket; scope-only (same line): /shortcut set -g <name> or /shortcut set <name> -g (share); /shortcut set -p <name> or /shortcut set <name> -p (private)"},{kind:"bullet",text:"/shortcut list \u2014 merged view (/shortcuts); list --chat | -p | list --global | -g"},{kind:"bullet",text:"/shortcut show <name>; show --global|-g|--chat|-p to read one bucket"},{kind:"bullet",text:"/shortcut remove <name> \u2014 private bucket; remove --global|-g drops shared \xB7 rm, del \xB7 --chat|-p"},{kind:"bullet",text:"/shortcut <name> publish \u2014 share to online catalog (platform login)"},{kind:"bullet",text:"/shortcut online trending | show <publicId> | <publicId> download \u2014 shortcuts only"},{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 Sc(e){if(e.length===0)return p("(no shortcuts \u2014 /shortcut add <name> <command\u2026> or /shortcut add --global <name> <command\u2026>)");let t=e.every(l=>l.scope==="global"),n=e.every(l=>l.scope==="chat"),r=t?"_Shared (every chat)_":n?"_This chat only_":"_This chat + shared_",o=t?"<i>Shared (every chat)</i>":n?"<i>This chat only</i>":"<i>This chat + shared</i>",s=e.some(l=>l.scope==="chat")&&e.some(l=>l.scope==="global"),i=["*Shortcuts*",r,"",...e.map(l=>{let d=s?l.scope==="chat"?"[chat] ":"[global] ":"";return`${Re}\`${d}${l.name}\` \u2192 ${l.body}`})].join(`
62
+ ${q(_)}${o}`;return de(s,i)}function yc(){return Z([{kind:"title",text:"/allow \u2014 add to allowlist"},{kind:"bullet",text:"/allow +<E164> \u2014 WhatsApp (country code, no spaces)"},{kind:"bullet",text:"/allow tg:<user_id> \u2014 Telegram numeric id"},{kind:"gap"},{kind:"p",text:"Examples: /allow +15551234567 \xB7 /allow tg:987654321"}])}function wc(){return Z([{kind:"title",text:"/deny \u2014 remove from allowlist"},{kind:"p",text:"Same forms as /allow: +E164 or tg:id"}])}function bc(){return Z([{kind:"title",text:"/bg \u2014 background job"},{kind:"p",text:"Usage: /bg [flags] <shell command>"},{kind:"bullet",text:"Optional name: /bg -n mybuild npm run build \xB7 /bg --name mybuild \u2026 \xB7 /bg --name=mybuild \u2026"},{kind:"bullet",text:"Notify on completion: /bg --notify <cmd> or /bg -N <cmd> \u2014 sends a message when the job finishes (with exit status)."},{kind:"bullet",text:"Combine flags: /bg -N -n deploy git pull && docker compose up -d"},{kind:"bullet",text:"Then use /log mybuild, /tail mybuild, /kill mybuild (or the 8-char id). Names: letters, digits, . _ - up to 64 chars."},{kind:"bullet",text:"Example: /bg sleep 30 && echo done"}])}function di(){return Z([{kind:"title",text:"/send \u2014 push a host file to this chat"},{kind:"p",text:"Usage: /send <selectors> [-- caption] \u2014 selectors resolve from session cwd."},{kind:"bullet",text:"/send ./photo.png"},{kind:"bullet",text:"/send ./clip1.mp4,./clip2.mp4 -- Launch set"},{kind:"bullet",text:"/send **/*.mp4"},{kind:"bullet",text:"/send /abs/path/report.pdf -- Q4 draft"},{kind:"bullet",text:"Selectors: file1,file2 | *.ext | **/*.ext"},{kind:"bullet",text:"Alias: /file \u2026"},{kind:"gap"},{kind:"p",text:"Caps: fileSendMaxBytes / fileReceiveMaxBytes (0 = no omnish cap). Where inbound media is saved: fileReceiveRootMode + fileReceiveRootPath / fileInboxSubdir \u2014 send /files for details."}])}function pi(){return Z([{kind:"title",text:"Files \u2014 send & receive"},{kind:"sub",text:"Host \u2192 chat"},{kind:"bullet",text:"/send <selectors> or /file \u2026 \u2014 selectors support file1,file2, *.ext, **/*.ext from this chat\u2019s session cwd (same as ! shell); optional caption after -- "},{kind:"bullet",text:"fileSendMaxBytes in config caps outbound size (0 = no omnish cap)."},{kind:"gap"},{kind:"sub",text:"Chat \u2192 host"},{kind:"bullet",text:"Send a photo, video, document, audio, etc. in this DM; omnish saves it and replies with Saved: <path> (or an error)."},{kind:"bullet",text:"Folders: <root>/<peer>/<YYYY-MM-DD>/<filename> \u2014 root comes from config below."},{kind:"bullet",text:"fileReceiveRootMode: downloads (home/Downloads/Omnish) \xB7 omnishData (data dir + fileInboxSubdir) \xB7 sessionCwd \xB7 processCwd \xB7 fixed (needs absolute fileReceiveRootPath)."},{kind:"bullet",text:"fileReceiveMaxBytes \u2014 inbound size cap (0 = no omnish cap)."},{kind:"gap"},{kind:"p",text:`Edit config on the host (${_}) 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 mi(){return Z([{kind:"title",text:"/receive \u2014 inbound files for this chat"},{kind:"p",text:"Stored on the host with your session (sessions.json). Does not edit config.json."},{kind:"bullet",text:"/receive \u2014 show current setting and resolved save folder"},{kind:"bullet",text:"/receive here \u2014 save uploads under this chat\u2019s session cwd (same as !cd); subfolders: peer / date / file"},{kind:"bullet",text:"/receive default \u2014 clear per-chat rule; use global fileReceiveRootMode (/files, config.json)"},{kind:"gap"},{kind:"p",text:"Aliases: here = cwd = session = dir \xB7 default = global = reset"}])}function kc(e,t){let n=Ql(t),r=oe(t),o="";try{o=Ut(e,t)}catch(s){o=`(${String(s)})`}return Z(n==="sessionCwd"?[{kind:"title",text:"/receive \u2014 this chat"},{kind:"p",text:"Per-chat: inbound media saves under your session folder (updated when you !cd)."},{kind:"bullet",text:`Session cwd: ${r.cwd}`},{kind:"bullet",text:`Next file root: ${o}`},{kind:"p",text:"Send /receive default to follow server config instead."}]:[{kind:"title",text:"/receive \u2014 this chat"},{kind:"p",text:"Per-chat override: off \u2014 using global fileReceiveRootMode from config."},{kind:"bullet",text:`Global mode: ${e.fileReceiveRootMode}`},{kind:"bullet",text:`Next file root: ${o}`},{kind:"p",text:"Send /receive here to pin saves to your current session folder."}])}function Sc(){return Z([{kind:"title",text:"Unknown /wa command"},{kind:"p",text:"Send /wa help for setup."}])}function vc(){return Z([{kind:"title",text:"Unknown /tg command"},{kind:"p",text:"Send /tg help or /tg token <botfather_token>."}])}function xc(){return Z([{kind:"title",text:"Free shell mode on"},{kind:"p",text:"Plain messages run as sync shell (no command prefix)."},{kind:"bullet",text:"Send !!stop to turn off."},{kind:"bullet",text:"Apps: plain DMs no longer go to the focused app \u2014 use >name text or /apps send."}])}function $c(e){return Z([{kind:"title",text:"Unknown command"},{kind:"p",text:`Try /help or ${e.commandPrefix}<command>.`},{kind:"gap"},...Tn(e)])}function Rc(e){return Z([{kind:"title",text:"No command matched"},{kind:"p",text:`Use ${JSON.stringify(e.commandPrefix)}, a slash command, or /apps help.`},{kind:"gap"},...Tn(e)])}function Cc(){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"},...ci()];return Z(e)}function fi(){return[{kind:"title",text:"Shortcuts (this gateway)"},{kind:"p",text:"Stored per-chat or shared for every chat on this gateway. Expansion: this chat wins, then shared. Bare token only \u2014 for task injection use /run recipes."},{kind:"p",text:"Scope flags: -g or --global = shared on this gateway; -p or --chat = private to this chat."},{kind:"gap"},{kind:"sub",text:"Manage"},{kind:"bullet",text:"/shortcut add <name> <command\u2026> \u2014 private by default; add -g|--global to share on this gateway"},{kind:"bullet",text:"/shortcut add -p|--chat <name> <command\u2026> \u2014 explicit private (this chat)"},{kind:"bullet",text:"/shortcut set <name> <command\u2026> \u2014 overwrite in chosen bucket; scope-only (same line): /shortcut set -g <name> or /shortcut set <name> -g (share); /shortcut set -p <name> or /shortcut set <name> -p (private)"},{kind:"bullet",text:"/shortcut list \u2014 merged view (/shortcuts); list --chat | -p | list --global | -g"},{kind:"bullet",text:"/shortcut show <name>; show --global|-g|--chat|-p to read one bucket"},{kind:"bullet",text:"/shortcut remove <name> \u2014 private bucket; remove --global|-g drops shared \xB7 rm, del \xB7 --chat|-p"},{kind:"bullet",text:"/shortcut <name> publish \u2014 share to online catalog (platform login)"},{kind:"bullet",text:"/shortcut online trending | show <publicId> | <publicId> download \u2014 shortcuts only"},{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 Mc(e){if(e.length===0)return p("(no shortcuts \u2014 /shortcut add <name> <command\u2026> or /shortcut add --global <name> <command\u2026>)");let t=e.every(l=>l.scope==="global"),n=e.every(l=>l.scope==="chat"),r=t?"_Shared (every chat)_":n?"_This chat only_":"_This chat + shared_",o=t?"<i>Shared (every chat)</i>":n?"<i>This chat only</i>":"<i>This chat + shared</i>",s=e.some(l=>l.scope==="chat")&&e.some(l=>l.scope==="global"),i=["*Shortcuts*",r,"",...e.map(l=>{let d=s?l.scope==="chat"?"[chat] ":"[global] ":"";return`${Re}\`${d}${l.name}\` \u2192 ${l.body}`})].join(`
63
63
  `),a=["<b>Shortcuts</b>",o,"",...e.map(l=>{let u=`${s?l.scope==="chat"?"[chat] ":"[global] ":""}${l.name}`;return`\u2022 <code>${q(u)}</code> \u2192 ${q(l.body)}`})].join(`
64
- `);return ue(i,a)}function ur(e,t,n="chat"){return p(`Shortcut saved: ${e}
64
+ `);return de(i,a)}function mr(e,t,n="chat"){return p(`Shortcut saved: ${e}
65
65
  \u2192 ${t}
66
- (${n==="global"?"Shared on this gateway (every chat unless this chat overrides the name).":"Stored for this chat only."})`)}function vc(e,t="chat"){return p(`Shortcut removed (${t==="global"?"shared":"this chat"}): ${e}`)}function xc(e){return p(`Unknown shortcut: ${e}`)}function $c(e,t){return p(`Unknown shortcut "${e}" in ${t==="global"?"shared shortcuts":"this chat"}.`)}function pi(e,t,n){let r=n?`
66
+ (${n==="global"?"Shared on this gateway (every chat unless this chat overrides the name).":"Stored for this chat only."})`)}function Tc(e,t="chat"){return p(`Shortcut removed (${t==="global"?"shared":"this chat"}): ${e}`)}function Ec(e){return p(`Unknown shortcut: ${e}`)}function Pc(e,t){return p(`Unknown shortcut "${e}" in ${t==="global"?"shared shortcuts":"this chat"}.`)}function hi(e,t,n){let r=n?`
67
67
 
68
68
  ${n}`:"";return p(`${e}
69
- \u2192 ${t}${r}`)}function Rc(){return[{kind:"title",text:"/run \u2014 recipe templates"},{kind:"sub",text:"Invoke"},{kind:"bullet",text:"/run <name> <task\u2026> \u2014 start attached session (plain DMs go to it until /apps detach)"},{kind:"bullet",text:"/r <name> <task\u2026> \u2014 short alias for /run"},{kind:"gap"},{kind:"sub",text:"Queue"},{kind:"bullet",text:"/run <name> -q <task\u2026> or /run <name> --queue <task\u2026> \u2014 FIFO per chat: head starts immediately; others wait in memory until the head exits cleanly (code=0, signal=0)"},{kind:"bullet",text:"Pending counts only jobs not yet started \u2014 the first queued item becomes Active right away, so /run queue can show Pending: 0 while a queued run is still executing"},{kind:"bullet",text:"/run queue \u2014 active session + recipe, numbered waiting list, paused flag"},{kind:"bullet",text:"/run queue resume \u2014 after a pause or non-clean exit, clear pause and start the next waiting item"},{kind:"bullet",text:"/run queue load <file.json> \u2014 enqueue jobs from JSON (paths relative to session cwd); /run queue load json [\u2026] \u2014 same payload inline; attach a file with caption /run queue load"},{kind:"bullet",text:'Queue JSON: [ { "recipe": "<name>", "task": "<text>" }, \u2026 ] or { "tasks": [ \u2026 ] } (max 64 jobs per load; same rules as /run <name> -q)'},{kind:"gap"},{kind:"sub",text:"Discover"},{kind:"bullet",text:"/run list \u2014 featured, gateway-shared, this chat, host templates; list --chat | -p | list --global | -g"},{kind:"bullet",text:"/run show <name> \u2014 merged resolution; show --global|-g|--chat|-p reads one user bucket"},{kind:"gap"},{kind:"sub",text:"Manage"},{kind:"p",text:"Recipe scope flags: -g or --global = shared on this gateway; -p or --chat = private to this chat."},{kind:"bullet",text:'/run add <name> <command\u2026> [--template "\u2026"] \u2014 this chat; add --global|-g share on this gateway'},{kind:"bullet",text:"/run set <name> <command\u2026> \u2014 overwrite in chosen bucket (--global|-g|--chat|-p); scope-only (same body): /run set -g <name> or /run set <name> -g (share); /run set -p <name> or /run set <name> -p (this chat only)"},{kind:"bullet",text:"/run remove <name> \u2014 this chat storage; remove --global|-g clears gateway-shared \xB7 rm, del \xB7 --chat|-p"},{kind:"gap"},{kind:"sub",text:"Notes"},{kind:"bullet",text:"Commands must reference the task env variable (default `$OMNISH_TASK`)."},{kind:"bullet",text:"Template placeholders `<<<OMNISH_TASK>>>`, `<<<```$OMNISH_TASK```>>>`, and `$OMNISH_TASK` are replaced with task text."},{kind:"bullet",text:"Host overrides file: "+Ur},{kind:"bullet",text:"Built-in Claude recipes omit dangerous flags unless recipesAllowDangerousBuiltins=true."},{kind:"gap"},{kind:"sub",text:"Online catalog"},{kind:"bullet",text:"/run online trending | search <query> | show <publicId> | <publicId> download \u2014 all kinds"},{kind:"bullet",text:"/run <recipe> publish \u2014 share to platform (requires omnish platform login)"}]}function Wf(e){switch(e){case"builtin":return"built-in";case"global":return"host recipes.json";case"shared":return"gateway-shared (/run add --global)";case"peer":return"this chat (/run add)";default:return e}}function Cc(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(`${Re}${o.name} \u2014 ${o.label??o.name}${i}${a}`);let l=o.description?` \u2014 ${q(o.description)}`:"",d=s&&o.dangerous?q(" [dangerous flags]"):"";n.push(`\u2022 ${q(o.name)} \u2014 ${q(o.label??o.name)}${l}${d}`)};if(e.featured.length>0){t.push("_Featured:_"),n.push("<i>Featured:</i>");for(let o of e.featured)r(o,!0);t.push(""),n.push("")}if(e.shared.length>0){t.push("_Shared (every chat):_"),n.push("<i>Shared (every chat):</i>");for(let o of e.shared)r(o,!1);t.push(""),n.push("")}if(e.yours.length>0){t.push("_This chat:_"),n.push("<i>This chat:</i>");for(let o of e.yours)r(o,!1);t.push(""),n.push("")}if(e.more.length>0){t.push("_More:_"),n.push("<i>More:</i>");for(let o of e.more)r(o,!1)}return e.featured.length===0&&e.shared.length===0&&e.yours.length===0&&e.more.length===0&&(t.push("(no recipes \u2014 add host file or /run add)"),n.push(q("(no recipes \u2014 add host file or /run add)"))),ue(t.join(`
69
+ \u2192 ${t}${r}`)}function Ac(){return[{kind:"title",text:"/run \u2014 recipe templates"},{kind:"sub",text:"Invoke"},{kind:"bullet",text:"/run <name> <task\u2026> \u2014 start attached session (plain DMs go to it until /apps detach)"},{kind:"bullet",text:"/r <name> <task\u2026> \u2014 short alias for /run"},{kind:"gap"},{kind:"sub",text:"Queue"},{kind:"bullet",text:"/run <name> -q <task\u2026> or /run <name> --queue <task\u2026> \u2014 FIFO per chat: head starts immediately; others wait in memory until the head exits cleanly (code=0, signal=0)"},{kind:"bullet",text:"Pending counts only jobs not yet started \u2014 the first queued item becomes Active right away, so /run queue can show Pending: 0 while a queued run is still executing"},{kind:"bullet",text:"/run queue \u2014 active session + recipe, numbered waiting list, paused flag"},{kind:"bullet",text:"/run queue resume \u2014 after a pause or non-clean exit, clear pause and start the next waiting item"},{kind:"bullet",text:"/run queue load <file.json> \u2014 enqueue jobs from JSON (paths relative to session cwd); /run queue load json [\u2026] \u2014 same payload inline; attach a file with caption /run queue load"},{kind:"bullet",text:'Queue JSON: [ { "recipe": "<name>", "task": "<text>" }, \u2026 ] or { "tasks": [ \u2026 ] } (max 64 jobs per load; same rules as /run <name> -q)'},{kind:"gap"},{kind:"sub",text:"Discover"},{kind:"bullet",text:"/run list \u2014 featured, gateway-shared, this chat, host templates; list --chat | -p | list --global | -g"},{kind:"bullet",text:"/run show <name> \u2014 merged resolution; show --global|-g|--chat|-p reads one user bucket"},{kind:"gap"},{kind:"sub",text:"Manage"},{kind:"p",text:"Recipe scope flags: -g or --global = shared on this gateway; -p or --chat = private to this chat."},{kind:"bullet",text:'/run add <name> <command\u2026> [--template "\u2026"] \u2014 this chat; add --global|-g share on this gateway'},{kind:"bullet",text:"/run set <name> <command\u2026> \u2014 overwrite in chosen bucket (--global|-g|--chat|-p); scope-only (same body): /run set -g <name> or /run set <name> -g (share); /run set -p <name> or /run set <name> -p (this chat only)"},{kind:"bullet",text:"/run remove <name> \u2014 this chat storage; remove --global|-g clears gateway-shared \xB7 rm, del \xB7 --chat|-p"},{kind:"gap"},{kind:"sub",text:"Notes"},{kind:"bullet",text:"Commands must reference the task env variable (default `$OMNISH_TASK`)."},{kind:"bullet",text:"Template placeholders `<<<OMNISH_TASK>>>`, `<<<```$OMNISH_TASK```>>>`, and `$OMNISH_TASK` are replaced with task text."},{kind:"bullet",text:"Host overrides file: "+jr},{kind:"bullet",text:"Built-in Claude recipes omit dangerous flags unless recipesAllowDangerousBuiltins=true."},{kind:"gap"},{kind:"sub",text:"Online catalog"},{kind:"bullet",text:"/run online trending | search <query> | show <publicId> | <publicId> download \u2014 all kinds"},{kind:"bullet",text:"/run <recipe> publish \u2014 share to platform (requires omnish platform login)"}]}function Gf(e){switch(e){case"builtin":return"built-in";case"global":return"host recipes.json";case"shared":return"gateway-shared (/run add --global)";case"peer":return"this chat (/run add)";default:return e}}function Ic(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(`${Re}${o.name} \u2014 ${o.label??o.name}${i}${a}`);let l=o.description?` \u2014 ${q(o.description)}`:"",d=s&&o.dangerous?q(" [dangerous flags]"):"";n.push(`\u2022 ${q(o.name)} \u2014 ${q(o.label??o.name)}${l}${d}`)};if(e.featured.length>0){t.push("_Featured:_"),n.push("<i>Featured:</i>");for(let o of e.featured)r(o,!0);t.push(""),n.push("")}if(e.shared.length>0){t.push("_Shared (every chat):_"),n.push("<i>Shared (every chat):</i>");for(let o of e.shared)r(o,!1);t.push(""),n.push("")}if(e.yours.length>0){t.push("_This chat:_"),n.push("<i>This chat:</i>");for(let o of e.yours)r(o,!1);t.push(""),n.push("")}if(e.more.length>0){t.push("_More:_"),n.push("<i>More:</i>");for(let o of e.more)r(o,!1)}return e.featured.length===0&&e.shared.length===0&&e.yours.length===0&&e.more.length===0&&(t.push("(no recipes \u2014 add host file or /run add)"),n.push(q("(no recipes \u2014 add host file or /run add)"))),de(t.join(`
70
70
  `).trimEnd(),n.join(`
71
- `).trimEnd())}function mi(e,t){let n=e.taskEnv??"OMNISH_TASK",r=[`Recipe: ${e.name}`,`Source: ${Wf(e.source)}`,`Label: ${e.label??"(none)"}`];if(e.steps&&e.steps.length>0){r.push(`Type: runbook (${e.steps.length} steps)`);for(let o=0;o<e.steps.length;o++){let s=e.steps[o],i=s.label?` (${s.label})`:"",a=s.continueOnFail?" [continue-on-fail]":"";r.push(` ${o+1}. ${s.cmd}${i}${a}`)}}else r.push(`Task env: ${n}`),r.push(`Command: ${e.command}`);if(e.promptTemplate){r.push(`Template: ${e.promptTemplate.length} chars`);let s=e.promptTemplate.replace(/\s+/g," ").trim().slice(0,200);r.push(`Preview: ${s}${e.promptTemplate.length>200?"\u2026":""}`)}return e.category&&r.push(`Category: ${e.category}`),e.description&&r.push(`Description: ${e.description}`),e.dangerous&&r.push("Note: built-in includes gated dangerous CLI flags."),t&&r.push("",t),p(r.join(`
72
- `))}function dr(e,t,n="chat"){let r=n==="global"?"Shared on this gateway (every chat unless this chat overrides the name).":"Stored for this chat only.",o=[`Recipe saved: ${e}`,`\u2192 ${t.command}`,`(task env: ${t.taskEnv??"OMNISH_TASK"})`];return t.promptTemplate&&o.push(`Template stored: ${t.promptTemplate.length} chars`),o.push(`(${r})`),p(o.join(`
73
- `))}function Mc(e,t="chat"){return p(`Recipe removed (${t==="global"?"gateway-shared storage":"this chat"}): ${e}`)}function Tc(e,t){return p(`Unknown recipe "${e}" in ${t==="global"?"gateway-shared storage":"this chat storage"}.`)}function Ec(e,t){let n=t==="global"?"_Gateway-shared recipes_":"_This chat recipes_",r=t==="global"?"<i>Gateway-shared recipes</i>":"<i>This chat recipes</i>";if(e.length===0)return p(t==="global"?"(no gateway-shared recipes \u2014 /run add --global <name> <command\u2026>)":"(no recipes in this chat \u2014 /run add <name> <command\u2026>)");let o=["*Recipes*",n,""],s=["<b>Recipes</b>",r,""];for(let i of e){let a=i.description?` \u2014 ${i.description}`:"";o.push(`${Re}${i.name} \u2014 ${i.label??i.name}${a}`);let l=i.description?` \u2014 ${q(i.description)}`:"";s.push(`\u2022 ${q(i.name)} \u2014 ${q(i.label??i.name)}${l}`)}return ue(o.join(`
71
+ `).trimEnd())}function gi(e,t){let n=e.taskEnv??"OMNISH_TASK",r=[`Recipe: ${e.name}`,`Source: ${Gf(e.source)}`,`Label: ${e.label??"(none)"}`];if(e.steps&&e.steps.length>0){r.push(`Type: runbook (${e.steps.length} steps)`);for(let o=0;o<e.steps.length;o++){let s=e.steps[o],i=s.label?` (${s.label})`:"",a=s.continueOnFail?" [continue-on-fail]":"";r.push(` ${o+1}. ${s.cmd}${i}${a}`)}}else r.push(`Task env: ${n}`),r.push(`Command: ${e.command}`);if(e.promptTemplate){r.push(`Template: ${e.promptTemplate.length} chars`);let s=e.promptTemplate.replace(/\s+/g," ").trim().slice(0,200);r.push(`Preview: ${s}${e.promptTemplate.length>200?"\u2026":""}`)}return e.category&&r.push(`Category: ${e.category}`),e.description&&r.push(`Description: ${e.description}`),e.dangerous&&r.push("Note: built-in includes gated dangerous CLI flags."),t&&r.push("",t),p(r.join(`
72
+ `))}function fr(e,t,n="chat"){let r=n==="global"?"Shared on this gateway (every chat unless this chat overrides the name).":"Stored for this chat only.",o=[`Recipe saved: ${e}`,`\u2192 ${t.command}`,`(task env: ${t.taskEnv??"OMNISH_TASK"})`];return t.promptTemplate&&o.push(`Template stored: ${t.promptTemplate.length} chars`),o.push(`(${r})`),p(o.join(`
73
+ `))}function Oc(e,t="chat"){return p(`Recipe removed (${t==="global"?"gateway-shared storage":"this chat"}): ${e}`)}function Lc(e,t){return p(`Unknown recipe "${e}" in ${t==="global"?"gateway-shared storage":"this chat storage"}.`)}function Nc(e,t){let n=t==="global"?"_Gateway-shared recipes_":"_This chat recipes_",r=t==="global"?"<i>Gateway-shared recipes</i>":"<i>This chat recipes</i>";if(e.length===0)return p(t==="global"?"(no gateway-shared recipes \u2014 /run add --global <name> <command\u2026>)":"(no recipes in this chat \u2014 /run add <name> <command\u2026>)");let o=["*Recipes*",n,""],s=["<b>Recipes</b>",r,""];for(let i of e){let a=i.description?` \u2014 ${i.description}`:"";o.push(`${Re}${i.name} \u2014 ${i.label??i.name}${a}`);let l=i.description?` \u2014 ${q(i.description)}`:"";s.push(`\u2022 ${q(i.name)} \u2014 ${q(i.label??i.name)}${l}`)}return de(o.join(`
74
74
  `).trimEnd(),s.join(`
75
- `).trimEnd())}function fi(e){return p(`Unknown recipe: ${e}
76
- /run list`)}function hi(){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:"bullet",text:"/apps <session> publish \u2014 share session command to online catalog"},{kind:"bullet",text:"/apps online trending | show <publicId> | <publicId> download \u2014 browse app templates only"},{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 Uf(e){let{errors:t,warns:n,infos:r}=si(e),o=[{kind:"title",text:"Security check"}];if(e.length===0)return o.push({kind:"p",text:"No issues reported by automated rules."},{kind:"gap"},{kind:"p",text:"Allowlisted remote shell access is still equivalent to sharing credentials with those identities."}),o;o.push({kind:"p",text:`${t} error(s), ${n} warning(s), ${r} note(s).`}),o.push({kind:"gap"});let s=(i,a)=>{if(a.length!==0){o.push({kind:"sub",text:`${i} (${a.length})`});for(let l of a){let d=l.severity.toUpperCase();o.push({kind:"bullet",text:Me(`[${d}] ${l.code}: ${l.message}`)}),l.detail&&o.push({kind:"bullet",text:Me(l.detail)}),l.fixHint&&o.push({kind:"bullet",text:Me(`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 Pc(e){return Z(Uf(e))}function Ac(){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 Ic(){return[{kind:"title",text:"/security commands"},{kind:"bullet",text:"/security \u2014 full report (same checks as omnish security)"},{kind:"bullet",text:"/security summary \u2014 one-line counts"},{kind:"bullet",text:"/security tips \u2014 short hardening checklist"},{kind:"bullet",text:"CLI: omnish security [--json] for scripts and monitoring"}]}function Se(e){return e.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;")}function $n(e){let t=e.trim();return t?t.length<=8?"(set)":`${t.slice(0,4)}\u2026${t.slice(-4)}`:"(empty)"}function Oc(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 Je(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 Lc=new Set(["downloads","omnishData","sessionCwd","processCwd","fixed"]),Bt=["gatewayMode","clusterEnabled","clusterRole","clusterLabel","clusterSenderBindings","commandPrefix","syncTimeoutMs","syncMaxBytes","jobLogTailLines","shell","appsCols","appsRows","appsFlushMs","appsMinIntervalMs","appsMaxFlushBytes","appsMaxSessions","appsMaxSessionsTotal","appsMaxWaChars","appsLogTailLines","appsSubmitDelayMs","appsClearInput","appsClearInputDelayMs","appsClearInputSequence","appsSkipClearOnPasswordPrompt","appsPasswordPromptHint","fileSendMaxBytes","fileReceiveMaxBytes","fileInboxSubdir","fileReceiveRootMode","fileReceiveRootPath","recipesAllowDangerousBuiltins","recipesMaxTaskChars","recipesMacroDefaultCommand","telegramBotToken","serviceInstallFromChat","updateCheckEnabled","updateCheckIntervalMs","updateCheckPackageName","updateInfoUrl","chatLlmFallbackEnabled","chatLlmShellCommand","chatLlmTimeoutMs","chatLlmMaxInputChars","chatLlmMaxOutputChars","chatLlmNeedsTty","chatLlmWorkDir","tunnelEnabled","tunnelRelayUrl","tunnelMaxActive","webhookEnabled","webhookPort","webhookHost","webhookToken","watchEnabled","watchDebounceMs","watchMaxEventsPerMinute","watchAutoRestore"];function Nc(e){return Bt.includes(e)}function Df(e){let t=ve(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: ${$n(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}`,`appsSkipClearOnPasswordPrompt: ${e.appsSkipClearOnPasswordPrompt}`,`appsPasswordPromptHint: ${e.appsPasswordPromptHint}`,"","*Files*",`fileSendMaxBytes / fileReceiveMaxBytes: ${e.fileSendMaxBytes} / ${e.fileReceiveMaxBytes}`,`fileInboxSubdir: ${e.fileInboxSubdir}`,`fileReceiveRootMode: ${e.fileReceiveRootMode}`,`fileReceiveRootPath: ${e.fileReceiveRootPath||"(empty)"}`,"","*Recipes*",`recipesAllowDangerousBuiltins: ${e.recipesAllowDangerousBuiltins}`,`recipesMaxTaskChars: ${e.recipesMaxTaskChars}`,`recipesMacroDefaultCommand: ${e.recipesMacroDefaultCommand}`,"","*Service (chat)*",`serviceInstallFromChat: ${e.serviceInstallFromChat}`,"","*Updates (optional)*",`updateCheckEnabled: ${e.updateCheckEnabled}`,`updateCheckIntervalMs: ${e.updateCheckIntervalMs}`,`updateCheckPackageName: ${e.updateCheckPackageName}`,`updateInfoUrl: ${e.updateInfoUrl?"(set)":"(empty)"}`,"","*Tunneling (chat /tunnel)*",`tunnelEnabled: ${e.tunnelEnabled}`,`tunnelRelayUrl: ${e.tunnelRelayUrl}`,`tunnelMaxActive: ${e.tunnelMaxActive}`,"","*Chat LLM fallback (optional)*",`chatLlmFallbackEnabled: ${e.chatLlmFallbackEnabled}`,`chatLlmShellCommand: ${e.chatLlmShellCommand?"(set)":"(empty)"}`,`chatLlmTimeoutMs: ${e.chatLlmTimeoutMs}`,`chatLlmMaxInputChars / chatLlmMaxOutputChars: ${e.chatLlmMaxInputChars} / ${e.chatLlmMaxOutputChars}`,`chatLlmNeedsTty: ${e.chatLlmNeedsTty}`,`chatLlmWorkDir: ${e.chatLlmWorkDir||"(empty \u2014 temp dir per run)"}`,"","*Webhook receiver (optional)*",`webhookEnabled: ${e.webhookEnabled}`,`webhookPort: ${e.webhookPort} (0 = random)`,`webhookHost: ${e.webhookHost}`,`webhookToken: ${$n(e.webhookToken)}`,"","*Watch (OS event eye)*",`watchEnabled: ${e.watchEnabled}`,`watchDebounceMs: ${e.watchDebounceMs}`,`watchMaxEventsPerMinute: ${e.watchMaxEventsPerMinute}`,`watchAutoRestore: ${e.watchAutoRestore}`,"",`File: ${_}`].join(`
77
- `)}function Hf(e){let t=ve(e);return["<b>Config</b> (secrets masked)","",`<b>gatewayMode</b> ${Se(e.gatewayMode)}`,`<b>commandPrefix</b> ${Se(e.commandPrefix)}`,`<b>shell</b> ${Se(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: ${Se(e.clusterRole)}`,`clusterLabel: ${Se(e.clusterLabel||"(hostname)")}`,`clusterSenderBindings: ${Object.keys(e.clusterSenderBindings??{}).length} entries`,"","<b>Telegram</b>",`telegramBotToken: ${Se($n(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>",`${Se(e.fileReceiveRootMode)} \xB7 inbox ${Se(e.fileInboxSubdir)}`,"","<b>Service (chat)</b>",`serviceInstallFromChat: ${e.serviceInstallFromChat}`,"","<b>Updates (optional)</b>",`updateCheckEnabled: ${e.updateCheckEnabled} \xB7 interval ms: ${e.updateCheckIntervalMs}`,`package: <code>${Se(e.updateCheckPackageName)}</code> \xB7 info URL: ${e.updateInfoUrl?"set":"empty"}`,"","<b>Tunneling (chat /tunnel)</b>",`tunnelEnabled: ${e.tunnelEnabled} \xB7 relay <code>${Se(e.tunnelRelayUrl)}</code> \xB7 max active: ${e.tunnelMaxActive}`,"","<b>Chat LLM fallback</b>",`enabled: ${e.chatLlmFallbackEnabled} \xB7 command: ${e.chatLlmShellCommand?"set":"empty"}`,`timeout ms: ${e.chatLlmTimeoutMs} \xB7 in/out chars: ${e.chatLlmMaxInputChars} / ${e.chatLlmMaxOutputChars}`,`needsTty: ${e.chatLlmNeedsTty} \xB7 workDir: ${Se(e.chatLlmWorkDir||"(empty)")}`,"","<b>Webhook receiver</b>",`enabled: ${e.webhookEnabled} \xB7 ${Se(e.webhookHost)}:${e.webhookPort} \xB7 token: ${Se($n(e.webhookToken))}`,"","<b>Watch (OS event eye)</b>",`enabled: ${e.watchEnabled} \xB7 debounce ms: ${e.watchDebounceMs} \xB7 max/min: ${e.watchMaxEventsPerMinute} \xB7 autoRestore: ${e.watchAutoRestore}`,"",`<code>${Se(_)}</code>`].join(`
78
- `)}function _c(e,t){if(!Nc(t))return null;let n=t;if(n==="telegramBotToken")return`telegramBotToken: ${$n(ve(e))}`;if(n==="webhookToken")return`webhookToken: ${$n(e.webhookToken)}`;let r=e[n];return`${t}: ${typeof r=="object"?JSON.stringify(r):String(r)}`}function Bf(e,t){let n=_c(e,t);if(!n)return null;let r=n.split(": ");return r.length<2?Se(n):`<b>${Se(r[0])}</b> ${Se(r.slice(1).join(": "))}`}function uo(e,t){let n=Oc(t),r=!1,o=!1,s=!1;if(e==="telegramBotToken"){if(!dt(n))throw new Error("Invalid bot token format (expect digits:secret from BotFather).");return Ot(n),r=!0,s=!0,{reloadSuggested:r,shellWarning:o,tokenSaved:s}}let i=$();switch(e){case"gatewayMode":{let a=gn(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=Je(n);if(a===null)throw new Error("clusterEnabled: true or false");i.clusterEnabled=a;break}case"clusterRole":{let a=n.trim().toLowerCase();if(a!=="primary"&&a!=="secondary")throw new Error("clusterRole: primary | secondary");i.clusterRole=a;break}case"clusterLabel":i.clusterLabel=n.trim().slice(0,128);break;case"clusterSenderBindings":{let a=n.trim();if(a===""||a==="{}"||a.toLowerCase()==="clear"){i.clusterSenderBindings={};break}let l;try{l=JSON.parse(a)}catch{throw new Error('clusterSenderBindings: pass a JSON object, e.g. {"wa:+15551234567":"workshop-box"}')}if(!l||typeof l!="object"||Array.isArray(l))throw new Error("clusterSenderBindings must be a JSON object of sender \u2192 label/id");let d={};for(let[u,c]of Object.entries(l)){if(typeof c!="string"||!c.trim())throw new Error(`clusterSenderBindings.${u}: value must be a non-empty string`);d[u]=c.trim()}i.clusterSenderBindings=d;break}case"commandPrefix":if(!n.trim())throw new Error("commandPrefix cannot be empty");i.commandPrefix=n.trim().slice(0,32);break;case"syncTimeoutMs":{let a=Number.parseInt(n,10);if(!Number.isFinite(a)||a<=0)throw new Error("syncTimeoutMs: positive integer (ms)");i.syncTimeoutMs=a;break}case"syncMaxBytes":{let a=Number.parseInt(n,10);if(!Number.isFinite(a)||a<=0)throw new Error("syncMaxBytes: positive integer");i.syncMaxBytes=a;break}case"jobLogTailLines":{let a=Number.parseInt(n,10);if(!Number.isFinite(a)||a<=0)throw new Error("jobLogTailLines: positive integer");i.jobLogTailLines=a;break}case"shell":if(!n.trim())throw new Error("shell: non-empty path");i.shell=n.trim().slice(0,4096),o=!0;break;case"recipesMacroDefaultCommand":if(!n.trim())throw new Error('recipesMacroDefaultCommand: non-empty shell snippet with "$OMNISH_TASK"');i.recipesMacroDefaultCommand=Oc(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=Je(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"appsSkipClearOnPasswordPrompt":{let a=Je(n);if(a===null)throw new Error("appsSkipClearOnPasswordPrompt: true or false");i.appsSkipClearOnPasswordPrompt=a;break}case"appsPasswordPromptHint":{let a=Je(n);if(a===null)throw new Error("appsPasswordPromptHint: true or false");i.appsPasswordPromptHint=a;break}case"fileInboxSubdir":i.fileInboxSubdir=n.trim().slice(0,80);break;case"fileReceiveRootMode":{let a=n.trim();if(!Lc.has(a))throw new Error(`fileReceiveRootMode: ${[...Lc].join(" | ")}`);i.fileReceiveRootMode=a;break}case"fileReceiveRootPath":i.fileReceiveRootPath=n.trim().slice(0,4096);break;case"recipesAllowDangerousBuiltins":{let a=Je(n);if(a===null)throw new Error("recipesAllowDangerousBuiltins: true or false");i.recipesAllowDangerousBuiltins=a;break}case"serviceInstallFromChat":{let a=Je(n);if(a===null)throw new Error("serviceInstallFromChat: true or false");i.serviceInstallFromChat=a;break}case"updateCheckEnabled":{let a=Je(n);if(a===null)throw new Error("updateCheckEnabled: true or false");i.updateCheckEnabled=a;break}case"updateCheckIntervalMs":{let a=Number.parseInt(n,10);if(!Number.isFinite(a)||a<36e5)throw new Error("updateCheckIntervalMs: integer ms, minimum 3600000 (1 hour)");i.updateCheckIntervalMs=Math.min(6048e5,a);break}case"updateCheckPackageName":if(!n.trim())throw new Error("updateCheckPackageName: non-empty npm package name");i.updateCheckPackageName=n.trim().slice(0,214);break;case"updateInfoUrl":i.updateInfoUrl=n.trim().slice(0,2048);break;case"tunnelEnabled":{let a=Je(n);if(a===null)throw new Error("tunnelEnabled: true or false");return W({tunnelEnabled:a}),{reloadSuggested:r,shellWarning:o,tokenSaved:s}}case"tunnelRelayUrl":{let a=n.trim();if(!a)throw new Error("tunnelRelayUrl: non-empty URL");let l;try{l=new URL(a)}catch{throw new Error("tunnelRelayUrl: invalid URL")}if(l.protocol!=="http:"&&l.protocol!=="https:")throw new Error("tunnelRelayUrl: use http:// or https://");return W({tunnelRelayUrl:a}),{reloadSuggested:r,shellWarning:o,tokenSaved:s}}case"tunnelMaxActive":{let a=Number.parseInt(n.trim(),10);if(!Number.isFinite(a)||a<1||a>50)throw new Error("tunnelMaxActive: integer 1\u201350");return W({tunnelMaxActive:a}),{reloadSuggested:r,shellWarning:o,tokenSaved:s}}case"chatLlmFallbackEnabled":{let a=Je(n);if(a===null)throw new Error("chatLlmFallbackEnabled: true or false");return W({chatLlmFallbackEnabled:a}),{reloadSuggested:r,shellWarning:o,tokenSaved:s}}case"chatLlmShellCommand":return W({chatLlmShellCommand:n}),{reloadSuggested:r,shellWarning:o,tokenSaved:s};case"chatLlmTimeoutMs":{let a=Number.parseInt(n.trim(),10);if(!Number.isFinite(a)||a<=0)throw new Error("chatLlmTimeoutMs: positive integer (ms)");return W({chatLlmTimeoutMs:a}),{reloadSuggested:r,shellWarning:o,tokenSaved:s}}case"chatLlmMaxInputChars":{let a=Number.parseInt(n.trim(),10);if(!Number.isFinite(a)||a<=0)throw new Error("chatLlmMaxInputChars: positive integer");return W({chatLlmMaxInputChars:a}),{reloadSuggested:r,shellWarning:o,tokenSaved:s}}case"chatLlmMaxOutputChars":{let a=Number.parseInt(n.trim(),10);if(!Number.isFinite(a)||a<=0)throw new Error("chatLlmMaxOutputChars: positive integer");return W({chatLlmMaxOutputChars:a}),{reloadSuggested:r,shellWarning:o,tokenSaved:s}}case"chatLlmNeedsTty":{let a=Je(n);if(a===null)throw new Error("chatLlmNeedsTty: true or false");return W({chatLlmNeedsTty:a}),{reloadSuggested:r,shellWarning:o,tokenSaved:s}}case"chatLlmWorkDir":return W({chatLlmWorkDir:n.trim()}),{reloadSuggested:r,shellWarning:o,tokenSaved:s};case"webhookEnabled":{let a=Je(n);if(a===null)throw new Error("webhookEnabled: true or false");return W({webhookEnabled:a}),r=!0,{reloadSuggested:r,shellWarning:o,tokenSaved:s}}case"webhookPort":{let a=Number.parseInt(n.trim(),10);if(!Number.isFinite(a)||a<0||a>65535)throw new Error("webhookPort: integer 0\u201365535 (0 = random free port)");return W({webhookPort:a}),r=!0,{reloadSuggested:r,shellWarning:o,tokenSaved:s}}case"webhookHost":if(!n.trim())throw new Error("webhookHost: non-empty bind address");return W({webhookHost:n.trim().slice(0,256)}),r=!0,{reloadSuggested:r,shellWarning:o,tokenSaved:s};case"webhookToken":return W({webhookToken:n.trim()}),r=!0,{reloadSuggested:r,shellWarning:o,tokenSaved:s};case"watchEnabled":{let a=Je(n);if(a===null)throw new Error("watchEnabled: true or false");return W({watchEnabled:a}),De(),{reloadSuggested:r,shellWarning:o,tokenSaved:s}}case"watchDebounceMs":{let a=Number.parseInt(n.trim(),10);if(!Number.isFinite(a)||a<500||a>6e4)throw new Error("watchDebounceMs: integer 500\u201360000");return W({watchDebounceMs:a}),De(),{reloadSuggested:r,shellWarning:o,tokenSaved:s}}case"watchMaxEventsPerMinute":{let a=Number.parseInt(n.trim(),10);if(!Number.isFinite(a)||a<1||a>120)throw new Error("watchMaxEventsPerMinute: integer 1\u2013120");return W({watchMaxEventsPerMinute:a}),De(),{reloadSuggested:r,shellWarning:o,tokenSaved:s}}case"watchAutoRestore":{let a=Je(n);if(a===null)throw new Error("watchAutoRestore: true or false");return W({watchAutoRestore:a}),De(),{reloadSuggested:r,shellWarning:o,tokenSaved:s}}}return _e(i),{reloadSuggested:r,shellWarning:o,tokenSaved:s}}async function Fc(e,t){let n=e.trim(),r=n.toLowerCase();if(!n||r==="help")return Z(rc());if(r==="keys"||r==="help keys")return p(["*Configurable keys* (/config set)","",Bt.join(", "),"","Examples:","/config set clusterLabel work-laptop",'/config set fileReceiveRootPath "/path/with spaces"',"/config set gatewayMode both","/config set tunnelEnabled true","/config set chatLlmFallbackEnabled false","/config set webhookEnabled true","/config set watchEnabled true"].join(`
79
- `));if(r==="show"){let i=$();return ue(Df(i),Hf(i))}let o=n.match(/^get\s+(\S+)\s*$/i);if(o){let i=o[1],a=$(),l=_c(a,i);if(!l)return p(`Unknown key "${i}". /config keys`);let d=Bf(a,i);return ue(`${l}
75
+ `).trimEnd())}function yi(e){return p(`Unknown recipe: ${e}
76
+ /run list`)}function wi(){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:"bullet",text:"/apps <session> publish \u2014 share session command to online catalog"},{kind:"bullet",text:"/apps online trending | show <publicId> | <publicId> download \u2014 browse app templates only"},{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 Jf(e){let{errors:t,warns:n,infos:r}=li(e),o=[{kind:"title",text:"Security check"}];if(e.length===0)return o.push({kind:"p",text:"No issues reported by automated rules."},{kind:"gap"},{kind:"p",text:"Allowlisted remote shell access is still equivalent to sharing credentials with those identities."}),o;o.push({kind:"p",text:`${t} error(s), ${n} warning(s), ${r} note(s).`}),o.push({kind:"gap"});let s=(i,a)=>{if(a.length!==0){o.push({kind:"sub",text:`${i} (${a.length})`});for(let l of a){let d=l.severity.toUpperCase();o.push({kind:"bullet",text:Me(`[${d}] ${l.code}: ${l.message}`)}),l.detail&&o.push({kind:"bullet",text:Me(l.detail)}),l.fixHint&&o.push({kind:"bullet",text:Me(`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 _c(e){return Z(Jf(e))}function Fc(){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 Wc(){return[{kind:"title",text:"/security commands"},{kind:"bullet",text:"/security \u2014 full report (same checks as omnish security)"},{kind:"bullet",text:"/security summary \u2014 one-line counts"},{kind:"bullet",text:"/security tips \u2014 short hardening checklist"},{kind:"bullet",text:"CLI: omnish security [--json] for scripts and monitoring"}]}function Se(e){return e.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;")}function En(e){let t=e.trim();return t?t.length<=8?"(set)":`${t.slice(0,4)}\u2026${t.slice(-4)}`:"(empty)"}function Uc(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 Je(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 Dc=new Set(["downloads","omnishData","sessionCwd","processCwd","fixed"]),Gt=["gatewayMode","clusterEnabled","clusterRole","clusterLabel","clusterSenderBindings","commandPrefix","syncTimeoutMs","syncMaxBytes","jobLogTailLines","shell","appsCols","appsRows","appsFlushMs","appsMinIntervalMs","appsMaxFlushBytes","appsMaxSessions","appsMaxSessionsTotal","appsMaxWaChars","appsLogTailLines","appsSubmitDelayMs","appsClearInput","appsClearInputDelayMs","appsClearInputSequence","appsSkipClearOnPasswordPrompt","appsPasswordPromptHint","fileSendMaxBytes","fileReceiveMaxBytes","fileInboxSubdir","fileReceiveRootMode","fileReceiveRootPath","recipesAllowDangerousBuiltins","recipesMaxTaskChars","recipesMacroDefaultCommand","telegramBotToken","serviceInstallFromChat","updateCheckEnabled","updateCheckIntervalMs","updateCheckPackageName","updateInfoUrl","chatLlmFallbackEnabled","chatLlmShellCommand","chatLlmTimeoutMs","chatLlmMaxInputChars","chatLlmMaxOutputChars","chatLlmNeedsTty","chatLlmWorkDir","tunnelEnabled","tunnelRelayUrl","tunnelMaxActive","webhookEnabled","webhookPort","webhookHost","webhookToken","watchEnabled","watchDebounceMs","watchMaxEventsPerMinute","watchAutoRestore"];function Hc(e){return Gt.includes(e)}function Kf(e){let t=ve(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: ${En(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}`,`appsSkipClearOnPasswordPrompt: ${e.appsSkipClearOnPasswordPrompt}`,`appsPasswordPromptHint: ${e.appsPasswordPromptHint}`,"","*Files*",`fileSendMaxBytes / fileReceiveMaxBytes: ${e.fileSendMaxBytes} / ${e.fileReceiveMaxBytes}`,`fileInboxSubdir: ${e.fileInboxSubdir}`,`fileReceiveRootMode: ${e.fileReceiveRootMode}`,`fileReceiveRootPath: ${e.fileReceiveRootPath||"(empty)"}`,"","*Recipes*",`recipesAllowDangerousBuiltins: ${e.recipesAllowDangerousBuiltins}`,`recipesMaxTaskChars: ${e.recipesMaxTaskChars}`,`recipesMacroDefaultCommand: ${e.recipesMacroDefaultCommand}`,"","*Service (chat)*",`serviceInstallFromChat: ${e.serviceInstallFromChat}`,"","*Updates (optional)*",`updateCheckEnabled: ${e.updateCheckEnabled}`,`updateCheckIntervalMs: ${e.updateCheckIntervalMs}`,`updateCheckPackageName: ${e.updateCheckPackageName}`,`updateInfoUrl: ${e.updateInfoUrl?"(set)":"(empty)"}`,"","*Tunneling (chat /tunnel)*",`tunnelEnabled: ${e.tunnelEnabled}`,`tunnelRelayUrl: ${e.tunnelRelayUrl}`,`tunnelMaxActive: ${e.tunnelMaxActive}`,"","*Chat LLM fallback (optional)*",`chatLlmFallbackEnabled: ${e.chatLlmFallbackEnabled}`,`chatLlmShellCommand: ${e.chatLlmShellCommand?"(set)":"(empty)"}`,`chatLlmTimeoutMs: ${e.chatLlmTimeoutMs}`,`chatLlmMaxInputChars / chatLlmMaxOutputChars: ${e.chatLlmMaxInputChars} / ${e.chatLlmMaxOutputChars}`,`chatLlmNeedsTty: ${e.chatLlmNeedsTty}`,`chatLlmWorkDir: ${e.chatLlmWorkDir||"(empty \u2014 temp dir per run)"}`,"","*Webhook receiver (optional)*",`webhookEnabled: ${e.webhookEnabled}`,`webhookPort: ${e.webhookPort} (0 = random)`,`webhookHost: ${e.webhookHost}`,`webhookToken: ${En(e.webhookToken)}`,"","*Watch (OS event eye)*",`watchEnabled: ${e.watchEnabled}`,`watchDebounceMs: ${e.watchDebounceMs}`,`watchMaxEventsPerMinute: ${e.watchMaxEventsPerMinute}`,`watchAutoRestore: ${e.watchAutoRestore}`,"",`File: ${_}`].join(`
77
+ `)}function zf(e){let t=ve(e);return["<b>Config</b> (secrets masked)","",`<b>gatewayMode</b> ${Se(e.gatewayMode)}`,`<b>commandPrefix</b> ${Se(e.commandPrefix)}`,`<b>shell</b> ${Se(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: ${Se(e.clusterRole)}`,`clusterLabel: ${Se(e.clusterLabel||"(hostname)")}`,`clusterSenderBindings: ${Object.keys(e.clusterSenderBindings??{}).length} entries`,"","<b>Telegram</b>",`telegramBotToken: ${Se(En(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>",`${Se(e.fileReceiveRootMode)} \xB7 inbox ${Se(e.fileInboxSubdir)}`,"","<b>Service (chat)</b>",`serviceInstallFromChat: ${e.serviceInstallFromChat}`,"","<b>Updates (optional)</b>",`updateCheckEnabled: ${e.updateCheckEnabled} \xB7 interval ms: ${e.updateCheckIntervalMs}`,`package: <code>${Se(e.updateCheckPackageName)}</code> \xB7 info URL: ${e.updateInfoUrl?"set":"empty"}`,"","<b>Tunneling (chat /tunnel)</b>",`tunnelEnabled: ${e.tunnelEnabled} \xB7 relay <code>${Se(e.tunnelRelayUrl)}</code> \xB7 max active: ${e.tunnelMaxActive}`,"","<b>Chat LLM fallback</b>",`enabled: ${e.chatLlmFallbackEnabled} \xB7 command: ${e.chatLlmShellCommand?"set":"empty"}`,`timeout ms: ${e.chatLlmTimeoutMs} \xB7 in/out chars: ${e.chatLlmMaxInputChars} / ${e.chatLlmMaxOutputChars}`,`needsTty: ${e.chatLlmNeedsTty} \xB7 workDir: ${Se(e.chatLlmWorkDir||"(empty)")}`,"","<b>Webhook receiver</b>",`enabled: ${e.webhookEnabled} \xB7 ${Se(e.webhookHost)}:${e.webhookPort} \xB7 token: ${Se(En(e.webhookToken))}`,"","<b>Watch (OS event eye)</b>",`enabled: ${e.watchEnabled} \xB7 debounce ms: ${e.watchDebounceMs} \xB7 max/min: ${e.watchMaxEventsPerMinute} \xB7 autoRestore: ${e.watchAutoRestore}`,"",`<code>${Se(_)}</code>`].join(`
78
+ `)}function Bc(e,t){if(!Hc(t))return null;let n=t;if(n==="telegramBotToken")return`telegramBotToken: ${En(ve(e))}`;if(n==="webhookToken")return`webhookToken: ${En(e.webhookToken)}`;let r=e[n];return`${t}: ${typeof r=="object"?JSON.stringify(r):String(r)}`}function qf(e,t){let n=Bc(e,t);if(!n)return null;let r=n.split(": ");return r.length<2?Se(n):`<b>${Se(r[0])}</b> ${Se(r.slice(1).join(": "))}`}function ho(e,t){let n=Uc(t),r=!1,o=!1,s=!1;if(e==="telegramBotToken"){if(!pt(n))throw new Error("Invalid bot token format (expect digits:secret from BotFather).");return Nt(n),r=!0,s=!0,{reloadSuggested:r,shellWarning:o,tokenSaved:s}}let i=$();switch(e){case"gatewayMode":{let a=Sn(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=Je(n);if(a===null)throw new Error("clusterEnabled: true or false");i.clusterEnabled=a;break}case"clusterRole":{let a=n.trim().toLowerCase();if(a!=="primary"&&a!=="secondary")throw new Error("clusterRole: primary | secondary");i.clusterRole=a;break}case"clusterLabel":i.clusterLabel=n.trim().slice(0,128);break;case"clusterSenderBindings":{let a=n.trim();if(a===""||a==="{}"||a.toLowerCase()==="clear"){i.clusterSenderBindings={};break}let l;try{l=JSON.parse(a)}catch{throw new Error('clusterSenderBindings: pass a JSON object, e.g. {"wa:+15551234567":"workshop-box"}')}if(!l||typeof l!="object"||Array.isArray(l))throw new Error("clusterSenderBindings must be a JSON object of sender \u2192 label/id");let d={};for(let[u,c]of Object.entries(l)){if(typeof c!="string"||!c.trim())throw new Error(`clusterSenderBindings.${u}: value must be a non-empty string`);d[u]=c.trim()}i.clusterSenderBindings=d;break}case"commandPrefix":if(!n.trim())throw new Error("commandPrefix cannot be empty");i.commandPrefix=n.trim().slice(0,32);break;case"syncTimeoutMs":{let a=Number.parseInt(n,10);if(!Number.isFinite(a)||a<=0)throw new Error("syncTimeoutMs: positive integer (ms)");i.syncTimeoutMs=a;break}case"syncMaxBytes":{let a=Number.parseInt(n,10);if(!Number.isFinite(a)||a<=0)throw new Error("syncMaxBytes: positive integer");i.syncMaxBytes=a;break}case"jobLogTailLines":{let a=Number.parseInt(n,10);if(!Number.isFinite(a)||a<=0)throw new Error("jobLogTailLines: positive integer");i.jobLogTailLines=a;break}case"shell":if(!n.trim())throw new Error("shell: non-empty path");i.shell=n.trim().slice(0,4096),o=!0;break;case"recipesMacroDefaultCommand":if(!n.trim())throw new Error('recipesMacroDefaultCommand: non-empty shell snippet with "$OMNISH_TASK"');i.recipesMacroDefaultCommand=Uc(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=Je(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"appsSkipClearOnPasswordPrompt":{let a=Je(n);if(a===null)throw new Error("appsSkipClearOnPasswordPrompt: true or false");i.appsSkipClearOnPasswordPrompt=a;break}case"appsPasswordPromptHint":{let a=Je(n);if(a===null)throw new Error("appsPasswordPromptHint: true or false");i.appsPasswordPromptHint=a;break}case"fileInboxSubdir":i.fileInboxSubdir=n.trim().slice(0,80);break;case"fileReceiveRootMode":{let a=n.trim();if(!Dc.has(a))throw new Error(`fileReceiveRootMode: ${[...Dc].join(" | ")}`);i.fileReceiveRootMode=a;break}case"fileReceiveRootPath":i.fileReceiveRootPath=n.trim().slice(0,4096);break;case"recipesAllowDangerousBuiltins":{let a=Je(n);if(a===null)throw new Error("recipesAllowDangerousBuiltins: true or false");i.recipesAllowDangerousBuiltins=a;break}case"serviceInstallFromChat":{let a=Je(n);if(a===null)throw new Error("serviceInstallFromChat: true or false");i.serviceInstallFromChat=a;break}case"updateCheckEnabled":{let a=Je(n);if(a===null)throw new Error("updateCheckEnabled: true or false");i.updateCheckEnabled=a;break}case"updateCheckIntervalMs":{let a=Number.parseInt(n,10);if(!Number.isFinite(a)||a<36e5)throw new Error("updateCheckIntervalMs: integer ms, minimum 3600000 (1 hour)");i.updateCheckIntervalMs=Math.min(6048e5,a);break}case"updateCheckPackageName":if(!n.trim())throw new Error("updateCheckPackageName: non-empty npm package name");i.updateCheckPackageName=n.trim().slice(0,214);break;case"updateInfoUrl":i.updateInfoUrl=n.trim().slice(0,2048);break;case"tunnelEnabled":{let a=Je(n);if(a===null)throw new Error("tunnelEnabled: true or false");return W({tunnelEnabled:a}),{reloadSuggested:r,shellWarning:o,tokenSaved:s}}case"tunnelRelayUrl":{let a=n.trim();if(!a)throw new Error("tunnelRelayUrl: non-empty URL");let l;try{l=new URL(a)}catch{throw new Error("tunnelRelayUrl: invalid URL")}if(l.protocol!=="http:"&&l.protocol!=="https:")throw new Error("tunnelRelayUrl: use http:// or https://");return W({tunnelRelayUrl:a}),{reloadSuggested:r,shellWarning:o,tokenSaved:s}}case"tunnelMaxActive":{let a=Number.parseInt(n.trim(),10);if(!Number.isFinite(a)||a<1||a>50)throw new Error("tunnelMaxActive: integer 1\u201350");return W({tunnelMaxActive:a}),{reloadSuggested:r,shellWarning:o,tokenSaved:s}}case"chatLlmFallbackEnabled":{let a=Je(n);if(a===null)throw new Error("chatLlmFallbackEnabled: true or false");return W({chatLlmFallbackEnabled:a}),{reloadSuggested:r,shellWarning:o,tokenSaved:s}}case"chatLlmShellCommand":return W({chatLlmShellCommand:n}),{reloadSuggested:r,shellWarning:o,tokenSaved:s};case"chatLlmTimeoutMs":{let a=Number.parseInt(n.trim(),10);if(!Number.isFinite(a)||a<=0)throw new Error("chatLlmTimeoutMs: positive integer (ms)");return W({chatLlmTimeoutMs:a}),{reloadSuggested:r,shellWarning:o,tokenSaved:s}}case"chatLlmMaxInputChars":{let a=Number.parseInt(n.trim(),10);if(!Number.isFinite(a)||a<=0)throw new Error("chatLlmMaxInputChars: positive integer");return W({chatLlmMaxInputChars:a}),{reloadSuggested:r,shellWarning:o,tokenSaved:s}}case"chatLlmMaxOutputChars":{let a=Number.parseInt(n.trim(),10);if(!Number.isFinite(a)||a<=0)throw new Error("chatLlmMaxOutputChars: positive integer");return W({chatLlmMaxOutputChars:a}),{reloadSuggested:r,shellWarning:o,tokenSaved:s}}case"chatLlmNeedsTty":{let a=Je(n);if(a===null)throw new Error("chatLlmNeedsTty: true or false");return W({chatLlmNeedsTty:a}),{reloadSuggested:r,shellWarning:o,tokenSaved:s}}case"chatLlmWorkDir":return W({chatLlmWorkDir:n.trim()}),{reloadSuggested:r,shellWarning:o,tokenSaved:s};case"webhookEnabled":{let a=Je(n);if(a===null)throw new Error("webhookEnabled: true or false");return W({webhookEnabled:a}),r=!0,{reloadSuggested:r,shellWarning:o,tokenSaved:s}}case"webhookPort":{let a=Number.parseInt(n.trim(),10);if(!Number.isFinite(a)||a<0||a>65535)throw new Error("webhookPort: integer 0\u201365535 (0 = random free port)");return W({webhookPort:a}),r=!0,{reloadSuggested:r,shellWarning:o,tokenSaved:s}}case"webhookHost":if(!n.trim())throw new Error("webhookHost: non-empty bind address");return W({webhookHost:n.trim().slice(0,256)}),r=!0,{reloadSuggested:r,shellWarning:o,tokenSaved:s};case"webhookToken":return W({webhookToken:n.trim()}),r=!0,{reloadSuggested:r,shellWarning:o,tokenSaved:s};case"watchEnabled":{let a=Je(n);if(a===null)throw new Error("watchEnabled: true or false");return W({watchEnabled:a}),De(),{reloadSuggested:r,shellWarning:o,tokenSaved:s}}case"watchDebounceMs":{let a=Number.parseInt(n.trim(),10);if(!Number.isFinite(a)||a<500||a>6e4)throw new Error("watchDebounceMs: integer 500\u201360000");return W({watchDebounceMs:a}),De(),{reloadSuggested:r,shellWarning:o,tokenSaved:s}}case"watchMaxEventsPerMinute":{let a=Number.parseInt(n.trim(),10);if(!Number.isFinite(a)||a<1||a>120)throw new Error("watchMaxEventsPerMinute: integer 1\u2013120");return W({watchMaxEventsPerMinute:a}),De(),{reloadSuggested:r,shellWarning:o,tokenSaved:s}}case"watchAutoRestore":{let a=Je(n);if(a===null)throw new Error("watchAutoRestore: true or false");return W({watchAutoRestore:a}),De(),{reloadSuggested:r,shellWarning:o,tokenSaved:s}}}return _e(i),{reloadSuggested:r,shellWarning:o,tokenSaved:s}}async function jc(e,t){let n=e.trim(),r=n.toLowerCase();if(!n||r==="help")return Z(cc());if(r==="keys"||r==="help keys")return p(["*Configurable keys* (/config set)","",Gt.join(", "),"","Examples:","/config set clusterLabel work-laptop",'/config set fileReceiveRootPath "/path/with spaces"',"/config set gatewayMode both","/config set tunnelEnabled true","/config set chatLlmFallbackEnabled false","/config set webhookEnabled true","/config set watchEnabled true"].join(`
79
+ `));if(r==="show"){let i=$();return de(Kf(i),zf(i))}let o=n.match(/^get\s+(\S+)\s*$/i);if(o){let i=o[1],a=$(),l=Bc(a,i);if(!l)return p(`Unknown key "${i}". /config keys`);let d=qf(a,i);return de(`${l}
80
80
 
81
- ${_}`,`${d}<br/><br/><code>${Se(_)}</code>`)}let s=n.match(/^set\s+(\S+)\s+([\s\S]+)$/i);if(s){let i=s[1],a=s[2]??"";if(!Nc(i))return p(`Unknown key "${i}". /config keys`);try{let{reloadSuggested:l,shellWarning:d,tokenSaved:u}=uo(i,a),c="";if(t?.reload&&l){let y=await t.reload();c=y.ok?`
81
+ ${_}`,`${d}<br/><br/><code>${Se(_)}</code>`)}let s=n.match(/^set\s+(\S+)\s+([\s\S]+)$/i);if(s){let i=s[1],a=s[2]??"";if(!Hc(i))return p(`Unknown key "${i}". /config keys`);try{let{reloadSuggested:l,shellWarning:d,tokenSaved:u}=ho(i,a),c="";if(t?.reload&&l){let y=await t.reload();c=y.ok?`
82
82
  Reload: ${y.summary}`:`
83
83
  Reload failed: ${y.error}`}else l?c=`
84
84
  Send /reload while omnish run is active (gatewayMode / Telegram).`:c=`
85
85
  Send /reload to pick up changes where applicable.`;let m=d?`
86
86
  \u26A0 shell changed \u2014 affects all commands run via omnish.`:"",f=u?`
87
- telegramBotToken saved (not echoed).`:"",h=`Set *${i}* (saved).${f}${m}${c}`,g=`<b>Set ${Se(i)}</b> (saved).${f?"<br/>token saved (not echoed).":""}${d?"<br/>\u26A0 shell path changed.":""}${Se(c)}`;return ue(h,g)}catch(l){return p(`Error: ${String(l)}`)}}return p("Unknown /config command. Try /config help or /config show")}pe();var gi=[...Bt,"platformToken","platformDeviceId"],yi={platform_url:"tunnelRelayUrl",tunnel_relay_url:"tunnelRelayUrl",platform_token:"platformToken",token:"platformToken",omnish_token:"platformToken",platform_device_id:"platformDeviceId",device_id:"platformDeviceId"};for(let e of gi)yi[e]=e;function po(e){let t=e.trim().toLowerCase().replace(/-/g,"_");return yi[t]??null}function Wc(){return Object.keys(yi).sort()}function Ke(e){let t=e.trim().toLowerCase();return t==="true"||t==="1"||t==="yes"||t==="on"?!0:t==="false"||t==="0"||t==="no"||t==="off"?!1:null}function jf(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 Gf(e){return N[e]}function Rn(e,t){let n=jf(t),r=$();switch(e){case"platformToken":return W({platformToken:n});case"platformDeviceId":return W({platformDeviceId:n});case"gatewayMode":{let o=gn(n);if(!o)throw new Error('gatewayMode: "whatsapp", "telegram", or "both"');return r.gatewayMode=o,_e(r),r}case"telegramBotToken":if(!dt(n))throw new Error("telegramBotToken: invalid bot token format");return Ot(n);case"tunnelRelayUrl":{let o=n.trim();if(!o)throw new Error("tunnelRelayUrl: non-empty URL");let s;try{s=new URL(o)}catch{throw new Error("tunnelRelayUrl: invalid URL")}if(s.protocol!=="http:"&&s.protocol!=="https:")throw new Error("tunnelRelayUrl: use http:// or https://");return W({tunnelRelayUrl:o})}case"tunnelEnabled":{let o=Ke(n);if(o===null)throw new Error("tunnelEnabled: true or false");return W({tunnelEnabled:o})}case"tunnelMaxActive":{let o=Number.parseInt(n,10);if(!Number.isFinite(o)||o<1||o>50)throw new Error("tunnelMaxActive: integer 1\u201350");return W({tunnelMaxActive:o})}case"clusterSenderBindings":{let o;try{o=JSON.parse(n)}catch{throw new Error("clusterSenderBindings: valid JSON object")}if(!o||typeof o!="object"||Array.isArray(o))throw new Error("clusterSenderBindings: JSON object required");return W({clusterSenderBindings:o})}default:break}if(e==="clusterEnabled"){let o=Ke(n);if(o===null)throw new Error("clusterEnabled: true or false");r.clusterEnabled=o}else if(e==="clusterRole"){if(n!=="primary"&&n!=="secondary")throw new Error('clusterRole: "primary" or "secondary"');r.clusterRole=n}else if(e==="clusterLabel")r.clusterLabel=n.trim().slice(0,64);else if(e==="commandPrefix"){if(!n)throw new Error("commandPrefix: non-empty");r.commandPrefix=n}else if(e==="shell"){if(!n)throw new Error("shell: non-empty path");r.shell=n}else if(e==="syncTimeoutMs"){let o=Number.parseInt(n,10);if(!Number.isFinite(o)||o<=0)throw new Error("syncTimeoutMs: positive integer");r.syncTimeoutMs=o}else if(e==="syncMaxBytes"){let o=Number.parseInt(n,10);if(!Number.isFinite(o)||o<=0)throw new Error("syncMaxBytes: positive integer");r.syncMaxBytes=o}else if(e==="jobLogTailLines"){let o=Number.parseInt(n,10);if(!Number.isFinite(o)||o<=0)throw new Error("jobLogTailLines: positive integer");r.jobLogTailLines=o}else if(e==="appsCols"||e==="appsRows"){let o=Number.parseInt(n,10);if(!Number.isFinite(o)||o<20)throw new Error(`${e}: integer >= 20`);r[e]=o}else if(e==="appsFlushMs"||e==="appsMinIntervalMs"||e==="appsMaxFlushBytes"||e==="appsMaxWaChars"||e==="appsLogTailLines"||e==="appsSubmitDelayMs"||e==="appsClearInputDelayMs"){let o=Number.parseInt(n,10);if(!Number.isFinite(o)||o<0)throw new Error(`${e}: non-negative integer`);r[e]=o}else if(e==="appsMaxSessions"||e==="appsMaxSessionsTotal"){let o=Number.parseInt(n,10);if(!Number.isFinite(o)||o<1)throw new Error(`${e}: positive integer`);r[e]=o}else if(e==="appsClearInput"){let o=Ke(n);if(o===null)throw new Error("appsClearInput: true or false");r.appsClearInput=o}else if(e==="appsClearInputSequence")r.appsClearInputSequence=n;else if(e==="appsSkipClearOnPasswordPrompt"){let o=Ke(n);if(o===null)throw new Error("appsSkipClearOnPasswordPrompt: true or false");r.appsSkipClearOnPasswordPrompt=o}else if(e==="appsPasswordPromptHint"){let o=Ke(n);if(o===null)throw new Error("appsPasswordPromptHint: true or false");r.appsPasswordPromptHint=o}else if(e==="fileSendMaxBytes"||e==="fileReceiveMaxBytes"){let o=Number.parseInt(n,10);if(!Number.isFinite(o)||o<0)throw new Error(`${e}: non-negative integer`);r[e]=o}else if(e==="fileInboxSubdir")r.fileInboxSubdir=n.trim().slice(0,128);else if(e==="fileReceiveRootMode"){if(!new Set(["downloads","omnishData","sessionCwd","processCwd","fixed"]).has(n))throw new Error("fileReceiveRootMode: downloads|omnishData|sessionCwd|processCwd|fixed");r.fileReceiveRootMode=n}else if(e==="fileReceiveRootPath")r.fileReceiveRootPath=n.trim().slice(0,4096);else if(e==="recipesAllowDangerousBuiltins"){let o=Ke(n);if(o===null)throw new Error("recipesAllowDangerousBuiltins: true or false");r.recipesAllowDangerousBuiltins=o}else if(e==="recipesMaxTaskChars"){let o=Number.parseInt(n,10);if(!Number.isFinite(o)||o<0)throw new Error("recipesMaxTaskChars: non-negative integer");r.recipesMaxTaskChars=o}else if(e==="recipesMacroDefaultCommand"){if(!n.includes("$OMNISH_TASK"))throw new Error('recipesMacroDefaultCommand: must include "$OMNISH_TASK"');r.recipesMacroDefaultCommand=n}else if(e==="serviceInstallFromChat"){let o=Ke(n);if(o===null)throw new Error("serviceInstallFromChat: true or false");r.serviceInstallFromChat=o}else if(e==="updateCheckEnabled"){let o=Ke(n);if(o===null)throw new Error("updateCheckEnabled: true or false");r.updateCheckEnabled=o}else if(e==="updateCheckIntervalMs"){let o=Number.parseInt(n,10);if(!Number.isFinite(o)||o<36e5)throw new Error("updateCheckIntervalMs: min 3600000");r.updateCheckIntervalMs=Math.min(6048e5,o)}else if(e==="updateCheckPackageName"){if(!n.trim())throw new Error("updateCheckPackageName: non-empty");r.updateCheckPackageName=n.trim().slice(0,214)}else if(e==="updateInfoUrl")r.updateInfoUrl=n.trim().slice(0,2048);else if(e==="chatLlmFallbackEnabled"){let o=Ke(n);if(o===null)throw new Error("chatLlmFallbackEnabled: true or false");return W({chatLlmFallbackEnabled:o})}else{if(e==="chatLlmShellCommand")return W({chatLlmShellCommand:n});if(e==="chatLlmTimeoutMs"){let o=Number.parseInt(n,10);if(!Number.isFinite(o)||o<=0)throw new Error("chatLlmTimeoutMs: positive integer");return W({chatLlmTimeoutMs:o})}else if(e==="chatLlmMaxInputChars"){let o=Number.parseInt(n,10);if(!Number.isFinite(o)||o<=0)throw new Error("chatLlmMaxInputChars: positive integer");return W({chatLlmMaxInputChars:o})}else if(e==="chatLlmMaxOutputChars"){let o=Number.parseInt(n,10);if(!Number.isFinite(o)||o<=0)throw new Error("chatLlmMaxOutputChars: positive integer");return W({chatLlmMaxOutputChars:o})}else if(e==="chatLlmNeedsTty"){let o=Ke(n);if(o===null)throw new Error("chatLlmNeedsTty: true or false");return W({chatLlmNeedsTty:o})}else{if(e==="chatLlmWorkDir")return W({chatLlmWorkDir:n.trim()});if(e==="webhookEnabled"){let o=Ke(n);if(o===null)throw new Error("webhookEnabled: true or false");return W({webhookEnabled:o})}else if(e==="webhookPort"){let o=Number.parseInt(n,10);if(!Number.isFinite(o)||o<0||o>65535)throw new Error("webhookPort: integer 0\u201365535");return W({webhookPort:o})}else if(e==="webhookHost"){if(!n.trim())throw new Error("webhookHost: non-empty");return W({webhookHost:n.trim().slice(0,256)})}else{if(e==="webhookToken")return W({webhookToken:n.trim()});if(e==="watchEnabled"){let o=Ke(n);if(o===null)throw new Error("watchEnabled: true or false");return W({watchEnabled:o})}else if(e==="watchDebounceMs"){let o=Number.parseInt(n,10);if(!Number.isFinite(o)||o<500||o>6e4)throw new Error("watchDebounceMs: integer 500\u201360000");return W({watchDebounceMs:o})}else if(e==="watchMaxEventsPerMinute"){let o=Number.parseInt(n,10);if(!Number.isFinite(o)||o<1||o>120)throw new Error("watchMaxEventsPerMinute: integer 1\u2013120");return W({watchMaxEventsPerMinute:o})}else if(e==="watchAutoRestore"){let o=Ke(n);if(o===null)throw new Error("watchAutoRestore: true or false");return W({watchAutoRestore:o})}else throw new Error(`Unsupported key: ${e}`)}}}return _e(r),r}function Uc(e){let t=Gf(e);if(e==="platformToken"||e==="platformDeviceId")return W({[e]:t});if(e==="tunnelRelayUrl")return W({tunnelRelayUrl:t});let n=$();return n[e]=t,_e(n),n}function tn(e,t){if(e==="platformToken"||e==="telegramBotToken"||e==="webhookToken"){let n=t.trim();return n?n.length<=8?"(set)":`${n.slice(0,4)}\u2026${n.slice(-4)}`:"(empty)"}return t}pe();G();import Cn from"node:fs";import Si from"node:os";import pr from"node:path";import jc from"node:crypto";var bi="\u2063omnish/c v1",Jf=/([nlra])=([^\s\]]*)/g;function ot(e){return e.replace(/-/g,"").slice(0,8)}function wi(e){return e.replace(/[\s\]\r\n]/g,"_").slice(0,64)}function Kf(e){let t=wi(ot(e.nodeId)),n=wi(e.label||""),r=e.role==="primary"?"p":"s",o=wi(e.activeNodeId?ot(e.activeNodeId):"");return`${bi} [n=${t} l=${n} r=${r} a=${o}]`}function Dc(e,t){let n=Kf(t);if(e.includes(n))return e;let r=e.replace(/\s+$/,"");return r.length===0?n:`${r}
87
+ telegramBotToken saved (not echoed).`:"",h=`Set *${i}* (saved).${f}${m}${c}`,g=`<b>Set ${Se(i)}</b> (saved).${f?"<br/>token saved (not echoed).":""}${d?"<br/>\u26A0 shell path changed.":""}${Se(c)}`;return de(h,g)}catch(l){return p(`Error: ${String(l)}`)}}return p("Unknown /config command. Try /config help or /config show")}me();var bi=[...Gt,"platformToken","platformDeviceId"],ki={platform_url:"tunnelRelayUrl",tunnel_relay_url:"tunnelRelayUrl",platform_token:"platformToken",token:"platformToken",omnish_token:"platformToken",platform_device_id:"platformDeviceId",device_id:"platformDeviceId"};for(let e of bi)ki[e]=e;function go(e){let t=e.trim().toLowerCase().replace(/-/g,"_");return ki[t]??null}function Gc(){return Object.keys(ki).sort()}function Ke(e){let t=e.trim().toLowerCase();return t==="true"||t==="1"||t==="yes"||t==="on"?!0:t==="false"||t==="0"||t==="no"||t==="off"?!1:null}function Yf(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 Qf(e){return N[e]}function Pn(e,t){let n=Yf(t),r=$();switch(e){case"platformToken":return W({platformToken:n});case"platformDeviceId":return W({platformDeviceId:n});case"gatewayMode":{let o=Sn(n);if(!o)throw new Error('gatewayMode: "whatsapp", "telegram", or "both"');return r.gatewayMode=o,_e(r),r}case"telegramBotToken":if(!pt(n))throw new Error("telegramBotToken: invalid bot token format");return Nt(n);case"tunnelRelayUrl":{let o=n.trim();if(!o)throw new Error("tunnelRelayUrl: non-empty URL");let s;try{s=new URL(o)}catch{throw new Error("tunnelRelayUrl: invalid URL")}if(s.protocol!=="http:"&&s.protocol!=="https:")throw new Error("tunnelRelayUrl: use http:// or https://");return W({tunnelRelayUrl:o})}case"tunnelEnabled":{let o=Ke(n);if(o===null)throw new Error("tunnelEnabled: true or false");return W({tunnelEnabled:o})}case"tunnelMaxActive":{let o=Number.parseInt(n,10);if(!Number.isFinite(o)||o<1||o>50)throw new Error("tunnelMaxActive: integer 1\u201350");return W({tunnelMaxActive:o})}case"clusterSenderBindings":{let o;try{o=JSON.parse(n)}catch{throw new Error("clusterSenderBindings: valid JSON object")}if(!o||typeof o!="object"||Array.isArray(o))throw new Error("clusterSenderBindings: JSON object required");return W({clusterSenderBindings:o})}default:break}if(e==="clusterEnabled"){let o=Ke(n);if(o===null)throw new Error("clusterEnabled: true or false");r.clusterEnabled=o}else if(e==="clusterRole"){if(n!=="primary"&&n!=="secondary")throw new Error('clusterRole: "primary" or "secondary"');r.clusterRole=n}else if(e==="clusterLabel")r.clusterLabel=n.trim().slice(0,64);else if(e==="commandPrefix"){if(!n)throw new Error("commandPrefix: non-empty");r.commandPrefix=n}else if(e==="shell"){if(!n)throw new Error("shell: non-empty path");r.shell=n}else if(e==="syncTimeoutMs"){let o=Number.parseInt(n,10);if(!Number.isFinite(o)||o<=0)throw new Error("syncTimeoutMs: positive integer");r.syncTimeoutMs=o}else if(e==="syncMaxBytes"){let o=Number.parseInt(n,10);if(!Number.isFinite(o)||o<=0)throw new Error("syncMaxBytes: positive integer");r.syncMaxBytes=o}else if(e==="jobLogTailLines"){let o=Number.parseInt(n,10);if(!Number.isFinite(o)||o<=0)throw new Error("jobLogTailLines: positive integer");r.jobLogTailLines=o}else if(e==="appsCols"||e==="appsRows"){let o=Number.parseInt(n,10);if(!Number.isFinite(o)||o<20)throw new Error(`${e}: integer >= 20`);r[e]=o}else if(e==="appsFlushMs"||e==="appsMinIntervalMs"||e==="appsMaxFlushBytes"||e==="appsMaxWaChars"||e==="appsLogTailLines"||e==="appsSubmitDelayMs"||e==="appsClearInputDelayMs"){let o=Number.parseInt(n,10);if(!Number.isFinite(o)||o<0)throw new Error(`${e}: non-negative integer`);r[e]=o}else if(e==="appsMaxSessions"||e==="appsMaxSessionsTotal"){let o=Number.parseInt(n,10);if(!Number.isFinite(o)||o<1)throw new Error(`${e}: positive integer`);r[e]=o}else if(e==="appsClearInput"){let o=Ke(n);if(o===null)throw new Error("appsClearInput: true or false");r.appsClearInput=o}else if(e==="appsClearInputSequence")r.appsClearInputSequence=n;else if(e==="appsSkipClearOnPasswordPrompt"){let o=Ke(n);if(o===null)throw new Error("appsSkipClearOnPasswordPrompt: true or false");r.appsSkipClearOnPasswordPrompt=o}else if(e==="appsPasswordPromptHint"){let o=Ke(n);if(o===null)throw new Error("appsPasswordPromptHint: true or false");r.appsPasswordPromptHint=o}else if(e==="fileSendMaxBytes"||e==="fileReceiveMaxBytes"){let o=Number.parseInt(n,10);if(!Number.isFinite(o)||o<0)throw new Error(`${e}: non-negative integer`);r[e]=o}else if(e==="fileInboxSubdir")r.fileInboxSubdir=n.trim().slice(0,128);else if(e==="fileReceiveRootMode"){if(!new Set(["downloads","omnishData","sessionCwd","processCwd","fixed"]).has(n))throw new Error("fileReceiveRootMode: downloads|omnishData|sessionCwd|processCwd|fixed");r.fileReceiveRootMode=n}else if(e==="fileReceiveRootPath")r.fileReceiveRootPath=n.trim().slice(0,4096);else if(e==="recipesAllowDangerousBuiltins"){let o=Ke(n);if(o===null)throw new Error("recipesAllowDangerousBuiltins: true or false");r.recipesAllowDangerousBuiltins=o}else if(e==="recipesMaxTaskChars"){let o=Number.parseInt(n,10);if(!Number.isFinite(o)||o<0)throw new Error("recipesMaxTaskChars: non-negative integer");r.recipesMaxTaskChars=o}else if(e==="recipesMacroDefaultCommand"){if(!n.includes("$OMNISH_TASK"))throw new Error('recipesMacroDefaultCommand: must include "$OMNISH_TASK"');r.recipesMacroDefaultCommand=n}else if(e==="serviceInstallFromChat"){let o=Ke(n);if(o===null)throw new Error("serviceInstallFromChat: true or false");r.serviceInstallFromChat=o}else if(e==="updateCheckEnabled"){let o=Ke(n);if(o===null)throw new Error("updateCheckEnabled: true or false");r.updateCheckEnabled=o}else if(e==="updateCheckIntervalMs"){let o=Number.parseInt(n,10);if(!Number.isFinite(o)||o<36e5)throw new Error("updateCheckIntervalMs: min 3600000");r.updateCheckIntervalMs=Math.min(6048e5,o)}else if(e==="updateCheckPackageName"){if(!n.trim())throw new Error("updateCheckPackageName: non-empty");r.updateCheckPackageName=n.trim().slice(0,214)}else if(e==="updateInfoUrl")r.updateInfoUrl=n.trim().slice(0,2048);else if(e==="chatLlmFallbackEnabled"){let o=Ke(n);if(o===null)throw new Error("chatLlmFallbackEnabled: true or false");return W({chatLlmFallbackEnabled:o})}else{if(e==="chatLlmShellCommand")return W({chatLlmShellCommand:n});if(e==="chatLlmTimeoutMs"){let o=Number.parseInt(n,10);if(!Number.isFinite(o)||o<=0)throw new Error("chatLlmTimeoutMs: positive integer");return W({chatLlmTimeoutMs:o})}else if(e==="chatLlmMaxInputChars"){let o=Number.parseInt(n,10);if(!Number.isFinite(o)||o<=0)throw new Error("chatLlmMaxInputChars: positive integer");return W({chatLlmMaxInputChars:o})}else if(e==="chatLlmMaxOutputChars"){let o=Number.parseInt(n,10);if(!Number.isFinite(o)||o<=0)throw new Error("chatLlmMaxOutputChars: positive integer");return W({chatLlmMaxOutputChars:o})}else if(e==="chatLlmNeedsTty"){let o=Ke(n);if(o===null)throw new Error("chatLlmNeedsTty: true or false");return W({chatLlmNeedsTty:o})}else{if(e==="chatLlmWorkDir")return W({chatLlmWorkDir:n.trim()});if(e==="webhookEnabled"){let o=Ke(n);if(o===null)throw new Error("webhookEnabled: true or false");return W({webhookEnabled:o})}else if(e==="webhookPort"){let o=Number.parseInt(n,10);if(!Number.isFinite(o)||o<0||o>65535)throw new Error("webhookPort: integer 0\u201365535");return W({webhookPort:o})}else if(e==="webhookHost"){if(!n.trim())throw new Error("webhookHost: non-empty");return W({webhookHost:n.trim().slice(0,256)})}else{if(e==="webhookToken")return W({webhookToken:n.trim()});if(e==="watchEnabled"){let o=Ke(n);if(o===null)throw new Error("watchEnabled: true or false");return W({watchEnabled:o})}else if(e==="watchDebounceMs"){let o=Number.parseInt(n,10);if(!Number.isFinite(o)||o<500||o>6e4)throw new Error("watchDebounceMs: integer 500\u201360000");return W({watchDebounceMs:o})}else if(e==="watchMaxEventsPerMinute"){let o=Number.parseInt(n,10);if(!Number.isFinite(o)||o<1||o>120)throw new Error("watchMaxEventsPerMinute: integer 1\u2013120");return W({watchMaxEventsPerMinute:o})}else if(e==="watchAutoRestore"){let o=Ke(n);if(o===null)throw new Error("watchAutoRestore: true or false");return W({watchAutoRestore:o})}else throw new Error(`Unsupported key: ${e}`)}}}return _e(r),r}function Jc(e){let t=Qf(e);if(e==="platformToken"||e==="platformDeviceId")return W({[e]:t});if(e==="tunnelRelayUrl")return W({tunnelRelayUrl:t});let n=$();return n[e]=t,_e(n),n}function sn(e,t){if(e==="platformToken"||e==="telegramBotToken"||e==="webhookToken"){let n=t.trim();return n?n.length<=8?"(set)":`${n.slice(0,4)}\u2026${n.slice(-4)}`:"(empty)"}return t}me();G();import An from"node:fs";import $i from"node:os";import hr from"node:path";import Yc from"node:crypto";var vi="\u2063omnish/c v1",Vf=/([nlra])=([^\s\]]*)/g;function ot(e){return e.replace(/-/g,"").slice(0,8)}function Si(e){return e.replace(/[\s\]\r\n]/g,"_").slice(0,64)}function Xf(e){let t=Si(ot(e.nodeId)),n=Si(e.label||""),r=e.role==="primary"?"p":"s",o=Si(e.activeNodeId?ot(e.activeNodeId):"");return`${vi} [n=${t} l=${n} r=${r} a=${o}]`}function Kc(e,t){let n=Xf(t);if(e.includes(n))return e;let r=e.replace(/\s+$/,"");return r.length===0?n:`${r}
88
88
 
89
- ${n}`}function Hc(e){if(!e||!e.includes(bi))return null;let t=e.indexOf(bi),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(Jf))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 Gc=3,zf="node-id",qf="cluster-local.json",Bc=8;function Jc(){return pr.join(H,qf)}function vi(){return pr.join(H,zf)}function Xe(){j(H);let e=vi();try{if(Cn.existsSync(e)){let n=Cn.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=jc.randomUUID();return Cn.writeFileSync(e,`${t}
90
- `,{mode:384}),t}function Kc(){return{schemaVersion:Gc,updatedAt:new Date().toISOString(),peers:[],senderBindings:{}}}function Yf(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 ge(){let e=Jc();try{let t=Cn.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=Yf(n.senderBindings);return{schemaVersion:Gc,updatedAt:typeof n.updatedAt=="string"?n.updatedAt:new Date().toISOString(),peers:r,senderBindings:o}}catch{return Kc()}}function Qf(e,t){let n=pr.dirname(e);j(n);let r=`${JSON.stringify(t,null,2)}
91
- `,o=pr.join(n,`.${pr.basename(e)}.tmp.${process.pid}.${jc.randomBytes(4).toString("hex")}`);Cn.writeFileSync(o,r,{mode:384}),Cn.renameSync(o,e)}function $i(e){let t=Jc(),n=Kc();for(let r=0;r<Bc;r++){n=ge(),e(n),n.updatedAt=new Date().toISOString();try{return Qf(t,n),n}catch{}}throw new Error(`Could not write cluster local state after ${Bc} attempts: ${t}`)}function zc(e){return(e.clusterLabel??"").trim()||Si.hostname()}function ye(e){return e.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;")}function qc(e,t){let n=e.peers.findIndex(r=>r.nodeId===t.nodeId);n>=0?e.peers[n]=t:e.peers.push(t)}function mr(e){return{nodeId:ot(Xe()),label:zc(e),role:e.clusterRole,lastSeenIso:new Date().toISOString()}}function Yc(e,t,n){let r=n.trim();if(!r)return{ok:!1,reason:"not-found"};let o=mr(t),s=[o];for(let l of e.peers)l.nodeId!==o.nodeId&&s.push(l);let i=r.toLowerCase();if(/^[0-9a-f]{8}$/i.test(r)){let l=s.find(d=>d.nodeId.toLowerCase()===i);if(l)return{ok:!0,peer:l}}let a=s.filter(l=>l.label.toLowerCase()===i);return a.length===1?{ok:!0,peer:a[0]}:a.length>1?{ok:!1,reason:"ambiguous-label",matches:a}:{ok:!1,reason:"not-found"}}function Ct(e,t){let n=ge(),r=n.senderBindings[t];if(r&&r.nodeId)return r;let o=e.clusterSenderBindings?.[t];if(typeof o=="string"&&o.trim()){let s=Yc(n,e,o);if(s.ok)return{senderKey:t,nodeId:s.peer.nodeId,sinceIso:new Date(0).toISOString(),source:"config"}}return null}function xi(e,t,n="chat"){let r=$(),o=ge(),s=Yc(o,r,t);if(!s.ok)return{state:o,resolved:s};let i=new Date().toISOString();return{state:$i(l=>{l.senderBindings[e]={senderKey:e,nodeId:s.peer.nodeId,sinceIso:i,source:n},qc(l,{...s.peer,lastSeenIso:i})}),resolved:s}}function Vf(e){let t=null;return{state:$i(r=>{r.senderBindings[e]&&(t=r.senderBindings[e]??null,delete r.senderBindings[e])}),removed:t}}function Qc(e,t){let n=ot(Xe()),r=new Date().toISOString();return $i(o=>{if(qc(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 Vc(e,t){let n=Ct(t,e);return n?n.nodeId===ot(Xe()):!1}function Xf(e,t){let n=mr(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 Xf(e,t)===ot(Xe())}function mo(e,t,n){let r=ot(Xe()),o=["*Computers*",""],s=n?Ct(t,n):null;n&&(s?o.push(`Your binding: \`${s.nodeId}\` (${s.source})`):o.push("Your binding: (none \u2014 send /c use <label-or-id>)"),o.push(""));let i=new Map;i.set(r,mr(t));for(let l of e.peers)i.set(l.nodeId,l);let a=[...i.values()].sort((l,d)=>l.label.localeCompare(d.label));if(a.length===0)return o.push("(no peers observed yet \u2014 run /c status from this chat)"),o.join(`
89
+ ${n}`}function zc(e){if(!e||!e.includes(vi))return null;let t=e.indexOf(vi),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(Vf))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 Qc=3,Zf="node-id",eh="cluster-local.json",qc=8;function Vc(){return hr.join(H,eh)}function Ri(){return hr.join(H,Zf)}function Xe(){j(H);let e=Ri();try{if(An.existsSync(e)){let n=An.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=Yc.randomUUID();return An.writeFileSync(e,`${t}
90
+ `,{mode:384}),t}function Xc(){return{schemaVersion:Qc,updatedAt:new Date().toISOString(),peers:[],senderBindings:{}}}function th(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 ge(){let e=Vc();try{let t=An.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=th(n.senderBindings);return{schemaVersion:Qc,updatedAt:typeof n.updatedAt=="string"?n.updatedAt:new Date().toISOString(),peers:r,senderBindings:o}}catch{return Xc()}}function nh(e,t){let n=hr.dirname(e);j(n);let r=`${JSON.stringify(t,null,2)}
91
+ `,o=hr.join(n,`.${hr.basename(e)}.tmp.${process.pid}.${Yc.randomBytes(4).toString("hex")}`);An.writeFileSync(o,r,{mode:384}),An.renameSync(o,e)}function Mi(e){let t=Vc(),n=Xc();for(let r=0;r<qc;r++){n=ge(),e(n),n.updatedAt=new Date().toISOString();try{return nh(t,n),n}catch{}}throw new Error(`Could not write cluster local state after ${qc} attempts: ${t}`)}function Zc(e){return(e.clusterLabel??"").trim()||$i.hostname()}function ye(e){return e.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;")}function eu(e,t){let n=e.peers.findIndex(r=>r.nodeId===t.nodeId);n>=0?e.peers[n]=t:e.peers.push(t)}function gr(e){return{nodeId:ot(Xe()),label:Zc(e),role:e.clusterRole,lastSeenIso:new Date().toISOString()}}function tu(e,t,n){let r=n.trim();if(!r)return{ok:!1,reason:"not-found"};let o=gr(t),s=[o];for(let l of e.peers)l.nodeId!==o.nodeId&&s.push(l);let i=r.toLowerCase();if(/^[0-9a-f]{8}$/i.test(r)){let l=s.find(d=>d.nodeId.toLowerCase()===i);if(l)return{ok:!0,peer:l}}let a=s.filter(l=>l.label.toLowerCase()===i);return a.length===1?{ok:!0,peer:a[0]}:a.length>1?{ok:!1,reason:"ambiguous-label",matches:a}:{ok:!1,reason:"not-found"}}function Mt(e,t){let n=ge(),r=n.senderBindings[t];if(r&&r.nodeId)return r;let o=e.clusterSenderBindings?.[t];if(typeof o=="string"&&o.trim()){let s=tu(n,e,o);if(s.ok)return{senderKey:t,nodeId:s.peer.nodeId,sinceIso:new Date(0).toISOString(),source:"config"}}return null}function Ci(e,t,n="chat"){let r=$(),o=ge(),s=tu(o,r,t);if(!s.ok)return{state:o,resolved:s};let i=new Date().toISOString();return{state:Mi(l=>{l.senderBindings[e]={senderKey:e,nodeId:s.peer.nodeId,sinceIso:i,source:n},eu(l,{...s.peer,lastSeenIso:i})}),resolved:s}}function rh(e){let t=null;return{state:Mi(r=>{r.senderBindings[e]&&(t=r.senderBindings[e]??null,delete r.senderBindings[e])}),removed:t}}function nu(e,t){let n=ot(Xe()),r=new Date().toISOString();return Mi(o=>{if(eu(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 ru(e,t){let n=Mt(t,e);return n?n.nodeId===ot(Xe()):!1}function oh(e,t){let n=gr(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 oh(e,t)===ot(Xe())}function yo(e,t,n){let r=ot(Xe()),o=["*Computers*",""],s=n?Mt(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,gr(t));for(let l of e.peers)i.set(l.nodeId,l);let a=[...i.values()].sort((l,d)=>l.label.localeCompare(d.label));if(a.length===0)return o.push("(no peers observed yet \u2014 run /c status from this chat)"),o.join(`
92
92
  `);for(let l of a){let d=l.nodeId===r,c=[(s?l.nodeId===s.nodeId:!1)?"your binding":null,d?"you":null].filter(Boolean).join(", "),m=c?` (${c})`:"";o.push(`${Re}*${l.label}*${m}
93
93
  id \`${l.nodeId}\` \xB7 role ${l.role}
94
94
  seen ${l.lastSeenIso}`)}return o.push(""),o.push(`Updated: ${e.updatedAt}`),o.join(`
95
- `)}function ki(e,t,n){let r=ot(Xe()),o=["<b>Computers</b>",""],s=n?Ct(t,n):null;n&&(s?o.push(`Your binding: <code>${ye(s.nodeId)}</code> (${ye(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,mr(t));for(let l of e.peers)i.set(l.nodeId,l);let a=[...i.values()].sort((l,d)=>l.label.localeCompare(d.label));if(a.length===0)return o.push("(no peers observed yet \u2014 run /c status from this chat)"),o.join(`
95
+ `)}function xi(e,t,n){let r=ot(Xe()),o=["<b>Computers</b>",""],s=n?Mt(t,n):null;n&&(s?o.push(`Your binding: <code>${ye(s.nodeId)}</code> (${ye(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,gr(t));for(let l of e.peers)i.set(l.nodeId,l);let a=[...i.values()].sort((l,d)=>l.label.localeCompare(d.label));if(a.length===0)return o.push("(no peers observed yet \u2014 run /c status from this chat)"),o.join(`
96
96
  `);for(let l of a){let d=l.nodeId===r,c=[(s?l.nodeId===s.nodeId:!1)?"your binding":null,d?"you":null].filter(Boolean).join(", "),m=c?` (${ye(c)})`:"";o.push(`\u2022 <b>${ye(l.label)}</b>${m}<br/> id <code>${ye(l.nodeId)}</code> \xB7 role ${ye(l.role)}<br/> seen ${ye(l.lastSeenIso)}`)}return o.push(""),o.push(`Updated: ${ye(e.updatedAt)}`),o.join(`
97
- `)}function Ri(e,t,n){return mo(e,t,n??null).replace(/\*([^*]+)\*/g,"$1").replace(/`([^`]+)`/g,"$1")}function Ci(e,t){let n=Xe(),r=ot(n),o=ge(),s=e.clusterRole,i=zc(e),a=t?Ct(e,t):null,l=a?a.nodeId===r:!1,d=t?a?`your binding: ${a.nodeId} (${a.source}${l?", here":""})`:"your binding: (none)":"",u=["*This computer*","",`label: ${i}`,`node id: \`${r}\` (full id in ${vi()})`,`host: ${Si.hostname()}`,`clusterRole: ${s}`,d,`peers known: ${o.peers.length}`,`senderBindings (chat): ${Object.keys(o.senderBindings).length}`].filter(Boolean).join(`
98
- `),c=["<b>This computer</b>","",`label: ${ye(i)}`,`node id: <code>${ye(r)}</code> (full id in ${ye(vi())})`,`host: ${ye(Si.hostname())}`,`clusterRole: ${ye(s)}`,t?ye(d):"",`peers known: ${o.peers.length}`,`senderBindings (chat): ${Object.keys(o.senderBindings).length}`].filter(Boolean).join(`
99
- `);return{wa:u,tg:c}}function Zf(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","",`${Re}/c use <label-or-id> \u2014 bind your messages to that machine`,`${Re}/c here \u2014 bind your messages to THIS machine`,`${Re}/c using \u2014 show your current binding`,`${Re}/c unuse \u2014 clear your chat-set binding (config default still applies, if any)`,`${Re}/c status \u2014 every online host replies with its own paragraph`,`${Re}/c list \u2014 locally known roster (single responder)`,`${Re}/c help \u2014 this help`,"",`clusterRole on this host: ${t}`].join(`
97
+ `)}function Ti(e,t,n){return yo(e,t,n??null).replace(/\*([^*]+)\*/g,"$1").replace(/`([^`]+)`/g,"$1")}function Ei(e,t){let n=Xe(),r=ot(n),o=ge(),s=e.clusterRole,i=Zc(e),a=t?Mt(e,t):null,l=a?a.nodeId===r:!1,d=t?a?`your binding: ${a.nodeId} (${a.source}${l?", here":""})`:"your binding: (none)":"",u=["*This computer*","",`label: ${i}`,`node id: \`${r}\` (full id in ${Ri()})`,`host: ${$i.hostname()}`,`clusterRole: ${s}`,d,`peers known: ${o.peers.length}`,`senderBindings (chat): ${Object.keys(o.senderBindings).length}`].filter(Boolean).join(`
98
+ `),c=["<b>This computer</b>","",`label: ${ye(i)}`,`node id: <code>${ye(r)}</code> (full id in ${ye(Ri())})`,`host: ${ye($i.hostname())}`,`clusterRole: ${ye(s)}`,t?ye(d):"",`peers known: ${o.peers.length}`,`senderBindings (chat): ${Object.keys(o.senderBindings).length}`].filter(Boolean).join(`
99
+ `);return{wa:u,tg:c}}function sh(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","",`${Re}/c use <label-or-id> \u2014 bind your messages to that machine`,`${Re}/c here \u2014 bind your messages to THIS machine`,`${Re}/c using \u2014 show your current binding`,`${Re}/c unuse \u2014 clear your chat-set binding (config default still applies, if any)`,`${Re}/c status \u2014 every online host replies with its own paragraph`,`${Re}/c list \u2014 locally known roster (single responder)`,`${Re}/c help \u2014 this help`,"",`clusterRole on this host: ${t}`].join(`
100
100
  `),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: ${ye(t)}`].join(`
101
- `);return ue(n,r)}function eh(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 Xc(e,t,n){let r=t.trim().split(/\s+/),o=r[0]?.toLowerCase()??"";if(!o||o==="help"){if(!e.clusterEnabled&&o!=="help"){let l=ge();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=ge();return He(a,e)?Zf(e):null}let s=ot(Xe());if(o==="use"||o==="bind"){let a=r.slice(1).join(" ").trim();if(!n){let y=ge();return He(y,e)?p("/c use is only available on chats with a known sender (allowlisted WhatsApp/Telegram)."):null}if(!a){let y=ge();return He(y,e)?p("Usage: /c use <label-or-id>"):null}e.clusterEnabled||Yr(!0);let d=ge().senderBindings[n]??null,{state:u,resolved:c}=xi(n,a,"chat");if(!c.ok)return He(u,e)?eh(a,c):null;let m=c.peer.nodeId===s,f=d?d.nodeId===s:!1;if(!m)return null;let h=[`*Bound to ${c.peer.label}*`,`id \`${c.peer.nodeId}\``,"","Your messages now route to this machine. Other senders are not affected.","",mo(u,e,n)].join(`
102
- `),g=[`<b>Bound to ${ye(c.peer.label)}</b>`,`id <code>${ye(c.peer.nodeId)}</code>`,"","Your messages now route to this machine. Other senders are not affected.","",ki(u,e,n)].join(`
103
- `);return ue(h,g)}if(o==="here"||o==="take"){if(!n){let c=ge();return He(c,e)?p("/c here is only available on chats with a known sender (allowlisted WhatsApp/Telegram)."):null}e.clusterEnabled||Yr(!0);let{state:a,resolved:l}=xi(n,s,"chat");if(!l.ok)return He(a,e)?p("Could not bind to this machine."):null;let d=["*Bound to this machine.*","","Your messages now route here. Other senders are not affected.","",mo(a,e,n)].join(`
104
- `),u=["<b>Bound to this machine.</b>","","Your messages now route here. Other senders are not affected.","",ki(a,e,n)].join(`
105
- `);return ue(d,u)}if(o==="using"){if(!n){let d=ge();return He(d,e)?p("/c using is only available on chats with a known sender (allowlisted WhatsApp/Telegram)."):null}let a=Ct(e,n);if(a){if(a.nodeId!==s)return null;let c=[...ge().peers,mr(e)].find(h=>h.nodeId===a.nodeId)?.label??"(unknown label)",m=[`*Bound to ${c}*`,`id \`${a.nodeId}\` \xB7 source ${a.source}`,a.source==="chat"?`since ${a.sinceIso}`:"(from config)"].join(`
101
+ `);return de(n,r)}function ih(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 ou(e,t,n){let r=t.trim().split(/\s+/),o=r[0]?.toLowerCase()??"";if(!o||o==="help"){if(!e.clusterEnabled&&o!=="help"){let l=ge();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=ge();return He(a,e)?sh(e):null}let s=ot(Xe());if(o==="use"||o==="bind"){let a=r.slice(1).join(" ").trim();if(!n){let y=ge();return He(y,e)?p("/c use is only available on chats with a known sender (allowlisted WhatsApp/Telegram)."):null}if(!a){let y=ge();return He(y,e)?p("Usage: /c use <label-or-id>"):null}e.clusterEnabled||Zr(!0);let d=ge().senderBindings[n]??null,{state:u,resolved:c}=Ci(n,a,"chat");if(!c.ok)return He(u,e)?ih(a,c):null;let m=c.peer.nodeId===s,f=d?d.nodeId===s:!1;if(!m)return null;let h=[`*Bound to ${c.peer.label}*`,`id \`${c.peer.nodeId}\``,"","Your messages now route to this machine. Other senders are not affected.","",yo(u,e,n)].join(`
102
+ `),g=[`<b>Bound to ${ye(c.peer.label)}</b>`,`id <code>${ye(c.peer.nodeId)}</code>`,"","Your messages now route to this machine. Other senders are not affected.","",xi(u,e,n)].join(`
103
+ `);return de(h,g)}if(o==="here"||o==="take"){if(!n){let c=ge();return He(c,e)?p("/c here is only available on chats with a known sender (allowlisted WhatsApp/Telegram)."):null}e.clusterEnabled||Zr(!0);let{state:a,resolved:l}=Ci(n,s,"chat");if(!l.ok)return He(a,e)?p("Could not bind to this machine."):null;let d=["*Bound to this machine.*","","Your messages now route here. Other senders are not affected.","",yo(a,e,n)].join(`
104
+ `),u=["<b>Bound to this machine.</b>","","Your messages now route here. Other senders are not affected.","",xi(a,e,n)].join(`
105
+ `);return de(d,u)}if(o==="using"){if(!n){let d=ge();return He(d,e)?p("/c using is only available on chats with a known sender (allowlisted WhatsApp/Telegram)."):null}let a=Mt(e,n);if(a){if(a.nodeId!==s)return null;let c=[...ge().peers,gr(e)].find(h=>h.nodeId===a.nodeId)?.label??"(unknown label)",m=[`*Bound to ${c}*`,`id \`${a.nodeId}\` \xB7 source ${a.source}`,a.source==="chat"?`since ${a.sinceIso}`:"(from config)"].join(`
106
106
  `),f=[`<b>Bound to ${ye(c)}</b>`,`id <code>${ye(a.nodeId)}</code> \xB7 source ${ye(a.source)}`,a.source==="chat"?`since ${ye(a.sinceIso)}`:"(from config)"].join(`
107
- `);return ue(m,f)}let l=ge();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=ge();return He(m,e)?p("/c unuse is only available on chats with a known sender (allowlisted WhatsApp/Telegram)."):null}let l=ge().senderBindings[n]??null,{state:d}=Vf(n);if(l){if(l.nodeId!==s)return null}else if(!He(d,e))return null;let u=Ct(e,n),c=u?`
107
+ `);return de(m,f)}let l=ge();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=ge();return He(m,e)?p("/c unuse is only available on chats with a known sender (allowlisted WhatsApp/Telegram)."):null}let l=ge().senderBindings[n]??null,{state:d}=rh(n);if(l){if(l.nodeId!==s)return null}else if(!He(d,e))return null;let u=Mt(e,n),c=u?`
108
108
  Config default still applies: \`${u.nodeId}\`.`:`
109
- No config default is set; nobody will answer until you /c use <label-or-id>.`;return p(`Cleared your chat binding.${c}`)}if(o==="step-down"||o==="stepdown"){let a=ge();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=Ci(e,n);return ue(a.wa,a.tg)}if(o==="list"){let a=ge(),l=n?Ct(e,n):null;if(l){if(l.nodeId!==s)return null}else if(!He(a,e))return null;return ue(mo(a,e,n??null),ki(a,e,n??null))}let i=ge();return He(i,e)?p(`Unknown /c subcommand "${o}". Try /c help`):null}function Zc(e,t){return Yr(!0),xi(e,t,"chat")}qe();be();function th(e){return`wa:${e}`}function Mi(e){return`tg:${e}`}function eu(e){return{peerKey:th(e.fromJid),text:e.text,waMessageId:e.messageId,mediaSavedPath:e.mediaSavedPath,mediaError:e.mediaError}}G();function fo(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(`
109
+ No config default is set; nobody will answer until you /c use <label-or-id>.`;return p(`Cleared your chat binding.${c}`)}if(o==="step-down"||o==="stepdown"){let a=ge();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=Ei(e,n);return de(a.wa,a.tg)}if(o==="list"){let a=ge(),l=n?Mt(e,n):null;if(l){if(l.nodeId!==s)return null}else if(!He(a,e))return null;return de(yo(a,e,n??null),xi(a,e,n??null))}let i=ge();return He(i,e)?p(`Unknown /c subcommand "${o}". Try /c help`):null}function su(e,t){return Zr(!0),Ci(e,t,"chat")}qe();be();function ah(e){return`wa:${e}`}function Pi(e){return`tg:${e}`}function iu(e){return{peerKey:ah(e.fromJid),text:e.text,waMessageId:e.messageId,mediaSavedPath:e.mediaSavedPath,mediaError:e.mediaError}}G();function wo(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(`
110
110
  `),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(`
111
111
  `),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(`
112
112
  `);return process.platform==="linux"?o:process.platform==="darwin"?s:process.platform==="win32"?i:[o,"","---","",s].join(`
113
- `)}G();import{execFileSync as jt}from"node:child_process";import Mn from"node:fs";import hr from"node:os";import st from"node:path";import Fe from"node:process";function nn(){let e=Fe.execPath,t=Fe.argv[1];if(!t||!t.trim())return{nodePath:e,scriptPath:"",omnishHome:H,error:"Cannot resolve gateway entry script (missing argv[1]). Run omnish from its CLI entry."};let n=st.resolve(t),r=Fe.env.OMNISH_HOME?.trim(),o=r?st.resolve(r):H;return{nodePath:e,scriptPath:n,omnishHome:o}}function tu(e){return/[ "'\\\s]/.test(e)?`"${e.replace(/\\/g,"\\\\").replace(/"/g,'\\"')}"`:e}function nh(e){return`Environment="OMNISH_HOME=${e.replace(/\\/g,"\\\\").replace(/"/g,'\\"')}"`}function rh(e){return`[Unit]
113
+ `)}G();import{execFileSync as Jt}from"node:child_process";import In from"node:fs";import wr from"node:os";import st from"node:path";import Fe from"node:process";function an(){let e=Fe.execPath,t=Fe.argv[1];if(!t||!t.trim())return{nodePath:e,scriptPath:"",omnishHome:H,error:"Cannot resolve gateway entry script (missing argv[1]). Run omnish from its CLI entry."};let n=st.resolve(t),r=Fe.env.OMNISH_HOME?.trim(),o=r?st.resolve(r):H;return{nodePath:e,scriptPath:n,omnishHome:o}}function au(e){return/[ "'\\\s]/.test(e)?`"${e.replace(/\\/g,"\\\\").replace(/"/g,'\\"')}"`:e}function lh(e){return`Environment="OMNISH_HOME=${e.replace(/\\/g,"\\\\").replace(/"/g,'\\"')}"`}function ch(e){return`[Unit]
114
114
  Description=omnish gateway (WhatsApp/Telegram)
115
115
  After=network-online.target
116
116
  Wants=network-online.target
117
117
 
118
118
  [Service]
119
119
  Type=simple
120
- ExecStart=${`${tu(e.nodePath)} ${tu(e.scriptPath)} run`}
121
- ${nh(e.omnishHome)}
120
+ ExecStart=${`${au(e.nodePath)} ${au(e.scriptPath)} run`}
121
+ ${lh(e.omnishHome)}
122
122
  Restart=on-failure
123
123
  RestartSec=5
124
124
 
125
125
  [Install]
126
126
  WantedBy=default.target
127
- `}function fr(e){return e.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;")}function oh(e){let t=hr.homedir(),n=st.join(e.omnishHome,"logs","launchd-stdout.log"),r=st.join(e.omnishHome,"logs","launchd-stderr.log");j(st.dirname(n));let o=fr(e.nodePath),s=fr(e.scriptPath),i=fr(e.omnishHome),a=fr(n),l=fr(r);return`<?xml version="1.0" encoding="UTF-8"?>
127
+ `}function yr(e){return e.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;")}function uh(e){let t=wr.homedir(),n=st.join(e.omnishHome,"logs","launchd-stdout.log"),r=st.join(e.omnishHome,"logs","launchd-stderr.log");j(st.dirname(n));let o=yr(e.nodePath),s=yr(e.scriptPath),i=yr(e.omnishHome),a=yr(n),l=yr(r);return`<?xml version="1.0" encoding="UTF-8"?>
128
128
  <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
129
129
  <plist version="1.0">
130
130
  <dict>
@@ -151,34 +151,34 @@ WantedBy=default.target
151
151
  <string>${l}</string>
152
152
  </dict>
153
153
  </plist>
154
- `}function ho(){let e=nn();if(e.error)return{ok:!1,detail:e.error};if(Fe.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(Fe.platform==="darwin")try{let t=st.join(hr.homedir(),"Library/LaunchAgents/dev.omnish.gateway.plist");j(st.dirname(t));let n=oh(e);Mn.writeFileSync(t,n,{mode:384});let r=typeof Fe.getuid=="function"?Fe.getuid():null;if(r===null||r<0)return{ok:!1,detail:"Could not read user id for launchctl."};let o=`gui/${r}`;try{jt("launchctl",["bootout",o,t],{stdio:"pipe"})}catch{}return jt("launchctl",["bootstrap",o,t],{stdio:"inherit"}),jt("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(Fe.platform==="linux")try{let t=st.join(hr.homedir(),".config","systemd","user");j(t);let n=st.join(t,"omnish.service"),r=rh(e);return Mn.writeFileSync(n,r,{mode:420}),jt("systemctl",["--user","daemon-reload"],{stdio:"inherit"}),jt("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: ${Fe.platform}`}}function go(){if(Fe.platform==="win32")return{ok:!0,detail:"Windows: remove the omnish task from Task Scheduler manually on this PC. See /service instructions."};if(Fe.platform==="darwin")try{let e=st.join(hr.homedir(),"Library/LaunchAgents/dev.omnish.gateway.plist"),n=`gui/${typeof Fe.getuid=="function"?Fe.getuid():0}`;if(Mn.existsSync(e)){try{jt("launchctl",["bootout",n,e],{stdio:"pipe"})}catch{}Mn.unlinkSync(e)}return{ok:!0,detail:"LaunchAgent dev.omnish.gateway removed (if it was present)."}}catch(e){return{ok:!1,detail:String(e)}}if(Fe.platform==="linux")try{let e=st.join(hr.homedir(),".config","systemd","user","omnish.service");try{jt("systemctl",["--user","disable","--now","omnish.service"],{stdio:"pipe"})}catch{}Mn.existsSync(e)&&Mn.unlinkSync(e);try{jt("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: ${Fe.platform}`}}import nu from"node:fs";var yo=48e3;function wo(e,t){try{if(!nu.existsSync(e))return"(no log file yet \u2014 start with `omnish run -d` or the systemd/LaunchAgent service.)";let n=nu.readFileSync(e),o=(n.length>yo?n.subarray(n.length-yo):n).toString("utf8");return n.length>yo&&(o=`\u2026(truncated to last ${yo} bytes)
154
+ `}function bo(){let e=an();if(e.error)return{ok:!1,detail:e.error};if(Fe.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(Fe.platform==="darwin")try{let t=st.join(wr.homedir(),"Library/LaunchAgents/dev.omnish.gateway.plist");j(st.dirname(t));let n=uh(e);In.writeFileSync(t,n,{mode:384});let r=typeof Fe.getuid=="function"?Fe.getuid():null;if(r===null||r<0)return{ok:!1,detail:"Could not read user id for launchctl."};let o=`gui/${r}`;try{Jt("launchctl",["bootout",o,t],{stdio:"pipe"})}catch{}return Jt("launchctl",["bootstrap",o,t],{stdio:"inherit"}),Jt("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(Fe.platform==="linux")try{let t=st.join(wr.homedir(),".config","systemd","user");j(t);let n=st.join(t,"omnish.service"),r=ch(e);return In.writeFileSync(n,r,{mode:420}),Jt("systemctl",["--user","daemon-reload"],{stdio:"inherit"}),Jt("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: ${Fe.platform}`}}function ko(){if(Fe.platform==="win32")return{ok:!0,detail:"Windows: remove the omnish task from Task Scheduler manually on this PC. See /service instructions."};if(Fe.platform==="darwin")try{let e=st.join(wr.homedir(),"Library/LaunchAgents/dev.omnish.gateway.plist"),n=`gui/${typeof Fe.getuid=="function"?Fe.getuid():0}`;if(In.existsSync(e)){try{Jt("launchctl",["bootout",n,e],{stdio:"pipe"})}catch{}In.unlinkSync(e)}return{ok:!0,detail:"LaunchAgent dev.omnish.gateway removed (if it was present)."}}catch(e){return{ok:!1,detail:String(e)}}if(Fe.platform==="linux")try{let e=st.join(wr.homedir(),".config","systemd","user","omnish.service");try{Jt("systemctl",["--user","disable","--now","omnish.service"],{stdio:"pipe"})}catch{}In.existsSync(e)&&In.unlinkSync(e);try{Jt("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: ${Fe.platform}`}}import lu from"node:fs";var So=48e3;function vo(e,t){try{if(!lu.existsSync(e))return"(no log file yet \u2014 start with `omnish run -d` or the systemd/LaunchAgent service.)";let n=lu.readFileSync(e),o=(n.length>So?n.subarray(n.length-So):n).toString("utf8");return n.length>So&&(o=`\u2026(truncated to last ${So} bytes)
155
155
  ${o}`),o.split(/\r?\n/).slice(-t).join(`
156
- `).trimEnd()||"(empty)"}catch(n){return`Could not read log: ${String(n)}`}}import Gt from"node:fs";import Rh from"node:path";G();import sh from"node:crypto";import Pi from"node:fs";import ih from"node:path";var Ai=/^[a-zA-Z0-9][a-zA-Z0-9_-]{0,31}$/,En="__omnish_recipes_global__",Ii=new Set(["help","list","show","add","set","remove","rm","del","run","r","online"]),Tn=new Map,ru=!1;function ah(){try{let e=Pi.readFileSync(Dr,"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]=Be(i))}Tn.set(n,o)}}catch{}}function lh(e){if(!Array.isArray(e)||e.length===0)return;let t=[];for(let n of e)if(typeof n=="string"&&n.trim())t.push({cmd:n.trim()});else if(n&&typeof n=="object"){let r=n,o=typeof r.cmd=="string"?r.cmd.trim():"";if(!o)continue;t.push({cmd:o,label:typeof r.label=="string"?r.label.trim().slice(0,80):void 0,continueOnFail:typeof r.continueOnFail=="boolean"?r.continueOnFail:void 0})}return t.length>0?t:void 0}function Be(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=lh(e.steps),o={command:String(e.command).trim(),taskEnv:t,label:typeof e.label=="string"?e.label.trim().slice(0,80):void 0,description:typeof e.description=="string"?e.description.trim().slice(0,200):void 0,category:typeof e.category=="string"?e.category.trim().slice(0,40):void 0,featured:typeof e.featured=="boolean"?e.featured:void 0,dangerous:typeof e.dangerous=="boolean"?e.dangerous:void 0};return n!==void 0&&(o.promptTemplate=n),r!==void 0&&(o.steps=r),o}function ko(){j(ih.dirname(Dr));let e={};for(let[t,n]of Tn)Object.entries(n).length>0&&(e[t]={...n});Pi.writeFileSync(Dr,JSON.stringify(e,null,2)+`
157
- `,{mode:384})}function So(){ru||(ah(),ru=!0)}function ou(e){return So(),Tn.get(e)??{}}function Ti(e){So();let t=Tn.get(e);return t||(t={},Tn.set(e,t)),t}function ch(e,t){let n=e.taskEnv??"OMNISH_TASK",r=t.recipesMacroDefaultCommand.trim();return rn(r,n).ok?Be({...e,command:r,promptTemplate:e.command}):e}function uh(e,t){if(e.promptTemplate||rn(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(`
158
- ### `)}function Oi(e,t){let n=Be(e),r=n.taskEnv??"OMNISH_TASK";return!n.promptTemplate&&uh(n,r)&&(n=ch(n,t)),n}function dh(e){try{let t=Pi.readFileSync(Ur,"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(!(!Ai.test(i)||Ii.has(i))&&s&&typeof s=="object"&&typeof s.command=="string"){let l=Oi(s,e),d=on(l);if(!d.ok){console.warn(`[omnish] recipes.json: skipping "${i}": ${d.error}`);continue}r[i]=l}}return r}catch{return{}}}function ph(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 bo(e,t,n){for(let[r,o]of Object.entries(t)){let s=r.toLowerCase();!Ai.test(s)||Ii.has(s)||o.command.trim()&&e.set(s,{...o,name:s,source:n})}}function Ei(e,t){let n=ou(e),r={};for(let[o,s]of Object.entries(n)){let i=Oi(s,t);on(i).ok&&(r[o]=i)}return r}function su(e,t){let n=new Map;return bo(n,ph(t),"builtin"),bo(n,dh(t),"global"),bo(n,Ei(En,t),"shared"),bo(n,Ei(e,t),"peer"),n}function je(e,t,n){let r=n.trim().toLowerCase();return su(e,t).get(r)}function iu(e){let t=e.trim();if(/^--global$/i.test(t))return{mode:"global",remainder:""};if(/^-g$/i.test(t))return{mode:"global",remainder:""};if(/^--chat$/i.test(t))return{mode:"chat",remainder:""};if(/^-p$/i.test(t))return{mode:"chat",remainder:""};let n=/^--global\s+/i.exec(t);if(n)return{mode:"global",remainder:t.slice(n[0].length).trimStart()};let r=/^-g\s+/i.exec(t);if(r)return{mode:"global",remainder:t.slice(r[0].length).trimStart()};let o=/^--chat\s+/i.exec(t);if(o)return{mode:"chat",remainder:t.slice(o[0].length).trimStart()};let s=/^-p\s+/i.exec(t);return s?{mode:"chat",remainder:t.slice(s[0].length).trimStart()}:{mode:"resolved",remainder:t}}function Li(e){let t=e.trim();if(/^--global$/i.test(t))return{scope:"global",remainder:"",explicit:!0};if(/^-g$/i.test(t))return{scope:"global",remainder:"",explicit:!0};if(/^--chat$/i.test(t))return{scope:"chat",remainder:"",explicit:!0};if(/^-p$/i.test(t))return{scope:"chat",remainder:"",explicit:!0};let n=/^--global\s+([\s\S]*)$/i.exec(t);if(n?.[1]!==void 0)return{scope:"global",remainder:n[1].trimStart(),explicit:!0};let r=/^-g\s+([\s\S]*)$/i.exec(t);if(r?.[1]!==void 0)return{scope:"global",remainder:r[1].trimStart(),explicit:!0};let o=/^--chat\s+([\s\S]*)$/i.exec(t);if(o?.[1]!==void 0)return{scope:"chat",remainder:o[1].trimStart(),explicit:!0};let s=/^-p\s+([\s\S]*)$/i.exec(t);return s?.[1]!==void 0?{scope:"chat",remainder:s[1].trimStart(),explicit:!0}:{scope:"chat",remainder:t,explicit:!1}}function Ni(e){let t=Li(e);return{scope:t.scope,remainder:t.remainder}}function au(e){let t=e.trim();return!t||/^-+$/i.test(t)?{filter:"merged"}:/^(?:--global|-g)$/i.test(t)?{filter:"global"}:/^(?:--chat|-p)$/i.test(t)?{filter:"chat"}:{filter:"merged",bad:t}}function mh(e){let t=e.trim().toLowerCase();if(t==="--global"||t==="-g")return"global";if(t==="--chat"||t==="-p")return"chat"}function lu(e){let t=e.trim().match(/^(\S+)\s+(--global|-g|--chat|-p)\s*$/i);if(!t?.[1]||!t[2])return;let n=mh(t[2]);if(n)return{name:t[1],target:n}}function Mt(e,t,n,r){let o=r.trim().toLowerCase(),s=e==="global"?En:t,i=ou(s)[o];if(i===void 0)return;let a=Oi(i,n);if(on(a).ok)return{...a,name:o,source:e==="global"?"shared":"peer"}}function cu(e,t,n){if(n==="merged")return[];let r=n==="global"?En:e,o=n==="global"?"shared":"peer",s=Ei(r,t);return Object.entries(s).map(([i,a])=>({...a,name:i,source:o})).sort((i,a)=>i.name.localeCompare(a.name))}function gt(e){let t=e.trim();if(!t)return{ok:!1,error:"Name is empty."};let n=t.toLowerCase();return Ai.test(n)?Ii.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 vo(e,t){let n=e.replace(/\r\n/g,`
159
- `).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 rn(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 on(e){let t=e.taskEnv??"OMNISH_TASK",n=rn(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 fh(e){let t=e,n=null,r=!1,o=-1,s=-1;for(let l=0;l<t.length;l+=1){let d=t[l];if(r){r=!1;continue}if(d==="\\"){r=!0;continue}if(n){d===n&&(n=null);continue}if(d==='"'||d==="'"){n=d;continue}if(d!=="-"||t[l+1]!=="-")continue;let u=l===0?"":t[l-1];if(u&&!/\s/.test(u))continue;let c=t.slice(l+2),m=/^(template|tamplate)\b/i.exec(c);if(!m)continue;let f=l+2+m[0].length,h=t[f]??"";if(!h||!/\s/.test(h))continue;o=l;let g=f;for(;g<t.length&&/\s/.test(t[g]);)g+=1;s=g;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 _i(e,t){let n=e.replace(/\r\n/g,`
160
- `).trim();if(!n)throw new Error("Recipe body is empty.");let r=n.match(/^--steps\s+([\s\S]+)$/i);if(r){let m=r[1].trim().split(/\s*;\s*|\n/).filter(h=>h.trim());if(m.length===0)throw new Error("No steps found. Separate steps with ; or newlines.");let f=m.map(h=>{let g=h.startsWith("+"),y=g?h.slice(1).trim():h.trim();if(!y)throw new Error("Empty step in recipe.");return{cmd:y,...g?{continueOnFail:!0}:{}}});return Be({command:f[0].cmd,steps:f})}let o="OMNISH_TASK",{command:s,template:i}=fh(n);if(i!==void 0){if(!s)throw new Error("Command part (before --template) is empty.");if(!i.trim())throw new Error("Template part (after --template) is empty.");let c=rn(s,o);if(!c.ok)throw new Error(c.error);return Be({command:s,promptTemplate:i})}let a=/\n---\n/,l=n.search(a);if(l>=0){let c=n.slice(0,l).trim(),m=n.slice(l).replace(/^\n---\n/,"").trim();if(!c)throw new Error("Command part (before ---) is empty.");if(!m)throw new Error("Template part (after ---) is empty.");let f=rn(c,o);if(!f.ok)throw new Error(f.error);return Be({command:c,promptTemplate:m})}if(rn(n,o).ok)return Be({command:n});let d=t.trim(),u=rn(d,o);if(!u.ok)throw new Error(`recipesMacroDefaultCommand invalid (${u.error}). Fix config or paste runner then --- then template.`);return Be({command:d,promptTemplate:n})}function xo(e,t,n){let r="`".repeat(3),o=`<<<${r}$${t}${r}>>>`,s=e;s.includes(o)&&(s=s.split(o).join(n));let i=`<<<${t}>>>`;s.includes(i)&&(s=s.split(i).join(n));let a=new RegExp(`\\$${t}\\b`,"g");return s.replace(a,n)}function An(e,t,n,r="chat",o){let s=gt(t);if(!s.ok)throw new Error(s.error);let i=Be({...n,command:n.command});if(o?.skipCommandValidation){if(!i.command.trim())throw new Error("Command is empty.")}else{let d=on(i);if(!d.ok)throw new Error(d.error)}let a=r==="global"?En:e,l=Ti(a);l[s.normalized]=i,ko()}function uu(e,t,n="chat"){let r=t.trim().toLowerCase(),o=n==="global"?En:e;So();let s=Tn.get(o);return!s||!(r in s)?!1:(delete s[r],ko(),!0)}function Fi(e,t,n,r){let o=gt(t);if(!o.ok)return{ok:!1,error:o.error};let s=o.normalized,i=e,a=En;So();let l=Ti(i),d=Ti(a),u=l[s],c=d[s],m=f=>{let h=Be({...f}),g=on(h);if(!g.ok)throw new Error(g.error);return h};try{if(n==="global"){if(u!==void 0){let g=m(u);return d[s]=g,delete l[s],ko(),{ok:!0,kind:"moved",target:"global",name:s}}if(c!==void 0)return{ok:!0,kind:"noop",message:`Recipe "${s}" is already gateway-shared.`};let h=je(e,r,s);return h?.source==="builtin"||h?.source==="global"?{ok:!1,error:`Recipe "${s}" is built-in or from host recipes.json \u2014 not moved via /run set. Edit recipes.json or use a different name.`}:{ok:!1,error:`No user recipe "${s}" in this chat to promote. Add with /run add ${s} \u2026 or demote from global with /run set -p ${s}.`}}if(c!==void 0){let h=m(c);return l[s]=h,delete d[s],ko(),{ok:!0,kind:"moved",target:"chat",name:s}}if(u!==void 0)return{ok:!0,kind:"noop",message:`Recipe "${s}" is already stored for this chat only.`};let f=je(e,r,s);return f?.source==="builtin"||f?.source==="global"?{ok:!1,error:`Recipe "${s}" is built-in or from host recipes.json \u2014 not moved via /run set.`}:{ok:!1,error:`No gateway-shared user recipe "${s}" to demote. Add with /run add --global ${s} \u2026 or promote from this chat with /run set -g ${s}.`}}catch(f){return{ok:!1,error:String(f)}}}function du(e,t){let n=[...su(e,t).values()],r=n.filter(a=>a.featured).sort((a,l)=>a.name.localeCompare(l.name)),o=n.filter(a=>a.source==="peer").sort((a,l)=>a.name.localeCompare(l.name)),s=n.filter(a=>a.source==="shared").sort((a,l)=>a.name.localeCompare(l.name)),i=n.filter(a=>!a.featured&&a.source!=="peer"&&a.source!=="shared").sort((a,l)=>a.name.localeCompare(l.name));return{featured:r,shared:s,yours:o,more:i}}function $o(e){let t=sh.randomBytes(4).toString("hex");return`r-${e.replace(/[^a-zA-Z0-9_-]/g,"").slice(0,12)}-${t}`.slice(0,32)}function pu(e,t){let n=[`runbook: ${e}`];for(let o of t){let s=o.skipped?"skip":o.exitCode===0?"ok":"FAIL",i=o.label||`step ${o.index+1}`;if(o.skipped)n.push(`[${s}] ${i}`);else{n.push(`[${s}] ${i}${o.timedOut?" (timeout)":o.exitCode!==0?` (exit ${o.exitCode})`:""}`);let a=o.output.trim();if(a){let l=a.length>300?a.slice(0,300)+"\u2026":a;n.push(l)}}}let r=t.every(o=>o.skipped||o.exitCode===0);return n.push(r?"All steps completed successfully.":"Runbook stopped on failure."),n.join(`
161
- `)}G();var mu={enter:"\r",cr:"\r",lf:`
162
- `,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 hh(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+=`
163
- `,n++;continue}if(o==="t"){t+=" ",n++;continue}if(o==="\\"){t+="\\",n++;continue}}t+=r}return t}function gh(e){let t=e.trim();if(!t)return"";if(t.startsWith("\\"))return hh(t);let n=t.toLowerCase();if(mu[n])return mu[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 Ro(e){let t=e.split(",").map(n=>n.trim()).filter(Boolean);return t.length===0?"":t.map(n=>gh(n)).join("")}var gr="\x1B";function Co(e){let t=e;return t=t.replace(new RegExp(`${gr}\\[[\\d;?]*[ -/]*[@-~]`,"g"),""),t=t.replace(new RegExp(`${gr}\\][^${gr}\\u0007]*(?:\\u0007|${gr}\\\\)`,"g"),""),t=t.replace(new RegExp(`${gr}[@-Z\\\\-_]`,"g"),""),t=t.replace(/\u0007/g,""),t}function yh(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 wh(e,t){return`${e}${t}`}var Mo=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=wh(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=Co(l)),!l.trim()))return;let d=o.appsMaxFlushBytes;if(l.length>d){let f=l.slice(d);l=l.slice(0,d)+`
164
- [\u2026+${f.length} chars; /apps since ${r} to read more]`,this.pending.set(t,(this.pending.get(t)??"")+f)}let c=(this.opts.getRunningCount(n)>1?`[${r}] `:"")+l,m=yh(c,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))}}}};var To=4096,bh=/\[sudo\]\s+password\s+for\s+/i,kh=/passphrase\s+for\s+/i,Sh=/(?:^|\n)\s*(?:Password|password):\s*$/;function Eo(e){let n=Co(e).replace(/\r/g,"").slice(-512);return!!(bh.test(n)||kh.test(n)||Sh.test(n))}G();import vh from"node:fs";import xh from"node:path";import*as fu from"node-pty";var $h=1024*1024,Po=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>$h&&this.ringChunks.length>0;){let n=this.ringChunks.shift();this.ringBytes-=n.length}}static start(t){j(nr(t.peerKey));let n=xh.join(nr(t.peerKey),`${t.name}.log`),r=vh.createWriteStream(n,{flags:"a",mode:384}),o={...process.env,TERM:"xterm-256color",COLORTERM:"truecolor",...t.cwd?{PWD:t.cwd}:{},...t.extraEnv??{}},s=fu.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.onOutput?.(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}recentOutputTail(t=4096){if(this.ringBytes===0)return"";let n=Math.max(1,t),r=[],o=Math.min(n,this.ringBytes);for(let s=this.ringChunks.length-1;s>=0&&o>0;s--){let i=this.ringChunks[s];i.length<=o?(r.unshift(i),o-=i.length):(r.unshift(i.subarray(i.length-o)),o=0)}return Buffer.concat(r).toString("utf8")}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()}};function Wi(e){return new Promise(t=>setTimeout(t,e))}async function Ao(e,t,n){let r=n.appsSubmitDelayMs,o=n.appsClearInputDelayMs,s=n.appsClearInput!==!1,a=n.appsSkipClearOnPasswordPrompt!==!1&&Eo(e.recentOutputTail(To)),l=s&&!a?Ro(n.appsClearInputSequence||"^A,^K"):"",u=t.replace(/\r\n/g,`
156
+ `).trimEnd()||"(empty)"}catch(n){return`Could not read log: ${String(n)}`}}import Kt from"node:fs";import Ah from"node:path";G();import dh from"node:crypto";import Oi from"node:fs";import ph from"node:path";var Li=/^[a-zA-Z0-9][a-zA-Z0-9_-]{0,31}$/,Ln="__omnish_recipes_global__",Ni=new Set(["help","list","show","add","set","remove","rm","del","run","r","online"]),On=new Map,cu=!1;function mh(){try{let e=Oi.readFileSync(Gr,"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]=Be(i))}On.set(n,o)}}catch{}}function fh(e){if(!Array.isArray(e)||e.length===0)return;let t=[];for(let n of e)if(typeof n=="string"&&n.trim())t.push({cmd:n.trim()});else if(n&&typeof n=="object"){let r=n,o=typeof r.cmd=="string"?r.cmd.trim():"";if(!o)continue;t.push({cmd:o,label:typeof r.label=="string"?r.label.trim().slice(0,80):void 0,continueOnFail:typeof r.continueOnFail=="boolean"?r.continueOnFail:void 0})}return t.length>0?t:void 0}function Be(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=fh(e.steps),o={command:String(e.command).trim(),taskEnv:t,label:typeof e.label=="string"?e.label.trim().slice(0,80):void 0,description:typeof e.description=="string"?e.description.trim().slice(0,200):void 0,category:typeof e.category=="string"?e.category.trim().slice(0,40):void 0,featured:typeof e.featured=="boolean"?e.featured:void 0,dangerous:typeof e.dangerous=="boolean"?e.dangerous:void 0};return n!==void 0&&(o.promptTemplate=n),r!==void 0&&(o.steps=r),o}function $o(){j(ph.dirname(Gr));let e={};for(let[t,n]of On)Object.entries(n).length>0&&(e[t]={...n});Oi.writeFileSync(Gr,JSON.stringify(e,null,2)+`
157
+ `,{mode:384})}function Ro(){cu||(mh(),cu=!0)}function uu(e){return Ro(),On.get(e)??{}}function Ai(e){Ro();let t=On.get(e);return t||(t={},On.set(e,t)),t}function hh(e,t){let n=e.taskEnv??"OMNISH_TASK",r=t.recipesMacroDefaultCommand.trim();return ln(r,n).ok?Be({...e,command:r,promptTemplate:e.command}):e}function gh(e,t){if(e.promptTemplate||ln(e.command,t).ok||!Nn(e.command,t))return!1;let n=e.command;return n.includes("```")||n.length>2e3||/^"Create\b/i.test(n)||n.includes("<<<")||n.includes(`
158
+ ### `)}function _i(e,t){let n=Be(e),r=n.taskEnv??"OMNISH_TASK";return!n.promptTemplate&&gh(n,r)&&(n=hh(n,t)),n}function yh(e){try{let t=Oi.readFileSync(jr,"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(!(!Li.test(i)||Ni.has(i))&&s&&typeof s=="object"&&typeof s.command=="string"){let l=_i(s,e),d=cn(l);if(!d.ok){console.warn(`[omnish] recipes.json: skipping "${i}": ${d.error}`);continue}r[i]=l}}return r}catch{return{}}}function wh(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 xo(e,t,n){for(let[r,o]of Object.entries(t)){let s=r.toLowerCase();!Li.test(s)||Ni.has(s)||o.command.trim()&&e.set(s,{...o,name:s,source:n})}}function Ii(e,t){let n=uu(e),r={};for(let[o,s]of Object.entries(n)){let i=_i(s,t);cn(i).ok&&(r[o]=i)}return r}function du(e,t){let n=new Map;return xo(n,wh(t),"builtin"),xo(n,yh(t),"global"),xo(n,Ii(Ln,t),"shared"),xo(n,Ii(e,t),"peer"),n}function je(e,t,n){let r=n.trim().toLowerCase();return du(e,t).get(r)}function pu(e){let t=e.trim();if(/^--global$/i.test(t))return{mode:"global",remainder:""};if(/^-g$/i.test(t))return{mode:"global",remainder:""};if(/^--chat$/i.test(t))return{mode:"chat",remainder:""};if(/^-p$/i.test(t))return{mode:"chat",remainder:""};let n=/^--global\s+/i.exec(t);if(n)return{mode:"global",remainder:t.slice(n[0].length).trimStart()};let r=/^-g\s+/i.exec(t);if(r)return{mode:"global",remainder:t.slice(r[0].length).trimStart()};let o=/^--chat\s+/i.exec(t);if(o)return{mode:"chat",remainder:t.slice(o[0].length).trimStart()};let s=/^-p\s+/i.exec(t);return s?{mode:"chat",remainder:t.slice(s[0].length).trimStart()}:{mode:"resolved",remainder:t}}function Fi(e){let t=e.trim();if(/^--global$/i.test(t))return{scope:"global",remainder:"",explicit:!0};if(/^-g$/i.test(t))return{scope:"global",remainder:"",explicit:!0};if(/^--chat$/i.test(t))return{scope:"chat",remainder:"",explicit:!0};if(/^-p$/i.test(t))return{scope:"chat",remainder:"",explicit:!0};let n=/^--global\s+([\s\S]*)$/i.exec(t);if(n?.[1]!==void 0)return{scope:"global",remainder:n[1].trimStart(),explicit:!0};let r=/^-g\s+([\s\S]*)$/i.exec(t);if(r?.[1]!==void 0)return{scope:"global",remainder:r[1].trimStart(),explicit:!0};let o=/^--chat\s+([\s\S]*)$/i.exec(t);if(o?.[1]!==void 0)return{scope:"chat",remainder:o[1].trimStart(),explicit:!0};let s=/^-p\s+([\s\S]*)$/i.exec(t);return s?.[1]!==void 0?{scope:"chat",remainder:s[1].trimStart(),explicit:!0}:{scope:"chat",remainder:t,explicit:!1}}function Wi(e){let t=Fi(e);return{scope:t.scope,remainder:t.remainder}}function mu(e){let t=e.trim();return!t||/^-+$/i.test(t)?{filter:"merged"}:/^(?:--global|-g)$/i.test(t)?{filter:"global"}:/^(?:--chat|-p)$/i.test(t)?{filter:"chat"}:{filter:"merged",bad:t}}function bh(e){let t=e.trim().toLowerCase();if(t==="--global"||t==="-g")return"global";if(t==="--chat"||t==="-p")return"chat"}function fu(e){let t=e.trim().match(/^(\S+)\s+(--global|-g|--chat|-p)\s*$/i);if(!t?.[1]||!t[2])return;let n=bh(t[2]);if(n)return{name:t[1],target:n}}function Tt(e,t,n,r){let o=r.trim().toLowerCase(),s=e==="global"?Ln:t,i=uu(s)[o];if(i===void 0)return;let a=_i(i,n);if(cn(a).ok)return{...a,name:o,source:e==="global"?"shared":"peer"}}function hu(e,t,n){if(n==="merged")return[];let r=n==="global"?Ln:e,o=n==="global"?"shared":"peer",s=Ii(r,t);return Object.entries(s).map(([i,a])=>({...a,name:i,source:o})).sort((i,a)=>i.name.localeCompare(a.name))}function yt(e){let t=e.trim();if(!t)return{ok:!1,error:"Name is empty."};let n=t.toLowerCase();return Li.test(n)?Ni.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 Co(e,t){let n=e.replace(/\r\n/g,`
159
+ `).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 Nn(e,t){return e.includes("$")?e.includes(t):!1}function ln(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(!Nn(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 cn(e){let t=e.taskEnv??"OMNISH_TASK",n=ln(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 kh(e){let t=e,n=null,r=!1,o=-1,s=-1;for(let l=0;l<t.length;l+=1){let d=t[l];if(r){r=!1;continue}if(d==="\\"){r=!0;continue}if(n){d===n&&(n=null);continue}if(d==='"'||d==="'"){n=d;continue}if(d!=="-"||t[l+1]!=="-")continue;let u=l===0?"":t[l-1];if(u&&!/\s/.test(u))continue;let c=t.slice(l+2),m=/^(template|tamplate)\b/i.exec(c);if(!m)continue;let f=l+2+m[0].length,h=t[f]??"";if(!h||!/\s/.test(h))continue;o=l;let g=f;for(;g<t.length&&/\s/.test(t[g]);)g+=1;s=g;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 Ui(e,t){let n=e.replace(/\r\n/g,`
160
+ `).trim();if(!n)throw new Error("Recipe body is empty.");let r=n.match(/^--steps\s+([\s\S]+)$/i);if(r){let m=r[1].trim().split(/\s*;\s*|\n/).filter(h=>h.trim());if(m.length===0)throw new Error("No steps found. Separate steps with ; or newlines.");let f=m.map(h=>{let g=h.startsWith("+"),y=g?h.slice(1).trim():h.trim();if(!y)throw new Error("Empty step in recipe.");return{cmd:y,...g?{continueOnFail:!0}:{}}});return Be({command:f[0].cmd,steps:f})}let o="OMNISH_TASK",{command:s,template:i}=kh(n);if(i!==void 0){if(!s)throw new Error("Command part (before --template) is empty.");if(!i.trim())throw new Error("Template part (after --template) is empty.");let c=ln(s,o);if(!c.ok)throw new Error(c.error);return Be({command:s,promptTemplate:i})}let a=/\n---\n/,l=n.search(a);if(l>=0){let c=n.slice(0,l).trim(),m=n.slice(l).replace(/^\n---\n/,"").trim();if(!c)throw new Error("Command part (before ---) is empty.");if(!m)throw new Error("Template part (after ---) is empty.");let f=ln(c,o);if(!f.ok)throw new Error(f.error);return Be({command:c,promptTemplate:m})}if(ln(n,o).ok)return Be({command:n});let d=t.trim(),u=ln(d,o);if(!u.ok)throw new Error(`recipesMacroDefaultCommand invalid (${u.error}). Fix config or paste runner then --- then template.`);return Be({command:d,promptTemplate:n})}function Mo(e,t,n){let r="`".repeat(3),o=`<<<${r}$${t}${r}>>>`,s=e;s.includes(o)&&(s=s.split(o).join(n));let i=`<<<${t}>>>`;s.includes(i)&&(s=s.split(i).join(n));let a=new RegExp(`\\$${t}\\b`,"g");return s.replace(a,n)}function _n(e,t,n,r="chat",o){let s=yt(t);if(!s.ok)throw new Error(s.error);let i=Be({...n,command:n.command});if(o?.skipCommandValidation){if(!i.command.trim())throw new Error("Command is empty.")}else{let d=cn(i);if(!d.ok)throw new Error(d.error)}let a=r==="global"?Ln:e,l=Ai(a);l[s.normalized]=i,$o()}function gu(e,t,n="chat"){let r=t.trim().toLowerCase(),o=n==="global"?Ln:e;Ro();let s=On.get(o);return!s||!(r in s)?!1:(delete s[r],$o(),!0)}function Di(e,t,n,r){let o=yt(t);if(!o.ok)return{ok:!1,error:o.error};let s=o.normalized,i=e,a=Ln;Ro();let l=Ai(i),d=Ai(a),u=l[s],c=d[s],m=f=>{let h=Be({...f}),g=cn(h);if(!g.ok)throw new Error(g.error);return h};try{if(n==="global"){if(u!==void 0){let g=m(u);return d[s]=g,delete l[s],$o(),{ok:!0,kind:"moved",target:"global",name:s}}if(c!==void 0)return{ok:!0,kind:"noop",message:`Recipe "${s}" is already gateway-shared.`};let h=je(e,r,s);return h?.source==="builtin"||h?.source==="global"?{ok:!1,error:`Recipe "${s}" is built-in or from host recipes.json \u2014 not moved via /run set. Edit recipes.json or use a different name.`}:{ok:!1,error:`No user recipe "${s}" in this chat to promote. Add with /run add ${s} \u2026 or demote from global with /run set -p ${s}.`}}if(c!==void 0){let h=m(c);return l[s]=h,delete d[s],$o(),{ok:!0,kind:"moved",target:"chat",name:s}}if(u!==void 0)return{ok:!0,kind:"noop",message:`Recipe "${s}" is already stored for this chat only.`};let f=je(e,r,s);return f?.source==="builtin"||f?.source==="global"?{ok:!1,error:`Recipe "${s}" is built-in or from host recipes.json \u2014 not moved via /run set.`}:{ok:!1,error:`No gateway-shared user recipe "${s}" to demote. Add with /run add --global ${s} \u2026 or promote from this chat with /run set -g ${s}.`}}catch(f){return{ok:!1,error:String(f)}}}function yu(e,t){let n=[...du(e,t).values()],r=n.filter(a=>a.featured).sort((a,l)=>a.name.localeCompare(l.name)),o=n.filter(a=>a.source==="peer").sort((a,l)=>a.name.localeCompare(l.name)),s=n.filter(a=>a.source==="shared").sort((a,l)=>a.name.localeCompare(l.name)),i=n.filter(a=>!a.featured&&a.source!=="peer"&&a.source!=="shared").sort((a,l)=>a.name.localeCompare(l.name));return{featured:r,shared:s,yours:o,more:i}}function To(e){let t=dh.randomBytes(4).toString("hex");return`r-${e.replace(/[^a-zA-Z0-9_-]/g,"").slice(0,12)}-${t}`.slice(0,32)}function wu(e,t){let n=[`runbook: ${e}`];for(let o of t){let s=o.skipped?"skip":o.exitCode===0?"ok":"FAIL",i=o.label||`step ${o.index+1}`;if(o.skipped)n.push(`[${s}] ${i}`);else{n.push(`[${s}] ${i}${o.timedOut?" (timeout)":o.exitCode!==0?` (exit ${o.exitCode})`:""}`);let a=o.output.trim();if(a){let l=a.length>300?a.slice(0,300)+"\u2026":a;n.push(l)}}}let r=t.every(o=>o.skipped||o.exitCode===0);return n.push(r?"All steps completed successfully.":"Runbook stopped on failure."),n.join(`
161
+ `)}G();var bu={enter:"\r",cr:"\r",lf:`
162
+ `,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 Sh(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+=`
163
+ `,n++;continue}if(o==="t"){t+=" ",n++;continue}if(o==="\\"){t+="\\",n++;continue}}t+=r}return t}function vh(e){let t=e.trim();if(!t)return"";if(t.startsWith("\\"))return Sh(t);let n=t.toLowerCase();if(bu[n])return bu[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 Eo(e){let t=e.split(",").map(n=>n.trim()).filter(Boolean);return t.length===0?"":t.map(n=>vh(n)).join("")}var br="\x1B";function Po(e){let t=e;return t=t.replace(new RegExp(`${br}\\[[\\d;?]*[ -/]*[@-~]`,"g"),""),t=t.replace(new RegExp(`${br}\\][^${br}\\u0007]*(?:\\u0007|${br}\\\\)`,"g"),""),t=t.replace(new RegExp(`${br}[@-Z\\\\-_]`,"g"),""),t=t.replace(/\u0007/g,""),t}function xh(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 $h(e,t){return`${e}${t}`}var Ao=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=$h(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=Po(l)),!l.trim()))return;let d=o.appsMaxFlushBytes;if(l.length>d){let f=l.slice(d);l=l.slice(0,d)+`
164
+ [\u2026+${f.length} chars; /apps since ${r} to read more]`,this.pending.set(t,(this.pending.get(t)??"")+f)}let c=(this.opts.getRunningCount(n)>1?`[${r}] `:"")+l,m=xh(c,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))}}}};var Io=4096,Rh=/\[sudo\]\s+password\s+for\s+/i,Ch=/passphrase\s+for\s+/i,Mh=/(?:^|\n)\s*(?:Password|password):\s*$/;function Oo(e){let n=Po(e).replace(/\r/g,"").slice(-512);return!!(Rh.test(n)||Ch.test(n)||Mh.test(n))}G();import Th from"node:fs";import Eh from"node:path";import*as ku from"node-pty";var Ph=1024*1024,Lo=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>Ph&&this.ringChunks.length>0;){let n=this.ringChunks.shift();this.ringBytes-=n.length}}static start(t){j(sr(t.peerKey));let n=Eh.join(sr(t.peerKey),`${t.name}.log`),r=Th.createWriteStream(n,{flags:"a",mode:384}),o={...process.env,TERM:"xterm-256color",COLORTERM:"truecolor",...t.cwd?{PWD:t.cwd}:{},...t.extraEnv??{}},s=ku.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.onOutput?.(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}recentOutputTail(t=4096){if(this.ringBytes===0)return"";let n=Math.max(1,t),r=[],o=Math.min(n,this.ringBytes);for(let s=this.ringChunks.length-1;s>=0&&o>0;s--){let i=this.ringChunks[s];i.length<=o?(r.unshift(i),o-=i.length):(r.unshift(i.subarray(i.length-o)),o=0)}return Buffer.concat(r).toString("utf8")}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()}};function Hi(e){return new Promise(t=>setTimeout(t,e))}async function No(e,t,n){let r=n.appsSubmitDelayMs,o=n.appsClearInputDelayMs,s=n.appsClearInput!==!1,a=n.appsSkipClearOnPasswordPrompt!==!1&&Oo(e.recentOutputTail(Io)),l=s&&!a?Eo(n.appsClearInputSequence||"^A,^K"):"",u=t.replace(/\r\n/g,`
165
165
  `).replace(/\r/g,"").split(`
166
- `);l&&(o>0&&await Wi(o),e.write(l));for(let c of u)c.length>0&&e.write(c),r>0&&await Wi(r),e.write("\r"),l&&(o>0&&await Wi(o),e.write(l))}var Ch=/^[a-zA-Z0-9_-]{1,32}$/;function We(e){return Ch.test(e)?null:"Session name must be 1\u201332 chars: letters, digits, _ or -."}function Pe(e,t){return`${e}:${t}`}function Mh(e){let t=e.lastIndexOf(":");return t<=0?null:{peerKey:e.slice(0,t),name:e.slice(t+1)}}function Th(e,t){return e===0&&(t===0||t==null)}function Eh(e,t){try{let r=Gt.readFileSync(e,"utf8").split(/\r?\n/);return r.slice(Math.max(0,r.length-t)).join(`
167
- `).trimEnd()}catch{return""}}var Jt=class{constructor(t,n){this.getCfg=t;this.send=n,this.router=new Mo({getCfg:()=>this.getCfg(),send:n,isMuted:(r,o)=>this.muted.has(Pe(r,o)),isRaw:(r,o)=>this.rawMode.has(Pe(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;passwordHintSent=new Set;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 Rh.join(nr(t),`${n}.log`)}getSession(t,n){return this.sessions.get(Pe(t,n))}removeSessionRecord(t,n){let r=Pe(t,n);this.sessions.delete(r),this.passwordHintSent.delete(r),this.focus.get(t)===n&&this.focus.set(t,null)}onSessionOutput(t,n){let r=Pe(t,n),o=this.getSession(t,n);if(!o?.alive)return;let s=this.getCfg(),i=o.recentOutputTail(To);if(!Eo(i)){this.passwordHintSent.delete(r);return}if(s.appsPasswordPromptHint===!1||this.passwordHintSent.has(r))return;this.passwordHintSent.add(r);let l=`Password prompt in app "${n}" \u2014 reply with your password only (no line-clear keys). It is not echoed in chat but is written to the session log.`;this.send(t,l).catch(()=>{})}async writeFocusedLine(t,n){let r=this.focus.get(t);if(!r)return!1;let o=this.getSession(t,r);return o?.alive?(await Ao(o,n,this.getCfg()),!0):!1}async writeNamedLine(t,n,r){let o=We(n);if(o)return o;let s=this.getSession(t,n);return s?.alive?(await Ao(s,r,this.getCfg()),null):`No app session "${n}". /apps list`}enqueueQueuedRun(t,n,r){let o=this.runQueue.get(t)??[];o.push(n),this.runQueue.set(t,o);let s=o.length,i=this.runQueuePaused.get(t)??!1,a=this.activeQueuedRunHead.get(t)??null,l=a?.sessionName??null;if(l?this.getSession(t,l)?.alive??!1:!1){let u=a?.recipeLabel?` (recipe: ${a.recipeLabel})`:"",c=s-1,m=c>0?`${c} other job(s) in line before this one.`:"Next in line after the active run finishes.";return`Queued "${n.recipeLabel}" (wait slot ${s} behind active ${l}${u}). ${m}`}return i?`Queued "${n.recipeLabel}" (position ${s}). Run queue is paused \u2014 /run queue resume to continue.`:this.drainNextQueuedRun(t,r)}resumeRunQueue(t,n){this.runQueuePaused.set(t,!1);let r=this.activeQueuedRunHead.get(t)??null,o=r?.sessionName??null;if(o?this.getSession(t,o)?.alive??!1:!1){let a=r?.recipeLabel?` (recipe: ${r.recipeLabel})`:"";return`Run queue: session "${o}"${a} is still running. Wait for exit code=0 and signal=0 before the next starts.`}return(this.runQueue.get(t)??[]).length===0?"Run queue is empty.":(this.activeQueuedRunHead.set(t,null),this.drainNextQueuedRun(t,n))}runQueueStatus(t){let n=this.runQueue.get(t)??[],r=this.activeQueuedRunHead.get(t)??null,o=r?.sessionName??null,s=this.runQueuePaused.get(t)??!1,i=o?this.getSession(t,o)?.alive??!1:!1,a=["Run queue (this chat)"];if(o&&i){let l=r?.recipeLabel?` \xB7 recipe: ${r.recipeLabel}`:"";a.push(`Active: ${o}${l}`)}else o?a.push(`Active: ${o} (exiting or stale)`):a.push("Active: (none)");if(a.push(`Pending: ${n.length} (waiting only \u2014 not counting the active run above)`),a.push(`Paused: ${s}`),n.length>0){a.push("Waiting (FIFO order):");for(let d=0;d<Math.min(n.length,20);d++)a.push(`${d+1}) ${n[d].recipeLabel}`);n.length>20&&a.push(`\u2026 and ${n.length-20} more`)}return a.push("Next auto-starts only after exit code=0 and signal=0."),a.push("Commands: /run queue resume"),a.join(`
168
- `)}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=$o(o.recipeLabel),i=this.runQueue.get(t)?.length??0,a=this.start(t,s,o.command,n,o.extraEnv);if(!a.includes("started and attached"))return r.unshift(o),this.runQueue.set(t,r),this.runQueuePaused.set(t,!0),this.activeQueuedRunHead.set(t,null),`${a}
166
+ `);l&&(o>0&&await Hi(o),e.write(l));for(let c of u)c.length>0&&e.write(c),r>0&&await Hi(r),e.write("\r"),l&&(o>0&&await Hi(o),e.write(l))}var Ih=/^[a-zA-Z0-9_-]{1,32}$/;function We(e){return Ih.test(e)?null:"Session name must be 1\u201332 chars: letters, digits, _ or -."}function Pe(e,t){return`${e}:${t}`}function Oh(e){let t=e.lastIndexOf(":");return t<=0?null:{peerKey:e.slice(0,t),name:e.slice(t+1)}}function Lh(e,t){return e===0&&(t===0||t==null)}function Nh(e,t){try{let r=Kt.readFileSync(e,"utf8").split(/\r?\n/);return r.slice(Math.max(0,r.length-t)).join(`
167
+ `).trimEnd()}catch{return""}}var zt=class{constructor(t,n){this.getCfg=t;this.send=n,this.router=new Ao({getCfg:()=>this.getCfg(),send:n,isMuted:(r,o)=>this.muted.has(Pe(r,o)),isRaw:(r,o)=>this.rawMode.has(Pe(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;passwordHintSent=new Set;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 Ah.join(sr(t),`${n}.log`)}getSession(t,n){return this.sessions.get(Pe(t,n))}removeSessionRecord(t,n){let r=Pe(t,n);this.sessions.delete(r),this.passwordHintSent.delete(r),this.focus.get(t)===n&&this.focus.set(t,null)}onSessionOutput(t,n){let r=Pe(t,n),o=this.getSession(t,n);if(!o?.alive)return;let s=this.getCfg(),i=o.recentOutputTail(Io);if(!Oo(i)){this.passwordHintSent.delete(r);return}if(s.appsPasswordPromptHint===!1||this.passwordHintSent.has(r))return;this.passwordHintSent.add(r);let l=`Password prompt in app "${n}" \u2014 reply with your password only (no line-clear keys). It is not echoed in chat but is written to the session log.`;this.send(t,l).catch(()=>{})}async writeFocusedLine(t,n){let r=this.focus.get(t);if(!r)return!1;let o=this.getSession(t,r);return o?.alive?(await No(o,n,this.getCfg()),!0):!1}async writeNamedLine(t,n,r){let o=We(n);if(o)return o;let s=this.getSession(t,n);return s?.alive?(await No(s,r,this.getCfg()),null):`No app session "${n}". /apps list`}enqueueQueuedRun(t,n,r){let o=this.runQueue.get(t)??[];o.push(n),this.runQueue.set(t,o);let s=o.length,i=this.runQueuePaused.get(t)??!1,a=this.activeQueuedRunHead.get(t)??null,l=a?.sessionName??null;if(l?this.getSession(t,l)?.alive??!1:!1){let u=a?.recipeLabel?` (recipe: ${a.recipeLabel})`:"",c=s-1,m=c>0?`${c} other job(s) in line before this one.`:"Next in line after the active run finishes.";return`Queued "${n.recipeLabel}" (wait slot ${s} behind active ${l}${u}). ${m}`}return i?`Queued "${n.recipeLabel}" (position ${s}). Run queue is paused \u2014 /run queue resume to continue.`:this.drainNextQueuedRun(t,r)}resumeRunQueue(t,n){this.runQueuePaused.set(t,!1);let r=this.activeQueuedRunHead.get(t)??null,o=r?.sessionName??null;if(o?this.getSession(t,o)?.alive??!1:!1){let a=r?.recipeLabel?` (recipe: ${r.recipeLabel})`:"";return`Run queue: session "${o}"${a} is still running. Wait for exit code=0 and signal=0 before the next starts.`}return(this.runQueue.get(t)??[]).length===0?"Run queue is empty.":(this.activeQueuedRunHead.set(t,null),this.drainNextQueuedRun(t,n))}runQueueStatus(t){let n=this.runQueue.get(t)??[],r=this.activeQueuedRunHead.get(t)??null,o=r?.sessionName??null,s=this.runQueuePaused.get(t)??!1,i=o?this.getSession(t,o)?.alive??!1:!1,a=["Run queue (this chat)"];if(o&&i){let l=r?.recipeLabel?` \xB7 recipe: ${r.recipeLabel}`:"";a.push(`Active: ${o}${l}`)}else o?a.push(`Active: ${o} (exiting or stale)`):a.push("Active: (none)");if(a.push(`Pending: ${n.length} (waiting only \u2014 not counting the active run above)`),a.push(`Paused: ${s}`),n.length>0){a.push("Waiting (FIFO order):");for(let d=0;d<Math.min(n.length,20);d++)a.push(`${d+1}) ${n[d].recipeLabel}`);n.length>20&&a.push(`\u2026 and ${n.length-20} more`)}return a.push("Next auto-starts only after exit code=0 and signal=0."),a.push("Commands: /run queue resume"),a.join(`
168
+ `)}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=To(o.recipeLabel),i=this.runQueue.get(t)?.length??0,a=this.start(t,s,o.command,n,o.extraEnv);if(!a.includes("started and attached"))return r.unshift(o),this.runQueue.set(t,r),this.runQueuePaused.set(t,!0),this.activeQueuedRunHead.set(t,null),`${a}
169
169
  Run queue paused; fix the issue, then /run queue resume.`;this.activeQueuedRunHead.set(t,{sessionName:s,recipeLabel:o.recipeLabel});let d=i>0?`
170
170
  Run queue: started head above; ${i} job(s) still waiting in FIFO.`:`
171
171
  Run queue: started head above; no further queued jobs.`;return`${a}${d}`}start(t,n,r,o,s){let i=We(n);if(i)return i;if(!r.trim())return`Usage: /apps start <name> <command\u2026>
172
- Example: /apps start sh bash`;if(this.sessions.has(Pe(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.`;te();let a=oe(t).cwd,l={...process.env,TERM:"xterm-256color",COLORTERM:"truecolor",...a?{PWD:a}:{},...s??{}},d;try{d=Po.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,onOutput:()=>{this.onSessionOutput(t,n)},onExit:u=>{this.handleSessionExit(t,n,u)}})}catch(u){return`App spawn failed: ${String(u)}
172
+ Example: /apps start sh bash`;if(this.sessions.has(Pe(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.`;te();let a=oe(t).cwd,l={...process.env,TERM:"xterm-256color",COLORTERM:"truecolor",...a?{PWD:a}:{},...s??{}},d;try{d=Lo.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,onOutput:()=>{this.onSessionOutput(t,n)},onExit:u=>{this.handleSessionExit(t,n,u)}})}catch(u){return`App spawn failed: ${String(u)}
173
173
  If install skipped native builds: pnpm approve-builds && pnpm install (needs @whiskeysockets/baileys, sharp, protobufjs, esbuild, node-pty).`}return this.sessions.set(Pe(t,n),d),this.focus.set(t,n),`App "${n}" started and attached.
174
174
  [cwd: ${a}]
175
- Plain DMs go to this session until /apps detach. Use >othername text for another session.`}async handleSessionExit(t,n,r){let o=Pe(t,n);if(!this.sessions.has(o))return;let s=this.activeQueuedRunHead.get(t)?.sessionName===n,i=this.focus.get(t)===n;this.sessions.delete(o),i&&this.focus.set(t,null),this.muted.delete(o),this.rawMode.delete(o),this.passwordHintSent.delete(o);let a=r.signal!=null?` signal=${r.signal}`:"",l=i?" (detached)":"",d=`[${n}] exited code=${r.exitCode}${a}${l}`;try{await this.send(t,d)}catch{}if(!s)return;this.activeQueuedRunHead.set(t,null);let u=this.getCfg(),c=Th(r.exitCode,r.signal),m=this.runQueue.get(t)?.length??0;if(c){if(this.runQueuePaused.set(t,!1),m>0){let f=this.drainNextQueuedRun(t,u);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=We(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=Mh(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)"}
175
+ Plain DMs go to this session until /apps detach. Use >othername text for another session.`}async handleSessionExit(t,n,r){let o=Pe(t,n);if(!this.sessions.has(o))return;let s=this.activeQueuedRunHead.get(t)?.sessionName===n,i=this.focus.get(t)===n;this.sessions.delete(o),i&&this.focus.set(t,null),this.muted.delete(o),this.rawMode.delete(o),this.passwordHintSent.delete(o);let a=r.signal!=null?` signal=${r.signal}`:"",l=i?" (detached)":"",d=`[${n}] exited code=${r.exitCode}${a}${l}`;try{await this.send(t,d)}catch{}if(!s)return;this.activeQueuedRunHead.set(t,null);let u=this.getCfg(),c=Lh(r.exitCode,r.signal),m=this.runQueue.get(t)?.length??0;if(c){if(this.runQueuePaused.set(t,!1),m>0){let f=this.drainNextQueuedRun(t,u);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=We(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=Oh(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)"}
176
176
  App sessions:
177
177
  ${n.join(`
178
- `)}`}getSessionCommand(t,n){if(We(n))return null;let o=this.getSession(t,n);return o?.command?.trim()?o.command.trim():null}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=We(o);if(s)return s;let i=this.getSession(t,o),a=this.logPath(t,o),l=0;try{l=Gt.statSync(a).size}catch{}let d=this.muted.has(Pe(t,o)),u=this.rawMode.has(Pe(t,o));return[`session: ${o}`,`alive: ${i?.alive??!1}`,`cmd: ${i?.command??"(unknown)"}`,`cwd: ${i?.cwd??"(unknown)"}`,`env keys: ${i?.envKeysCount??"?"}`,`terminal size: ${r.appsCols}x${r.appsRows}`,`ring bytes (approx): ${i?.ringByteCount??0}`,`log: ${a} (${l} bytes)`,`mute outbound: ${d}`,`raw outbound: ${u}`].join(`
178
+ `)}`}getSessionCommand(t,n){if(We(n))return null;let o=this.getSession(t,n);return o?.command?.trim()?o.command.trim():null}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=We(o);if(s)return s;let i=this.getSession(t,o),a=this.logPath(t,o),l=0;try{l=Kt.statSync(a).size}catch{}let d=this.muted.has(Pe(t,o)),u=this.rawMode.has(Pe(t,o));return[`session: ${o}`,`alive: ${i?.alive??!1}`,`cmd: ${i?.command??"(unknown)"}`,`cwd: ${i?.cwd??"(unknown)"}`,`env keys: ${i?.envKeysCount??"?"}`,`terminal size: ${r.appsCols}x${r.appsRows}`,`ring bytes (approx): ${i?.ringByteCount??0}`,`log: ${a} (${l} bytes)`,`mute outbound: ${d}`,`raw outbound: ${u}`].join(`
179
179
  `)}async sendText(t,n,r){let o=We(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,`
180
- `);return await Ao(s,i,this.getCfg()),`Sent to "${n}" (${i.length} chars + Enter per line).`}sendKey(t,n,r){let o=We(n);if(o)return o;let s=this.getSession(t,n);if(!s?.alive)return`No session "${n}".`;let i=Ro(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=We(n);if(o)return o;let s=this.logPath(t,n);if(!Gt.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 Eh(s,a)||"(empty log)"}readSince(t,n,r){let o=this.logPath(t,n);try{let i=Gt.statSync(o).size,a=Gt.openSync(o,"r");try{let l=Math.min(r,i),d=i-l,u=Buffer.alloc(d);return d>0&&Gt.readSync(a,u,0,d,l),{text:u.toString("utf8"),nextOffset:i}}finally{Gt.closeSync(a)}}catch{return{text:"",nextOffset:r}}}mute(t,n){let r=We(n);return r||(this.muted.add(Pe(t,n)),`Muted chat output for "${n}" (log still grows). /apps unmute ${n}`)}unmute(t,n){let r=We(n);return r||(this.muted.delete(Pe(t,n)),`Unmuted "${n}".`)}setRaw(t,n,r){let o=We(n);if(o)return o;let s=Pe(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=We(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=We(n);if(r)return r;let o=this.getSession(t,n);if(!o?.alive)return`No running session "${n}".`;o.kill("SIGTERM");let s=Pe(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=We(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=We(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(Pe(t,n)),this.rawMode.delete(Pe(t,n)),this.passwordHintSent.delete(Pe(t,n));let s=this.logPath(t,n);try{Gt.rmSync(s,{force:!0})}catch{}return`Removed "${n}" metadata and log.`}};qe();be();import wr from"node:fs";import br from"node:path";import hu from"node:fs";import gu from"node:path";var Ph=new Set(["jpg","jpeg","png","gif","webp","bmp","tif","tiff","heic","avif"]),Ah=new Set(["mp4","mov","webm","mkv","avi","m4v","3gp"]),Ih=new Set(["mp3","ogg","opus","wav","m4a","flac","aac","wma"]),Io={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 Oh(e){let t=e.replace(/^\./,"").toLowerCase();return Ph.has(t)?{category:"image",mimetype:Io[t]??"image/jpeg"}:Ah.has(t)?{category:"video",mimetype:Io[t]??"video/mp4"}:Ih.has(t)?{category:"audio",mimetype:Io[t]??"audio/mpeg"}:{category:"document",mimetype:Io[t]??"application/octet-stream"}}function Kt(e,t){let n;try{n=hu.realpathSync(e)}catch{return{error:"File not found or unreadable."}}let r;try{r=hu.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=gu.basename(n),s=gu.extname(n).slice(1),{category:i,mimetype:a}=Oh(s);return{absPath:n,category:i,mimetype:a,displayName:o}}import Lh from"node:os";import*as wu from"node-pty";function Nh(e,t){return e.length<=t?e:`${e.slice(0,t)}
181
- [...truncated]`}function _h(e){if(e===void 0||e===0)return null;let t=Lh.constants.signals;for(let[n,r]of Object.entries(t))if(r===e)return n;return null}function yu(e){try{process.platform==="win32"?e.kill():e.kill("SIGTERM")}catch{e.kill()}}function sn(e,t,n){return new Promise(r=>{let o=Date.now(),s=!1,i="",a=n.cwd,l=null,d=!1,u=null,c=null,m,f=y=>{if(d)return;d=!0,m!==void 0&&clearTimeout(m),u?.dispose(),u=null,r(y);let b=c;c=null,queueMicrotask(()=>b?.dispose())},g={...n.env??process.env,TERM:"xterm-256color",COLORTERM:"truecolor",...a?{PWD:a}:{}};try{l=wu.spawn(e,["-c",t],{name:"xterm-256color",cols:120,rows:40,cwd:a,env:g})}catch(y){f({code:null,stdout:"",stderr:String(y),durationMs:Date.now()-o,timedOut:!1,signal:null});return}m=setTimeout(()=>{s=!0,l&&yu(l)},n.timeoutMs),u=l.onData(y=>{i+=y,i.length>n.maxBytes&&(i=Nh(i,n.maxBytes),l&&yu(l))}),c=l.onExit(y=>{f({code:y.exitCode,stdout:i,stderr:"",durationMs:Date.now()-o,timedOut:s,signal:_h(y.signal)})})})}function yr(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 Fh(e,t,n){let r=e;for(let o=0;o<14;o++){let s=yr(r,t,n),i=new Date(s).getDay();if(i>=1&&i<=5)return s;r=s}return yr(r,t,n)}function Wh(e,t,n,r){let o=e;for(let s=0;s<370;s++){let i=yr(o,n,r);if(new Date(i).getDay()===t)return i;o=i}return yr(o,n,r)}function Uh(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 Dh(e,t){return e.kind==="ondemand"||e.kind==="heartbeat"?Number.POSITIVE_INFINITY:e.kind==="daily"?yr(t,e.hour,e.minute):e.kind==="weekdays"?Fh(t,e.hour,e.minute):e.kind==="hourly"?Uh(t,e.minute):Wh(t,e.weekday,e.hour,e.minute)}function Su(e,t,n,r,o=32){if(e.kind==="ondemand"||e.kind==="heartbeat")return[];let s=t??n-1,i=[],a=s;for(;i.length<o;){let l=Dh(e,a);if(l>r)break;i.push(l),a=l}return i}var Hh={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 Ui(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 Bh(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 Oo(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=Ui(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=Ui(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=Bh(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=Hh[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=Ui(e[2]);return o?{ok:!0,schedule:{kind:"weekly",weekday:r,hour:o.hour,minute:o.minute}}:{ok:!1,error:"weekly needs HH:MM (24h) after weekday."}}if(t==="heartbeat"){if(e.length<2||e.length>3)return{ok:!1,error:"Usage: heartbeat <interval> [grace] \u2014 e.g. heartbeat 1h, heartbeat 30m 10m"};let n=bu(e[1]);if(n===null||n<6e4)return{ok:!1,error:"heartbeat interval must be >= 1m. Use 5m, 1h, 2h, 1d, etc."};let r=Math.floor(n*.5);if(e.length===3){let o=bu(e[2]);if(o===null||o<3e4)return{ok:!1,error:"heartbeat grace must be >= 30s. Use 1m, 5m, etc."};r=o}return{ok:!0,schedule:{kind:"heartbeat",intervalMs:n,graceMs:r}}}return{ok:!1,error:`Unknown schedule kind "${t}".`}}function bu(e){let t=e.trim().toLowerCase();if(!t)return null;let n=t.match(/^(?:(\d+)d)?(?:(\d+)h)?(?:(\d+)m)?(?:(\d+)s)?$/);if(!n||t===""||!n[1]&&!n[2]&&!n[3]&&!n[4])return null;let r=n[1]?Number(n[1]):0,o=n[2]?Number(n[2]):0,s=n[3]?Number(n[3]):0,i=n[4]?Number(n[4]):0;return((r*24+o)*60+s)*6e4+i*1e3}function ku(e){let t=Math.floor(e/1e3),n=Math.floor(t/86400),r=Math.floor(t%86400/3600),o=Math.floor(t%3600/60),s=[];return n>0&&s.push(`${n}d`),r>0&&s.push(`${r}h`),o>0&&s.push(`${o}m`),s.length>0?s.join(""):`${t}s`}function In(e){let t=r=>r<10?`0${r}`:String(r),n=(r,o)=>`${t(r)}:${t(o)}`;switch(e.kind){case"ondemand":return"ondemand";case"daily":return`daily ${n(e.hour,e.minute)}`;case"weekdays":return`weekdays ${n(e.hour,e.minute)}`;case"hourly":return e.minute===0?"hourly":`hourly :${t(e.minute)}`;case"weekly":return`weekly ${["sun","mon","tue","wed","thu","fri","sat"][e.weekday]??e.weekday} ${n(e.hour,e.minute)}`;case"heartbeat":return`heartbeat every ${ku(e.intervalMs)} grace ${ku(e.graceMs)}`}}G();be();import jh from"better-sqlite3";var vu=2,zt=null;function xu(){j(ut);let e=new jh(Va);e.pragma("journal_mode = WAL"),e.exec(`
180
+ `);return await No(s,i,this.getCfg()),`Sent to "${n}" (${i.length} chars + Enter per line).`}sendKey(t,n,r){let o=We(n);if(o)return o;let s=this.getSession(t,n);if(!s?.alive)return`No session "${n}".`;let i=Eo(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=We(n);if(o)return o;let s=this.logPath(t,n);if(!Kt.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 Nh(s,a)||"(empty log)"}readSince(t,n,r){let o=this.logPath(t,n);try{let i=Kt.statSync(o).size,a=Kt.openSync(o,"r");try{let l=Math.min(r,i),d=i-l,u=Buffer.alloc(d);return d>0&&Kt.readSync(a,u,0,d,l),{text:u.toString("utf8"),nextOffset:i}}finally{Kt.closeSync(a)}}catch{return{text:"",nextOffset:r}}}mute(t,n){let r=We(n);return r||(this.muted.add(Pe(t,n)),`Muted chat output for "${n}" (log still grows). /apps unmute ${n}`)}unmute(t,n){let r=We(n);return r||(this.muted.delete(Pe(t,n)),`Unmuted "${n}".`)}setRaw(t,n,r){let o=We(n);if(o)return o;let s=Pe(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=We(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=We(n);if(r)return r;let o=this.getSession(t,n);if(!o?.alive)return`No running session "${n}".`;o.kill("SIGTERM");let s=Pe(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=We(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=We(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(Pe(t,n)),this.rawMode.delete(Pe(t,n)),this.passwordHintSent.delete(Pe(t,n));let s=this.logPath(t,n);try{Kt.rmSync(s,{force:!0})}catch{}return`Removed "${n}" metadata and log.`}};qe();be();import Sr from"node:fs";import vr from"node:path";import Su from"node:fs";import vu from"node:path";var _h=new Set(["jpg","jpeg","png","gif","webp","bmp","tif","tiff","heic","avif"]),Fh=new Set(["mp4","mov","webm","mkv","avi","m4v","3gp"]),Wh=new Set(["mp3","ogg","opus","wav","m4a","flac","aac","wma"]),_o={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 Uh(e){let t=e.replace(/^\./,"").toLowerCase();return _h.has(t)?{category:"image",mimetype:_o[t]??"image/jpeg"}:Fh.has(t)?{category:"video",mimetype:_o[t]??"video/mp4"}:Wh.has(t)?{category:"audio",mimetype:_o[t]??"audio/mpeg"}:{category:"document",mimetype:_o[t]??"application/octet-stream"}}function qt(e,t){let n;try{n=Su.realpathSync(e)}catch{return{error:"File not found or unreadable."}}let r;try{r=Su.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=vu.basename(n),s=vu.extname(n).slice(1),{category:i,mimetype:a}=Uh(s);return{absPath:n,category:i,mimetype:a,displayName:o}}import Dh from"node:os";import*as $u from"node-pty";function Hh(e,t){return e.length<=t?e:`${e.slice(0,t)}
181
+ [...truncated]`}function Bh(e){if(e===void 0||e===0)return null;let t=Dh.constants.signals;for(let[n,r]of Object.entries(t))if(r===e)return n;return null}function xu(e){try{process.platform==="win32"?e.kill():e.kill("SIGTERM")}catch{e.kill()}}function un(e,t,n){return new Promise(r=>{let o=Date.now(),s=!1,i="",a=n.cwd,l=null,d=!1,u=null,c=null,m,f=y=>{if(d)return;d=!0,m!==void 0&&clearTimeout(m),u?.dispose(),u=null,r(y);let b=c;c=null,queueMicrotask(()=>b?.dispose())},g={...n.env??process.env,TERM:"xterm-256color",COLORTERM:"truecolor",...a?{PWD:a}:{}};try{l=$u.spawn(e,["-c",t],{name:"xterm-256color",cols:120,rows:40,cwd:a,env:g})}catch(y){f({code:null,stdout:"",stderr:String(y),durationMs:Date.now()-o,timedOut:!1,signal:null});return}m=setTimeout(()=>{s=!0,l&&xu(l)},n.timeoutMs),u=l.onData(y=>{i+=y,i.length>n.maxBytes&&(i=Hh(i,n.maxBytes),l&&xu(l))}),c=l.onExit(y=>{f({code:y.exitCode,stdout:i,stderr:"",durationMs:Date.now()-o,timedOut:s,signal:Bh(y.signal)})})})}function kr(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 jh(e,t,n){let r=e;for(let o=0;o<14;o++){let s=kr(r,t,n),i=new Date(s).getDay();if(i>=1&&i<=5)return s;r=s}return kr(r,t,n)}function Gh(e,t,n,r){let o=e;for(let s=0;s<370;s++){let i=kr(o,n,r);if(new Date(i).getDay()===t)return i;o=i}return kr(o,n,r)}function Jh(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 Kh(e,t){return e.kind==="ondemand"||e.kind==="heartbeat"?Number.POSITIVE_INFINITY:e.kind==="daily"?kr(t,e.hour,e.minute):e.kind==="weekdays"?jh(t,e.hour,e.minute):e.kind==="hourly"?Jh(t,e.minute):Gh(t,e.weekday,e.hour,e.minute)}function Mu(e,t,n,r,o=32){if(e.kind==="ondemand"||e.kind==="heartbeat")return[];let s=t??n-1,i=[],a=s;for(;i.length<o;){let l=Kh(e,a);if(l>r)break;i.push(l),a=l}return i}var zh={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 Bi(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 qh(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 Fo(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=Bi(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=Bi(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=qh(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=zh[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=Bi(e[2]);return o?{ok:!0,schedule:{kind:"weekly",weekday:r,hour:o.hour,minute:o.minute}}:{ok:!1,error:"weekly needs HH:MM (24h) after weekday."}}if(t==="heartbeat"){if(e.length<2||e.length>3)return{ok:!1,error:"Usage: heartbeat <interval> [grace] \u2014 e.g. heartbeat 1h, heartbeat 30m 10m"};let n=Ru(e[1]);if(n===null||n<6e4)return{ok:!1,error:"heartbeat interval must be >= 1m. Use 5m, 1h, 2h, 1d, etc."};let r=Math.floor(n*.5);if(e.length===3){let o=Ru(e[2]);if(o===null||o<3e4)return{ok:!1,error:"heartbeat grace must be >= 30s. Use 1m, 5m, etc."};r=o}return{ok:!0,schedule:{kind:"heartbeat",intervalMs:n,graceMs:r}}}return{ok:!1,error:`Unknown schedule kind "${t}".`}}function Ru(e){let t=e.trim().toLowerCase();if(!t)return null;let n=t.match(/^(?:(\d+)d)?(?:(\d+)h)?(?:(\d+)m)?(?:(\d+)s)?$/);if(!n||t===""||!n[1]&&!n[2]&&!n[3]&&!n[4])return null;let r=n[1]?Number(n[1]):0,o=n[2]?Number(n[2]):0,s=n[3]?Number(n[3]):0,i=n[4]?Number(n[4]):0;return((r*24+o)*60+s)*6e4+i*1e3}function Cu(e){let t=Math.floor(e/1e3),n=Math.floor(t/86400),r=Math.floor(t%86400/3600),o=Math.floor(t%3600/60),s=[];return n>0&&s.push(`${n}d`),r>0&&s.push(`${r}h`),o>0&&s.push(`${o}m`),s.length>0?s.join(""):`${t}s`}function Fn(e){let t=r=>r<10?`0${r}`:String(r),n=(r,o)=>`${t(r)}:${t(o)}`;switch(e.kind){case"ondemand":return"ondemand";case"daily":return`daily ${n(e.hour,e.minute)}`;case"weekdays":return`weekdays ${n(e.hour,e.minute)}`;case"hourly":return e.minute===0?"hourly":`hourly :${t(e.minute)}`;case"weekly":return`weekly ${["sun","mon","tue","wed","thu","fri","sat"][e.weekday]??e.weekday} ${n(e.hour,e.minute)}`;case"heartbeat":return`heartbeat every ${Cu(e.intervalMs)} grace ${Cu(e.graceMs)}`}}G();be();import Yh from"better-sqlite3";var Tu=2,Yt=null;function Eu(){j(dt);let e=new Yh(rl);e.pragma("journal_mode = WAL"),e.exec(`
182
182
  CREATE TABLE IF NOT EXISTS cowork_meta (
183
183
  key TEXT PRIMARY KEY,
184
184
  value TEXT NOT NULL
@@ -197,108 +197,108 @@ ${n.join(`
197
197
  last_ok INTEGER NOT NULL,
198
198
  updated_at_ms INTEGER NOT NULL
199
199
  );
200
- `);let t=e.prepare("SELECT value FROM cowork_meta WHERE key = 'schema_version'").get(),n=t?Number(t.value):0;return n<vu&&(n<2&&e.exec(`
200
+ `);let t=e.prepare("SELECT value FROM cowork_meta WHERE key = 'schema_version'").get(),n=t?Number(t.value):0;return n<Tu&&(n<2&&e.exec(`
201
201
  CREATE TABLE IF NOT EXISTS cowork_task_state (
202
202
  task_id TEXT PRIMARY KEY,
203
203
  last_ok INTEGER NOT NULL,
204
204
  updated_at_ms INTEGER NOT NULL
205
205
  );
206
- `),e.prepare("INSERT OR REPLACE INTO cowork_meta (key, value) VALUES ('schema_version', ?)").run(String(vu))),e}function an(e){zt||(zt=xu()),Jh(e)}function $u(){if(zt){try{zt.close()}catch{}zt=null}}function ln(){return zt||(zt=xu()),zt}function Gh(e){let n=ln().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 Lo(e){let t=Gh(e.id);return t??e.lastCompletedSlotMs}function No(e,t){if(t.length===0)return;let n=ln(),r=n.prepare(`
206
+ `),e.prepare("INSERT OR REPLACE INTO cowork_meta (key, value) VALUES ('schema_version', ?)").run(String(Tu))),e}function dn(e){Yt||(Yt=Eu()),Vh(e)}function Pu(){if(Yt){try{Yt.close()}catch{}Yt=null}}function pn(){return Yt||(Yt=Eu()),Yt}function Qh(e){let n=pn().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 Wo(e){let t=Qh(e.id);return t??e.lastCompletedSlotMs}function Uo(e,t){if(t.length===0)return;let n=pn(),r=n.prepare(`
207
207
  INSERT INTO cowork_slot_completion (task_id, slot_ms, kind, completed_at_ms, log_path)
208
208
  VALUES (?, ?, ?, ?, ?)
209
- `);n.transaction(()=>{for(let s of t)r.run(e,s.slotMs,s.kind,s.completedAtMs,s.logPath)})()}function Di(e){ln().prepare("INSERT OR REPLACE INTO cowork_task_state (task_id, last_ok, updated_at_ms) VALUES (?, 1, ?)").run(e,Date.now())}function _o(e){return ln().prepare("SELECT updated_at_ms FROM cowork_task_state WHERE task_id = ?").get(e)?.updated_at_ms??null}function Fo(e){let t=ln().prepare("SELECT last_ok FROM cowork_task_state WHERE task_id = ?").get(e);return t==null?null:t.last_ok===1}function Wo(e,t){ln().prepare("INSERT OR REPLACE INTO cowork_task_state (task_id, last_ok, updated_at_ms) VALUES (?, ?, ?)").run(e,t?1:0,Date.now())}function Jh(e){let n=ln().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{No(o.id,[{slotMs:o.lastCompletedSlotMs,kind:"migrated",logPath:null,completedAtMs:r}]),M.info({taskId:o.id,slotMs:o.lastCompletedSlotMs},"cowork seeded completion from tasks.json")}catch(i){M.warn({err:String(i),taskId:o.id},"cowork seed from tasks.json failed")}}function Hi(e,t){if(e.startsWith("tg:")){let n=e.slice(3);return Gr(t.telegramAllowFrom).has(n)}return rl(jr(t.allowFrom),e.replace(/^wa:/,""))}var Kh=8,Ru=12e4,Cu=10,zh=1e3;function qh(e){return e.toISOString().replace(/[:.]/g,"-")}function Yh(e){let t="";for(let n of e)n==="*"?t+="[^/\\\\]*":n==="?"?t+="[^/\\\\]":/[.+^${}()|[\]\\]/.test(n)?t+=`\\${n}`:t+=n;return new RegExp(`^${t}$`)}function Qh(e,t,n){let r=Ge(e,t),o=br.dirname(r),s=br.basename(r);if(o.includes("*")||o.includes("?"))return[];if(!s.includes("*")&&!s.includes("?")){try{let d=wr.statSync(r);if(d.isFile()&&d.mtimeMs>=n)return[r]}catch{}return[]}let i;try{i=wr.readdirSync(o)}catch{return[]}let a=Yh(s),l=[];for(let d of i){if(!a.test(d))continue;let u=br.join(o,d);try{let c=wr.statSync(u);c.isFile()&&c.mtimeMs>=n&&l.push(u)}catch{}}return l}function Mu(e,t,n){let r=Kt(e,t.fileSendMaxBytes);return"error"in r?{ok:!1,displayName:n??br.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 Tu(e,t,n,r){let o=oe(t.ownerPeerKey),s=t.cwd.trim()?t.cwd:o.cwd,i=Ge(t.outputDir,o.cwd);try{wr.mkdirSync(i,{recursive:!0,mode:448})}catch(k){M.warn({err:String(k),outDir:i},"cowork mkdir outputDir")}let a=n.slotMs!==null?new Date(n.slotMs).toLocaleString(void 0,{dateStyle:"short",timeStyle:"short"}):"on-demand",l=n.onDemand?"on-demand":n.catchUp?"catch-up":"scheduled",d=Date.now(),u=await sn(e.shell,t.command,{timeoutMs:e.syncTimeoutMs,maxBytes:e.syncMaxBytes,cwd:s}),c=Date.now()-d,m=`${qh(new Date)}-${t.id}-${l}.log`,f=br.join(i,m),g=[`cowork task=${t.name} id=${t.id}`,`slot=${a} kind=${l}`,`cwd=${s}`,`exit=${u.code} timedOut=${u.timedOut} durationMs=${c}`,"---",""].join(`
209
+ `);n.transaction(()=>{for(let s of t)r.run(e,s.slotMs,s.kind,s.completedAtMs,s.logPath)})()}function ji(e){pn().prepare("INSERT OR REPLACE INTO cowork_task_state (task_id, last_ok, updated_at_ms) VALUES (?, 1, ?)").run(e,Date.now())}function Do(e){return pn().prepare("SELECT updated_at_ms FROM cowork_task_state WHERE task_id = ?").get(e)?.updated_at_ms??null}function Ho(e){let t=pn().prepare("SELECT last_ok FROM cowork_task_state WHERE task_id = ?").get(e);return t==null?null:t.last_ok===1}function Bo(e,t){pn().prepare("INSERT OR REPLACE INTO cowork_task_state (task_id, last_ok, updated_at_ms) VALUES (?, ?, ?)").run(e,t?1:0,Date.now())}function Vh(e){let n=pn().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{Uo(o.id,[{slotMs:o.lastCompletedSlotMs,kind:"migrated",logPath:null,completedAtMs:r}]),M.info({taskId:o.id,slotMs:o.lastCompletedSlotMs},"cowork seeded completion from tasks.json")}catch(i){M.warn({err:String(i),taskId:o.id},"cowork seed from tasks.json failed")}}function Gi(e,t){if(e.startsWith("tg:")){let n=e.slice(3);return qr(t.telegramAllowFrom).has(n)}return cl(zr(t.allowFrom),e.replace(/^wa:/,""))}var Xh=8,Au=12e4,Iu=10,Zh=1e3;function eg(e){return e.toISOString().replace(/[:.]/g,"-")}function tg(e){let t="";for(let n of e)n==="*"?t+="[^/\\\\]*":n==="?"?t+="[^/\\\\]":/[.+^${}()|[\]\\]/.test(n)?t+=`\\${n}`:t+=n;return new RegExp(`^${t}$`)}function ng(e,t,n){let r=Ge(e,t),o=vr.dirname(r),s=vr.basename(r);if(o.includes("*")||o.includes("?"))return[];if(!s.includes("*")&&!s.includes("?")){try{let d=Sr.statSync(r);if(d.isFile()&&d.mtimeMs>=n)return[r]}catch{}return[]}let i;try{i=Sr.readdirSync(o)}catch{return[]}let a=tg(s),l=[];for(let d of i){if(!a.test(d))continue;let u=vr.join(o,d);try{let c=Sr.statSync(u);c.isFile()&&c.mtimeMs>=n&&l.push(u)}catch{}}return l}function Ou(e,t,n){let r=qt(e,t.fileSendMaxBytes);return"error"in r?{ok:!1,displayName:n??vr.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 Lu(e,t,n,r){let o=oe(t.ownerPeerKey),s=t.cwd.trim()?t.cwd:o.cwd,i=Ge(t.outputDir,o.cwd);try{Sr.mkdirSync(i,{recursive:!0,mode:448})}catch(k){M.warn({err:String(k),outDir:i},"cowork mkdir outputDir")}let a=n.slotMs!==null?new Date(n.slotMs).toLocaleString(void 0,{dateStyle:"short",timeStyle:"short"}):"on-demand",l=n.onDemand?"on-demand":n.catchUp?"catch-up":"scheduled",d=Date.now(),u=await un(e.shell,t.command,{timeoutMs:e.syncTimeoutMs,maxBytes:e.syncMaxBytes,cwd:s}),c=Date.now()-d,m=`${eg(new Date)}-${t.id}-${l}.log`,f=vr.join(i,m),g=[`cowork task=${t.name} id=${t.id}`,`slot=${a} kind=${l}`,`cwd=${s}`,`exit=${u.code} timedOut=${u.timedOut} durationMs=${c}`,"---",""].join(`
210
210
  `)+(u.stdout||"")+(u.stderr?`
211
211
  --- stderr ---
212
- ${u.stderr}`:"");try{wr.writeFileSync(f,g,{mode:384})}catch(k){M.warn({err:String(k),logPath:f},"cowork write log")}let y=u.code===0&&!u.timedOut&&u.signal===null,v=xe().find(k=>k.id===t.id&&k.ownerPeerKey===t.ownerPeerKey),E=v?.notify??t.notify,R=v?.notifyWhen??t.notifyWhen??"always",P=v?.attachLog??t.attachLog,T=v?.attachFiles??t.attachFiles,O=!0;if(R==="failure")O=!y;else if(R==="state-change"){let k=Fo(t.id);O=k===null||k!==y}Wo(t.id,y);let A=u.timedOut?"timeout":u.signal?`signal ${u.signal}`:u.code!==0&&u.code!==null?`exit ${u.code}`:null,K=`slot: ${a} \xB7 ${l}${A?` \xB7 ${A}`:""}`,X=(u.stdout||"").replace(/\s+$/,""),se=(u.stderr||"").trim(),he=X||(se?`(stderr) ${se}`:"(no output)"),D=R==="state-change"?y?" [recovered]":" [failing]":"",ae=`${t.name}${D} : ${A?`[${A}] `:""}${he}`,Y=n.onDemand?[K,`output: ${he}`]:[ae];if(O){let k=[],x=[],L=d-zh,F=[],Q=new Set;if(Array.isArray(T))for(let de of T){let at;try{at=Qh(de,s,L)}catch(lt){M.warn({err:String(lt),pat:de},"cowork attach glob"),k.push(`attach: ${de} skipped (glob error)`);continue}if(at.length!==0)for(let lt of at)Q.has(lt)||(Q.add(lt),F.push(lt))}if(P){let de=Mu(f,e,`${t.name}-${l}.log`);de.ok?x.push(de.spec):k.push(`attach: ${de.displayName} skipped (${de.reason})`)}let z=0;for(let de of F){if(x.length>=Cu){z+=1;continue}let at=Mu(de,e);at.ok?x.push(at.spec):k.push(`attach: ${at.displayName} skipped (${at.reason})`)}z>0&&k.push(`attached: skipped ${z} file(s) over cap ${Cu}`),k.length>0&&Y.push(...k);let we=Y.join(`
213
- `),Ue=p(we),Ie=kn(E,t.ownerPeerKey,e);for(let de of Ie){let at=ke(Ue,de.startsWith("tg:")?"telegram":"whatsapp").text;try{await r.sendToPeer(de,at)}catch(lt){M.warn({err:String(lt),pk:de},"cowork notify failed")}for(let lt of x)try{await r.sendMediaToPeer(de,lt)}catch($m){M.warn({err:String($m),pk:de,file:lt.displayName},"cowork media notify failed")}}}return{commandOk:y,logPath:f}}function Uo(e){let t=!1;an(xe());let n=async()=>{if(t)return;t=!0;let s=Date.now(),i=0,a=0,l=0,d=0,u=0;try{let c=e.getConfig(),{batch:m,remainingAfter:f}=cl(Kh);a=m.length,l=f,i=m.length+f;for(let y of m)try{let v=xe().find(E=>E.name===y.name.toLowerCase()&&E.ownerPeerKey===y.ownerPeerKey);if(v&&v.enabled){if(!Hi(v.ownerPeerKey,c)){M.warn({task:v.name,peer:v.ownerPeerKey},"cowork: skipping on-demand run \u2014 owner no longer on allowlist");continue}d+=1,await Tu(c,v,{slotMs:null,catchUp:!1,onDemand:!0},e)}}catch(b){M.warn({err:String(b),pending:y.name},"cowork on-demand run failed")}let h=xe();an(h);let g=Date.now();for(let y of h){if(!y.enabled||y.schedule.kind!=="heartbeat"||!Hi(y.ownerPeerKey,c))continue;let b=_o(y.id);if(b===null)continue;let v=b+y.schedule.intervalMs+y.schedule.graceMs;if(g>v){if(Fo(y.id)===!1)continue;Wo(y.id,!1);let R=Math.round((g-b)/6e4),P=`${y.name} [heartbeat missed] \u2014 last check-in ${R}m ago`,T=y.notify,O=kn(T,y.ownerPeerKey,c);for(let A of O)try{await e.sendToPeer(A,P)}catch(K){M.warn({err:String(K),pk:A},"cowork heartbeat notify failed")}}else if(g<=v&&Fo(y.id)===!1){Wo(y.id,!0);let R=`${y.name} [heartbeat recovered]`,P=y.notify,T=kn(P,y.ownerPeerKey,c);for(let O of T)try{await e.sendToPeer(O,R)}catch(A){M.warn({err:String(A),pk:O},"cowork heartbeat recovery notify failed")}}}for(let y of h){if(!y.enabled||y.schedule.kind==="ondemand"||y.schedule.kind==="heartbeat")continue;if(!Hi(y.ownerPeerKey,c)){M.warn({task:y.name,peer:y.ownerPeerKey},"cowork: skipping scheduled run \u2014 owner no longer on allowlist");continue}let b=Lo(y),v=Su(y.schedule,b,y.createdAtMs,g);if(v.length===0)continue;let E=v[v.length-1],R=g-E>Ru;try{u+=1;let{commandOk:P,logPath:T}=await Tu(c,y,{slotMs:E,catchUp:R,onDemand:!1},e);if(P){let O=Date.now(),A=g-E<=Ru?"on_time":"catch_up";if(v.length===1)No(y.id,[{slotMs:E,kind:A,logPath:T,completedAtMs:O}]);else{let X=v.slice(0,-1).map(se=>({slotMs:se,kind:"coalesced",logPath:null,completedAtMs:O}));X.push({slotMs:E,kind:A,logPath:T,completedAtMs:O}),No(y.id,X)}}}catch(P){M.warn({err:String(P),task:y.name},"cowork scheduled run failed")}}}finally{let c=Date.now()-s;M.info({tickMs:c,pendingDepthStart:i,pendingDequeued:a,pendingRemainingAfter:l,pendingRunsStarted:d,scheduledRan:u},"cowork tick"),t=!1}},r=setInterval(()=>void n(),3e4),o=setTimeout(()=>void n(),5e3);return()=>{clearInterval(r),clearTimeout(o),$u()}}import Vh from"node:fs";import Eu from"node:path";import{fileURLToPath as Xh}from"node:url";var Do=null;function Ze(){if(Do!==null)return Do;let e=Eu.dirname(Xh(import.meta.url)),t=Eu.join(e,"..","package.json"),n=Vh.readFileSync(t,"utf8"),r=JSON.parse(n);return Do=typeof r.version=="string"&&r.version.trim()?r.version.trim():"0.0.0",Do}be();G();import{spawn as Zh}from"node:child_process";import Pu from"node:fs";import Au from"node:path";var eg=new Set(["PATH","HOME","USER","LOGNAME","SHELL","LANG","LC_ALL","LC_CTYPE","LC_MESSAGES","TMPDIR","TZ"]),tg=new Set(["OPENAI_API_KEY","ANTHROPIC_API_KEY","GOOGLE_API_KEY","GEMINI_API_KEY","MISTRAL_API_KEY","GROQ_API_KEY","COHERE_API_KEY","HUGGINGFACE_TOKEN","TOGETHER_API_KEY","FIREWORKS_API_KEY","PERPLEXITY_API_KEY","DEEPSEEK_API_KEY","XAI_API_KEY","CURSOR_API_KEY"]);function ng(e,t){let n={OMNISH_PEER_KEY:e,OMNISH_CHAT_MESSAGE:t};for(let r of eg){let o=process.env[r];o!==void 0&&(n[r]=o)}for(let[r,o]of Object.entries(process.env))o&&(r.startsWith("OMNISH_")||tg.has(r))&&(n[r]=o);return n}var Iu=new Map;function rg(e,t){let r=(Iu.get(e)??Promise.resolve()).then(t).catch(o=>{M.warn({peerKey:e,err:String(o)},"chat LLM fallback queue task failed")});Iu.set(e,r)}function og(e){te();let t=e.chatLlmWorkDir.trim();if(t){let r=Au.resolve(t);return j(r),{cwd:r,cleanup:()=>{}}}let n=Pu.mkdtempSync(Au.join(H,"chat-llm-"));return{cwd:n,cleanup:()=>{try{Pu.rmSync(n,{recursive:!0,force:!0})}catch{}}}}function sg(e,t){return e.length<=t?e:`${e.slice(0,t)}
214
- [...input truncated]`}function ig(e,t){let n=[],r=e.stdout.trimEnd(),o=e.stderr.trimEnd();return r&&n.push(r.length>t?`${r.slice(0,t)}
212
+ ${u.stderr}`:"");try{Sr.writeFileSync(f,g,{mode:384})}catch(k){M.warn({err:String(k),logPath:f},"cowork write log")}let y=u.code===0&&!u.timedOut&&u.signal===null,v=xe().find(k=>k.id===t.id&&k.ownerPeerKey===t.ownerPeerKey),E=v?.notify??t.notify,R=v?.notifyWhen??t.notifyWhen??"always",P=v?.attachLog??t.attachLog,T=v?.attachFiles??t.attachFiles,O=!0;if(R==="failure")O=!y;else if(R==="state-change"){let k=Ho(t.id);O=k===null||k!==y}Bo(t.id,y);let A=u.timedOut?"timeout":u.signal?`signal ${u.signal}`:u.code!==0&&u.code!==null?`exit ${u.code}`:null,K=`slot: ${a} \xB7 ${l}${A?` \xB7 ${A}`:""}`,X=(u.stdout||"").replace(/\s+$/,""),ie=(u.stderr||"").trim(),he=X||(ie?`(stderr) ${ie}`:"(no output)"),D=R==="state-change"?y?" [recovered]":" [failing]":"",le=`${t.name}${D} : ${A?`[${A}] `:""}${he}`,Y=n.onDemand?[K,`output: ${he}`]:[le];if(O){let k=[],x=[],L=d-Zh,F=[],Q=new Set;if(Array.isArray(T))for(let pe of T){let lt;try{lt=ng(pe,s,L)}catch(ct){M.warn({err:String(ct),pat:pe},"cowork attach glob"),k.push(`attach: ${pe} skipped (glob error)`);continue}if(lt.length!==0)for(let ct of lt)Q.has(ct)||(Q.add(ct),F.push(ct))}if(P){let pe=Ou(f,e,`${t.name}-${l}.log`);pe.ok?x.push(pe.spec):k.push(`attach: ${pe.displayName} skipped (${pe.reason})`)}let z=0;for(let pe of F){if(x.length>=Iu){z+=1;continue}let lt=Ou(pe,e);lt.ok?x.push(lt.spec):k.push(`attach: ${lt.displayName} skipped (${lt.reason})`)}z>0&&k.push(`attached: skipped ${z} file(s) over cap ${Iu}`),k.length>0&&Y.push(...k);let we=Y.join(`
213
+ `),Ue=p(we),Ie=Rn(E,t.ownerPeerKey,e);for(let pe of Ie){let lt=ke(Ue,pe.startsWith("tg:")?"telegram":"whatsapp").text;try{await r.sendToPeer(pe,lt)}catch(ct){M.warn({err:String(ct),pk:pe},"cowork notify failed")}for(let ct of x)try{await r.sendMediaToPeer(pe,ct)}catch(Pm){M.warn({err:String(Pm),pk:pe,file:ct.displayName},"cowork media notify failed")}}}return{commandOk:y,logPath:f}}function jo(e){let t=!1;dn(xe());let n=async()=>{if(t)return;t=!0;let s=Date.now(),i=0,a=0,l=0,d=0,u=0;try{let c=e.getConfig(),{batch:m,remainingAfter:f}=hl(Xh);a=m.length,l=f,i=m.length+f;for(let y of m)try{let v=xe().find(E=>E.name===y.name.toLowerCase()&&E.ownerPeerKey===y.ownerPeerKey);if(v&&v.enabled){if(!Gi(v.ownerPeerKey,c)){M.warn({task:v.name,peer:v.ownerPeerKey},"cowork: skipping on-demand run \u2014 owner no longer on allowlist");continue}d+=1,await Lu(c,v,{slotMs:null,catchUp:!1,onDemand:!0},e)}}catch(b){M.warn({err:String(b),pending:y.name},"cowork on-demand run failed")}let h=xe();dn(h);let g=Date.now();for(let y of h){if(!y.enabled||y.schedule.kind!=="heartbeat"||!Gi(y.ownerPeerKey,c))continue;let b=Do(y.id);if(b===null)continue;let v=b+y.schedule.intervalMs+y.schedule.graceMs;if(g>v){if(Ho(y.id)===!1)continue;Bo(y.id,!1);let R=Math.round((g-b)/6e4),P=`${y.name} [heartbeat missed] \u2014 last check-in ${R}m ago`,T=y.notify,O=Rn(T,y.ownerPeerKey,c);for(let A of O)try{await e.sendToPeer(A,P)}catch(K){M.warn({err:String(K),pk:A},"cowork heartbeat notify failed")}}else if(g<=v&&Ho(y.id)===!1){Bo(y.id,!0);let R=`${y.name} [heartbeat recovered]`,P=y.notify,T=Rn(P,y.ownerPeerKey,c);for(let O of T)try{await e.sendToPeer(O,R)}catch(A){M.warn({err:String(A),pk:O},"cowork heartbeat recovery notify failed")}}}for(let y of h){if(!y.enabled||y.schedule.kind==="ondemand"||y.schedule.kind==="heartbeat")continue;if(!Gi(y.ownerPeerKey,c)){M.warn({task:y.name,peer:y.ownerPeerKey},"cowork: skipping scheduled run \u2014 owner no longer on allowlist");continue}let b=Wo(y),v=Mu(y.schedule,b,y.createdAtMs,g);if(v.length===0)continue;let E=v[v.length-1],R=g-E>Au;try{u+=1;let{commandOk:P,logPath:T}=await Lu(c,y,{slotMs:E,catchUp:R,onDemand:!1},e);if(P){let O=Date.now(),A=g-E<=Au?"on_time":"catch_up";if(v.length===1)Uo(y.id,[{slotMs:E,kind:A,logPath:T,completedAtMs:O}]);else{let X=v.slice(0,-1).map(ie=>({slotMs:ie,kind:"coalesced",logPath:null,completedAtMs:O}));X.push({slotMs:E,kind:A,logPath:T,completedAtMs:O}),Uo(y.id,X)}}}catch(P){M.warn({err:String(P),task:y.name},"cowork scheduled run failed")}}}finally{let c=Date.now()-s;M.info({tickMs:c,pendingDepthStart:i,pendingDequeued:a,pendingRemainingAfter:l,pendingRunsStarted:d,scheduledRan:u},"cowork tick"),t=!1}},r=setInterval(()=>void n(),3e4),o=setTimeout(()=>void n(),5e3);return()=>{clearInterval(r),clearTimeout(o),Pu()}}import rg from"node:fs";import Nu from"node:path";import{fileURLToPath as og}from"node:url";var Go=null;function Ze(){if(Go!==null)return Go;let e=Nu.dirname(og(import.meta.url)),t=Nu.join(e,"..","package.json"),n=rg.readFileSync(t,"utf8"),r=JSON.parse(n);return Go=typeof r.version=="string"&&r.version.trim()?r.version.trim():"0.0.0",Go}be();G();import{spawn as sg}from"node:child_process";import _u from"node:fs";import Fu from"node:path";var ig=new Set(["PATH","HOME","USER","LOGNAME","SHELL","LANG","LC_ALL","LC_CTYPE","LC_MESSAGES","TMPDIR","TZ"]),ag=new Set(["OPENAI_API_KEY","ANTHROPIC_API_KEY","GOOGLE_API_KEY","GEMINI_API_KEY","MISTRAL_API_KEY","GROQ_API_KEY","COHERE_API_KEY","HUGGINGFACE_TOKEN","TOGETHER_API_KEY","FIREWORKS_API_KEY","PERPLEXITY_API_KEY","DEEPSEEK_API_KEY","XAI_API_KEY","CURSOR_API_KEY"]);function lg(e,t){let n={OMNISH_PEER_KEY:e,OMNISH_CHAT_MESSAGE:t};for(let r of ig){let o=process.env[r];o!==void 0&&(n[r]=o)}for(let[r,o]of Object.entries(process.env))o&&(r.startsWith("OMNISH_")||ag.has(r))&&(n[r]=o);return n}var Wu=new Map;function cg(e,t){let r=(Wu.get(e)??Promise.resolve()).then(t).catch(o=>{M.warn({peerKey:e,err:String(o)},"chat LLM fallback queue task failed")});Wu.set(e,r)}function ug(e){te();let t=e.chatLlmWorkDir.trim();if(t){let r=Fu.resolve(t);return j(r),{cwd:r,cleanup:()=>{}}}let n=_u.mkdtempSync(Fu.join(H,"chat-llm-"));return{cwd:n,cleanup:()=>{try{_u.rmSync(n,{recursive:!0,force:!0})}catch{}}}}function dg(e,t){return e.length<=t?e:`${e.slice(0,t)}
214
+ [...input truncated]`}function pg(e,t){let n=[],r=e.stdout.trimEnd(),o=e.stderr.trimEnd();return r&&n.push(r.length>t?`${r.slice(0,t)}
215
215
  [...truncated]`:r),o&&(n.push("\u2014 stderr \u2014"),n.push(o.length>t?`${o.slice(0,t)}
216
216
  [...truncated]`:o)),n.length===0&&n.push("(no output)"),e.timedOut?n.push(`(timed out after ${Math.round(e.durationMs/1e3)}s)`):e.code!==0&&e.code!==null?n.push(`(exit ${e.code})`):e.signal&&n.push(`(signal ${e.signal})`),n.join(`
217
- `)}function Ho(e){let t=e&&typeof e=="object"&&"code"in e?String(e.code):"";return t==="EPIPE"||t==="EOF"}function Ou(e){try{e?.stdin?.end()}catch(t){if(!Ho(t))throw t}}function ag(e,t,n,r){return new Promise(o=>{let s=Date.now(),i=!1,a="",l="",d=!1,u=null,c,m=b=>{d||(d=!0,clearTimeout(c),o(b))},f=r.maxOutChars,h=(b,v)=>{b==="out"?a+=v:l+=v;let E=a.length+l.length;if(E>f){let R=E-f;if(l.length>=R)l=`${l.slice(0,Math.max(0,l.length-R))}
217
+ `)}function Jo(e){let t=e&&typeof e=="object"&&"code"in e?String(e.code):"";return t==="EPIPE"||t==="EOF"}function Uu(e){try{e?.stdin?.end()}catch(t){if(!Jo(t))throw t}}function mg(e,t,n,r){return new Promise(o=>{let s=Date.now(),i=!1,a="",l="",d=!1,u=null,c,m=b=>{d||(d=!0,clearTimeout(c),o(b))},f=r.maxOutChars,h=(b,v)=>{b==="out"?a+=v:l+=v;let E=a.length+l.length;if(E>f){let R=E-f;if(l.length>=R)l=`${l.slice(0,Math.max(0,l.length-R))}
218
218
  [...truncated]`;else{let P=R-l.length;l="",a=`${a.slice(0,Math.max(0,a.length-P))}
219
- [...truncated]`}try{u?.kill("SIGTERM")}catch{}}};try{u=Zh(e,["-c",t],{cwd:r.cwd,env:r.env,stdio:["pipe","pipe","pipe"]})}catch(b){m({code:null,stdout:"",stderr:String(b),durationMs:Date.now()-s,timedOut:!1,signal:null});return}c=setTimeout(()=>{i=!0;try{u?.kill("SIGTERM")}catch{}},r.timeoutMs),u.stdout?.setEncoding("utf8"),u.stderr?.setEncoding("utf8"),u.stdout?.on("data",b=>h("out",b)),u.stderr?.on("data",b=>h("err",b));let g=u.stdin;g&&g.on("error",b=>{Ho(b)||m({code:null,stdout:a,stderr:`${l}
220
- ${String(b)}`,durationMs:Date.now()-s,timedOut:i,signal:null})}),(()=>{if(!(!g||d))try{g.write(n,"utf8",b=>{if(b&&!Ho(b)){try{u?.kill("SIGTERM")}catch{}m({code:null,stdout:a,stderr:`${l}
221
- ${String(b)}`,durationMs:Date.now()-s,timedOut:i,signal:null});return}Ou(u)})}catch(b){if(!Ho(b)){m({code:null,stdout:a,stderr:`${l}
222
- ${String(b)}`,durationMs:Date.now()-s,timedOut:i,signal:null});return}Ou(u)}})(),u.on("error",b=>{m({code:null,stdout:a,stderr:`${l}
223
- ${String(b)}`,durationMs:Date.now()-s,timedOut:i,signal:null})}),u.on("close",(b,v)=>{m({code:b,stdout:a,stderr:l,durationMs:Date.now()-s,timedOut:i,signal:v??null})})})}async function lg(e,t,n,r){let o=e.chatLlmShellCommand.trim();if(!o){await r("(chat LLM fallback: chatLlmShellCommand is empty)");return}let{cwd:s,cleanup:i}=og(e);try{let a=sg(n,e.chatLlmMaxInputChars),l=ng(t,a),d=e.chatLlmMaxOutputChars,u;e.chatLlmNeedsTty?u=await sn(e.shell,o,{cwd:s,timeoutMs:e.chatLlmTimeoutMs,maxBytes:d,env:l}):u=await ag(e.shell,o,a,{cwd:s,timeoutMs:e.chatLlmTimeoutMs,maxOutChars:d,env:l});let c=ig(u,d);await r(c)}catch(a){M.warn({peerKey:t,err:String(a)},"chat LLM fallback run failed"),await r(`(assistant error) ${String(a)}`)}finally{i()}}function On(e,t,n,r){M.info({peerKey:t,len:n.length},"chat LLM fallback enqueued"),rg(t,async()=>{await lg(e,t,n,r),M.info({peerKey:t},"chat LLM fallback completed")})}G();import{spawn as cg}from"node:child_process";import ug from"node:crypto";import ze from"node:fs";import Bi from"node:path";var Lu=64,dg=/^[a-zA-Z0-9._-]+$/;function pg(e){let t=e.trim();return t?t.length>Lu?`Job name must be at most ${Lu} characters.`:dg.test(t)?null:"Job name may only use letters, digits, and . _ -":"Job name must not be empty."}function Nu(e){let t=e.trim();if(!t)return{error:"empty"};let n=null,r=!1,o=!0;for(;o;){o=!1;let i=t.trim();if(/^--notify(?:\s|$)/i.test(i)){r=!0,t=i.slice(8).trim(),o=!0;continue}if(/^-N(?:\s|$)/.test(i)){r=!0,t=i.slice(2).trim(),o=!0;continue}let a=[[/^--name\s*=\s*(\S+)\s+([\s\S]+)$/i,1,2],[/^--name\s+(\S+)\s+([\s\S]+)$/i,1,2],[/^-n\s+(\S+)\s+([\s\S]+)$/i,1,2]];for(let[l,d,u]of a){let c=i.match(l);if(c){n=c[d],t=c[u].trim(),o=!0;break}}}let s=t.trim();if(n===null&&[/^-n\s+\S+\s*$/i,/^--name\s+\S+\s*$/i,/^--name=\S+\s*$/i,/^-n\s*$/i,/^--name\s*$/i].some(a=>a.test(s)))return{error:"Add a shell command after the name."};if(n!==null){let i=pg(n);if(i)return{error:i}}return s?{cmd:s,name:n,notify:r}:{error:"Add a shell command after the flags."}}function mg(e,t){let n=t.trim();if(!n)return{ok:!1,error:"Missing job id or name."};let r=n.toLowerCase(),o=/^[a-f0-9]{8}$/;if(o.test(r)&&e.find(i=>i.id===r))return{ok:!0,id:r};for(let s of e)if(s.name&&s.name.toLowerCase()===r)return{ok:!0,id:s.id};return o.test(r)?{ok:!1,error:`Unknown job id: ${r}`}:{ok:!1,error:`Unknown job name: ${n}`}}function Ln(e,t){ze.writeFileSync(e,JSON.stringify(t,null,2)+`
224
- `,{mode:384})}function kr(e){try{return JSON.parse(ze.readFileSync(e,"utf8"))}catch{return null}}function _u(e){let t=e.name?`${e.id} (${e.name})`:e.id,n=e.finishedAt&&e.startedAt?Date.parse(e.finishedAt)-Date.parse(e.startedAt):null,r=n!==null?`${(n/1e3).toFixed(1)}s`:"?",o=e.exitCode===0&&!e.signal,s=e.status==="killed"?`killed (signal ${e.signal??"?"})`:o?"completed successfully":e.signal?`signal ${e.signal}`:`exit ${e.exitCode??"?"}`,i=e.cmd.length>100?`${e.cmd.slice(0,100)}\u2026`:e.cmd;return[`Job ${t} ${s}`,`duration: ${r}`,`$ ${i}`,`/log ${e.name??e.id}`].join(`
225
- `)}var Tt=class{running=new Map;metaPath(t){return Bi.join(tt,`${t}.meta.json`)}logPath(t){return Bi.join(tt,`${t}.log`)}spawnJob(t,n,r={}){te();let o=ug.randomBytes(4).toString("hex"),s=this.logPath(o),i=this.metaPath(o);ze.writeFileSync(s,"",{flag:"w",mode:384});let a=ze.createWriteStream(s,{flags:"a"}),l=new Date().toISOString(),d=r.cwd,u=cg(t,["-c",n],{stdio:["ignore","pipe","pipe"],env:{...process.env,TERM:"dumb",...d?{PWD:d}:{}},...d?{cwd:d}:{}});this.running.set(o,u);let c={id:o,cmd:n,...r.name?{name:r.name}:{},pid:u.pid??null,startedAt:l,status:"running",exitCode:null,signal:null,...r.notifyPeerKey?{notifyPeerKey:r.notifyPeerKey}:{}};return Ln(i,c),u.stdout?.on("data",m=>{a.write(m)}),u.stderr?.on("data",m=>{a.write(m)}),u.on("close",(m,f)=>{this.running.delete(o),a.end();let h=kr(i)??c,g={...h,status:h.status==="killed"?"killed":"done",exitCode:m,signal:f??null,finishedAt:new Date().toISOString()};Ln(i,g),r.onComplete&&r.onComplete(g)}),u.on("error",m=>{this.running.delete(o),a.end(()=>{try{ze.appendFileSync(s,`
219
+ [...truncated]`}try{u?.kill("SIGTERM")}catch{}}};try{u=sg(e,["-c",t],{cwd:r.cwd,env:r.env,stdio:["pipe","pipe","pipe"]})}catch(b){m({code:null,stdout:"",stderr:String(b),durationMs:Date.now()-s,timedOut:!1,signal:null});return}c=setTimeout(()=>{i=!0;try{u?.kill("SIGTERM")}catch{}},r.timeoutMs),u.stdout?.setEncoding("utf8"),u.stderr?.setEncoding("utf8"),u.stdout?.on("data",b=>h("out",b)),u.stderr?.on("data",b=>h("err",b));let g=u.stdin;g&&g.on("error",b=>{Jo(b)||m({code:null,stdout:a,stderr:`${l}
220
+ ${String(b)}`,durationMs:Date.now()-s,timedOut:i,signal:null})}),(()=>{if(!(!g||d))try{g.write(n,"utf8",b=>{if(b&&!Jo(b)){try{u?.kill("SIGTERM")}catch{}m({code:null,stdout:a,stderr:`${l}
221
+ ${String(b)}`,durationMs:Date.now()-s,timedOut:i,signal:null});return}Uu(u)})}catch(b){if(!Jo(b)){m({code:null,stdout:a,stderr:`${l}
222
+ ${String(b)}`,durationMs:Date.now()-s,timedOut:i,signal:null});return}Uu(u)}})(),u.on("error",b=>{m({code:null,stdout:a,stderr:`${l}
223
+ ${String(b)}`,durationMs:Date.now()-s,timedOut:i,signal:null})}),u.on("close",(b,v)=>{m({code:b,stdout:a,stderr:l,durationMs:Date.now()-s,timedOut:i,signal:v??null})})})}async function fg(e,t,n,r){let o=e.chatLlmShellCommand.trim();if(!o){await r("(chat LLM fallback: chatLlmShellCommand is empty)");return}let{cwd:s,cleanup:i}=ug(e);try{let a=dg(n,e.chatLlmMaxInputChars),l=lg(t,a),d=e.chatLlmMaxOutputChars,u;e.chatLlmNeedsTty?u=await un(e.shell,o,{cwd:s,timeoutMs:e.chatLlmTimeoutMs,maxBytes:d,env:l}):u=await mg(e.shell,o,a,{cwd:s,timeoutMs:e.chatLlmTimeoutMs,maxOutChars:d,env:l});let c=pg(u,d);await r(c)}catch(a){M.warn({peerKey:t,err:String(a)},"chat LLM fallback run failed"),await r(`(assistant error) ${String(a)}`)}finally{i()}}function Wn(e,t,n,r){M.info({peerKey:t,len:n.length},"chat LLM fallback enqueued"),cg(t,async()=>{await fg(e,t,n,r),M.info({peerKey:t},"chat LLM fallback completed")})}G();import{spawn as hg}from"node:child_process";import gg from"node:crypto";import ze from"node:fs";import Ji from"node:path";var Du=64,yg=/^[a-zA-Z0-9._-]+$/;function wg(e){let t=e.trim();return t?t.length>Du?`Job name must be at most ${Du} characters.`:yg.test(t)?null:"Job name may only use letters, digits, and . _ -":"Job name must not be empty."}function Hu(e){let t=e.trim();if(!t)return{error:"empty"};let n=null,r=!1,o=!0;for(;o;){o=!1;let i=t.trim();if(/^--notify(?:\s|$)/i.test(i)){r=!0,t=i.slice(8).trim(),o=!0;continue}if(/^-N(?:\s|$)/.test(i)){r=!0,t=i.slice(2).trim(),o=!0;continue}let a=[[/^--name\s*=\s*(\S+)\s+([\s\S]+)$/i,1,2],[/^--name\s+(\S+)\s+([\s\S]+)$/i,1,2],[/^-n\s+(\S+)\s+([\s\S]+)$/i,1,2]];for(let[l,d,u]of a){let c=i.match(l);if(c){n=c[d],t=c[u].trim(),o=!0;break}}}let s=t.trim();if(n===null&&[/^-n\s+\S+\s*$/i,/^--name\s+\S+\s*$/i,/^--name=\S+\s*$/i,/^-n\s*$/i,/^--name\s*$/i].some(a=>a.test(s)))return{error:"Add a shell command after the name."};if(n!==null){let i=wg(n);if(i)return{error:i}}return s?{cmd:s,name:n,notify:r}:{error:"Add a shell command after the flags."}}function bg(e,t){let n=t.trim();if(!n)return{ok:!1,error:"Missing job id or name."};let r=n.toLowerCase(),o=/^[a-f0-9]{8}$/;if(o.test(r)&&e.find(i=>i.id===r))return{ok:!0,id:r};for(let s of e)if(s.name&&s.name.toLowerCase()===r)return{ok:!0,id:s.id};return o.test(r)?{ok:!1,error:`Unknown job id: ${r}`}:{ok:!1,error:`Unknown job name: ${n}`}}function Un(e,t){ze.writeFileSync(e,JSON.stringify(t,null,2)+`
224
+ `,{mode:384})}function xr(e){try{return JSON.parse(ze.readFileSync(e,"utf8"))}catch{return null}}function Bu(e){let t=e.name?`${e.id} (${e.name})`:e.id,n=e.finishedAt&&e.startedAt?Date.parse(e.finishedAt)-Date.parse(e.startedAt):null,r=n!==null?`${(n/1e3).toFixed(1)}s`:"?",o=e.exitCode===0&&!e.signal,s=e.status==="killed"?`killed (signal ${e.signal??"?"})`:o?"completed successfully":e.signal?`signal ${e.signal}`:`exit ${e.exitCode??"?"}`,i=e.cmd.length>100?`${e.cmd.slice(0,100)}\u2026`:e.cmd;return[`Job ${t} ${s}`,`duration: ${r}`,`$ ${i}`,`/log ${e.name??e.id}`].join(`
225
+ `)}var Et=class{running=new Map;metaPath(t){return Ji.join(tt,`${t}.meta.json`)}logPath(t){return Ji.join(tt,`${t}.log`)}spawnJob(t,n,r={}){te();let o=gg.randomBytes(4).toString("hex"),s=this.logPath(o),i=this.metaPath(o);ze.writeFileSync(s,"",{flag:"w",mode:384});let a=ze.createWriteStream(s,{flags:"a"}),l=new Date().toISOString(),d=r.cwd,u=hg(t,["-c",n],{stdio:["ignore","pipe","pipe"],env:{...process.env,TERM:"dumb",...d?{PWD:d}:{}},...d?{cwd:d}:{}});this.running.set(o,u);let c={id:o,cmd:n,...r.name?{name:r.name}:{},pid:u.pid??null,startedAt:l,status:"running",exitCode:null,signal:null,...r.notifyPeerKey?{notifyPeerKey:r.notifyPeerKey}:{}};return Un(i,c),u.stdout?.on("data",m=>{a.write(m)}),u.stderr?.on("data",m=>{a.write(m)}),u.on("close",(m,f)=>{this.running.delete(o),a.end();let h=xr(i)??c,g={...h,status:h.status==="killed"?"killed":"done",exitCode:m,signal:f??null,finishedAt:new Date().toISOString()};Un(i,g),r.onComplete&&r.onComplete(g)}),u.on("error",m=>{this.running.delete(o),a.end(()=>{try{ze.appendFileSync(s,`
226
226
  [spawn error] ${String(m)}
227
- `)}catch{}});let h={...kr(i)??c,status:"done",exitCode:null,signal:null,finishedAt:new Date().toISOString()};Ln(i,h),r.onComplete&&r.onComplete(h)}),{id:o,meta:c}}list(){te();let t=[];try{t=ze.readdirSync(tt)}catch{return[]}let n=[];for(let r of t){if(!r.endsWith(".meta.json"))continue;let o=kr(Bi.join(tt,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(!ze.existsSync(r))return"(no log file)";let s=ze.readFileSync(r,"utf8").split(`
227
+ `)}catch{}});let h={...xr(i)??c,status:"done",exitCode:null,signal:null,finishedAt:new Date().toISOString()};Un(i,h),r.onComplete&&r.onComplete(h)}),{id:o,meta:c}}list(){te();let t=[];try{t=ze.readdirSync(tt)}catch{return[]}let n=[];for(let r of t){if(!r.endsWith(".meta.json"))continue;let o=xr(Ji.join(tt,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(!ze.existsSync(r))return"(no log file)";let s=ze.readFileSync(r,"utf8").split(`
228
228
  `);return s.slice(Math.max(0,s.length-n)).join(`
229
- `).trimEnd()||"(empty log)"}readSince(t,n){let r=this.logPath(t);if(!ze.existsSync(r))return{text:"",nextOffset:n};let s=ze.statSync(r).size;if(n>=s)return{text:"",nextOffset:s};let i=Buffer.alloc(s-n),a=ze.openSync(r,"r");try{ze.readSync(a,i,0,i.length,n)}finally{ze.closeSync(a)}return{text:i.toString("utf8"),nextOffset:s}}resolveJobRef(t){return mg(this.list(),t)}kill(t){let n=this.metaPath(t),r=kr(n);if(!r)return`Unknown job id: ${t}`;let o=this.running.get(t);if(o&&!o.killed)return o.kill("SIGTERM"),Ln(n,{...r,status:"killed",signal:"SIGTERM"}),`Sent SIGTERM to job ${t} (pid ${r.pid??"?"})`;if(r.pid)try{return process.kill(r.pid,"SIGTERM"),Ln(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=kr(this.metaPath(t));r&&Ln(this.metaPath(t),{...r,status:"killed",signal:"SIGTERM"})}this.running.clear()}};import zg from"node:fs";import{Bot as qg,InputFile as Yg}from"grammy";G();import Fu from"node:fs";import Nn from"node:path";function fg(e){return e.replace(/[^a-zA-Z0-9._-]+/g,"_").slice(0,120)||"unknown"}function hg(e){let r=Nn.basename(e.trim()||"file").replace(/[^a-zA-Z0-9._-]+/g,"_").replace(/^\.+/,"").slice(0,180);return r.length>0?r:"file"}function gg(e,t){let n=new Date().toISOString().slice(0,10),r=fg(t);return Nn.join(e,r,n)}function _n(e,t,n){let r=gg(e,t);j(r);let o=hg(n),s=Nn.join(r,o);if(!Fu.existsSync(s))return s;let i=Nn.extname(o),a=i?o.slice(0,-i.length):o;for(let l=1;l<1e4;l+=1){let d=`${a}-${l}${i}`;if(s=Nn.join(r,d),!Fu.existsSync(s))return s}return Nn.join(r,`${a}-${Date.now()}${i}`)}be();import yg from"node:dns";import wg from"node:https";import{URL as bg}from"node:url";function Fn(e){if(e instanceof Error){let t=e.cause;return t!=null?`${e.message} (${String(t)})`:e.message}return String(e)}be();function Wu(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 kg(e,t){let r=t.split("/").filter(o=>o.length>0).map(encodeURIComponent).join("/");return`https://api.telegram.org/file/bot${e}/${r}`}var Uu="omnish (Telegram bot; https://github.com/labKnowledge/whatsLive)";function Sg(e){let t=new bg(e);return new Promise((n,r)=>{let o=wg.request({hostname:t.hostname,port:t.port||443,path:t.pathname+t.search,method:"GET",headers:{"User-Agent":Uu,Accept:"*/*"},lookup(s,i,a){yg.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 Du(e,t,n){let r;try{r=await e.api.getFile(t)}catch(i){return M.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=kg(e.token,r.file_path),s;try{let i=await fetch(o,{headers:{"User-Agent":Uu,Accept:"*/*"}});if(!i.ok)return M.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 M.warn({err:String(a),fileId:t},"telegram file read body failed"),{error:`Could not read file from Telegram (${String(a)}).`}}}catch(i){M.warn({err:String(i),fileId:t},"telegram file fetch failed; retrying via HTTPS IPv4");try{let a=await Sg(o);if(a.status<200||a.status>=300)return M.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 M.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: ${Fn(i)}; IPv4 fallback: ${Fn(a)}).`}}}return n>0&&s.byteLength>n?{error:`Media too large (max ${n} bytes).`}:{buffer:s}}pe();qe();import{downloadMediaMessage as Rg,extensionForMediaMessage as Cg,getContentType as Bo,isJidGroup as Gu,isLidUser as Mg}from"@whiskeysockets/baileys";import Tg from"node:fs";be();var qt=new Map;function vg(){let e=Date.now();for(let[t,n]of qt)e-n>6e5&&qt.delete(t);for(;qt.size>500;){let t=qt.keys().next().value;if(t===void 0)break;qt.delete(t)}}function xg(e){!e||typeof e!="string"||(vg(),qt.set(e,Date.now()))}function ji(e){xg(e?.key?.id??void 0)}function $g(e){if(!e)return!1;let t=qt.get(e);return t===void 0?!1:Date.now()-t>6e5?(qt.delete(e),!1):!0}function Hu(e){return!$g(e)}function Eg(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 Pg(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 Ju(e){return[Eg(e),Pg(e)].filter(n=>n.trim().length>0).join(`
230
- `).trim()}function Gi(e){return e.replace(/[\u0000-\u0008\u000B\u000C\u000E-\u001F\u007F]/g,"").replace(/\uFEFF/g,"").trim()}function Ku(e){let t=Bo(e??void 0);return t==="imageMessage"||t==="videoMessage"||t==="audioMessage"||t==="documentMessage"||t==="stickerMessage"}function Ag(e){let t=Bo(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 Bu(e){try{if(!e)return"file.bin";let t=Cg(e);return`file${t&&t.length>0?t.startsWith(".")?t:`.${t}`:".bin"}`}catch{return`${(Bo(e??void 0)??"file").replace("Message","")||"file"}.bin`}}function Ig(e){let t=Bo(e??void 0);if(!t)return Bu(e);let n=e?.[t];return n?.fileName&&typeof n.fileName=="string"&&n.fileName.trim()?n.fileName.trim():Bu(e)}async function ju(e,t,n,r){let o=t.message??void 0;if(!Ku(o))return{};let s=r.fileReceiveMaxBytes,i=Ag(o);if(s>0&&i!==void 0&&i>s)return{error:`Media too large (max ${s} bytes).`};let a;try{a=await Rg(t,"buffer",{},{logger:e.logger,reuploadRequest:e.updateMediaMessage})}catch(c){return M.warn({err:String(c),detail:Fn(c)},"whatsapp downloadMediaMessage failed"),{error:`Could not download media from WhatsApp (${Fn(c)}).`}}if(s>0&&a.length>s)return{error:`Media too large (max ${s} bytes).`};let l;try{l=Ft(r,n)}catch(c){return{error:String(c)}}let d=Ig(o),u=_n(l,n,d);try{Tg.writeFileSync(u,a,{mode:384})}catch{return{error:"Could not write media to inbox."}}return{path:u}}async function Og(e,t){let n=t.remoteJid??"";if(!n)return{fromJid:"",fromE164:""};if(t.remoteJidAlt){let r=ee(t.remoteJidAlt)??ee(n)??"";return{fromJid:n,fromE164:r}}if(Mg(n))try{let r=await e.signalRepository?.lidMapping?.getPNForLID?.(n);if(r){let o=ee(r)??"";if(o)return{fromJid:n,fromE164:o}}}catch{}return{fromJid:n,fromE164:ee(n)??""}}function Lg(e){try{if(!$().clusterEnabled)return;let n=Ju(e.message??void 0);if(!n)return;let r=Hc(n);if(!r)return;let o=e.key?.remoteJid??"",s=null;if(o&&!Gu(o)){let i=ee(o);i&&(s=`wa:${i}`)}Qc(r,s)}catch(t){M.warn({err:String(t)},"cluster footer observation failed")}}function zu(e,t){let n=r=>{r.type==="notify"&&(async()=>{for(let o of r.messages){let s=o.key;if(!s||s.fromMe&&(Lg(o),!Hu(s.id)))continue;let i=s.remoteJid;if(!i||Gu(i)||i.toLowerCase().endsWith("@status")||i==="status@broadcast")continue;let l=Gi(Ju(o.message??void 0)),{fromJid:d,fromE164:u}=await Og(e,s),c=`wa:${d}`,m=Ku(o.message??void 0);if(m&&!l){(async()=>{try{let g=$(),y=await ju(e,o,c,g);await t({fromJid:d,fromE164:u,text:"",messageId:s.id??void 0,mediaSavedPath:y.path,mediaError:y.error})}catch(g){M.error({err:String(g),fromJid:d},"whatsapp media-only background task failed")}})();continue}let f,h;if(m){let g=$(),y=await ju(e,o,c,g);f=y.path,h=y.error}!l&&!f&&!h||await t({fromJid:d,fromE164:u,text:l,messageId:s.id??void 0,mediaSavedPath:f,mediaError:h})}})()};return e.ev.on("messages.upsert",n),()=>{e.ev.off("messages.upsert",n)}}be();import Bg from"node:fs";import Ng from"node:process";import _g,{DisconnectReason as cn,fetchLatestBaileysVersion as Fg,makeCacheableSignalKeyStore as Wg,useMultiFileAuthState as Ug}from"@whiskeysockets/baileys";import Dg from"qrcode-terminal";G();be();var Hg="0.1.0";function Ji(e){return e?.error?.output?.statusCode}async function jo(e={}){te();let t=e.authDir??ne,n=e.verbose===!0,r=sl(),{state:o,saveCreds:s}=await Ug(t),{version:i}=await Fg(),a=_g({version:i,logger:r,printQRInTerminal:!1,browser:["omnish","cli",Hg],auth:{creds:o.creds,keys:Wg(o.keys,r)},syncFullHistory:!1,markOnlineOnConnect:!1});return a.ev.on("creds.update",s),a.ev.on("connection.update",l=>{let{connection:d,lastDisconnect:u,qr:c}=l;if(c&&(e.onQr?.(c),e.printQr)){let m=Ng.stdout,f=w(m,"\xB7".repeat(42));console.log(V(m,"Scan with WhatsApp \u2192 Linked devices")),console.log(f),Dg.generate(c,{small:!0}),console.log(f)}if(d==="close"){let m=Ji(u);n&&m===cn.loggedOut&&r.warn("WhatsApp session logged out (401).")}d==="open"&&n&&r.info("WhatsApp Web connected.")}),a.ws&&typeof a.ws.on=="function"&&a.ws.on("error",l=>{r.error({err:String(l)},"WebSocket error")}),a}function Wn(e){try{e.end(new Error("omnish: socket closed"))}catch{e.ws?.close()}}function Ki(e){return e?.output?.statusCode??e?.status??e?.error?.output?.statusCode}function Go(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 un(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 Xu=3500,jg=400,qu=18e4,Yu=9e4,Qu=3e5;function zi(e,t=Xu){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(`
229
+ `).trimEnd()||"(empty log)"}readSince(t,n){let r=this.logPath(t);if(!ze.existsSync(r))return{text:"",nextOffset:n};let s=ze.statSync(r).size;if(n>=s)return{text:"",nextOffset:s};let i=Buffer.alloc(s-n),a=ze.openSync(r,"r");try{ze.readSync(a,i,0,i.length,n)}finally{ze.closeSync(a)}return{text:i.toString("utf8"),nextOffset:s}}resolveJobRef(t){return bg(this.list(),t)}kill(t){let n=this.metaPath(t),r=xr(n);if(!r)return`Unknown job id: ${t}`;let o=this.running.get(t);if(o&&!o.killed)return o.kill("SIGTERM"),Un(n,{...r,status:"killed",signal:"SIGTERM"}),`Sent SIGTERM to job ${t} (pid ${r.pid??"?"})`;if(r.pid)try{return process.kill(r.pid,"SIGTERM"),Un(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=xr(this.metaPath(t));r&&Un(this.metaPath(t),{...r,status:"killed",signal:"SIGTERM"})}this.running.clear()}};import Zg from"node:fs";import{Bot as ey,InputFile as ty}from"grammy";G();import ju from"node:fs";import Dn from"node:path";function kg(e){return e.replace(/[^a-zA-Z0-9._-]+/g,"_").slice(0,120)||"unknown"}function Sg(e){let r=Dn.basename(e.trim()||"file").replace(/[^a-zA-Z0-9._-]+/g,"_").replace(/^\.+/,"").slice(0,180);return r.length>0?r:"file"}function vg(e,t){let n=new Date().toISOString().slice(0,10),r=kg(t);return Dn.join(e,r,n)}function Hn(e,t,n){let r=vg(e,t);j(r);let o=Sg(n),s=Dn.join(r,o);if(!ju.existsSync(s))return s;let i=Dn.extname(o),a=i?o.slice(0,-i.length):o;for(let l=1;l<1e4;l+=1){let d=`${a}-${l}${i}`;if(s=Dn.join(r,d),!ju.existsSync(s))return s}return Dn.join(r,`${a}-${Date.now()}${i}`)}be();import xg from"node:dns";import $g from"node:https";import{URL as Rg}from"node:url";function Bn(e){if(e instanceof Error){let t=e.cause;return t!=null?`${e.message} (${String(t)})`:e.message}return String(e)}be();function Gu(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 Cg(e,t){let r=t.split("/").filter(o=>o.length>0).map(encodeURIComponent).join("/");return`https://api.telegram.org/file/bot${e}/${r}`}var Ju="omnish (Telegram bot; https://github.com/labKnowledge/whatsLive)";function Mg(e){let t=new Rg(e);return new Promise((n,r)=>{let o=$g.request({hostname:t.hostname,port:t.port||443,path:t.pathname+t.search,method:"GET",headers:{"User-Agent":Ju,Accept:"*/*"},lookup(s,i,a){xg.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 Ku(e,t,n){let r;try{r=await e.api.getFile(t)}catch(i){return M.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=Cg(e.token,r.file_path),s;try{let i=await fetch(o,{headers:{"User-Agent":Ju,Accept:"*/*"}});if(!i.ok)return M.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 M.warn({err:String(a),fileId:t},"telegram file read body failed"),{error:`Could not read file from Telegram (${String(a)}).`}}}catch(i){M.warn({err:String(i),fileId:t},"telegram file fetch failed; retrying via HTTPS IPv4");try{let a=await Mg(o);if(a.status<200||a.status>=300)return M.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 M.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: ${Bn(i)}; IPv4 fallback: ${Bn(a)}).`}}}return n>0&&s.byteLength>n?{error:`Media too large (max ${n} bytes).`}:{buffer:s}}me();qe();import{downloadMediaMessage as Ag,extensionForMediaMessage as Ig,getContentType as Ko,isJidGroup as Qu,isLidUser as Og}from"@whiskeysockets/baileys";import Lg from"node:fs";be();var Qt=new Map;function Tg(){let e=Date.now();for(let[t,n]of Qt)e-n>6e5&&Qt.delete(t);for(;Qt.size>500;){let t=Qt.keys().next().value;if(t===void 0)break;Qt.delete(t)}}function Eg(e){!e||typeof e!="string"||(Tg(),Qt.set(e,Date.now()))}function Ki(e){Eg(e?.key?.id??void 0)}function Pg(e){if(!e)return!1;let t=Qt.get(e);return t===void 0?!1:Date.now()-t>6e5?(Qt.delete(e),!1):!0}function zu(e){return!Pg(e)}function Ng(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 _g(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 Vu(e){return[Ng(e),_g(e)].filter(n=>n.trim().length>0).join(`
230
+ `).trim()}function zi(e){return e.replace(/[\u0000-\u0008\u000B\u000C\u000E-\u001F\u007F]/g,"").replace(/\uFEFF/g,"").trim()}function Xu(e){let t=Ko(e??void 0);return t==="imageMessage"||t==="videoMessage"||t==="audioMessage"||t==="documentMessage"||t==="stickerMessage"}function Fg(e){let t=Ko(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 qu(e){try{if(!e)return"file.bin";let t=Ig(e);return`file${t&&t.length>0?t.startsWith(".")?t:`.${t}`:".bin"}`}catch{return`${(Ko(e??void 0)??"file").replace("Message","")||"file"}.bin`}}function Wg(e){let t=Ko(e??void 0);if(!t)return qu(e);let n=e?.[t];return n?.fileName&&typeof n.fileName=="string"&&n.fileName.trim()?n.fileName.trim():qu(e)}async function Yu(e,t,n,r){let o=t.message??void 0;if(!Xu(o))return{};let s=r.fileReceiveMaxBytes,i=Fg(o);if(s>0&&i!==void 0&&i>s)return{error:`Media too large (max ${s} bytes).`};let a;try{a=await Ag(t,"buffer",{},{logger:e.logger,reuploadRequest:e.updateMediaMessage})}catch(c){return M.warn({err:String(c),detail:Bn(c)},"whatsapp downloadMediaMessage failed"),{error:`Could not download media from WhatsApp (${Bn(c)}).`}}if(s>0&&a.length>s)return{error:`Media too large (max ${s} bytes).`};let l;try{l=Ut(r,n)}catch(c){return{error:String(c)}}let d=Wg(o),u=Hn(l,n,d);try{Lg.writeFileSync(u,a,{mode:384})}catch{return{error:"Could not write media to inbox."}}return{path:u}}async function Ug(e,t){let n=t.remoteJid??"";if(!n)return{fromJid:"",fromE164:""};if(t.remoteJidAlt){let r=ee(t.remoteJidAlt)??ee(n)??"";return{fromJid:n,fromE164:r}}if(Og(n))try{let r=await e.signalRepository?.lidMapping?.getPNForLID?.(n);if(r){let o=ee(r)??"";if(o)return{fromJid:n,fromE164:o}}}catch{}return{fromJid:n,fromE164:ee(n)??""}}function Dg(e){try{if(!$().clusterEnabled)return;let n=Vu(e.message??void 0);if(!n)return;let r=zc(n);if(!r)return;let o=e.key?.remoteJid??"",s=null;if(o&&!Qu(o)){let i=ee(o);i&&(s=`wa:${i}`)}nu(r,s)}catch(t){M.warn({err:String(t)},"cluster footer observation failed")}}function Zu(e,t){let n=r=>{r.type==="notify"&&(async()=>{for(let o of r.messages){let s=o.key;if(!s||s.fromMe&&(Dg(o),!zu(s.id)))continue;let i=s.remoteJid;if(!i||Qu(i)||i.toLowerCase().endsWith("@status")||i==="status@broadcast")continue;let l=zi(Vu(o.message??void 0)),{fromJid:d,fromE164:u}=await Ug(e,s),c=`wa:${d}`,m=Xu(o.message??void 0);if(m&&!l){(async()=>{try{let g=$(),y=await Yu(e,o,c,g);await t({fromJid:d,fromE164:u,text:"",messageId:s.id??void 0,mediaSavedPath:y.path,mediaError:y.error})}catch(g){M.error({err:String(g),fromJid:d},"whatsapp media-only background task failed")}})();continue}let f,h;if(m){let g=$(),y=await Yu(e,o,c,g);f=y.path,h=y.error}!l&&!f&&!h||await t({fromJid:d,fromE164:u,text:l,messageId:s.id??void 0,mediaSavedPath:f,mediaError:h})}})()};return e.ev.on("messages.upsert",n),()=>{e.ev.off("messages.upsert",n)}}be();import qg from"node:fs";import Hg from"node:process";import Bg,{DisconnectReason as mn,fetchLatestBaileysVersion as jg,makeCacheableSignalKeyStore as Gg,useMultiFileAuthState as Jg}from"@whiskeysockets/baileys";import Kg from"qrcode-terminal";G();be();var zg="0.1.0";function qi(e){return e?.error?.output?.statusCode}async function zo(e={}){te();let t=e.authDir??ne,n=e.verbose===!0,r=dl(),{state:o,saveCreds:s}=await Jg(t),{version:i}=await jg(),a=Bg({version:i,logger:r,printQRInTerminal:!1,browser:["omnish","cli",zg],auth:{creds:o.creds,keys:Gg(o.keys,r)},syncFullHistory:!1,markOnlineOnConnect:!1});return a.ev.on("creds.update",s),a.ev.on("connection.update",l=>{let{connection:d,lastDisconnect:u,qr:c}=l;if(c&&(e.onQr?.(c),e.printQr)){let m=Hg.stdout,f=w(m,"\xB7".repeat(42));console.log(V(m,"Scan with WhatsApp \u2192 Linked devices")),console.log(f),Kg.generate(c,{small:!0}),console.log(f)}if(d==="close"){let m=qi(u);n&&m===mn.loggedOut&&r.warn("WhatsApp session logged out (401).")}d==="open"&&n&&r.info("WhatsApp Web connected.")}),a.ws&&typeof a.ws.on=="function"&&a.ws.on("error",l=>{r.error({err:String(l)},"WebSocket error")}),a}function jn(e){try{e.end(new Error("omnish: socket closed"))}catch{e.ws?.close()}}function Yi(e){return e?.output?.statusCode??e?.status??e?.error?.output?.statusCode}function qo(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 fn(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 od=3500,Yg=400,ed=18e4,td=9e4,nd=3e5;function Qi(e,t=od){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(`
231
231
 
232
- `);i>Math.floor(t*.35)&&(o=r+i+2)}n.push(e.slice(r,o)),r=o}return n}function Jo(e){return new Promise(t=>setTimeout(t,e))}async function Vu(e,t){await Promise.race([e,Jo(qu).then(()=>{M.warn({jid:t,ms:qu},"whatsapp outbound self-heal: prior send chain waited too long; continuing")})])}async function Gg(e,t,n,r=3){let o;for(let s=1;s<=r;s+=1)try{let i=await un(e.sendMessage(t,{text:n}),Yu,`whatsapp sendMessage timed out after ${Yu}ms`);ji(i);return}catch(i){o=i;let a=String(i);if(/not connected|closed|timed out|timeout/i.test(a)&&s<r){await Jo(800*s);continue}throw i}throw o}function Jg(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 Kg(e,t,n,r=3){let o;for(let s=1;s<=r;s+=1)try{let i=await un(e.sendMessage(t,n),Qu,`whatsapp sendMedia timed out after ${Qu}ms`);ji(i);return}catch(i){o=i;let a=String(i);if(/not connected|closed|timed out|timeout/i.test(a)&&s<r){await Jo(800*s);continue}throw i}throw o}function Zu(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=Vu(a,o).then(async()=>{let d=zi(i,Xu);for(let u=0;u<d.length;u+=1)await Gg(e,o,d[u]??""),u<d.length-1&&await Jo(jg)}).catch(d=>{M.error({err:String(d),jid:o},"sendText failed")});n.set(o,l),await l.finally(()=>{n.get(o)===l&&n.delete(o)})},async sendMedia(o,s){let i=Bg.readFileSync(s.absPath),a=Jg(s,i),l=n.get(o)??Promise.resolve(),d=Vu(l,o).then(async()=>{await Kg(e,o,a)}).catch(u=>{M.error({err:String(u),jid:o},"sendMedia failed")});n.set(o,d),await d.finally(()=>{n.get(o)===d&&n.delete(o)})}}}function ed(e){let t=e.trim();return t?/^\/id(?:@[\w_]+)?$/i.test(t):!1}function td(e){let t=String(e).replace(/\D/g,"");return`Your Telegram user id: ${t}
232
+ `);i>Math.floor(t*.35)&&(o=r+i+2)}n.push(e.slice(r,o)),r=o}return n}function Yo(e){return new Promise(t=>setTimeout(t,e))}async function rd(e,t){await Promise.race([e,Yo(ed).then(()=>{M.warn({jid:t,ms:ed},"whatsapp outbound self-heal: prior send chain waited too long; continuing")})])}async function Qg(e,t,n,r=3){let o;for(let s=1;s<=r;s+=1)try{let i=await fn(e.sendMessage(t,{text:n}),td,`whatsapp sendMessage timed out after ${td}ms`);Ki(i);return}catch(i){o=i;let a=String(i);if(/not connected|closed|timed out|timeout/i.test(a)&&s<r){await Yo(800*s);continue}throw i}throw o}function Vg(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 Xg(e,t,n,r=3){let o;for(let s=1;s<=r;s+=1)try{let i=await fn(e.sendMessage(t,n),nd,`whatsapp sendMedia timed out after ${nd}ms`);Ki(i);return}catch(i){o=i;let a=String(i);if(/not connected|closed|timed out|timeout/i.test(a)&&s<r){await Yo(800*s);continue}throw i}throw o}function sd(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=rd(a,o).then(async()=>{let d=Qi(i,od);for(let u=0;u<d.length;u+=1)await Qg(e,o,d[u]??""),u<d.length-1&&await Yo(Yg)}).catch(d=>{M.error({err:String(d),jid:o},"sendText failed")});n.set(o,l),await l.finally(()=>{n.get(o)===l&&n.delete(o)})},async sendMedia(o,s){let i=qg.readFileSync(s.absPath),a=Vg(s,i),l=n.get(o)??Promise.resolve(),d=rd(l,o).then(async()=>{await Xg(e,o,a)}).catch(u=>{M.error({err:String(u),jid:o},"sendMedia failed")});n.set(o,d),await d.finally(()=>{n.get(o)===d&&n.delete(o)})}}}function id(e){let t=e.trim();return t?/^\/id(?:@[\w_]+)?$/i.test(t):!1}function ad(e){let t=String(e).replace(/\D/g,"");return`Your Telegram user id: ${t}
233
233
  Add to allowlist on this gateway host:
234
- omnish allow tg:${t}`}var Qg=400;async function nd(e,t,n,r){let o=t(),s=await Du(e,r.fileId,o.fileReceiveMaxBytes);if("error"in s)return{mediaError:s.error};let i;try{i=Ft(o,n)}catch(l){return{mediaError:String(l)}}let a=_n(i,n,r.baseName);try{return zg.writeFileSync(a,s.buffer,{mode:384}),{mediaSavedPath:a}}catch{return{mediaError:"Could not write media to inbox."}}}function Vg(e){return new Promise(t=>setTimeout(t,e))}async function qi(e,t,n,r={}){let o=new qg(e);await o.api.deleteWebhook({drop_pending_updates:!1});let s=new Map,i=r.decorate??(u=>u);async function a(u,c){let m=Math.min(t().appsMaxWaChars,4096),f=ke(c,"telegram"),h=i(f.text,Mi(u)),g=f.parseModeHtml,b=(s.get(u)??Promise.resolve()).then(async()=>{let v=zi(h,m);for(let E=0;E<v.length;E+=1){let R=v[E]??"",P=g?{parse_mode:"HTML"}:void 0;try{await o.api.sendMessage(u,R,P)}catch(T){if(g){M.warn({err:String(T),chatId:u},"telegram HTML send failed; retrying plain");let O=R.replace(/<[^>]+>/g,"");await o.api.sendMessage(u,O)}else throw T}E<v.length-1&&await Vg(Qg)}}).catch(v=>{M.error({err:String(v),chatId:u},"telegram sendText failed")});s.set(u,b),await b.finally(()=>{s.get(u)===b&&s.delete(u)})}async function l(u,c){let m=new Yg(c.absPath,c.displayName),f=c.caption,g=(s.get(u)??Promise.resolve()).then(async()=>{switch(c.category){case"image":await o.api.sendPhoto(u,m,f?{caption:f}:void 0);break;case"video":await o.api.sendVideo(u,m,f?{caption:f}:void 0);break;case"audio":await o.api.sendAudio(u,m,f?{caption:f}:void 0);break;case"document":await o.api.sendDocument(u,m,f?{caption:f}:void 0);break}}).catch(y=>{M.error({err:String(y),chatId:u},"telegram sendMedia failed")});s.set(u,g),await g.finally(()=>{s.get(u)===g&&s.delete(u)})}o.on("message",async u=>{let c=u.chat,m=u.message;if(!c||c.type!=="private"||!u.from||!m)return;let f="text"in m&&m.text?m.text:"",h="caption"in m&&m.caption?m.caption:"",g=Gi([f,h].filter(Boolean).join(`
235
- `));if(g&&ed(g)){let T=u.from.id;await o.api.sendMessage(c.id,td(T));return}let y=Wu(m),b=Mi(c.id),v=c.id,E=async T=>{if(T.kind==="file")await l(v,T.spec);else if(T.kind==="files")for(let O of T.specs)await l(v,O);else if(T.kind==="texts")for(let O of T.bodies)await a(v,O);else await a(v,T.body)};if(y&&!g){(async()=>{try{let T=await nd(o,t,b,y);if(!T.mediaSavedPath&&!T.mediaError)return;await n({peerKey:b,text:"",tgChatId:c.id,tgReplyToMessageId:m.message_id,mediaSavedPath:T.mediaSavedPath,mediaError:T.mediaError},E)}catch(T){M.error({err:String(T),chatId:v},"telegram media-only background task failed")}})();return}let R,P;if(y){let T=await nd(o,t,b,y);R=T.mediaSavedPath,P=T.mediaError}!g&&!R&&!P||await n({peerKey:b,text:g,tgChatId:c.id,tgReplyToMessageId:m.message_id,mediaSavedPath:R,mediaError:P},E)}),o.catch(u=>{M.error({err:String(u)},"telegram bot error")});let d=o.start();return{bot:o,sendText:a,sendMedia:l,stop:async()=>{await o.stop(),await d.catch(()=>{})}}}qe();import Xg from"node:fs";import Zg from"node:path";function ey(e){let t=Zg.join(e,"creds.json");try{let n=Xg.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 rd(e){let t=ey(e);if(!t)return null;let n=t.phoneNumber?.trim();if(n){let s=ee(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=ee(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 zo from"node:fs";import et from"node:process";G();G();import od from"node:fs";var ty="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 Ko(e){let t=String(e).toLowerCase();return t.includes("401")||t.includes("logged out")?!0:Ki(e)===cn.loggedOut}function ny(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 Yi(e){let t=!1;for(let n=0;n<3;n++){if(e.signal?.aborted)throw new Error("Pairing cancelled.");let r=await jo({authDir:e.authDir,printQr:e.printQr===!0,verbose:e.verbose===!0,onQr:e.onQr});e.onSocketReady?.(r);try{let o=un(Go(r),6e5,ty);e.signal?await Promise.race([o,ny(e.signal)]):await o;return}catch(o){if(o instanceof Error&&o.message==="Pairing cancelled.")throw o;if(Ki(o)===cn.restartRequired&&!t){t=!0,e.onRestart515?.(),await new Promise(i=>setTimeout(i,1500));continue}throw o}finally{e.onSocketClosed?.(),Wn(r)}}throw new Error("Pairing failed after restart (515) retries.")}async function sd(e){let t=e.authDir??ne;te();for(let n=1;n<=2;n++)try{await Yi({...e,authDir:t});return}catch(r){if(r instanceof Error&&r.message==="Pairing cancelled.")throw r;if(n===1&&Ko(r)){od.rmSync(t,{recursive:!0,force:!0}),od.mkdirSync(t,{recursive:!0,mode:448});continue}throw r}}async function ry(e,t){let n=et.stdout;console.log(`
234
+ omnish allow tg:${t}`}var ny=400;async function ld(e,t,n,r){let o=t(),s=await Ku(e,r.fileId,o.fileReceiveMaxBytes);if("error"in s)return{mediaError:s.error};let i;try{i=Ut(o,n)}catch(l){return{mediaError:String(l)}}let a=Hn(i,n,r.baseName);try{return Zg.writeFileSync(a,s.buffer,{mode:384}),{mediaSavedPath:a}}catch{return{mediaError:"Could not write media to inbox."}}}function ry(e){return new Promise(t=>setTimeout(t,e))}async function Vi(e,t,n,r={}){let o=new ey(e);await o.api.deleteWebhook({drop_pending_updates:!1});let s=new Map,i=r.decorate??(u=>u);async function a(u,c){let m=Math.min(t().appsMaxWaChars,4096),f=ke(c,"telegram"),h=i(f.text,Pi(u)),g=f.parseModeHtml,b=(s.get(u)??Promise.resolve()).then(async()=>{let v=Qi(h,m);for(let E=0;E<v.length;E+=1){let R=v[E]??"",P=g?{parse_mode:"HTML"}:void 0;try{await o.api.sendMessage(u,R,P)}catch(T){if(g){M.warn({err:String(T),chatId:u},"telegram HTML send failed; retrying plain");let O=R.replace(/<[^>]+>/g,"");await o.api.sendMessage(u,O)}else throw T}E<v.length-1&&await ry(ny)}}).catch(v=>{M.error({err:String(v),chatId:u},"telegram sendText failed")});s.set(u,b),await b.finally(()=>{s.get(u)===b&&s.delete(u)})}async function l(u,c){let m=new ty(c.absPath,c.displayName),f=c.caption,g=(s.get(u)??Promise.resolve()).then(async()=>{switch(c.category){case"image":await o.api.sendPhoto(u,m,f?{caption:f}:void 0);break;case"video":await o.api.sendVideo(u,m,f?{caption:f}:void 0);break;case"audio":await o.api.sendAudio(u,m,f?{caption:f}:void 0);break;case"document":await o.api.sendDocument(u,m,f?{caption:f}:void 0);break}}).catch(y=>{M.error({err:String(y),chatId:u},"telegram sendMedia failed")});s.set(u,g),await g.finally(()=>{s.get(u)===g&&s.delete(u)})}o.on("message",async u=>{let c=u.chat,m=u.message;if(!c||c.type!=="private"||!u.from||!m)return;let f="text"in m&&m.text?m.text:"",h="caption"in m&&m.caption?m.caption:"",g=zi([f,h].filter(Boolean).join(`
235
+ `));if(g&&id(g)){let T=u.from.id;await o.api.sendMessage(c.id,ad(T));return}let y=Gu(m),b=Pi(c.id),v=c.id,E=async T=>{if(T.kind==="file")await l(v,T.spec);else if(T.kind==="files")for(let O of T.specs)await l(v,O);else if(T.kind==="texts")for(let O of T.bodies)await a(v,O);else await a(v,T.body)};if(y&&!g){(async()=>{try{let T=await ld(o,t,b,y);if(!T.mediaSavedPath&&!T.mediaError)return;await n({peerKey:b,text:"",tgChatId:c.id,tgReplyToMessageId:m.message_id,mediaSavedPath:T.mediaSavedPath,mediaError:T.mediaError},E)}catch(T){M.error({err:String(T),chatId:v},"telegram media-only background task failed")}})();return}let R,P;if(y){let T=await ld(o,t,b,y);R=T.mediaSavedPath,P=T.mediaError}!g&&!R&&!P||await n({peerKey:b,text:g,tgChatId:c.id,tgReplyToMessageId:m.message_id,mediaSavedPath:R,mediaError:P},E)}),o.catch(u=>{M.error({err:String(u)},"telegram bot error")});let d=o.start();return{bot:o,sendText:a,sendMedia:l,stop:async()=>{await o.stop(),await d.catch(()=>{})}}}qe();import oy from"node:fs";import sy from"node:path";function iy(e){let t=sy.join(e,"creds.json");try{let n=oy.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 cd(e){let t=iy(e);if(!t)return null;let n=t.phoneNumber?.trim();if(n){let s=ee(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=ee(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 Vo from"node:fs";import et from"node:process";G();G();import ud from"node:fs";var ay="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 Qo(e){let t=String(e).toLowerCase();return t.includes("401")||t.includes("logged out")?!0:Yi(e)===mn.loggedOut}function ly(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 Xi(e){let t=!1;for(let n=0;n<3;n++){if(e.signal?.aborted)throw new Error("Pairing cancelled.");let r=await zo({authDir:e.authDir,printQr:e.printQr===!0,verbose:e.verbose===!0,onQr:e.onQr});e.onSocketReady?.(r);try{let o=fn(qo(r),6e5,ay);e.signal?await Promise.race([o,ly(e.signal)]):await o;return}catch(o){if(o instanceof Error&&o.message==="Pairing cancelled.")throw o;if(Yi(o)===mn.restartRequired&&!t){t=!0,e.onRestart515?.(),await new Promise(i=>setTimeout(i,1500));continue}throw o}finally{e.onSocketClosed?.(),jn(r)}}throw new Error("Pairing failed after restart (515) retries.")}async function dd(e){let t=e.authDir??ne;te();for(let n=1;n<=2;n++)try{await Xi({...e,authDir:t});return}catch(r){if(r instanceof Error&&r.message==="Pairing cancelled.")throw r;if(n===1&&Qo(r)){ud.rmSync(t,{recursive:!0,force:!0}),ud.mkdirSync(t,{recursive:!0,mode:448});continue}throw r}}async function cy(e,t){let n=et.stdout;console.log(`
236
236
  ${Ce(n,"omnish link")} ${w(n,"\u2014 QR appears below; scan from WhatsApp \u2192 Linked devices.")}
237
- `),await Yi({authDir:e,verbose:t,printQr:!0,onRestart515:()=>{console.warn(`
237
+ `),await Xi({authDir:e,verbose:t,printQr:!0,onRestart515:()=>{console.warn(`
238
238
  ${B(et.stderr,"WhatsApp requested a restart after pairing (code 515). This is normal. Opening a new connection\u2026")}
239
- `)}})}async function id(e={}){let t=e.authDir??ne,n=e.verbose===!0;te(),e.force&&(zo.rmSync(t,{recursive:!0,force:!0}),zo.mkdirSync(t,{recursive:!0,mode:448}),console.log(`${me(et.stdout,"Cleared saved session (--force).")} ${w(et.stdout,"Requesting a new QR\u2026")}
240
- `));for(let r=1;r<=2;r++)try{await ry(t,n),console.log(`
241
- ${ce(et.stdout,"Linked.")} ${S(et.stdout,"Session saved. You can run")} ${ce(et.stdout,"omnish run")} ${S(et.stdout,"now.")}
242
- `);return}catch(o){if(r===1&&Ko(o)){console.warn(`
239
+ `)}})}async function pd(e={}){let t=e.authDir??ne,n=e.verbose===!0;te(),e.force&&(Vo.rmSync(t,{recursive:!0,force:!0}),Vo.mkdirSync(t,{recursive:!0,mode:448}),console.log(`${fe(et.stdout,"Cleared saved session (--force).")} ${w(et.stdout,"Requesting a new QR\u2026")}
240
+ `));for(let r=1;r<=2;r++)try{await cy(t,n),console.log(`
241
+ ${ue(et.stdout,"Linked.")} ${S(et.stdout,"Session saved. You can run")} ${ue(et.stdout,"omnish run")} ${S(et.stdout,"now.")}
242
+ `);return}catch(o){if(r===1&&Qo(o)){console.warn(`
243
243
  ${B(et.stderr,"WhatsApp returned logged-out (401). This often happens after Ctrl+C during link or corrupt auth files.")}
244
244
  ${B(et.stderr,"Clearing auth dir and retrying once with a fresh QR\u2026")}
245
- `),zo.rmSync(t,{recursive:!0,force:!0}),zo.mkdirSync(t,{recursive:!0,mode:448});continue}throw Ko(o)&&console.error(`
245
+ `),Vo.rmSync(t,{recursive:!0,force:!0}),Vo.mkdirSync(t,{recursive:!0,mode:448});continue}throw Qo(o)&&console.error(`
246
246
  ${C(et.stderr,"Still failing after a clean auth directory. Try:")}
247
247
  ${w(et.stderr,` pnpm approve-builds && pnpm install
248
248
  (pnpm may have skipped Baileys/sharp/protobuf build scripts.)
249
249
  Then: omnish link --force
250
- `)}`),o}}G();import{spawn as oy,spawnSync as sy}from"node:child_process";import Un from"node:fs";import iy from"node:path";import Dn from"node:process";function qo(e,t={}){te(),j(iy.dirname(e));let n=Dn.argv[1];if(!n)return{ok:!1,message:"Cannot resolve entry script; invoke via node path/to/dist/index.js."};let r=["run"];t.verbose&&r.push("-vb");let o=Un.openSync(e,"a"),s=oy(Dn.execPath,[n,...r],{detached:!0,stdio:["ignore",o,o],env:{...Dn.env,OMNISH_BACKGROUND_GATEWAY:"1"}});return Un.closeSync(o),s.unref(),s.pid?{ok:!0,pid:s.pid}:{ok:!1,message:"Failed to start background gateway."}}function Yo(){if(te(),!Un.existsSync(le))return{outcome:"no_pidfile"};let e=Un.readFileSync(le,"utf8").trim(),t=Number(e);if(!Number.isFinite(t)||t<=0){try{Un.unlinkSync(le)}catch{}return{outcome:"invalid_pidfile"}}try{Dn.kill(t,0)}catch{try{Un.unlinkSync(le)}catch{}return{outcome:"stale_cleaned",pid:t}}try{return Dn.kill(t,"SIGTERM"),{outcome:"sent_signal",pid:t}}catch(n){return Dn.platform==="win32"&&sy("taskkill",["/PID",String(t),"/T"],{windowsHide:!0}).status===0?{outcome:"taskkill_ok",pid:t}:{outcome:"failed",message:`could not signal process: ${String(n)}`}}}import pd from"node:crypto";import Vi from"node:fs";import ay from"node:net";qe();be();G();import ud from"node:fs";var ad=Number(process.env.PLATFORM_INBOUND_MEDIA_MAX_BYTES)>0?Number(process.env.PLATFORM_INBOUND_MEDIA_MAX_BYTES):54525952,ld=50;function cd(e){try{return JSON.parse(e)}catch{return null}}function Vo(e){return e.fileSendMaxBytes<=0?8388608:Math.min(e.fileSendMaxBytes,8388608)}function dd(e){if(ud.statSync(e.absPath).size>8388608)throw new Error(`File too large for attached mode (max ${8388608} bytes).`);let n=ud.readFileSync(e.absPath).toString("base64");return{name:e.displayName,mimetype:e.mimetype,category:e.category,dataBase64:n,...e.caption?{caption:e.caption}:{}}}function Qi(e,t,n,r){return t.kind==="texts"?{body:t.bodies.map(s=>ke(s,r).text).filter(s=>s.trim()).join(`
250
+ `)}`),o}}G();import{spawn as uy,spawnSync as dy}from"node:child_process";import Gn from"node:fs";import py from"node:path";import Jn from"node:process";function Xo(e,t={}){te(),j(py.dirname(e));let n=Jn.argv[1];if(!n)return{ok:!1,message:"Cannot resolve entry script; invoke via node path/to/dist/index.js."};let r=["run"];t.verbose&&r.push("-vb");let o=Gn.openSync(e,"a"),s=uy(Jn.execPath,[n,...r],{detached:!0,stdio:["ignore",o,o],env:{...Jn.env,OMNISH_BACKGROUND_GATEWAY:"1"}});return Gn.closeSync(o),s.unref(),s.pid?{ok:!0,pid:s.pid}:{ok:!1,message:"Failed to start background gateway."}}function Zo(){if(te(),!Gn.existsSync(ce))return{outcome:"no_pidfile"};let e=Gn.readFileSync(ce,"utf8").trim(),t=Number(e);if(!Number.isFinite(t)||t<=0){try{Gn.unlinkSync(ce)}catch{}return{outcome:"invalid_pidfile"}}try{Jn.kill(t,0)}catch{try{Gn.unlinkSync(ce)}catch{}return{outcome:"stale_cleaned",pid:t}}try{return Jn.kill(t,"SIGTERM"),{outcome:"sent_signal",pid:t}}catch(n){return Jn.platform==="win32"&&dy("taskkill",["/PID",String(t),"/T"],{windowsHide:!0}).status===0?{outcome:"taskkill_ok",pid:t}:{outcome:"failed",message:`could not signal process: ${String(n)}`}}}import wd from"node:crypto";import ea from"node:fs";import my from"node:net";qe();be();G();import gd from"node:fs";var md=Number(process.env.PLATFORM_INBOUND_MEDIA_MAX_BYTES)>0?Number(process.env.PLATFORM_INBOUND_MEDIA_MAX_BYTES):54525952,fd=50;function hd(e){try{return JSON.parse(e)}catch{return null}}function ts(e){return e.fileSendMaxBytes<=0?8388608:Math.min(e.fileSendMaxBytes,8388608)}function yd(e){if(gd.statSync(e.absPath).size>8388608)throw new Error(`File too large for attached mode (max ${8388608} bytes).`);let n=gd.readFileSync(e.absPath).toString("base64");return{name:e.displayName,mimetype:e.mimetype,category:e.category,dataBase64:n,...e.caption?{caption:e.caption}:{}}}function Zi(e,t,n,r){return t.kind==="texts"?{body:t.bodies.map(s=>ke(s,r).text).filter(s=>s.trim()).join(`
251
251
 
252
- `),...n?{messageId:n}:{}}:t.kind==="text"?{body:ke(t.body,r).text,...n?{messageId:n}:{}}:t.kind==="file"?{...n?{messageId:n}:{},files:[dd(t.spec)]}:{...n?{messageId:n}:{},files:t.specs.map(dd)}}var Sr=null;function ly(){try{let e=Vi.readFileSync(hn,"utf8"),t=JSON.parse(e);return typeof t.token=="string"?t.token:""}catch{return""}}function cy(e){Vi.writeFileSync(hn,JSON.stringify(e,null,2)+`
253
- `,{mode:384})}function uy(){try{Vi.unlinkSync(hn)}catch{}}async function dy(e,t){let n=t.getCfg(),r=typeof e.absPath=="string"?e.absPath.trim():"";if(!r)return{ok:!1,error:"Missing absPath."};let o=typeof e.caption=="string"&&e.caption.length>0?e.caption:void 0,s=t.sendPlatformMedia&&!t.getWaOutbound()&&!t.getTgSendMedia()?Vo(n):n.fileSendMaxBytes,i=Kt(r,s);if("error"in i)return{ok:!1,error:i.error};let a={absPath:i.absPath,category:i.category,mimetype:i.mimetype,displayName:i.displayName,caption:o};if(e.channel==="whatsapp"){let l=t.getWaOutbound(),d=typeof e.e164=="string"?e.e164.trim():"";if(!d.startsWith("+"))return{ok:!1,error:"WhatsApp destination must be E.164 (e.g. +15551234567)."};let u=It(d);if(!l){let m=t.sendPlatformMedia;if(!m)return{ok:!1,error:"WhatsApp outbound is not connected."};try{return await m(`wa:${u}`,a),{ok:!0}}catch(f){return{ok:!1,error:String(f)}}}let c=n.gatewayMode;if(c!=="whatsapp"&&c!=="both")return{ok:!1,error:"gatewayMode does not include WhatsApp."};try{return await l.sendMedia(u,a),{ok:!0}}catch(m){return{ok:!1,error:String(m)}}}if(e.channel==="telegram"){let l=t.getTgSendMedia();if(!l){let u=t.sendPlatformMedia;if(!u)return{ok:!1,error:"Telegram outbound is not connected."};if(!Number.isFinite(e.chatId))return{ok:!1,error:"Invalid Telegram chat id."};let c=`tg:${e.chatId}`;try{return await u(c,a),{ok:!0}}catch(m){return{ok:!1,error:String(m)}}}let d=n.gatewayMode;if(d!=="telegram"&&d!=="both")return{ok:!1,error:"gatewayMode does not include Telegram."};if(!Number.isFinite(e.chatId))return{ok:!1,error:"Invalid Telegram chat id."};try{return await l(e.chatId,a),{ok:!0}}catch(u){return{ok:!1,error:String(u)}}}return{ok:!1,error:"Unknown channel."}}async function py(e,t){let n=t.getCfg(),r=typeof e.text=="string"?e.text.trim():"";if(!r)return{ok:!1,error:"Missing or empty text."};if(e.channel==="whatsapp"){let o=t.getWaOutbound();if(!o){let l=t.sendPlatformText;if(!l)return{ok:!1,error:"WhatsApp outbound is not connected."};let d=typeof e.e164=="string"?e.e164.trim():"";if(!d.startsWith("+"))return{ok:!1,error:"WhatsApp destination must be E.164 (e.g. +15551234567)."};let u=It(d);try{return await l(`wa:${u}`,r),{ok:!0}}catch(c){return{ok:!1,error:String(c)}}}let s=n.gatewayMode;if(s!=="whatsapp"&&s!=="both")return{ok:!1,error:"gatewayMode does not include WhatsApp."};let i=typeof e.e164=="string"?e.e164.trim():"";if(!i.startsWith("+"))return{ok:!1,error:"WhatsApp destination must be E.164 (e.g. +15551234567)."};let a=It(i);try{return await o.sendText(a,r),{ok:!0}}catch(l){return{ok:!1,error:String(l)}}}if(e.channel==="telegram"){let o=t.getTgSendText();if(!o){let i=t.sendPlatformText;if(!i)return{ok:!1,error:"Telegram outbound is not connected."};if(!Number.isFinite(e.chatId))return{ok:!1,error:"Invalid Telegram chat id."};try{return await i(`tg:${e.chatId}`,r),{ok:!0}}catch(a){return{ok:!1,error:String(a)}}}let s=n.gatewayMode;if(s!=="telegram"&&s!=="both")return{ok:!1,error:"gatewayMode does not include Telegram."};if(!Number.isFinite(e.chatId))return{ok:!1,error:"Invalid Telegram chat id."};try{return await o(e.chatId,p(r)),{ok:!0}}catch(i){return{ok:!1,error:String(i)}}}return{ok:!1,error:"Unknown channel."}}async function my(e,t,n){let r;try{r=JSON.parse(e)}catch{return{ok:!1,error:"Invalid JSON."}}if(!r||typeof r!="object")return{ok:!1,error:"Invalid request."};if(typeof r.token!="string"||!n)return{ok:!1,error:"Unauthorized."};try{let o=Buffer.from(r.token,"utf8"),s=Buffer.from(n,"utf8");if(o.length!==s.length||!pd.timingSafeEqual(o,s))return{ok:!1,error:"Unauthorized."}}catch{return{ok:!1,error:"Unauthorized."}}return r.op==="sendMedia"?dy(r,t):r.op==="sendText"?py(r,t):{ok:!1,error:"Unsupported operation."}}function Xo(e){if(Sr)return;let t=pd.randomBytes(32).toString("hex"),n=ay.createServer(r=>{let o="";r.setTimeout(12e4),r.on("data",s=>{o+=s.toString("utf8");let i=o.indexOf(`
254
- `);if(i===-1)return;let a=o.slice(0,i).trim();o=o.slice(i+1);let l=ly();my(a,e,l).then(d=>{r.write(`${JSON.stringify(d)}
255
- `),r.end()})}),r.on("error",()=>{})});n.listen(0,"127.0.0.1",()=>{let r=n.address();if(!r||typeof r=="string"){M.error("gateway control: could not read listen address");return}let o={token:t,host:r.address,port:r.port};cy(o),M.info({port:r.port},"gateway control listening")}),n.on("error",r=>{M.error({err:String(r)},"gateway control server error")}),Sr=n}function Hn(){if(Sr){try{Sr.close()}catch{}Sr=null,uy()}}be();import fy from"node:crypto";import hy from"node:http";var gy=256*1024;function yy(e,t){if(!e||!t)return!1;try{let n=Buffer.from(e,"utf8"),r=Buffer.from(t,"utf8");return n.length===r.length&&fy.timingSafeEqual(n,r)}catch{return!1}}function wy(e){let t=typeof e.source=="string"?e.source:"webhook";if(e.action==="completed"&&e.workflow_run&&typeof e.workflow_run=="object"){let a=e.workflow_run,l=a.name??"?",d=a.conclusion??"?",u=a.head_branch??"?",c=a.html_url??"",m=e.repository?.full_name??"?";return`[${t}] ${m} \u2014 ${l}
252
+ `),...n?{messageId:n}:{}}:t.kind==="text"?{body:ke(t.body,r).text,...n?{messageId:n}:{}}:t.kind==="file"?{...n?{messageId:n}:{},files:[yd(t.spec)]}:{...n?{messageId:n}:{},files:t.specs.map(yd)}}var $r=null;function fy(){try{let e=ea.readFileSync(kn,"utf8"),t=JSON.parse(e);return typeof t.token=="string"?t.token:""}catch{return""}}function hy(e){ea.writeFileSync(kn,JSON.stringify(e,null,2)+`
253
+ `,{mode:384})}function gy(){try{ea.unlinkSync(kn)}catch{}}async function yy(e,t){let n=t.getCfg(),r=typeof e.absPath=="string"?e.absPath.trim():"";if(!r)return{ok:!1,error:"Missing absPath."};let o=typeof e.caption=="string"&&e.caption.length>0?e.caption:void 0,s=t.sendPlatformMedia&&!t.getWaOutbound()&&!t.getTgSendMedia()?ts(n):n.fileSendMaxBytes,i=qt(r,s);if("error"in i)return{ok:!1,error:i.error};let a={absPath:i.absPath,category:i.category,mimetype:i.mimetype,displayName:i.displayName,caption:o};if(e.channel==="whatsapp"){let l=t.getWaOutbound(),d=typeof e.e164=="string"?e.e164.trim():"";if(!d.startsWith("+"))return{ok:!1,error:"WhatsApp destination must be E.164 (e.g. +15551234567)."};let u=Lt(d);if(!l){let m=t.sendPlatformMedia;if(!m)return{ok:!1,error:"WhatsApp outbound is not connected."};try{return await m(`wa:${u}`,a),{ok:!0}}catch(f){return{ok:!1,error:String(f)}}}let c=n.gatewayMode;if(c!=="whatsapp"&&c!=="both")return{ok:!1,error:"gatewayMode does not include WhatsApp."};try{return await l.sendMedia(u,a),{ok:!0}}catch(m){return{ok:!1,error:String(m)}}}if(e.channel==="telegram"){let l=t.getTgSendMedia();if(!l){let u=t.sendPlatformMedia;if(!u)return{ok:!1,error:"Telegram outbound is not connected."};if(!Number.isFinite(e.chatId))return{ok:!1,error:"Invalid Telegram chat id."};let c=`tg:${e.chatId}`;try{return await u(c,a),{ok:!0}}catch(m){return{ok:!1,error:String(m)}}}let d=n.gatewayMode;if(d!=="telegram"&&d!=="both")return{ok:!1,error:"gatewayMode does not include Telegram."};if(!Number.isFinite(e.chatId))return{ok:!1,error:"Invalid Telegram chat id."};try{return await l(e.chatId,a),{ok:!0}}catch(u){return{ok:!1,error:String(u)}}}return{ok:!1,error:"Unknown channel."}}async function wy(e,t){let n=t.getCfg(),r=typeof e.text=="string"?e.text.trim():"";if(!r)return{ok:!1,error:"Missing or empty text."};if(e.channel==="whatsapp"){let o=t.getWaOutbound();if(!o){let l=t.sendPlatformText;if(!l)return{ok:!1,error:"WhatsApp outbound is not connected."};let d=typeof e.e164=="string"?e.e164.trim():"";if(!d.startsWith("+"))return{ok:!1,error:"WhatsApp destination must be E.164 (e.g. +15551234567)."};let u=Lt(d);try{return await l(`wa:${u}`,r),{ok:!0}}catch(c){return{ok:!1,error:String(c)}}}let s=n.gatewayMode;if(s!=="whatsapp"&&s!=="both")return{ok:!1,error:"gatewayMode does not include WhatsApp."};let i=typeof e.e164=="string"?e.e164.trim():"";if(!i.startsWith("+"))return{ok:!1,error:"WhatsApp destination must be E.164 (e.g. +15551234567)."};let a=Lt(i);try{return await o.sendText(a,r),{ok:!0}}catch(l){return{ok:!1,error:String(l)}}}if(e.channel==="telegram"){let o=t.getTgSendText();if(!o){let i=t.sendPlatformText;if(!i)return{ok:!1,error:"Telegram outbound is not connected."};if(!Number.isFinite(e.chatId))return{ok:!1,error:"Invalid Telegram chat id."};try{return await i(`tg:${e.chatId}`,r),{ok:!0}}catch(a){return{ok:!1,error:String(a)}}}let s=n.gatewayMode;if(s!=="telegram"&&s!=="both")return{ok:!1,error:"gatewayMode does not include Telegram."};if(!Number.isFinite(e.chatId))return{ok:!1,error:"Invalid Telegram chat id."};try{return await o(e.chatId,p(r)),{ok:!0}}catch(i){return{ok:!1,error:String(i)}}}return{ok:!1,error:"Unknown channel."}}async function by(e,t,n){let r;try{r=JSON.parse(e)}catch{return{ok:!1,error:"Invalid JSON."}}if(!r||typeof r!="object")return{ok:!1,error:"Invalid request."};if(typeof r.token!="string"||!n)return{ok:!1,error:"Unauthorized."};try{let o=Buffer.from(r.token,"utf8"),s=Buffer.from(n,"utf8");if(o.length!==s.length||!wd.timingSafeEqual(o,s))return{ok:!1,error:"Unauthorized."}}catch{return{ok:!1,error:"Unauthorized."}}return r.op==="sendMedia"?yy(r,t):r.op==="sendText"?wy(r,t):{ok:!1,error:"Unsupported operation."}}function ns(e){if($r)return;let t=wd.randomBytes(32).toString("hex"),n=my.createServer(r=>{let o="";r.setTimeout(12e4),r.on("data",s=>{o+=s.toString("utf8");let i=o.indexOf(`
254
+ `);if(i===-1)return;let a=o.slice(0,i).trim();o=o.slice(i+1);let l=fy();by(a,e,l).then(d=>{r.write(`${JSON.stringify(d)}
255
+ `),r.end()})}),r.on("error",()=>{})});n.listen(0,"127.0.0.1",()=>{let r=n.address();if(!r||typeof r=="string"){M.error("gateway control: could not read listen address");return}let o={token:t,host:r.address,port:r.port};hy(o),M.info({port:r.port},"gateway control listening")}),n.on("error",r=>{M.error({err:String(r)},"gateway control server error")}),$r=n}function Kn(){if($r){try{$r.close()}catch{}$r=null,gy()}}be();import ky from"node:crypto";import Sy from"node:http";var vy=256*1024;function xy(e,t){if(!e||!t)return!1;try{let n=Buffer.from(e,"utf8"),r=Buffer.from(t,"utf8");return n.length===r.length&&ky.timingSafeEqual(n,r)}catch{return!1}}function $y(e){let t=typeof e.source=="string"?e.source:"webhook";if(e.action==="completed"&&e.workflow_run&&typeof e.workflow_run=="object"){let a=e.workflow_run,l=a.name??"?",d=a.conclusion??"?",u=a.head_branch??"?",c=a.html_url??"",m=e.repository?.full_name??"?";return`[${t}] ${m} \u2014 ${l}
256
256
  result: ${d}
257
257
  branch: ${u}${c?`
258
258
  ${c}`:""}`}if(e.object_kind==="pipeline"&&e.object_attributes&&typeof e.object_attributes=="object"){let a=e.object_attributes,l=a.status??"?",d=a.ref??"?",u=a.id??"?",c=e.project?.path_with_namespace??"?";return`[${t}] ${c} \u2014 pipeline #${u}
259
259
  status: ${l}
260
260
  ref: ${d}`}let n=typeof e.text=="string"?e.text:null,r=typeof e.message=="string"?e.message:null,o=typeof e.title=="string"?e.title:null,s=typeof e.status=="string"?e.status:null;if(n)return`[${t}] ${n}`;let i=[`[${t}]`];if(o&&i.push(o),r&&i.push(r),s&&i.push(`status: ${s}`),i.length===1){let a=JSON.stringify(e).slice(0,500);i.push(a)}return i.join(`
261
- `)}var Xi=null;function Zo(e,t){if(Xi)return{stop:()=>{}};let n=hy.createServer((r,o)=>{if(r.method!=="POST"){o.writeHead(405,{"Content-Type":"application/json"}),o.end(JSON.stringify({ok:!1,error:"Method not allowed"}));return}let s=r.headers.authorization,i=s?.startsWith("Bearer ")?s.slice(7):void 0,a=new URL(r.url??"/",`http://${r.headers.host??"localhost"}`).searchParams.get("token")??void 0;if(!yy(i??a,e.token)){o.writeHead(401,{"Content-Type":"application/json"}),o.end(JSON.stringify({ok:!1,error:"Unauthorized"}));return}let d="",u=0;r.on("data",c=>{if(u+=c.length,u>gy){o.writeHead(413,{"Content-Type":"application/json"}),o.end(JSON.stringify({ok:!1,error:"Payload too large"})),r.destroy();return}d+=c.toString("utf8")}),r.on("end",()=>{let c;try{c=JSON.parse(d)}catch{o.writeHead(400,{"Content-Type":"application/json"}),o.end(JSON.stringify({ok:!1,error:"Invalid JSON"}));return}let f=new URL(r.url??"/",`http://${r.headers.host??"localhost"}`).searchParams.get("source")??r.headers["x-webhook-source"]??void 0;f&&(c.source=f);let h=typeof c.peerKey=="string"&&c.peerKey||t.getDefaultPeerKey();if(!h){o.writeHead(400,{"Content-Type":"application/json"}),o.end(JSON.stringify({ok:!1,error:"No target peer. Set peerKey in body or configure an allowlisted identity."}));return}let g=wy(c);t.sendToPeer(h,g).then(()=>{o.writeHead(200,{"Content-Type":"application/json"}),o.end(JSON.stringify({ok:!0}))},y=>{M.warn({err:String(y)},"webhook: sendToPeer failed"),o.writeHead(502,{"Content-Type":"application/json"}),o.end(JSON.stringify({ok:!1,error:"Failed to deliver message"}))})})});return n.listen(e.port,e.host,()=>{let r=n.address(),o=typeof r=="object"&&r?r.port:e.port;M.info({port:o,host:e.host},"webhook receiver listening")}),n.on("error",r=>{M.error({err:String(r)},"webhook receiver error")}),Xi=n,{stop:()=>{try{n.close()}catch{}Xi=null}}}pe();import Yw from"node:crypto";import Pa from"node:fs";be();import by from"node:fs";function md(e,t,n){let r=e.fileReceiveMaxBytes,o;try{o=Buffer.from(n.dataBase64,"base64")}catch{return{mediaError:"Invalid inbound media payload."}}if(r>0&&o.length>r)return{mediaError:`Media too large (max ${r} bytes).`};let s;try{s=Ft(e,t)}catch(a){return{mediaError:String(a)}}let i=_n(s,t,n.name);try{return by.writeFileSync(i,o,{mode:384}),{mediaSavedPath:i}}catch{return{mediaError:"Could not write media to inbox."}}}G();rs();be();import ra from"ws";import $y from"ws";var xy=["/platform/device","/control/device"];function os(e){let t=e.replace(/\/$/,"");return xy.map(n=>{let r=new URL(n,t);return r.protocol=r.protocol==="https:"?"wss:":"ws:",r.toString()})}var Ry=Math.ceil(ad*1.4)+512*1024;function Cy(e){let t=String(e instanceof Error?e.message:e);return/Unexpected server response:\s*400/.test(t)||/Unexpected server response:\s*404/.test(t)}async function wd(e,t){let n=os(e),r=null;for(let s of n)try{return{ws:await new Promise((a,l)=>{let d=new $y(s,{headers:{Authorization:`Bearer ${t}`},maxPayload:Ry});d.once("open",()=>a(d)),d.once("error",l)}),pathname:new URL(s).pathname}}catch(i){if(r=i instanceof Error?i:new Error(String(i)),Cy(i))continue;throw r}let o="Could not open a device WebSocket. Redeploy the relay (for /control/device fallback) and ensure /platform/* or /control/* route to port 8788.";throw r&&/Unexpected server response:\s*400/.test(r.message)?new Error(`${r.message} \u2014 ${o}`):new Error(r?`${r.message} \u2014 ${o}`:o)}var My=1e3,Ty=6e4,ss=class{constructor(t){this.options=t}ws=null;stopped=!1;pingTimer=null;registerResolve=null;registerReject=null;registeredAccount=null;registeredDeviceId=null;reconnectAttempt=0;reconnectTimer=null;outboundQueue=[];getRegisteredAccount(){return this.registeredAccount}async connect(){return this.connectOnce()}async connectOnce(){let{env:t}=this.options,n=new Promise((a,l)=>{this.registerResolve=a,this.registerReject=l}),{ws:r,pathname:o}=await wd(t.platformUrl,t.token);this.ws=r,o!=="/platform/device"&&M.info({pathname:o},"platform device websocket connected via fallback path"),r.on("message",a=>{this.handleMessage(a.toString())}),r.on("close",()=>{this.stopped||(M.warn("platform device websocket closed; scheduling reconnect"),this.scheduleReconnect())}),r.on("error",a=>{M.warn({err:String(a)},"platform device websocket error")});let s=setTimeout(()=>{this.registerReject?.(new Error("Platform device register timeout (15s)"))},15e3);this.sendRaw({type:"register",...t.deviceId?{deviceId:t.deviceId}:{},label:process.env.OMNISH_DEVICE_LABEL?.trim()||"default"});let i=await n;return clearTimeout(s),this.registeredDeviceId=i.deviceId,this.reconnectAttempt=0,this.pingTimer&&clearInterval(this.pingTimer),this.pingTimer=setInterval(()=>{this.sendRaw({type:"ping"})},3e4),this.flushOutboundQueue(),i}scheduleReconnect(){if(this.stopped||this.reconnectTimer)return;this.pingTimer&&(clearInterval(this.pingTimer),this.pingTimer=null),this.ws=null;let t=Math.min(My*2**this.reconnectAttempt,Ty);this.reconnectAttempt+=1,this.reconnectTimer=setTimeout(()=>{this.reconnectTimer=null,!this.stopped&&this.connectOnce().catch(n=>{M.warn({err:String(n)},"platform device reconnect failed"),this.scheduleReconnect()})},t),this.reconnectTimer.unref?.()}async handleMessage(t){let n=cd(t);if(n){if(n.type==="registered"&&"deviceId"in n){n.account&&(this.registeredAccount=n.account),this.registerResolve?.({deviceId:n.deviceId,...n.account?{account:n.account}:{}}),this.registerResolve=null,this.registerReject=null;return}if(n.type==="message"){await this.options.onMessage(n);return}if(n.type==="reply_error"){await this.options.onReplyError?.(n.peerKey,n.error,n.messageId);return}n.type==="error"&&(M.warn({message:n.message},"platform error"),this.registerReject?.(new Error(n.message)),this.registerResolve=null,this.registerReject=null)}}sendReply(t,n,r,o){this.enqueue({type:"reply",peerKey:t,...n?{body:n}:{},...r?{messageId:r}:{},...o?.length?{files:o}:{}})}sendRoutedReply(t,n){this.enqueue({type:"reply",peerKey:t,...n})}enqueue(t){if(this.ws?.readyState===ra.OPEN){this.sendRaw(t);return}if(this.outboundQueue.length>=ld){M.warn("platform outbound queue full; dropping reply");return}this.outboundQueue.push(t)}flushOutboundQueue(){for(;this.outboundQueue.length>0&&this.ws?.readyState===ra.OPEN;){let t=this.outboundQueue.shift();t&&this.sendRaw(t)}}sendRaw(t){this.ws?.readyState===ra.OPEN&&this.ws.send(JSON.stringify(t))}stop(){this.stopped=!0,this.reconnectTimer&&(clearTimeout(this.reconnectTimer),this.reconnectTimer=null),this.pingTimer&&(clearInterval(this.pingTimer),this.pingTimer=null);try{this.ws?.close()}catch{}this.ws=null,this.outboundQueue.length=0}};import aa from"node:readline/promises";import{stdin as la,stdout as ps}from"node:process";pe();var Ey=/^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$/i;function kd(e){let t=e.trim().toLowerCase();return t?t.length>63?"Tunnel name must be at most 63 characters.":Ey.test(t)?null:"Tunnel name may only use letters, digits, and hyphens.":"Tunnel name must not be empty."}function Sd(e){let t=Number.parseInt(e,10);return!Number.isInteger(t)||t<1||t>65535?null:t}function bd(e,t){let n="127.0.0.1",r,o,s=!1,i=[];for(let d=0;d<t.length;d++){let u=t[d];if(u==="--host"){let c=t[++d];if(!c)return{kind:"error",message:"--host requires an address."};n=c;continue}if(u.startsWith("--host=")){n=u.slice(7);continue}if(u==="--relay"){let c=t[++d];if(!c)return{kind:"error",message:"--relay requires a URL."};r=c;continue}if(u.startsWith("--relay=")){r=u.slice(8);continue}if(u==="--name"){let c=t[++d];if(!c)return{kind:"error",message:"--name requires a slug."};o=c;continue}if(u.startsWith("--name=")){o=u.slice(7);continue}if(u==="--background"||u==="-b"){s=!0;continue}i.push(u)}let a=i[0];if(!a)return{kind:"error",message:`Usage: omnish tunnel ${e} <port> [--host <addr>] [--name <slug>] [--relay <url>] [--background]`};let l=Sd(a);if(l===null)return{kind:"error",message:"Port must be an integer between 1 and 65535."};if(o){let d=kd(o);if(d)return{kind:"error",message:d}}return{kind:"expose",options:{kind:e,port:l,host:n,relayUrl:r??"",name:o,background:s}}}function oa(e){let[t,...n]=e,r=(t??"").trim().toLowerCase();if(!r||r==="help"||r==="-h"||r==="--help")return{kind:"help"};if(r==="signup"){let o,s,i,a;for(let l=0;l<n.length;l++){let d=n[l];if(d==="--email"){o=n[++l];continue}if(d.startsWith("--email=")){o=d.slice(8);continue}if(d==="--phone"){s=n[++l];continue}if(d.startsWith("--phone=")){s=d.slice(8);continue}if(d==="--password"){i=n[++l];continue}if(d.startsWith("--password=")){i=d.slice(11);continue}if(d==="--relay"){a=n[++l];continue}if(d.startsWith("--relay=")){a=d.slice(8);continue}}return{kind:"signup",email:o,phone:s,password:i,relayUrl:a}}if(r==="login"){let o,s,i,a,l;for(let d=0;d<n.length;d++){let u=n[d];if(u==="--token"){o=n[++d];continue}if(u.startsWith("--token=")){o=u.slice(8);continue}if(u==="--email"){s=n[++d];continue}if(u.startsWith("--email=")){s=u.slice(8);continue}if(u==="--phone"){i=n[++d];continue}if(u.startsWith("--phone=")){i=u.slice(8);continue}if(u==="--password"){a=n[++d];continue}if(u.startsWith("--password=")){a=u.slice(11);continue}if(u==="--relay"){l=n[++d];continue}if(u.startsWith("--relay=")){l=u.slice(8);continue}o||(o=u)}return{kind:"login",token:o,email:s,phone:i,password:a,relayUrl:l}}if(r==="logout")return{kind:"logout"};if(r==="list")return{kind:"list"};if(r==="status"){let o;for(let s=0;s<n.length;s++){let i=n[s];if(i==="--relay"){o=n[s+1],s++;continue}i.startsWith("--relay=")&&(o=i.slice(8))}return{kind:"status",relayUrl:o}}if(r==="stop"){let o=n[0]?.trim();return o?{kind:"stop",target:o}:{kind:"error",message:"Usage: omnish tunnel stop <id|slug>"}}return r==="http"?bd("http",n):r==="tcp"?bd("tcp",n):{kind:"error",message:`Unknown tunnel subcommand "${t}". Try: omnish tunnel help`}}function Py(e){let t=[],n="",r=null;for(let o=0;o<e.length;o++){let s=e[o];if(r){s===r?r=null:n+=s;continue}if(s==='"'||s==="'"){r=s;continue}if(/\s/.test(s)){n.length&&(t.push(n),n="");continue}n+=s}return n.length&&t.push(n),t}function vd(e){let t=e.trim();if(!t||t==="help")return{kind:"help"};let n=Py(t),r=n[0]?.toLowerCase();if(r==="login"||r==="logout"||r==="status"||r==="signup")return oa(n);if(t.toLowerCase()==="list"||t.toLowerCase()==="ls")return{kind:"list"};let o=t.match(/^stop\s+(\S+)\s*$/i);if(o)return{kind:"stop",target:o[1]};let s=t.match(/^(http|tcp)\s+(\d+)(?:\s+--name\s+(\S+))?(?:\s+--host\s+(\S+))?\s*$/i);if(s){let i=s[1].toLowerCase(),a=Sd(s[2]);if(a===null)return{kind:"error",message:"Port must be an integer between 1 and 65535."};let l=s[3],d=s[4]??"127.0.0.1";if(l){let u=kd(l);if(u)return{kind:"error",message:u}}return{kind:"expose",options:{kind:i,port:a,host:d,relayUrl:"",name:l,background:!0}}}return{kind:"error",message:"Usage: /tunnel login \u2026 | /tunnel status | /tunnel http <port> | /tunnel tcp <port> | /tunnels | /tunnel stop <id>"}}import Ay from"ws";import{URL as xd}from"node:url";function jn(e){let t=new xd(e);return t.protocol=t.protocol==="https:"?"wss:":"ws:",(!t.pathname||t.pathname==="/")&&(t.pathname="/control"),t.toString()}function $d(e){return new xd("/health",e).toString()}async function is(e,t,n=1e4){let r=t.trim();if(!r)return{ok:!1,healthOk:!1,controlOk:!1,error:"Tunnel token is missing."};let o=$d(e),s=!1,i;try{let d=await fetch(o,{method:"GET",signal:AbortSignal.timeout(n)});if(!d.ok)return{ok:!1,healthOk:!1,controlOk:!1,error:`Health HTTP ${d.status} (${o})`};let u=await d.json();if(!u?.ok)return{ok:!1,healthOk:!1,controlOk:!1,error:"Health JSON missing ok:true"};s=!0,typeof u.version=="string"&&(i=u.version)}catch(d){return{ok:!1,healthOk:!1,controlOk:!1,error:`Health fetch failed: ${String(d)}`}}let a=jn(e),l=!1;try{await new Promise((d,u)=>{let c=new Ay(a,{headers:{Authorization:`Bearer ${r}`}}),m=setTimeout(()=>{c.terminate(),u(new Error("WSS auth timeout"))},n);c.once("message",f=>{clearTimeout(m);try{let h=JSON.parse(f.toString());if(h.type!=="auth_ok"){u(new Error(`Expected auth_ok, got ${h.type??"?"}`));return}c.close(),d()}catch(h){u(h)}}),c.once("error",f=>{clearTimeout(m),u(f)})}),l=!0}catch(d){return{ok:!1,healthOk:!0,healthVersion:i,controlOk:!1,error:`Control WebSocket: ${String(d)}`}}return{ok:!0,healthOk:s,healthVersion:i,controlOk:!0}}import Md from"node:crypto";import Iy from"node:http";import Oy from"node:net";import Yt from"ws";function as(e,t,n=Buffer.alloc(0)){let r=Buffer.allocUnsafe(9+n.length);return r.writeUInt8(e,0),r.writeUInt32BE(t,1),r.writeUInt32BE(n.length,5),n.copy(r,9),r}function Rd(e){if(e.length<9)return null;let t=e.readUInt8(0),n=e.readUInt32BE(1),r=e.readUInt32BE(5);return e.length<9+r?null:{frameType:t,streamId:n,payload:e.subarray(9,9+r)}}function sa(e){try{let t=JSON.parse(e);return!t||typeof t!="object"||typeof t.type!="string"?null:t}catch{return null}}function Gn(e){return JSON.stringify(e)}function Ly(e){let t=Md.createHash("sha1").update(e,"utf8").digest("hex").slice(0,8);return Number.parseInt(t,16)>>>0}var ls=class{constructor(t){this.opts=t;let n=Md.randomBytes(4).toString("hex");this.record={id:n,kind:t.expose.kind,slug:t.expose.name?.trim().toLowerCase()??"",localHost:t.expose.host,localPort:t.expose.port,publicUrl:"",status:"connecting",startedAt:new Date().toISOString()}}ws=null;record;tcpStreams=new Map;tcpStreamIds=new Map;stopped=!1;pingTimer=null;getRecord(){return{...this.record}}setStatus(t,n){this.record.status=t,this.record.error=n,this.opts.onStatus?.(this.getRecord())}async start(){if(this.stopped)throw new Error("Tunnel client already stopped.");let t=jn(this.opts.relayUrl),n=new Yt(t,{headers:{Authorization:`Bearer ${this.opts.token}`}});this.ws=n,await new Promise((o,s)=>{let i=d=>{l(),s(d)},a=()=>{l(),o()},l=()=>{n.off("error",i),n.off("open",a)};n.once("error",i),n.once("open",a)}),n.on("message",(o,s)=>{if(s){this.handleBinary(Buffer.isBuffer(o)?o:Buffer.from(o));return}let i=typeof o=="string"?o:o.toString("utf8");this.handleControl(i)}),n.on("close",()=>{this.stopped||this.setStatus("error","Relay connection closed."),this.cleanupTcpStreams()}),n.on("error",o=>{this.stopped||this.setStatus("error",String(o))}),this.pingTimer=setInterval(()=>{n.readyState===Yt.OPEN&&n.send(Gn({type:"ping"}))},3e4),n.send(Gn({type:"register",id:this.record.id,kind:this.opts.expose.kind,localHost:this.opts.expose.host,localPort:this.opts.expose.port,...this.opts.expose.name?{name:this.opts.expose.name}:{}}));let r=await this.waitForRegistered();return this.record.slug=r.slug,this.record.publicUrl=r.publicUrl,this.setStatus("active"),this.getRecord()}waitForRegistered(){let t=this.ws;return t?new Promise((n,r)=>{let o=s=>{if(typeof s!="string"&&!Buffer.isBuffer(s))return;let i=typeof s=="string"?s:s.toString("utf8"),a=sa(i);if(a){if(a.type==="registered"&&a.id===this.record.id){t.off("message",o),n(a);return}a.type==="error"&&(t.off("message",o),r(new Error(a.message)))}};t.on("message",o)}):Promise.reject(new Error("WebSocket not connected."))}async handleControl(t){let n=sa(t);if(n&&!(n.type==="pong"||n.type==="auth_ok")){if(n.type==="error"){this.setStatus("error",n.message);return}if(n.type==="http"){await this.handleHttpRequest(n);return}if(n.type==="tcp_open"){await this.handleTcpOpen(n.streamId);return}n.type==="tcp_close"&&this.closeTcpStream(n.streamId)}}handleBinary(t){let n=Rd(t);if(!n)return;let r=[...this.tcpStreamIds.entries()].find(([,s])=>s===n.streamId)?.[0];if(!r)return;let o=this.tcpStreams.get(r);if(o){if(n.frameType===2){o.write(n.payload);return}n.frameType===3&&(o.end(),this.tcpStreams.delete(r),this.tcpStreamIds.delete(r))}}async handleHttpRequest(t){let n=this.ws;if(!n||n.readyState!==Yt.OPEN)return;let r=t.bodyBase64?Buffer.from(t.bodyBase64,"base64"):void 0,o={host:this.opts.expose.host,port:this.opts.expose.port,method:t.method,path:t.path,headers:{...t.headers}};await new Promise(s=>{let i=Iy.request(o,a=>{let l=[];a.on("data",d=>l.push(Buffer.isBuffer(d)?d:Buffer.from(d))),a.on("end",()=>{let d=Buffer.concat(l);n.send(Gn({type:"http_res",requestId:t.requestId,status:a.statusCode??502,headers:a.headers,...d.length>0?{bodyBase64:d.toString("base64")}:{}})),s()})});i.on("error",a=>{n.send(Gn({type:"http_res",requestId:t.requestId,status:502,headers:{"content-type":"text/plain"},bodyBase64:Buffer.from(String(a)).toString("base64")})),s()}),r&&r.length>0&&i.write(r),i.end()})}async handleTcpOpen(t){let n=this.ws;if(!n||n.readyState!==Yt.OPEN)return;let r=Ly(t);this.tcpStreamIds.set(t,r);let o=Oy.connect({host:this.opts.expose.host,port:this.opts.expose.port});this.tcpStreams.set(t,o),o.on("data",s=>{n.readyState===Yt.OPEN&&n.send(as(2,r,Buffer.isBuffer(s)?s:Buffer.from(s)))}),o.on("close",()=>{n.readyState===Yt.OPEN&&n.send(as(3,r)),this.tcpStreams.delete(t),this.tcpStreamIds.delete(t)}),o.on("error",()=>{n.readyState===Yt.OPEN&&n.send(as(3,r)),this.tcpStreams.delete(t),this.tcpStreamIds.delete(t)})}closeTcpStream(t){let n=this.tcpStreams.get(t);n&&n.destroy(),this.tcpStreams.delete(t),this.tcpStreamIds.delete(t)}cleanupTcpStreams(){for(let t of this.tcpStreams.values())t.destroy();this.tcpStreams.clear(),this.tcpStreamIds.clear()}async stop(){if(this.stopped)return;this.stopped=!0,this.pingTimer&&(clearInterval(this.pingTimer),this.pingTimer=null);let t=this.ws;t&&t.readyState===Yt.OPEN&&(t.send(Gn({type:"unregister",id:this.record.id})),t.close()),this.cleanupTcpStreams(),this.setStatus("stopped")}};var cs=class{clients=new Map;list(){return[...new Set(this.clients.values())].map(t=>t.getRecord())}getActiveCount(){return[...new Set(this.clients.values())].filter(t=>t.getRecord().status==="active").length}async expose(t,n){let r=ht();if(!r)throw new Error("No tunnel token configured. Run `omnish tunnel login` or set OMNISH_TUNNEL_TOKEN.");let o=t.tunnelMaxActive>0?t.tunnelMaxActive:5;if(this.getActiveCount()>=o)throw new Error(`Active tunnel limit reached (${o}). Stop one with \`omnish tunnel stop <id>\`.`);let s=n.relayUrl||rt(t.tunnelRelayUrl||$e),i=new ls({relayUrl:s,token:r,expose:n,onStatus:l=>{(l.status==="stopped"||l.status==="error")&&this.clients.delete(l.id)}}),a=await i.start();return this.clients.set(a.id,i),a.slug&&this.clients.set(a.slug,i),a}async stop(t){let n=t.trim().toLowerCase(),r=this.clients.get(n)??this.clients.get(t.trim());if(!r)return null;let o=r.getRecord();return await r.stop(),this.clients.delete(o.id),o.slug&&this.clients.delete(o.slug),r.getRecord()}async stopAll(){let t=[...new Set(this.clients.values())];await Promise.all(t.map(n=>n.stop())),this.clients.clear()}};async function Td(e,t){let n=await fetch(e,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(t),signal:AbortSignal.timeout(15e3)}),r=await n.json();return!n.ok||!r.token?{ok:!1,error:r.error??`HTTP ${n.status}`}:{ok:!0,token:r.token}}function us(e,t){let n=new URL("/auth/signup",e).toString();return Td(n,{...t.email?{email:t.email}:{},...t.phone?{phone:t.phone}:{},password:t.password})}function ds(e,t){let n=new URL("/auth/login",e).toString();return Td(n,{...t.email?{email:t.email}:{},...t.phone?{phone:t.phone}:{},password:t.password})}var Jn=new cs;function dn(){return Jn}function Ny(e){let t=[J(e,"omnish tunnel"),w(e,"Expose local HTTP or TCP ports through the omnish relay."),"",J(e,"Usage:"),` ${S(e,"omnish tunnel signup [--email <email>] [--phone <phone>] [--password <pass>] [--relay <url>]")}`,` ${S(e,"omnish tunnel login [--token <token>] [--relay <url>]")}`,` ${S(e,"omnish tunnel login --email <email> --password <pass> [--relay <url>]")}`,` ${S(e,"omnish tunnel logout")}`,` ${S(e,"omnish tunnel status [--relay <url>]")}`,` ${S(e,"omnish tunnel http <port> [--host <addr>] [--name <slug>] [--relay <url>] [--background]")}`,` ${S(e,"omnish tunnel tcp <port> [--host <addr>] [--name <slug>] [--relay <url>] [--background]")}`,` ${S(e,"omnish tunnel list")}`,` ${S(e,"omnish tunnel stop <id|slug>")}`,"",w(e,"Secrets live in ~/.omnish/tunnel-auth.json or OMNISH_TUNNEL_TOKEN."),w(e,`Default relay: ${$e}`),""];console.log(t.join(`
262
- `))}async function _y(){let e=aa.createInterface({input:la,output:ps});try{return(await e.question("Tunnel token: ")).trim()}finally{e.close()}}async function Ed(e){let t=oa(e),n=ps,r=process.stderr;if(t.kind==="help"){Ny(n);return}if(t.kind==="error"){console.error(C(r,t.message)),process.exitCode=1;return}let o=$();if(t.kind==="signup"){let s=t.relayUrl||rt(o.tunnelRelayUrl||$e),i=aa.createInterface({input:la,output:ps});try{let a=t.email?.trim()||(await i.question("Email (or leave empty for phone): ")).trim(),l=t.phone?.trim()||"";if(a||(l=l||(await i.question("Phone: ")).trim()),!a&&!l){console.error(C(r,"Email or phone is required.")),process.exitCode=1;return}let d=t.password||(await i.question("Password (min 8 chars): ")).trim();if(d.length<8){console.error(C(r,"Password must be at least 8 characters.")),process.exitCode=1;return}let u=await us(s,{...a?{email:a}:{},...l?{phone:l}:{},password:d});if(!u.ok){console.error(C(r,u.error)),process.exitCode=1;return}ft({token:u.token,...t.relayUrl?{relayUrl:t.relayUrl}:{}}),console.log(B(n,"Account created. Token saved."))}finally{i.close()}return}if(t.kind==="login"){if(t.email||t.phone){let a=t.relayUrl||rt(o.tunnelRelayUrl||$e),l=aa.createInterface({input:la,output:ps});try{let d=t.password||(await l.question("Password: ")).trim(),u=await ds(a,{...t.email?{email:t.email}:{},...t.phone?{phone:t.phone}:{},password:d});if(!u.ok){console.error(C(r,u.error)),process.exitCode=1;return}ft({token:u.token,...t.relayUrl?{relayUrl:t.relayUrl}:{}}),console.log(B(n,"Logged in. Token saved."))}finally{l.close()}return}let i=t.token?.trim()||await _y();if(!i){console.error(C(r,"Tunnel token is required.")),process.exitCode=1;return}ft({token:i,...t.relayUrl?{relayUrl:t.relayUrl}:{}}),console.log(B(n,"Tunnel token saved."));return}if(t.kind==="logout"){lo(),console.log(B(n,"Tunnel token removed."));return}if(t.kind==="status"){let s=t.relayUrl||rt(o.tunnelRelayUrl||$e),i=ht(),a=!!process.env.OMNISH_TUNNEL_TOKEN?.trim(),l=await is(s,i);console.log(`${w(n,"relay:")} ${S(n,s)}`),console.log(`${w(n,"token:")} ${S(n,i?`configured${a?" (OMNISH_TUNNEL_TOKEN)":""}`:C(r,"missing"))}`),console.log(`${w(n,"health:")} ${l.healthOk?S(n,`ok${l.healthVersion?` (${l.healthVersion})`:""}`):C(r,"fail")}`),console.log(`${w(n,"control:")} ${l.controlOk?S(n,"auth ok"):C(r,"fail")}${l.error&&!l.ok?` \u2014 ${l.error}`:""}`),console.log(`${w(n,"active:")} ${S(n,String(Jn.getActiveCount()))}`);return}if(t.kind==="list"){let s=Jn.list();if(s.length===0){console.log(B(n,"(no active tunnels)"));return}for(let i of s)console.log(`${S(n,i.id)} ${i.kind} ${i.status} ${i.publicUrl||"(pending)"}
263
- ${w(n,`${i.localHost}:${i.localPort}`)}`);return}if(t.kind==="stop"){let s=await Jn.stop(t.target);if(!s){console.error(C(r,`No active tunnel matched "${t.target}".`)),process.exitCode=1;return}console.log(B(n,`Stopped tunnel ${s.id}.`));return}if(t.kind==="expose"){let s=t.options.relayUrl||rt(o.tunnelRelayUrl||$e),i=await Jn.expose(o,{...t.options,relayUrl:s});console.log(B(n,`${i.kind.toUpperCase()} tunnel active`)),console.log(`${w(n,"public:")} ${S(n,i.publicUrl)}`),console.log(`${w(n,"local:")} ${S(n,`${i.localHost}:${i.localPort}`)}`),console.log(`${w(n,"id:")} ${S(n,i.id)}`),t.options.background||(console.log(w(n,"Press Ctrl+C to stop.")),await new Promise(a=>{let l=async()=>{await Jn.stop(i.id),a()};process.once("SIGINT",l),process.once("SIGTERM",l)}))}}function Pd(e){let t=e.trim().match(/^(\d+)\.(\d+)\.(\d+)/);return t?[Number(t[1]),Number(t[2]),Number(t[3])]:null}function Ad(e,t){let n=Pd(e),r=Pd(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 Fy="https://registry.npmjs.org",Id=null,ca=0;function vr(){return Id}function Wy(e){let t=e.trim();return t.length<1||t.length>214?!1:!/\s/.test(t)}function Od(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 Uy(e){let t=e.trim(),n=`${Fy}/${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 Dy(e){try{let t=await fetch(e,{headers:{Accept:"application/json"},signal:AbortSignal.timeout(15e3)});if(!t.ok)return{error:`updateInfoUrl: HTTP ${t.status}`};let n=await t.arrayBuffer(),r=n.byteLength>65536?n.slice(0,65536):n,o=new TextDecoder("utf-8",{fatal:!1}).decode(r),s;try{s=JSON.parse(o)}catch{return{error:"updateInfoUrl: body is not JSON"}}if(!s||typeof s!="object"||Array.isArray(s))return{error:"updateInfoUrl: JSON must be an object"};let i=s,a=null;typeof i.message=="string"&&i.message.trim()&&(a=i.message.trim().slice(0,1500));let l=null,d=i.link??i.url;return typeof d=="string"&&d.trim()&&(l=Od(d)),{message:a,link:l}}catch(t){return{error:`updateInfoUrl: ${String(t)}`}}}function Hy(e){return!Number.isFinite(e)||e<36e5?36e5:e>6048e5?6048e5:Math.floor(e)}async function xr(e,t){let n=Wy(t.updateCheckPackageName)?t.updateCheckPackageName.trim():"omnish",r=await Uy(n),o="version"in r?r.version:null,s="error"in r?r.error:null,i=!1;o&&!s&&(i=Ad(e,o)<0);let a=null,l=null,d=null,u=Od(t.updateInfoUrl);if(u){let m=await Dy(u);"error"in m?d=m.error:(a=m.message,l=m.link)}let c={runningVersion:e,checkedAtIso:new Date().toISOString(),registryPackage:n,registryLatest:o,registryError:s,updateAvailable:i,infoMessage:a,infoLink:l,infoError:d};return Id=c,c}function ms(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 fs(e){let t=!1,n=async()=>{if(t)return;let s=e.getConfig();if(!s.updateCheckEnabled)return;let i=Hy(s.updateCheckIntervalMs),a=Date.now();if(!(ca!==0&&a-ca<i)){ca=a;try{let l=await xr(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)}}pe();pe();function Qt(e){return(e??"").trim()}function By(){return Qt(process.env.OMNISH_PLATFORM_URL)||Qt(process.env.OMNISH_COMM_LAYER_URL)||Qt(process.env.OMNISH_TUNNEL_RELAY)}function jy(){return Qt(process.env.OMNISH_TOKEN)||Qt(process.env.OMNISH_DEVICE_TOKEN)||Qt(process.env.OMNISH_TUNNEL_TOKEN)}function ua(){return ht()}function Kn(){return jy()?"env":$().platformToken.trim()?"config":xt()?.token?"file":"default"}function $r(){let e=$();return ti(e.tunnelRelayUrl||$e)}function zn(){return By()?"env":$().tunnelRelayUrl.trim()?"config":xt()?.relayUrl?.trim()?"file":"default"}function da(){let e=Qt(process.env.OMNISH_DEVICE_ID);if(e)return e;let t=$().platformDeviceId.trim();if(t)return t}function pa(){return Qt(process.env.OMNISH_DEVICE_ID)?"env":$().platformDeviceId.trim()?"config":"default"}function fe(){let e=$r(),t=ua();if(!e||!t)return null;let n=da();return{platformUrl:e.replace(/\/$/,""),token:t,deviceId:n}}import Ld from"node:path";import{glob as Gy,stat as Jy}from"node:fs/promises";function hs(e){let t=e.trim();if(!t||t.startsWith("-- ")||t==="--")return null;let n=t.indexOf(" -- "),r,o;return n!==-1?(r=t.slice(0,n).trim(),o=t.slice(n+4).trim()||void 0):r=t,(r.startsWith('"')&&r.endsWith('"')||r.startsWith("'")&&r.endsWith("'"))&&(r=r.slice(1,-1)),r.trim()?{selectorPart:r,caption:o}:null}function Ky(e){return/[*?[]/.test(e)}function zy(e){return e.split(",").map(t=>t.trim()).filter(t=>t.length>0)}async function Rr(e,t){let n=zy(t),r=new Set,o=[];for(let s of n){if(Ky(s)){for await(let a of Gy(s,{cwd:e,withFileTypes:!1})){let l=Ld.resolve(e,a);r.has(l)||(r.add(l),o.push(l))}continue}let i=Ld.resolve(e,s);r.has(i)||(r.add(i),o.push(i))}return o}async function Cr(e){for(let t of e)try{if(!(await Jy(t)).isFile())return{ok:!1,error:`Not a file: ${t}`}}catch{return{ok:!1,error:`File not found: ${t}`}}return{ok:!0}}G();import Fd from"node:fs";import qy from"node:path";var pn="__omnish_shortcuts_global__",Nd=500,Wd=/^[a-zA-Z0-9][a-zA-Z0-9_-]{0,31}$/,Yy=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"]),Mr=new Map,_d=!1;function Qy(){try{let e=Fd.readFileSync(Wr,"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())}Mr.set(n,o)}}catch{}}function gs(){j(qy.dirname(Wr));let e={};for(let[t,n]of Mr)Object.entries(n).length>0&&(e[t]={...n});Fd.writeFileSync(Wr,JSON.stringify(e,null,2)+`
264
- `,{mode:384})}function ys(){_d||(Qy(),_d=!0)}function Vy(e){return Yy.has(e.trim().toLowerCase())}function wt(e){let t=e.trim();if(!t)return{ok:!1,error:"Name is empty."};let n=t.toLowerCase();return Wd.test(n)?Vy(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 ma(e){let t=e.replace(/\r\n/g,`
265
- `).replace(/\n/g," ").trim();return t?t.length>Nd?{ok:!1,error:`Body too long (max ${Nd} characters).`}:{ok:!0,body:t}:{ok:!1,error:"Body is empty."}}function yt(e,t){ys();let n=Mr.get(e);return!n&&t&&(n={},Mr.set(e,n)),n??{}}function Ud(e){let t=e.trim();if(/^--global$/i.test(t))return{mode:"global",remainder:""};if(/^-g$/i.test(t))return{mode:"global",remainder:""};if(/^--chat$/i.test(t))return{mode:"chat",remainder:""};if(/^-p$/i.test(t))return{mode:"chat",remainder:""};let n=/^--global\s+/i.exec(t);if(n)return{mode:"global",remainder:t.slice(n[0].length).trimStart()};let r=/^-g\s+/i.exec(t);if(r)return{mode:"global",remainder:t.slice(r[0].length).trimStart()};let o=/^--chat\s+/i.exec(t);if(o)return{mode:"chat",remainder:t.slice(o[0].length).trimStart()};let s=/^-p\s+/i.exec(t);return s?{mode:"chat",remainder:t.slice(s[0].length).trimStart()}:{mode:"resolved",remainder:t}}function fa(e){let t=e.trim();if(/^--global$/i.test(t))return{scope:"global",remainder:"",explicit:!0};if(/^-g$/i.test(t))return{scope:"global",remainder:"",explicit:!0};if(/^--chat$/i.test(t))return{scope:"chat",remainder:"",explicit:!0};if(/^-p$/i.test(t))return{scope:"chat",remainder:"",explicit:!0};let n=/^--global\s+([\s\S]*)$/i.exec(t);if(n?.[1]!==void 0)return{scope:"global",remainder:n[1].trimStart(),explicit:!0};let r=/^-g\s+([\s\S]*)$/i.exec(t);if(r?.[1]!==void 0)return{scope:"global",remainder:r[1].trimStart(),explicit:!0};let o=/^--chat\s+([\s\S]*)$/i.exec(t);if(o?.[1]!==void 0)return{scope:"chat",remainder:o[1].trimStart(),explicit:!0};let s=/^-p\s+([\s\S]*)$/i.exec(t);return s?.[1]!==void 0?{scope:"chat",remainder:s[1].trimStart(),explicit:!0}:{scope:"chat",remainder:t,explicit:!1}}function ha(e){let t=fa(e);return{scope:t.scope,remainder:t.remainder}}function Dd(e){let t=e.trim();return!t||/^-+$/i.test(t)?{filter:"merged"}:/^(?:--global|-g)$/i.test(t)?{filter:"global"}:/^(?:--chat|-p)$/i.test(t)?{filter:"chat"}:{filter:"merged",bad:t}}function Xy(e){let t=e.trim().toLowerCase();if(t==="--global"||t==="-g")return"global";if(t==="--chat"||t==="-p")return"chat"}function Hd(e){let t=e.trim().match(/^(\S+)\s+(--global|-g|--chat|-p)\s*$/i);if(!t?.[1]||!t[2])return;let n=Xy(t[2]);if(n)return{name:t[1],target:n}}function Zy(e,t){ys();let n=e,r=yt(n,!1),o=yt(pn,!1);if(t==="chat")return Object.entries(r).map(([a,l])=>({name:a,body:l,scope:"chat"})).sort((a,l)=>a.name.localeCompare(l.name));if(t==="global")return Object.entries(o).map(([a,l])=>({name:a,body:l,scope:"global"})).sort((a,l)=>a.name.localeCompare(l.name));let s=new Set([...Object.keys(r),...Object.keys(o)]),i=[];for(let a of[...s].sort()){if(a in r){let d=r[a];d!==void 0&&i.push({name:a,body:d,scope:"chat"});continue}let l=o[a];l!==void 0&&i.push({name:a,body:l,scope:"global"})}return i}function Bd(e,t="merged"){return Zy(e,t)}function ga(e,t){let n=t.trim().toLowerCase(),r=yt(e,!1)[n];return r!==void 0?r:yt(pn,!1)[n]}function ws(e,t){let n=wt(t);if(!n.ok)return;let r=n.normalized,o=yt(e,!1)[r];if(o!==void 0)return{name:r,body:o,scope:"chat"};let s=yt(pn,!1)[r];if(s!==void 0)return{name:r,body:s,scope:"global"}}function Et(e,t,n){let r=n.trim().toLowerCase(),o=e==="global"?pn:t;return yt(o,!1)[r]}function Tr(e,t,n,r="chat"){let o=wt(t);if(!o.ok)throw new Error(o.error);let s=ma(n);if(!s.ok)throw new Error(s.error);let i=r==="global"?pn:e,a=yt(i,!0);a[o.normalized]=s.body,gs()}function jd(e,t,n="chat"){let r=t.trim().toLowerCase(),o=n==="global"?pn:e;ys();let s=Mr.get(o);return!s||!(r in s)?!1:(delete s[r],gs(),!0)}function ya(e,t,n){let r=wt(t);if(!r.ok)return{ok:!1,error:r.error};let o=r.normalized,s=e;ys();let i=yt(s,!0),a=yt(pn,!0),l=i[o],d=a[o];if(n==="global"){if(l!==void 0){let u=ma(l);return u.ok?(a[o]=u.body,delete i[o],gs(),{ok:!0,kind:"moved",target:"global",name:o}):{ok:!1,error:u.error}}return d!==void 0?{ok:!0,kind:"noop",message:`Shortcut "${o}" is already shared on this gateway.`}:{ok:!1,error:`No shortcut "${o}" in this chat to share. Add with /shortcut add ${o} \u2026 or make the shared copy private with /shortcut set -p ${o}.`}}if(d!==void 0){let u=ma(d);return u.ok?(i[o]=u.body,delete a[o],gs(),{ok:!0,kind:"moved",target:"chat",name:o}):{ok:!1,error:u.error}}return l!==void 0?{ok:!0,kind:"noop",message:`Shortcut "${o}" is already private to this chat.`}:{ok:!1,error:`No shared shortcut "${o}" to make private. Add with /shortcut add --global ${o} \u2026 or share from this chat with /shortcut set -g ${o}.`}}function Gd(e){let t=e.trim();return t.length>0&&!/\s/.test(t)&&Wd.test(t.toLowerCase())}import Jd from"node:fs";var Kd=64,ew=1024*1024;function zd(e){return e.fileReceiveMaxBytes>0?e.fileReceiveMaxBytes:ew}function qd(e){let t;try{t=JSON.parse(e)}catch(o){return{ok:!1,error:`Invalid JSON: ${String(o)}`}}let n;if(Array.isArray(t))n=t;else if(t&&typeof t=="object"&&!Array.isArray(t)){let o=t,s=Object.keys(o);if(s.length!==1||s[0]!=="tasks")return{ok:!1,error:'JSON must be an array or a single-key object: { "tasks": [ \u2026 ] }.'};let i=o.tasks;if(!Array.isArray(i))return{ok:!1,error:'"tasks" must be an array.'};n=i}else return{ok:!1,error:'JSON must be an array or { "tasks": [ \u2026 ] }.'};if(n.length===0)return{ok:!1,error:"Queue JSON must contain at least one job."};if(n.length>Kd)return{ok:!1,error:`Too many jobs (max ${Kd}).`};let r=[];for(let o=0;o<n.length;o++){let s=n[o];if(!s||typeof s!="object"||Array.isArray(s))return{ok:!1,error:`Job ${o+1}: must be an object with "recipe" and "task".`};let i=s;if(Object.keys(i).length!==2||typeof i.recipe!="string"||typeof i.task!="string")return{ok:!1,error:`Job ${o+1}: must contain only "recipe" and "task" string fields.`};r.push({recipe:i.recipe,task:i.task})}return{ok:!0,jobs:r}}function Yd(e,t,n){let r=[];for(let o=0;o<n.length;o++){let{recipe:s,task:i}=n[o],a=je(e,t,s);if(!a)return{ok:!1,error:`Job ${o+1}: unknown recipe "${s}".`};let l=a.taskEnv??"OMNISH_TASK";if(!Pn(a.command,l))return{ok:!1,error:`Job ${o+1}: recipe "${s}" command must reference "$${l}".`};let d=vo(i,t.recipesMaxTaskChars);if(!d.ok)return{ok:!1,error:`Job ${o+1}: ${d.error}`};let u=a.promptTemplate?xo(a.promptTemplate,l,d.task):d.task,c={[l]:u};r.push({command:a.command,extraEnv:c,recipeLabel:s})}return{ok:!0,items:r}}function wa(e,t){let n;try{n=Jd.statSync(e)}catch{return{ok:!1,error:`Cannot read file: ${e}`}}if(!n.isFile())return{ok:!1,error:`Not a file: ${e}`};if(n.size>t)return{ok:!1,error:`File too large (max ${t} bytes for queue load).`};try{return{ok:!0,text:Jd.readFileSync(e,"utf8")}}catch(r){return{ok:!1,error:String(r)}}}G();import Qd from"node:fs";import qn from"node:process";var tw=120;function Pt(e){return e.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;")}function Vd(e){let t=e.trim();return t==="/service"||t.startsWith("/service ")?t.slice(8).trim():null}async function Xd(e,t){let n=t.trim().split(/\s+/),r=(n[0]??"").toLowerCase();if(!t.trim()||r==="help")return Z(nc(e));if(r==="status"){let s=nn(),i=(()=>{try{return Qd.existsSync(le)?`gateway.pid: ${Qd.readFileSync(le,"utf8").trim()}`:"gateway.pid: (missing)"}catch(m){return`gateway.pid: (read error: ${String(m)})`}})(),a=qn.env.OMNISH_BACKGROUND_GATEWAY==="1"?"This process: background gateway (OMNISH_BACKGROUND_GATEWAY=1).":"This process: foreground gateway session.",l=typeof qn.env.OMNISH_HOME=="string"&&qn.env.OMNISH_HOME.trim()?`OMNISH_HOME env: ${qn.env.OMNISH_HOME.trim()}`:"OMNISH_HOME env: (not set \u2014 using default data dir)",d=s.error?s.error:`Node: ${s.nodePath}
266
- Script: ${s.scriptPath}`,u=["*Service status*","",`platform: ${qn.platform}`,a,l,`data dir: ${H}`,i,`default log: ${Ne}`,"",d,"",e.serviceInstallFromChat?"Install from chat: enabled (/service install).":"Install from chat: off \u2014 `/config set serviceInstallFromChat true` to allow /service install."].join(`
267
- `),c=["<b>Service status</b>","",`<code>${Pt(qn.platform)}</code>`,`<br/><code>${Pt(a)}</code>`,`<br/><code>${Pt(l)}</code>`,`<br/>data dir: <code>${Pt(H)}</code>`,`<br/><code>${Pt(i)}</code>`,`<br/>default log: <code>${Pt(Ne)}</code>`,"",`<pre>${Pt(d)}</pre>`,"",e.serviceInstallFromChat?"Install from chat: enabled.":"Install from chat: off \u2014 <code>/config set serviceInstallFromChat true</code>."].join(`
268
- `);return ue(u,c)}if(r==="instructions"){let s=nn();if(s.error)return p(s.error);let i=fo(s);return p(`*Install hints*
261
+ `)}var ta=null;function rs(e,t){if(ta)return{stop:()=>{}};let n=Sy.createServer((r,o)=>{if(r.method!=="POST"){o.writeHead(405,{"Content-Type":"application/json"}),o.end(JSON.stringify({ok:!1,error:"Method not allowed"}));return}let s=r.headers.authorization,i=s?.startsWith("Bearer ")?s.slice(7):void 0,a=new URL(r.url??"/",`http://${r.headers.host??"localhost"}`).searchParams.get("token")??void 0;if(!xy(i??a,e.token)){o.writeHead(401,{"Content-Type":"application/json"}),o.end(JSON.stringify({ok:!1,error:"Unauthorized"}));return}let d="",u=0;r.on("data",c=>{if(u+=c.length,u>vy){o.writeHead(413,{"Content-Type":"application/json"}),o.end(JSON.stringify({ok:!1,error:"Payload too large"})),r.destroy();return}d+=c.toString("utf8")}),r.on("end",()=>{let c;try{c=JSON.parse(d)}catch{o.writeHead(400,{"Content-Type":"application/json"}),o.end(JSON.stringify({ok:!1,error:"Invalid JSON"}));return}let f=new URL(r.url??"/",`http://${r.headers.host??"localhost"}`).searchParams.get("source")??r.headers["x-webhook-source"]??void 0;f&&(c.source=f);let h=typeof c.peerKey=="string"&&c.peerKey||t.getDefaultPeerKey();if(!h){o.writeHead(400,{"Content-Type":"application/json"}),o.end(JSON.stringify({ok:!1,error:"No target peer. Set peerKey in body or configure an allowlisted identity."}));return}let g=$y(c);t.sendToPeer(h,g).then(()=>{o.writeHead(200,{"Content-Type":"application/json"}),o.end(JSON.stringify({ok:!0}))},y=>{M.warn({err:String(y)},"webhook: sendToPeer failed"),o.writeHead(502,{"Content-Type":"application/json"}),o.end(JSON.stringify({ok:!1,error:"Failed to deliver message"}))})})});return n.listen(e.port,e.host,()=>{let r=n.address(),o=typeof r=="object"&&r?r.port:e.port;M.info({port:o,host:e.host},"webhook receiver listening")}),n.on("error",r=>{M.error({err:String(r)},"webhook receiver error")}),ta=n,{stop:()=>{try{n.close()}catch{}ta=null}}}me();import tb from"node:crypto";import Na from"node:fs";be();import Ry from"node:fs";function bd(e,t,n){let r=e.fileReceiveMaxBytes,o;try{o=Buffer.from(n.dataBase64,"base64")}catch{return{mediaError:"Invalid inbound media payload."}}if(r>0&&o.length>r)return{mediaError:`Media too large (max ${r} bytes).`};let s;try{s=Ut(e,t)}catch(a){return{mediaError:String(a)}}let i=Hn(s,t,n.name);try{return Ry.writeFileSync(i,o,{mode:384}),{mediaSavedPath:i}}catch{return{mediaError:"Could not write media to inbox."}}}G();Cr();be();import da from"ws";import Iy from"ws";var Ay=["/platform/device","/control/device"];function as(e){let t=e.replace(/\/$/,"");return Ay.map(n=>{let r=new URL(n,t);return r.protocol=r.protocol==="https:"?"wss:":"ws:",r.toString()})}var Oy=Math.ceil(md*1.4)+512*1024;function Ly(e){let t=String(e instanceof Error?e.message:e);return/Unexpected server response:\s*400/.test(t)||/Unexpected server response:\s*404/.test(t)}async function xd(e,t){let n=as(e),r=null;for(let s of n)try{return{ws:await new Promise((a,l)=>{let d=new Iy(s,{headers:{Authorization:`Bearer ${t}`},maxPayload:Oy});d.once("open",()=>a(d)),d.once("error",l)}),pathname:new URL(s).pathname}}catch(i){if(r=i instanceof Error?i:new Error(String(i)),Ly(i))continue;throw r}let o="Could not open a device WebSocket. Redeploy the relay (for /control/device fallback) and ensure /platform/* or /control/* route to port 8788.";throw r&&/Unexpected server response:\s*400/.test(r.message)?new Error(`${r.message} \u2014 ${o}`):new Error(r?`${r.message} \u2014 ${o}`:o)}var Ny=1e3,_y=6e4,ls=class{constructor(t){this.options=t}ws=null;stopped=!1;pingTimer=null;registerResolve=null;registerReject=null;registeredAccount=null;registeredDeviceId=null;reconnectAttempt=0;reconnectTimer=null;outboundQueue=[];getRegisteredAccount(){return this.registeredAccount}async connect(){return this.connectOnce()}async connectOnce(){let{env:t}=this.options,n=new Promise((a,l)=>{this.registerResolve=a,this.registerReject=l}),{ws:r,pathname:o}=await xd(t.platformUrl,t.token);this.ws=r,o!=="/platform/device"&&M.info({pathname:o},"platform device websocket connected via fallback path"),r.on("message",a=>{this.handleMessage(a.toString())}),r.on("close",()=>{this.stopped||(M.warn("platform device websocket closed; scheduling reconnect"),this.scheduleReconnect())}),r.on("error",a=>{M.warn({err:String(a)},"platform device websocket error")});let s=setTimeout(()=>{this.registerReject?.(new Error("Platform device register timeout (15s)"))},15e3);this.sendRaw({type:"register",...t.deviceId?{deviceId:t.deviceId}:{},label:process.env.OMNISH_DEVICE_LABEL?.trim()||"default"});let i=await n;return clearTimeout(s),this.registeredDeviceId=i.deviceId,this.reconnectAttempt=0,this.pingTimer&&clearInterval(this.pingTimer),this.pingTimer=setInterval(()=>{this.sendRaw({type:"ping"})},3e4),this.flushOutboundQueue(),i}scheduleReconnect(){if(this.stopped||this.reconnectTimer)return;this.pingTimer&&(clearInterval(this.pingTimer),this.pingTimer=null),this.ws=null;let t=Math.min(Ny*2**this.reconnectAttempt,_y);this.reconnectAttempt+=1,this.reconnectTimer=setTimeout(()=>{this.reconnectTimer=null,!this.stopped&&this.connectOnce().catch(n=>{M.warn({err:String(n)},"platform device reconnect failed"),this.scheduleReconnect()})},t),this.reconnectTimer.unref?.()}async handleMessage(t){let n=hd(t);if(n){if(n.type==="registered"&&"deviceId"in n){n.account&&(this.registeredAccount=n.account),this.registerResolve?.({deviceId:n.deviceId,...n.account?{account:n.account}:{}}),this.registerResolve=null,this.registerReject=null;return}if(n.type==="message"){await this.options.onMessage(n);return}if(n.type==="reply_error"){await this.options.onReplyError?.(n.peerKey,n.error,n.messageId);return}n.type==="error"&&(M.warn({message:n.message},"platform error"),this.registerReject?.(new Error(n.message)),this.registerResolve=null,this.registerReject=null)}}sendReply(t,n,r,o){this.enqueue({type:"reply",peerKey:t,...n?{body:n}:{},...r?{messageId:r}:{},...o?.length?{files:o}:{}})}sendRoutedReply(t,n){this.enqueue({type:"reply",peerKey:t,...n})}enqueue(t){if(this.ws?.readyState===da.OPEN){this.sendRaw(t);return}if(this.outboundQueue.length>=fd){M.warn("platform outbound queue full; dropping reply");return}this.outboundQueue.push(t)}flushOutboundQueue(){for(;this.outboundQueue.length>0&&this.ws?.readyState===da.OPEN;){let t=this.outboundQueue.shift();t&&this.sendRaw(t)}}sendRaw(t){this.ws?.readyState===da.OPEN&&this.ws.send(JSON.stringify(t))}stop(){this.stopped=!0,this.reconnectTimer&&(clearTimeout(this.reconnectTimer),this.reconnectTimer=null),this.pingTimer&&(clearInterval(this.pingTimer),this.pingTimer=null);try{this.ws?.close()}catch{}this.ws=null,this.outboundQueue.length=0}};import ha from"node:readline/promises";import{stdin as ga,stdout as hs}from"node:process";me();var Fy=/^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$/i;function Rd(e){let t=e.trim().toLowerCase();return t?t.length>63?"Tunnel name must be at most 63 characters.":Fy.test(t)?null:"Tunnel name may only use letters, digits, and hyphens.":"Tunnel name must not be empty."}function Cd(e){let t=Number.parseInt(e,10);return!Number.isInteger(t)||t<1||t>65535?null:t}function $d(e,t){let n="127.0.0.1",r,o,s=!1,i=[];for(let d=0;d<t.length;d++){let u=t[d];if(u==="--host"){let c=t[++d];if(!c)return{kind:"error",message:"--host requires an address."};n=c;continue}if(u.startsWith("--host=")){n=u.slice(7);continue}if(u==="--relay"){let c=t[++d];if(!c)return{kind:"error",message:"--relay requires a URL."};r=c;continue}if(u.startsWith("--relay=")){r=u.slice(8);continue}if(u==="--name"){let c=t[++d];if(!c)return{kind:"error",message:"--name requires a slug."};o=c;continue}if(u.startsWith("--name=")){o=u.slice(7);continue}if(u==="--background"||u==="-b"){s=!0;continue}i.push(u)}let a=i[0];if(!a)return{kind:"error",message:`Usage: omnish tunnel ${e} <port> [--host <addr>] [--name <slug>] [--relay <url>] [--background]`};let l=Cd(a);if(l===null)return{kind:"error",message:"Port must be an integer between 1 and 65535."};if(o){let d=Rd(o);if(d)return{kind:"error",message:d}}return{kind:"expose",options:{kind:e,port:l,host:n,relayUrl:r??"",name:o,background:s}}}function pa(e){let[t,...n]=e,r=(t??"").trim().toLowerCase();if(!r||r==="help"||r==="-h"||r==="--help")return{kind:"help"};if(r==="signup"){let o,s,i,a;for(let l=0;l<n.length;l++){let d=n[l];if(d==="--email"){o=n[++l];continue}if(d.startsWith("--email=")){o=d.slice(8);continue}if(d==="--phone"){s=n[++l];continue}if(d.startsWith("--phone=")){s=d.slice(8);continue}if(d==="--password"){i=n[++l];continue}if(d.startsWith("--password=")){i=d.slice(11);continue}if(d==="--relay"){a=n[++l];continue}if(d.startsWith("--relay=")){a=d.slice(8);continue}}return{kind:"signup",email:o,phone:s,password:i,relayUrl:a}}if(r==="login"){let o,s,i,a,l;for(let d=0;d<n.length;d++){let u=n[d];if(u==="--token"){o=n[++d];continue}if(u.startsWith("--token=")){o=u.slice(8);continue}if(u==="--email"){s=n[++d];continue}if(u.startsWith("--email=")){s=u.slice(8);continue}if(u==="--phone"){i=n[++d];continue}if(u.startsWith("--phone=")){i=u.slice(8);continue}if(u==="--password"){a=n[++d];continue}if(u.startsWith("--password=")){a=u.slice(11);continue}if(u==="--relay"){l=n[++d];continue}if(u.startsWith("--relay=")){l=u.slice(8);continue}o||(o=u)}return{kind:"login",token:o,email:s,phone:i,password:a,relayUrl:l}}if(r==="logout")return{kind:"logout"};if(r==="list")return{kind:"list"};if(r==="status"){let o;for(let s=0;s<n.length;s++){let i=n[s];if(i==="--relay"){o=n[s+1],s++;continue}i.startsWith("--relay=")&&(o=i.slice(8))}return{kind:"status",relayUrl:o}}if(r==="stop"){let o=n[0]?.trim();return o?{kind:"stop",target:o}:{kind:"error",message:"Usage: omnish tunnel stop <id|slug>"}}return r==="http"?$d("http",n):r==="tcp"?$d("tcp",n):{kind:"error",message:`Unknown tunnel subcommand "${t}". Try: omnish tunnel help`}}function Wy(e){let t=[],n="",r=null;for(let o=0;o<e.length;o++){let s=e[o];if(r){s===r?r=null:n+=s;continue}if(s==='"'||s==="'"){r=s;continue}if(/\s/.test(s)){n.length&&(t.push(n),n="");continue}n+=s}return n.length&&t.push(n),t}function Md(e){let t=e.trim();if(!t||t==="help")return{kind:"help"};let n=Wy(t),r=n[0]?.toLowerCase();if(r==="login"||r==="logout"||r==="status"||r==="signup")return pa(n);if(t.toLowerCase()==="list"||t.toLowerCase()==="ls")return{kind:"list"};let o=t.match(/^stop\s+(\S+)\s*$/i);if(o)return{kind:"stop",target:o[1]};let s=t.match(/^(http|tcp)\s+(\d+)(?:\s+--name\s+(\S+))?(?:\s+--host\s+(\S+))?\s*$/i);if(s){let i=s[1].toLowerCase(),a=Cd(s[2]);if(a===null)return{kind:"error",message:"Port must be an integer between 1 and 65535."};let l=s[3],d=s[4]??"127.0.0.1";if(l){let u=Rd(l);if(u)return{kind:"error",message:u}}return{kind:"expose",options:{kind:i,port:a,host:d,relayUrl:"",name:l,background:!0}}}return{kind:"error",message:"Usage: /tunnel login \u2026 | /tunnel status | /tunnel http <port> | /tunnel tcp <port> | /tunnels | /tunnel stop <id>"}}import Uy from"ws";import{URL as Td}from"node:url";function Yn(e){let t=new Td(e);return t.protocol=t.protocol==="https:"?"wss:":"ws:",(!t.pathname||t.pathname==="/")&&(t.pathname="/control"),t.toString()}function Ed(e){return new Td("/health",e).toString()}async function cs(e,t,n=1e4){let r=t.trim();if(!r)return{ok:!1,healthOk:!1,controlOk:!1,error:"Tunnel token is missing."};let o=Ed(e),s=!1,i;try{let d=await fetch(o,{method:"GET",signal:AbortSignal.timeout(n)});if(!d.ok)return{ok:!1,healthOk:!1,controlOk:!1,error:`Health HTTP ${d.status} (${o})`};let u=await d.json();if(!u?.ok)return{ok:!1,healthOk:!1,controlOk:!1,error:"Health JSON missing ok:true"};s=!0,typeof u.version=="string"&&(i=u.version)}catch(d){return{ok:!1,healthOk:!1,controlOk:!1,error:`Health fetch failed: ${String(d)}`}}let a=Yn(e),l=!1;try{await new Promise((d,u)=>{let c=new Uy(a,{headers:{Authorization:`Bearer ${r}`}}),m=setTimeout(()=>{c.terminate(),u(new Error("WSS auth timeout"))},n);c.once("message",f=>{clearTimeout(m);try{let h=JSON.parse(f.toString());if(h.type!=="auth_ok"){u(new Error(`Expected auth_ok, got ${h.type??"?"}`));return}c.close(),d()}catch(h){u(h)}}),c.once("error",f=>{clearTimeout(m),u(f)})}),l=!0}catch(d){return{ok:!1,healthOk:!0,healthVersion:i,controlOk:!1,error:`Control WebSocket: ${String(d)}`}}return{ok:!0,healthOk:s,healthVersion:i,controlOk:!0}}rn();on();on();rn();import Id from"node:crypto";import Dy from"node:http";import Hy from"node:net";import Xt from"ws";function us(e,t,n=Buffer.alloc(0)){let r=Buffer.allocUnsafe(9+n.length);return r.writeUInt8(e,0),r.writeUInt32BE(t,1),r.writeUInt32BE(n.length,5),n.copy(r,9),r}function Pd(e){if(e.length<9)return null;let t=e.readUInt8(0),n=e.readUInt32BE(1),r=e.readUInt32BE(5);return e.length<9+r?null:{frameType:t,streamId:n,payload:e.subarray(9,9+r)}}function ma(e){try{let t=JSON.parse(e);return!t||typeof t!="object"||typeof t.type!="string"?null:t}catch{return null}}function Qn(e){return JSON.stringify(e)}function By(e){let t=Id.createHash("sha1").update(e,"utf8").digest("hex").slice(0,8);return Number.parseInt(t,16)>>>0}var ds=class{constructor(t){this.opts=t;let n=Id.randomBytes(4).toString("hex");this.record={id:n,kind:t.expose.kind,slug:t.expose.name?.trim().toLowerCase()??"",localHost:t.expose.host,localPort:t.expose.port,publicUrl:"",status:"connecting",startedAt:new Date().toISOString()}}ws=null;record;tcpStreams=new Map;tcpStreamIds=new Map;stopped=!1;pingTimer=null;getRecord(){return{...this.record}}setStatus(t,n){this.record.status=t,this.record.error=n,this.opts.onStatus?.(this.getRecord())}async start(){if(this.stopped)throw new Error("Tunnel client already stopped.");let t=Yn(this.opts.relayUrl),n=new Xt(t,{headers:{Authorization:`Bearer ${this.opts.token}`}});this.ws=n,await new Promise((o,s)=>{let i=d=>{l(),s(d)},a=()=>{l(),o()},l=()=>{n.off("error",i),n.off("open",a)};n.once("error",i),n.once("open",a)}),n.on("message",(o,s)=>{if(s){this.handleBinary(Buffer.isBuffer(o)?o:Buffer.from(o));return}let i=typeof o=="string"?o:o.toString("utf8");this.handleControl(i)}),n.on("close",()=>{this.stopped||this.setStatus("error","Relay connection closed."),this.cleanupTcpStreams()}),n.on("error",o=>{this.stopped||this.setStatus("error",String(o))}),this.pingTimer=setInterval(()=>{n.readyState===Xt.OPEN&&n.send(Qn({type:"ping"}))},3e4),n.send(Qn({type:"register",id:this.record.id,kind:this.opts.expose.kind,localHost:this.opts.expose.host,localPort:this.opts.expose.port,...this.opts.expose.name?{name:this.opts.expose.name}:{}}));let r=await this.waitForRegistered();return this.record.slug=r.slug,this.record.publicUrl=r.publicUrl,this.setStatus("active"),this.getRecord()}waitForRegistered(){let t=this.ws;return t?new Promise((n,r)=>{let o=s=>{if(typeof s!="string"&&!Buffer.isBuffer(s))return;let i=typeof s=="string"?s:s.toString("utf8"),a=ma(i);if(a){if(a.type==="registered"&&a.id===this.record.id){t.off("message",o),n(a);return}a.type==="error"&&(t.off("message",o),r(new Error(a.message)))}};t.on("message",o)}):Promise.reject(new Error("WebSocket not connected."))}async handleControl(t){let n=ma(t);if(n&&!(n.type==="pong"||n.type==="auth_ok")){if(n.type==="error"){this.setStatus("error",n.message);return}if(n.type==="http"){await this.handleHttpRequest(n);return}if(n.type==="tcp_open"){await this.handleTcpOpen(n.streamId);return}n.type==="tcp_close"&&this.closeTcpStream(n.streamId)}}handleBinary(t){let n=Pd(t);if(!n)return;let r=[...this.tcpStreamIds.entries()].find(([,s])=>s===n.streamId)?.[0];if(!r)return;let o=this.tcpStreams.get(r);if(o){if(n.frameType===2){o.write(n.payload);return}n.frameType===3&&(o.end(),this.tcpStreams.delete(r),this.tcpStreamIds.delete(r))}}async handleHttpRequest(t){let n=this.ws;if(!n||n.readyState!==Xt.OPEN)return;let r=t.bodyBase64?Buffer.from(t.bodyBase64,"base64"):void 0,o={host:this.opts.expose.host,port:this.opts.expose.port,method:t.method,path:t.path,headers:{...t.headers}};await new Promise(s=>{let i=Dy.request(o,a=>{let l=[];a.on("data",d=>l.push(Buffer.isBuffer(d)?d:Buffer.from(d))),a.on("end",()=>{let d=Buffer.concat(l);n.send(Qn({type:"http_res",requestId:t.requestId,status:a.statusCode??502,headers:a.headers,...d.length>0?{bodyBase64:d.toString("base64")}:{}})),s()})});i.on("error",a=>{n.send(Qn({type:"http_res",requestId:t.requestId,status:502,headers:{"content-type":"text/plain"},bodyBase64:Buffer.from(String(a)).toString("base64")})),s()}),r&&r.length>0&&i.write(r),i.end()})}async handleTcpOpen(t){let n=this.ws;if(!n||n.readyState!==Xt.OPEN)return;let r=By(t);this.tcpStreamIds.set(t,r);let o=Hy.connect({host:this.opts.expose.host,port:this.opts.expose.port});this.tcpStreams.set(t,o),o.on("data",s=>{n.readyState===Xt.OPEN&&n.send(us(2,r,Buffer.isBuffer(s)?s:Buffer.from(s)))}),o.on("close",()=>{n.readyState===Xt.OPEN&&n.send(us(3,r)),this.tcpStreams.delete(t),this.tcpStreamIds.delete(t)}),o.on("error",()=>{n.readyState===Xt.OPEN&&n.send(us(3,r)),this.tcpStreams.delete(t),this.tcpStreamIds.delete(t)})}closeTcpStream(t){let n=this.tcpStreams.get(t);n&&n.destroy(),this.tcpStreams.delete(t),this.tcpStreamIds.delete(t)}cleanupTcpStreams(){for(let t of this.tcpStreams.values())t.destroy();this.tcpStreams.clear(),this.tcpStreamIds.clear()}async stop(){if(this.stopped)return;this.stopped=!0,this.pingTimer&&(clearInterval(this.pingTimer),this.pingTimer=null);let t=this.ws;t&&t.readyState===Xt.OPEN&&(t.send(Qn({type:"unregister",id:this.record.id})),t.close()),this.cleanupTcpStreams(),this.setStatus("stopped")}};var ps=class{clients=new Map;list(){return[...new Set(this.clients.values())].map(t=>t.getRecord())}getActiveCount(){return[...new Set(this.clients.values())].filter(t=>t.getRecord().status==="active").length}async expose(t,n){let r=gt();if(!r)throw new Error("No tunnel token configured. Run `omnish tunnel login` or set OMNISH_TUNNEL_TOKEN.");let o=t.tunnelMaxActive>0?t.tunnelMaxActive:5;if(this.getActiveCount()>=o)throw new Error(`Active tunnel limit reached (${o}). Stop one with \`omnish tunnel stop <id>\`.`);let s=n.relayUrl||rt(t.tunnelRelayUrl||$e),i=new ds({relayUrl:s,token:r,expose:n,onStatus:l=>{(l.status==="stopped"||l.status==="error")&&this.clients.delete(l.id)}}),a=await i.start();return this.clients.set(a.id,i),a.slug&&this.clients.set(a.slug,i),a}async stop(t){let n=t.trim().toLowerCase(),r=this.clients.get(n)??this.clients.get(t.trim());if(!r)return null;let o=r.getRecord();return await r.stop(),this.clients.delete(o.id),o.slug&&this.clients.delete(o.slug),r.getRecord()}async stopAll(){let t=[...new Set(this.clients.values())];await Promise.all(t.map(n=>n.stop())),this.clients.clear()}};async function Od(e,t){let n=await fetch(e,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(t),signal:AbortSignal.timeout(15e3)}),r=await n.json();return!n.ok||!r.token?{ok:!1,error:r.error??`HTTP ${n.status}`}:{ok:!0,token:r.token}}function ms(e,t){let n=new URL("/auth/signup",e).toString();return Od(n,{...t.email?{email:t.email}:{},...t.phone?{phone:t.phone}:{},password:t.password})}function fs(e,t){let n=new URL("/auth/login",e).toString();return Od(n,{...t.email?{email:t.email}:{},...t.phone?{phone:t.phone}:{},password:t.password})}var Vn=new ps;function gn(){return Vn}function jy(e){let t=[J(e,"omnish tunnel"),w(e,"Expose local HTTP or TCP ports through the omnish relay."),"",J(e,"Usage:"),` ${S(e,"omnish tunnel signup [--email <email>] [--phone <phone>] [--password <pass>] [--relay <url>]")}`,` ${S(e,"omnish tunnel login [--token <token>] [--relay <url>]")}`,` ${S(e,"omnish tunnel login --email <email> --password <pass> [--relay <url>]")}`,` ${S(e,"omnish tunnel logout")}`,` ${S(e,"omnish tunnel status [--relay <url>]")}`,` ${S(e,"omnish tunnel http <port> [--host <addr>] [--name <slug>] [--relay <url>] [--background]")}`,` ${S(e,"omnish tunnel tcp <port> [--host <addr>] [--name <slug>] [--relay <url>] [--background]")}`,` ${S(e,"omnish tunnel list")}`,` ${S(e,"omnish tunnel stop <id|slug>")}`,"",w(e,"Secrets live in ~/.omnish/tunnel-auth.json or OMNISH_TUNNEL_TOKEN."),w(e,`Default relay: ${$e}`),""];console.log(t.join(`
262
+ `))}async function Gy(){let e=ha.createInterface({input:ga,output:hs});try{return(await e.question("Tunnel token: ")).trim()}finally{e.close()}}async function Ld(e){let t=pa(e),n=hs,r=process.stderr;if(t.kind==="help"){jy(n);return}if(t.kind==="error"){console.error(C(r,t.message)),process.exitCode=1;return}let o=$();if(t.kind==="signup"){let s=t.relayUrl||rt(o.tunnelRelayUrl||$e),i=ha.createInterface({input:ga,output:hs});try{let a=t.email?.trim()||(await i.question("Email (or leave empty for phone): ")).trim(),l=t.phone?.trim()||"";if(a||(l=l||(await i.question("Phone: ")).trim()),!a&&!l){console.error(C(r,"Email or phone is required.")),process.exitCode=1;return}let d=t.password||(await i.question("Password (min 8 chars): ")).trim();if(d.length<8){console.error(C(r,"Password must be at least 8 characters.")),process.exitCode=1;return}let u=await ms(s,{...a?{email:a}:{},...l?{phone:l}:{},password:d});if(!u.ok){console.error(C(r,u.error)),process.exitCode=1;return}ht({token:u.token,...t.relayUrl?{relayUrl:t.relayUrl}:{}}),console.log(B(n,"Account created. Token saved."))}finally{i.close()}return}if(t.kind==="login"){if(t.email||t.phone){let a=t.relayUrl||rt(o.tunnelRelayUrl||$e),l=ha.createInterface({input:ga,output:hs});try{let d=t.password||(await l.question("Password: ")).trim(),u=await fs(a,{...t.email?{email:t.email}:{},...t.phone?{phone:t.phone}:{},password:d});if(!u.ok){console.error(C(r,u.error)),process.exitCode=1;return}ht({token:u.token,...t.relayUrl?{relayUrl:t.relayUrl}:{}}),console.log(B(n,"Logged in. Token saved."))}finally{l.close()}return}let i=t.token?.trim()||await Gy();if(!i){console.error(C(r,"Tunnel token is required.")),process.exitCode=1;return}ht({token:i,...t.relayUrl?{relayUrl:t.relayUrl}:{}}),console.log(B(n,"Tunnel token saved."));return}if(t.kind==="logout"){mo(),console.log(B(n,"Tunnel token removed."));return}if(t.kind==="status"){let s=t.relayUrl||rt(o.tunnelRelayUrl||$e),i=gt(),a=!!process.env.OMNISH_TUNNEL_TOKEN?.trim(),l=await cs(s,i);console.log(`${w(n,"relay:")} ${S(n,s)}`),console.log(`${w(n,"token:")} ${S(n,i?`configured${a?" (OMNISH_TUNNEL_TOKEN)":""}`:C(r,"missing"))}`),console.log(`${w(n,"health:")} ${l.healthOk?S(n,`ok${l.healthVersion?` (${l.healthVersion})`:""}`):C(r,"fail")}`),console.log(`${w(n,"control:")} ${l.controlOk?S(n,"auth ok"):C(r,"fail")}${l.error&&!l.ok?` \u2014 ${l.error}`:""}`),console.log(`${w(n,"active:")} ${S(n,String(Vn.getActiveCount()))}`);return}if(t.kind==="list"){let s=Vn.list();if(s.length===0){console.log(B(n,"(no active tunnels)"));return}for(let i of s)console.log(`${S(n,i.id)} ${i.kind} ${i.status} ${i.publicUrl||"(pending)"}
263
+ ${w(n,`${i.localHost}:${i.localPort}`)}`);return}if(t.kind==="stop"){let s=await Vn.stop(t.target);if(!s){console.error(C(r,`No active tunnel matched "${t.target}".`)),process.exitCode=1;return}console.log(B(n,`Stopped tunnel ${s.id}.`));return}if(t.kind==="expose"){let s=t.options.relayUrl||rt(o.tunnelRelayUrl||$e),i=await Vn.expose(o,{...t.options,relayUrl:s});console.log(B(n,`${i.kind.toUpperCase()} tunnel active`)),console.log(`${w(n,"public:")} ${S(n,i.publicUrl)}`),console.log(`${w(n,"local:")} ${S(n,`${i.localHost}:${i.localPort}`)}`),console.log(`${w(n,"id:")} ${S(n,i.id)}`),t.options.background||(console.log(w(n,"Press Ctrl+C to stop.")),await new Promise(a=>{let l=async()=>{await Vn.stop(i.id),a()};process.once("SIGINT",l),process.once("SIGTERM",l)}))}}function Nd(e){let t=e.trim().match(/^(\d+)\.(\d+)\.(\d+)/);return t?[Number(t[1]),Number(t[2]),Number(t[3])]:null}function _d(e,t){let n=Nd(e),r=Nd(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 Jy="https://registry.npmjs.org",Fd=null,ya=0;function Mr(){return Fd}function Ky(e){let t=e.trim();return t.length<1||t.length>214?!1:!/\s/.test(t)}function Wd(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 zy(e){let t=e.trim(),n=`${Jy}/${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 qy(e){try{let t=await fetch(e,{headers:{Accept:"application/json"},signal:AbortSignal.timeout(15e3)});if(!t.ok)return{error:`updateInfoUrl: HTTP ${t.status}`};let n=await t.arrayBuffer(),r=n.byteLength>65536?n.slice(0,65536):n,o=new TextDecoder("utf-8",{fatal:!1}).decode(r),s;try{s=JSON.parse(o)}catch{return{error:"updateInfoUrl: body is not JSON"}}if(!s||typeof s!="object"||Array.isArray(s))return{error:"updateInfoUrl: JSON must be an object"};let i=s,a=null;typeof i.message=="string"&&i.message.trim()&&(a=i.message.trim().slice(0,1500));let l=null,d=i.link??i.url;return typeof d=="string"&&d.trim()&&(l=Wd(d)),{message:a,link:l}}catch(t){return{error:`updateInfoUrl: ${String(t)}`}}}function Yy(e){return!Number.isFinite(e)||e<36e5?36e5:e>6048e5?6048e5:Math.floor(e)}async function Tr(e,t){let n=Ky(t.updateCheckPackageName)?t.updateCheckPackageName.trim():"omnish",r=await zy(n),o="version"in r?r.version:null,s="error"in r?r.error:null,i=!1;o&&!s&&(i=_d(e,o)<0);let a=null,l=null,d=null,u=Wd(t.updateInfoUrl);if(u){let m=await qy(u);"error"in m?d=m.error:(a=m.message,l=m.link)}let c={runningVersion:e,checkedAtIso:new Date().toISOString(),registryPackage:n,registryLatest:o,registryError:s,updateAvailable:i,infoMessage:a,infoLink:l,infoError:d};return Fd=c,c}function gs(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 ys(e){let t=!1,n=async()=>{if(t)return;let s=e.getConfig();if(!s.updateCheckEnabled)return;let i=Yy(s.updateCheckIntervalMs),a=Date.now();if(!(ya!==0&&a-ya<i)){ya=a;try{let l=await Tr(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)}}me();it();import Ud from"node:path";import{glob as Qy,stat as Vy}from"node:fs/promises";function ws(e){let t=e.trim();if(!t||t.startsWith("-- ")||t==="--")return null;let n=t.indexOf(" -- "),r,o;return n!==-1?(r=t.slice(0,n).trim(),o=t.slice(n+4).trim()||void 0):r=t,(r.startsWith('"')&&r.endsWith('"')||r.startsWith("'")&&r.endsWith("'"))&&(r=r.slice(1,-1)),r.trim()?{selectorPart:r,caption:o}:null}function Xy(e){return/[*?[]/.test(e)}function Zy(e){return e.split(",").map(t=>t.trim()).filter(t=>t.length>0)}async function Er(e,t){let n=Zy(t),r=new Set,o=[];for(let s of n){if(Xy(s)){for await(let a of Qy(s,{cwd:e,withFileTypes:!1})){let l=Ud.resolve(e,a);r.has(l)||(r.add(l),o.push(l))}continue}let i=Ud.resolve(e,s);r.has(i)||(r.add(i),o.push(i))}return o}async function Pr(e){for(let t of e)try{if(!(await Vy(t)).isFile())return{ok:!1,error:`Not a file: ${t}`}}catch{return{ok:!1,error:`File not found: ${t}`}}return{ok:!0}}G();import Bd from"node:fs";import ew from"node:path";var yn="__omnish_shortcuts_global__",Dd=500,jd=/^[a-zA-Z0-9][a-zA-Z0-9_-]{0,31}$/,tw=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"]),Ar=new Map,Hd=!1;function nw(){try{let e=Bd.readFileSync(Br,"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())}Ar.set(n,o)}}catch{}}function bs(){j(ew.dirname(Br));let e={};for(let[t,n]of Ar)Object.entries(n).length>0&&(e[t]={...n});Bd.writeFileSync(Br,JSON.stringify(e,null,2)+`
264
+ `,{mode:384})}function ks(){Hd||(nw(),Hd=!0)}function rw(e){return tw.has(e.trim().toLowerCase())}function bt(e){let t=e.trim();if(!t)return{ok:!1,error:"Name is empty."};let n=t.toLowerCase();return jd.test(n)?rw(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 wa(e){let t=e.replace(/\r\n/g,`
265
+ `).replace(/\n/g," ").trim();return t?t.length>Dd?{ok:!1,error:`Body too long (max ${Dd} characters).`}:{ok:!0,body:t}:{ok:!1,error:"Body is empty."}}function wt(e,t){ks();let n=Ar.get(e);return!n&&t&&(n={},Ar.set(e,n)),n??{}}function Gd(e){let t=e.trim();if(/^--global$/i.test(t))return{mode:"global",remainder:""};if(/^-g$/i.test(t))return{mode:"global",remainder:""};if(/^--chat$/i.test(t))return{mode:"chat",remainder:""};if(/^-p$/i.test(t))return{mode:"chat",remainder:""};let n=/^--global\s+/i.exec(t);if(n)return{mode:"global",remainder:t.slice(n[0].length).trimStart()};let r=/^-g\s+/i.exec(t);if(r)return{mode:"global",remainder:t.slice(r[0].length).trimStart()};let o=/^--chat\s+/i.exec(t);if(o)return{mode:"chat",remainder:t.slice(o[0].length).trimStart()};let s=/^-p\s+/i.exec(t);return s?{mode:"chat",remainder:t.slice(s[0].length).trimStart()}:{mode:"resolved",remainder:t}}function ba(e){let t=e.trim();if(/^--global$/i.test(t))return{scope:"global",remainder:"",explicit:!0};if(/^-g$/i.test(t))return{scope:"global",remainder:"",explicit:!0};if(/^--chat$/i.test(t))return{scope:"chat",remainder:"",explicit:!0};if(/^-p$/i.test(t))return{scope:"chat",remainder:"",explicit:!0};let n=/^--global\s+([\s\S]*)$/i.exec(t);if(n?.[1]!==void 0)return{scope:"global",remainder:n[1].trimStart(),explicit:!0};let r=/^-g\s+([\s\S]*)$/i.exec(t);if(r?.[1]!==void 0)return{scope:"global",remainder:r[1].trimStart(),explicit:!0};let o=/^--chat\s+([\s\S]*)$/i.exec(t);if(o?.[1]!==void 0)return{scope:"chat",remainder:o[1].trimStart(),explicit:!0};let s=/^-p\s+([\s\S]*)$/i.exec(t);return s?.[1]!==void 0?{scope:"chat",remainder:s[1].trimStart(),explicit:!0}:{scope:"chat",remainder:t,explicit:!1}}function ka(e){let t=ba(e);return{scope:t.scope,remainder:t.remainder}}function Jd(e){let t=e.trim();return!t||/^-+$/i.test(t)?{filter:"merged"}:/^(?:--global|-g)$/i.test(t)?{filter:"global"}:/^(?:--chat|-p)$/i.test(t)?{filter:"chat"}:{filter:"merged",bad:t}}function ow(e){let t=e.trim().toLowerCase();if(t==="--global"||t==="-g")return"global";if(t==="--chat"||t==="-p")return"chat"}function Kd(e){let t=e.trim().match(/^(\S+)\s+(--global|-g|--chat|-p)\s*$/i);if(!t?.[1]||!t[2])return;let n=ow(t[2]);if(n)return{name:t[1],target:n}}function sw(e,t){ks();let n=e,r=wt(n,!1),o=wt(yn,!1);if(t==="chat")return Object.entries(r).map(([a,l])=>({name:a,body:l,scope:"chat"})).sort((a,l)=>a.name.localeCompare(l.name));if(t==="global")return Object.entries(o).map(([a,l])=>({name:a,body:l,scope:"global"})).sort((a,l)=>a.name.localeCompare(l.name));let s=new Set([...Object.keys(r),...Object.keys(o)]),i=[];for(let a of[...s].sort()){if(a in r){let d=r[a];d!==void 0&&i.push({name:a,body:d,scope:"chat"});continue}let l=o[a];l!==void 0&&i.push({name:a,body:l,scope:"global"})}return i}function zd(e,t="merged"){return sw(e,t)}function Sa(e,t){let n=t.trim().toLowerCase(),r=wt(e,!1)[n];return r!==void 0?r:wt(yn,!1)[n]}function Ss(e,t){let n=bt(t);if(!n.ok)return;let r=n.normalized,o=wt(e,!1)[r];if(o!==void 0)return{name:r,body:o,scope:"chat"};let s=wt(yn,!1)[r];if(s!==void 0)return{name:r,body:s,scope:"global"}}function Pt(e,t,n){let r=n.trim().toLowerCase(),o=e==="global"?yn:t;return wt(o,!1)[r]}function Ir(e,t,n,r="chat"){let o=bt(t);if(!o.ok)throw new Error(o.error);let s=wa(n);if(!s.ok)throw new Error(s.error);let i=r==="global"?yn:e,a=wt(i,!0);a[o.normalized]=s.body,bs()}function qd(e,t,n="chat"){let r=t.trim().toLowerCase(),o=n==="global"?yn:e;ks();let s=Ar.get(o);return!s||!(r in s)?!1:(delete s[r],bs(),!0)}function va(e,t,n){let r=bt(t);if(!r.ok)return{ok:!1,error:r.error};let o=r.normalized,s=e;ks();let i=wt(s,!0),a=wt(yn,!0),l=i[o],d=a[o];if(n==="global"){if(l!==void 0){let u=wa(l);return u.ok?(a[o]=u.body,delete i[o],bs(),{ok:!0,kind:"moved",target:"global",name:o}):{ok:!1,error:u.error}}return d!==void 0?{ok:!0,kind:"noop",message:`Shortcut "${o}" is already shared on this gateway.`}:{ok:!1,error:`No shortcut "${o}" in this chat to share. Add with /shortcut add ${o} \u2026 or make the shared copy private with /shortcut set -p ${o}.`}}if(d!==void 0){let u=wa(d);return u.ok?(i[o]=u.body,delete a[o],bs(),{ok:!0,kind:"moved",target:"chat",name:o}):{ok:!1,error:u.error}}return l!==void 0?{ok:!0,kind:"noop",message:`Shortcut "${o}" is already private to this chat.`}:{ok:!1,error:`No shared shortcut "${o}" to make private. Add with /shortcut add --global ${o} \u2026 or share from this chat with /shortcut set -g ${o}.`}}function Yd(e){let t=e.trim();return t.length>0&&!/\s/.test(t)&&jd.test(t.toLowerCase())}import Qd from"node:fs";var Vd=64,iw=1024*1024;function Xd(e){return e.fileReceiveMaxBytes>0?e.fileReceiveMaxBytes:iw}function Zd(e){let t;try{t=JSON.parse(e)}catch(o){return{ok:!1,error:`Invalid JSON: ${String(o)}`}}let n;if(Array.isArray(t))n=t;else if(t&&typeof t=="object"&&!Array.isArray(t)){let o=t,s=Object.keys(o);if(s.length!==1||s[0]!=="tasks")return{ok:!1,error:'JSON must be an array or a single-key object: { "tasks": [ \u2026 ] }.'};let i=o.tasks;if(!Array.isArray(i))return{ok:!1,error:'"tasks" must be an array.'};n=i}else return{ok:!1,error:'JSON must be an array or { "tasks": [ \u2026 ] }.'};if(n.length===0)return{ok:!1,error:"Queue JSON must contain at least one job."};if(n.length>Vd)return{ok:!1,error:`Too many jobs (max ${Vd}).`};let r=[];for(let o=0;o<n.length;o++){let s=n[o];if(!s||typeof s!="object"||Array.isArray(s))return{ok:!1,error:`Job ${o+1}: must be an object with "recipe" and "task".`};let i=s;if(Object.keys(i).length!==2||typeof i.recipe!="string"||typeof i.task!="string")return{ok:!1,error:`Job ${o+1}: must contain only "recipe" and "task" string fields.`};r.push({recipe:i.recipe,task:i.task})}return{ok:!0,jobs:r}}function ep(e,t,n){let r=[];for(let o=0;o<n.length;o++){let{recipe:s,task:i}=n[o],a=je(e,t,s);if(!a)return{ok:!1,error:`Job ${o+1}: unknown recipe "${s}".`};let l=a.taskEnv??"OMNISH_TASK";if(!Nn(a.command,l))return{ok:!1,error:`Job ${o+1}: recipe "${s}" command must reference "$${l}".`};let d=Co(i,t.recipesMaxTaskChars);if(!d.ok)return{ok:!1,error:`Job ${o+1}: ${d.error}`};let u=a.promptTemplate?Mo(a.promptTemplate,l,d.task):d.task,c={[l]:u};r.push({command:a.command,extraEnv:c,recipeLabel:s})}return{ok:!0,items:r}}function xa(e,t){let n;try{n=Qd.statSync(e)}catch{return{ok:!1,error:`Cannot read file: ${e}`}}if(!n.isFile())return{ok:!1,error:`Not a file: ${e}`};if(n.size>t)return{ok:!1,error:`File too large (max ${t} bytes for queue load).`};try{return{ok:!0,text:Qd.readFileSync(e,"utf8")}}catch(r){return{ok:!1,error:String(r)}}}G();import tp from"node:fs";import Xn from"node:process";var aw=120;function At(e){return e.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;")}function np(e){let t=e.trim();return t==="/service"||t.startsWith("/service ")?t.slice(8).trim():null}async function rp(e,t){let n=t.trim().split(/\s+/),r=(n[0]??"").toLowerCase();if(!t.trim()||r==="help")return Z(lc(e));if(r==="status"){let s=an(),i=(()=>{try{return tp.existsSync(ce)?`gateway.pid: ${tp.readFileSync(ce,"utf8").trim()}`:"gateway.pid: (missing)"}catch(m){return`gateway.pid: (read error: ${String(m)})`}})(),a=Xn.env.OMNISH_BACKGROUND_GATEWAY==="1"?"This process: background gateway (OMNISH_BACKGROUND_GATEWAY=1).":"This process: foreground gateway session.",l=typeof Xn.env.OMNISH_HOME=="string"&&Xn.env.OMNISH_HOME.trim()?`OMNISH_HOME env: ${Xn.env.OMNISH_HOME.trim()}`:"OMNISH_HOME env: (not set \u2014 using default data dir)",d=s.error?s.error:`Node: ${s.nodePath}
266
+ Script: ${s.scriptPath}`,u=["*Service status*","",`platform: ${Xn.platform}`,a,l,`data dir: ${H}`,i,`default log: ${Ne}`,"",d,"",e.serviceInstallFromChat?"Install from chat: enabled (/service install).":"Install from chat: off \u2014 `/config set serviceInstallFromChat true` to allow /service install."].join(`
267
+ `),c=["<b>Service status</b>","",`<code>${At(Xn.platform)}</code>`,`<br/><code>${At(a)}</code>`,`<br/><code>${At(l)}</code>`,`<br/>data dir: <code>${At(H)}</code>`,`<br/><code>${At(i)}</code>`,`<br/>default log: <code>${At(Ne)}</code>`,"",`<pre>${At(d)}</pre>`,"",e.serviceInstallFromChat?"Install from chat: enabled.":"Install from chat: off \u2014 <code>/config set serviceInstallFromChat true</code>."].join(`
268
+ `);return de(u,c)}if(r==="instructions"){let s=an();if(s.error)return p(s.error);let i=wo(s);return p(`*Install hints*
269
269
 
270
- ${i}`)}if(r==="logs"){let s=n.length>=2?Number.parseInt(n[1],10):80,i=Number.isFinite(s)&&s>0?Math.min(s,tw):80,a=wo(Ne,i),l=[`*Gateway log* (last ${i} lines)
270
+ ${i}`)}if(r==="logs"){let s=n.length>=2?Number.parseInt(n[1],10):80,i=Number.isFinite(s)&&s>0?Math.min(s,aw):80,a=vo(Ne,i),l=[`*Gateway log* (last ${i} lines)
271
271
  ${Ne}
272
272
  `,"```",a,"```"].join(`
273
- `),d=`<b>Gateway log</b> (last ${i} lines)<br/><code>${Pt(Ne)}</code><pre>${Pt(a)}</pre>`;return ue(l,d)}if(r==="install"){if(!e.serviceInstallFromChat)return p("Install from chat is disabled. Same trust as shell \u2014 enable with:\n`/config set serviceInstallFromChat true`\nThen `/service install` again.");let s=ho();return p(s.ok?`*Installed*
273
+ `),d=`<b>Gateway log</b> (last ${i} lines)<br/><code>${At(Ne)}</code><pre>${At(a)}</pre>`;return de(l,d)}if(r==="install"){if(!e.serviceInstallFromChat)return p("Install from chat is disabled. Same trust as shell \u2014 enable with:\n`/config set serviceInstallFromChat true`\nThen `/service install` again.");let s=bo();return p(s.ok?`*Installed*
274
274
  ${s.detail}`:`*Install failed*
275
- ${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=go();return p(`*Uninstall*
276
- ${s.detail}`)}return p("Unknown /service command. Try /service help")}pe();function bs(){let e=fe();return e?.platformUrl?e.platformUrl.replace(/\/$/,""):($().tunnelRelayUrl||$e).trim().replace(/\/$/,"")}function ks(){let e=fe();return e?.token?{Authorization:`Bearer ${e.token}`}:{}}async function Er(e){try{return await e.json()}catch{return{error:`HTTP ${e.status}`}}}function nw(){return`Set platform URL: omnish config add tunnelRelayUrl ${$e} \u2014 publish: omnish platform login`}function ba(){return fe()?.token?{ok:!0}:{ok:!1,error:`Publish requires a platform account token. ${nw()}`}}async function Zd(e){let t=new URLSearchParams;e?.kind&&t.set("kind",e.kind),e?.limit&&t.set("limit",String(e.limit));let n=t.toString(),r=`${bs()}/v1/catalog/trending${n?`?${n}`:""}`,o=await fetch(r,{headers:ks()}),s=await Er(o);return o.ok?s:{error:s.error??`HTTP ${o.status}`}}async function ep(e,t){let n=new URLSearchParams;n.set("q",e),t?.kind&&n.set("kind",t.kind),t?.category&&n.set("category",t.category),t?.limit&&n.set("limit",String(t.limit));let r=`${bs()}/v1/catalog/search?${n}`,o=await fetch(r,{headers:ks()}),s=await Er(o);return o.ok?s:{error:s.error??`HTTP ${o.status}`}}async function tp(e){let t=encodeURIComponent(e.trim()),n=`${bs()}/v1/catalog/${t}`,r=await fetch(n,{headers:ks()}),o=await Er(r);return r.ok?o:{error:o.error??`HTTP ${r.status}`}}async function np(e){let t=encodeURIComponent(e.trim()),n=`${bs()}/v1/catalog/${t}/download`,r=await fetch(n,{method:"POST",headers:ks()}),o=await Er(r);return r.ok?o:{error:o.error??`HTTP ${r.status}`}}async function rp(e){let t=ba();if(!t.ok)return{error:t.error};let n=fe(),r=`${n.platformUrl.replace(/\/$/,"")}/v1/catalog`,o=await fetch(r,{method:"POST",headers:{"content-type":"application/json",Authorization:`Bearer ${n.token}`},body:JSON.stringify(e)}),s=await Er(o);return o.ok?s:{error:s.error??`HTTP ${o.status}`}}function op(e){return e==="global"?"global":"chat"}function sp(e,t,n,r="global"){switch(e.kind){case"recipe":{let o=e.payload,s=Be({...o,dangerous:void 0}),i=on(s);if(!i.ok)return{ok:!1,error:i.error};let a=gt(e.name);if(!a.ok)return{ok:!1,error:a.error};try{An(t,a.normalized,s,op(r))}catch(d){return{ok:!1,error:String(d.message??d)}}let l=r==="global"?`/run ${a.normalized} <task>`:`/run ${a.normalized} <task> (this chat)`;return{ok:!0,message:`Installed recipe "${a.normalized}" (${r}). ${l}`}}case"app":{let o=e.payload;if(!o.command?.trim())return{ok:!1,error:"App payload has no command."};let s=Be({command:o.command.trim(),label:o.label??e.title,description:o.description??e.description,category:e.category||"app"}),i=gt(e.name);if(!i.ok)return{ok:!1,error:i.error};try{An(t,i.normalized,s,op(r),{skipCommandValidation:!0})}catch(a){return{ok:!1,error:String(a.message??a)}}return{ok:!0,message:`Installed app template "${i.normalized}" (${r}). /apps start <session> ${o.command.trim()}`}}case"shortcut":{let o=e.payload,s=wt(e.name);if(!s.ok)return{ok:!1,error:s.error};try{Tr(t,s.normalized,o.body,r==="global"?"global":"chat")}catch(i){return{ok:!1,error:String(i.message??i)}}return{ok:!0,message:`Installed shortcut "${s.normalized}" (${r}). Type ${s.normalized} to expand.`}}case"cowork":{let o=e.payload,s=xe();if(Ye(s,e.name,t))return{ok:!1,error:`Cowork task "${e.name}" already exists for this chat. Remove it first or rename.`};let i=Date.now(),a={id:sr(),name:e.name,ownerPeerKey:t,command:o.command,cwd:o.cwd??"",outputDir:o.outputDir??"",schedule:o.schedule,enabled:o.enabled??!0,notify:o.notify??"self",notifyWhen:o.notifyWhen??"always",attachLog:o.attachLog??!1,attachFiles:o.attachFiles??[],lastCompletedSlotMs:null,createdAtMs:i};return s.push(a),Oe(s),{ok:!0,message:`Installed cowork task "${e.name}" for this chat. /cowork show ${e.name}`}}default:return{ok:!1,error:"Unknown catalog kind."}}}function ip(e){return{name:e.name,command:e.command,cwd:e.cwd,outputDir:e.outputDir,schedule:e.schedule,enabled:e.enabled,notify:e.notify,notifyWhen:e.notifyWhen,attachLog:e.attachLog,attachFiles:e.attachFiles}}function ap(e){let{dangerous:t,...n}=e;return Be(n)}var ka=new Map;function Sa(e,t){return`${e}:${t}`}function va(e,t,n){ka.set(Sa(e,t),n)}function lp(e,t){return ka.get(Sa(e,t))}function Ss(e,t,n){let r=Number.parseInt(n,10);if(!Number.isFinite(r)||r<1)return null;let o=ka.get(Sa(e,t));return!o||r>o.length?null:o[r-1].publicId}function mn(e){return!!(e&&typeof e=="object"&&typeof e.error=="string")}function vs(e,t){return`${e.commandPrefix} online ${t}`}function cp(e){let t=o=>vs(e,o),n=e.defaultKind===void 0?" [recipe|app|cowork|shortcut]":` (${e.defaultKind} only)`,r=[`Online catalog \u2014 ${e.defaultKind??"all kinds"}`,"",t(`trending${e.defaultKind===void 0?" [kind]":""}`),t(`search <query>${n}`),t("show <publicId>"),t("<publicId> download [--chat|-p for this chat only]"),t("trending <n> download \u2014 install #n from last list"),t("list \u2014 repeat last trending/search list")];return e.commandPrefix==="/run"&&r.push("","Also: /apps online \u2026 \xB7 /cowork online \u2026 \xB7 /shortcut online \u2026","","Publish (platform account):","/run <recipe> publish [--title \u2026] [--category \u2026]","/apps <session> publish","/cowork <name> publish","/shortcut <name> publish"),p(r.join(`
277
- `))}function xs(e,t,n){if(e.length===0)return p(`${t}
278
- (no results)`);let r=[t,""];return e.forEach((o,s)=>{let i=o.category?` \xB7 ${o.category}`:"";r.push(`${s+1}. ${o.publicId} (${o.kind}${i}) \u2193${o.downloadCount} \u2014 ${o.title||o.name}`),o.description&&r.push(` ${o.description.slice(0,120)}`)}),r.push("",`Install: ${vs(n,"<publicId> download")} \u2014 or ${vs(n,"trending <n> download")}`),p(r.join(`
279
- `))}function up(e,t){let n=[`${e.publicId} (${e.kind})`,`title: ${e.title||e.name}`,e.description?`description: ${e.description}`:"",e.category?`category: ${e.category}`:"",e.tags?.length?`tags: ${e.tags.join(", ")}`:"",`downloads: ${e.downloadCount}`,`author: ${e.authorLabel||"(unknown)"}`,"","payload:"].filter(Boolean),r=e.payload;if(e.kind==="recipe"||e.kind==="app"){let o=r;if(n.push(` command: ${o.command??""}`),o.promptTemplate&&n.push(` template: ${o.promptTemplate.slice(0,400)}${o.promptTemplate.length>400?"\u2026":""}`),Array.isArray(o.steps)&&o.steps.length>0){n.push(` steps: ${o.steps.length}`);for(let[s,i]of o.steps.entries()){let a=typeof i=="string"?i:i.cmd;n.push(` ${s+1}. ${a??""}`)}}}else if(e.kind==="shortcut")n.push(` body: ${r.body}`);else if(e.kind==="cowork"){let o=r;n.push(` command: ${o.command??""}`),n.push(` schedule: ${o.schedule?.kind??"?"}`)}return n.push("",vs(t,`${e.publicId} download`)),p(n.join(`
280
- `))}var xa=new Set(["recipe","app","cowork","shortcut"]),rw={commandPrefix:"/run",listScope:"run"},dp={commandPrefix:"/apps",defaultKind:"app",listScope:"apps"},pp={commandPrefix:"/cowork",defaultKind:"cowork",listScope:"cowork"},mp={commandPrefix:"/shortcut",defaultKind:"shortcut",listScope:"shortcut"};function ow(e){if(!e)return;let t=e.toLowerCase();return xa.has(t)?t:void 0}function At(e,t){return`${e.commandPrefix} online ${t}`}function sw(e,t){if(e.defaultKind)return t&&t.toLowerCase()!==e.defaultKind?{error:`This command only lists ${e.defaultKind} entries. Omit the kind suffix.`}:e.defaultKind;let n=ow(t);return t&&!n?{error:`Unknown kind "${t}". Use: recipe, app, cowork, shortcut`}:n}function iw(e,t){if(e.defaultKind){if(t.length>1&&xa.has(t[t.length-1].toLowerCase())){if(t[t.length-1].toLowerCase()!==e.defaultKind)return{error:`This command only searches ${e.defaultKind} entries. Omit the kind suffix.`};t.pop()}return{kind:e.defaultKind,query:t.join(" ")}}let n;return t.length>1&&xa.has(t[t.length-1].toLowerCase())&&(n=t.pop().toLowerCase()),{kind:n,query:t.join(" ")}}async function Yn(e,t,n,r){let o=e.trim();if(!o||/^help$/i.test(o))return cp(r);let s=o.match(/^(.+?)\s+download(?:\s+(.*))?$/i);if(s){let d=s[1].trim(),u=(s[2]??"").trim(),c=/--chat|-p/i.test(u)?"chat":"global",m=null,f=/^trending\s+(\d+)$/i.exec(d),h=/^search\s+(\S+)\s+(\d+)$/i.exec(d);if(f){if(m=Ss(t,r.listScope,f[1]),!m)return p(`No trending list in this chat \u2014 run ${At(r,"trending")} first, or use ${At(r,"<publicId> download")}.`)}else if(h){if(m=Ss(t,r.listScope,h[2]),!m)return p(`No search list in this chat \u2014 run ${At(r,"search <query>")} first, or use ${At(r,"<publicId> download")}.`)}else if(/^\d+$/.test(d)){if(m=Ss(t,r.listScope,d),!m)return p(`No list item #${d}. Run trending or search first, or use ${At(r,"<publicId> download")}.`)}else m=d;let g=await np(m);if(mn(g))return p(`Download failed: ${g.error}`);if(r.defaultKind&&g.kind!==r.defaultKind)return p(`Entry "${m}" is kind=${g.kind}, not ${r.defaultKind}. Use /run online show ${m} from /run.`);let y=sp(g,t,n,c);return y.ok?p(y.message):p(`Download failed: ${y.error}`)}let i=/^show\s+(\S+)\s*$/i.exec(o);if(i){let d=await tp(i[1]);return mn(d)?p(`Not found: ${d.error}`):r.defaultKind&&d.kind!==r.defaultKind?p(`Entry "${i[1]}" is kind=${d.kind}, not ${r.defaultKind}. Use /run online show ${i[1]}.`):up(d,r)}let a=/^trending(?:\s+(\S+))?\s*$/i.exec(o);if(a){let d=sw(r,a[1]);if(typeof d=="object"&&"error"in d)return p(d.error);let u=await Zd(d?{kind:d}:void 0);if(mn(u))return p(`Trending failed: ${u.error}`);va(t,r.listScope,u.items);let c=r.defaultKind?`Trending (${r.defaultKind})`:"Trending";return xs(u.items,c,r)}let l=/^search\s+([\s\S]+)$/i.exec(o);if(l){let d=l[1].trim().split(/\s+/),u=iw(r,d);if("error"in u)return p(u.error);let{kind:c,query:m}=u;if(!m)return p(`Usage: ${At(r,"search <query>")}`);let f=await ep(m,c?{kind:c}:void 0);return mn(f)?p(`Search failed: ${f.error}`):(va(t,r.listScope,f.items),xs(f.items,`Search: ${m}`,r))}if(/^list\s*$/i.test(o)){let d=lp(t,r.listScope);return d?.length?xs(d,"Last list",r):p(`No cached list. ${At(r,"trending")} or ${At(r,"search <query>")}`)}return p(`Unknown ${r.commandPrefix} online command. ${At(r,"help")}`)}async function fp(e,t,n){return Yn(e,t,n,rw)}function Pr(e){let t=e.trim(),n=[],r,o,s,i=a=>{let l=t.match(a);if(!l?.[1])return;let d=l[1].trim();if(t=(t.slice(0,l.index)+t.slice(l.index+l[0].length)).trim(),a.source.includes("title"))r=d.slice(0,120);else if(a.source.includes("description"))o=d.slice(0,500);else if(a.source.includes("category"))s=d.slice(0,40);else if(a.source.includes("tag"))for(let u of d.split(/[,;]/)){let c=u.trim();c&&n.push(c.slice(0,32))}};for(let a=0;a<8;a+=1){let l=t;if(i(/--title\s+"([^"]+)"/i),i(/--title\s+(\S+)/i),i(/--description\s+"([^"]+)"/i),i(/--description\s+(\S+)/i),i(/--category\s+"([^"]+)"/i),i(/--category\s+(\S+)/i),i(/--tag\s+"([^"]+)"/i),i(/--tag\s+(\S+)/i),t===l)break}return{title:r,description:o,category:s,tags:n,remainder:t.trim()}}function aw(e,t,n){return{kind:"recipe",name:e,title:n.title??t.label??e,description:n.description??t.description,category:n.category??t.category,tags:n.tags,payload:ap(t)}}function hp(e,t,n,r){let o=Pr(r),s=n.trim().toLowerCase(),i=je(e,t,s);return i?i.source==="builtin"?{ok:!1,error:"Built-in recipes cannot be published. Copy with /run add first."}:{ok:!0,body:aw(i.name,i,o)}:{ok:!1,error:`Unknown recipe "${n}".`}}function gp(e,t,n){let r=Pr(n),o=ws(e,t);return o?{ok:!0,body:{kind:"shortcut",name:o.name,title:r.title??o.name,description:r.description,category:r.category??"shortcut",tags:r.tags,payload:{body:o.body}}}:{ok:!1,error:`Unknown shortcut "${t}".`}}function yp(e,t,n){let r=Pr(n),o=xe(),s=Ye(o,t,e);return s?{ok:!0,body:{kind:"cowork",name:s.name,title:r.title??s.name,description:r.description,category:r.category??"cowork",tags:r.tags,payload:ip(s)}}:{ok:!1,error:`Unknown cowork task "${t}" for this chat.`}}function wp(e,t,n){let r=Pr(n),o=e.trim().toLowerCase();return t.trim()?{ok:!0,body:{kind:"app",name:o,title:r.title??o,description:r.description,category:r.category??"app",tags:r.tags,payload:{command:t.trim(),label:r.title}}}:{ok:!1,error:"Session has no command to publish."}}async function Qn(e){let t=ba();if(!t.ok)return{ok:!1,error:t.error};let n=await rp(e);if(mn(n))return{ok:!1,error:n.error};let r=n;return{ok:!0,message:`${r.updated?"Updated":"Published"}: ${r.publicId} (${e.kind}). Others: /run online ${r.publicId} download`}}function lw(){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","add <name> heartbeat <interval> [grace] \u2014 dead-man's-switch (no command needed)"," e.g. /cowork add backup heartbeat 1h 10m","set <name> cmd -- <command\u2026> | schedule <\u2026> | out <path> | cwd <path>"," notify self|wa|tg|all|none"," when always|failure|state-change \u2014 notify condition (default: always)"," attach on|off \u2014 send run log as a file with notify messages"," files clear | <glob/path \u2026> \u2014 optional artifacts (basename * ?); quote paths with spaces","list | show <name> | run <name> | checkin <name> | enable <name> | disable <name> | remove <name>","publish <name> \u2014 share task to online catalog (platform login)","online trending | show <publicId> | <publicId> download \u2014 cowork tasks only","","Output logs: task outputDir (default ~/Cowork/<name>). Missed times catch up when the gateway is back."].join(`
281
- `))}function bp(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 cw(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}}async function Sp(e,t,n){let r=e.trim();if(!r||/^help$/i.test(r))return lw();let o=/^online\b([\s\S]*)$/i.exec(r);if(o)return Yn((o[1]??"").trim(),t,n,pp);let s=/^(\S+)\s+publish\b([\s\S]*)$/i.exec(r);if(s){let f=yp(t,s[1],s[2]??"");if(!f.ok)return p(f.error);let h=await Qn(f.body);return p(h.ok?h.message:h.error)}let i=r.split(/\s+/)[0].toLowerCase();if(/^list$/i.test(i)){let f=xe().filter(g=>g.ownerPeerKey===t);if(f.length===0)return p("(no cowork tasks for this chat)");let h=f.map(g=>{let y=g.enabled?"":" (disabled)",b=g.notifyWhen&&g.notifyWhen!=="always"?` when=${g.notifyWhen}`:"";return`${Re}${g.name} ${In(g.schedule)} notify=${g.notify}${b}${y}`});return p(h.join(`
282
- `))}let a=r.match(/^show\s+(\S+)\s*$/i);if(a){let f=a[1],h=xe();an(h);let g=Ye(h,f,t);if(!g)return p(`Unknown task "${f}". /cowork list`);let y=Lo(g),b=[`name: ${g.name}`,`id: ${g.id}`,`schedule: ${In(g.schedule)}`,`enabled: ${g.enabled}`,`notify: ${g.notify}`,`notifyWhen: ${g.notifyWhen??"always"}`];if(g.schedule.kind==="heartbeat"){let v=_o(g.id);b.push(`lastCheckin: ${v?new Date(v).toLocaleString():"(never \u2014 send /cowork checkin "+g.name+")"}`)}else b.push(`attachLog: ${g.attachLog}`),b.push(`files: ${g.attachFiles.length?g.attachFiles.join(", "):"(none)"}`),b.push(`cwd: ${g.cwd||"(session cwd)"}`),b.push(`out: ${g.outputDir}`),b.push(`cmd: ${g.command}`),b.push(`last slot: ${y?new Date(y).toLocaleString():"(never)"}`);return p(b.join(`
283
- `))}if(/^add$/i.test(i)){let f=r.slice(3).trim(),h=f.match(/^(\S+)\s+(heartbeat\s+.+)$/i);if(h){let T=js(h[1]);if(!T.ok)return p(T.error);let O=h[2].split(/\s+/).filter(Boolean),A=Oo(O);if(!A.ok)return p(A.error);let K=xe();if(Ye(K,T.name,t))return p(`Task "${T.name}" already exists. Remove it first or pick another name.`);let X=Qr(T.name),se={id:sr(),name:T.name,ownerPeerKey:t,command:"",cwd:"",outputDir:X,schedule:A.schedule,enabled:!0,notify:"self",notifyWhen:"always",attachLog:!1,attachFiles:[],lastCompletedSlotMs:null,createdAtMs:Date.now()};return K.push(se),Oe(K),an(K),Di(se.id),p([`Saved heartbeat task "${T.name}" (${In(A.schedule)}).`,`Send /cowork checkin ${T.name} to record a heartbeat.`,"Alerts if no check-in arrives within the expected interval + grace period."].join(`
284
- `))}let g=cw(f);if("error"in g)return p(g.error);let y=js(g.name);if(!y.ok)return p(y.error);let b=Oo(g.scheduleWords);if(!b.ok)return p(b.error);let v=xe();if(Ye(v,y.name,t))return p(`Task "${y.name}" already exists. Remove it first or pick another name.`);let E=oe(t),R=Qr(y.name),P={id:sr(),name:y.name,ownerPeerKey:t,command:g.command,cwd:"",outputDir:R,schedule:b.schedule,enabled:!0,notify:"self",notifyWhen:"always",attachLog:!1,attachFiles:[],lastCompletedSlotMs:null,createdAtMs:Date.now()};return v.push(P),Oe(v),p([`Saved cowork task "${y.name}" (${In(b.schedule)}).`,`Output: ${R}`,`Notify: self \u2014 change with /cowork set ${y.name} notify wa|tg|all|none`].join(`
285
- `))}let l=r.match(/^run\s+(\S+)\s*$/i);if(l){let f=l[1].toLowerCase(),h=Ye(xe(),f,t);return h?h.enabled?(ll({ownerPeerKey:t,name:h.name,at:Date.now()}),p(`On-demand run queued for "${h.name}" (runs within ~30s while omnish run is active).`)):p(`Cowork "${h.name}" is disabled. /cowork enable ${h.name}`):p(`Unknown task "${l[1]}". /cowork list`)}let d=r.match(/^checkin\s+(\S+)\s*$/i);if(d){let f=d[1].toLowerCase(),h=xe();an(h);let g=Ye(h,f,t);return g?g.schedule.kind!=="heartbeat"?p(`"${g.name}" is not a heartbeat task.`):(Di(g.id),p(`Heartbeat recorded for "${g.name}".`)):p(`Unknown task "${d[1]}". /cowork list`)}let u=r.match(/^(?:remove|rm|del)\s+(\S+)\s*$/i);if(u){let f=u[1],h=xe(),g=h.findIndex(y=>y.name===f.toLowerCase()&&y.ownerPeerKey===t);return g===-1?p(`Unknown task "${f}".`):(h.splice(g,1),Oe(h),p(`Removed cowork task "${f.toLowerCase()}".`))}let c=r.match(/^enable\s+(\S+)\s*$/i);if(c)return kp(c[1],t,!0);let m=r.match(/^disable\s+(\S+)\s*$/i);return m?kp(m[1],t,!1):/^set$/i.test(i)?mw(r.slice(3).trim(),t):p("Unknown /cowork command. Try /cowork help")}function kp(e,t,n){let r=xe(),o=Ye(r,e,t);return o?(o.enabled=n,Oe(r),p(`Cowork "${o.name}" ${n?"enabled":"disabled"}.`)):p(`Unknown task "${e}".`)}function uw(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 dw(e){let t=e.toLowerCase();return t==="always"||t==="all"?"always":t==="failure"||t==="fail"||t==="failures"?"failure":t==="state-change"||t==="statechange"||t==="change"||t==="transition"?"state-change":null}function pw(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 mw(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 | when \u2026 | attach <on|off> | files \u2026");let r=n[1],o=n[2].trim(),s=xe(),i=Ye(s,r,t);if(!i)return p(`Unknown task "${r}".`);if(o.toLowerCase().startsWith("cmd ")){let a=o.slice(4).trim(),l=" -- ",d=a.indexOf(l),u;if(d!==-1)u=a.slice(d+l.length).trim();else if(a.startsWith("--"))u=a.slice(2).trim();else return p("Usage: /cowork set <name> cmd -- <command\u2026>");return u?(i.command=u,Oe(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=Oo(a);return l.ok?(i.schedule=l.schedule,Oe(s),p(`Schedule for "${i.name}": ${In(l.schedule)}`)):p(l.error)}if(o.toLowerCase().startsWith("out ")){let a=o.slice(4).trim(),l=oe(t);return i.outputDir=Ge(a,l.cwd),Oe(s),p(`outputDir: ${i.outputDir}`)}if(o.toLowerCase().startsWith("cwd ")){let a=o.slice(4).trim(),l=oe(t);return i.cwd=a?Ge(a,l.cwd):"",Oe(s),p(`cwd: ${i.cwd||"(session cwd at run time)"}`)}if(o.toLowerCase().startsWith("notify ")){let a=o.slice(7).trim(),l=uw(a);return l?(i.notify=l,Oe(s),p(`notify: ${l}`)):p("notify must be self, wa, tg, all, or none.")}if(o.toLowerCase().startsWith("when ")){let a=o.slice(5).trim(),l=dw(a);return l?(i.notifyWhen=l,Oe(s),p(`notifyWhen: ${l}`)):p("when must be always, failure, or state-change.")}if(o.toLowerCase().startsWith("attach ")){let a=o.slice(7).trim(),l=bp(a);if(l.length!==1)return p("Usage: /cowork set <name> attach on|off");let d=pw(l[0]);return d===null?p("attach must be on or off"):(i.attachLog=d,Oe(s),p(`attachLog: ${d}`))}if(o.toLowerCase().startsWith("files ")){let a=o.slice(6).trim(),l=bp(a);return l.length===1&&l[0].toLowerCase()==="clear"?(i.attachFiles=[],Oe(s),p("files: (cleared)")):l.length===0?p("Usage: /cowork set <name> files clear | <pattern \u2026> \u2014 quote paths with spaces"):(i.attachFiles=l,Oe(s),p(`files: ${i.attachFiles.join(", ")}`))}return p("Unknown set field. Try: cmd, schedule, out, cwd, notify, when, attach, files")}pe();import Lw from"node:fs";G();import vp from"node:os";import fw from"node:path";var hw=new Set(["create","delete","rename","update"]);function gw(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 yw(e){let t=e.split(",").map(n=>n.trim().toLowerCase());return t.length===0?!1:t.every(n=>hw.has(n))}function ww(e){return e.split(",").map(t=>t.trim().toLowerCase())}function $a(e,t,n){let r=e.trim();if(r.startsWith("-")&&(r=r.slice(1)),!r)return{};let o=Ge(r,n);return r.includes("*")||r.includes("?")?{glob:r.replace(/\\/g,"/")}:fw.isAbsolute(o)||r.startsWith("~")||r.startsWith("./")||r.startsWith("../")?{path:o}:r.startsWith("/")?{path:o}:{glob:r.includes("/")?r:`**/${r}`}}function xp(e,t=vp.homedir()){let n=e.replace(/\s+&&\s+/g," ").trim(),r=gw(n);if(r.length===0)return{ok:!1,error:"Usage: /watch add fs <name> <path> [events] [-exclude \u2026] [--exclude \u2026]"};let o=0,s=r[o];o+=1;let i=["create","delete","rename"],a=[],l=[];for(;o<r.length;){let u=r[o];if(u==="--exclude"){if(o+=1,o>=r.length)return{ok:!1,error:"--exclude requires a pattern."};let c=r[o];o+=1;let m=Ge(s,t),f=$a(c.startsWith("-")?c:`-${c}`,m,t);f.path?a.push(f.path):f.glob&&l.push(f.glob);continue}if(u.startsWith("--"))return{ok:!1,error:`Unknown flag: ${u}`};if(u.startsWith("-")){let c=Ge(s,t),m=$a(u,c,t);m.path?a.push(m.path):m.glob&&l.push(m.glob),o+=1;continue}if(yw(u)){i=ww(u),o+=1;continue}return{ok:!1,error:`Unexpected token "${u}". Put path first, then events or -excludes.`}}return{ok:!0,value:{rootPath:Ge(s,t),events:i,excludePaths:a,excludeGlobs:l}}}function Ra(e,t,n=vp.homedir()){let r=$a(e.startsWith("-")?e:`-${e}`,t,n);return!r.path&&!r.glob?{error:"Empty exclude pattern."}:r}import{spawnSync as $s}from"node:child_process";import $p from"node:fs";import bw from"node:os";import kw from"node:path";var Sw=40,vw=10,Rs=15e3,xw=["~/Projects","~/deploy","~/Downloads","~/src","/var/www","/srv"],Rp={linux:"/var/log/dpkg.log (also /var/log/apt/history.log)",darwin:"/var/log/install.log",win32:"Windows Application event log (install/remove)"};function $w(e){return e.startsWith("~/")?kw.join(bw.homedir(),e.slice(2)):e}function Rw(e,t){return t?.trim()?e.toLowerCase().includes(t.trim().toLowerCase()):!0}function Cw(e){let t=n=>n==="running"||n==="active";return[...e].sort((n,r)=>{let o=t(n.state),s=t(r.state);return o!==s?o?-1:1:n.name.localeCompare(r.name)})}function Mw(e){let t=[];for(let n of e.split(`
286
- `)){let r=n.trim();if(!r)continue;let o=r.split(/\s+/);if(o.length<4)continue;let s=o[0];if(!s.endsWith(".service"))continue;let i=o[2].toLowerCase(),a=s.slice(0,-8);t.push({name:a,state:i})}return t}function Tw(e){let t=[],n=e.split(`
287
- `);for(let r=0;r<n.length;r++){let o=n[r].trim();if(!o||o.startsWith("PID"))continue;let s=o.split(/\s+/);if(s.length<3)continue;let i=s[s.length-1];if(!i.includes("."))continue;let l=s[0]==="-"?"stopped":"running";t.push({name:i,state:l})}return t}function Ew(e){let t=[],n="";for(let r of e.split(`
288
- `)){let o=r.match(/SERVICE_NAME:\s*(.+)/i);if(o){n=o[1].trim();continue}let s=r.match(/^\s*STATE\s*:\s*\d+\s+(\S+)/i);if(s&&n){let i=s[1].toUpperCase(),a=i.toLowerCase();i==="RUNNING"?a="running":i==="STOPPED"&&(a="stopped"),t.push({name:n,state:a}),n=""}}return t}function Pw(){let e=$s("systemctl",["list-units","--type=service","--all","--no-pager","--plain","--no-legend"],{encoding:"utf8",timeout:Rs});return e.error||e.status!==0?{entries:[],error:"systemctl unavailable \u2014 install systemd or run on Linux."}:{entries:Mw(e.stdout??"")}}function Aw(){let e=$s("launchctl",["list"],{encoding:"utf8",timeout:Rs});return e.error||e.status!==0?{entries:[],error:"launchctl unavailable."}:{entries:Tw(e.stdout??"")}}function Iw(){let e=$s("sc",["query","type=","service","state=","all"],{encoding:"utf8",timeout:Rs,windowsHide:!0});if(!e.error&&e.status===0&&(e.stdout??"").includes("SERVICE_NAME"))return{entries:Ew(e.stdout??"")};let t=$s("powershell",["-NoProfile","-Command","Get-Service | ForEach-Object { $_.Name + ' ' + $_.Status }"],{encoding:"utf8",timeout:Rs,windowsHide:!0});if(t.error||t.status!==0)return{entries:[],error:"sc query and Get-Service unavailable."};let n=[];for(let r of(t.stdout??"").split(`
289
- `)){let o=r.trim();if(!o)continue;let s=o.lastIndexOf(" ");if(s<=0)continue;let i=o.slice(0,s).trim(),a=o.slice(s+1).trim().toLowerCase();n.push({name:i,state:a})}return{entries:n}}function Cs(e){let t=e?.limit??Sw,n=e?.filter,r,o=process.platform;if(o==="linux")r=Pw();else if(o==="darwin")r=Aw();else if(o==="win32")r=Iw();else return{entries:[],truncated:!1,totalMatched:0,error:`Service discovery not supported on ${o}.`};if(r.error)return{entries:[],truncated:!1,totalMatched:0,error:r.error};let s=Cw(r.entries.filter(l=>Rw(l.name,n))),i=s.length,a=i>t;return{entries:s.slice(0,t),truncated:a,totalMatched:i}}function Ow(e,t=vw){let n=[];for(let r=0;r<e.length;r+=t)n.push(e.slice(r,r+t));return n}function Ca(e,t){if(e.error)return p(e.error);if(e.entries.length===0){let o=t?.trim()?`No services match "${t}". Try /watch svc list without a filter.`:"No services found on this host.";return p(o)}let n=e.entries.map(o=>`${Re}${o.name} \u2014 ${o.state}`),r=t?.trim()?`Services matching "${t}" (${e.entries.length} shown):`:`Services on ${process.platform} (${e.entries.length} shown):`;if(n.unshift(r),e.truncated){let o=e.totalMatched-e.entries.length;n.push("",`${o} more \u2014 narrow with /watch svc list <filter>`)}return n.push("","Copy-paste templates follow in the next message."),p(n.join(`
290
- `))}function Ma(e,t){let n=t?.trim()||"<name>";if(e.length===0)return p("No services to template \u2014 run /watch svc list first.");let r=e.map(i=>i.name),o=Ow(r),s=[t?.trim()?`Copy-paste (/watch add svc ${n} \u2026):`:"Copy-paste (replace <name>):",""];for(let i of o)s.push(`/watch add svc ${n} ${i.join(" ")}`);return p(s.join(`
291
- `))}function Cp(){let e=process.platform,t=e==="linux"?"linux":e==="darwin"?"darwin":"win32",n=["Watch hints",""];n.push("Package log (pkg watches):"),n.push(` ${Rp[t]??Rp.linux}`),n.push("","Filesystem roots that exist on this host:");let r=[];for(let o of xw){let s=$w(o);try{$p.existsSync(s)&&$p.statSync(s).isDirectory()&&r.push(o)}catch{}}if(r.length===0)n.push(" (none of the usual paths found \u2014 use any directory you own)");else for(let o of r)n.push(` ${o}`),n.push(` /watch add fs <name> ${o} create,delete,rename`);return n.push("","Service names: /watch svc list [filter]"),p(n.join(`
292
- `))}function U(e){return{replies:[e]}}function Nw(...e){return{replies:e}}function _w(e){let t=e.trim();if(!t)return{};let n=t.split(/\s+/);if(n.length>=2){let s=Zr(n[0]);return s.ok?{ruleName:s.name,filter:n.slice(1).join(" ")}:{filter:t}}let r=n[0],o=Zr(r);if(o.ok){let s=Cs({filter:r,limit:1});if(s.totalMatched===0&&!s.error)return{ruleName:o.name}}return{filter:r}}function Fw(){return p(["Watch \u2014 OS event eye (watchEnabled + omnish run). Docs: docs/features/watch.md","","add fs <name> <path> [events] [-/exclude \u2026] [--exclude glob]",' e.g. /watch add fs home ~/Projects create,delete -~/Projects/tmp --exclude "**/.keyfolder"',"add pkg <name> | add svc <name> <unit\u2026>","svc list [filter] \u2014 services on this host (+ templates in next message)","svc templates [ruleName] [filter] \u2014 copy-paste /watch add svc lines only","hints \u2014 suggested FS paths and package log location","list | status | reload | show <name> | recent [N]","pause|stop <name> \u2014 stop alerts, keep rule","resume <name> | enable <name> | disable <name> | rm <name>","exclude <name> list|add <pattern>|rm <pattern>","set <name> notify self|wa|tg|all|none","set <name> when always|state-change","on | off \u2014 global watchEnabled in config","Rules are device-wide (~/.omnish/watch/rules.json); any allowlisted peer can manage.","notify:self alerts the peer who created the rule."].join(`
293
- `))}function Mp(){return{excludePaths:[],excludeGlobs:[]}}function Ww(e){return e.notify==="self"?`notify=self (${e.ownerPeerKey})`:`notify=${e.notify}`}function Uw(e,t){qs(e.id),t==="pause"||t==="stop"?e.paused=!0:t==="resume"?e.paused=!1:t==="enable"?(e.enabled=!0,e.paused=!1):t==="disable"&&(e.enabled=!1,e.paused=!1)}function Tp(e,t){let n=e.trim();if(!n||/^help$/i.test(n))return U(Fw());let r=n.split(/\s+/)[0].toLowerCase();if(Vr(),/^hints$/i.test(r))return U(Cp());if(/^svc$/i.test(r)){let u=n.slice(3).trim(),m=(u.split(/\s+/)[0]??"").toLowerCase();if(m==="list"){let f=u.slice(4).trim()||void 0,h=Cs({filter:f});return h.error?U(p(h.error)):h.entries.length===0?U(Ca(h,f)):Nw(Ca(h,f),Ma(h.entries))}if(m==="templates"){let f=u.slice(9).trim(),{ruleName:h,filter:g}=_w(f),y=Cs({filter:g});return y.error?U(p(y.error)):U(Ma(y.entries,h))}return U(p("svc subcommands: list [filter] | templates [ruleName] [filter]"))}if(/^status$/i.test(r)){let u=$(),{summary:c}=ar(),m=[`watchEnabled: ${u.watchEnabled} watchAutoRestore: ${u.watchAutoRestore}`,`debounce: ${u.watchDebounceMs}ms max/min: ${u.watchMaxEventsPerMinute}`,`rules file: ${Ks()} (${c.total} saved, ${c.active} active, ${c.paused} paused, ${c.disabled} disabled)`,`events db: ${Hr}`];c.total>0&&!u.watchEnabled?m.push("Rules are on disk; adapters stopped \u2014 /watch on to start."):c.total>0&&u.watchEnabled&&!u.watchAutoRestore?m.push("watchAutoRestore is off \u2014 /watch reload to start adapters without restarting gateway."):c.paused>0&&m.push(`${c.paused} paused rule(s) stay stopped until /watch resume <name>.`);let f=Fl();return f?(m.push("","Adapters:"),m.push(...f.getStatusLines())):u.watchEnabled&&u.watchAutoRestore?m.push("","(manager starting \u2014 try /watch status again)"):u.watchEnabled?m.push("","(manager idle \u2014 /watch reload)"):m.push("","(manager off \u2014 /watch on)"),U(p(m.join(`
294
- `)))}if(/^reload$/i.test(r)){let{summary:u}=ar();return $().watchEnabled?(Wl(),U(p(`Reloading from ${Ks()}: ${u.total} rule(s) on disk, ${u.active} eligible to run. /watch status for adapters.`))):U(p(`Rules on disk: ${u.total} (${u.active} active). watchEnabled is false \u2014 /watch on first.`))}if(/^list$/i.test(r)){let u=Le();if(u.length===0)return U(p("(no watch rules on this device)"));let c=u.map(m=>{let f=m.enabled?m.paused?"paused":"on":"disabled",h=m.kind==="fs"?m.path:m.kind==="svc"?m.units.join(","):"system";return`${Re}${m.name} [${m.kind}] ${f} ${Ww(m)} when=${m.notifyWhen} \u2014 ${h}`});return U(p(c.join(`
295
- `)))}let o=n.match(/^recent(?:\s+(\d+))?\s*$/i);if(o){let u=o[1]?Number(o[1]):15,c=Le(),m=new Set(c.map(g=>g.id)),f=fl(u,m);if(f.length===0)return U(p("(no recent watch events)"));let h=f.map(g=>`${new Date(g.tsMs).toLocaleString()} ${g.ruleName} ${g.summary}`);return U(p(h.join(`
296
- `)))}let s=n.match(/^show\s+(\S+)\s*$/i);if(s){let u=Zt(Le(),s[1]);if(!u)return U(p(`Unknown rule "${s[1]}". /watch list`));let c=[`name: ${u.name}`,`kind: ${u.kind}`,`creator: ${u.ownerPeerKey}`,`enabled: ${u.enabled}`,`paused: ${u.paused}`,`notify: ${u.notify}`,`notifyWhen: ${u.notifyWhen}`,`adapter: ${u.adapterStatus||"(unknown)"}`];return u.kind==="fs"&&(c.push(`path: ${u.path}`),c.push(`events: ${u.events.join(",")}`),c.push(`excludePaths: ${u.excludePaths.length?u.excludePaths.join(", "):"(none)"}`),c.push(`excludeGlobs: ${u.excludeGlobs.length?u.excludeGlobs.join(", "):"(none)"}`)),u.kind==="svc"&&c.push(`units: ${u.units.join(", ")||"(none)"}`),U(p(c.join(`
297
- `)))}if(/^on$/i.test(r))return W({watchEnabled:!0}),De(),U(p("watchEnabled: true (gateway picks up watchers when running)."));if(/^off$/i.test(r))return W({watchEnabled:!1}),De(),U(p("watchEnabled: false \u2014 all adapters stopped."));let i=n.match(/^exclude\s+(\S+)\s+(\S+)(?:\s+([\s\S]+))?\s*$/i);if(i){let u=i[1],c=i[2].toLowerCase(),m=(i[3]??"").trim(),f=Le(),h=Zt(f,u);if(!h)return U(p(`Unknown rule "${u}".`));if(h.kind!=="fs")return U(p("exclude applies to filesystem rules only."));if(c==="list"){let g=[`excludePaths: ${h.excludePaths.length?h.excludePaths.join(`
275
+ ${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=ko();return p(`*Uninstall*
276
+ ${s.detail}`)}return p("Unknown /service command. Try /service help")}me();it();on();function vs(){let e=se();return e?.platformUrl?e.platformUrl.replace(/\/$/,""):($().tunnelRelayUrl||$e).trim().replace(/\/$/,"")}function xs(){let e=se();return e?.token?{Authorization:`Bearer ${e.token}`}:{}}async function Or(e){try{return await e.json()}catch{return{error:`HTTP ${e.status}`}}}function lw(){return`Set platform URL: omnish config add tunnelRelayUrl ${$e} \u2014 publish: omnish platform login`}function $a(){return se()?.token?{ok:!0}:{ok:!1,error:`Publish requires a platform account token. ${lw()}`}}async function op(e){let t=new URLSearchParams;e?.kind&&t.set("kind",e.kind),e?.limit&&t.set("limit",String(e.limit));let n=t.toString(),r=`${vs()}/v1/catalog/trending${n?`?${n}`:""}`,o=await fetch(r,{headers:xs()}),s=await Or(o);return o.ok?s:{error:s.error??`HTTP ${o.status}`}}async function sp(e,t){let n=new URLSearchParams;n.set("q",e),t?.kind&&n.set("kind",t.kind),t?.category&&n.set("category",t.category),t?.limit&&n.set("limit",String(t.limit));let r=`${vs()}/v1/catalog/search?${n}`,o=await fetch(r,{headers:xs()}),s=await Or(o);return o.ok?s:{error:s.error??`HTTP ${o.status}`}}async function ip(e){let t=encodeURIComponent(e.trim()),n=`${vs()}/v1/catalog/${t}`,r=await fetch(n,{headers:xs()}),o=await Or(r);return r.ok?o:{error:o.error??`HTTP ${r.status}`}}async function ap(e){let t=encodeURIComponent(e.trim()),n=`${vs()}/v1/catalog/${t}/download`,r=await fetch(n,{method:"POST",headers:xs()}),o=await Or(r);return r.ok?o:{error:o.error??`HTTP ${r.status}`}}async function lp(e){let t=$a();if(!t.ok)return{error:t.error};let n=se(),r=`${n.platformUrl.replace(/\/$/,"")}/v1/catalog`,o=await fetch(r,{method:"POST",headers:{"content-type":"application/json",Authorization:`Bearer ${n.token}`},body:JSON.stringify(e)}),s=await Or(o);return o.ok?s:{error:s.error??`HTTP ${o.status}`}}function cp(e){return e==="global"?"global":"chat"}function up(e,t,n,r="global"){switch(e.kind){case"recipe":{let o=e.payload,s=Be({...o,dangerous:void 0}),i=cn(s);if(!i.ok)return{ok:!1,error:i.error};let a=yt(e.name);if(!a.ok)return{ok:!1,error:a.error};try{_n(t,a.normalized,s,cp(r))}catch(d){return{ok:!1,error:String(d.message??d)}}let l=r==="global"?`/run ${a.normalized} <task>`:`/run ${a.normalized} <task> (this chat)`;return{ok:!0,message:`Installed recipe "${a.normalized}" (${r}). ${l}`}}case"app":{let o=e.payload;if(!o.command?.trim())return{ok:!1,error:"App payload has no command."};let s=Be({command:o.command.trim(),label:o.label??e.title,description:o.description??e.description,category:e.category||"app"}),i=yt(e.name);if(!i.ok)return{ok:!1,error:i.error};try{_n(t,i.normalized,s,cp(r),{skipCommandValidation:!0})}catch(a){return{ok:!1,error:String(a.message??a)}}return{ok:!0,message:`Installed app template "${i.normalized}" (${r}). /apps start <session> ${o.command.trim()}`}}case"shortcut":{let o=e.payload,s=bt(e.name);if(!s.ok)return{ok:!1,error:s.error};try{Ir(t,s.normalized,o.body,r==="global"?"global":"chat")}catch(i){return{ok:!1,error:String(i.message??i)}}return{ok:!0,message:`Installed shortcut "${s.normalized}" (${r}). Type ${s.normalized} to expand.`}}case"cowork":{let o=e.payload,s=xe();if(Ye(s,e.name,t))return{ok:!1,error:`Cowork task "${e.name}" already exists for this chat. Remove it first or rename.`};let i=Date.now(),a={id:lr(),name:e.name,ownerPeerKey:t,command:o.command,cwd:o.cwd??"",outputDir:o.outputDir??"",schedule:o.schedule,enabled:o.enabled??!0,notify:o.notify??"self",notifyWhen:o.notifyWhen??"always",attachLog:o.attachLog??!1,attachFiles:o.attachFiles??[],lastCompletedSlotMs:null,createdAtMs:i};return s.push(a),Oe(s),{ok:!0,message:`Installed cowork task "${e.name}" for this chat. /cowork show ${e.name}`}}default:return{ok:!1,error:"Unknown catalog kind."}}}function dp(e){return{name:e.name,command:e.command,cwd:e.cwd,outputDir:e.outputDir,schedule:e.schedule,enabled:e.enabled,notify:e.notify,notifyWhen:e.notifyWhen,attachLog:e.attachLog,attachFiles:e.attachFiles}}function pp(e){let{dangerous:t,...n}=e;return Be(n)}var Ra=new Map;function Ca(e,t){return`${e}:${t}`}function Ma(e,t,n){Ra.set(Ca(e,t),n)}function mp(e,t){return Ra.get(Ca(e,t))}function $s(e,t,n){let r=Number.parseInt(n,10);if(!Number.isFinite(r)||r<1)return null;let o=Ra.get(Ca(e,t));return!o||r>o.length?null:o[r-1].publicId}function wn(e){return!!(e&&typeof e=="object"&&typeof e.error=="string")}function Rs(e,t){return`${e.commandPrefix} online ${t}`}function fp(e){let t=o=>Rs(e,o),n=e.defaultKind===void 0?" [recipe|app|cowork|shortcut]":` (${e.defaultKind} only)`,r=[`Online catalog \u2014 ${e.defaultKind??"all kinds"}`,"",t(`trending${e.defaultKind===void 0?" [kind]":""}`),t(`search <query>${n}`),t("show <publicId>"),t("<publicId> download [--chat|-p for this chat only]"),t("trending <n> download \u2014 install #n from last list"),t("list \u2014 repeat last trending/search list")];return e.commandPrefix==="/run"&&r.push("","Also: /apps online \u2026 \xB7 /cowork online \u2026 \xB7 /shortcut online \u2026","","Publish (platform account):","/run <recipe> publish [--title \u2026] [--category \u2026]","/apps <session> publish","/cowork <name> publish","/shortcut <name> publish"),p(r.join(`
277
+ `))}function Cs(e,t,n){if(e.length===0)return p(`${t}
278
+ (no results)`);let r=[t,""];return e.forEach((o,s)=>{let i=o.category?` \xB7 ${o.category}`:"";r.push(`${s+1}. ${o.publicId} (${o.kind}${i}) \u2193${o.downloadCount} \u2014 ${o.title||o.name}`),o.description&&r.push(` ${o.description.slice(0,120)}`)}),r.push("",`Install: ${Rs(n,"<publicId> download")} \u2014 or ${Rs(n,"trending <n> download")}`),p(r.join(`
279
+ `))}function hp(e,t){let n=[`${e.publicId} (${e.kind})`,`title: ${e.title||e.name}`,e.description?`description: ${e.description}`:"",e.category?`category: ${e.category}`:"",e.tags?.length?`tags: ${e.tags.join(", ")}`:"",`downloads: ${e.downloadCount}`,`author: ${e.authorLabel||"(unknown)"}`,"","payload:"].filter(Boolean),r=e.payload;if(e.kind==="recipe"||e.kind==="app"){let o=r;if(n.push(` command: ${o.command??""}`),o.promptTemplate&&n.push(` template: ${o.promptTemplate.slice(0,400)}${o.promptTemplate.length>400?"\u2026":""}`),Array.isArray(o.steps)&&o.steps.length>0){n.push(` steps: ${o.steps.length}`);for(let[s,i]of o.steps.entries()){let a=typeof i=="string"?i:i.cmd;n.push(` ${s+1}. ${a??""}`)}}}else if(e.kind==="shortcut")n.push(` body: ${r.body}`);else if(e.kind==="cowork"){let o=r;n.push(` command: ${o.command??""}`),n.push(` schedule: ${o.schedule?.kind??"?"}`)}return n.push("",Rs(t,`${e.publicId} download`)),p(n.join(`
280
+ `))}var Ta=new Set(["recipe","app","cowork","shortcut"]),cw={commandPrefix:"/run",listScope:"run"},gp={commandPrefix:"/apps",defaultKind:"app",listScope:"apps"},yp={commandPrefix:"/cowork",defaultKind:"cowork",listScope:"cowork"},wp={commandPrefix:"/shortcut",defaultKind:"shortcut",listScope:"shortcut"};function uw(e){if(!e)return;let t=e.toLowerCase();return Ta.has(t)?t:void 0}function It(e,t){return`${e.commandPrefix} online ${t}`}function dw(e,t){if(e.defaultKind)return t&&t.toLowerCase()!==e.defaultKind?{error:`This command only lists ${e.defaultKind} entries. Omit the kind suffix.`}:e.defaultKind;let n=uw(t);return t&&!n?{error:`Unknown kind "${t}". Use: recipe, app, cowork, shortcut`}:n}function pw(e,t){if(e.defaultKind){if(t.length>1&&Ta.has(t[t.length-1].toLowerCase())){if(t[t.length-1].toLowerCase()!==e.defaultKind)return{error:`This command only searches ${e.defaultKind} entries. Omit the kind suffix.`};t.pop()}return{kind:e.defaultKind,query:t.join(" ")}}let n;return t.length>1&&Ta.has(t[t.length-1].toLowerCase())&&(n=t.pop().toLowerCase()),{kind:n,query:t.join(" ")}}async function Zn(e,t,n,r){let o=e.trim();if(!o||/^help$/i.test(o))return fp(r);let s=o.match(/^(.+?)\s+download(?:\s+(.*))?$/i);if(s){let d=s[1].trim(),u=(s[2]??"").trim(),c=/--chat|-p/i.test(u)?"chat":"global",m=null,f=/^trending\s+(\d+)$/i.exec(d),h=/^search\s+(\S+)\s+(\d+)$/i.exec(d);if(f){if(m=$s(t,r.listScope,f[1]),!m)return p(`No trending list in this chat \u2014 run ${It(r,"trending")} first, or use ${It(r,"<publicId> download")}.`)}else if(h){if(m=$s(t,r.listScope,h[2]),!m)return p(`No search list in this chat \u2014 run ${It(r,"search <query>")} first, or use ${It(r,"<publicId> download")}.`)}else if(/^\d+$/.test(d)){if(m=$s(t,r.listScope,d),!m)return p(`No list item #${d}. Run trending or search first, or use ${It(r,"<publicId> download")}.`)}else m=d;let g=await ap(m);if(wn(g))return p(`Download failed: ${g.error}`);if(r.defaultKind&&g.kind!==r.defaultKind)return p(`Entry "${m}" is kind=${g.kind}, not ${r.defaultKind}. Use /run online show ${m} from /run.`);let y=up(g,t,n,c);return y.ok?p(y.message):p(`Download failed: ${y.error}`)}let i=/^show\s+(\S+)\s*$/i.exec(o);if(i){let d=await ip(i[1]);return wn(d)?p(`Not found: ${d.error}`):r.defaultKind&&d.kind!==r.defaultKind?p(`Entry "${i[1]}" is kind=${d.kind}, not ${r.defaultKind}. Use /run online show ${i[1]}.`):hp(d,r)}let a=/^trending(?:\s+(\S+))?\s*$/i.exec(o);if(a){let d=dw(r,a[1]);if(typeof d=="object"&&"error"in d)return p(d.error);let u=await op(d?{kind:d}:void 0);if(wn(u))return p(`Trending failed: ${u.error}`);Ma(t,r.listScope,u.items);let c=r.defaultKind?`Trending (${r.defaultKind})`:"Trending";return Cs(u.items,c,r)}let l=/^search\s+([\s\S]+)$/i.exec(o);if(l){let d=l[1].trim().split(/\s+/),u=pw(r,d);if("error"in u)return p(u.error);let{kind:c,query:m}=u;if(!m)return p(`Usage: ${It(r,"search <query>")}`);let f=await sp(m,c?{kind:c}:void 0);return wn(f)?p(`Search failed: ${f.error}`):(Ma(t,r.listScope,f.items),Cs(f.items,`Search: ${m}`,r))}if(/^list\s*$/i.test(o)){let d=mp(t,r.listScope);return d?.length?Cs(d,"Last list",r):p(`No cached list. ${It(r,"trending")} or ${It(r,"search <query>")}`)}return p(`Unknown ${r.commandPrefix} online command. ${It(r,"help")}`)}async function bp(e,t,n){return Zn(e,t,n,cw)}function Lr(e){let t=e.trim(),n=[],r,o,s,i=a=>{let l=t.match(a);if(!l?.[1])return;let d=l[1].trim();if(t=(t.slice(0,l.index)+t.slice(l.index+l[0].length)).trim(),a.source.includes("title"))r=d.slice(0,120);else if(a.source.includes("description"))o=d.slice(0,500);else if(a.source.includes("category"))s=d.slice(0,40);else if(a.source.includes("tag"))for(let u of d.split(/[,;]/)){let c=u.trim();c&&n.push(c.slice(0,32))}};for(let a=0;a<8;a+=1){let l=t;if(i(/--title\s+"([^"]+)"/i),i(/--title\s+(\S+)/i),i(/--description\s+"([^"]+)"/i),i(/--description\s+(\S+)/i),i(/--category\s+"([^"]+)"/i),i(/--category\s+(\S+)/i),i(/--tag\s+"([^"]+)"/i),i(/--tag\s+(\S+)/i),t===l)break}return{title:r,description:o,category:s,tags:n,remainder:t.trim()}}function mw(e,t,n){return{kind:"recipe",name:e,title:n.title??t.label??e,description:n.description??t.description,category:n.category??t.category,tags:n.tags,payload:pp(t)}}function kp(e,t,n,r){let o=Lr(r),s=n.trim().toLowerCase(),i=je(e,t,s);return i?i.source==="builtin"?{ok:!1,error:"Built-in recipes cannot be published. Copy with /run add first."}:{ok:!0,body:mw(i.name,i,o)}:{ok:!1,error:`Unknown recipe "${n}".`}}function Sp(e,t,n){let r=Lr(n),o=Ss(e,t);return o?{ok:!0,body:{kind:"shortcut",name:o.name,title:r.title??o.name,description:r.description,category:r.category??"shortcut",tags:r.tags,payload:{body:o.body}}}:{ok:!1,error:`Unknown shortcut "${t}".`}}function vp(e,t,n){let r=Lr(n),o=xe(),s=Ye(o,t,e);return s?{ok:!0,body:{kind:"cowork",name:s.name,title:r.title??s.name,description:r.description,category:r.category??"cowork",tags:r.tags,payload:dp(s)}}:{ok:!1,error:`Unknown cowork task "${t}" for this chat.`}}function xp(e,t,n){let r=Lr(n),o=e.trim().toLowerCase();return t.trim()?{ok:!0,body:{kind:"app",name:o,title:r.title??o,description:r.description,category:r.category??"app",tags:r.tags,payload:{command:t.trim(),label:r.title}}}:{ok:!1,error:"Session has no command to publish."}}async function er(e){let t=$a();if(!t.ok)return{ok:!1,error:t.error};let n=await lp(e);if(wn(n))return{ok:!1,error:n.error};let r=n;return{ok:!0,message:`${r.updated?"Updated":"Published"}: ${r.publicId} (${e.kind}). Others: /run online ${r.publicId} download`}}function fw(){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","add <name> heartbeat <interval> [grace] \u2014 dead-man's-switch (no command needed)"," e.g. /cowork add backup heartbeat 1h 10m","set <name> cmd -- <command\u2026> | schedule <\u2026> | out <path> | cwd <path>"," notify self|wa|tg|all|none"," when always|failure|state-change \u2014 notify condition (default: always)"," attach on|off \u2014 send run log as a file with notify messages"," files clear | <glob/path \u2026> \u2014 optional artifacts (basename * ?); quote paths with spaces","list | show <name> | run <name> | checkin <name> | enable <name> | disable <name> | remove <name>","publish <name> \u2014 share task to online catalog (platform login)","online trending | show <publicId> | <publicId> download \u2014 cowork tasks only","","Output logs: task outputDir (default ~/Cowork/<name>). Missed times catch up when the gateway is back."].join(`
281
+ `))}function $p(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 hw(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}}async function Cp(e,t,n){let r=e.trim();if(!r||/^help$/i.test(r))return fw();let o=/^online\b([\s\S]*)$/i.exec(r);if(o)return Zn((o[1]??"").trim(),t,n,yp);let s=/^(\S+)\s+publish\b([\s\S]*)$/i.exec(r);if(s){let f=vp(t,s[1],s[2]??"");if(!f.ok)return p(f.error);let h=await er(f.body);return p(h.ok?h.message:h.error)}let i=r.split(/\s+/)[0].toLowerCase();if(/^list$/i.test(i)){let f=xe().filter(g=>g.ownerPeerKey===t);if(f.length===0)return p("(no cowork tasks for this chat)");let h=f.map(g=>{let y=g.enabled?"":" (disabled)",b=g.notifyWhen&&g.notifyWhen!=="always"?` when=${g.notifyWhen}`:"";return`${Re}${g.name} ${Fn(g.schedule)} notify=${g.notify}${b}${y}`});return p(h.join(`
282
+ `))}let a=r.match(/^show\s+(\S+)\s*$/i);if(a){let f=a[1],h=xe();dn(h);let g=Ye(h,f,t);if(!g)return p(`Unknown task "${f}". /cowork list`);let y=Wo(g),b=[`name: ${g.name}`,`id: ${g.id}`,`schedule: ${Fn(g.schedule)}`,`enabled: ${g.enabled}`,`notify: ${g.notify}`,`notifyWhen: ${g.notifyWhen??"always"}`];if(g.schedule.kind==="heartbeat"){let v=Do(g.id);b.push(`lastCheckin: ${v?new Date(v).toLocaleString():"(never \u2014 send /cowork checkin "+g.name+")"}`)}else b.push(`attachLog: ${g.attachLog}`),b.push(`files: ${g.attachFiles.length?g.attachFiles.join(", "):"(none)"}`),b.push(`cwd: ${g.cwd||"(session cwd)"}`),b.push(`out: ${g.outputDir}`),b.push(`cmd: ${g.command}`),b.push(`last slot: ${y?new Date(y).toLocaleString():"(never)"}`);return p(b.join(`
283
+ `))}if(/^add$/i.test(i)){let f=r.slice(3).trim(),h=f.match(/^(\S+)\s+(heartbeat\s+.+)$/i);if(h){let T=Ks(h[1]);if(!T.ok)return p(T.error);let O=h[2].split(/\s+/).filter(Boolean),A=Fo(O);if(!A.ok)return p(A.error);let K=xe();if(Ye(K,T.name,t))return p(`Task "${T.name}" already exists. Remove it first or pick another name.`);let X=eo(T.name),ie={id:lr(),name:T.name,ownerPeerKey:t,command:"",cwd:"",outputDir:X,schedule:A.schedule,enabled:!0,notify:"self",notifyWhen:"always",attachLog:!1,attachFiles:[],lastCompletedSlotMs:null,createdAtMs:Date.now()};return K.push(ie),Oe(K),dn(K),ji(ie.id),p([`Saved heartbeat task "${T.name}" (${Fn(A.schedule)}).`,`Send /cowork checkin ${T.name} to record a heartbeat.`,"Alerts if no check-in arrives within the expected interval + grace period."].join(`
284
+ `))}let g=hw(f);if("error"in g)return p(g.error);let y=Ks(g.name);if(!y.ok)return p(y.error);let b=Fo(g.scheduleWords);if(!b.ok)return p(b.error);let v=xe();if(Ye(v,y.name,t))return p(`Task "${y.name}" already exists. Remove it first or pick another name.`);let E=oe(t),R=eo(y.name),P={id:lr(),name:y.name,ownerPeerKey:t,command:g.command,cwd:"",outputDir:R,schedule:b.schedule,enabled:!0,notify:"self",notifyWhen:"always",attachLog:!1,attachFiles:[],lastCompletedSlotMs:null,createdAtMs:Date.now()};return v.push(P),Oe(v),p([`Saved cowork task "${y.name}" (${Fn(b.schedule)}).`,`Output: ${R}`,`Notify: self \u2014 change with /cowork set ${y.name} notify wa|tg|all|none`].join(`
285
+ `))}let l=r.match(/^run\s+(\S+)\s*$/i);if(l){let f=l[1].toLowerCase(),h=Ye(xe(),f,t);return h?h.enabled?(fl({ownerPeerKey:t,name:h.name,at:Date.now()}),p(`On-demand run queued for "${h.name}" (runs within ~30s while omnish run is active).`)):p(`Cowork "${h.name}" is disabled. /cowork enable ${h.name}`):p(`Unknown task "${l[1]}". /cowork list`)}let d=r.match(/^checkin\s+(\S+)\s*$/i);if(d){let f=d[1].toLowerCase(),h=xe();dn(h);let g=Ye(h,f,t);return g?g.schedule.kind!=="heartbeat"?p(`"${g.name}" is not a heartbeat task.`):(ji(g.id),p(`Heartbeat recorded for "${g.name}".`)):p(`Unknown task "${d[1]}". /cowork list`)}let u=r.match(/^(?:remove|rm|del)\s+(\S+)\s*$/i);if(u){let f=u[1],h=xe(),g=h.findIndex(y=>y.name===f.toLowerCase()&&y.ownerPeerKey===t);return g===-1?p(`Unknown task "${f}".`):(h.splice(g,1),Oe(h),p(`Removed cowork task "${f.toLowerCase()}".`))}let c=r.match(/^enable\s+(\S+)\s*$/i);if(c)return Rp(c[1],t,!0);let m=r.match(/^disable\s+(\S+)\s*$/i);return m?Rp(m[1],t,!1):/^set$/i.test(i)?bw(r.slice(3).trim(),t):p("Unknown /cowork command. Try /cowork help")}function Rp(e,t,n){let r=xe(),o=Ye(r,e,t);return o?(o.enabled=n,Oe(r),p(`Cowork "${o.name}" ${n?"enabled":"disabled"}.`)):p(`Unknown task "${e}".`)}function gw(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 yw(e){let t=e.toLowerCase();return t==="always"||t==="all"?"always":t==="failure"||t==="fail"||t==="failures"?"failure":t==="state-change"||t==="statechange"||t==="change"||t==="transition"?"state-change":null}function ww(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 bw(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 | when \u2026 | attach <on|off> | files \u2026");let r=n[1],o=n[2].trim(),s=xe(),i=Ye(s,r,t);if(!i)return p(`Unknown task "${r}".`);if(o.toLowerCase().startsWith("cmd ")){let a=o.slice(4).trim(),l=" -- ",d=a.indexOf(l),u;if(d!==-1)u=a.slice(d+l.length).trim();else if(a.startsWith("--"))u=a.slice(2).trim();else return p("Usage: /cowork set <name> cmd -- <command\u2026>");return u?(i.command=u,Oe(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=Fo(a);return l.ok?(i.schedule=l.schedule,Oe(s),p(`Schedule for "${i.name}": ${Fn(l.schedule)}`)):p(l.error)}if(o.toLowerCase().startsWith("out ")){let a=o.slice(4).trim(),l=oe(t);return i.outputDir=Ge(a,l.cwd),Oe(s),p(`outputDir: ${i.outputDir}`)}if(o.toLowerCase().startsWith("cwd ")){let a=o.slice(4).trim(),l=oe(t);return i.cwd=a?Ge(a,l.cwd):"",Oe(s),p(`cwd: ${i.cwd||"(session cwd at run time)"}`)}if(o.toLowerCase().startsWith("notify ")){let a=o.slice(7).trim(),l=gw(a);return l?(i.notify=l,Oe(s),p(`notify: ${l}`)):p("notify must be self, wa, tg, all, or none.")}if(o.toLowerCase().startsWith("when ")){let a=o.slice(5).trim(),l=yw(a);return l?(i.notifyWhen=l,Oe(s),p(`notifyWhen: ${l}`)):p("when must be always, failure, or state-change.")}if(o.toLowerCase().startsWith("attach ")){let a=o.slice(7).trim(),l=$p(a);if(l.length!==1)return p("Usage: /cowork set <name> attach on|off");let d=ww(l[0]);return d===null?p("attach must be on or off"):(i.attachLog=d,Oe(s),p(`attachLog: ${d}`))}if(o.toLowerCase().startsWith("files ")){let a=o.slice(6).trim(),l=$p(a);return l.length===1&&l[0].toLowerCase()==="clear"?(i.attachFiles=[],Oe(s),p("files: (cleared)")):l.length===0?p("Usage: /cowork set <name> files clear | <pattern \u2026> \u2014 quote paths with spaces"):(i.attachFiles=l,Oe(s),p(`files: ${i.attachFiles.join(", ")}`))}return p("Unknown set field. Try: cmd, schedule, out, cwd, notify, when, attach, files")}me();import Dw from"node:fs";G();import Mp from"node:os";import kw from"node:path";var Sw=new Set(["create","delete","rename","update"]);function vw(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 xw(e){let t=e.split(",").map(n=>n.trim().toLowerCase());return t.length===0?!1:t.every(n=>Sw.has(n))}function $w(e){return e.split(",").map(t=>t.trim().toLowerCase())}function Ea(e,t,n){let r=e.trim();if(r.startsWith("-")&&(r=r.slice(1)),!r)return{};let o=Ge(r,n);return r.includes("*")||r.includes("?")?{glob:r.replace(/\\/g,"/")}:kw.isAbsolute(o)||r.startsWith("~")||r.startsWith("./")||r.startsWith("../")?{path:o}:r.startsWith("/")?{path:o}:{glob:r.includes("/")?r:`**/${r}`}}function Tp(e,t=Mp.homedir()){let n=e.replace(/\s+&&\s+/g," ").trim(),r=vw(n);if(r.length===0)return{ok:!1,error:"Usage: /watch add fs <name> <path> [events] [-exclude \u2026] [--exclude \u2026]"};let o=0,s=r[o];o+=1;let i=["create","delete","rename"],a=[],l=[];for(;o<r.length;){let u=r[o];if(u==="--exclude"){if(o+=1,o>=r.length)return{ok:!1,error:"--exclude requires a pattern."};let c=r[o];o+=1;let m=Ge(s,t),f=Ea(c.startsWith("-")?c:`-${c}`,m,t);f.path?a.push(f.path):f.glob&&l.push(f.glob);continue}if(u.startsWith("--"))return{ok:!1,error:`Unknown flag: ${u}`};if(u.startsWith("-")){let c=Ge(s,t),m=Ea(u,c,t);m.path?a.push(m.path):m.glob&&l.push(m.glob),o+=1;continue}if(xw(u)){i=$w(u),o+=1;continue}return{ok:!1,error:`Unexpected token "${u}". Put path first, then events or -excludes.`}}return{ok:!0,value:{rootPath:Ge(s,t),events:i,excludePaths:a,excludeGlobs:l}}}function Pa(e,t,n=Mp.homedir()){let r=Ea(e.startsWith("-")?e:`-${e}`,t,n);return!r.path&&!r.glob?{error:"Empty exclude pattern."}:r}import{spawnSync as Ms}from"node:child_process";import Ep from"node:fs";import Rw from"node:os";import Cw from"node:path";var Mw=40,Tw=10,Ts=15e3,Ew=["~/Projects","~/deploy","~/Downloads","~/src","/var/www","/srv"],Pp={linux:"/var/log/dpkg.log (also /var/log/apt/history.log)",darwin:"/var/log/install.log",win32:"Windows Application event log (install/remove)"};function Pw(e){return e.startsWith("~/")?Cw.join(Rw.homedir(),e.slice(2)):e}function Aw(e,t){return t?.trim()?e.toLowerCase().includes(t.trim().toLowerCase()):!0}function Iw(e){let t=n=>n==="running"||n==="active";return[...e].sort((n,r)=>{let o=t(n.state),s=t(r.state);return o!==s?o?-1:1:n.name.localeCompare(r.name)})}function Ow(e){let t=[];for(let n of e.split(`
286
+ `)){let r=n.trim();if(!r)continue;let o=r.split(/\s+/);if(o.length<4)continue;let s=o[0];if(!s.endsWith(".service"))continue;let i=o[2].toLowerCase(),a=s.slice(0,-8);t.push({name:a,state:i})}return t}function Lw(e){let t=[],n=e.split(`
287
+ `);for(let r=0;r<n.length;r++){let o=n[r].trim();if(!o||o.startsWith("PID"))continue;let s=o.split(/\s+/);if(s.length<3)continue;let i=s[s.length-1];if(!i.includes("."))continue;let l=s[0]==="-"?"stopped":"running";t.push({name:i,state:l})}return t}function Nw(e){let t=[],n="";for(let r of e.split(`
288
+ `)){let o=r.match(/SERVICE_NAME:\s*(.+)/i);if(o){n=o[1].trim();continue}let s=r.match(/^\s*STATE\s*:\s*\d+\s+(\S+)/i);if(s&&n){let i=s[1].toUpperCase(),a=i.toLowerCase();i==="RUNNING"?a="running":i==="STOPPED"&&(a="stopped"),t.push({name:n,state:a}),n=""}}return t}function _w(){let e=Ms("systemctl",["list-units","--type=service","--all","--no-pager","--plain","--no-legend"],{encoding:"utf8",timeout:Ts});return e.error||e.status!==0?{entries:[],error:"systemctl unavailable \u2014 install systemd or run on Linux."}:{entries:Ow(e.stdout??"")}}function Fw(){let e=Ms("launchctl",["list"],{encoding:"utf8",timeout:Ts});return e.error||e.status!==0?{entries:[],error:"launchctl unavailable."}:{entries:Lw(e.stdout??"")}}function Ww(){let e=Ms("sc",["query","type=","service","state=","all"],{encoding:"utf8",timeout:Ts,windowsHide:!0});if(!e.error&&e.status===0&&(e.stdout??"").includes("SERVICE_NAME"))return{entries:Nw(e.stdout??"")};let t=Ms("powershell",["-NoProfile","-Command","Get-Service | ForEach-Object { $_.Name + ' ' + $_.Status }"],{encoding:"utf8",timeout:Ts,windowsHide:!0});if(t.error||t.status!==0)return{entries:[],error:"sc query and Get-Service unavailable."};let n=[];for(let r of(t.stdout??"").split(`
289
+ `)){let o=r.trim();if(!o)continue;let s=o.lastIndexOf(" ");if(s<=0)continue;let i=o.slice(0,s).trim(),a=o.slice(s+1).trim().toLowerCase();n.push({name:i,state:a})}return{entries:n}}function Es(e){let t=e?.limit??Mw,n=e?.filter,r,o=process.platform;if(o==="linux")r=_w();else if(o==="darwin")r=Fw();else if(o==="win32")r=Ww();else return{entries:[],truncated:!1,totalMatched:0,error:`Service discovery not supported on ${o}.`};if(r.error)return{entries:[],truncated:!1,totalMatched:0,error:r.error};let s=Iw(r.entries.filter(l=>Aw(l.name,n))),i=s.length,a=i>t;return{entries:s.slice(0,t),truncated:a,totalMatched:i}}function Uw(e,t=Tw){let n=[];for(let r=0;r<e.length;r+=t)n.push(e.slice(r,r+t));return n}function Aa(e,t){if(e.error)return p(e.error);if(e.entries.length===0){let o=t?.trim()?`No services match "${t}". Try /watch svc list without a filter.`:"No services found on this host.";return p(o)}let n=e.entries.map(o=>`${Re}${o.name} \u2014 ${o.state}`),r=t?.trim()?`Services matching "${t}" (${e.entries.length} shown):`:`Services on ${process.platform} (${e.entries.length} shown):`;if(n.unshift(r),e.truncated){let o=e.totalMatched-e.entries.length;n.push("",`${o} more \u2014 narrow with /watch svc list <filter>`)}return n.push("","Copy-paste templates follow in the next message."),p(n.join(`
290
+ `))}function Ia(e,t){let n=t?.trim()||"<name>";if(e.length===0)return p("No services to template \u2014 run /watch svc list first.");let r=e.map(i=>i.name),o=Uw(r),s=[t?.trim()?`Copy-paste (/watch add svc ${n} \u2026):`:"Copy-paste (replace <name>):",""];for(let i of o)s.push(`/watch add svc ${n} ${i.join(" ")}`);return p(s.join(`
291
+ `))}function Ap(){let e=process.platform,t=e==="linux"?"linux":e==="darwin"?"darwin":"win32",n=["Watch hints",""];n.push("Package log (pkg watches):"),n.push(` ${Pp[t]??Pp.linux}`),n.push("","Filesystem roots that exist on this host:");let r=[];for(let o of Ew){let s=Pw(o);try{Ep.existsSync(s)&&Ep.statSync(s).isDirectory()&&r.push(o)}catch{}}if(r.length===0)n.push(" (none of the usual paths found \u2014 use any directory you own)");else for(let o of r)n.push(` ${o}`),n.push(` /watch add fs <name> ${o} create,delete,rename`);return n.push("","Service names: /watch svc list [filter]"),p(n.join(`
292
+ `))}function U(e){return{replies:[e]}}function Hw(...e){return{replies:e}}function Bw(e){let t=e.trim();if(!t)return{};let n=t.split(/\s+/);if(n.length>=2){let s=ro(n[0]);return s.ok?{ruleName:s.name,filter:n.slice(1).join(" ")}:{filter:t}}let r=n[0],o=ro(r);if(o.ok){let s=Es({filter:r,limit:1});if(s.totalMatched===0&&!s.error)return{ruleName:o.name}}return{filter:r}}function jw(){return p(["Watch \u2014 OS event eye (watchEnabled + omnish run). Docs: docs/features/watch.md","","add fs <name> <path> [events] [-/exclude \u2026] [--exclude glob]",' e.g. /watch add fs home ~/Projects create,delete -~/Projects/tmp --exclude "**/.keyfolder"',"add pkg <name> | add svc <name> <unit\u2026>","svc list [filter] \u2014 services on this host (+ templates in next message)","svc templates [ruleName] [filter] \u2014 copy-paste /watch add svc lines only","hints \u2014 suggested FS paths and package log location","list | status | reload | show <name> | recent [N]","pause|stop <name> \u2014 stop alerts, keep rule","resume <name> | enable <name> | disable <name> | rm <name>","exclude <name> list|add <pattern>|rm <pattern>","set <name> notify self|wa|tg|all|none","set <name> when always|state-change","on | off \u2014 global watchEnabled in config","Rules are device-wide (~/.omnish/watch/rules.json); any allowlisted peer can manage.","notify:self alerts the peer who created the rule."].join(`
293
+ `))}function Ip(){return{excludePaths:[],excludeGlobs:[]}}function Gw(e){return e.notify==="self"?`notify=self (${e.ownerPeerKey})`:`notify=${e.notify}`}function Jw(e,t){Vs(e.id),t==="pause"||t==="stop"?e.paused=!0:t==="resume"?e.paused=!1:t==="enable"?(e.enabled=!0,e.paused=!1):t==="disable"&&(e.enabled=!1,e.paused=!1)}function Op(e,t){let n=e.trim();if(!n||/^help$/i.test(n))return U(jw());let r=n.split(/\s+/)[0].toLowerCase();if(to(),/^hints$/i.test(r))return U(Ap());if(/^svc$/i.test(r)){let u=n.slice(3).trim(),m=(u.split(/\s+/)[0]??"").toLowerCase();if(m==="list"){let f=u.slice(4).trim()||void 0,h=Es({filter:f});return h.error?U(p(h.error)):h.entries.length===0?U(Aa(h,f)):Hw(Aa(h,f),Ia(h.entries))}if(m==="templates"){let f=u.slice(9).trim(),{ruleName:h,filter:g}=Bw(f),y=Es({filter:g});return y.error?U(p(y.error)):U(Ia(y.entries,h))}return U(p("svc subcommands: list [filter] | templates [ruleName] [filter]"))}if(/^status$/i.test(r)){let u=$(),{summary:c}=ur(),m=[`watchEnabled: ${u.watchEnabled} watchAutoRestore: ${u.watchAutoRestore}`,`debounce: ${u.watchDebounceMs}ms max/min: ${u.watchMaxEventsPerMinute}`,`rules file: ${Ys()} (${c.total} saved, ${c.active} active, ${c.paused} paused, ${c.disabled} disabled)`,`events db: ${Jr}`];c.total>0&&!u.watchEnabled?m.push("Rules are on disk; adapters stopped \u2014 /watch on to start."):c.total>0&&u.watchEnabled&&!u.watchAutoRestore?m.push("watchAutoRestore is off \u2014 /watch reload to start adapters without restarting gateway."):c.paused>0&&m.push(`${c.paused} paused rule(s) stay stopped until /watch resume <name>.`);let f=jl();return f?(m.push("","Adapters:"),m.push(...f.getStatusLines())):u.watchEnabled&&u.watchAutoRestore?m.push("","(manager starting \u2014 try /watch status again)"):u.watchEnabled?m.push("","(manager idle \u2014 /watch reload)"):m.push("","(manager off \u2014 /watch on)"),U(p(m.join(`
294
+ `)))}if(/^reload$/i.test(r)){let{summary:u}=ur();return $().watchEnabled?(Gl(),U(p(`Reloading from ${Ys()}: ${u.total} rule(s) on disk, ${u.active} eligible to run. /watch status for adapters.`))):U(p(`Rules on disk: ${u.total} (${u.active} active). watchEnabled is false \u2014 /watch on first.`))}if(/^list$/i.test(r)){let u=Le();if(u.length===0)return U(p("(no watch rules on this device)"));let c=u.map(m=>{let f=m.enabled?m.paused?"paused":"on":"disabled",h=m.kind==="fs"?m.path:m.kind==="svc"?m.units.join(","):"system";return`${Re}${m.name} [${m.kind}] ${f} ${Gw(m)} when=${m.notifyWhen} \u2014 ${h}`});return U(p(c.join(`
295
+ `)))}let o=n.match(/^recent(?:\s+(\d+))?\s*$/i);if(o){let u=o[1]?Number(o[1]):15,c=Le(),m=new Set(c.map(g=>g.id)),f=kl(u,m);if(f.length===0)return U(p("(no recent watch events)"));let h=f.map(g=>`${new Date(g.tsMs).toLocaleString()} ${g.ruleName} ${g.summary}`);return U(p(h.join(`
296
+ `)))}let s=n.match(/^show\s+(\S+)\s*$/i);if(s){let u=tn(Le(),s[1]);if(!u)return U(p(`Unknown rule "${s[1]}". /watch list`));let c=[`name: ${u.name}`,`kind: ${u.kind}`,`creator: ${u.ownerPeerKey}`,`enabled: ${u.enabled}`,`paused: ${u.paused}`,`notify: ${u.notify}`,`notifyWhen: ${u.notifyWhen}`,`adapter: ${u.adapterStatus||"(unknown)"}`];return u.kind==="fs"&&(c.push(`path: ${u.path}`),c.push(`events: ${u.events.join(",")}`),c.push(`excludePaths: ${u.excludePaths.length?u.excludePaths.join(", "):"(none)"}`),c.push(`excludeGlobs: ${u.excludeGlobs.length?u.excludeGlobs.join(", "):"(none)"}`)),u.kind==="svc"&&c.push(`units: ${u.units.join(", ")||"(none)"}`),U(p(c.join(`
297
+ `)))}if(/^on$/i.test(r))return W({watchEnabled:!0}),De(),U(p("watchEnabled: true (gateway picks up watchers when running)."));if(/^off$/i.test(r))return W({watchEnabled:!1}),De(),U(p("watchEnabled: false \u2014 all adapters stopped."));let i=n.match(/^exclude\s+(\S+)\s+(\S+)(?:\s+([\s\S]+))?\s*$/i);if(i){let u=i[1],c=i[2].toLowerCase(),m=(i[3]??"").trim(),f=Le(),h=tn(f,u);if(!h)return U(p(`Unknown rule "${u}".`));if(h.kind!=="fs")return U(p("exclude applies to filesystem rules only."));if(c==="list"){let g=[`excludePaths: ${h.excludePaths.length?h.excludePaths.join(`
298
298
  `):"(none)"}`,`excludeGlobs: ${h.excludeGlobs.length?h.excludeGlobs.join(`
299
299
  `):"(none)"}`];return U(p(g.join(`
300
- `)))}if(c==="add"){if(!m)return U(p("Usage: /watch exclude <name> add <pattern>"));let g=Ra(m,h.path);if("error"in g)return U(p(g.error));if(g.path){if(Qe(g.path))return U(p("That path is blocked."));h.excludePaths.includes(g.path)||h.excludePaths.push(g.path)}return g.glob&&!h.excludeGlobs.includes(g.glob)&&h.excludeGlobs.push(g.glob),pt(Sn(f,h)),De(),U(p(`Added exclude to "${h.name}".`))}if(c==="rm"||c==="remove"){if(!m)return U(p("Usage: /watch exclude <name> rm <pattern>"));let g=Ra(m,h.path);return"error"in g?U(p(g.error)):(g.path&&(h.excludePaths=h.excludePaths.filter(y=>y!==g.path)),g.glob&&(h.excludeGlobs=h.excludeGlobs.filter(y=>y!==g.glob)),pt(Sn(f,h)),De(),U(p(`Removed exclude from "${h.name}".`)))}return U(p("exclude subcommands: list | add | rm"))}if(/^add$/i.test(r)){let u=n.slice(3).trim(),c=u.split(/\s+/);if(c.length<2)return U(p("Usage: /watch add fs|pkg|svc <name> \u2026"));let m=c[0].toLowerCase(),f=m==="fs"||m==="pkg"||m==="svc"?m:null;if(!f)return U(p("Kind must be fs, pkg, or svc."));let h=c[1],g=Zr(h);if(!g.ok)return U(p(g.error));let y=Le();if(y.length>=Js)return U(p(`Max ${Js} watch rules on this device.`));if(Zt(y,g.name))return U(p(`Rule "${g.name}" already exists.`));let b;if(f==="fs"){let R=u.slice(m.length+1+h.length).trim(),P=xp(R);if(!P.ok)return U(p(P.error));let{rootPath:T,events:O,excludePaths:A,excludeGlobs:K}=P.value;if(Qe(T))return U(p("That path is blocked (sensitive). Choose another directory."));if(!Lw.existsSync(T))return U(p(`Path not found: ${T}`));for(let X of A)if(Qe(X))return U(p(`Excluded path blocked: ${X}`));b={id:eo(),name:g.name,ownerPeerKey:t,kind:"fs",enabled:!0,paused:!1,notify:"self",notifyWhen:"always",path:T,events:O,units:[],excludePaths:A,excludeGlobs:K,adapterStatus:"",createdAtMs:Date.now()}}else if(f==="pkg")b={id:eo(),name:g.name,ownerPeerKey:t,kind:"pkg",enabled:!0,paused:!1,notify:"self",notifyWhen:"always",path:"",events:[],units:[],...Mp(),adapterStatus:"",createdAtMs:Date.now()};else{let R=c.slice(2);if(R.length===0)return U(p("Usage: /watch add svc <name> <unit\u2026>"));b={id:eo(),name:g.name,ownerPeerKey:t,kind:"svc",enabled:!0,paused:!1,notify:"self",notifyWhen:"always",path:"",events:[],units:R,...Mp(),adapterStatus:"",createdAtMs:Date.now()}}pt(Sn(y,b)),De();let E=$().watchEnabled?"":" Rule saved to disk. /watch on to start adapters.";return U(p(`Watch rule "${b.name}" added (${b.kind}).${E}`))}let a=n.match(/^set\s+(\S+)\s+(\S+)\s+(\S+)\s*$/i);if(a){let[,u,c,m]=a,f=Le(),h=Zt(f,u);if(!h)return U(p(`Unknown rule "${u}".`));let g=c.toLowerCase(),y=m.toLowerCase();if(g==="notify"){let b=y==="wa"||y==="whatsapp"?"wa":y==="tg"||y==="telegram"?"tg":y==="all"?"all":y==="none"?"none":y==="self"?"self":null;if(!b)return U(p("notify must be self, wa, tg, all, or none."));h.notify=b}else if(g==="when"){let b=y==="state-change"?"state-change":y==="always"?"always":null;if(!b)return U(p("when must be always or state-change."));h.notifyWhen=b}else return U(p("set supports: notify, when"));return pt(Sn(f,h)),De(),U(p(`Updated ${h.name} ${g}=${y}.`))}let l=n.match(/^(?:rm|remove)\s+(\S+)\s*$/i);if(l){let u=l[1],c=Le(),m=Zt(c,u);return m?(qs(m.id),pt(kl(c,u)),De(),U(p(`Removed watch rule "${u}".`))):U(p(`Unknown rule "${u}".`))}let d=n.match(/^(pause|stop|resume|enable|disable)\s+(\S+)\s*$/i);if(d){let u=d[1].toLowerCase(),c=d[2],m=Le(),f=Zt(m,c);if(!f)return U(p(`Unknown rule "${c}".`));Uw(f,u==="stop"?"pause":u),pt(Sn(m,f)),De();let g=u==="stop"?"paused (stop)":`${u}d`;return U(p(`${f.name}: ${g}.`))}return U(p("Unknown /watch command. Try /watch help"))}pe();function Ep(){return p(["Tunnel commands:",'/tunnel signup --email "you@example.com" --password "yourpass"','/tunnel signup --phone "+1234567890" --password "yourpass"','/tunnel login --email "you@example.com" --password "yourpass"','/tunnel login --token "<token>" [--relay <url>]',"/tunnel logout","/tunnel status [--relay <url>]","/tunnel http <port> [--name <slug>] [--host <addr>]","/tunnel tcp <port> [--name <slug>] [--host <addr>]","/tunnels","/tunnel stop <id|slug>"].join(`
301
- `))}async function Ta(e,t,n){let r=vd(e);if(r.kind==="help")return Ep();if(r.kind==="error")return p(r.message);if(r.kind==="signup"){let o=r.email?.trim()||"",s=r.phone?.trim()||"",i=r.password||"";if(!o&&!s)return p('Usage: /tunnel signup --email "you@example.com" --password "yourpass"');if(i.length<8)return p("Password must be at least 8 characters.");let a=$(),l=r.relayUrl?.trim()||rt(a.tunnelRelayUrl||$e),d=await us(l,{...o?{email:o}:{},...s?{phone:s}:{},password:i});return d.ok?(ft({token:d.token,...r.relayUrl?.trim()?{relayUrl:r.relayUrl.trim()}:{}}),p("Account created. Token saved.")):p(`Signup failed: ${d.error}`)}if(r.kind==="login"){if(r.email||r.phone){let i=r.password||"";if(!i)return p('Usage: /tunnel login --email "you@example.com" --password "yourpass"');let a=$(),l=r.relayUrl?.trim()||rt(a.tunnelRelayUrl||$e),d=await ds(l,{...r.email?{email:r.email}:{},...r.phone?{phone:r.phone}:{},password:i});return d.ok?(ft({token:d.token,...r.relayUrl?.trim()?{relayUrl:r.relayUrl.trim()}:{}}),p("Logged in. Token saved.")):p(`Login failed: ${d.error}`)}let s=r.token?.trim();return s?(ft({token:s,...r.relayUrl?.trim()?{relayUrl:r.relayUrl.trim()}:{}}),p("Tunnel token saved on this host (not shown). If OMNISH_TUNNEL_TOKEN is set in the gateway environment, it overrides the file until unset.")):p('Usage: /tunnel login --token "<token>" [--relay <url>]')}if(r.kind==="logout")return lo(),p("Tunnel token file removed. Unset OMNISH_TUNNEL_TOKEN in the gateway environment if you rely on it.");if(r.kind==="status"){let o=$(),s=r.relayUrl?.trim()||rt(o.tunnelRelayUrl||$e),i=ht(),a=!!process.env.OMNISH_TUNNEL_TOKEN?.trim(),d=!!xt()?.token?.trim(),u=await is(s,i),c=[`Relay: ${s}`,`Token: ${i?"configured":"missing"}${a?" (OMNISH_TUNNEL_TOKEN)":d?" (tunnel-auth.json)":""}`,`Health: ${u.healthOk?`ok${u.healthVersion?` (version ${u.healthVersion})`:""}`:"fail"}`,`Control (WSS): ${u.controlOk?"auth ok":"fail"}`,`Active tunnels: ${n.getActiveCount()}`];return!u.ok&&u.error&&c.push(`Detail: ${u.error}`),p(c.join(`
300
+ `)))}if(c==="add"){if(!m)return U(p("Usage: /watch exclude <name> add <pattern>"));let g=Pa(m,h.path);if("error"in g)return U(p(g.error));if(g.path){if(Qe(g.path))return U(p("That path is blocked."));h.excludePaths.includes(g.path)||h.excludePaths.push(g.path)}return g.glob&&!h.excludeGlobs.includes(g.glob)&&h.excludeGlobs.push(g.glob),mt(Cn(f,h)),De(),U(p(`Added exclude to "${h.name}".`))}if(c==="rm"||c==="remove"){if(!m)return U(p("Usage: /watch exclude <name> rm <pattern>"));let g=Pa(m,h.path);return"error"in g?U(p(g.error)):(g.path&&(h.excludePaths=h.excludePaths.filter(y=>y!==g.path)),g.glob&&(h.excludeGlobs=h.excludeGlobs.filter(y=>y!==g.glob)),mt(Cn(f,h)),De(),U(p(`Removed exclude from "${h.name}".`)))}return U(p("exclude subcommands: list | add | rm"))}if(/^add$/i.test(r)){let u=n.slice(3).trim(),c=u.split(/\s+/);if(c.length<2)return U(p("Usage: /watch add fs|pkg|svc <name> \u2026"));let m=c[0].toLowerCase(),f=m==="fs"||m==="pkg"||m==="svc"?m:null;if(!f)return U(p("Kind must be fs, pkg, or svc."));let h=c[1],g=ro(h);if(!g.ok)return U(p(g.error));let y=Le();if(y.length>=qs)return U(p(`Max ${qs} watch rules on this device.`));if(tn(y,g.name))return U(p(`Rule "${g.name}" already exists.`));let b;if(f==="fs"){let R=u.slice(m.length+1+h.length).trim(),P=Tp(R);if(!P.ok)return U(p(P.error));let{rootPath:T,events:O,excludePaths:A,excludeGlobs:K}=P.value;if(Qe(T))return U(p("That path is blocked (sensitive). Choose another directory."));if(!Dw.existsSync(T))return U(p(`Path not found: ${T}`));for(let X of A)if(Qe(X))return U(p(`Excluded path blocked: ${X}`));b={id:oo(),name:g.name,ownerPeerKey:t,kind:"fs",enabled:!0,paused:!1,notify:"self",notifyWhen:"always",path:T,events:O,units:[],excludePaths:A,excludeGlobs:K,adapterStatus:"",createdAtMs:Date.now()}}else if(f==="pkg")b={id:oo(),name:g.name,ownerPeerKey:t,kind:"pkg",enabled:!0,paused:!1,notify:"self",notifyWhen:"always",path:"",events:[],units:[],...Ip(),adapterStatus:"",createdAtMs:Date.now()};else{let R=c.slice(2);if(R.length===0)return U(p("Usage: /watch add svc <name> <unit\u2026>"));b={id:oo(),name:g.name,ownerPeerKey:t,kind:"svc",enabled:!0,paused:!1,notify:"self",notifyWhen:"always",path:"",events:[],units:R,...Ip(),adapterStatus:"",createdAtMs:Date.now()}}mt(Cn(y,b)),De();let E=$().watchEnabled?"":" Rule saved to disk. /watch on to start adapters.";return U(p(`Watch rule "${b.name}" added (${b.kind}).${E}`))}let a=n.match(/^set\s+(\S+)\s+(\S+)\s+(\S+)\s*$/i);if(a){let[,u,c,m]=a,f=Le(),h=tn(f,u);if(!h)return U(p(`Unknown rule "${u}".`));let g=c.toLowerCase(),y=m.toLowerCase();if(g==="notify"){let b=y==="wa"||y==="whatsapp"?"wa":y==="tg"||y==="telegram"?"tg":y==="all"?"all":y==="none"?"none":y==="self"?"self":null;if(!b)return U(p("notify must be self, wa, tg, all, or none."));h.notify=b}else if(g==="when"){let b=y==="state-change"?"state-change":y==="always"?"always":null;if(!b)return U(p("when must be always or state-change."));h.notifyWhen=b}else return U(p("set supports: notify, when"));return mt(Cn(f,h)),De(),U(p(`Updated ${h.name} ${g}=${y}.`))}let l=n.match(/^(?:rm|remove)\s+(\S+)\s*$/i);if(l){let u=l[1],c=Le(),m=tn(c,u);return m?(Vs(m.id),mt(Cl(c,u)),De(),U(p(`Removed watch rule "${u}".`))):U(p(`Unknown rule "${u}".`))}let d=n.match(/^(pause|stop|resume|enable|disable)\s+(\S+)\s*$/i);if(d){let u=d[1].toLowerCase(),c=d[2],m=Le(),f=tn(m,c);if(!f)return U(p(`Unknown rule "${c}".`));Jw(f,u==="stop"?"pause":u),mt(Cn(m,f)),De();let g=u==="stop"?"paused (stop)":`${u}d`;return U(p(`${f.name}: ${g}.`))}return U(p("Unknown /watch command. Try /watch help"))}me();rn();on();function Lp(){return p(["Tunnel commands:",'/tunnel signup --email "you@example.com" --password "yourpass"','/tunnel signup --phone "+1234567890" --password "yourpass"','/tunnel login --email "you@example.com" --password "yourpass"','/tunnel login --token "<token>" [--relay <url>]',"/tunnel logout","/tunnel status [--relay <url>]","/tunnel http <port> [--name <slug>] [--host <addr>]","/tunnel tcp <port> [--name <slug>] [--host <addr>]","/tunnels","/tunnel stop <id|slug>"].join(`
301
+ `))}async function Oa(e,t,n){let r=Md(e);if(r.kind==="help")return Lp();if(r.kind==="error")return p(r.message);if(r.kind==="signup"){let o=r.email?.trim()||"",s=r.phone?.trim()||"",i=r.password||"";if(!o&&!s)return p('Usage: /tunnel signup --email "you@example.com" --password "yourpass"');if(i.length<8)return p("Password must be at least 8 characters.");let a=$(),l=r.relayUrl?.trim()||rt(a.tunnelRelayUrl||$e),d=await ms(l,{...o?{email:o}:{},...s?{phone:s}:{},password:i});return d.ok?(ht({token:d.token,...r.relayUrl?.trim()?{relayUrl:r.relayUrl.trim()}:{}}),p("Account created. Token saved.")):p(`Signup failed: ${d.error}`)}if(r.kind==="login"){if(r.email||r.phone){let i=r.password||"";if(!i)return p('Usage: /tunnel login --email "you@example.com" --password "yourpass"');let a=$(),l=r.relayUrl?.trim()||rt(a.tunnelRelayUrl||$e),d=await fs(l,{...r.email?{email:r.email}:{},...r.phone?{phone:r.phone}:{},password:i});return d.ok?(ht({token:d.token,...r.relayUrl?.trim()?{relayUrl:r.relayUrl.trim()}:{}}),p("Logged in. Token saved.")):p(`Login failed: ${d.error}`)}let s=r.token?.trim();return s?(ht({token:s,...r.relayUrl?.trim()?{relayUrl:r.relayUrl.trim()}:{}}),p("Tunnel token saved on this host (not shown). If OMNISH_TUNNEL_TOKEN is set in the gateway environment, it overrides the file until unset.")):p('Usage: /tunnel login --token "<token>" [--relay <url>]')}if(r.kind==="logout")return mo(),p("Tunnel token file removed. Unset OMNISH_TUNNEL_TOKEN in the gateway environment if you rely on it.");if(r.kind==="status"){let o=$(),s=r.relayUrl?.trim()||rt(o.tunnelRelayUrl||$e),i=gt(),a=!!process.env.OMNISH_TUNNEL_TOKEN?.trim(),d=!!$t()?.token?.trim(),u=await cs(s,i),c=[`Relay: ${s}`,`Token: ${i?"configured":"missing"}${a?" (OMNISH_TUNNEL_TOKEN)":d?" (tunnel-auth.json)":""}`,`Health: ${u.healthOk?`ok${u.healthVersion?` (version ${u.healthVersion})`:""}`:"fail"}`,`Control (WSS): ${u.controlOk?"auth ok":"fail"}`,`Active tunnels: ${n.getActiveCount()}`];return!u.ok&&u.error&&c.push(`Detail: ${u.error}`),p(c.join(`
302
302
  `))}if(!t.tunnelEnabled)return p("Chat tunneling is disabled. Use /config set tunnelEnabled true (then expose with /tunnel http \u2026).");if(r.kind==="list"){let o=n.list();return o.length===0?p("(no active tunnels)"):p(o.map(s=>`${s.id} ${s.kind} ${s.status}
303
303
  ${s.publicUrl||"(pending)"}
304
304
  ${s.localHost}:${s.localPort}`).join(`
@@ -306,76 +306,76 @@ ${s.detail}`)}return p("Unknown /service command. Try /service help")}pe();funct
306
306
  `))}if(r.kind==="stop"){let o=await n.stop(r.target);return o?p(`Stopped tunnel ${o.id}.`):p(`No active tunnel matched "${r.target}".`)}if(r.kind==="expose"){let o=await n.expose(t,r.options);return p(`${o.kind.toUpperCase()} tunnel active
307
307
  public: ${o.publicUrl}
308
308
  local: ${o.localHost}:${o.localPort}
309
- id: ${o.id}`)}return Ep()}function Ea(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 I(e){return{kind:"text",body:e}}function Dw(e,t){return`${e}:${t}`}function Hw(e,t){return`${e}:apps:${t}`}async function Bw(e,t){let n=e.trim();if(n===""||/^status$/i.test(n)||/^show$/i.test(n)||/^get$/i.test(n)){let a=$();return oc({gatewayMode:a.gatewayMode,authPresent:nt(),tokenSet:!!ve(a),allowN:a.allowFrom.length,tgAllowN:a.telegramAllowFrom.length,updateBrief:ms(vr())})}if(/^help$/i.test(n))return Z(ii());let r=gn(n);if(!r)return kc();qr(r);let o=$(),s,i=!1;if(t?.reload){let a=await t.reload();s=a.ok?a.summary:`Reload failed: ${a.error}`}else i=!0;return uc(o.gatewayMode,s,i)}function jw(e){let t=e.trim();if(!dt(t))return cc();let n=Ot(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.'),lc(r)}async function Pp(e,t,n){let r=oe(t),o=Jl(n);if(o!==null){let a=Kl(r.cwd,o),l=zl(a);return l.ok?(ro(t,a),p(`cwd: ${a}`)):p(`cd: ${l.error}`)}let s=await sn(e.shell,n,{timeoutMs:e.syncTimeoutMs,maxBytes:e.syncMaxBytes,cwd:r.cwd}),i=[];return i.push(`$ ${n}`),s.stdout.trim()&&i.push(s.stdout.trimEnd()),s.stderr.trim()&&(i.push("\u2014 stderr \u2014"),i.push(s.stderr.trimEnd())),s.timedOut?i.push(`Timed out (${Math.round(e.syncTimeoutMs/1e3)}s limit).`):s.code!==0&&s.code!==null&&i.push(`Exit ${s.code}`),p(i.join(`
310
- `))}async function Vn(e,t,n,r,o,s,i,a,l=null,d=!1,u){let c=s.text.trim(),m=s.peerKey,f=c.match(/^!!\s*(start|stop)\s*$/i);if(f)return f[1].toLowerCase()==="start"?(o.set(m,!0),I(yc())):(o.set(m,!1),I(p("Free shell mode off.")));if(c.startsWith(e.commandPrefix)){let g=c.slice(e.commandPrefix.length).trim();if(!g)return I(p(`Send ${e.commandPrefix}<command> or /help`));if(!d&&Gd(g)){let y=ga(m,g);if(y!==void 0)return await Vn(e,t,n,r,o,{...s,text:y},i,a,l,!0,u)}return I(await Pp(e,m,g))}if(/^\/help\s+files$/i.test(c.trim()))return I(ci());if(c==="/help"||c==="help")return I(Z(xn(e)));if(c.startsWith("/")){if(/^\/files(?:\s+help)?$/i.test(c.trim()))return I(ci());let g=c.match(/^\/receive\b(?:\s+(\S+))?$/i);if(g){let k=(g[1]??"status").toLowerCase(),x=new Set(["here","cwd","session","dir"]),L=new Set(["default","global","reset"]);if(x.has(k)){Xs(m,"sessionCwd");let F=oe(m).cwd;return I(p(`Inbound files will save under this chat\u2019s session folder:
309
+ id: ${o.id}`)}return Lp()}function La(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 I(e){return{kind:"text",body:e}}function Kw(e,t){return`${e}:${t}`}function zw(e,t){return`${e}:apps:${t}`}async function qw(e,t){let n=e.trim();if(n===""||/^status$/i.test(n)||/^show$/i.test(n)||/^get$/i.test(n)){let a=$();return uc({gatewayMode:a.gatewayMode,authPresent:nt(),tokenSet:!!ve(a),allowN:a.allowFrom.length,tgAllowN:a.telegramAllowFrom.length,updateBrief:gs(Mr())})}if(/^help$/i.test(n))return Z(ci());let r=Sn(n);if(!r)return Cc();Xr(r);let o=$(),s,i=!1;if(t?.reload){let a=await t.reload();s=a.ok?a.summary:`Reload failed: ${a.error}`}else i=!0;return gc(o.gatewayMode,s,i)}function Yw(e){let t=e.trim();if(!pt(t))return hc();let n=Nt(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.'),fc(r)}async function Np(e,t,n){let r=oe(t),o=Vl(n);if(o!==null){let a=Xl(r.cwd,o),l=Zl(a);return l.ok?(ao(t,a),p(`cwd: ${a}`)):p(`cd: ${l.error}`)}let s=await un(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(`
310
+ `))}async function tr(e,t,n,r,o,s,i,a,l=null,d=!1,u){let c=s.text.trim(),m=s.peerKey,f=c.match(/^!!\s*(start|stop)\s*$/i);if(f)return f[1].toLowerCase()==="start"?(o.set(m,!0),I(xc())):(o.set(m,!1),I(p("Free shell mode off.")));if(c.startsWith(e.commandPrefix)){let g=c.slice(e.commandPrefix.length).trim();if(!g)return I(p(`Send ${e.commandPrefix}<command> or /help`));if(!d&&Yd(g)){let y=Sa(m,g);if(y!==void 0)return await tr(e,t,n,r,o,{...s,text:y},i,a,l,!0,u)}return I(await Np(e,m,g))}if(/^\/help\s+files$/i.test(c.trim()))return I(pi());if(c==="/help"||c==="help")return I(Z(Tn(e)));if(c.startsWith("/")){if(/^\/files(?:\s+help)?$/i.test(c.trim()))return I(pi());let g=c.match(/^\/receive\b(?:\s+(\S+))?$/i);if(g){let k=(g[1]??"status").toLowerCase(),x=new Set(["here","cwd","session","dir"]),L=new Set(["default","global","reset"]);if(x.has(k)){ti(m,"sessionCwd");let F=oe(m).cwd;return I(p(`Inbound files will save under this chat\u2019s session folder:
311
311
  ${F}
312
312
  (layout: \u2026/<peer>/<date>/<file> under that root).
313
313
 
314
- Change folder with ${e.commandPrefix}cd \u2026 Send /receive default to use the server config again.`))}return L.has(k)?(Xs(m,"default"),I(p("Per-chat inbound folder cleared. Uploads now follow fileReceiveRootMode in config.json (/files)."))):I(k==="help"?ui():k==="status"?fc(e,m):ui())}if(c==="/reload"||c==="/restart"){if(!a?.reload)return I(p("Reload is only available while the omnish gateway (omnish run) is running."));let k=await a.reload();return I(p(k.ok?k.summary:`Reload failed: ${k.error}`))}if(c==="/updates"||c.startsWith("/updates ")){let k=c.slice(8).trim().toLowerCase();if(k==="cached"||k==="last"){let L=vr();return I(L?cr(L):p("No update snapshot yet. Send /updates (live check) once, or enable updateCheckEnabled and wait for the scheduled check."))}let x=await xr(Ze(),$());return I(cr(x))}let y=c.match(/^\/security(?:\s+(\S+))?\s*$/i);if(y){let k=(y[1]??"").toLowerCase(),x=$(),L=Rt(x);return I(k==="help"||k==="?"?Z(Ic()):k==="summary"||k==="brief"?p(ec(L,"Send /security for the full report.")):k==="tips"?Z(Ac()):k===""||k==="full"||k==="report"?Pc(L):p("Unknown /security subcommand. Try /security, /security summary, /security tips, or /security help"))}let b=c.match(/^\/(gateway|gw|mode)\b(?:\s+(.*))?$/i);if(b){let k=(b[2]??"").trim();return I(await Bw(k,a))}if(c==="/config"||c.startsWith("/config ")){let k=c.slice(7).trim();return I(await Fc(k,a))}let v=Vd(c);if(v!==null)return I(await Xd(e,v));let E=Ea(c);if(E!==null){let k=Xc(e,E,l);return k===null?null:I(k)}let R=c.match(/^\/(send|file)\b(?:\s+([\s\S]+))?$/i);if(R){let k=(R[2]??"").trim();if(!k)return I(li());let x=hs(k);if(!x)return I(li());let L=oe(m).cwd,F=await Rr(L,x.selectorPart);if(F.length===0)return I(p(`No files matched: ${x.selectorPart}`));let Q=await Cr(F);if(!Q.ok)return I(p(Q.error));let z=fe()?Vo(e):e.fileSendMaxBytes,we=[];for(let Ue of F){let Ie=Kt(Ue,z);if("error"in Ie)return I(p(Ie.error));we.push({absPath:Ie.absPath,category:Ie.category,mimetype:Ie.mimetype,displayName:Ie.displayName,caption:x.caption})}return we.length===1?{kind:"file",spec:we[0]}:{kind:"files",specs:we}}if(c==="/allowlist"){let k=$();return I(ac([{label:"allowFrom (WhatsApp)",items:k.allowFrom},{label:"telegramAllowFrom",items:k.telegramAllowFrom}]))}let P=c.match(/^\/allow\b\s+(.+)$/i);if(P)try{let k=Kr(P[1].trim());return I(ai(k))}catch(k){return I(p(String(k)))}if(/^\/allow\b\s*$/i.test(c))return I(dc());let T=c.match(/^\/deny\b\s+(.+)$/i);if(T)try{let k=zr(T[1].trim());return I(ai(k))}catch(k){return I(p(String(k)))}if(/^\/deny\b\s*$/i.test(c))return I(pc());let O=c.match(/^\/(whatsapp|wa|telegram|tg)\b(?:\s+(.*))?$/i);if(O){let k=O[1].toLowerCase(),x=(O[2]??"").trim(),L=k==="whatsapp"||k==="wa";if(k==="telegram"||k==="tg"){let Q=x.match(/^token\s+(\S+)\s*$/i);return Q?I(jw(Q[1]??"")):x===""||/^help$/i.test(x)?I(Z(ic($()))):I(gc())}if(L)return x===""||/^help$/i.test(x)?I(Z(sc(e))):I(hc())}let A=c.match(/^\/(cowork|cw)\b(?:\s+([\s\S]*))?$/i);if(A){let k=(A[2]??"").trim();return I(await Sp(k,m,e))}let K=c.match(/^\/watch\b(?:\s+([\s\S]*))?$/i);if(K){let k=(K[1]??"").trim(),x=Tp(k,m);return x.replies.length===1?I(x.replies[0]):{kind:"texts",bodies:x.replies}}if(c==="/apps"||c.startsWith("/apps "))return I(await qw(c,m,e,i,r));let X=Gw(c);if(X!==null)return I(await Kw(X,m,e,i,s.mediaSavedPath,u));if(c.startsWith("/bg")){let k=c.slice(3).trim();if(!k)return I(mc());let x=Nu(k);if("error"in x)return I(p(x.error==="empty"?"Usage: /bg <command> or /bg -n <name> <command>.":x.error));let L=oe(m).cwd,F=x.notify&&u?.sendToPeer?Ie=>{let de=_u(Ie);u.sendToPeer(m,de).catch(()=>{})}:void 0,{id:Q,meta:z}=t.spawnJob(e.shell,x.cmd,{cwd:L,name:x.name,notifyPeerKey:x.notify?m:null,onComplete:F}),we=z.name?`${Q} (${z.name})`:Q,Ue=x.notify?`
314
+ Change folder with ${e.commandPrefix}cd \u2026 Send /receive default to use the server config again.`))}return L.has(k)?(ti(m,"default"),I(p("Per-chat inbound folder cleared. Uploads now follow fileReceiveRootMode in config.json (/files)."))):I(k==="help"?mi():k==="status"?kc(e,m):mi())}if(c==="/reload"||c==="/restart"){if(!a?.reload)return I(p("Reload is only available while the omnish gateway (omnish run) is running."));let k=await a.reload();return I(p(k.ok?k.summary:`Reload failed: ${k.error}`))}if(c==="/updates"||c.startsWith("/updates ")){let k=c.slice(8).trim().toLowerCase();if(k==="cached"||k==="last"){let L=Mr();return I(L?pr(L):p("No update snapshot yet. Send /updates (live check) once, or enable updateCheckEnabled and wait for the scheduled check."))}let x=await Tr(Ze(),$());return I(pr(x))}let y=c.match(/^\/security(?:\s+(\S+))?\s*$/i);if(y){let k=(y[1]??"").toLowerCase(),x=$(),L=Ct(x);return I(k==="help"||k==="?"?Z(Wc()):k==="summary"||k==="brief"?p(ic(L,"Send /security for the full report.")):k==="tips"?Z(Fc()):k===""||k==="full"||k==="report"?_c(L):p("Unknown /security subcommand. Try /security, /security summary, /security tips, or /security help"))}let b=c.match(/^\/(gateway|gw|mode)\b(?:\s+(.*))?$/i);if(b){let k=(b[2]??"").trim();return I(await qw(k,a))}if(c==="/config"||c.startsWith("/config ")){let k=c.slice(7).trim();return I(await jc(k,a))}let v=np(c);if(v!==null)return I(await rp(e,v));let E=La(c);if(E!==null){let k=ou(e,E,l);return k===null?null:I(k)}let R=c.match(/^\/(send|file)\b(?:\s+([\s\S]+))?$/i);if(R){let k=(R[2]??"").trim();if(!k)return I(di());let x=ws(k);if(!x)return I(di());let L=oe(m).cwd,F=await Er(L,x.selectorPart);if(F.length===0)return I(p(`No files matched: ${x.selectorPart}`));let Q=await Pr(F);if(!Q.ok)return I(p(Q.error));let z=se()?ts(e):e.fileSendMaxBytes,we=[];for(let Ue of F){let Ie=qt(Ue,z);if("error"in Ie)return I(p(Ie.error));we.push({absPath:Ie.absPath,category:Ie.category,mimetype:Ie.mimetype,displayName:Ie.displayName,caption:x.caption})}return we.length===1?{kind:"file",spec:we[0]}:{kind:"files",specs:we}}if(c==="/allowlist"){let k=$();return I(mc([{label:"allowFrom (WhatsApp)",items:k.allowFrom},{label:"telegramAllowFrom",items:k.telegramAllowFrom}]))}let P=c.match(/^\/allow\b\s+(.+)$/i);if(P)try{let k=Qr(P[1].trim());return I(ui(k))}catch(k){return I(p(String(k)))}if(/^\/allow\b\s*$/i.test(c))return I(yc());let T=c.match(/^\/deny\b\s+(.+)$/i);if(T)try{let k=Vr(T[1].trim());return I(ui(k))}catch(k){return I(p(String(k)))}if(/^\/deny\b\s*$/i.test(c))return I(wc());let O=c.match(/^\/(whatsapp|wa|telegram|tg)\b(?:\s+(.*))?$/i);if(O){let k=O[1].toLowerCase(),x=(O[2]??"").trim(),L=k==="whatsapp"||k==="wa";if(k==="telegram"||k==="tg"){let Q=x.match(/^token\s+(\S+)\s*$/i);return Q?I(Yw(Q[1]??"")):x===""||/^help$/i.test(x)?I(Z(pc($()))):I(vc())}if(L)return x===""||/^help$/i.test(x)?I(Z(dc(e))):I(Sc())}let A=c.match(/^\/(cowork|cw)\b(?:\s+([\s\S]*))?$/i);if(A){let k=(A[2]??"").trim();return I(await Cp(k,m,e))}let K=c.match(/^\/watch\b(?:\s+([\s\S]*))?$/i);if(K){let k=(K[1]??"").trim(),x=Op(k,m);return x.replies.length===1?I(x.replies[0]):{kind:"texts",bodies:x.replies}}if(c==="/apps"||c.startsWith("/apps "))return I(await eb(c,m,e,i,r));let X=Qw(c);if(X!==null)return I(await Xw(X,m,e,i,s.mediaSavedPath,u));if(c.startsWith("/bg")){let k=c.slice(3).trim();if(!k)return I(bc());let x=Hu(k);if("error"in x)return I(p(x.error==="empty"?"Usage: /bg <command> or /bg -n <name> <command>.":x.error));let L=oe(m).cwd,F=x.notify&&u?.sendToPeer?Ie=>{let pe=Bu(Ie);u.sendToPeer(m,pe).catch(()=>{})}:void 0,{id:Q,meta:z}=t.spawnJob(e.shell,x.cmd,{cwd:L,name:x.name,notifyPeerKey:x.notify?m:null,onComplete:F}),we=z.name?`${Q} (${z.name})`:Q,Ue=x.notify?`
315
315
  Notify on completion: on`:"";return I(p(`Job ${we} started.
316
316
  [cwd: ${L}]
317
317
  /log ${z.name??Q}
318
- /tail ${z.name??Q}${Ue}`))}if(c==="/tunnels")return I(await Ta("list",e,dn()));let se=c.match(/^\/tunnel\b(?:\s+([\s\S]*))?$/i);if(se){let k=(se[1]??"").trim();return I(await Ta(k||"help",e,dn()))}if(c==="/jobs"){let k=t.list().slice(0,20);return k.length===0?I(p("(no jobs yet)")):I(p(k.map(x=>{let L=x.finishedAt&&x.startedAt?`${((Date.parse(x.finishedAt)-Date.parse(x.startedAt))/1e3).toFixed(1)}s`:"\u2026";return`${x.name?`${x.id} ${x.name}`:x.id} ${x.status} exit=${x.exitCode??"?"} ${L}
318
+ /tail ${z.name??Q}${Ue}`))}if(c==="/tunnels")return I(await Oa("list",e,gn()));let ie=c.match(/^\/tunnel\b(?:\s+([\s\S]*))?$/i);if(ie){let k=(ie[1]??"").trim();return I(await Oa(k||"help",e,gn()))}if(c==="/jobs"){let k=t.list().slice(0,20);return k.length===0?I(p("(no jobs yet)")):I(p(k.map(x=>{let L=x.finishedAt&&x.startedAt?`${((Date.parse(x.finishedAt)-Date.parse(x.startedAt))/1e3).toFixed(1)}s`:"\u2026";return`${x.name?`${x.id} ${x.name}`:x.id} ${x.status} exit=${x.exitCode??"?"} ${L}
319
319
  ${x.cmd.slice(0,120)}${x.cmd.length>120?"\u2026":""}`}).join(`
320
320
 
321
- `)))}let he=c.match(/^\/log\s+(\S+)(?:\s+(\d+))?\s*$/i);if(he){let k=he[1],x=t.resolveJobRef(k);if(!x.ok)return I(p(x.error));let L=x.id,F=he[2]?Number.parseInt(he[2],10):e.jobLogTailLines,Q=Number.isFinite(F)&&F>0?Math.min(F,500):e.jobLogTailLines;return I(p(t.tailLog(L,Q)))}let D=c.match(/^\/tail\s+(\S+)\s*$/i);if(D){let k=D[1],x=t.resolveJobRef(k);if(!x.ok)return I(p(x.error));let L=x.id,F=Dw(m,L),Q=n.get(F)??0,{text:z,nextOffset:we}=t.readSince(L,Q);return n.set(F,we),I(z?p(z.trimEnd()||"(no new output)"):p(`(no new output; offset ${we})`))}let ae=c.match(/^\/kill\s+(\S+)\s*$/i);if(ae){let k=ae[1],x=t.resolveJobRef(k);return x.ok?I(p(t.kill(x.id))):I(p(x.error))}let Y=c.match(/^\/(shortcut|shortcuts|alias|aliases)\b(?:\s+([\s\S]*))?$/i);if(Y){let k=Y[1].toLowerCase(),x=(Y[2]??"").trim();return k==="shortcuts"&&!x?x="list":((k==="alias"||k==="aliases")&&!x||k==="shortcut"&&!x)&&(x="help"),I(await zw(x,m,e))}if(!d){let k=c.trim().match(/^\/([a-zA-Z0-9][a-zA-Z0-9_-]{0,31})\s*$/);if(k){let x=k[1],L=ga(m,x);if(L!==void 0)return await Vn(e,t,n,r,o,{...s,text:L},i,a,l,!0,u)}}return I(wc(e))}let h=c.match(/^>(\S+)\s*(.*)$/s);if(h){let g=h[1],y=h[2]??"",b=await i.writeNamedLine(m,g,y);return b?I(p(b)):null}return await i.writeFocusedLine(m,c)?null:o.get(m)&&c?I(await Pp(e,m,c)):e.chatLlmFallbackEnabled&&e.chatLlmShellCommand.trim().length>0&&u?.onPlainTextLlmFallback?(u.onPlainTextLlmFallback(m,c),null):I(bc(e))}function Gw(e){return e==="/run"||e.startsWith("/run ")?e.slice(4).trim():e==="/r"||e.startsWith("/r ")?e.slice(2).trim():null}function Jw(e){let t=e.trim(),n=t.match(/^(\S+)\s+(?:--queue|-q)\s+([\s\S]+)$/i);if(n)return{recipe:n[1],task:n[2],queued:!0};let r=t.match(/^(\S+)\s+([\s\S]+)$/);return r?{recipe:r[1],task:r[2],queued:!1}:null}async function Kw(e,t,n,r,o,s){let i=e.trim();if(!i||/^help$/i.test(i))return Z(Rc());let a=/^online\b([\s\S]*)$/i.exec(i);if(a)return fp((a[1]??"").trim(),t,n);let l=/^(\S+)\s+publish\b([\s\S]*)$/i.exec(i);if(l){let b=hp(t,n,l[1],l[2]??"");if(!b.ok)return p(b.error);let v=await Qn(b.body);return p(v.ok?v.message:v.error)}let d=/^list\b([\s\S]*)$/i.exec(i);if(d){let b=(d[1]??"").trim(),{filter:v,bad:E}=au(b);if(E)return p(`Unknown /run list suffix: "${E}". Use: list | list --chat | list -p | list --global | list -g`);if(v==="merged")return Cc(du(t,n));let R=cu(t,n,v);return Ec(R,v)}let u=/^show\b([\s\S]*)$/i.exec(i);if(u){let b=(u[1]??"").trim(),{mode:v,remainder:E}=iu(b),R=/^(\S+)\s*$/i.exec(E);if(!R?.[1])return p("Usage: /run show <name> \u2014 or show --global|-g|--chat|-p <name> (-g shared, -p private)");let P=R[1];if(v==="resolved"){let se=je(t,n,P);return se?mi(se):fi(P)}let T=v==="global"?"global":"chat",O=Mt(T,t,n,P);if(O)return mi(O,T==="global"?"From gateway-shared recipes (--global).":"From this chat only (--chat).");let A=T==="chat"&&Mt("global",t,n,P)?`
322
- (Gateway-shared recipe exists: /run show --global ${P})`:"",K=T==="global"&&Mt("chat",t,n,P)?`
323
- (This chat stores an override: /run show --chat ${P})`:"",X=T==="global"?`Unknown recipe "${P}" in gateway-shared storage.`:`Unknown recipe "${P}" in this chat storage.`;return p(`${X}${A}${K}`)}let c=/^add\b([\s\S]*)$/i.exec(i);if(c){let{scope:b,remainder:v}=Ni((c[1]??"").trim()),E=v.match(/^(\S+)\s+([\s\S]+)$/);if(!E)return p(b==="global"?'Usage: /run add --global <name> <command\u2026> [--template "\u2026"] \u2014 cmd must reference "$OMNISH_TASK"':"Usage: /run add [--global|-g|--chat|-p] <name> <command\u2026> \u2014 see /run help");try{let R=_i(E[2],n.recipesMacroDefaultCommand);An(t,E[1],R,b);let P=gt(E[1]),T=P.ok?P.normalized:E[1].toLowerCase(),O=Mt(b,t,n,T)??je(t,n,T);return O?dr(T,O,b):p("Recipe save failed.")}catch(R){return p(String(R))}}let m=/^set\b([\s\S]*)$/i.exec(i);if(m){let{scope:b,remainder:v,explicit:E}=Li((m[1]??"").trim()),R=lu(v);if(R){if(E)return p("Cannot combine a leading scope flag (-g, --global, --chat, -p) with a trailing scope flag on the same line.");let O=gt(R.name);if(!O.ok)return p(O.error);let A=Fi(t,O.normalized,R.target,n);if(!A.ok)return p(A.error);if(A.kind==="noop")return p(A.message);let K=Mt(A.target,t,n,O.normalized)??je(t,n,O.normalized);return K?dr(O.normalized,K,A.target):p("Recipe scope update failed.")}let P=v.match(/^(\S+)\s*$/);if(P?.[1]&&E){let O=gt(P[1]);if(!O.ok)return p(O.error);let A=Fi(t,O.normalized,b,n);if(!A.ok)return p(A.error);if(A.kind==="noop")return p(A.message);let K=Mt(A.target,t,n,O.normalized)??je(t,n,O.normalized);return K?dr(O.normalized,K,A.target):p("Recipe scope update failed.")}let T=v.match(/^(\S+)\s+([\s\S]+)$/);if(!T)return p(b==="global"?'Usage: /run set --global <name> <command\u2026> [--template "\u2026"] \u2014 or scope-only: /run set --global <name> | /run set <name> -g':"Usage: /run set [--global|-g|--chat|-p] <name> <command\u2026> \u2014 or move scope without changing the body: /run set -g <name> | /run set <name> -p \u2014 see /run help");try{let O=_i(T[2],n.recipesMacroDefaultCommand);An(t,T[1],O,b);let A=gt(T[1]),K=A.ok?A.normalized:T[1].toLowerCase(),X=Mt(b,t,n,K)??je(t,n,K);return X?dr(K,X,b):p("Recipe save failed.")}catch(O){return p(String(O))}}let f=/^(?:remove|rm|del)\b([\s\S]*)$/i.exec(i);if(f){let{scope:b,remainder:v}=Ni((f[1]??"").trim()),E=v.match(/^(\S+)\s*$/);if(!E?.[1])return p(b==="global"?"Usage: /run remove --global <name> (aliases: rm, del)":"Usage: /run remove [--global|-g|--chat|-p] <name>");let R=E[1];return uu(t,R,b)?Mc(R,b):b==="chat"&&Mt("global",t,n,R)?p(`No recipe "${R}" in this chat. There is a gateway-shared recipe with that name \u2014 remove it with:
324
- /run remove --global ${R}`):Tc(R,b)}let h=/^queue\s+load\b([\s\S]*)$/i.exec(i);if(h){let b=(h[1]??"").trim(),v=zd(n),E=null,R=/^json(?:\s+([\s\S]+))?$/i.exec(b);if(R){let A=(R[1]??"").trim();if(!A)return p('Usage: /run queue load json [{"recipe":"\u2026","task":"\u2026"}, \u2026] \u2014 or { "tasks": [ \u2026 ] }');E=A}else if(b.length>0){let A=b;(A.startsWith('"')&&A.endsWith('"')||A.startsWith("'")&&A.endsWith("'"))&&(A=A.slice(1,-1));let K=oe(t).cwd,X=await Rr(K,A);if(X.length===0)return p(`No files matched: ${A}`);if(X.length>1)return p("Queue load: specify a single JSON file.");let se=await Cr(X);if(!se.ok)return p(se.error);let he=wa(X[0],v);if(!he.ok)return p(he.error);E=he.text}else if(o){let A=wa(o,v);if(!A.ok)return p(A.error);E=A.text}else return p("Usage: /run queue load <file.json> \u2014 or /run queue load json [\u2026] \u2014 or attach a file with caption /run queue load");let P=qd(E);if(!P.ok)return p(P.error);let T=Yd(t,n,P.jobs);if(!T.ok)return p(T.error);let O=[];for(let A of T.items)O.push(r.enqueueQueuedRun(t,A,n));return p(O.join(`
325
- `))}if(/^queue$/i.test(i))return p(r.runQueueStatus(t));if(/^queue\s+resume\s*$/i.test(i))return p(r.resumeRunQueue(t,n));let g=Jw(i);if(g){let{recipe:b,task:v,queued:E}=g,R=je(t,n,b);if(!R)return fi(b);if(R.steps&&R.steps.length>0){let X=oe(t).cwd,se=R.steps.length,he=R.steps;if(s?.sendToPeer){let D=s.sendToPeer;(async()=>{let ae=[],Y=!1;for(let x=0;x<he.length;x++){let L=he[x];if(Y){ae.push({index:x,label:L.label??`step ${x+1}`,cmd:L.cmd,exitCode:null,timedOut:!1,skipped:!0,output:""});continue}let F=await sn(n.shell,L.cmd,{timeoutMs:n.syncTimeoutMs,maxBytes:n.syncMaxBytes,cwd:X}),Q=[F.stdout,F.stderr].filter(Boolean).join(`
326
- `).trim(),z=F.code===0&&!F.timedOut;ae.push({index:x,label:L.label??`step ${x+1}`,cmd:L.cmd,exitCode:F.code,timedOut:F.timedOut,skipped:!1,output:Q}),!z&&!L.continueOnFail&&(Y=!0)}let k=pu(b,ae);await D(t,k)})().catch(()=>{})}return p(`Runbook "${b}" started (${se} steps). Results will be sent when complete.`)}let P=R.taskEnv??"OMNISH_TASK";if(!Pn(R.command,P))return p(`Recipe "${b}" command must reference "$${P}".`);let T=vo(v,n.recipesMaxTaskChars);if(!T.ok)return p(T.error);let O=R.promptTemplate?xo(R.promptTemplate,P,T.task):T.task,A={[P]:O};if(E)return p(r.enqueueQueuedRun(t,{command:R.command,extraEnv:A,recipeLabel:b},n));let K=$o(b);return p(r.start(t,K,R.command,n,A))}let y=i.match(/^(\S+)$/);if(y){let b=y[1],v=b.toLowerCase();return v==="add"||v==="set"?p('Usage: /run add <name> cmd [--template "\u2026"] \u2014 cmd must include "$OMNISH_TASK"'):v==="show"?p("Usage: /run show <name> \u2014 optional show --global|-g|--chat|-p <name>"):v==="remove"||v==="rm"||v==="del"?p("Usage: /run remove [--global|-g|--chat|-p] <name>"):je(t,n,v)?p(`Usage: /run ${b} <task text\u2026> \u2014 replaces <<<OMNISH_TASK>>> / $OMNISH_TASK in stored templates`):p(`Unknown recipe "${b}". /run list`)}return p("/run: could not parse. /run help")}async function zw(e,t,n){let r=e.trim();if(!r||/^help$/i.test(r))return Z(di());let o=/^online\b([\s\S]*)$/i.exec(r);if(o)return Yn((o[1]??"").trim(),t,n,mp);let s=/^(\S+)\s+publish\b([\s\S]*)$/i.exec(r);if(s){let c=gp(t,s[1],s[2]??"");if(!c.ok)return p(c.error);let m=await Qn(c.body);return p(m.ok?m.message:m.error)}let i=/^list\b([\s\S]*)$/i.exec(r);if(i){let c=(i[1]??"").trim(),{filter:m,bad:f}=Dd(c);return f?p(`Unknown /shortcut list suffix: "${f}". Use: list | list --chat | list -p | list --global | list -g`):Sc(Bd(t,m))}let a=/^show\b([\s\S]*)$/i.exec(r);if(a){let c=(a[1]??"").trim(),{mode:m,remainder:f}=Ud(c),h=/^(\S+)\s*$/i.exec(f);if(!h?.[1])return p("Usage: /shortcut show <name> \u2014 or show --global|-g|--chat|-p <name> (-g shared, -p private)");let g=h[1];if(m==="resolved"){let P=ws(t,g);if(!P)return xc(g);let T=P.scope==="global"?"Shared shortcut (all chats use it unless this chat overrides the name).":"This chat only.";return pi(g,P.body,T)}let y=m==="global"?"global":"chat",b=Et(y,t,g);if(b!==void 0)return pi(g,b,y==="global"?"From the shared shortcut list (--global / -g).":"From this chat only (--chat / -p).");let v=y==="chat"&&Et("global",t,g)!==void 0?`
327
- (Shared shortcut exists: /shortcut show --global ${g})`:"",E=y==="global"&&Et("chat",t,g)!==void 0?`
328
- (This chat overrides the name: /shortcut show --chat ${g})`:"",R=y==="global"?`Unknown shortcut "${g}" in shared shortcuts.`:`Unknown shortcut "${g}" in this chat.`;return p(`${R}${v}${E}`)}let l=/^add\b([\s\S]*)$/i.exec(r);if(l){let{scope:c,remainder:m}=ha((l[1]??"").trim()),f=m.match(/^(\S+)\s+([\s\S]+)$/);if(!f)return p(c==="global"?"Usage: /shortcut add --global <name> <command\u2026> (short: add -g <name> <command\u2026>) \u2014 -g shared, -p private":"Usage: /shortcut add [--global|-g|--chat|-p] <name> <command\u2026> \u2014 -g shared, -p private");try{Tr(t,f[1],f[2],c);let h=wt(f[1]),g=h.ok?h.normalized:f[1].trim().toLowerCase(),y=Et(c,t,g)??"";return ur(g,y,c)}catch(h){return p(String(h))}}let d=/^set\b([\s\S]*)$/i.exec(r);if(d){let{scope:c,remainder:m,explicit:f}=fa((d[1]??"").trim()),h=Hd(m);if(h){if(f)return p("Cannot combine a leading scope flag (-g, --global, --chat, -p) with a trailing flag on the same line.");let b=wt(h.name);if(!b.ok)return p(b.error);let v=ya(t,b.normalized,h.target);if(!v.ok)return p(v.error);if(v.kind==="noop")return p(v.message);let E=Et(v.target,t,b.normalized)??"";return ur(b.normalized,E,v.target)}let g=m.match(/^(\S+)\s*$/);if(g?.[1]&&f){let b=wt(g[1]);if(!b.ok)return p(b.error);let v=ya(t,b.normalized,c);if(!v.ok)return p(v.error);if(v.kind==="noop")return p(v.message);let E=Et(v.target,t,b.normalized)??"";return ur(b.normalized,E,v.target)}let y=m.match(/^(\S+)\s+([\s\S]+)$/);if(!y)return p(c==="global"?"Usage: /shortcut set --global <name> <command\u2026> \u2014 or scope-only: /shortcut set -g <name> | /shortcut set <name> -g (-g shared)":"Usage: /shortcut set [--global|-g|--chat|-p] <name> <command\u2026> \u2014 or move scope: /shortcut set -g <name> | /shortcut set <name> -p (-g shared, -p private)");try{Tr(t,y[1],y[2],c);let b=wt(y[1]),v=b.ok?b.normalized:y[1].trim().toLowerCase(),E=Et(c,t,v)??"";return ur(v,E,c)}catch(b){return p(String(b))}}let u=/^(?:remove|rm|del)\b([\s\S]*)$/i.exec(r);if(u){let{scope:c,remainder:m}=ha((u[1]??"").trim()),f=m.match(/^(\S+)\s*$/);if(!f?.[1])return p(c==="global"?"Usage: /shortcut remove --global <name> (aliases: rm, del)":"Usage: /shortcut remove [--global|-g|--chat|-p] <name>");let h=f[1];return jd(t,h,c)?vc(h,c):c==="chat"&&Et("global",t,h)!==void 0?p(`No shortcut "${h}" in this chat. There is a shared shortcut with that name \u2014 remove it with:
329
- /shortcut remove --global ${h}`):$c(h,c)}return Z(di())}async function qw(e,t,n,r,o){let s=e.slice(5).trim(),i=s.toLowerCase();if(!i||i==="help")return Z(hi());let a=s.match(/^(\S+)\s*(.*)$/s);if(!a)return Z(hi());let l=a[1].toLowerCase(),d=(a[2]??"").trim();if(l==="online")return Yn(d,t,n,dp);let u=d.match(/^publish\b([\s\S]*)$/i);if(u){let c=l,m=r.getSessionCommand(t,c);if(!m)return p(`No running session "${c}" with a command. /apps list`);let f=wp(c,m,u[1]??"");if(!f.ok)return p(f.error);let h=await Qn(f.body);return p(h.ok?h.message:h.error)}switch(l){case"start":{let c=d.match(/^(\S+)\s+([\s\S]+)$/);return c?p(r.start(t,c[1],c[2],n)):p("Usage: /apps start <name> <command\u2026>")}case"attach":{let c=d.split(/\s+/)[0];return c?p(r.attach(t,c)):p("Usage: /apps attach <name>")}case"detach":return p(r.detach(t));case"list":return p(r.list(t));case"info":case"get":{let c=d.split(/\s+/)[0];return p(r.info(t,c||void 0))}case"send":{let c=d.match(/^(\S+)\s+([\s\S]+)$/);return c?p(await r.sendText(t,c[1],c[2])):p("Usage: /apps send <name> <text\u2026>")}case"key":{let c=d.match(/^(\S+)\s+([\s\S]+)$/);return c?p(r.sendKey(t,c[1],c[2].trim())):p("Usage: /apps key <name> <KEY[,KEY\u2026]>")}case"tail":{let c=d.match(/^(\S+)(?:\s+(\d+))?\s*$/);if(!c)return p("Usage: /apps tail <name> [lines]");let m=c[2]?Number.parseInt(c[2],10):n.appsLogTailLines;return p(r.tail(t,c[1],m))}case"since":{let c=d.split(/\s+/)[0];if(!c)return p("Usage: /apps since <name>");let m=Hw(t,c),f=o.get(m)??0,{text:h,nextOffset:g}=r.readSince(t,c,f);return o.set(m,g),p(h.trimEnd()||"(no new log bytes)")}case"mute":{let c=d.split(/\s+/)[0];return c?p(r.mute(t,c)):p("Usage: /apps mute <name>")}case"unmute":{let c=d.split(/\s+/)[0];return c?p(r.unmute(t,c)):p("Usage: /apps unmute <name>")}case"raw":{let c=d.match(/^(\S+)\s+(on|off)\s*$/i);return c?p(r.setRaw(t,c[1],c[2].toLowerCase()==="on")):p("Usage: /apps raw <name> on|off")}case"resize":{let c=d.trim().split(/\s+/).filter(Boolean);if(c.length<3)return p("Usage: /apps resize <name> <cols> <rows>");let m=c[0],f=Number(c[1]),h=Number(c[2]);return!Number.isFinite(f)||!Number.isFinite(h)?p("cols and rows must be numbers."):p(r.resize(t,m,f,h))}case"stop":{let c=d.split(/\s+/)[0];return c?p(r.stop(t,c)):p("Usage: /apps stop <name>")}case"kill":{let c=d.split(/\s+/)[0];return c?p(r.kill(t,c)):p("Usage: /apps kill <name>")}case"rm":{let c=d.split(/\s+/)[0];return c?p(r.rm(t,c)):p("Usage: /apps rm <name>")}default:return p(`Unknown /apps subcommand "${l}". /apps help`)}}function Ap(e,t,n){return!e.clusterEnabled||Ea(t.trim())!==null?!0:n?Vc(n,e):!1}be();qe();function Ip(){return p("Not allowlisted on this omnish device.")}async function Ar(e,t,n,r,o,s,i,a,l,d,u,c){if((c?.surface??(s.peerKey.startsWith("tg:")?"telegram":"whatsapp"))==="telegram"){let f=Gr(e.telegramAllowFrom),h=s.peerKey.startsWith("tg:")?s.peerKey.slice(3):"";if(!h||!f.has(h)){M.warn({denied:s.peerKey,uid:h},"telegram denied"),await u({kind:"text",body:Ip()});return}}else{let f=jr(e.allowFrom),h=l.startsWith("wa:")&&/^wa:\+\d+$/.test(l)?l.slice(3):s.peerKey.replace(/^wa:/,""),g=ee(h)||"";if(!g||!f.has(g)){M.warn({denied:s.peerKey,phone:g,senderKey:l},"denied"),await u({kind:"text",body:Ip()});return}}try{if(!!!(s.mediaSavedPath||s.mediaError)&&!Ap(e,s.text,l))return;if(s.mediaError&&await u({kind:"text",body:p(s.mediaError)}),s.mediaSavedPath&&await u({kind:"text",body:p(`Saved: ${s.mediaSavedPath}`)}),s.text.trim()){let h=await Vn(e,t,n,r,o,s,i,a,l,!1,d);if(h!==null)if(h.kind==="texts")for(let g of h.bodies)await u({kind:"text",body:g});else await u(h)}}catch(f){M.error({err:String(f)},"inbound handler error"),await u({kind:"text",body:p(`Error: ${String(f)}`)}).catch(()=>{})}}function Qw(){if(process.env.OMNISH_BACKGROUND_GATEWAY==="1")try{Pa.readFileSync(le,"utf8").trim()===String(process.pid)&&Pa.unlinkSync(le)}catch{}}async function Op(e){let t=new Tt,n=new Map,r=new Map,o=new Map,s=null,i=new Map,a=async(D,ae)=>{let Y=i.get(D);s?.sendReply(D,ae,Y)},l=async(D,ae)=>{let Y=D.startsWith("tg:")?"telegram":"whatsapp",k=i.get(D),x=Qi(D,{kind:"file",spec:ae},k,Y);s?.sendRoutedReply(D,x)},d=async(D,ae)=>{let Y=i.get(D),k=D.startsWith("tg:")?"telegram":"whatsapp";s?.sendReply(D,ke(p(ae),k).text,Y)},u={onPlainTextLlmFallback(D,ae){On($(),D,ae,Y=>a(D,Y))},sendToPeer:a},c=new Jt(()=>$(),a),m={async reload(){return{ok:!0,summary:"Attached mode: config reloaded from disk. Messengers run on the omnish platform; this device executes commands locally."}}};if(s=new ss({env:e,onReplyError:async(D,ae,Y)=>{let k=D.startsWith("tg:")?"telegram":"whatsapp";s?.sendReply(D,ke(p(`Error sending: ${ae}`),k).text,Y)},onMessage:async D=>{let ae=ta(),Y=D.peerKey;i.set(Y,D.messageId);let k=D.surface==="telegram"?"telegram":"whatsapp",x=D.senderE164&&k==="whatsapp"?`wa:${D.senderE164}`:Y;yn()&&M.info({peerKey:Y,senderKey:x,surface:k},"platform inbound message");let L=D.mediaSavedPath,F=D.mediaError;if(D.inboundMedia){let z=md(ae,Y,D.inboundMedia);L=z.mediaSavedPath??L,F=z.mediaError??F}let Q={peerKey:Y,text:D.text,...L?{mediaSavedPath:L}:{},...F?{mediaError:F}:{}};await Ar(ae,t,n,r,o,Q,c,m,x,u,async z=>{try{if(z.kind==="texts"){for(let Ue of z.bodies){let Ie=ke(Ue,k);s?.sendReply(Y,Ie.text,D.messageId)}return}if(z.kind==="text"){let Ue=ke(z.body,k);s?.sendReply(Y,Ue.text,D.messageId);return}let we=Qi(Y,z,D.messageId,k);s?.sendRoutedReply(Y,we)}catch(we){s?.sendReply(Y,`Error sending file: ${String(we)}`,D.messageId)}},{surface:k})}}),process.env.OMNISH_BACKGROUND_GATEWAY==="1")try{Pa.writeFileSync(le,`${process.pid}
330
- `,{mode:384})}catch(D){M.warn({err:String(D)},"could not write gateway pidfile")}Xo({getCfg:()=>$(),getWaOutbound:()=>null,getTgSendMedia:()=>null,getTgSendText:()=>null,sendPlatformMedia:l,sendPlatformText:d});let f=null,h=$();if(h.webhookEnabled){let D=h.webhookToken||Yw.randomBytes(32).toString("hex");h.webhookToken||W({webhookToken:D}),f=Zo({port:h.webhookPort,host:h.webhookHost,token:D},{sendToPeer:a,getDefaultPeerKey:()=>null}).stop}let g=fs({getRunningVersion:Ze,getConfig:$,log:M}),y=Uo({getConfig:$,sendToPeer:a,sendMediaToPeer:l}),b=no({getConfig:$,sendToPeer:a}),{deviceId:v,account:E}=await s.connect(),R=await ns(e,E??null),P=5*60*1e3,T=setInterval(()=>{ns(e,null).catch(()=>{})},P);T.unref?.();let O=e.deviceId?.trim();if(O){O!==v&&M.warn({configuredDeviceId:O,registeredDeviceId:v},"platform_device_id does not match registered device id");try{await na(e,O)}catch(D){M.warn({err:String(D)},"could not set platform default device")}}let A=R?.gatewayMode??E?.gatewayMode,K=R?.connectors.whatsapp??E?.connectors?.whatsapp,X=R?.connectors.telegram??E?.connectors?.telegram,se=[K?`whatsapp:${K.linked?"linked":K.status}`:null,X?`telegram:${X.linked?"linked":X.status}`:null].filter(Boolean).join(", ");console.error(`omnish attached to platform (device ${v})`+(A?` \u2014 gatewayMode=${A}`:"")+(se?` \u2014 ${se}`:""));let he=()=>{clearInterval(T),y(),b(),g?.(),f?.(),Qw(),Hn(),s?.stop(),c.dispose(),t.killAllRunning(),dn().stopAll().catch(()=>{}),console.error(`
331
- ${C(process.stderr,"shutting down\u2026")}`),process.exit(0)};process.on("SIGINT",he),process.on("SIGTERM",he),await new Promise(()=>{})}function Aa(e,t){return async n=>{if(t.surface==="whatsapp"&&t.waJid){if(n.kind==="file")await e.sendWaMedia?.(t.waJid,n.spec);else if(n.kind==="files")for(let r of n.specs)await e.sendWaMedia?.(t.waJid,r);else if(n.kind==="texts")for(let r of n.bodies)await e.sendWaText?.(t.waJid,ke(r,"whatsapp").text);else await e.sendWaText?.(t.waJid,ke(n.body,"whatsapp").text);return}if(t.surface==="telegram"&&e.sendTg)if(n.kind==="texts")for(let r of n.bodies)await e.sendTg({kind:"text",body:r});else await e.sendTg(n)}}pe();G();function Ms(){if(fe()){let i=$(),a=Rt(i);return lr(a)?{ok:!1,message:`Fix security errors before starting the gateway.
321
+ `)))}let he=c.match(/^\/log\s+(\S+)(?:\s+(\d+))?\s*$/i);if(he){let k=he[1],x=t.resolveJobRef(k);if(!x.ok)return I(p(x.error));let L=x.id,F=he[2]?Number.parseInt(he[2],10):e.jobLogTailLines,Q=Number.isFinite(F)&&F>0?Math.min(F,500):e.jobLogTailLines;return I(p(t.tailLog(L,Q)))}let D=c.match(/^\/tail\s+(\S+)\s*$/i);if(D){let k=D[1],x=t.resolveJobRef(k);if(!x.ok)return I(p(x.error));let L=x.id,F=Kw(m,L),Q=n.get(F)??0,{text:z,nextOffset:we}=t.readSince(L,Q);return n.set(F,we),I(z?p(z.trimEnd()||"(no new output)"):p(`(no new output; offset ${we})`))}let le=c.match(/^\/kill\s+(\S+)\s*$/i);if(le){let k=le[1],x=t.resolveJobRef(k);return x.ok?I(p(t.kill(x.id))):I(p(x.error))}let Y=c.match(/^\/(shortcut|shortcuts|alias|aliases)\b(?:\s+([\s\S]*))?$/i);if(Y){let k=Y[1].toLowerCase(),x=(Y[2]??"").trim();return k==="shortcuts"&&!x?x="list":((k==="alias"||k==="aliases")&&!x||k==="shortcut"&&!x)&&(x="help"),I(await Zw(x,m,e))}if(!d){let k=c.trim().match(/^\/([a-zA-Z0-9][a-zA-Z0-9_-]{0,31})\s*$/);if(k){let x=k[1],L=Sa(m,x);if(L!==void 0)return await tr(e,t,n,r,o,{...s,text:L},i,a,l,!0,u)}}return I($c(e))}let h=c.match(/^>(\S+)\s*(.*)$/s);if(h){let g=h[1],y=h[2]??"",b=await i.writeNamedLine(m,g,y);return b?I(p(b)):null}return await i.writeFocusedLine(m,c)?null:o.get(m)&&c?I(await Np(e,m,c)):e.chatLlmFallbackEnabled&&e.chatLlmShellCommand.trim().length>0&&u?.onPlainTextLlmFallback?(u.onPlainTextLlmFallback(m,c),null):I(Rc(e))}function Qw(e){return e==="/run"||e.startsWith("/run ")?e.slice(4).trim():e==="/r"||e.startsWith("/r ")?e.slice(2).trim():null}function Vw(e){let t=e.trim(),n=t.match(/^(\S+)\s+(?:--queue|-q)\s+([\s\S]+)$/i);if(n)return{recipe:n[1],task:n[2],queued:!0};let r=t.match(/^(\S+)\s+([\s\S]+)$/);return r?{recipe:r[1],task:r[2],queued:!1}:null}async function Xw(e,t,n,r,o,s){let i=e.trim();if(!i||/^help$/i.test(i))return Z(Ac());let a=/^online\b([\s\S]*)$/i.exec(i);if(a)return bp((a[1]??"").trim(),t,n);let l=/^(\S+)\s+publish\b([\s\S]*)$/i.exec(i);if(l){let b=kp(t,n,l[1],l[2]??"");if(!b.ok)return p(b.error);let v=await er(b.body);return p(v.ok?v.message:v.error)}let d=/^list\b([\s\S]*)$/i.exec(i);if(d){let b=(d[1]??"").trim(),{filter:v,bad:E}=mu(b);if(E)return p(`Unknown /run list suffix: "${E}". Use: list | list --chat | list -p | list --global | list -g`);if(v==="merged")return Ic(yu(t,n));let R=hu(t,n,v);return Nc(R,v)}let u=/^show\b([\s\S]*)$/i.exec(i);if(u){let b=(u[1]??"").trim(),{mode:v,remainder:E}=pu(b),R=/^(\S+)\s*$/i.exec(E);if(!R?.[1])return p("Usage: /run show <name> \u2014 or show --global|-g|--chat|-p <name> (-g shared, -p private)");let P=R[1];if(v==="resolved"){let ie=je(t,n,P);return ie?gi(ie):yi(P)}let T=v==="global"?"global":"chat",O=Tt(T,t,n,P);if(O)return gi(O,T==="global"?"From gateway-shared recipes (--global).":"From this chat only (--chat).");let A=T==="chat"&&Tt("global",t,n,P)?`
322
+ (Gateway-shared recipe exists: /run show --global ${P})`:"",K=T==="global"&&Tt("chat",t,n,P)?`
323
+ (This chat stores an override: /run show --chat ${P})`:"",X=T==="global"?`Unknown recipe "${P}" in gateway-shared storage.`:`Unknown recipe "${P}" in this chat storage.`;return p(`${X}${A}${K}`)}let c=/^add\b([\s\S]*)$/i.exec(i);if(c){let{scope:b,remainder:v}=Wi((c[1]??"").trim()),E=v.match(/^(\S+)\s+([\s\S]+)$/);if(!E)return p(b==="global"?'Usage: /run add --global <name> <command\u2026> [--template "\u2026"] \u2014 cmd must reference "$OMNISH_TASK"':"Usage: /run add [--global|-g|--chat|-p] <name> <command\u2026> \u2014 see /run help");try{let R=Ui(E[2],n.recipesMacroDefaultCommand);_n(t,E[1],R,b);let P=yt(E[1]),T=P.ok?P.normalized:E[1].toLowerCase(),O=Tt(b,t,n,T)??je(t,n,T);return O?fr(T,O,b):p("Recipe save failed.")}catch(R){return p(String(R))}}let m=/^set\b([\s\S]*)$/i.exec(i);if(m){let{scope:b,remainder:v,explicit:E}=Fi((m[1]??"").trim()),R=fu(v);if(R){if(E)return p("Cannot combine a leading scope flag (-g, --global, --chat, -p) with a trailing scope flag on the same line.");let O=yt(R.name);if(!O.ok)return p(O.error);let A=Di(t,O.normalized,R.target,n);if(!A.ok)return p(A.error);if(A.kind==="noop")return p(A.message);let K=Tt(A.target,t,n,O.normalized)??je(t,n,O.normalized);return K?fr(O.normalized,K,A.target):p("Recipe scope update failed.")}let P=v.match(/^(\S+)\s*$/);if(P?.[1]&&E){let O=yt(P[1]);if(!O.ok)return p(O.error);let A=Di(t,O.normalized,b,n);if(!A.ok)return p(A.error);if(A.kind==="noop")return p(A.message);let K=Tt(A.target,t,n,O.normalized)??je(t,n,O.normalized);return K?fr(O.normalized,K,A.target):p("Recipe scope update failed.")}let T=v.match(/^(\S+)\s+([\s\S]+)$/);if(!T)return p(b==="global"?'Usage: /run set --global <name> <command\u2026> [--template "\u2026"] \u2014 or scope-only: /run set --global <name> | /run set <name> -g':"Usage: /run set [--global|-g|--chat|-p] <name> <command\u2026> \u2014 or move scope without changing the body: /run set -g <name> | /run set <name> -p \u2014 see /run help");try{let O=Ui(T[2],n.recipesMacroDefaultCommand);_n(t,T[1],O,b);let A=yt(T[1]),K=A.ok?A.normalized:T[1].toLowerCase(),X=Tt(b,t,n,K)??je(t,n,K);return X?fr(K,X,b):p("Recipe save failed.")}catch(O){return p(String(O))}}let f=/^(?:remove|rm|del)\b([\s\S]*)$/i.exec(i);if(f){let{scope:b,remainder:v}=Wi((f[1]??"").trim()),E=v.match(/^(\S+)\s*$/);if(!E?.[1])return p(b==="global"?"Usage: /run remove --global <name> (aliases: rm, del)":"Usage: /run remove [--global|-g|--chat|-p] <name>");let R=E[1];return gu(t,R,b)?Oc(R,b):b==="chat"&&Tt("global",t,n,R)?p(`No recipe "${R}" in this chat. There is a gateway-shared recipe with that name \u2014 remove it with:
324
+ /run remove --global ${R}`):Lc(R,b)}let h=/^queue\s+load\b([\s\S]*)$/i.exec(i);if(h){let b=(h[1]??"").trim(),v=Xd(n),E=null,R=/^json(?:\s+([\s\S]+))?$/i.exec(b);if(R){let A=(R[1]??"").trim();if(!A)return p('Usage: /run queue load json [{"recipe":"\u2026","task":"\u2026"}, \u2026] \u2014 or { "tasks": [ \u2026 ] }');E=A}else if(b.length>0){let A=b;(A.startsWith('"')&&A.endsWith('"')||A.startsWith("'")&&A.endsWith("'"))&&(A=A.slice(1,-1));let K=oe(t).cwd,X=await Er(K,A);if(X.length===0)return p(`No files matched: ${A}`);if(X.length>1)return p("Queue load: specify a single JSON file.");let ie=await Pr(X);if(!ie.ok)return p(ie.error);let he=xa(X[0],v);if(!he.ok)return p(he.error);E=he.text}else if(o){let A=xa(o,v);if(!A.ok)return p(A.error);E=A.text}else return p("Usage: /run queue load <file.json> \u2014 or /run queue load json [\u2026] \u2014 or attach a file with caption /run queue load");let P=Zd(E);if(!P.ok)return p(P.error);let T=ep(t,n,P.jobs);if(!T.ok)return p(T.error);let O=[];for(let A of T.items)O.push(r.enqueueQueuedRun(t,A,n));return p(O.join(`
325
+ `))}if(/^queue$/i.test(i))return p(r.runQueueStatus(t));if(/^queue\s+resume\s*$/i.test(i))return p(r.resumeRunQueue(t,n));let g=Vw(i);if(g){let{recipe:b,task:v,queued:E}=g,R=je(t,n,b);if(!R)return yi(b);if(R.steps&&R.steps.length>0){let X=oe(t).cwd,ie=R.steps.length,he=R.steps;if(s?.sendToPeer){let D=s.sendToPeer;(async()=>{let le=[],Y=!1;for(let x=0;x<he.length;x++){let L=he[x];if(Y){le.push({index:x,label:L.label??`step ${x+1}`,cmd:L.cmd,exitCode:null,timedOut:!1,skipped:!0,output:""});continue}let F=await un(n.shell,L.cmd,{timeoutMs:n.syncTimeoutMs,maxBytes:n.syncMaxBytes,cwd:X}),Q=[F.stdout,F.stderr].filter(Boolean).join(`
326
+ `).trim(),z=F.code===0&&!F.timedOut;le.push({index:x,label:L.label??`step ${x+1}`,cmd:L.cmd,exitCode:F.code,timedOut:F.timedOut,skipped:!1,output:Q}),!z&&!L.continueOnFail&&(Y=!0)}let k=wu(b,le);await D(t,k)})().catch(()=>{})}return p(`Runbook "${b}" started (${ie} steps). Results will be sent when complete.`)}let P=R.taskEnv??"OMNISH_TASK";if(!Nn(R.command,P))return p(`Recipe "${b}" command must reference "$${P}".`);let T=Co(v,n.recipesMaxTaskChars);if(!T.ok)return p(T.error);let O=R.promptTemplate?Mo(R.promptTemplate,P,T.task):T.task,A={[P]:O};if(E)return p(r.enqueueQueuedRun(t,{command:R.command,extraEnv:A,recipeLabel:b},n));let K=To(b);return p(r.start(t,K,R.command,n,A))}let y=i.match(/^(\S+)$/);if(y){let b=y[1],v=b.toLowerCase();return v==="add"||v==="set"?p('Usage: /run add <name> cmd [--template "\u2026"] \u2014 cmd must include "$OMNISH_TASK"'):v==="show"?p("Usage: /run show <name> \u2014 optional show --global|-g|--chat|-p <name>"):v==="remove"||v==="rm"||v==="del"?p("Usage: /run remove [--global|-g|--chat|-p] <name>"):je(t,n,v)?p(`Usage: /run ${b} <task text\u2026> \u2014 replaces <<<OMNISH_TASK>>> / $OMNISH_TASK in stored templates`):p(`Unknown recipe "${b}". /run list`)}return p("/run: could not parse. /run help")}async function Zw(e,t,n){let r=e.trim();if(!r||/^help$/i.test(r))return Z(fi());let o=/^online\b([\s\S]*)$/i.exec(r);if(o)return Zn((o[1]??"").trim(),t,n,wp);let s=/^(\S+)\s+publish\b([\s\S]*)$/i.exec(r);if(s){let c=Sp(t,s[1],s[2]??"");if(!c.ok)return p(c.error);let m=await er(c.body);return p(m.ok?m.message:m.error)}let i=/^list\b([\s\S]*)$/i.exec(r);if(i){let c=(i[1]??"").trim(),{filter:m,bad:f}=Jd(c);return f?p(`Unknown /shortcut list suffix: "${f}". Use: list | list --chat | list -p | list --global | list -g`):Mc(zd(t,m))}let a=/^show\b([\s\S]*)$/i.exec(r);if(a){let c=(a[1]??"").trim(),{mode:m,remainder:f}=Gd(c),h=/^(\S+)\s*$/i.exec(f);if(!h?.[1])return p("Usage: /shortcut show <name> \u2014 or show --global|-g|--chat|-p <name> (-g shared, -p private)");let g=h[1];if(m==="resolved"){let P=Ss(t,g);if(!P)return Ec(g);let T=P.scope==="global"?"Shared shortcut (all chats use it unless this chat overrides the name).":"This chat only.";return hi(g,P.body,T)}let y=m==="global"?"global":"chat",b=Pt(y,t,g);if(b!==void 0)return hi(g,b,y==="global"?"From the shared shortcut list (--global / -g).":"From this chat only (--chat / -p).");let v=y==="chat"&&Pt("global",t,g)!==void 0?`
327
+ (Shared shortcut exists: /shortcut show --global ${g})`:"",E=y==="global"&&Pt("chat",t,g)!==void 0?`
328
+ (This chat overrides the name: /shortcut show --chat ${g})`:"",R=y==="global"?`Unknown shortcut "${g}" in shared shortcuts.`:`Unknown shortcut "${g}" in this chat.`;return p(`${R}${v}${E}`)}let l=/^add\b([\s\S]*)$/i.exec(r);if(l){let{scope:c,remainder:m}=ka((l[1]??"").trim()),f=m.match(/^(\S+)\s+([\s\S]+)$/);if(!f)return p(c==="global"?"Usage: /shortcut add --global <name> <command\u2026> (short: add -g <name> <command\u2026>) \u2014 -g shared, -p private":"Usage: /shortcut add [--global|-g|--chat|-p] <name> <command\u2026> \u2014 -g shared, -p private");try{Ir(t,f[1],f[2],c);let h=bt(f[1]),g=h.ok?h.normalized:f[1].trim().toLowerCase(),y=Pt(c,t,g)??"";return mr(g,y,c)}catch(h){return p(String(h))}}let d=/^set\b([\s\S]*)$/i.exec(r);if(d){let{scope:c,remainder:m,explicit:f}=ba((d[1]??"").trim()),h=Kd(m);if(h){if(f)return p("Cannot combine a leading scope flag (-g, --global, --chat, -p) with a trailing flag on the same line.");let b=bt(h.name);if(!b.ok)return p(b.error);let v=va(t,b.normalized,h.target);if(!v.ok)return p(v.error);if(v.kind==="noop")return p(v.message);let E=Pt(v.target,t,b.normalized)??"";return mr(b.normalized,E,v.target)}let g=m.match(/^(\S+)\s*$/);if(g?.[1]&&f){let b=bt(g[1]);if(!b.ok)return p(b.error);let v=va(t,b.normalized,c);if(!v.ok)return p(v.error);if(v.kind==="noop")return p(v.message);let E=Pt(v.target,t,b.normalized)??"";return mr(b.normalized,E,v.target)}let y=m.match(/^(\S+)\s+([\s\S]+)$/);if(!y)return p(c==="global"?"Usage: /shortcut set --global <name> <command\u2026> \u2014 or scope-only: /shortcut set -g <name> | /shortcut set <name> -g (-g shared)":"Usage: /shortcut set [--global|-g|--chat|-p] <name> <command\u2026> \u2014 or move scope: /shortcut set -g <name> | /shortcut set <name> -p (-g shared, -p private)");try{Ir(t,y[1],y[2],c);let b=bt(y[1]),v=b.ok?b.normalized:y[1].trim().toLowerCase(),E=Pt(c,t,v)??"";return mr(v,E,c)}catch(b){return p(String(b))}}let u=/^(?:remove|rm|del)\b([\s\S]*)$/i.exec(r);if(u){let{scope:c,remainder:m}=ka((u[1]??"").trim()),f=m.match(/^(\S+)\s*$/);if(!f?.[1])return p(c==="global"?"Usage: /shortcut remove --global <name> (aliases: rm, del)":"Usage: /shortcut remove [--global|-g|--chat|-p] <name>");let h=f[1];return qd(t,h,c)?Tc(h,c):c==="chat"&&Pt("global",t,h)!==void 0?p(`No shortcut "${h}" in this chat. There is a shared shortcut with that name \u2014 remove it with:
329
+ /shortcut remove --global ${h}`):Pc(h,c)}return Z(fi())}async function eb(e,t,n,r,o){let s=e.slice(5).trim(),i=s.toLowerCase();if(!i||i==="help")return Z(wi());let a=s.match(/^(\S+)\s*(.*)$/s);if(!a)return Z(wi());let l=a[1].toLowerCase(),d=(a[2]??"").trim();if(l==="online")return Zn(d,t,n,gp);let u=d.match(/^publish\b([\s\S]*)$/i);if(u){let c=l,m=r.getSessionCommand(t,c);if(!m)return p(`No running session "${c}" with a command. /apps list`);let f=xp(c,m,u[1]??"");if(!f.ok)return p(f.error);let h=await er(f.body);return p(h.ok?h.message:h.error)}switch(l){case"start":{let c=d.match(/^(\S+)\s+([\s\S]+)$/);return c?p(r.start(t,c[1],c[2],n)):p("Usage: /apps start <name> <command\u2026>")}case"attach":{let c=d.split(/\s+/)[0];return c?p(r.attach(t,c)):p("Usage: /apps attach <name>")}case"detach":return p(r.detach(t));case"list":return p(r.list(t));case"info":case"get":{let c=d.split(/\s+/)[0];return p(r.info(t,c||void 0))}case"send":{let c=d.match(/^(\S+)\s+([\s\S]+)$/);return c?p(await r.sendText(t,c[1],c[2])):p("Usage: /apps send <name> <text\u2026>")}case"key":{let c=d.match(/^(\S+)\s+([\s\S]+)$/);return c?p(r.sendKey(t,c[1],c[2].trim())):p("Usage: /apps key <name> <KEY[,KEY\u2026]>")}case"tail":{let c=d.match(/^(\S+)(?:\s+(\d+))?\s*$/);if(!c)return p("Usage: /apps tail <name> [lines]");let m=c[2]?Number.parseInt(c[2],10):n.appsLogTailLines;return p(r.tail(t,c[1],m))}case"since":{let c=d.split(/\s+/)[0];if(!c)return p("Usage: /apps since <name>");let m=zw(t,c),f=o.get(m)??0,{text:h,nextOffset:g}=r.readSince(t,c,f);return o.set(m,g),p(h.trimEnd()||"(no new log bytes)")}case"mute":{let c=d.split(/\s+/)[0];return c?p(r.mute(t,c)):p("Usage: /apps mute <name>")}case"unmute":{let c=d.split(/\s+/)[0];return c?p(r.unmute(t,c)):p("Usage: /apps unmute <name>")}case"raw":{let c=d.match(/^(\S+)\s+(on|off)\s*$/i);return c?p(r.setRaw(t,c[1],c[2].toLowerCase()==="on")):p("Usage: /apps raw <name> on|off")}case"resize":{let c=d.trim().split(/\s+/).filter(Boolean);if(c.length<3)return p("Usage: /apps resize <name> <cols> <rows>");let m=c[0],f=Number(c[1]),h=Number(c[2]);return!Number.isFinite(f)||!Number.isFinite(h)?p("cols and rows must be numbers."):p(r.resize(t,m,f,h))}case"stop":{let c=d.split(/\s+/)[0];return c?p(r.stop(t,c)):p("Usage: /apps stop <name>")}case"kill":{let c=d.split(/\s+/)[0];return c?p(r.kill(t,c)):p("Usage: /apps kill <name>")}case"rm":{let c=d.split(/\s+/)[0];return c?p(r.rm(t,c)):p("Usage: /apps rm <name>")}default:return p(`Unknown /apps subcommand "${l}". /apps help`)}}function _p(e,t,n){return!e.clusterEnabled||La(t.trim())!==null?!0:n?ru(n,e):!1}be();qe();function Fp(){return p("Not allowlisted on this omnish device.")}async function Nr(e,t,n,r,o,s,i,a,l,d,u,c){if((c?.surface??(s.peerKey.startsWith("tg:")?"telegram":"whatsapp"))==="telegram"){let f=qr(e.telegramAllowFrom),h=s.peerKey.startsWith("tg:")?s.peerKey.slice(3):"";if(!h||!f.has(h)){M.warn({denied:s.peerKey,uid:h},"telegram denied"),await u({kind:"text",body:Fp()});return}}else{let f=zr(e.allowFrom),h=l.startsWith("wa:")&&/^wa:\+\d+$/.test(l)?l.slice(3):s.peerKey.replace(/^wa:/,""),g=ee(h)||"";if(!g||!f.has(g)){M.warn({denied:s.peerKey,phone:g,senderKey:l},"denied"),await u({kind:"text",body:Fp()});return}}try{if(!!!(s.mediaSavedPath||s.mediaError)&&!_p(e,s.text,l))return;if(s.mediaError&&await u({kind:"text",body:p(s.mediaError)}),s.mediaSavedPath&&await u({kind:"text",body:p(`Saved: ${s.mediaSavedPath}`)}),s.text.trim()){let h=await tr(e,t,n,r,o,s,i,a,l,!1,d);if(h!==null)if(h.kind==="texts")for(let g of h.bodies)await u({kind:"text",body:g});else await u(h)}}catch(f){M.error({err:String(f)},"inbound handler error"),await u({kind:"text",body:p(`Error: ${String(f)}`)}).catch(()=>{})}}function nb(){if(process.env.OMNISH_BACKGROUND_GATEWAY==="1")try{Na.readFileSync(ce,"utf8").trim()===String(process.pid)&&Na.unlinkSync(ce)}catch{}}async function Wp(e){let t=new Et,n=new Map,r=new Map,o=new Map,s=null,i=new Map,a=async(D,le)=>{let Y=i.get(D);s?.sendReply(D,le,Y)},l=async(D,le)=>{let Y=D.startsWith("tg:")?"telegram":"whatsapp",k=i.get(D),x=Zi(D,{kind:"file",spec:le},k,Y);s?.sendRoutedReply(D,x)},d=async(D,le)=>{let Y=i.get(D),k=D.startsWith("tg:")?"telegram":"whatsapp";s?.sendReply(D,ke(p(le),k).text,Y)},u={onPlainTextLlmFallback(D,le){Wn($(),D,le,Y=>a(D,Y))},sendToPeer:a},c=new zt(()=>$(),a),m={async reload(){return{ok:!0,summary:"Attached mode: config reloaded from disk. Messengers run on the omnish platform; this device executes commands locally."}}};if(s=new ls({env:e,onReplyError:async(D,le,Y)=>{let k=D.startsWith("tg:")?"telegram":"whatsapp";s?.sendReply(D,ke(p(`Error sending: ${le}`),k).text,Y)},onMessage:async D=>{let le=la(),Y=D.peerKey;i.set(Y,D.messageId);let k=D.surface==="telegram"?"telegram":"whatsapp",x=D.senderE164&&k==="whatsapp"?`wa:${D.senderE164}`:Y;vn()&&M.info({peerKey:Y,senderKey:x,surface:k},"platform inbound message");let L=D.mediaSavedPath,F=D.mediaError;if(D.inboundMedia){let z=bd(le,Y,D.inboundMedia);L=z.mediaSavedPath??L,F=z.mediaError??F}let Q={peerKey:Y,text:D.text,...L?{mediaSavedPath:L}:{},...F?{mediaError:F}:{}};await Nr(le,t,n,r,o,Q,c,m,x,u,async z=>{try{if(z.kind==="texts"){for(let Ue of z.bodies){let Ie=ke(Ue,k);s?.sendReply(Y,Ie.text,D.messageId)}return}if(z.kind==="text"){let Ue=ke(z.body,k);s?.sendReply(Y,Ue.text,D.messageId);return}let we=Zi(Y,z,D.messageId,k);s?.sendRoutedReply(Y,we)}catch(we){s?.sendReply(Y,`Error sending file: ${String(we)}`,D.messageId)}},{surface:k})}}),process.env.OMNISH_BACKGROUND_GATEWAY==="1")try{Na.writeFileSync(ce,`${process.pid}
330
+ `,{mode:384})}catch(D){M.warn({err:String(D)},"could not write gateway pidfile")}ns({getCfg:()=>$(),getWaOutbound:()=>null,getTgSendMedia:()=>null,getTgSendText:()=>null,sendPlatformMedia:l,sendPlatformText:d});let f=null,h=$();if(h.webhookEnabled){let D=h.webhookToken||tb.randomBytes(32).toString("hex");h.webhookToken||W({webhookToken:D}),f=rs({port:h.webhookPort,host:h.webhookHost,token:D},{sendToPeer:a,getDefaultPeerKey:()=>null}).stop}let g=ys({getRunningVersion:Ze,getConfig:$,log:M}),y=jo({getConfig:$,sendToPeer:a,sendMediaToPeer:l}),b=io({getConfig:$,sendToPeer:a}),{deviceId:v,account:E}=await s.connect(),R=await is(e,E??null),P=5*60*1e3,T=setInterval(()=>{is(e,null).catch(()=>{})},P);T.unref?.();let O=e.deviceId?.trim();if(O){O!==v&&M.warn({configuredDeviceId:O,registeredDeviceId:v},"platform_device_id does not match registered device id");try{await ua(e,O)}catch(D){M.warn({err:String(D)},"could not set platform default device")}}let A=R?.gatewayMode??E?.gatewayMode,K=R?.connectors.whatsapp??E?.connectors?.whatsapp,X=R?.connectors.telegram??E?.connectors?.telegram,ie=[K?`whatsapp:${K.linked?"linked":K.status}`:null,X?`telegram:${X.linked?"linked":X.status}`:null].filter(Boolean).join(", ");console.error(`omnish attached to platform (device ${v})`+(A?` \u2014 gatewayMode=${A}`:"")+(ie?` \u2014 ${ie}`:""));let he=()=>{clearInterval(T),y(),b(),g?.(),f?.(),nb(),Kn(),s?.stop(),c.dispose(),t.killAllRunning(),gn().stopAll().catch(()=>{}),console.error(`
331
+ ${C(process.stderr,"shutting down\u2026")}`),process.exit(0)};process.on("SIGINT",he),process.on("SIGTERM",he),await new Promise(()=>{})}function _a(e,t){return async n=>{if(t.surface==="whatsapp"&&t.waJid){if(n.kind==="file")await e.sendWaMedia?.(t.waJid,n.spec);else if(n.kind==="files")for(let r of n.specs)await e.sendWaMedia?.(t.waJid,r);else if(n.kind==="texts")for(let r of n.bodies)await e.sendWaText?.(t.waJid,ke(r,"whatsapp").text);else await e.sendWaText?.(t.waJid,ke(n.body,"whatsapp").text);return}if(t.surface==="telegram"&&e.sendTg)if(n.kind==="texts")for(let r of n.bodies)await e.sendTg({kind:"text",body:r});else await e.sendTg(n)}}me();it();G();function Ps(){if(se()){let i=$(),a=Ct(i);return dr(a)?{ok:!1,message:`Fix security errors before starting the gateway.
332
332
 
333
333
  ${a.filter(u=>u.severity==="error").map(u=>{let c=[u.message];return u.detail&&c.push(u.detail),u.fixHint&&c.push(`Fix: ${u.fixHint}`),c.join(`
334
334
  `)}).join(`
335
335
 
336
336
  `)}`}:{ok:!0}}let e=$(),t=e.gatewayMode,n=t==="whatsapp"||t==="both",r=t==="telegram"||t==="both",o=ve(e);if(r&&!o)return{ok:!1,message:`Telegram enabled (gatewayMode) but no bot token. Set telegramBotToken in config or TELEGRAM_BOT_TOKEN.
337
- config: ${_}`};if(n&&!nt())return{ok:!1,message:"WhatsApp enabled but no session. Run `omnish link` first."};let s=Rt(e);return lr(s)?{ok:!1,message:`Fix security errors before starting the gateway (or change gatewayMode / token).
337
+ config: ${_}`};if(n&&!nt())return{ok:!1,message:"WhatsApp enabled but no session. Run `omnish link` first."};let s=Ct(e);return dr(s)?{ok:!1,message:`Fix security errors before starting the gateway (or change gatewayMode / token).
338
338
 
339
339
  ${s.filter(l=>l.severity==="error").map(l=>{let d=[l.message];return l.detail&&d.push(l.detail),l.fixHint&&d.push(`Fix: ${l.fixHint}`),d.join(`
340
340
  `)}).join(`
341
341
 
342
- `)}`}:{ok:!0}}G();import As from"node:fs";import Is from"node:path";pe();import{spawn as Vw}from"node:child_process";import Lp from"node:fs";import{stdout as Xw}from"node:process";G();var Ia=["tunnelRelayUrl","platformToken","platformDeviceId"],Np=[{label:"Platform (attached gateway + tunnel)",keys:["tunnelRelayUrl","platformToken","platformDeviceId"]},{label:"Tunnel",keys:["tunnelEnabled","tunnelMaxActive"]},{label:"Gateway",keys:["gatewayMode","commandPrefix","shell"]},{label:"Cluster",keys:["clusterEnabled","clusterRole","clusterLabel","clusterSenderBindings"]},{label:"Telegram",keys:["telegramBotToken"]},{label:"Apps",keys:["appsCols","appsRows","appsFlushMs","appsMinIntervalMs","appsMaxFlushBytes","appsMaxSessions","appsMaxSessionsTotal","appsMaxWaChars","appsLogTailLines","appsSubmitDelayMs","appsClearInput","appsClearInputDelayMs","appsClearInputSequence","appsSkipClearOnPasswordPrompt","appsPasswordPromptHint"]},{label:"Files",keys:["fileSendMaxBytes","fileReceiveMaxBytes","fileInboxSubdir","fileReceiveRootMode","fileReceiveRootPath"]},{label:"Recipes",keys:["recipesAllowDangerousBuiltins","recipesMaxTaskChars","recipesMacroDefaultCommand"]},{label:"Sync / jobs",keys:["syncTimeoutMs","syncMaxBytes","jobLogTailLines"]},{label:"Service / updates",keys:["serviceInstallFromChat","updateCheckEnabled","updateCheckIntervalMs","updateCheckPackageName","updateInfoUrl"]},{label:"Chat LLM fallback",keys:["chatLlmFallbackEnabled","chatLlmShellCommand","chatLlmTimeoutMs","chatLlmMaxInputChars","chatLlmMaxOutputChars","chatLlmNeedsTty","chatLlmWorkDir"]}];function Es(e){let t=[J(e,"omnish config"),w(e,"Manage ~/.omnish/config.json (env vars still override for platform credentials)."),"",J(e,"Usage:"),` ${S(e,"omnish config add <key> <value> [key value ...]")}`,` ${S(e,"omnish config show <key>[,<key>|*]")}`,` ${S(e,"omnish config edit")}`,` ${S(e,"omnish config edit <key> <value>")}`,` ${S(e,"omnish config delete <key>[,<key>]")}`,"",w(e,"Platform aliases: platform_url \u2192 tunnelRelayUrl, platform_token \u2192 platformToken,"),w(e," platform_device_id \u2192 platformDeviceId"),` ${S(e,"omnish config show platform")} ${w(e,"\u2014 attached-mode diagnostics (URL, token, devices)")}`,w(e," Full setup: omnish help platform"),w(e,"add sets/overwrites scalar keys (not array append). show * lists all keys."),""];console.log(t.join(`
343
- `))}function _p(e){return e.split(/[,\s]+/).map(t=>t.trim()).filter(Boolean)}function Oa(e){if(e.length===0)return[];let t=e.join(" ");if(t.includes(",")){let n=[];for(let r of t.split(",")){let o=r.trim();if(!o)continue;let s=o.split(/\s+/);if(s.length<2)throw new Error(`Invalid pair "${o}" \u2014 expected "key value"`);n.push({key:s[0],value:s.slice(1).join(" ")})}return n}if(e.length===2)return[{key:e[0],value:e[1]}];if(e.length%2===0){let n=[];for(let r=0;r<e.length;r+=2)n.push({key:e[r],value:e[r+1]});return n}return[{key:e[0],value:e.slice(1).join(" ")}]}function Ts(e,t){if(e==="clusterSenderBindings")return JSON.stringify(t.clusterSenderBindings);let n=String(t[e]??"");return e==="platformToken"||e==="telegramBotToken"?tn(e,n):n}function Fp(e){return e==="tunnelRelayUrl"?{value:$r(),source:zn()}:e==="platformToken"?{value:tn(e,ua()),source:Kn()}:e==="platformDeviceId"?{value:(da()??"")||"(empty)",source:pa()}:null}function Zw(e){if(e!=="platformToken"&&e!=="tunnelRelayUrl")return;let n=$().platformToken.trim()||xt()?.token?.trim()||"";n&&ft({token:n,relayUrl:$r()})}async function eb(){let e=process.env.VISUAL?.trim()||process.env.EDITOR?.trim()||(process.platform==="win32"?"notepad":"nano"),t=Lp.readFileSync(_,"utf8");await new Promise((n,r)=>{let o=Vw(e,[_],{stdio:"inherit"});o.on("error",r),o.on("exit",s=>{s===0?n():r(new Error(`Editor exited with code ${s??1}`))})});try{$()}catch(n){throw Lp.writeFileSync(_,t,{mode:384}),new Error(`Invalid config after edit: ${n instanceof Error?n.message:String(n)}`)}}async function Wp(e){let t=Xw,n=process.stderr,r=(e[0]??"").trim().toLowerCase(),o=e.slice(1);if(!r||r==="help"||r==="--help"||r==="-h"){Es(t);return}if(r==="add"||r==="edit"){try{if(r==="edit"&&o.length===0){await eb(),console.log(B(t,`Updated ${_}`));return}let s=Oa(o);if(s.length===0){console.error(C(n,"Expected at least one key/value pair.")),process.exitCode=1;return}for(let{key:i,value:a}of s){let l=po(i);if(!l){console.error(C(n,`Unknown key "${i}". Try: ${Wc().slice(0,8).join(", ")}\u2026 (omnish config show *)`)),process.exitCode=1;return}Rn(l,a),Zw(l);let d=$(),u=l==="tunnelRelayUrl"?d.tunnelRelayUrl:Ia.includes(l)?tn(l,String(d[l]??"")):Ts(l,d);console.log(B(t,`${i} \u2192 ${u}`))}console.log(B(t,_))}catch(s){console.error(C(n,s instanceof Error?s.message:String(s))),process.exitCode=1}return}if(r==="show"){let s=o.join(" ").trim()||"*";if(s==="*"){let l=$(),d=[J(t,"Config"),w(t,_),""];for(let m of Np){d.push(J(t,m.label));for(let f of m.keys){let h=Ts(f,l),g=Fp(f);g&&Ia.includes(f)?d.push(` ${S(t,f)}: ${h} ${w(t,`(effective: ${g.value}, source: ${g.source})`)}`):d.push(` ${S(t,f)}: ${h}`)}d.push("")}let u=new Set(Np.flatMap(m=>m.keys)),c=gi.filter(m=>!u.has(m));if(c.length>0){d.push(J(t,"Other"));for(let m of c)d.push(` ${S(t,m)}: ${Ts(m,l)}`)}console.log(d.join(`
344
- `));return}let i=_p(s),a=$();for(let l of i){let d=po(l);if(!d){console.error(C(n,`Unknown key "${l}".`)),process.exitCode=1;return}let u=Ts(d,a),c=Fp(d);c&&Ia.includes(d)?console.log(`${l}: ${u} (effective: ${c.value}, source: ${c.source})`):console.log(`${l}: ${u}`)}return}if(r==="delete"){let s=o.join(" ").trim();if(!s){console.error(C(n,"Expected at least one key to delete.")),process.exitCode=1;return}if(s==="*"){console.error(C(n,'Refusing "delete *". List keys explicitly.')),process.exitCode=1;return}try{for(let i of _p(s)){let a=po(i);if(!a){console.error(C(n,`Unknown key "${i}".`)),process.exitCode=1;return}Uc(a),console.log(B(t,`cleared ${i}`))}console.log(B(t,_))}catch(i){console.error(C(n,i instanceof Error?i.message:String(i))),process.exitCode=1}return}console.error(C(n,`Unknown subcommand "${r}".`)),Es(t),process.exitCode=1}async function Up(e){let t=fe(),n=[];if(n.push(J(e,"platform")),!t){n.push(` ${w(e,"attached:")} ${me(e,"no")} \u2014 set platform_url + platform_token (omnish config add) or env`);let a=zn(),l=Kn();return(a==="env"||l==="env")&&n.push(` ${w(e,"note:")} ${V(e,"partial env override (need URL + token)")}`),n}let r=zn(),o=Kn(),s=pa(),i=r==="env"||o==="env"||s==="env"?` ${w(e,"(env overrides config)")}`:"";n.push(` ${w(e,"attached:")} ${S(e,"yes")}${i}`),n.push(` ${w(e,"url:")} ${S(e,t.platformUrl)} ${V(e,`[${r}]`)}`),n.push(` ${w(e,"token:")} ${S(e,tn("platformToken",t.token))} ${V(e,`[${o}]`)}`),t.deviceId&&n.push(` ${w(e,"device:")} ${S(e,t.deviceId)} ${V(e,`[${s}]`)}`);try{let{fetchPlatformAccount:a}=await Promise.resolve().then(()=>(rs(),yd)),l=await a(t);n.push(` ${w(e,"gatewayMode:")} ${S(e,l.gatewayMode)} ${V(e,"(platform)")}`);let d=c=>{let m=l.connectors[c];return m?m.linked?S(e,"linked"):V(e,m.status):me(e,"idle")};n.push(` ${w(e,"whatsapp:")} ${d("whatsapp")}`),n.push(` ${w(e,"telegram:")} ${d("telegram")}`);let u=l.routing.onlineCount;n.push(` ${w(e,"routing:")} default=${l.routing.defaultDeviceId??V(e,"(none)")} online=${u}`),t.deviceId&&l.routing.defaultDeviceId&&t.deviceId!==l.routing.defaultDeviceId&&n.push(` ${w(e,"warn:")} ${me(e,"platform_device_id \u2260 platform defaultDeviceId \u2014 run omnish run or set default on dashboard")}`),u===0&&n.push(` ${w(e,"warn:")} ${me(e,"no device online on platform")}`)}catch{n.push(` ${w(e,"account:")} ${V(e,"(could not fetch /v1/me \u2014 check token and URL)")}`)}return n}rs();function Hp(){let e=fe();return e||(console.error(C(process.stderr,"Set platform_url + platform_token (omnish config add) or OMNISH_PLATFORM_URL + OMNISH_TOKEN.")),process.exitCode=1,null)}function tb(e){let t=e.trim();return t?t.startsWith("wa:")?{kind:"wa",value:t.slice(3).trim()}:t.startsWith("tg:")?{kind:"tg",value:t.slice(3).trim()}:/^\+?\d{8,}$/.test(t.replace(/\s/g,""))?{kind:"wa",value:t}:/^\d+$/.test(t)?{kind:"tg",value:t}:null:null}function Dp(e){return e.replace(/\D/g,"")}async function Bp(){let e=Hp();if(!e)return;let t=await Bn(e),n=t.connectors.whatsapp,r=t.connectors.telegram;console.log(B(process.stdout,"Platform account")),console.log(` gatewayMode: ${t.gatewayMode}`),console.log(` whatsapp: ${n?.linked?"linked":n?.status??"idle"} (allowlist: ${t.allowFrom.length})`);let o=r?.tokenConfigured?"token saved":"no token";console.log(` telegram: ${r?.linked?"linked":r?.status??"idle"} (${o}, allowlist: ${t.telegramAllowFrom.length})`),console.log(` defaultDeviceId: ${t.defaultDeviceId??"\u2014"}`),console.log(` online devices: ${t.routing.onlineCount}`),t.allowFrom.length&&console.log(` allowFrom: ${t.allowFrom.map(s=>`+${s}`).join(", ")}`),t.telegramAllowFrom.length&&console.log(` telegramAllowFrom: ${t.telegramAllowFrom.join(", ")}`)}async function jp(e){let t=Hp();if(!t)return;let n=(e[0]??"").toLowerCase();if(!n||n==="-h"||n==="--help"){console.log(["Usage:"," omnish platform allow list"," omnish platform allow add wa:+15551234567 tg:123456789"," omnish platform allow set --wa +1555,... --tg 123,...",""].join(`
345
- `));return}if(n==="list"){let r=await Bn(t);console.log(B(process.stdout,"Platform allowlists")),console.log(` WhatsApp (${r.allowFrom.length}): ${r.allowFrom.length?r.allowFrom.map(o=>`+${o}`).join(", "):"(empty \u2014 any sender)"}`),console.log(` Telegram (${r.telegramAllowFrom.length}): ${r.telegramAllowFrom.length?r.telegramAllowFrom.join(", "):"(empty \u2014 any sender)"}`);return}if(n==="add"){let r=await Bn(t),o=[...r.allowFrom],s=[...r.telegramAllowFrom];for(let a of e.slice(1)){let l=tb(a);if(!l){console.error(C(process.stderr,`Unrecognized entry: ${a}`)),process.exitCode=1;return}if(l.kind==="wa"){let d=Dp(l.value);d&&!o.includes(d)&&o.push(d)}else{let d=l.value.replace(/\D/g,"");d&&!s.includes(d)&&s.push(d)}}let i=await ts(t,{allowFrom:o,telegramAllowFrom:s});console.log(B(process.stdout,`Allowlists updated (${i.allowFrom.length} WhatsApp, ${i.telegramAllowFrom.length} Telegram).`));return}if(n==="set"){let r,o;for(let a=1;a<e.length;a++){let l=e[a];l==="--wa"&&e[a+1]?r=e[++a].split(",").map(d=>Dp(d.trim())).filter(Boolean):l==="--tg"&&e[a+1]&&(o=e[++a].split(",").map(d=>d.trim().replace(/^tg:/i,"").replace(/\D/g,"")).filter(Boolean))}if(r===void 0&&o===void 0){console.error(C(process.stderr,"Use --wa and/or --tg with comma-separated values.")),process.exitCode=1;return}let s={};r!==void 0&&(s.allowFrom=r),o!==void 0&&(s.telegramAllowFrom=o);let i=await ts(t,s);console.log(B(process.stdout,`Allowlists set (${i.allowFrom.length} WhatsApp, ${i.telegramAllowFrom.length} Telegram).`));return}console.error(C(process.stderr,`Unknown allow subcommand: ${e[0]}`)),process.exitCode=1}async function Gp(e){let t="",n="",r="";for(let o=0;o<e.length;o++){let s=e[o];(s==="--url"||s==="-u")&&e[o+1]?t=e[++o].trim():(s==="--token"||s==="-t")&&e[o+1]?n=e[++o].trim():s==="--device-id"&&e[o+1]&&(r=e[++o].trim())}if(!t||!n){let o=Oa(e);for(let s of o)s.key==="platform_url"&&(t=s.value),s.key==="platform_token"&&(n=s.value),s.key==="platform_device_id"&&(r=s.value)}if(!t||!n){console.error(C(process.stderr,"Usage: omnish platform login --url <url> --token <token> [--device-id <id>]")),process.exitCode=1;return}Rn("tunnelRelayUrl",t.replace(/\/$/,"")),Rn("platformToken",n),r&&Rn("platformDeviceId",r),console.log(B(process.stdout,"Platform credentials saved to config.json.")),console.log(" Run: omnish platform status && omnish run")}import nb from"qrcode-terminal";var rb=1e3,ob=120;function Jp(){let e=fe();return e||(console.error(C(process.stderr,"Set platform_url + platform_token (omnish config add) or OMNISH_PLATFORM_URL + OMNISH_TOKEN.")),process.exitCode=1,null)}function Na(e){let t=e.replace(/\/$/,"");return/^https?:\/\//i.test(t)?t:`http://${t}`}async function Kp(e){let t=`${Na(e.platformUrl)}/v1/connectors/whatsapp/status`,n=await fetch(t,{headers:{Authorization:`Bearer ${e.token}`}}),r=await n.json().catch(()=>({}));if(!n.ok)throw new Error(r.error||`HTTP ${n.status}`);return r}async function sb(e){let t=await fetch(`${Na(e.platformUrl)}/v1/connectors/whatsapp/start`,{method:"POST",headers:{"content-type":"application/json",Authorization:`Bearer ${e.token}`},body:JSON.stringify({})}),n=await t.json().catch(()=>({}));if(!t.ok)throw new Error(n.error||`HTTP ${t.status}`);return n}function zp(e){let t=process.stdout,n=w(t,"\xB7".repeat(42));console.log(V(t,"Scan with WhatsApp \u2192 Linked devices")),console.log(n),nb.generate(e,{small:!0}),console.log(n)}var Ps="",La=!1;function ib(e){let t=e.status??"";return t==="qr"||t==="connecting"||t==="reconnecting"||t==="pairing_restart"}async function ab(e){for(let t=0;t<ob;t++){let n=await Kp(e);if(n.status==="linked"||n.linked)return n;if(n.status==="pairing_restart"&&!La&&(La=!0,console.log(w(process.stdout,"Finishing WhatsApp link after scan (server restart) \u2014 wait a few seconds\u2026"))),n.qr&&n.qr!==Ps&&(Ps=n.qr,zp(n.qr)),!ib(n))throw new Error(n.error||n.statusMessage||`Unexpected status: ${n.status}`);await new Promise(r=>setTimeout(r,rb))}throw new Error("Timed out waiting for WhatsApp to link (scan the QR within ~2 minutes).")}async function qp(){let e=Jp();if(!e)return;let t=await Kp(e);if(t.status==="linked"||t.linked){console.log(B(process.stdout,"WhatsApp already linked on the platform.")),t.linkedPhoneE164&&(console.log(` Phone: +${t.linkedPhoneE164}`),console.log(` Hint: omnish platform allow add wa:+${t.linkedPhoneE164}`));return}console.log(B(process.stdout,"Starting WhatsApp pairing on the platform\u2026")),La=!1,Ps="",t=await sb(e),t.qr&&(zp(t.qr),Ps=t.qr),t=await ab(e),console.log(B(process.stdout,"WhatsApp linked on the platform.")),t.linkedPhoneE164&&(console.log(` Phone: +${t.linkedPhoneE164}`),console.log(` Next: omnish platform allow add wa:+${t.linkedPhoneE164}`),console.log(" Then: omnish run"))}async function Yp(){let e=Jp();if(!e)return;let t=await fetch(`${Na(e.platformUrl)}/v1/connectors/whatsapp/unlink`,{method:"POST",headers:{"content-type":"application/json",Authorization:`Bearer ${e.token}`},body:JSON.stringify({})}),n=await t.json().catch(()=>({}));if(!t.ok){console.error(C(process.stderr,n.error||`HTTP ${t.status}`)),process.exitCode=1;return}console.log(B(process.stdout,`WhatsApp unlinked (status: ${n.status??"idle"}).`))}import lb from"ws";function Qp(e,t,n){return new Promise(r=>{let o=new lb(e,{headers:{Authorization:`Bearer ${t}`}}),s=setTimeout(()=>{o.terminate(),r({ok:!1,message:"timeout"})},n);o.once("open",()=>{clearTimeout(s),o.close(),r({ok:!0})}),o.once("unexpected-response",(i,a)=>{clearTimeout(s),r({ok:!1,status:a.statusCode,message:`HTTP ${a.statusCode} ${a.statusMessage}`})}),o.once("error",i=>{clearTimeout(s),r({ok:!1,message:String(i?.message??i)})})})}async function Vp(e,t,n=12e3){let r=e.replace(/\/$/,""),o=jn(r),s=await Qp(o,t,n),i,a,l="";for(let d of os(r)){let u=await Qp(d,t,n);if(u.ok)return i=new URL(d).pathname,{ok:!0,controlWsOk:s.ok,deviceWsOk:!0,deviceWsPath:i};a=u.status,l=u.message}return a===400?{ok:!1,controlWsOk:s.ok,deviceWsOk:!1,deviceWsStatus:400,error:"Device WebSocket returned HTTP 400 on /platform/device and could not use /control/device. Redeploy the latest relay image (adds /control/device), or route /platform/* to port 8788 in Caddy/EasyPanel."}:{ok:!1,controlWsOk:s.ok,deviceWsOk:!1,deviceWsStatus:a,error:`Device WebSocket failed: ${l}`}}var cb=400,ub=8*1024*1024,db=2*1024*1024;function pb(e){let t=ne,n=!1,r=!1;for(let o=0;o<e.length;o++){let s=e[o];s==="-h"||s==="--help"?r=!0:s==="--force"?n=!0:(s==="--auth-dir"||s==="--authDir")&&e[o+1]&&(t=Is.resolve(e[++o]))}return{authDir:t,force:n,help:r}}function mb(e){let t={},n=0,r=0;function o(s,i){let a;try{a=As.readdirSync(i,{withFileTypes:!0})}catch{return}for(let l of a){let d=l.name;if(d==="."||d==="..")continue;let u=Is.join(i,d),m=(s?`${s}/${d}`:d).split(Is.sep).join("/");if(!l.isSymbolicLink()){if(l.isDirectory())o(m,u);else if(l.isFile()){let f=As.readFileSync(u);if(f.length>db)throw new Error(`file too large: ${m}`);if(n+=f.length,n>ub)throw new Error("total auth size exceeds limit (8 MiB)");if(r+=1,r>cb)throw new Error("too many files (max 400)");t[m]=f.toString("base64")}}}}return o("",e),t}function Os(e){console.log([ce(e,"omnish platform"),w(e,"Attached mode: messengers on the hosted platform; shell on this machine (omnish run)."),"",J(e,"When to use"),w(e," Link WhatsApp/Telegram once on the platform dashboard. Run omnish run on laptops, servers, or containers without local Baileys."),"",J(e,"Prerequisites"),w(e," 1. Platform account token (signup/login on relay dashboard)."),w(e," 2. Messengers linked on the platform (dashboard QR or import-whatsapp)."),w(e," 3. allowFrom / telegramAllowFrom on dashboard Allowlists (Telegram: DM bot /id for your user id)."),"",J(e,"Configure"),` ${S(e,"omnish config add platform_url <url> platform_token <token>")}`,w(e," Optional: platform_device_id <id>"),w(e," Env overrides: OMNISH_PLATFORM_URL, OMNISH_TOKEN, OMNISH_DEVICE_ID"),w(e," (aliases: OMNISH_COMM_LAYER_URL, OMNISH_DEVICE_TOKEN, OMNISH_TUNNEL_*)"),"",J(e,"Workflow"),` ${S(e,"omnish platform login --url <url> --token <t>")} ${w(e,"\u2014 save credentials (or config add)")}`,` ${S(e,"omnish platform status")} ${w(e,"\u2014 account, connectors, allowlists")}`,` ${S(e,"omnish platform allow add tg:<id>")} ${w(e,"\u2014 or dashboard + bot /id")}`,` ${S(e,"omnish platform probe")} ${w(e,"\u2014 test WebSocket routing")}`,` ${S(e,"omnish run")} ${w(e,"\u2014 attach device; expect 'omnish attached to platform'")}`,"",J(e,"Subcommands"),S(e,"omnish platform login --url <url> --token <token> [--device-id <id>]"),w(e," Save platform URL and token to config.json."),"",S(e,"omnish platform status"),w(e," Show gatewayMode, connector status, allowlists, online devices."),"",S(e,"omnish platform allow list|add|set"),w(e," Manage platform allowlists (PUT /v1/me/allowlists)."),"",S(e,"omnish platform probe"),w(e," Test control-plane WebSocket routing (diagnose attached omnish run 400 errors)."),"",S(e,"omnish platform link-whatsapp"),w(e," Pair WhatsApp on the platform (terminal QR + poll until linked)."),"",S(e,"omnish platform unlink-whatsapp"),w(e," Remove platform WhatsApp session and auth files."),"",S(e,"omnish platform import-whatsapp [--auth-dir <path>] [--force]"),w(e," Upload local Baileys auth (after omnish link on this host). Use link-whatsapp or dashboard QR when possible."),w(e," Stop local omnish run before import to avoid WhatsApp session conflicts."),"",w(e,"Standalone omnish link on this host is not used for platform WhatsApp when attached."),w(e,"Docs: docs/guides/platform-reference.md (full API/CLI)"),w(e," docs/guides/platform-attached-mode.md (walkthrough)"),""].join(`
346
- `))}async function fb(e){let{authDir:t,force:n,help:r}=pb(e);if(r){console.log(["Usage: omnish platform import-whatsapp [--auth-dir <path>] [--force]","","Requires OMNISH_PLATFORM_URL and OMNISH_TOKEN.","Default auth directory: ~/.omnish/auth (Baileys useMultiFileAuthState).","Stop `omnish run` on this machine before importing to avoid WhatsApp session conflicts.",""].join(`
347
- `));return}let o=fe();if(!o){console.error(C(process.stderr,"Set OMNISH_PLATFORM_URL (control plane URL) and OMNISH_TOKEN (from dashboard signup/login).")),process.exitCode=1;return}if(!As.existsSync(t)){console.error(C(process.stderr,`Auth directory not found: ${t}`)),process.exitCode=1;return}te();let s=Is.join(t,"creds.json");if(!As.existsSync(s)){console.error(C(process.stderr,`No creds.json in ${t} \u2014 link WhatsApp locally first (omnish link).`)),process.exitCode=1;return}let i;try{i=mb(t)}catch(f){console.error(C(process.stderr,String(f))),process.exitCode=1;return}if(Object.keys(i).length===0){console.error(C(process.stderr,"No files collected (empty auth directory?).")),process.exitCode=1;return}let a=o.platformUrl.replace(/\/$/,""),d=`${/^https?:\/\//i.test(a)?a:`http://${a}`}/v1/connectors/whatsapp/import`,u=await fetch(d,{method:"POST",headers:{"content-type":"application/json",Authorization:`Bearer ${o.token}`},body:JSON.stringify({files:i,force:n})}),c=await u.json().catch(()=>({}));if(!u.ok){console.error(C(process.stderr,c.error||`HTTP ${u.status}`)),process.exitCode=1;return}let m=c.qr?" QR emitted \u2014 open the platform dashboard if you need to scan.":"";console.log(B(process.stdout,`Uploaded Baileys auth. Platform connector status: ${c.status??"unknown"}.${m}`))}async function hb(){let e=process.stdout,t=process.stderr,n=fe();if(!n){console.error(C(t,"Set platform_url + platform_token (omnish config add) or OMNISH_PLATFORM_URL + OMNISH_TOKEN.")),process.exitCode=1;return}console.log(`${ce(e,"Platform probe")} ${w(e,n.platformUrl)}`);let r=await Vp(n.platformUrl,n.token);console.log(` ${w(e,"wss /control:")} ${r.controlWsOk?S(e,"ok"):me(e,"fail")}`);for(let o of["/platform/device","/control/device"]){if(r.ok&&r.deviceWsPath&&r.deviceWsPath!==o){console.log(` ${w(e,`wss ${o}:`)} ${V(e,"skipped (fallback used)")}`);continue}if(r.ok&&r.deviceWsPath===o){console.log(` ${w(e,`wss ${o}:`)} ${S(e,"ok")}`);continue}!r.ok&&o==="/platform/device"&&console.log(` ${w(e,`wss ${o}:`)} ${me(e,"fail")}${r.deviceWsStatus?` (HTTP ${r.deviceWsStatus})`:""}`),!r.ok&&o==="/control/device"&&console.log(` ${w(e,`wss ${o}:`)} ${me(e,"fail")}`)}if(r.ok){r.deviceWsPath==="/control/device"&&console.log(w(e," Using /control/device fallback (/platform/* not on control port \u2014 redeploy Caddy when convenient).")),console.log(B(e,"Attached omnish run should be able to connect."));return}console.error(C(t,r.error??"Platform probe failed.")),r.controlWsOk&&!r.deviceWsOk&&console.error(C(t,"Fix: redeploy the latest tunnel-relay image (enables wss /control/device), update omnish CLI, then retry. Also route /platform/* to 8788 when you can (Caddyfile.standalone).")),process.exitCode=1}async function Xp(e){let t=(e[0]??"").toLowerCase();if(!t||t==="-h"||t==="--help"){Os(process.stdout);return}if(t==="login"){await Gp(e.slice(1));return}if(t==="status"){await Bp();return}if(t==="allow"){await jp(e.slice(1));return}if(t==="probe"){await hb();return}if(t==="link-whatsapp"){await qp();return}if(t==="unlink-whatsapp"){await Yp();return}if(t==="import-whatsapp"){await fb(e.slice(1));return}console.error(C(process.stderr,`Unknown platform subcommand: ${e[0]}`)),Os(process.stderr),process.exitCode=1}G();import tm from"node:crypto";import fn from"node:fs";import nm from"node:path";function gb(){return tm.randomBytes(24).toString("hex")}function Zp(){return tm.randomBytes(32).toString("hex")}function em(e){try{let t=fn.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 yb(){let e=em(ct);if(e)return e;if(e=em(er),e){j(nm.dirname(ct)),fn.writeFileSync(ct,JSON.stringify(e,null,2)+`
348
- `,{mode:384});try{fn.unlinkSync(er)}catch{}return e}return null}function rm(e){j(nm.dirname(ct));let t=yb(),n=(e??"").trim();if(n){let o={token:n,secret:t?.secret??Zp()};fn.writeFileSync(ct,JSON.stringify(o,null,2)+`
349
- `,{mode:384});try{fn.existsSync(er)&&fn.unlinkSync(er)}catch{}return o}if(t)return t;let r={token:gb(),secret:Zp()};return fn.writeFileSync(ct,JSON.stringify(r,null,2)+`
350
- `,{mode:384}),r}pe();import Vt from"node:fs";import xb from"node:http";import Ae from"node:path";import it from"node:process";import{fileURLToPath as $b}from"node:url";import Rb from"node:os";qe();G();import _a from"node:crypto";var Fa="omnish_cfg_sess",om=7*24*60*60*1e3;function Wa(e){let t=Date.now()+om,n=Buffer.from(JSON.stringify({exp:t}),"utf8").toString("base64url"),r=_a.createHmac("sha256",e).update(n).digest("hex");return`${n}.${r}`}function sm(e,t){let n=wb(t??"")[Fa];if(!n||!n.includes("."))return!1;let r=n.lastIndexOf("."),o=n.slice(0,r),s=n.slice(r+1),i=_a.createHmac("sha256",e).update(o).digest("hex");try{let a=Buffer.from(s,"hex"),l=Buffer.from(i,"hex");if(a.length!==l.length||!_a.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 Ua(e){let t=Math.floor(om/1e3);return`${Fa}=${e}; HttpOnly; Path=/; SameSite=Lax; Max-Age=${t}`}function im(){return`${Fa}=; HttpOnly; Path=/; SameSite=Lax; Max-Age=0`}function wb(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}G();import Da from"node:fs";import Ls from"node:process";function am(e){try{return Ls.kill(e,0),!0}catch{return!1}}function lm(e){let t=Date.now()+e;for(;Date.now()<t;);}function bb(){try{let e=Da.readFileSync(Fr,"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 cm(e){Da.writeFileSync(Fr,`${JSON.stringify(e)}
351
- `,{mode:384})}function Ir(){try{Da.unlinkSync(Fr)}catch{}}function um(e){let t=bb();if(t&&t.port===e&&t.pid!==Ls.pid){if(!am(t.pid)){Ir();return}try{Ls.kill(t.pid,"SIGTERM")}catch{}if(lm(350),am(t.pid)){try{Ls.kill(t.pid,"SIGKILL")}catch{}lm(100)}Ir()}}pe();G();import Ha from"node:fs";import kb from"node:process";function Sb(e){try{return kb.kill(e,0),!0}catch{return!1}}function vb(){try{let e=Ha.readFileSync(le,"utf8").trim(),t=Number.parseInt(e,10);return Number.isFinite(t)&&t>0?t:null}catch{return null}}function Ns(){let e=vb();return e===null?!1:Sb(e)}var Ba=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&&(Wn(this.currentSock),this.currentSock=null)}beginPairing(t){if(this.busy)throw new Error("Pairing already in progress.");if(Ns())throw new Error("Gateway appears to be running (gateway.pid). Stop `omnish run` or your service before pairing from the browser.");if(!t.force&&nt())throw new Error("WhatsApp session already linked. Use \u201CReplace session\u201D or run `omnish link --force` from the CLI.");te(),t.force&&(Ha.rmSync(ne,{recursive:!0,force:!0}),Ha.mkdirSync(ne,{recursive:!0,mode:448})),this.busy=!0,this.abort=new AbortController;let n=this.abort.signal;(async()=>{try{await sd({authDir:ne,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}})()}},Or=new Ba;var ie="application/json; charset=utf-8",Lr=null;function Cb(){Ir();let e=Lr;Lr=null,e?e.close(()=>{it.exit(0)}):it.exit(0),setTimeout(()=>it.exit(0),4e3).unref()}function Mb(){let e=it.env.OMNISH_CONFIG_UI_STATIC?.trim(),t=it.env.OMNISH_UI_STATIC?.trim()||e;if(t&&Vt.existsSync(Ae.join(t,"index.html")))return t;let n=Ae.dirname($b(import.meta.url)),r=Ae.join(n,"ui");if(Vt.existsSync(Ae.join(r,"index.html")))return r;let o=Ae.join(n,"..","..","dist","ui");if(Vt.existsSync(Ae.join(o,"index.html")))return o;let s=Ae.join(it.cwd(),"dist","ui");if(Vt.existsSync(Ae.join(s,"index.html")))return s;throw new Error("omnish ui static files not found (expected dist/ui/index.html). Run `pnpm build`.")}function Tb(e){let t=Ae.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 Eb(e,t){let r=decodeURIComponent(t.split("?")[0]??"").replace(/^\/+/,""),o=Ae.normalize(Ae.join(e,r));return!o.startsWith(Ae.normalize(e+Ae.sep))&&o!==Ae.normalize(e)?null:o}async function ja(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 Pb(e){return Bt.includes(e)}function Ga(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:!!ve(e),telegramBotTokenEnvOverride:typeof it.env.TELEGRAM_BOT_TOKEN=="string"&&it.env.TELEGRAM_BOT_TOKEN.trim().length>0}}function Ab(){let e=$();return{version:Ze(),dataDir:H,configPath:_,waAuthDir:ne,whatsappLinked:nt(),gatewayPidHint:Vt.existsSync(Ae.join(H,"gateway.pid")),gatewayRunning:Ns(),gatewayLogFile:Ne,gatewayMode:e.gatewayMode,telegramBotTokenMasked:ve(e).length===0?"":Ga(e).telegramBotToken,telegramBotTokenEnvOverride:typeof it.env.TELEGRAM_BOT_TOKEN=="string"&&it.env.TELEGRAM_BOT_TOKEN.trim().length>0}}function Ib(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=Br(n).filter(a=>a!=="*"),s=[];for(let a of r){let l=Ee(a);if(!l)throw new Error(`Invalid Telegram allow entry: ${a}`);s.push(l)}let i=$();i.allowFrom=o.sort(),i.telegramAllowFrom=[...new Set(s)].sort(),_e(i)}function Ob(e){return typeof e=="string"?e:typeof e=="boolean"||typeof e=="number"?String(e):JSON.stringify(e)}async function dm(e){let t=Mb(),{meta:n}=e;um(e.port);let r=xb.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=Wa(n.secret);s.statusCode=302,s.setHeader("Location","/"),s.setHeader("Set-Cookie",Ua(f)),s.end();return}}if(a.startsWith("/api/")){let m=o.headers.cookie,f=sm(n.secret,m);if(o.method==="POST"&&a==="/api/session"){let h=await ja(o),g=typeof h?.token=="string"?h.token.trim():"";if(!g||g!==n.token){s.statusCode=401,s.setHeader("Content-Type",ie),s.end(JSON.stringify({ok:!1,error:"Invalid token."}));return}let y=Wa(n.secret);s.statusCode=200,s.setHeader("Content-Type",ie),s.setHeader("Set-Cookie",Ua(y)),s.end(JSON.stringify({ok:!0}));return}if(o.method==="GET"&&a==="/api/me"){s.statusCode=f?200:401,s.setHeader("Content-Type",ie),s.end(JSON.stringify({ok:f}));return}if(!f){s.statusCode=401,s.setHeader("Content-Type",ie),s.end(JSON.stringify({ok:!1,error:"Unauthorized."}));return}if(o.method==="GET"&&a==="/api/status"){s.statusCode=200,s.setHeader("Content-Type",ie),s.end(JSON.stringify({ok:!0,...Ab()}));return}if(o.method==="GET"&&a==="/api/config"){s.statusCode=200,s.setHeader("Content-Type",ie),s.end(JSON.stringify({ok:!0,config:Ga($())}));return}if(o.method==="PUT"&&a==="/api/config"){let h=await ja(o);if(!h||typeof h!="object"){s.statusCode=400,s.setHeader("Content-Type",ie),s.end(JSON.stringify({ok:!1,error:"Expected JSON object."}));return}let g=h;for(let[y,b]of Object.entries(g))if(b!==void 0&&!(y==="allowFrom"||y==="telegramAllowFrom")){if(y==="telegramBotToken"){let v=typeof b=="string"?b.trim():"";if(!v||v==="(set)"||v.includes("\u2026"))continue;if(!dt(v))throw new Error("Invalid Telegram bot token format.");uo("telegramBotToken",v);continue}if(!Pb(y))throw new Error(`Unknown or unsupported config key: ${y}`);uo(y,Ob(b))}if("allowFrom"in g||"telegramAllowFrom"in g){let y=$();Ib("allowFrom"in g?g.allowFrom:y.allowFrom,"telegramAllowFrom"in g?g.telegramAllowFrom:y.telegramAllowFrom)}s.statusCode=200,s.setHeader("Content-Type",ie),s.end(JSON.stringify({ok:!0,config:Ga($())}));return}if(o.method==="POST"&&a==="/api/logout"){s.statusCode=200,s.setHeader("Content-Type",ie),s.setHeader("Set-Cookie",im()),s.end(JSON.stringify({ok:!0}));return}if(o.method==="POST"&&a==="/api/shutdown"){s.statusCode=200,s.setHeader("Content-Type",ie),s.end(JSON.stringify({ok:!0})),setImmediate(()=>{Or.requestCancel(),Cb()});return}if(o.method==="POST"&&a==="/api/gateway/start"){if(Ns()){s.statusCode=409,s.setHeader("Content-Type",ie),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 h=Ms();if(!h.ok){s.statusCode=400,s.setHeader("Content-Type",ie),s.end(JSON.stringify({ok:!1,error:h.message}));return}let g=Ne,y=qo(g);if(!y.ok){s.statusCode=500,s.setHeader("Content-Type",ie),s.end(JSON.stringify({ok:!1,error:y.message}));return}s.statusCode=200,s.setHeader("Content-Type",ie),s.end(JSON.stringify({ok:!0,pid:y.pid,logFile:g}));return}if(o.method==="POST"&&a==="/api/gateway/stop"){let h=Yo();switch(h.outcome){case"no_pidfile":s.statusCode=400,s.setHeader("Content-Type",ie),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",ie),s.end(JSON.stringify({ok:!1,error:"Invalid pidfile (removed)."}));return;case"stale_cleaned":s.statusCode=200,s.setHeader("Content-Type",ie),s.end(JSON.stringify({ok:!0,stale:!0,pid:h.pid,message:"Process was not running; stale pidfile removed."}));return;case"sent_signal":s.statusCode=200,s.setHeader("Content-Type",ie),s.end(JSON.stringify({ok:!0,pid:h.pid}));return;case"taskkill_ok":s.statusCode=200,s.setHeader("Content-Type",ie),s.end(JSON.stringify({ok:!0,pid:h.pid,taskkill:!0}));return;case"failed":s.statusCode=500,s.setHeader("Content-Type",ie),s.end(JSON.stringify({ok:!1,error:h.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
342
+ `)}`}:{ok:!0}}it();G();it();import Ls from"node:fs";import Ns from"node:path";me();import{spawn as rb}from"node:child_process";import Up from"node:fs";import{stdout as ob}from"node:process";it();G();rn();var Fa=["tunnelRelayUrl","platformToken","platformDeviceId"],Dp=[{label:"Platform (attached gateway + tunnel)",keys:["tunnelRelayUrl","platformToken","platformDeviceId"]},{label:"Tunnel",keys:["tunnelEnabled","tunnelMaxActive"]},{label:"Gateway",keys:["gatewayMode","commandPrefix","shell"]},{label:"Cluster",keys:["clusterEnabled","clusterRole","clusterLabel","clusterSenderBindings"]},{label:"Telegram",keys:["telegramBotToken"]},{label:"Apps",keys:["appsCols","appsRows","appsFlushMs","appsMinIntervalMs","appsMaxFlushBytes","appsMaxSessions","appsMaxSessionsTotal","appsMaxWaChars","appsLogTailLines","appsSubmitDelayMs","appsClearInput","appsClearInputDelayMs","appsClearInputSequence","appsSkipClearOnPasswordPrompt","appsPasswordPromptHint"]},{label:"Files",keys:["fileSendMaxBytes","fileReceiveMaxBytes","fileInboxSubdir","fileReceiveRootMode","fileReceiveRootPath"]},{label:"Recipes",keys:["recipesAllowDangerousBuiltins","recipesMaxTaskChars","recipesMacroDefaultCommand"]},{label:"Sync / jobs",keys:["syncTimeoutMs","syncMaxBytes","jobLogTailLines"]},{label:"Service / updates",keys:["serviceInstallFromChat","updateCheckEnabled","updateCheckIntervalMs","updateCheckPackageName","updateInfoUrl"]},{label:"Chat LLM fallback",keys:["chatLlmFallbackEnabled","chatLlmShellCommand","chatLlmTimeoutMs","chatLlmMaxInputChars","chatLlmMaxOutputChars","chatLlmNeedsTty","chatLlmWorkDir"]}];function Is(e){let t=[J(e,"omnish config"),w(e,"Manage ~/.omnish/config.json (env vars still override for platform credentials)."),"",J(e,"Usage:"),` ${S(e,"omnish config add <key> <value> [key value ...]")}`,` ${S(e,"omnish config show <key>[,<key>|*]")}`,` ${S(e,"omnish config edit")}`,` ${S(e,"omnish config edit <key> <value>")}`,` ${S(e,"omnish config delete <key>[,<key>]")}`,"",w(e,"Platform aliases: platform_url \u2192 tunnelRelayUrl, platform_token \u2192 platformToken,"),w(e," platform_device_id \u2192 platformDeviceId"),` ${S(e,"omnish config show platform")} ${w(e,"\u2014 attached-mode diagnostics (URL, token, devices)")}`,w(e," Full setup: omnish help platform"),w(e,"add sets/overwrites scalar keys (not array append). show * lists all keys."),""];console.log(t.join(`
343
+ `))}function Hp(e){return e.split(/[,\s]+/).map(t=>t.trim()).filter(Boolean)}function Wa(e){if(e.length===0)return[];let t=e.join(" ");if(t.includes(",")){let n=[];for(let r of t.split(",")){let o=r.trim();if(!o)continue;let s=o.split(/\s+/);if(s.length<2)throw new Error(`Invalid pair "${o}" \u2014 expected "key value"`);n.push({key:s[0],value:s.slice(1).join(" ")})}return n}if(e.length===2)return[{key:e[0],value:e[1]}];if(e.length%2===0){let n=[];for(let r=0;r<e.length;r+=2)n.push({key:e[r],value:e[r+1]});return n}return[{key:e[0],value:e.slice(1).join(" ")}]}function As(e,t){if(e==="clusterSenderBindings")return JSON.stringify(t.clusterSenderBindings);let n=String(t[e]??"");return e==="platformToken"||e==="telegramBotToken"?sn(e,n):n}function Bp(e){return e==="tunnelRelayUrl"?{value:Rr(),source:qn()}:e==="platformToken"?{value:sn(e,na()),source:zn()}:e==="platformDeviceId"?{value:(ra()??"")||"(empty)",source:oa()}:null}function sb(e){if(e!=="platformToken"&&e!=="tunnelRelayUrl")return;let n=$().platformToken.trim()||$t()?.token?.trim()||"";n&&ht({token:n,relayUrl:Rr()})}async function ib(){let e=process.env.VISUAL?.trim()||process.env.EDITOR?.trim()||(process.platform==="win32"?"notepad":"nano"),t=Up.readFileSync(_,"utf8");await new Promise((n,r)=>{let o=rb(e,[_],{stdio:"inherit"});o.on("error",r),o.on("exit",s=>{s===0?n():r(new Error(`Editor exited with code ${s??1}`))})});try{$()}catch(n){throw Up.writeFileSync(_,t,{mode:384}),new Error(`Invalid config after edit: ${n instanceof Error?n.message:String(n)}`)}}async function jp(e){let t=ob,n=process.stderr,r=(e[0]??"").trim().toLowerCase(),o=e.slice(1);if(!r||r==="help"||r==="--help"||r==="-h"){Is(t);return}if(r==="add"||r==="edit"){try{if(r==="edit"&&o.length===0){await ib(),console.log(B(t,`Updated ${_}`));return}let s=Wa(o);if(s.length===0){console.error(C(n,"Expected at least one key/value pair.")),process.exitCode=1;return}for(let{key:i,value:a}of s){let l=go(i);if(!l){console.error(C(n,`Unknown key "${i}". Try: ${Gc().slice(0,8).join(", ")}\u2026 (omnish config show *)`)),process.exitCode=1;return}Pn(l,a),sb(l);let d=$(),u=l==="tunnelRelayUrl"?d.tunnelRelayUrl:Fa.includes(l)?sn(l,String(d[l]??"")):As(l,d);console.log(B(t,`${i} \u2192 ${u}`))}console.log(B(t,_))}catch(s){console.error(C(n,s instanceof Error?s.message:String(s))),process.exitCode=1}return}if(r==="show"){let s=o.join(" ").trim()||"*";if(s==="*"){let l=$(),d=[J(t,"Config"),w(t,_),""];for(let m of Dp){d.push(J(t,m.label));for(let f of m.keys){let h=As(f,l),g=Bp(f);g&&Fa.includes(f)?d.push(` ${S(t,f)}: ${h} ${w(t,`(effective: ${g.value}, source: ${g.source})`)}`):d.push(` ${S(t,f)}: ${h}`)}d.push("")}let u=new Set(Dp.flatMap(m=>m.keys)),c=bi.filter(m=>!u.has(m));if(c.length>0){d.push(J(t,"Other"));for(let m of c)d.push(` ${S(t,m)}: ${As(m,l)}`)}console.log(d.join(`
344
+ `));return}let i=Hp(s),a=$();for(let l of i){let d=go(l);if(!d){console.error(C(n,`Unknown key "${l}".`)),process.exitCode=1;return}let u=As(d,a),c=Bp(d);c&&Fa.includes(d)?console.log(`${l}: ${u} (effective: ${c.value}, source: ${c.source})`):console.log(`${l}: ${u}`)}return}if(r==="delete"){let s=o.join(" ").trim();if(!s){console.error(C(n,"Expected at least one key to delete.")),process.exitCode=1;return}if(s==="*"){console.error(C(n,'Refusing "delete *". List keys explicitly.')),process.exitCode=1;return}try{for(let i of Hp(s)){let a=go(i);if(!a){console.error(C(n,`Unknown key "${i}".`)),process.exitCode=1;return}Jc(a),console.log(B(t,`cleared ${i}`))}console.log(B(t,_))}catch(i){console.error(C(n,i instanceof Error?i.message:String(i))),process.exitCode=1}return}console.error(C(n,`Unknown subcommand "${r}".`)),Is(t),process.exitCode=1}async function Gp(e){let t=se(),n=[];if(n.push(J(e,"platform")),!t){n.push(` ${w(e,"attached:")} ${fe(e,"no")} \u2014 set platform_url + platform_token (omnish config add) or env`);let a=qn(),l=zn();return(a==="env"||l==="env")&&n.push(` ${w(e,"note:")} ${V(e,"partial env override (need URL + token)")}`),n}let r=qn(),o=zn(),s=oa(),i=r==="env"||o==="env"||s==="env"?` ${w(e,"(env overrides config)")}`:"";n.push(` ${w(e,"attached:")} ${S(e,"yes")}${i}`),n.push(` ${w(e,"url:")} ${S(e,t.platformUrl)} ${V(e,`[${r}]`)}`),n.push(` ${w(e,"token:")} ${S(e,sn("platformToken",t.token))} ${V(e,`[${o}]`)}`),t.deviceId&&n.push(` ${w(e,"device:")} ${S(e,t.deviceId)} ${V(e,`[${s}]`)}`);try{let{fetchPlatformAccount:a}=await Promise.resolve().then(()=>(Cr(),vd)),l=await a(t);n.push(` ${w(e,"gatewayMode:")} ${S(e,l.gatewayMode)} ${V(e,"(platform)")}`);let d=c=>{let m=l.connectors[c];return m?m.linked?S(e,"linked"):V(e,m.status):fe(e,"idle")};n.push(` ${w(e,"whatsapp:")} ${d("whatsapp")}`),n.push(` ${w(e,"telegram:")} ${d("telegram")}`);let u=l.routing.onlineCount;n.push(` ${w(e,"routing:")} default=${l.routing.defaultDeviceId??V(e,"(none)")} online=${u}`),t.deviceId&&l.routing.defaultDeviceId&&t.deviceId!==l.routing.defaultDeviceId&&n.push(` ${w(e,"warn:")} ${fe(e,"platform_device_id \u2260 platform defaultDeviceId \u2014 run omnish run or set default on dashboard")}`),u===0&&n.push(` ${w(e,"warn:")} ${fe(e,"no device online on platform")}`)}catch{n.push(` ${w(e,"account:")} ${V(e,"(could not fetch /v1/me \u2014 check token and URL)")}`)}return n}Cr();it();function Kp(){let e=se();return e||(console.error(C(process.stderr,"Set platform_url + platform_token (omnish config add) or OMNISH_PLATFORM_URL + OMNISH_TOKEN.")),process.exitCode=1,null)}function ab(e){let t=e.trim();return t?t.startsWith("wa:")?{kind:"wa",value:t.slice(3).trim()}:t.startsWith("tg:")?{kind:"tg",value:t.slice(3).trim()}:/^\+?\d{8,}$/.test(t.replace(/\s/g,""))?{kind:"wa",value:t}:/^\d+$/.test(t)?{kind:"tg",value:t}:null:null}function Jp(e){return e.replace(/\D/g,"")}async function zp(){let e=Kp();if(!e)return;let t=await hn(e),n=t.connectors.whatsapp,r=t.connectors.telegram;console.log(B(process.stdout,"Platform account")),console.log(` gatewayMode: ${t.gatewayMode}`),console.log(` whatsapp: ${n?.linked?"linked":n?.status??"idle"} (allowlist: ${t.allowFrom.length})`);let o=r?.tokenConfigured?"token saved":"no token";console.log(` telegram: ${r?.linked?"linked":r?.status??"idle"} (${o}, allowlist: ${t.telegramAllowFrom.length})`),console.log(` defaultDeviceId: ${t.defaultDeviceId??"\u2014"}`),console.log(` online devices: ${t.routing.onlineCount}`),t.allowFrom.length&&console.log(` allowFrom: ${t.allowFrom.map(s=>`+${s}`).join(", ")}`),t.telegramAllowFrom.length&&console.log(` telegramAllowFrom: ${t.telegramAllowFrom.join(", ")}`)}async function qp(e){let t=Kp();if(!t)return;let n=(e[0]??"").toLowerCase();if(!n||n==="-h"||n==="--help"){console.log(["Usage:"," omnish platform allow list"," omnish platform allow add wa:+15551234567 tg:123456789"," omnish platform allow set --wa +1555,... --tg 123,...",""].join(`
345
+ `));return}if(n==="list"){let r=await hn(t);console.log(B(process.stdout,"Platform allowlists")),console.log(` WhatsApp (${r.allowFrom.length}): ${r.allowFrom.length?r.allowFrom.map(o=>`+${o}`).join(", "):"(empty \u2014 any sender)"}`),console.log(` Telegram (${r.telegramAllowFrom.length}): ${r.telegramAllowFrom.length?r.telegramAllowFrom.join(", "):"(empty \u2014 any sender)"}`);return}if(n==="add"){let r=await hn(t),o=[...r.allowFrom],s=[...r.telegramAllowFrom];for(let a of e.slice(1)){let l=ab(a);if(!l){console.error(C(process.stderr,`Unrecognized entry: ${a}`)),process.exitCode=1;return}if(l.kind==="wa"){let d=Jp(l.value);d&&!o.includes(d)&&o.push(d)}else{let d=l.value.replace(/\D/g,"");d&&!s.includes(d)&&s.push(d)}}let i=await ss(t,{allowFrom:o,telegramAllowFrom:s});console.log(B(process.stdout,`Allowlists updated (${i.allowFrom.length} WhatsApp, ${i.telegramAllowFrom.length} Telegram).`));return}if(n==="set"){let r,o;for(let a=1;a<e.length;a++){let l=e[a];l==="--wa"&&e[a+1]?r=e[++a].split(",").map(d=>Jp(d.trim())).filter(Boolean):l==="--tg"&&e[a+1]&&(o=e[++a].split(",").map(d=>d.trim().replace(/^tg:/i,"").replace(/\D/g,"")).filter(Boolean))}if(r===void 0&&o===void 0){console.error(C(process.stderr,"Use --wa and/or --tg with comma-separated values.")),process.exitCode=1;return}let s={};r!==void 0&&(s.allowFrom=r),o!==void 0&&(s.telegramAllowFrom=o);let i=await ss(t,s);console.log(B(process.stdout,`Allowlists set (${i.allowFrom.length} WhatsApp, ${i.telegramAllowFrom.length} Telegram).`));return}console.error(C(process.stderr,`Unknown allow subcommand: ${e[0]}`)),process.exitCode=1}async function Yp(e){let t="",n="",r="";for(let o=0;o<e.length;o++){let s=e[o];(s==="--url"||s==="-u")&&e[o+1]?t=e[++o].trim():(s==="--token"||s==="-t")&&e[o+1]?n=e[++o].trim():s==="--device-id"&&e[o+1]&&(r=e[++o].trim())}if(!t||!n){let o=Wa(e);for(let s of o)s.key==="platform_url"&&(t=s.value),s.key==="platform_token"&&(n=s.value),s.key==="platform_device_id"&&(r=s.value)}if(!t||!n){console.error(C(process.stderr,"Usage: omnish platform login --url <url> --token <token> [--device-id <id>]")),process.exitCode=1;return}Pn("tunnelRelayUrl",t.replace(/\/$/,"")),Pn("platformToken",n),r&&Pn("platformDeviceId",r),console.log(B(process.stdout,"Platform credentials saved to config.json.")),console.log(" Run: omnish platform status && omnish run")}import lb from"qrcode-terminal";it();var cb=1e3,ub=120;function Qp(){let e=se();return e||(console.error(C(process.stderr,"Set platform_url + platform_token (omnish config add) or OMNISH_PLATFORM_URL + OMNISH_TOKEN.")),process.exitCode=1,null)}function Da(e){let t=e.replace(/\/$/,"");return/^https?:\/\//i.test(t)?t:`http://${t}`}async function Vp(e){let t=`${Da(e.platformUrl)}/v1/connectors/whatsapp/status`,n=await fetch(t,{headers:{Authorization:`Bearer ${e.token}`}}),r=await n.json().catch(()=>({}));if(!n.ok)throw new Error(r.error||`HTTP ${n.status}`);return r}async function db(e){let t=await fetch(`${Da(e.platformUrl)}/v1/connectors/whatsapp/start`,{method:"POST",headers:{"content-type":"application/json",Authorization:`Bearer ${e.token}`},body:JSON.stringify({})}),n=await t.json().catch(()=>({}));if(!t.ok)throw new Error(n.error||`HTTP ${t.status}`);return n}function Xp(e){let t=process.stdout,n=w(t,"\xB7".repeat(42));console.log(V(t,"Scan with WhatsApp \u2192 Linked devices")),console.log(n),lb.generate(e,{small:!0}),console.log(n)}var Os="",Ua=!1;function pb(e){let t=e.status??"";return t==="qr"||t==="connecting"||t==="reconnecting"||t==="pairing_restart"}async function mb(e){for(let t=0;t<ub;t++){let n=await Vp(e);if(n.status==="linked"||n.linked)return n;if(n.status==="pairing_restart"&&!Ua&&(Ua=!0,console.log(w(process.stdout,"Finishing WhatsApp link after scan (server restart) \u2014 wait a few seconds\u2026"))),n.qr&&n.qr!==Os&&(Os=n.qr,Xp(n.qr)),!pb(n))throw new Error(n.error||n.statusMessage||`Unexpected status: ${n.status}`);await new Promise(r=>setTimeout(r,cb))}throw new Error("Timed out waiting for WhatsApp to link (scan the QR within ~2 minutes).")}async function Zp(){let e=Qp();if(!e)return;let t=await Vp(e);if(t.status==="linked"||t.linked){console.log(B(process.stdout,"WhatsApp already linked on the platform.")),t.linkedPhoneE164&&(console.log(` Phone: +${t.linkedPhoneE164}`),console.log(` Hint: omnish platform allow add wa:+${t.linkedPhoneE164}`));return}console.log(B(process.stdout,"Starting WhatsApp pairing on the platform\u2026")),Ua=!1,Os="",t=await db(e),t.qr&&(Xp(t.qr),Os=t.qr),t=await mb(e),console.log(B(process.stdout,"WhatsApp linked on the platform.")),t.linkedPhoneE164&&(console.log(` Phone: +${t.linkedPhoneE164}`),console.log(` Next: omnish platform allow add wa:+${t.linkedPhoneE164}`),console.log(" Then: omnish run"))}async function em(){let e=Qp();if(!e)return;let t=await fetch(`${Da(e.platformUrl)}/v1/connectors/whatsapp/unlink`,{method:"POST",headers:{"content-type":"application/json",Authorization:`Bearer ${e.token}`},body:JSON.stringify({})}),n=await t.json().catch(()=>({}));if(!t.ok){console.error(C(process.stderr,n.error||`HTTP ${t.status}`)),process.exitCode=1;return}console.log(B(process.stdout,`WhatsApp unlinked (status: ${n.status??"idle"}).`))}import fb from"ws";function tm(e,t,n){return new Promise(r=>{let o=new fb(e,{headers:{Authorization:`Bearer ${t}`}}),s=setTimeout(()=>{o.terminate(),r({ok:!1,message:"timeout"})},n);o.once("open",()=>{clearTimeout(s),o.close(),r({ok:!0})}),o.once("unexpected-response",(i,a)=>{clearTimeout(s),r({ok:!1,status:a.statusCode,message:`HTTP ${a.statusCode} ${a.statusMessage}`})}),o.once("error",i=>{clearTimeout(s),r({ok:!1,message:String(i?.message??i)})})})}async function nm(e,t,n=12e3){let r=e.replace(/\/$/,""),o=Yn(r),s=await tm(o,t,n),i,a,l="";for(let d of as(r)){let u=await tm(d,t,n);if(u.ok)return i=new URL(d).pathname,{ok:!0,controlWsOk:s.ok,deviceWsOk:!0,deviceWsPath:i};a=u.status,l=u.message}return a===400?{ok:!1,controlWsOk:s.ok,deviceWsOk:!1,deviceWsStatus:400,error:"Device WebSocket returned HTTP 400 on /platform/device and could not use /control/device. Redeploy the latest relay image (adds /control/device), or route /platform/* to port 8788 in Caddy/EasyPanel."}:{ok:!1,controlWsOk:s.ok,deviceWsOk:!1,deviceWsStatus:a,error:`Device WebSocket failed: ${l}`}}var hb=400,gb=8*1024*1024,yb=2*1024*1024;function wb(e){let t=ne,n=!1,r=!1;for(let o=0;o<e.length;o++){let s=e[o];s==="-h"||s==="--help"?r=!0:s==="--force"?n=!0:(s==="--auth-dir"||s==="--authDir")&&e[o+1]&&(t=Ns.resolve(e[++o]))}return{authDir:t,force:n,help:r}}function bb(e){let t={},n=0,r=0;function o(s,i){let a;try{a=Ls.readdirSync(i,{withFileTypes:!0})}catch{return}for(let l of a){let d=l.name;if(d==="."||d==="..")continue;let u=Ns.join(i,d),m=(s?`${s}/${d}`:d).split(Ns.sep).join("/");if(!l.isSymbolicLink()){if(l.isDirectory())o(m,u);else if(l.isFile()){let f=Ls.readFileSync(u);if(f.length>yb)throw new Error(`file too large: ${m}`);if(n+=f.length,n>gb)throw new Error("total auth size exceeds limit (8 MiB)");if(r+=1,r>hb)throw new Error("too many files (max 400)");t[m]=f.toString("base64")}}}}return o("",e),t}function _s(e){console.log([ue(e,"omnish platform"),w(e,"Attached mode: messengers on the hosted platform; shell on this machine (omnish run)."),"",J(e,"When to use"),w(e," Link WhatsApp/Telegram once on the platform dashboard. Run omnish run on laptops, servers, or containers without local Baileys."),"",J(e,"Prerequisites"),w(e," 1. Platform account token (signup/login on relay dashboard)."),w(e," 2. Messengers linked on the platform (dashboard QR or import-whatsapp)."),w(e," 3. allowFrom / telegramAllowFrom on dashboard Allowlists (Telegram: DM bot /id for your user id)."),"",J(e,"Configure"),` ${S(e,"omnish config add platform_url <url> platform_token <token>")}`,w(e," Optional: platform_device_id <id>"),w(e," Env overrides: OMNISH_PLATFORM_URL, OMNISH_TOKEN, OMNISH_DEVICE_ID"),w(e," (aliases: OMNISH_COMM_LAYER_URL, OMNISH_DEVICE_TOKEN, OMNISH_TUNNEL_*)"),"",J(e,"Workflow"),` ${S(e,"omnish platform login --url <url> --token <t>")} ${w(e,"\u2014 save credentials (or config add)")}`,` ${S(e,"omnish platform status")} ${w(e,"\u2014 account, connectors, allowlists")}`,` ${S(e,"omnish platform allow add tg:<id>")} ${w(e,"\u2014 or dashboard + bot /id")}`,` ${S(e,"omnish platform probe")} ${w(e,"\u2014 test WebSocket routing")}`,` ${S(e,"omnish run")} ${w(e,"\u2014 attach device; expect 'omnish attached to platform'")}`,"",J(e,"Subcommands"),S(e,"omnish platform login --url <url> --token <token> [--device-id <id>]"),w(e," Save platform URL and token to config.json."),"",S(e,"omnish platform status"),w(e," Show gatewayMode, connector status, allowlists, online devices."),"",S(e,"omnish platform allow list|add|set"),w(e," Manage platform allowlists (PUT /v1/me/allowlists)."),"",S(e,"omnish platform probe"),w(e," Test control-plane WebSocket routing (diagnose attached omnish run 400 errors)."),"",S(e,"omnish platform link-whatsapp"),w(e," Pair WhatsApp on the platform (terminal QR + poll until linked)."),"",S(e,"omnish platform unlink-whatsapp"),w(e," Remove platform WhatsApp session and auth files."),"",S(e,"omnish platform import-whatsapp [--auth-dir <path>] [--force]"),w(e," Upload local Baileys auth (after omnish link on this host). Use link-whatsapp or dashboard QR when possible."),w(e," Stop local omnish run before import to avoid WhatsApp session conflicts."),"",w(e,"Standalone omnish link on this host is not used for platform WhatsApp when attached."),w(e,"Docs: docs/guides/platform-reference.md (full API/CLI)"),w(e," docs/guides/platform-attached-mode.md (walkthrough)"),""].join(`
346
+ `))}async function kb(e){let{authDir:t,force:n,help:r}=wb(e);if(r){console.log(["Usage: omnish platform import-whatsapp [--auth-dir <path>] [--force]","","Requires OMNISH_PLATFORM_URL and OMNISH_TOKEN.","Default auth directory: ~/.omnish/auth (Baileys useMultiFileAuthState).","Stop `omnish run` on this machine before importing to avoid WhatsApp session conflicts.",""].join(`
347
+ `));return}let o=se();if(!o){console.error(C(process.stderr,"Set OMNISH_PLATFORM_URL (control plane URL) and OMNISH_TOKEN (from dashboard signup/login).")),process.exitCode=1;return}if(!Ls.existsSync(t)){console.error(C(process.stderr,`Auth directory not found: ${t}`)),process.exitCode=1;return}te();let s=Ns.join(t,"creds.json");if(!Ls.existsSync(s)){console.error(C(process.stderr,`No creds.json in ${t} \u2014 link WhatsApp locally first (omnish link).`)),process.exitCode=1;return}let i;try{i=bb(t)}catch(f){console.error(C(process.stderr,String(f))),process.exitCode=1;return}if(Object.keys(i).length===0){console.error(C(process.stderr,"No files collected (empty auth directory?).")),process.exitCode=1;return}let a=o.platformUrl.replace(/\/$/,""),d=`${/^https?:\/\//i.test(a)?a:`http://${a}`}/v1/connectors/whatsapp/import`,u=await fetch(d,{method:"POST",headers:{"content-type":"application/json",Authorization:`Bearer ${o.token}`},body:JSON.stringify({files:i,force:n})}),c=await u.json().catch(()=>({}));if(!u.ok){console.error(C(process.stderr,c.error||`HTTP ${u.status}`)),process.exitCode=1;return}let m=c.qr?" QR emitted \u2014 open the platform dashboard if you need to scan.":"";console.log(B(process.stdout,`Uploaded Baileys auth. Platform connector status: ${c.status??"unknown"}.${m}`))}async function Sb(){let e=process.stdout,t=process.stderr,n=se();if(!n){console.error(C(t,"Set platform_url + platform_token (omnish config add) or OMNISH_PLATFORM_URL + OMNISH_TOKEN.")),process.exitCode=1;return}console.log(`${ue(e,"Platform probe")} ${w(e,n.platformUrl)}`);let r=await nm(n.platformUrl,n.token);console.log(` ${w(e,"wss /control:")} ${r.controlWsOk?S(e,"ok"):fe(e,"fail")}`);for(let o of["/platform/device","/control/device"]){if(r.ok&&r.deviceWsPath&&r.deviceWsPath!==o){console.log(` ${w(e,`wss ${o}:`)} ${V(e,"skipped (fallback used)")}`);continue}if(r.ok&&r.deviceWsPath===o){console.log(` ${w(e,`wss ${o}:`)} ${S(e,"ok")}`);continue}!r.ok&&o==="/platform/device"&&console.log(` ${w(e,`wss ${o}:`)} ${fe(e,"fail")}${r.deviceWsStatus?` (HTTP ${r.deviceWsStatus})`:""}`),!r.ok&&o==="/control/device"&&console.log(` ${w(e,`wss ${o}:`)} ${fe(e,"fail")}`)}if(r.ok){r.deviceWsPath==="/control/device"&&console.log(w(e," Using /control/device fallback (/platform/* not on control port \u2014 redeploy Caddy when convenient).")),console.log(B(e,"Attached omnish run should be able to connect."));return}console.error(C(t,r.error??"Platform probe failed.")),r.controlWsOk&&!r.deviceWsOk&&console.error(C(t,"Fix: redeploy the latest tunnel-relay image (enables wss /control/device), update omnish CLI, then retry. Also route /platform/* to 8788 when you can (Caddyfile.standalone).")),process.exitCode=1}async function rm(e){let t=(e[0]??"").toLowerCase();if(!t||t==="-h"||t==="--help"){_s(process.stdout);return}if(t==="login"){await Yp(e.slice(1));return}if(t==="status"){await zp();return}if(t==="allow"){await qp(e.slice(1));return}if(t==="probe"){await Sb();return}if(t==="link-whatsapp"){await Zp();return}if(t==="unlink-whatsapp"){await em();return}if(t==="import-whatsapp"){await kb(e.slice(1));return}console.error(C(process.stderr,`Unknown platform subcommand: ${e[0]}`)),_s(process.stderr),process.exitCode=1}G();import im from"node:crypto";import bn from"node:fs";import am from"node:path";function vb(){return im.randomBytes(24).toString("hex")}function om(){return im.randomBytes(32).toString("hex")}function sm(e){try{let t=bn.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 xb(){let e=sm(ut);if(e)return e;if(e=sm(rr),e){j(am.dirname(ut)),bn.writeFileSync(ut,JSON.stringify(e,null,2)+`
348
+ `,{mode:384});try{bn.unlinkSync(rr)}catch{}return e}return null}function lm(e){j(am.dirname(ut));let t=xb(),n=(e??"").trim();if(n){let o={token:n,secret:t?.secret??om()};bn.writeFileSync(ut,JSON.stringify(o,null,2)+`
349
+ `,{mode:384});try{bn.existsSync(rr)&&bn.unlinkSync(rr)}catch{}return o}if(t)return t;let r={token:vb(),secret:om()};return bn.writeFileSync(ut,JSON.stringify(r,null,2)+`
350
+ `,{mode:384}),r}me();import Zt from"node:fs";import Eb from"node:http";import Ae from"node:path";import at from"node:process";import{fileURLToPath as Pb}from"node:url";import Ab from"node:os";qe();G();import Ha from"node:crypto";var Ba="omnish_cfg_sess",cm=7*24*60*60*1e3;function ja(e){let t=Date.now()+cm,n=Buffer.from(JSON.stringify({exp:t}),"utf8").toString("base64url"),r=Ha.createHmac("sha256",e).update(n).digest("hex");return`${n}.${r}`}function um(e,t){let n=$b(t??"")[Ba];if(!n||!n.includes("."))return!1;let r=n.lastIndexOf("."),o=n.slice(0,r),s=n.slice(r+1),i=Ha.createHmac("sha256",e).update(o).digest("hex");try{let a=Buffer.from(s,"hex"),l=Buffer.from(i,"hex");if(a.length!==l.length||!Ha.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 Ga(e){let t=Math.floor(cm/1e3);return`${Ba}=${e}; HttpOnly; Path=/; SameSite=Lax; Max-Age=${t}`}function dm(){return`${Ba}=; HttpOnly; Path=/; SameSite=Lax; Max-Age=0`}function $b(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}G();import Ja from"node:fs";import Fs from"node:process";function pm(e){try{return Fs.kill(e,0),!0}catch{return!1}}function mm(e){let t=Date.now()+e;for(;Date.now()<t;);}function Rb(){try{let e=Ja.readFileSync(Hr,"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 fm(e){Ja.writeFileSync(Hr,`${JSON.stringify(e)}
351
+ `,{mode:384})}function _r(){try{Ja.unlinkSync(Hr)}catch{}}function hm(e){let t=Rb();if(t&&t.port===e&&t.pid!==Fs.pid){if(!pm(t.pid)){_r();return}try{Fs.kill(t.pid,"SIGTERM")}catch{}if(mm(350),pm(t.pid)){try{Fs.kill(t.pid,"SIGKILL")}catch{}mm(100)}_r()}}me();G();import Ka from"node:fs";import Cb from"node:process";function Mb(e){try{return Cb.kill(e,0),!0}catch{return!1}}function Tb(){try{let e=Ka.readFileSync(ce,"utf8").trim(),t=Number.parseInt(e,10);return Number.isFinite(t)&&t>0?t:null}catch{return null}}function Ws(){let e=Tb();return e===null?!1:Mb(e)}var za=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&&(jn(this.currentSock),this.currentSock=null)}beginPairing(t){if(this.busy)throw new Error("Pairing already in progress.");if(Ws())throw new Error("Gateway appears to be running (gateway.pid). Stop `omnish run` or your service before pairing from the browser.");if(!t.force&&nt())throw new Error("WhatsApp session already linked. Use \u201CReplace session\u201D or run `omnish link --force` from the CLI.");te(),t.force&&(Ka.rmSync(ne,{recursive:!0,force:!0}),Ka.mkdirSync(ne,{recursive:!0,mode:448})),this.busy=!0,this.abort=new AbortController;let n=this.abort.signal;(async()=>{try{await dd({authDir:ne,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}})()}},Fr=new za;var ae="application/json; charset=utf-8",Wr=null;function Ib(){_r();let e=Wr;Wr=null,e?e.close(()=>{at.exit(0)}):at.exit(0),setTimeout(()=>at.exit(0),4e3).unref()}function Ob(){let e=at.env.OMNISH_CONFIG_UI_STATIC?.trim(),t=at.env.OMNISH_UI_STATIC?.trim()||e;if(t&&Zt.existsSync(Ae.join(t,"index.html")))return t;let n=Ae.dirname(Pb(import.meta.url)),r=Ae.join(n,"ui");if(Zt.existsSync(Ae.join(r,"index.html")))return r;let o=Ae.join(n,"..","..","dist","ui");if(Zt.existsSync(Ae.join(o,"index.html")))return o;let s=Ae.join(at.cwd(),"dist","ui");if(Zt.existsSync(Ae.join(s,"index.html")))return s;throw new Error("omnish ui static files not found (expected dist/ui/index.html). Run `pnpm build`.")}function Lb(e){let t=Ae.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 Nb(e,t){let r=decodeURIComponent(t.split("?")[0]??"").replace(/^\/+/,""),o=Ae.normalize(Ae.join(e,r));return!o.startsWith(Ae.normalize(e+Ae.sep))&&o!==Ae.normalize(e)?null:o}async function qa(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 _b(e){return Gt.includes(e)}function Ya(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:!!ve(e),telegramBotTokenEnvOverride:typeof at.env.TELEGRAM_BOT_TOKEN=="string"&&at.env.TELEGRAM_BOT_TOKEN.trim().length>0}}function Fb(){let e=$();return{version:Ze(),dataDir:H,configPath:_,waAuthDir:ne,whatsappLinked:nt(),gatewayPidHint:Zt.existsSync(Ae.join(H,"gateway.pid")),gatewayRunning:Ws(),gatewayLogFile:Ne,gatewayMode:e.gatewayMode,telegramBotTokenMasked:ve(e).length===0?"":Ya(e).telegramBotToken,telegramBotTokenEnvOverride:typeof at.env.TELEGRAM_BOT_TOKEN=="string"&&at.env.TELEGRAM_BOT_TOKEN.trim().length>0}}function Wb(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=Kr(n).filter(a=>a!=="*"),s=[];for(let a of r){let l=Ee(a);if(!l)throw new Error(`Invalid Telegram allow entry: ${a}`);s.push(l)}let i=$();i.allowFrom=o.sort(),i.telegramAllowFrom=[...new Set(s)].sort(),_e(i)}function Ub(e){return typeof e=="string"?e:typeof e=="boolean"||typeof e=="number"?String(e):JSON.stringify(e)}async function gm(e){let t=Ob(),{meta:n}=e;hm(e.port);let r=Eb.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=ja(n.secret);s.statusCode=302,s.setHeader("Location","/"),s.setHeader("Set-Cookie",Ga(f)),s.end();return}}if(a.startsWith("/api/")){let m=o.headers.cookie,f=um(n.secret,m);if(o.method==="POST"&&a==="/api/session"){let h=await qa(o),g=typeof h?.token=="string"?h.token.trim():"";if(!g||g!==n.token){s.statusCode=401,s.setHeader("Content-Type",ae),s.end(JSON.stringify({ok:!1,error:"Invalid token."}));return}let y=ja(n.secret);s.statusCode=200,s.setHeader("Content-Type",ae),s.setHeader("Set-Cookie",Ga(y)),s.end(JSON.stringify({ok:!0}));return}if(o.method==="GET"&&a==="/api/me"){s.statusCode=f?200:401,s.setHeader("Content-Type",ae),s.end(JSON.stringify({ok:f}));return}if(!f){s.statusCode=401,s.setHeader("Content-Type",ae),s.end(JSON.stringify({ok:!1,error:"Unauthorized."}));return}if(o.method==="GET"&&a==="/api/status"){s.statusCode=200,s.setHeader("Content-Type",ae),s.end(JSON.stringify({ok:!0,...Fb()}));return}if(o.method==="GET"&&a==="/api/config"){s.statusCode=200,s.setHeader("Content-Type",ae),s.end(JSON.stringify({ok:!0,config:Ya($())}));return}if(o.method==="PUT"&&a==="/api/config"){let h=await qa(o);if(!h||typeof h!="object"){s.statusCode=400,s.setHeader("Content-Type",ae),s.end(JSON.stringify({ok:!1,error:"Expected JSON object."}));return}let g=h;for(let[y,b]of Object.entries(g))if(b!==void 0&&!(y==="allowFrom"||y==="telegramAllowFrom")){if(y==="telegramBotToken"){let v=typeof b=="string"?b.trim():"";if(!v||v==="(set)"||v.includes("\u2026"))continue;if(!pt(v))throw new Error("Invalid Telegram bot token format.");ho("telegramBotToken",v);continue}if(!_b(y))throw new Error(`Unknown or unsupported config key: ${y}`);ho(y,Ub(b))}if("allowFrom"in g||"telegramAllowFrom"in g){let y=$();Wb("allowFrom"in g?g.allowFrom:y.allowFrom,"telegramAllowFrom"in g?g.telegramAllowFrom:y.telegramAllowFrom)}s.statusCode=200,s.setHeader("Content-Type",ae),s.end(JSON.stringify({ok:!0,config:Ya($())}));return}if(o.method==="POST"&&a==="/api/logout"){s.statusCode=200,s.setHeader("Content-Type",ae),s.setHeader("Set-Cookie",dm()),s.end(JSON.stringify({ok:!0}));return}if(o.method==="POST"&&a==="/api/shutdown"){s.statusCode=200,s.setHeader("Content-Type",ae),s.end(JSON.stringify({ok:!0})),setImmediate(()=>{Fr.requestCancel(),Ib()});return}if(o.method==="POST"&&a==="/api/gateway/start"){if(Ws()){s.statusCode=409,s.setHeader("Content-Type",ae),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 h=Ps();if(!h.ok){s.statusCode=400,s.setHeader("Content-Type",ae),s.end(JSON.stringify({ok:!1,error:h.message}));return}let g=Ne,y=Xo(g);if(!y.ok){s.statusCode=500,s.setHeader("Content-Type",ae),s.end(JSON.stringify({ok:!1,error:y.message}));return}s.statusCode=200,s.setHeader("Content-Type",ae),s.end(JSON.stringify({ok:!0,pid:y.pid,logFile:g}));return}if(o.method==="POST"&&a==="/api/gateway/stop"){let h=Zo();switch(h.outcome){case"no_pidfile":s.statusCode=400,s.setHeader("Content-Type",ae),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",ae),s.end(JSON.stringify({ok:!1,error:"Invalid pidfile (removed)."}));return;case"stale_cleaned":s.statusCode=200,s.setHeader("Content-Type",ae),s.end(JSON.stringify({ok:!0,stale:!0,pid:h.pid,message:"Process was not running; stale pidfile removed."}));return;case"sent_signal":s.statusCode=200,s.setHeader("Content-Type",ae),s.end(JSON.stringify({ok:!0,pid:h.pid}));return;case"taskkill_ok":s.statusCode=200,s.setHeader("Content-Type",ae),s.end(JSON.stringify({ok:!0,pid:h.pid,taskkill:!0}));return;case"failed":s.statusCode=500,s.setHeader("Content-Type",ae),s.end(JSON.stringify({ok:!1,error:h.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
352
352
 
353
- `);let h=Or.subscribe(g=>{s.write(`data: ${JSON.stringify(g)}
353
+ `);let h=Fr.subscribe(g=>{s.write(`data: ${JSON.stringify(g)}
354
354
 
355
- `)});o.on("close",()=>{h()});return}if(o.method==="POST"&&a==="/api/wa/link/start"){let g=(await ja(o))?.force===!0;try{Or.beginPairing({force:g})}catch(y){let b=String(y),v=b.includes("already in progress")||b.includes("Gateway appears")?409:400;s.statusCode=v,s.setHeader("Content-Type",ie),s.end(JSON.stringify({ok:!1,error:b}));return}s.statusCode=202,s.setHeader("Content-Type",ie),s.end(JSON.stringify({ok:!0}));return}if(o.method==="POST"&&a==="/api/wa/link/cancel"){Or.requestCancel(),s.statusCode=200,s.setHeader("Content-Type",ie),s.end(JSON.stringify({ok:!0}));return}s.statusCode=404,s.setHeader("Content-Type",ie),s.end(JSON.stringify({ok:!1,error:"Not found."}));return}let l=a==="/"?"index.html":a.replace(/^\/+/,""),d=Eb(t,l),u=Ae.join(t,"index.html");d&&Vt.existsSync(d)&&Vt.statSync(d).isFile()&&(u=d);let c=Vt.readFileSync(u);s.statusCode=200,s.setHeader("Content-Type",Tb(u)),s.setHeader("Cache-Control",Ae.basename(u)==="index.html"?"no-store":"public, max-age=3600"),s.end(c)}catch(i){s.statusCode=500,s.setHeader("Content-Type",ie),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())}),Lr=r,cm({pid:it.pid,port:e.port,host:e.host,startedAt:new Date().toISOString()}),r.on("close",()=>{Ir(),Lr===r&&(Lr=null)})}function pm(e){let t=Rb.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 Ub from"node:readline";import fm from"node:path";import Te from"node:process";pe();qe();G();import Lb from"node:net";import Nb from"node:fs";function _b(){try{let e=Nb.readFileSync(hn,"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 Nr(e){let t=_b();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)}
356
- `;return new Promise(o=>{let s=!1,i="";function a(d){s||(s=!0,o(d))}let l=Lb.connect({host:t.host,port:t.port},()=>{l.write(r)});l.setTimeout(6e5),l.on("data",d=>{i+=d.toString("utf8");let u=i.indexOf(`
357
- `);if(u>=0){let c=i.slice(0,u).trim();try{let m=JSON.parse(c);m.ok?a(null):a(m.error||"Unknown error from gateway control.")}catch{a("Invalid response from gateway control.")}l.destroy()}}),l.on("error",d=>{a(`Control connection failed: ${String(d)}`)}),l.on("timeout",()=>{l.destroy(),a("Gateway control timed out.")}),l.on("close",()=>{!s&&i.trim()===""&&a("Gateway closed the control connection without a response.")})})}qe();function Fb(e){let t=e.trimStart();if(/^--text=/i.test(t)){let n=t.slice(t.indexOf("=")+1).trimEnd();return n.length>0?n:null}if(/^--text(\s+|$)/i.test(t)){let n=t.replace(/^--text\s*/i,"").trim();return n.length>0?n:null}if(/^-t=/i.test(t)){let n=t.slice(t.indexOf("=")+1).trimEnd();return n.length>0?n:null}if(/^-t(\s+|$)/i.test(t)){let n=t.replace(/^-t\s*/i,"").trim();return n.length>0?n:null}}function Wb(e){let t=e.toLowerCase();if(t==="*"||t==="all")return{channel:"all"};if(t==="wa"||t==="whatsapp"||t==="wa:all"||t==="whatsapp:all")return{channel:"whatsapp-all"};if(t==="tg"||t==="telegram"||t==="tg:all"||t==="telegram:all")return{channel:"telegram-all"};if(t.startsWith("tg:")||t.startsWith("telegram:")){let n=Ee(e);if(!n)return null;let r=Number(n);return Number.isFinite(r)?{channel:"telegram",chatId:r}:null}if(t.startsWith("wa:")){let n=e.slice(3).trim();if(!n)return null;let r=n.split(",").map(o=>ee(o.trim())).filter(o=>!!o);return r.length===0?null:{channel:"whatsapp",e164s:r}}if(e.includes(",")){let n=e.split(",").map(r=>ee(r.trim())).filter(r=>!!r);return n.length===0?null:{channel:"whatsapp",e164s:n}}if(e.startsWith("+")){let n=ee(e);return n?{channel:"whatsapp",e164s:[n]}:null}return null}function mm(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=Wb(o);if(!i)return null;let a=Fb(s);if(a===null)return null;if(a!==void 0)return{...i,mode:"text",body:a};let l=hs(s);if(!l)return null;let{selectorPart:d,caption:u}=l;return{...i,mode:"media",selectorPart:d,caption:u}}function Ja(){return["/sendto wa|tg|* <selectors> [-- caption]","/sendto +E164 or +E164,+E164 <selectors> [-- caption]","/sendto <dest> --text <message> or --text=<msg> or -t <msg>","/sendto tg:<chat_id> <selectors> [-- caption] (compat: single Telegram id)","selectors: file1,file2 or *.mp4 or **/*.mp4 (from current session cwd); a path ./--text sends a file named --text","examples: /sendto wa ./promo.mp4 | /sendto * **/*.mp4 -- Daily clips","examples: /sendto +15551234567 --text Hello | /sendto tg:123 -t=Status OK","examples: /sendto +15550000001,+15550000002 intro.png,deck.pdf -- Launch","Also supports compat aliases: wa:all, whatsapp:all, tg:all, telegram:all.","Requires `omnish run` on this machine."].join(`
358
- `)}G();var Xn="wa:cli:interactive",Db={onPlainTextLlmFallback(e,t){On($(),e,t,async n=>{n.trim()&&console.log(B(Te.stdout,n))})}};function Hb(e){let t=e.trim();if(!t)return null;let n=t.toLowerCase();if(n.startsWith("tg:")||n.startsWith("telegram:")){let s=Ee(t);return s?`tg:${s}`:null}let r=n.startsWith("wa:")?t.slice(3):t,o=ee(r);return o?`wa:${o}`:null}function Bb(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=Hb(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 Ka(e){let t=Te.cwd(),n=[`${ce(e,"omnish i")} ${w(e,"[options]")}`,V(e,"Interactive shell \u2014 same commands as WhatsApp/Telegram chat."),"",J(e,"Usage:"),` ${S(e,"omnish i [options]")}`,` ${S(e,"omnish interactive [options]")}`,"",J(e,"Options:"),...St(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:")} ${S(e,"Full local access like your shell; not gated by inbox allowlist.")}`,`${w(e,"Jobs:")} ${S(e,"/bg and /jobs apply only to this REPL session (not the gateway process).")}`,`${w(e,"Files:")} ${S(e,"Use /sendto for files or --text for plain messages through the gateway; plain /send needs a chat peer.")}`,`${w(e,"Gateway:")} ${S(e,"/reload and /updates require omnish run; /sendto requires omnish run for WA/TG delivery.")}`,"",Ja(),"",`${w(e,"cwd:")} ${S(e,`session starts at ${t} (change with !cd or ${$().commandPrefix}cd).`)}`];console.log(n.join(`
359
- `))}function hm(e,t){let n=new Set,r=new Set;if(e.channel==="whatsapp-all"||e.channel==="all"){let o=new Set;for(let s of t.allowFrom){let i=ee(String(s));i&&!o.has(i)&&(o.add(i),n.add(i))}}if(e.channel==="telegram-all"||e.channel==="all")for(let o of t.telegramAllowFrom){let s=Ee(String(o));if(!s)continue;let i=Number(s);Number.isFinite(i)&&r.add(i)}if(e.channel==="whatsapp")for(let o of e.e164s)n.add(o);return e.channel==="telegram"&&r.add(e.chatId),{waTargets:n,tgTargets:r}}async function jb(e){let t=$();if(e.mode==="text"){let{waTargets:c,tgTargets:m}=hm(e,t);if(c.size===0&&m.size===0)return{error:"No recipients matched the requested /sendto destination."};let f=[];for(let h of c){let g=await Nr({op:"sendText",channel:"whatsapp",e164:h,text:e.body});g&&f.push(`[wa:${h}] ${g}`)}for(let h of m){let g=await Nr({op:"sendText",channel:"telegram",chatId:h,text:e.body});g&&f.push(`[tg:${h}] ${g}`)}return f.length>0?{error:f.join(`
360
- `)}:{error:null,kind:"text",recipientsSent:c.size+m.size}}let n=oe(Xn).cwd,r=await Rr(n,e.selectorPart);if(r.length===0)return{error:`No files matched: ${e.selectorPart}`};let o=await Cr(r);if(!o.ok)return{error:o.error};let s=r.map(c=>Kt(c,t.fileSendMaxBytes));for(let c of s)if("error"in c)return{error:c.error};let{waTargets:i,tgTargets:a}=hm(e,t);if(i.size===0&&a.size===0)return{error:"No recipients matched the requested /sendto destination."};let l=[];for(let c of i)for(let m of s){if("error"in m)continue;let f=await Nr({op:"sendMedia",channel:"whatsapp",e164:c,absPath:m.absPath,caption:e.caption});f&&l.push(`[wa:${c}] ${m.displayName}: ${f}`)}for(let c of a)for(let m of s){if("error"in m)continue;let f=await Nr({op:"sendMedia",channel:"telegram",chatId:c,absPath:m.absPath,caption:e.caption});f&&l.push(`[tg:${c}] ${m.displayName}: ${f}`)}if(l.length>0)return{error:l.join(`
361
- `)};let d=i.size+a.size,u=s.length;return{error:null,kind:"media",recipientsSent:d,filesSent:u,messagesSent:d*u}}async function Gb(e,t,n,r,o,s,i){let a=e.trim();if(!a)return;let l=mm(a);if(l!==null||/^\/sendto(\s|$)/i.test(a)){if(l===null){console.log(C(Te.stderr,`Invalid /sendto.
362
- `+Ja()));return}let c=await jb(l);if(c.error!==null)console.log(C(Te.stderr,c.error));else if(c.kind==="text")console.log(B(Te.stdout,`Sent text to ${c.recipientsSent} recipient(s).`));else{let m=`Sent ${c.filesSent} file(s) to ${c.recipientsSent} recipient(s) (${c.messagesSent} message(s)).`;console.log(B(Te.stdout,m))}return}let d={peerKey:Xn,text:e},u=await Vn($(),t,n,r,o,d,s,void 0,i,!1,Db);u!==null&&await Jb(u)}async function Jb(e){if(e===null)return;if(e.kind==="file"||e.kind==="files"){console.log(B(Te.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(`
363
- `)));return}if(e.kind==="texts"){for(let n=0;n<e.bodies.length;n++){let r=ke(e.bodies[n],"whatsapp").text;r.trim()&&(n>0&&console.log(""),console.log(r))}return}let t=ke(e.body,"whatsapp").text;t.trim()&&console.log(t)}async function gm(e){let t=Bb(e);if(t.error==="help"){Ka(Te.stdout);return}if(t.error&&t.error!==null){console.error(C(Te.stderr,t.error)),console.error(w(Te.stderr,"Try: omnish i --help")),Te.exitCode=1;return}let{senderKey:n,oneShot:r}=t.opts,o=n??Xn;te(),ro(Xn,Te.cwd());let s=new Tt,i=new Map,a=new Map,l=new Map,d=new Jt(()=>$(),async(f,h)=>{Te.stdout.write(h),h.endsWith(`
355
+ `)});o.on("close",()=>{h()});return}if(o.method==="POST"&&a==="/api/wa/link/start"){let g=(await qa(o))?.force===!0;try{Fr.beginPairing({force:g})}catch(y){let b=String(y),v=b.includes("already in progress")||b.includes("Gateway appears")?409:400;s.statusCode=v,s.setHeader("Content-Type",ae),s.end(JSON.stringify({ok:!1,error:b}));return}s.statusCode=202,s.setHeader("Content-Type",ae),s.end(JSON.stringify({ok:!0}));return}if(o.method==="POST"&&a==="/api/wa/link/cancel"){Fr.requestCancel(),s.statusCode=200,s.setHeader("Content-Type",ae),s.end(JSON.stringify({ok:!0}));return}s.statusCode=404,s.setHeader("Content-Type",ae),s.end(JSON.stringify({ok:!1,error:"Not found."}));return}let l=a==="/"?"index.html":a.replace(/^\/+/,""),d=Nb(t,l),u=Ae.join(t,"index.html");d&&Zt.existsSync(d)&&Zt.statSync(d).isFile()&&(u=d);let c=Zt.readFileSync(u);s.statusCode=200,s.setHeader("Content-Type",Lb(u)),s.setHeader("Cache-Control",Ae.basename(u)==="index.html"?"no-store":"public, max-age=3600"),s.end(c)}catch(i){s.statusCode=500,s.setHeader("Content-Type",ae),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())}),Wr=r,fm({pid:at.pid,port:e.port,host:e.host,startedAt:new Date().toISOString()}),r.on("close",()=>{_r(),Wr===r&&(Wr=null)})}function ym(e){let t=Ab.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 Jb from"node:readline";import bm from"node:path";import Te from"node:process";me();qe();G();import Db from"node:net";import Hb from"node:fs";function Bb(){try{let e=Hb.readFileSync(kn,"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 Ur(e){let t=Bb();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)}
356
+ `;return new Promise(o=>{let s=!1,i="";function a(d){s||(s=!0,o(d))}let l=Db.connect({host:t.host,port:t.port},()=>{l.write(r)});l.setTimeout(6e5),l.on("data",d=>{i+=d.toString("utf8");let u=i.indexOf(`
357
+ `);if(u>=0){let c=i.slice(0,u).trim();try{let m=JSON.parse(c);m.ok?a(null):a(m.error||"Unknown error from gateway control.")}catch{a("Invalid response from gateway control.")}l.destroy()}}),l.on("error",d=>{a(`Control connection failed: ${String(d)}`)}),l.on("timeout",()=>{l.destroy(),a("Gateway control timed out.")}),l.on("close",()=>{!s&&i.trim()===""&&a("Gateway closed the control connection without a response.")})})}qe();function Qa(e){return e.channel==="all"||e.channel==="whatsapp-all"||e.channel==="telegram-all"}function jb(e){let t=e.trimStart();if(/^--text=/i.test(t)){let n=t.slice(t.indexOf("=")+1).trimEnd();return n.length>0?n:null}if(/^--text(\s+|$)/i.test(t)){let n=t.replace(/^--text\s*/i,"").trim();return n.length>0?n:null}if(/^-t=/i.test(t)){let n=t.slice(t.indexOf("=")+1).trimEnd();return n.length>0?n:null}if(/^-t(\s+|$)/i.test(t)){let n=t.replace(/^-t\s*/i,"").trim();return n.length>0?n:null}}function Gb(e){let t=e.toLowerCase();if(t==="*"||t==="all")return{channel:"all"};if(t==="wa"||t==="whatsapp"||t==="wa:all"||t==="whatsapp:all")return{channel:"whatsapp-all"};if(t==="tg"||t==="telegram"||t==="tg:all"||t==="telegram:all")return{channel:"telegram-all"};if(t.startsWith("tg:")||t.startsWith("telegram:")){let n=Ee(e);if(!n)return null;let r=Number(n);return Number.isFinite(r)?{channel:"telegram",chatId:r}:null}if(t.startsWith("wa:")){let n=e.slice(3).trim();if(!n)return null;let r=n.split(",").map(o=>ee(o.trim())).filter(o=>!!o);return r.length===0?null:{channel:"whatsapp",e164s:r}}if(e.includes(",")){let n=e.split(",").map(r=>ee(r.trim())).filter(r=>!!r);return n.length===0?null:{channel:"whatsapp",e164s:n}}if(e.startsWith("+")){let n=ee(e);return n?{channel:"whatsapp",e164s:[n]}:null}return null}function wm(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=Gb(o);if(!i)return null;let a=jb(s);if(a===null)return null;if(a!==void 0)return{...i,mode:"text",body:a};let l=ws(s);if(!l)return null;let{selectorPart:d,caption:u}=l;return{...i,mode:"media",selectorPart:d,caption:u}}function Va(){return["/sendto wa|tg|* <selectors> [-- caption]","/sendto +E164 or +E164,+E164 <selectors> [-- caption]","/sendto <dest> --text <message> or --text=<msg> or -t <msg>","/sendto tg:<chat_id> <selectors> [-- caption] (compat: single Telegram id)","selectors: file1,file2 or *.mp4 or **/*.mp4 (from current session cwd); a path ./--text sends a file named --text","examples: /sendto wa ./promo.mp4 | /sendto * **/*.mp4 -- Daily clips","examples: /sendto +15551234567 --text Hello | /sendto tg:123 -t=Status OK","examples: /sendto +15550000001,+15550000002 intro.png,deck.pdf -- Launch","Also supports compat aliases: wa:all, whatsapp:all, tg:all, telegram:all.","Requires `omnish run` on this machine."].join(`
358
+ `)}Cr();it();G();var nr="wa:cli:interactive",Kb={onPlainTextLlmFallback(e,t){Wn($(),e,t,async n=>{n.trim()&&console.log(B(Te.stdout,n))})}};function zb(e){let t=e.trim();if(!t)return null;let n=t.toLowerCase();if(n.startsWith("tg:")||n.startsWith("telegram:")){let s=Ee(t);return s?`tg:${s}`:null}let r=n.startsWith("wa:")?t.slice(3):t,o=ee(r);return o?`wa:${o}`:null}function qb(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=zb(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 Xa(e){let t=Te.cwd(),n=[`${ue(e,"omnish i")} ${w(e,"[options]")}`,V(e,"Interactive shell \u2014 same commands as WhatsApp/Telegram chat."),"",J(e,"Usage:"),` ${S(e,"omnish i [options]")}`,` ${S(e,"omnish interactive [options]")}`,"",J(e,"Options:"),...vt(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:")} ${S(e,"Full local access like your shell; not gated by inbox allowlist.")}`,`${w(e,"Jobs:")} ${S(e,"/bg and /jobs apply only to this REPL session (not the gateway process).")}`,`${w(e,"Files:")} ${S(e,"Use /sendto for files or --text for plain messages through the gateway; plain /send needs a chat peer.")}`,`${w(e,"Gateway:")} ${S(e,"/reload and /updates require omnish run; /sendto requires omnish run for WA/TG delivery.")}`,"",Va(),"",`${w(e,"cwd:")} ${S(e,`session starts at ${t} (change with !cd or ${$().commandPrefix}cd).`)}`];console.log(n.join(`
359
+ `))}function km(e,t){let n=new Set,r=new Set;if(e.channel==="whatsapp-all"||e.channel==="all"){let o=new Set;for(let s of t.allowFrom){let i=ee(String(s));i&&!o.has(i)&&(o.add(i),n.add(i))}}if(e.channel==="telegram-all"||e.channel==="all")for(let o of t.telegramAllowFrom){let s=Ee(String(o));if(!s)continue;let i=Number(s);Number.isFinite(i)&&r.add(i)}if(e.channel==="whatsapp")for(let o of e.e164s)n.add(o);return e.channel==="telegram"&&r.add(e.chatId),{waTargets:n,tgTargets:r}}function Sm(e){let t="No recipients matched the requested /sendto destination.";return Qa(e)?se()?`${t} Check platform dashboard allowlists (or omnish platform status), or use explicit +E164 / tg:<id>.`:`${t} Set allowFrom / telegramAllowFrom in config.json, or use explicit +E164 / tg:<id>.`:t}async function Yb(e){let t=Qa(e)?await ca():$();if(e.mode==="text"){let{waTargets:c,tgTargets:m}=km(e,t);if(c.size===0&&m.size===0)return{error:Sm(e)};let f=[];for(let h of c){let g=await Ur({op:"sendText",channel:"whatsapp",e164:h,text:e.body});g&&f.push(`[wa:${h}] ${g}`)}for(let h of m){let g=await Ur({op:"sendText",channel:"telegram",chatId:h,text:e.body});g&&f.push(`[tg:${h}] ${g}`)}return f.length>0?{error:f.join(`
360
+ `)}:{error:null,kind:"text",recipientsSent:c.size+m.size}}let n=oe(nr).cwd,r=await Er(n,e.selectorPart);if(r.length===0)return{error:`No files matched: ${e.selectorPart}`};let o=await Pr(r);if(!o.ok)return{error:o.error};let s=r.map(c=>qt(c,t.fileSendMaxBytes));for(let c of s)if("error"in c)return{error:c.error};let{waTargets:i,tgTargets:a}=km(e,t);if(i.size===0&&a.size===0)return{error:Sm(e)};let l=[];for(let c of i)for(let m of s){if("error"in m)continue;let f=await Ur({op:"sendMedia",channel:"whatsapp",e164:c,absPath:m.absPath,caption:e.caption});f&&l.push(`[wa:${c}] ${m.displayName}: ${f}`)}for(let c of a)for(let m of s){if("error"in m)continue;let f=await Ur({op:"sendMedia",channel:"telegram",chatId:c,absPath:m.absPath,caption:e.caption});f&&l.push(`[tg:${c}] ${m.displayName}: ${f}`)}if(l.length>0)return{error:l.join(`
361
+ `)};let d=i.size+a.size,u=s.length;return{error:null,kind:"media",recipientsSent:d,filesSent:u,messagesSent:d*u}}async function Qb(e,t,n,r,o,s,i){let a=e.trim();if(!a)return;let l=wm(a);if(l!==null||/^\/sendto(\s|$)/i.test(a)){if(l===null){console.log(C(Te.stderr,`Invalid /sendto.
362
+ `+Va()));return}let c=await Yb(l);if(c.error!==null)console.log(C(Te.stderr,c.error));else if(c.kind==="text")console.log(B(Te.stdout,`Sent text to ${c.recipientsSent} recipient(s).`));else{let m=`Sent ${c.filesSent} file(s) to ${c.recipientsSent} recipient(s) (${c.messagesSent} message(s)).`;console.log(B(Te.stdout,m))}return}let d={peerKey:nr,text:e},u=await tr($(),t,n,r,o,d,s,void 0,i,!1,Kb);u!==null&&await Vb(u)}async function Vb(e){if(e===null)return;if(e.kind==="file"||e.kind==="files"){console.log(B(Te.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(`
363
+ `)));return}if(e.kind==="texts"){for(let n=0;n<e.bodies.length;n++){let r=ke(e.bodies[n],"whatsapp").text;r.trim()&&(n>0&&console.log(""),console.log(r))}return}let t=ke(e.body,"whatsapp").text;t.trim()&&console.log(t)}async function vm(e){let t=qb(e);if(t.error==="help"){Xa(Te.stdout);return}if(t.error&&t.error!==null){console.error(C(Te.stderr,t.error)),console.error(w(Te.stderr,"Try: omnish i --help")),Te.exitCode=1;return}let{senderKey:n,oneShot:r}=t.opts,o=n??nr;te(),ao(nr,Te.cwd());let s=new Et,i=new Map,a=new Map,l=new Map,d=new zt(()=>$(),async(f,h)=>{Te.stdout.write(h),h.endsWith(`
364
364
  `)||Te.stdout.write(`
365
- `)}),u=async f=>{try{await Gb(f,s,i,a,l,d,o)}catch(h){console.error(C(Te.stderr,String(h)))}};if(r!==null){await u(r),d.dispose(),s.killAllRunning();return}let c=Ub.createInterface({input:Te.stdin,output:Te.stdout}),m=fm.basename(oe(Xn).cwd);c.setPrompt(`${m}> `),c.on("line",f=>{u(f).then(()=>{let h=fm.basename(oe(Xn).cwd);c.setPrompt(`${h}> `),c.prompt()})}),c.on("close",()=>{d.dispose(),s.killAllRunning(),Te.stdout.write(`
366
- `)}),c.prompt()}Kb.setDefaultResultOrder("ipv4first");function bm(){let e=process.stdout,t=[`${Ce(e,"omnish run")} ${w(e,"[options]")}`,V(e,"Listen for DMs and run shell commands from allowlisted chats."),"",J(e,"Usage:"),` ${S(e,"omnish run [options]")}`,"",J(e,"Options:"),...St(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: ${Ne}).`},{left:"-vb, --verbose",right:"Baileys/gateway debug logs on stderr (legacy: OMNISH_VERBOSE=1)."},{left:"-h, --help",right:"Show this help."}],r=>w(e,r)),"",`${S(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).")}`,""],n=fe();n?t.push(J(e,"Attached mode (platform credentials detected):"),` ${w(e,"url:")} ${S(e,n.platformUrl)} ${V(e,`[${zn()}]`)}`,` ${w(e,"token:")} ${S(e,tn("platformToken",n.token))} ${V(e,`[${Kn()}]`)}`,w(e," Messengers run on the platform; allowlist is set on the dashboard. Run: omnish platform probe"),""):t.push(w(e,"Platform attached mode: omnish config add platform_url <url> platform_token <token>"),w(e," then omnish platform probe && omnish run \u2014 see omnish help platform"),""),console.log(t.join(`
367
- `))}function km(){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=>w(e,i.left)),r=Math.max(...n.map(so)),o=t.map((i,a)=>Zs(" ",r,n[a],S(e,i.right))),s=[`${Ce(e,"omnish link")} ${w(e,"[options]")}`,V(e,"Connect WhatsApp (QR) or save a Telegram bot token."),"",J(e,"Usage:"),` ${S(e,"omnish link [--force]")}`,` ${S(e,"omnish link --tg <bot_token>")}`,"",J(e,"Modes:"),...o,` ${V(e,"Do not combine --tg with --force.")}`,"",J(e,"Options:"),...St(e," ",[{left:"-f, --force",right:"WhatsApp only: delete saved session before pairing."},{left:"-vb, --verbose",right:"Baileys debug logs on stderr during pairing (legacy: OMNISH_VERBOSE=1)."},{left:"-h, --help",right:"Show this help."}],i=>w(e,i)),"",`${S(e,"Next:")} ${ce(e,"omnish allow tg:<your_user_id>")} ${w(e,"then")} ${ce(e,"omnish run")}`,`${w(e,"Config:")} ${S(e,_)}`,""];fe()&&s.push(J(e,"Platform attached mode:"),w(e," omnish link on this host is for standalone only. Link WhatsApp on the platform dashboard or:"),` ${S(e,"omnish platform import-whatsapp")} ${w(e,"(after a local omnish link, with omnish run stopped)")}`,w(e," Telegram: set bot token on the platform dashboard, not --tg here."),""),console.log(s.join(`
368
- `))}function qb(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==="-vb"||o==="--verbose"){Ds(!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}
369
- 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 qa(){let e=process.stdout,t=`${Ce(e,"omnish")} ${w(e,`v${Ze()}`)}`,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: ${le})`},{left:"service <subcommand>",right:"Boot install hints, logs, systemd/LaunchAgent \u2014 omnish service help"},{left:"logout",right:"Delete saved WhatsApp session"},{left:"allow +<E164> | tg:<id>",right:"Add allowlist entry"},{left:"deny +<E164> | tg:<id>",right:"Remove allowlist entry"},{left:"status [--check-updates]",right:"Channels, identity, allowlists, jobs, security, cluster (if enabled)"},{left:"commands",right:"Chat commands for allowlisted users (same as /help)"},{left:"security [--json]",right:"Configuration security report (JSON for scripts)"},{left:"cluster [status | use <sender> <label-or-id>]",right:"Per-sender machine bindings"},{left:"i | interactive [options]",right:"Local REPL (chat commands; /sendto needs omnish run)"},{left:"ui [options]",right:"Browser setup UI on LAN (token-gated) \u2014 omnish ui --help"},{left:"config <add|show|edit|delete>",right:"Manage config.json (platform URL/token, tunnel, gateway) \u2014 omnish config help"},{left:"platform <subcommand>",right:"Attached mode: configure, probe, import WA \u2014 omnish help platform"},{left:"tunnel <subcommand>",right:"Expose local HTTP/TCP via omnish relay \u2014 omnish tunnel help"}],r=[{left:"-v, --version",right:"Print version and exit."},{left:"-h, --help",right:"Show this help (same as omnish help)."}],o=[t,V(e,"Allowlisted inbox \u2192 your real shell. No AI."),"",J(e,"Usage:"),` ${S(e,"omnish [options] <command> [args...]")}`,"",J(e,"Options:"),...St(e," ",r,s=>w(e,s)),"",J(e,"Commands:"),...St(e," ",n,s=>w(e,s)),"",`${w(e,"Config:")} ${S(e,`${_} \u2014 gatewayMode: "whatsapp" | "telegram" | "both"`)}`,`${w(e,"Platform:")} ${S(e,"platform_url + platform_token \u2192 attached omnish run (omnish help platform)")}`,`${w(e,"Verbose:")} ${S(e,"omnish run --verbose (legacy: OMNISH_VERBOSE=1, WHATSVERBOSE=1)")}`,`${w(e,"Env:")} ${S(e,"TELEGRAM_BOT_TOKEN (optional override)")}`,`${w(e,"Data:")} ${S(e,"~/.omnish by default; ~/.whatslive reused if it already exists. OMNISH_HOME overrides.")}`,`${w(e,"See also:")} ${S(e,"https://omnish.dev")}`,""];console.log(o.join(`
370
- `))}function Yb(e){let t=(e??"").trim().toLowerCase(),n=process.stdout;if(!t){qa();return}switch(t){case"link":km();return;case"run":bm();return;case"service":vm();return;case"i":case"interactive":Ka(n);return;case"ui":xm();return;case"config":Es(n);return;case"platform":Os(n);return;default:console.error(C(process.stderr,`No detailed help for "${e}". Try: omnish help`)),process.exitCode=1}}function Qb(e){let t=!1,n="",r=!1,o=!1;for(let i=0;i<e.length;i++){let a=e[i]??"";if(a==="-d"||a==="--background")t=!0;else if(a==="-vb"||a==="--verbose")o=!0;else if(a==="--log-file"||a==="--log"){let l=e[++i];l||(console.error(C(process.stderr,"--log-file requires a path.")),process.exit(1)),n=l}else if(a==="--help"||a==="-h")r=!0;else{let l=process.stderr;console.error(C(l,`unknown run option: ${a}`)),console.error(C(l,"Try: omnish run --help")),process.exit(1)}}let s=n.trim()!==""?za.isAbsolute(n)?n:za.resolve(process.cwd(),n):Ne;return{background:t,logFile:s,help:r,verbose:o}}function Vb(e,t){let n=qo(e,{verbose:t});n.ok||(console.error(C(process.stderr,n.message)),process.exit(1));let r=process.stdout;console.log(`${B(r,`gateway started in background (pid ${n.pid}).`)} ${w(r,`Log: ${e}`)}`)}function Xb(){let e=Yo();switch(e.outcome){case"no_pidfile":console.error(C(process.stderr,`no pidfile at ${le} \u2014 is a background gateway running?`)),process.exitCode=1;return;case"invalid_pidfile":console.error(C(process.stderr,"invalid pidfile.")),process.exitCode=1;return;case"stale_cleaned":console.log(B(process.stdout,`process ${e.pid} is not running; removing stale pidfile.`));return;case"sent_signal":console.log(B(process.stdout,`sent SIGTERM to gateway (pid ${e.pid}).`));return;case"taskkill_ok":console.log(B(process.stdout,`stopped gateway (pid ${e.pid}) using taskkill.`));return;case"failed":console.error(C(process.stderr,e.message)),process.exitCode=1;return}}function ym(){if(process.env.OMNISH_BACKGROUND_GATEWAY==="1")try{Xt.readFileSync(le,"utf8").trim()===String(process.pid)&&Xt.unlinkSync(le)}catch{}}function Zb(e){return e.length<=8?"(set)":`${e.slice(0,4)}\u2026${e.slice(-4)}`}function ek(){if(!Xt.existsSync(le))return"gateway process: not running (no pid file)";let e=Xt.readFileSync(le,"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 Sm=120;function vm(){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 ${Sm}).`},{left:"install",right:"Write user systemd / LaunchAgent unit (needs serviceInstallFromChat=true)."},{left:"uninstall",right:"Remove that unit (same gate as install)."}],n=[`${Ce(e,"omnish service")} ${w(e,"<subcommand>")}`,V(e,"Boot integration, crash restart policy, and logs (same ideas as /service in chat)."),"",J(e,"Usage:"),` ${S(e,"omnish service <subcommand>")}`,"",J(e,"Subcommands:"),...St(e," ",t,r=>w(e,r)),"",J(e,"Restart and reload:"),` ${S(e,"Process")} ${w(e,"\u2014 Linux user unit: Restart=on-failure, RestartSec=5. macOS: KeepAlive. Full restart loads config from disk.")}`,` ${S(e,"Config live")} ${w(e,"\u2014 allowlisted chat while gateway runs: /reload or /restart")}`,"",`${w(e,"Docs:")} ${S(e,"docs/guides/background-and-boot.md")} ${V(e,"\xB7")} https://omnish.dev`,""];console.log(n.join(`
371
- `))}function tk(e){let t=process.stdout,n=process.stderr,r=(e[0]??"help").toLowerCase();if(r==="help"||r==="--help"||r==="-h"){vm();return}if(r==="instructions"){let o=nn();if(o.error){console.error(C(n,o.error)),process.exitCode=1;return}console.log(fo(o));return}if(r==="status"){let o=nn(),s=(()=>{try{return Xt.existsSync(le)?`gateway.pid: ${Xt.readFileSync(le,"utf8").trim()}`:"gateway.pid: (missing)"}catch(c){return`gateway.pid: (read error: ${String(c)})`}})(),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}
372
- Script: ${o.scriptPath}`,u=$().serviceInstallFromChat?"Install from CLI/chat: enabled (omnish service install / /service install).":"Install from CLI/chat: off \u2014 set serviceInstallFromChat true in config (same trust as shell).";console.log(Ce(t,"omnish service status")),console.log(""),console.log(`${w(t,"platform:")} ${S(t,process.platform)}`),console.log(`${w(t,"session:")} ${S(t,i)}`),console.log(`${w(t,"env:")} ${S(t,a)}`),console.log(`${w(t,"data dir:")} ${S(t,H)}`),console.log(`${w(t,"pidfile:")} ${S(t,s)}`),console.log(`${w(t,"default log:")} ${S(t,Ne)}`),console.log(""),console.log(l),console.log(""),console.log(S(t,u));return}if(r==="logs"){let o=e.length>=2?Number.parseInt(e[1],10):80,s=Number.isFinite(o)&&o>0?Math.min(o,Sm):80,i=wo(Ne,s);console.log(`${w(t,"file:")} ${S(t,Ne)}`),console.log(`${w(t,"lines:")} ${S(t,String(s))}`),console.log(""),console.log(i);return}if(r==="install"){if(!$().serviceInstallFromChat){console.error(C(n,"Install is disabled. Set serviceInstallFromChat to true in config (same trust as shell), then run again.")),process.exitCode=1;return}let s=ho();s.ok?console.log(B(t,s.detail)):(console.error(C(n,s.detail)),process.exitCode=1);return}if(r==="uninstall"){if(!$().serviceInstallFromChat){console.error(C(n,"Uninstall is disabled. Set serviceInstallFromChat to true in config or remove the unit file on the host manually.")),process.exitCode=1;return}let s=go();s.ok?console.log(B(t,s.detail)):(console.error(C(n,s.detail)),process.exitCode=1);return}console.error(C(n,`Unknown subcommand "${r}". Try: omnish service help`)),process.exitCode=1}function nk(e){let t=e.trim();if(!t)return null;let n=t.toLowerCase();if(n.startsWith("tg:")||n.startsWith("telegram:")){let s=Ee(t);return s?`tg:${s}`:null}let r=n.startsWith("wa:")?t.slice(3):t,o=ee(r);return o?`wa:${o}`:null}async function rk(){let e=null,t=Ms();t.ok||(console.error(C(process.stderr,t.message)),process.exit(1));let n=fe();if(n){await Op(n);return}let r=$(),o=r.gatewayMode,s=o==="whatsapp"||o==="both",i=o==="telegram"||o==="both",a=ve(r),l=Rt(r),d=process.stderr,u=Xl(l,"warn");if(u.length>0&&(console.warn(`${B(d,`Security (${u.length} finding(s)):`)}
373
- `),console.warn(oi(u,d))),process.env.OMNISH_BACKGROUND_GATEWAY==="1")try{Xt.writeFileSync(le,`${process.pid}
374
- `,{mode:384})}catch(k){M.warn({err:String(k)},"could not write gateway pidfile")}let c=(k,x)=>{let L=$();if(!L.clusterEnabled)return k;let F=Xe(),Q=(L.clusterLabel??"").trim()||wm.hostname(),z=null;if(x.startsWith("tg:"))z=x;else if(x){let Ue=ee(x);Ue&&(z=`wa:${Ue}`)}let we=z?Ct(L,z):null;return Dc(k,{nodeId:F,label:Q,role:L.clusterRole,activeNodeId:we?.nodeId??""})},m=new Tt,f=new Map,h=new Map,g=new Map,y=null,b={stop:null,sendText:null,sendMedia:null},v=null,E=!1,R=async(k,x)=>{if(k.startsWith("wa:")){let L=k.slice(3);y&&await y.sendText(L,x)}else if(k.startsWith("tg:")){let L=Number(k.slice(3));b.sendText&&Number.isFinite(L)&&await b.sendText(L,p(x))}},P={onPlainTextLlmFallback(k,x){On($(),k,x,L=>R(k,L))},sendToPeer:R},T=async(k,x)=>{if(k.startsWith("wa:")){let L=k.slice(3);y&&await y.sendMedia(L,x)}else if(k.startsWith("tg:")){let L=Number(k.slice(3));b.sendMedia&&Number.isFinite(L)&&await b.sendMedia(L,x)}},O=()=>new Jt(()=>$(),R),A=O();v=A;let K,X={async reload(){try{M.info("gateway reload requested from chat"),await b.stop?.().catch(()=>{}),b.stop=null,b.sendText=null,b.sendMedia=null;let k=$(),x=k.gatewayMode==="telegram"||k.gatewayMode==="both",L=ve(k);if(x&&L){let z=await qi(L,()=>$(),K,{decorate:c});b.sendText=z.sendText,b.sendMedia=z.sendMedia,b.stop=z.stop}let F=["Reload complete.",`gatewayMode: ${k.gatewayMode}`,x&&L?"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(`
375
- `),Q=ms(vr());return{ok:!0,summary:Q?`${F}
365
+ `)}),u=async f=>{try{await Qb(f,s,i,a,l,d,o)}catch(h){console.error(C(Te.stderr,String(h)))}};if(r!==null){await u(r),d.dispose(),s.killAllRunning();return}let c=Jb.createInterface({input:Te.stdin,output:Te.stdout}),m=bm.basename(oe(nr).cwd);c.setPrompt(`${m}> `),c.on("line",f=>{u(f).then(()=>{let h=bm.basename(oe(nr).cwd);c.setPrompt(`${h}> `),c.prompt()})}),c.on("close",()=>{d.dispose(),s.killAllRunning(),Te.stdout.write(`
366
+ `)}),c.prompt()}Xb.setDefaultResultOrder("ipv4first");function Rm(){let e=process.stdout,t=[`${Ce(e,"omnish run")} ${w(e,"[options]")}`,V(e,"Listen for DMs and run shell commands from allowlisted chats."),"",J(e,"Usage:"),` ${S(e,"omnish run [options]")}`,"",J(e,"Options:"),...vt(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: ${Ne}).`},{left:"-vb, --verbose",right:"Baileys/gateway debug logs on stderr (legacy: OMNISH_VERBOSE=1)."},{left:"-h, --help",right:"Show this help."}],r=>w(e,r)),"",`${S(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).")}`,""],n=se();n?t.push(J(e,"Attached mode (platform credentials detected):"),` ${w(e,"url:")} ${S(e,n.platformUrl)} ${V(e,`[${qn()}]`)}`,` ${w(e,"token:")} ${S(e,sn("platformToken",n.token))} ${V(e,`[${zn()}]`)}`,w(e," Messengers run on the platform; allowlist is set on the dashboard. Run: omnish platform probe"),""):t.push(w(e,"Platform attached mode: omnish config add platform_url <url> platform_token <token>"),w(e," then omnish platform probe && omnish run \u2014 see omnish help platform"),""),console.log(t.join(`
367
+ `))}function Cm(){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=>w(e,i.left)),r=Math.max(...n.map(co)),o=t.map((i,a)=>ni(" ",r,n[a],S(e,i.right))),s=[`${Ce(e,"omnish link")} ${w(e,"[options]")}`,V(e,"Connect WhatsApp (QR) or save a Telegram bot token."),"",J(e,"Usage:"),` ${S(e,"omnish link [--force]")}`,` ${S(e,"omnish link --tg <bot_token>")}`,"",J(e,"Modes:"),...o,` ${V(e,"Do not combine --tg with --force.")}`,"",J(e,"Options:"),...vt(e," ",[{left:"-f, --force",right:"WhatsApp only: delete saved session before pairing."},{left:"-vb, --verbose",right:"Baileys debug logs on stderr during pairing (legacy: OMNISH_VERBOSE=1)."},{left:"-h, --help",right:"Show this help."}],i=>w(e,i)),"",`${S(e,"Next:")} ${ue(e,"omnish allow tg:<your_user_id>")} ${w(e,"then")} ${ue(e,"omnish run")}`,`${w(e,"Config:")} ${S(e,_)}`,""];se()&&s.push(J(e,"Platform attached mode:"),w(e," omnish link on this host is for standalone only. Link WhatsApp on the platform dashboard or:"),` ${S(e,"omnish platform import-whatsapp")} ${w(e,"(after a local omnish link, with omnish run stopped)")}`,w(e," Telegram: set bot token on the platform dashboard, not --tg here."),""),console.log(s.join(`
368
+ `))}function ek(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==="-vb"||o==="--verbose"){js(!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}
369
+ 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 el(){let e=process.stdout,t=`${Ce(e,"omnish")} ${w(e,`v${Ze()}`)}`,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: ${ce})`},{left:"service <subcommand>",right:"Boot install hints, logs, systemd/LaunchAgent \u2014 omnish service help"},{left:"logout",right:"Delete saved WhatsApp session"},{left:"allow +<E164> | tg:<id>",right:"Add allowlist entry"},{left:"deny +<E164> | tg:<id>",right:"Remove allowlist entry"},{left:"status [--check-updates]",right:"Channels, identity, allowlists, jobs, security, cluster (if enabled)"},{left:"commands",right:"Chat commands for allowlisted users (same as /help)"},{left:"security [--json]",right:"Configuration security report (JSON for scripts)"},{left:"cluster [status | use <sender> <label-or-id>]",right:"Per-sender machine bindings"},{left:"i | interactive [options]",right:"Local REPL (chat commands; /sendto needs omnish run)"},{left:"ui [options]",right:"Browser setup UI on LAN (token-gated) \u2014 omnish ui --help"},{left:"config <add|show|edit|delete>",right:"Manage config.json (platform URL/token, tunnel, gateway) \u2014 omnish config help"},{left:"platform <subcommand>",right:"Attached mode: configure, probe, import WA \u2014 omnish help platform"},{left:"tunnel <subcommand>",right:"Expose local HTTP/TCP via omnish relay \u2014 omnish tunnel help"}],r=[{left:"-v, --version",right:"Print version and exit."},{left:"-h, --help",right:"Show this help (same as omnish help)."}],o=[t,V(e,"Allowlisted inbox \u2192 your real shell. No AI."),"",J(e,"Usage:"),` ${S(e,"omnish [options] <command> [args...]")}`,"",J(e,"Options:"),...vt(e," ",r,s=>w(e,s)),"",J(e,"Commands:"),...vt(e," ",n,s=>w(e,s)),"",`${w(e,"Config:")} ${S(e,`${_} \u2014 gatewayMode: "whatsapp" | "telegram" | "both"`)}`,`${w(e,"Platform:")} ${S(e,"platform_url + platform_token \u2192 attached omnish run (omnish help platform)")}`,`${w(e,"Verbose:")} ${S(e,"omnish run --verbose (legacy: OMNISH_VERBOSE=1, WHATSVERBOSE=1)")}`,`${w(e,"Env:")} ${S(e,"TELEGRAM_BOT_TOKEN (optional override)")}`,`${w(e,"Data:")} ${S(e,"~/.omnish by default; ~/.whatslive reused if it already exists. OMNISH_HOME overrides.")}`,`${w(e,"See also:")} ${S(e,"https://omnish.dev")}`,""];console.log(o.join(`
370
+ `))}function tk(e){let t=(e??"").trim().toLowerCase(),n=process.stdout;if(!t){el();return}switch(t){case"link":Cm();return;case"run":Rm();return;case"service":Tm();return;case"i":case"interactive":Xa(n);return;case"ui":Em();return;case"config":Is(n);return;case"platform":_s(n);return;default:console.error(C(process.stderr,`No detailed help for "${e}". Try: omnish help`)),process.exitCode=1}}function nk(e){let t=!1,n="",r=!1,o=!1;for(let i=0;i<e.length;i++){let a=e[i]??"";if(a==="-d"||a==="--background")t=!0;else if(a==="-vb"||a==="--verbose")o=!0;else if(a==="--log-file"||a==="--log"){let l=e[++i];l||(console.error(C(process.stderr,"--log-file requires a path.")),process.exit(1)),n=l}else if(a==="--help"||a==="-h")r=!0;else{let l=process.stderr;console.error(C(l,`unknown run option: ${a}`)),console.error(C(l,"Try: omnish run --help")),process.exit(1)}}let s=n.trim()!==""?Za.isAbsolute(n)?n:Za.resolve(process.cwd(),n):Ne;return{background:t,logFile:s,help:r,verbose:o}}function rk(e,t){let n=Xo(e,{verbose:t});n.ok||(console.error(C(process.stderr,n.message)),process.exit(1));let r=process.stdout;console.log(`${B(r,`gateway started in background (pid ${n.pid}).`)} ${w(r,`Log: ${e}`)}`)}function ok(){let e=Zo();switch(e.outcome){case"no_pidfile":console.error(C(process.stderr,`no pidfile at ${ce} \u2014 is a background gateway running?`)),process.exitCode=1;return;case"invalid_pidfile":console.error(C(process.stderr,"invalid pidfile.")),process.exitCode=1;return;case"stale_cleaned":console.log(B(process.stdout,`process ${e.pid} is not running; removing stale pidfile.`));return;case"sent_signal":console.log(B(process.stdout,`sent SIGTERM to gateway (pid ${e.pid}).`));return;case"taskkill_ok":console.log(B(process.stdout,`stopped gateway (pid ${e.pid}) using taskkill.`));return;case"failed":console.error(C(process.stderr,e.message)),process.exitCode=1;return}}function xm(){if(process.env.OMNISH_BACKGROUND_GATEWAY==="1")try{en.readFileSync(ce,"utf8").trim()===String(process.pid)&&en.unlinkSync(ce)}catch{}}function sk(e){return e.length<=8?"(set)":`${e.slice(0,4)}\u2026${e.slice(-4)}`}function ik(){if(!en.existsSync(ce))return"gateway process: not running (no pid file)";let e=en.readFileSync(ce,"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 Mm=120;function Tm(){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 ${Mm}).`},{left:"install",right:"Write user systemd / LaunchAgent unit (needs serviceInstallFromChat=true)."},{left:"uninstall",right:"Remove that unit (same gate as install)."}],n=[`${Ce(e,"omnish service")} ${w(e,"<subcommand>")}`,V(e,"Boot integration, crash restart policy, and logs (same ideas as /service in chat)."),"",J(e,"Usage:"),` ${S(e,"omnish service <subcommand>")}`,"",J(e,"Subcommands:"),...vt(e," ",t,r=>w(e,r)),"",J(e,"Restart and reload:"),` ${S(e,"Process")} ${w(e,"\u2014 Linux user unit: Restart=on-failure, RestartSec=5. macOS: KeepAlive. Full restart loads config from disk.")}`,` ${S(e,"Config live")} ${w(e,"\u2014 allowlisted chat while gateway runs: /reload or /restart")}`,"",`${w(e,"Docs:")} ${S(e,"docs/guides/background-and-boot.md")} ${V(e,"\xB7")} https://omnish.dev`,""];console.log(n.join(`
371
+ `))}function ak(e){let t=process.stdout,n=process.stderr,r=(e[0]??"help").toLowerCase();if(r==="help"||r==="--help"||r==="-h"){Tm();return}if(r==="instructions"){let o=an();if(o.error){console.error(C(n,o.error)),process.exitCode=1;return}console.log(wo(o));return}if(r==="status"){let o=an(),s=(()=>{try{return en.existsSync(ce)?`gateway.pid: ${en.readFileSync(ce,"utf8").trim()}`:"gateway.pid: (missing)"}catch(c){return`gateway.pid: (read error: ${String(c)})`}})(),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}
372
+ Script: ${o.scriptPath}`,u=$().serviceInstallFromChat?"Install from CLI/chat: enabled (omnish service install / /service install).":"Install from CLI/chat: off \u2014 set serviceInstallFromChat true in config (same trust as shell).";console.log(Ce(t,"omnish service status")),console.log(""),console.log(`${w(t,"platform:")} ${S(t,process.platform)}`),console.log(`${w(t,"session:")} ${S(t,i)}`),console.log(`${w(t,"env:")} ${S(t,a)}`),console.log(`${w(t,"data dir:")} ${S(t,H)}`),console.log(`${w(t,"pidfile:")} ${S(t,s)}`),console.log(`${w(t,"default log:")} ${S(t,Ne)}`),console.log(""),console.log(l),console.log(""),console.log(S(t,u));return}if(r==="logs"){let o=e.length>=2?Number.parseInt(e[1],10):80,s=Number.isFinite(o)&&o>0?Math.min(o,Mm):80,i=vo(Ne,s);console.log(`${w(t,"file:")} ${S(t,Ne)}`),console.log(`${w(t,"lines:")} ${S(t,String(s))}`),console.log(""),console.log(i);return}if(r==="install"){if(!$().serviceInstallFromChat){console.error(C(n,"Install is disabled. Set serviceInstallFromChat to true in config (same trust as shell), then run again.")),process.exitCode=1;return}let s=bo();s.ok?console.log(B(t,s.detail)):(console.error(C(n,s.detail)),process.exitCode=1);return}if(r==="uninstall"){if(!$().serviceInstallFromChat){console.error(C(n,"Uninstall is disabled. Set serviceInstallFromChat to true in config or remove the unit file on the host manually.")),process.exitCode=1;return}let s=ko();s.ok?console.log(B(t,s.detail)):(console.error(C(n,s.detail)),process.exitCode=1);return}console.error(C(n,`Unknown subcommand "${r}". Try: omnish service help`)),process.exitCode=1}function lk(e){let t=e.trim();if(!t)return null;let n=t.toLowerCase();if(n.startsWith("tg:")||n.startsWith("telegram:")){let s=Ee(t);return s?`tg:${s}`:null}let r=n.startsWith("wa:")?t.slice(3):t,o=ee(r);return o?`wa:${o}`:null}async function ck(){let e=null,t=Ps();t.ok||(console.error(C(process.stderr,t.message)),process.exit(1));let n=se();if(n){await Wp(n);return}let r=$(),o=r.gatewayMode,s=o==="whatsapp"||o==="both",i=o==="telegram"||o==="both",a=ve(r),l=Ct(r),d=process.stderr,u=oc(l,"warn");if(u.length>0&&(console.warn(`${B(d,`Security (${u.length} finding(s)):`)}
373
+ `),console.warn(ai(u,d))),process.env.OMNISH_BACKGROUND_GATEWAY==="1")try{en.writeFileSync(ce,`${process.pid}
374
+ `,{mode:384})}catch(k){M.warn({err:String(k)},"could not write gateway pidfile")}let c=(k,x)=>{let L=$();if(!L.clusterEnabled)return k;let F=Xe(),Q=(L.clusterLabel??"").trim()||$m.hostname(),z=null;if(x.startsWith("tg:"))z=x;else if(x){let Ue=ee(x);Ue&&(z=`wa:${Ue}`)}let we=z?Mt(L,z):null;return Kc(k,{nodeId:F,label:Q,role:L.clusterRole,activeNodeId:we?.nodeId??""})},m=new Et,f=new Map,h=new Map,g=new Map,y=null,b={stop:null,sendText:null,sendMedia:null},v=null,E=!1,R=async(k,x)=>{if(k.startsWith("wa:")){let L=k.slice(3);y&&await y.sendText(L,x)}else if(k.startsWith("tg:")){let L=Number(k.slice(3));b.sendText&&Number.isFinite(L)&&await b.sendText(L,p(x))}},P={onPlainTextLlmFallback(k,x){Wn($(),k,x,L=>R(k,L))},sendToPeer:R},T=async(k,x)=>{if(k.startsWith("wa:")){let L=k.slice(3);y&&await y.sendMedia(L,x)}else if(k.startsWith("tg:")){let L=Number(k.slice(3));b.sendMedia&&Number.isFinite(L)&&await b.sendMedia(L,x)}},O=()=>new zt(()=>$(),R),A=O();v=A;let K,X={async reload(){try{M.info("gateway reload requested from chat"),await b.stop?.().catch(()=>{}),b.stop=null,b.sendText=null,b.sendMedia=null;let k=$(),x=k.gatewayMode==="telegram"||k.gatewayMode==="both",L=ve(k);if(x&&L){let z=await Vi(L,()=>$(),K,{decorate:c});b.sendText=z.sendText,b.sendMedia=z.sendMedia,b.stop=z.stop}let F=["Reload complete.",`gatewayMode: ${k.gatewayMode}`,x&&L?"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(`
375
+ `),Q=gs(Mr());return{ok:!0,summary:Q?`${F}
376
376
 
377
- Updates (last check): ${Q}`:F}}catch(k){return{ok:!1,error:String(k)}}}};if(K=async(k,x)=>{let L=$();await Ar(L,m,f,h,g,k,A,X,k.peerKey,P,Aa({sendTg:x},{surface:"telegram"}),{surface:"telegram"})},i){let k=await qi(a,()=>$(),K,{decorate:c});b.sendText=k.sendText,b.sendMedia=k.sendMedia,b.stop=k.stop}Xo({getCfg:()=>$(),getWaOutbound:()=>y,getTgSendMedia:()=>b.sendMedia,getTgSendText:()=>b.sendText});let se=null;{let k=$();if(k.webhookEnabled){let x=k.webhookToken||zb.randomBytes(32).toString("hex");k.webhookToken||W({webhookToken:x}),se=Zo({port:k.webhookPort,host:k.webhookHost,token:x},{sendToPeer:R,getDefaultPeerKey:()=>{let F=$();return F.allowFrom.length>0?`wa:${It(F.allowFrom[0])}`:F.telegramAllowFrom.length>0?`tg:${F.telegramAllowFrom[0]}`:null}}).stop}}e=fs({getRunningVersion:Ze,getConfig:$,log:M});let he=Uo({getConfig:$,sendToPeer:R,sendMediaToPeer:T}),D=no({getConfig:$,sendToPeer:R}),ae=!i,Y=()=>{E=!0,he(),D(),e?.(),e=null,se?.(),ym(),Hn(),b.stop?.().catch(()=>{}),v?.dispose(),m.killAllRunning(),dn().stopAll().catch(()=>{}),console.error(`
378
- ${C(process.stderr,"shutting down\u2026")}`),process.exit(0)};if(process.on("SIGINT",Y),process.on("SIGTERM",Y),s)for(;!E;){let k=!1,x;try{x=await jo({printQr:!1,verbose:yn()}),await un(Go(x),3e5,"Gateway: timed out waiting for WhatsApp connection (5 min).")}catch(F){console.error(C(process.stderr,`connect failed: ${String(F)}`)),await new Promise(Q=>setTimeout(Q,5e3));continue}y=Zu(x,{decorate:c});let L=zu(x,async F=>{let Q=$(),z=F.fromE164||ee(F.fromJid)||"",we=eu(F),Ue=`wa:${z}`;await Ar(Q,m,f,h,g,we,A,X,Ue,P,Aa({sendWaText:(Ie,de)=>y.sendText(Ie,de),sendWaMedia:(Ie,de)=>y.sendMedia(Ie,de)},{surface:"whatsapp",waJid:F.fromJid}),{surface:"whatsapp"})});if(await new Promise(F=>{let Q=z=>{z.connection==="close"&&(Ji(z.lastDisconnect)===cn.loggedOut&&(k=!0),x.ev.off("connection.update",Q),F())};x.ev.on("connection.update",Q)}),L(),ae&&(A.dispose(),A=O(),v=A),Wn(x),y=null,k&&(console.error(C(process.stderr,"session logged out. Run `omnish link` again.")),he(),e?.(),e=null,ym(),Hn(),b.stop?.().catch(()=>{}),process.exit(1)),E)break;await new Promise(F=>setTimeout(F,3e3))}else for(;!E;)await new Promise(k=>setTimeout(k,500));he(),e?.(),Hn(),b.stop?.().catch(()=>{}),A.dispose()}function ok(e){let t=!1,n="0.0.0.0",r=3789,o;for(let s=0;s<e.length;s++){let i=e[s];if(i==="--help"||i==="-h"){t=!0;continue}if(i==="--host"||i==="-H"){let l=e[++s];l||(console.error(C(process.stderr,"--host requires an address (e.g. 127.0.0.1).")),process.exit(1)),n=l;continue}if(i==="--port"||i==="-p"){let l=e[++s],d=Number.parseInt(l??"",10);Number.isFinite(d)||(console.error(C(process.stderr,"--port requires a number.")),process.exit(1)),r=d;continue}if(i==="--token"||i==="-t"){let l=e[++s];(!l||l.startsWith("-"))&&(console.error(C(process.stderr,"--token requires a secret string.")),process.exit(1)),o=l;continue}let a=process.stderr;console.error(C(a,`unknown ui argument: ${i}`)),console.error(C(a,"Try: omnish ui --help")),process.exit(1)}return{help:t,host:n,port:r,token:o}}function xm(){let e=process.stdout,t=[`${Ce(e,"omnish ui")} ${w(e,"[options]")}`,V(e,"Serve the browser setup panel on your LAN (token-gated). Same trust as editing config on disk."),"",J(e,"Usage:"),` ${S(e,"omnish ui [options]")}`,"",J(e,"Options:"),...St(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 ${ct}).`},{left:"-h, --help",right:"This help."}],n=>w(e,n)),"",`${w(e,"Warning:")} ${me(e,"Listening on all interfaces exposes a remote control panel on your network \u2014 use a trusted LAN.")}`,""];console.log(t.join(`
379
- `))}async function sk(){let[,,e,...t]=process.argv;if(e==="--version"||e==="-v"||e==="-V"){let n=process.stdout;console.log(`${Ce(n,"omnish")} ${w(n,Ze())}`);return}if(e==="--help"||e==="-h"){qa();return}if(e==="help"){Yb(t[0]);return}switch(te(),e){case"link":{let n=qb(t);if(n.kind==="help"){km();return}if(n.kind==="error"){let r=process.stderr,o=n.message.replace(/^\[omnish\]\s*/,"");console.error(C(r,o)),process.exitCode=1;return}if(n.kind==="tg"){let r=n.token.trim(),o=process.stderr,s=process.stdout;if(!dt(r)){console.error(C(o,"That does not look like a Telegram bot token (expect digits:secret from @BotFather).")),process.exitCode=1;return}Ot(r);let i=nt()?"both":"telegram";qr(i),console.log([`${ce(s,"Telegram")} ${S(s,"bot token saved to")} ${w(s,_)}`,`${w(s,"gatewayMode:")} ${ce(s,i)}`,"",S(s,"Next:"),` ${w(s,"1.")} ${S(s,"Find your numeric user id (e.g. t.me/userinfobot), then:")} ${ce(s,"omnish allow tg:<id>")}`,` ${w(s,"2.")} ${ce(s,"omnish run")}`,""].join(`
380
- `));return}await id({verbose:yn(),force:n.force});return}case"run":{let n=Qb(t);if(n.help){bm();return}if(n.verbose&&Ds(!0),n.background){Vb(n.logFile,n.verbose);return}await rk();return}case"stop":Xb();return;case"logout":{try{Xt.rmSync(ne,{recursive:!0,force:!0}),console.log(B(process.stdout,"Session removed. Run `omnish link` to pair again."))}catch(n){console.error(C(process.stderr,`logout failed: ${String(n)}`)),process.exitCode=1}return}case"allow":{let n=t[0],r=process.stdout,o=process.stderr;if(!n){console.error(C(o,"Usage: omnish allow +<E164> or omnish allow tg:<user_id>")),process.exitCode=1;return}try{let s=Kr(n);console.log(`${w(r,"allowFrom:")} ${S(r,s.allowFrom.join(", ")||"(empty)")}`),console.log(`${w(r,"telegramAllowFrom:")} ${S(r,s.telegramAllowFrom.join(", ")||"(empty)")}`)}catch(s){console.error(C(o,String(s).replace(/^\[omnish\]\s*/,""))),process.exitCode=1}return}case"deny":{let n=t[0],r=process.stdout,o=process.stderr;if(!n){console.error(C(o,"Usage: omnish deny +<E164> or omnish deny tg:<user_id>")),process.exitCode=1;return}try{let s=zr(n);console.log(`${w(r,"allowFrom:")} ${S(r,s.allowFrom.join(", ")||"(empty)")}`),console.log(`${w(r,"telegramAllowFrom:")} ${S(r,s.telegramAllowFrom.join(", ")||"(empty)")}`)}catch(s){console.error(C(o,String(s).replace(/^\[omnish\]\s*/,""))),process.exitCode=1}return}case"i":case"interactive":{await gm(t);return}case"status":{let n=process.stdout,r=$(),o=t.includes("--check-updates"),s=new Tt().list(),i=s.filter(h=>h.status==="running").length,a=ve(r),l=Rt(r),d=r.gatewayMode==="whatsapp"||r.gatewayMode==="both",u=r.gatewayMode==="telegram"||r.gatewayMode==="both",c=nt(),m=c?rd(ne):null,f=[];if(f.push(`${Ce(n,"omnish")} ${w(n,Ze())}`,`${w(n,"gatewayMode:")} ${S(n,r.gatewayMode)}`,`${w(n,"data dir:")} ${S(n,za.dirname(ne))}`,"",`${w(n,"gateway process:")} ${S(n,ek().replace(/^gateway process: /,""))}`,"",Dt(n),ce(n,"whatsapp"),` ${w(n,"in use:")} ${d?S(n,"yes"):me(n,"no (gatewayMode is telegram-only)")}`),d){let h=c?S(n,`linked (${ne})`):me(n,"missing \u2014 run omnish link");f.push(` ${w(n,"session:")} ${h}`),c&&m&&f.push(` ${w(n,"linked as:")} ${S(n,m)}`),c&&!m&&f.push(` ${w(n,"linked as:")} ${V(n,"(not in creds yet \u2014 try again after omnish link completes)")}`)}if(f.push(` ${J(n,"Allowed")}`),r.allowFrom.length===0)f.push(` ${V(n,"(none)")}`);else for(let h of r.allowFrom)f.push(` ${w(n,"whatsapp:")} ${S(n,h)}`);if(f.push("",Dt(n),ce(n,"telegram")),f.push(` ${w(n,"in use:")} ${u?S(n,"yes"):me(n,"no (gatewayMode is whatsapp-only)")}`),u){let h=a?S(n,Zb(a)):me(n,"(none) \u2014 omnish link --tg <token> or TELEGRAM_BOT_TOKEN");f.push(` ${w(n,"bot token:")} ${h}`)}if(f.push(` ${J(n,"Allowed")}`),r.telegramAllowFrom.length===0)f.push(` ${V(n,"(none)")}`);else for(let h of r.telegramAllowFrom)f.push(` ${w(n,"telegram:")} ${S(n,h)}`);if(f.push("",Dt(n),...await Up(n)),f.push("",Dt(n),`${ce(n,"jobs")} ${w(n,`(recent): ${s.length} total, ${i} running`)}`,tc(l,n)),console.log(f.join(`
381
- `)),o){let h=await xr(Ze(),r),g=cr(h);console.log(""),console.log(Dt(n)),console.log(ke(g,"whatsapp").text)}if(r.clusterEnabled){let h=Xe(),g=ge(),y=Object.keys(g.senderBindings).length,b=Object.keys(r.clusterSenderBindings??{}).length;console.log(""),console.log(Dt(n)),console.log(ce(n,"cluster")),console.log(` ${w(n,"\xB7")} ${S(n,`enabled \xB7 label ${r.clusterLabel||wm.hostname()} \xB7 bindings ${y} chat / ${b} default`)}`),console.log(` ${w(n,"node:")} ${S(n,`${h.slice(0,8)}\u2026`)}`)}return}case"commands":{let n=process.stdout,r=$();console.log(Yl(xn(r),n)),console.log(""),console.log(Dt(n)),console.log(S(n,"Keys editable from chat via /config set (same trust as shell):")),console.log(w(n,Bt.join(", "))),console.log(`${w(n,"See also:")} ${S(n,_)}`);return}case"cluster":{let n=process.stdout,r=process.stderr,o=(t[0]??"status").toLowerCase();if(o==="status"){let s=$(),i=Xe();if(console.log(`${w(n,"clusterEnabled:")} ${S(n,String(s.clusterEnabled))}`),console.log(`${w(n,"clusterLabel:")} ${S(n,s.clusterLabel||"(hostname)")}`),console.log(`${w(n,"clusterRole:")} ${S(n,`${s.clusterRole} (informational; no longer used to gate traffic)`)}`),console.log(`${w(n,"node id:")} ${ce(n,i)}`),s.clusterEnabled){let a=ge(),l=Ci(s,null);console.log(""),console.log(S(n,l.wa.replace(/\*([^*]+)\*/g,"$1").replace(/`([^`]+)`/g,"$1"))),console.log(""),console.log(Ri(a,s,null));let d=Object.keys(a.senderBindings).length,u=Object.entries(s.clusterSenderBindings??{});if(d>0){console.log(""),console.log(ce(n,"Chat bindings (cluster-local.json)"));for(let[c,m]of Object.entries(a.senderBindings))console.log(` ${w(n,c)} ${V(n,"->")} ${S(n,`${m.nodeId} (${m.source}, since ${m.sinceIso})`)}`)}if(u.length>0){console.log(""),console.log(ce(n,"Config defaults (clusterSenderBindings)"));for(let[c,m]of u)console.log(` ${w(n,c)} ${V(n,"->")} ${S(n,m)}`)}}else console.log(""),console.log(me(n,"(cluster disabled \u2014 /config set clusterEnabled true to enable, then /c use <label-or-id> from each sender)"));return}if(o==="use"||o==="bind"){let s=t[1],i=t.slice(2).join(" ").trim();if(!s||!i){console.error(C(r,"Usage: omnish cluster use <senderE164|tg:id> <label-or-id>")),process.exitCode=1;return}let a=nk(s);if(!a){console.error(C(r,`Could not parse sender "${s}". Use +E164 (WhatsApp) or tg:<user_id> (Telegram).`)),process.exitCode=1;return}let{state:l,resolved:d}=Zc(a,i);if(!d.ok){if(d.reason==="ambiguous-label"){let c=(d.matches??[]).map(m=>`${m.nodeId}(${m.label})`).join(", ");console.error(C(r,`Label "${i}" matches multiple machines: ${c}. Use the 8-character id.`))}else console.error(C(r,`No machine matches "${i}". Run /c status from the chat first to populate the roster, or pass an 8-character node id.`));process.exitCode=1;return}console.log(`${ce(n,"cluster:")} ${S(n,`${a} -> ${d.peer.nodeId} (${d.peer.label}).`)}`);let u=$();console.log(""),console.log(Ri(l,u,a));return}if(o==="here"){console.error(C(r,"omnish cluster here is no longer available. Use: omnish cluster use <senderE164|tg:id> <label-or-id>")),console.error(C(r,"Or send /c here from the controller's chat on the machine you want to bind.")),process.exitCode=1;return}console.error(C(r,"Usage: omnish cluster [status | use <sender> <label-or-id>]")),process.exitCode=1;return}case"security":{let n=$(),r=Rt(n),o=t.includes("--json");console.log(o?Zl(r):oi(r,process.stdout)),lr(r)&&(process.exitCode=1);return}case"service":{tk(t);return}case"config":{await Wp(t);return}case"tunnel":{await Ed(t);return}case"platform":{await Xp(t);return}case"ui":{let n=ok(t);if(n.help){xm();return}if(!Number.isFinite(n.port)||n.port<1||n.port>65535){console.error(C(process.stderr,"port must be between 1 and 65535.")),process.exitCode=1;return}let r=rm(n.token);await dm({host:n.host,port:n.port,meta:r});let o=process.stdout,s=pm(n.port);console.log(""),console.log(`${Ce(o,"ui")} ${w(o,"listening")}`),console.log(`${w(o,"bind:")} ${S(o,`${n.host}:${n.port}`)}`),console.log(`${w(o,"setup token:")} ${S(o,r.token)}`),console.log(`${w(o,"token file:")} ${V(o,ct)}`),console.log(""),console.log(me(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(` ${S(o,`http://127.0.0.1:${n.port}/`)}`);for(let i of s)console.log(` ${S(o,i)}`);console.log(""),console.log(`${w(o,"Quick link (same Wi\u2011Fi):")} ${S(o,`http://127.0.0.1:${n.port}/?token=${encodeURIComponent(r.token)}`)}`),console.log("");return}default:qa(),e&&(process.exitCode=1)}}sk().catch(e=>{console.error(C(process.stderr,String(e))),process.exit(1)});
377
+ Updates (last check): ${Q}`:F}}catch(k){return{ok:!1,error:String(k)}}}};if(K=async(k,x)=>{let L=$();await Nr(L,m,f,h,g,k,A,X,k.peerKey,P,_a({sendTg:x},{surface:"telegram"}),{surface:"telegram"})},i){let k=await Vi(a,()=>$(),K,{decorate:c});b.sendText=k.sendText,b.sendMedia=k.sendMedia,b.stop=k.stop}ns({getCfg:()=>$(),getWaOutbound:()=>y,getTgSendMedia:()=>b.sendMedia,getTgSendText:()=>b.sendText});let ie=null;{let k=$();if(k.webhookEnabled){let x=k.webhookToken||Zb.randomBytes(32).toString("hex");k.webhookToken||W({webhookToken:x}),ie=rs({port:k.webhookPort,host:k.webhookHost,token:x},{sendToPeer:R,getDefaultPeerKey:()=>{let F=$();return F.allowFrom.length>0?`wa:${Lt(F.allowFrom[0])}`:F.telegramAllowFrom.length>0?`tg:${F.telegramAllowFrom[0]}`:null}}).stop}}e=ys({getRunningVersion:Ze,getConfig:$,log:M});let he=jo({getConfig:$,sendToPeer:R,sendMediaToPeer:T}),D=io({getConfig:$,sendToPeer:R}),le=!i,Y=()=>{E=!0,he(),D(),e?.(),e=null,ie?.(),xm(),Kn(),b.stop?.().catch(()=>{}),v?.dispose(),m.killAllRunning(),gn().stopAll().catch(()=>{}),console.error(`
378
+ ${C(process.stderr,"shutting down\u2026")}`),process.exit(0)};if(process.on("SIGINT",Y),process.on("SIGTERM",Y),s)for(;!E;){let k=!1,x;try{x=await zo({printQr:!1,verbose:vn()}),await fn(qo(x),3e5,"Gateway: timed out waiting for WhatsApp connection (5 min).")}catch(F){console.error(C(process.stderr,`connect failed: ${String(F)}`)),await new Promise(Q=>setTimeout(Q,5e3));continue}y=sd(x,{decorate:c});let L=Zu(x,async F=>{let Q=$(),z=F.fromE164||ee(F.fromJid)||"",we=iu(F),Ue=`wa:${z}`;await Nr(Q,m,f,h,g,we,A,X,Ue,P,_a({sendWaText:(Ie,pe)=>y.sendText(Ie,pe),sendWaMedia:(Ie,pe)=>y.sendMedia(Ie,pe)},{surface:"whatsapp",waJid:F.fromJid}),{surface:"whatsapp"})});if(await new Promise(F=>{let Q=z=>{z.connection==="close"&&(qi(z.lastDisconnect)===mn.loggedOut&&(k=!0),x.ev.off("connection.update",Q),F())};x.ev.on("connection.update",Q)}),L(),le&&(A.dispose(),A=O(),v=A),jn(x),y=null,k&&(console.error(C(process.stderr,"session logged out. Run `omnish link` again.")),he(),e?.(),e=null,xm(),Kn(),b.stop?.().catch(()=>{}),process.exit(1)),E)break;await new Promise(F=>setTimeout(F,3e3))}else for(;!E;)await new Promise(k=>setTimeout(k,500));he(),e?.(),Kn(),b.stop?.().catch(()=>{}),A.dispose()}function uk(e){let t=!1,n="0.0.0.0",r=3789,o;for(let s=0;s<e.length;s++){let i=e[s];if(i==="--help"||i==="-h"){t=!0;continue}if(i==="--host"||i==="-H"){let l=e[++s];l||(console.error(C(process.stderr,"--host requires an address (e.g. 127.0.0.1).")),process.exit(1)),n=l;continue}if(i==="--port"||i==="-p"){let l=e[++s],d=Number.parseInt(l??"",10);Number.isFinite(d)||(console.error(C(process.stderr,"--port requires a number.")),process.exit(1)),r=d;continue}if(i==="--token"||i==="-t"){let l=e[++s];(!l||l.startsWith("-"))&&(console.error(C(process.stderr,"--token requires a secret string.")),process.exit(1)),o=l;continue}let a=process.stderr;console.error(C(a,`unknown ui argument: ${i}`)),console.error(C(a,"Try: omnish ui --help")),process.exit(1)}return{help:t,host:n,port:r,token:o}}function Em(){let e=process.stdout,t=[`${Ce(e,"omnish ui")} ${w(e,"[options]")}`,V(e,"Serve the browser setup panel on your LAN (token-gated). Same trust as editing config on disk."),"",J(e,"Usage:"),` ${S(e,"omnish ui [options]")}`,"",J(e,"Options:"),...vt(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 ${ut}).`},{left:"-h, --help",right:"This help."}],n=>w(e,n)),"",`${w(e,"Warning:")} ${fe(e,"Listening on all interfaces exposes a remote control panel on your network \u2014 use a trusted LAN.")}`,""];console.log(t.join(`
379
+ `))}async function dk(){let[,,e,...t]=process.argv;if(e==="--version"||e==="-v"||e==="-V"){let n=process.stdout;console.log(`${Ce(n,"omnish")} ${w(n,Ze())}`);return}if(e==="--help"||e==="-h"){el();return}if(e==="help"){tk(t[0]);return}switch(te(),e){case"link":{let n=ek(t);if(n.kind==="help"){Cm();return}if(n.kind==="error"){let r=process.stderr,o=n.message.replace(/^\[omnish\]\s*/,"");console.error(C(r,o)),process.exitCode=1;return}if(n.kind==="tg"){let r=n.token.trim(),o=process.stderr,s=process.stdout;if(!pt(r)){console.error(C(o,"That does not look like a Telegram bot token (expect digits:secret from @BotFather).")),process.exitCode=1;return}Nt(r);let i=nt()?"both":"telegram";Xr(i),console.log([`${ue(s,"Telegram")} ${S(s,"bot token saved to")} ${w(s,_)}`,`${w(s,"gatewayMode:")} ${ue(s,i)}`,"",S(s,"Next:"),` ${w(s,"1.")} ${S(s,"Find your numeric user id (e.g. t.me/userinfobot), then:")} ${ue(s,"omnish allow tg:<id>")}`,` ${w(s,"2.")} ${ue(s,"omnish run")}`,""].join(`
380
+ `));return}await pd({verbose:vn(),force:n.force});return}case"run":{let n=nk(t);if(n.help){Rm();return}if(n.verbose&&js(!0),n.background){rk(n.logFile,n.verbose);return}await ck();return}case"stop":ok();return;case"logout":{try{en.rmSync(ne,{recursive:!0,force:!0}),console.log(B(process.stdout,"Session removed. Run `omnish link` to pair again."))}catch(n){console.error(C(process.stderr,`logout failed: ${String(n)}`)),process.exitCode=1}return}case"allow":{let n=t[0],r=process.stdout,o=process.stderr;if(!n){console.error(C(o,"Usage: omnish allow +<E164> or omnish allow tg:<user_id>")),process.exitCode=1;return}try{let s=Qr(n);console.log(`${w(r,"allowFrom:")} ${S(r,s.allowFrom.join(", ")||"(empty)")}`),console.log(`${w(r,"telegramAllowFrom:")} ${S(r,s.telegramAllowFrom.join(", ")||"(empty)")}`)}catch(s){console.error(C(o,String(s).replace(/^\[omnish\]\s*/,""))),process.exitCode=1}return}case"deny":{let n=t[0],r=process.stdout,o=process.stderr;if(!n){console.error(C(o,"Usage: omnish deny +<E164> or omnish deny tg:<user_id>")),process.exitCode=1;return}try{let s=Vr(n);console.log(`${w(r,"allowFrom:")} ${S(r,s.allowFrom.join(", ")||"(empty)")}`),console.log(`${w(r,"telegramAllowFrom:")} ${S(r,s.telegramAllowFrom.join(", ")||"(empty)")}`)}catch(s){console.error(C(o,String(s).replace(/^\[omnish\]\s*/,""))),process.exitCode=1}return}case"i":case"interactive":{await vm(t);return}case"status":{let n=process.stdout,r=$(),o=t.includes("--check-updates"),s=new Et().list(),i=s.filter(h=>h.status==="running").length,a=ve(r),l=Ct(r),d=r.gatewayMode==="whatsapp"||r.gatewayMode==="both",u=r.gatewayMode==="telegram"||r.gatewayMode==="both",c=nt(),m=c?cd(ne):null,f=[];if(f.push(`${Ce(n,"omnish")} ${w(n,Ze())}`,`${w(n,"gatewayMode:")} ${S(n,r.gatewayMode)}`,`${w(n,"data dir:")} ${S(n,Za.dirname(ne))}`,"",`${w(n,"gateway process:")} ${S(n,ik().replace(/^gateway process: /,""))}`,"",Bt(n),ue(n,"whatsapp"),` ${w(n,"in use:")} ${d?S(n,"yes"):fe(n,"no (gatewayMode is telegram-only)")}`),d){let h=c?S(n,`linked (${ne})`):fe(n,"missing \u2014 run omnish link");f.push(` ${w(n,"session:")} ${h}`),c&&m&&f.push(` ${w(n,"linked as:")} ${S(n,m)}`),c&&!m&&f.push(` ${w(n,"linked as:")} ${V(n,"(not in creds yet \u2014 try again after omnish link completes)")}`)}if(f.push(` ${J(n,"Allowed")}`),r.allowFrom.length===0)f.push(` ${V(n,"(none)")}`);else for(let h of r.allowFrom)f.push(` ${w(n,"whatsapp:")} ${S(n,h)}`);if(f.push("",Bt(n),ue(n,"telegram")),f.push(` ${w(n,"in use:")} ${u?S(n,"yes"):fe(n,"no (gatewayMode is whatsapp-only)")}`),u){let h=a?S(n,sk(a)):fe(n,"(none) \u2014 omnish link --tg <token> or TELEGRAM_BOT_TOKEN");f.push(` ${w(n,"bot token:")} ${h}`)}if(f.push(` ${J(n,"Allowed")}`),r.telegramAllowFrom.length===0)f.push(` ${V(n,"(none)")}`);else for(let h of r.telegramAllowFrom)f.push(` ${w(n,"telegram:")} ${S(n,h)}`);if(f.push("",Bt(n),...await Gp(n)),f.push("",Bt(n),`${ue(n,"jobs")} ${w(n,`(recent): ${s.length} total, ${i} running`)}`,ac(l,n)),console.log(f.join(`
381
+ `)),o){let h=await Tr(Ze(),r),g=pr(h);console.log(""),console.log(Bt(n)),console.log(ke(g,"whatsapp").text)}if(r.clusterEnabled){let h=Xe(),g=ge(),y=Object.keys(g.senderBindings).length,b=Object.keys(r.clusterSenderBindings??{}).length;console.log(""),console.log(Bt(n)),console.log(ue(n,"cluster")),console.log(` ${w(n,"\xB7")} ${S(n,`enabled \xB7 label ${r.clusterLabel||$m.hostname()} \xB7 bindings ${y} chat / ${b} default`)}`),console.log(` ${w(n,"node:")} ${S(n,`${h.slice(0,8)}\u2026`)}`)}return}case"commands":{let n=process.stdout,r=$();console.log(tc(Tn(r),n)),console.log(""),console.log(Bt(n)),console.log(S(n,"Keys editable from chat via /config set (same trust as shell):")),console.log(w(n,Gt.join(", "))),console.log(`${w(n,"See also:")} ${S(n,_)}`);return}case"cluster":{let n=process.stdout,r=process.stderr,o=(t[0]??"status").toLowerCase();if(o==="status"){let s=$(),i=Xe();if(console.log(`${w(n,"clusterEnabled:")} ${S(n,String(s.clusterEnabled))}`),console.log(`${w(n,"clusterLabel:")} ${S(n,s.clusterLabel||"(hostname)")}`),console.log(`${w(n,"clusterRole:")} ${S(n,`${s.clusterRole} (informational; no longer used to gate traffic)`)}`),console.log(`${w(n,"node id:")} ${ue(n,i)}`),s.clusterEnabled){let a=ge(),l=Ei(s,null);console.log(""),console.log(S(n,l.wa.replace(/\*([^*]+)\*/g,"$1").replace(/`([^`]+)`/g,"$1"))),console.log(""),console.log(Ti(a,s,null));let d=Object.keys(a.senderBindings).length,u=Object.entries(s.clusterSenderBindings??{});if(d>0){console.log(""),console.log(ue(n,"Chat bindings (cluster-local.json)"));for(let[c,m]of Object.entries(a.senderBindings))console.log(` ${w(n,c)} ${V(n,"->")} ${S(n,`${m.nodeId} (${m.source}, since ${m.sinceIso})`)}`)}if(u.length>0){console.log(""),console.log(ue(n,"Config defaults (clusterSenderBindings)"));for(let[c,m]of u)console.log(` ${w(n,c)} ${V(n,"->")} ${S(n,m)}`)}}else console.log(""),console.log(fe(n,"(cluster disabled \u2014 /config set clusterEnabled true to enable, then /c use <label-or-id> from each sender)"));return}if(o==="use"||o==="bind"){let s=t[1],i=t.slice(2).join(" ").trim();if(!s||!i){console.error(C(r,"Usage: omnish cluster use <senderE164|tg:id> <label-or-id>")),process.exitCode=1;return}let a=lk(s);if(!a){console.error(C(r,`Could not parse sender "${s}". Use +E164 (WhatsApp) or tg:<user_id> (Telegram).`)),process.exitCode=1;return}let{state:l,resolved:d}=su(a,i);if(!d.ok){if(d.reason==="ambiguous-label"){let c=(d.matches??[]).map(m=>`${m.nodeId}(${m.label})`).join(", ");console.error(C(r,`Label "${i}" matches multiple machines: ${c}. Use the 8-character id.`))}else console.error(C(r,`No machine matches "${i}". Run /c status from the chat first to populate the roster, or pass an 8-character node id.`));process.exitCode=1;return}console.log(`${ue(n,"cluster:")} ${S(n,`${a} -> ${d.peer.nodeId} (${d.peer.label}).`)}`);let u=$();console.log(""),console.log(Ti(l,u,a));return}if(o==="here"){console.error(C(r,"omnish cluster here is no longer available. Use: omnish cluster use <senderE164|tg:id> <label-or-id>")),console.error(C(r,"Or send /c here from the controller's chat on the machine you want to bind.")),process.exitCode=1;return}console.error(C(r,"Usage: omnish cluster [status | use <sender> <label-or-id>]")),process.exitCode=1;return}case"security":{let n=$(),r=Ct(n),o=t.includes("--json");console.log(o?sc(r):ai(r,process.stdout)),dr(r)&&(process.exitCode=1);return}case"service":{ak(t);return}case"config":{await jp(t);return}case"tunnel":{await Ld(t);return}case"platform":{await rm(t);return}case"ui":{let n=uk(t);if(n.help){Em();return}if(!Number.isFinite(n.port)||n.port<1||n.port>65535){console.error(C(process.stderr,"port must be between 1 and 65535.")),process.exitCode=1;return}let r=lm(n.token);await gm({host:n.host,port:n.port,meta:r});let o=process.stdout,s=ym(n.port);console.log(""),console.log(`${Ce(o,"ui")} ${w(o,"listening")}`),console.log(`${w(o,"bind:")} ${S(o,`${n.host}:${n.port}`)}`),console.log(`${w(o,"setup token:")} ${S(o,r.token)}`),console.log(`${w(o,"token file:")} ${V(o,ut)}`),console.log(""),console.log(fe(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(` ${S(o,`http://127.0.0.1:${n.port}/`)}`);for(let i of s)console.log(` ${S(o,i)}`);console.log(""),console.log(`${w(o,"Quick link (same Wi\u2011Fi):")} ${S(o,`http://127.0.0.1:${n.port}/?token=${encodeURIComponent(r.token)}`)}`),console.log("");return}default:el(),e&&(process.exitCode=1)}}dk().catch(e=>{console.error(C(process.stderr,String(e))),process.exit(1)});