omnish 1.2.2-beta.1 → 1.2.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +13 -0
- package/README.md +41 -7
- package/dist/index.js +167 -155
- package/package.json +10 -19
package/dist/index.js
CHANGED
|
@@ -1,24 +1,24 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import
|
|
3
|
-
`,{mode:384})}function
|
|
4
|
-
`,{mode:384})}var
|
|
5
|
-
`).replace(/^\n+/,"").trimEnd()}function
|
|
6
|
-
`);let n=e.filter(i=>i.severity==="error"),r=e.filter(i=>i.severity==="warn"),o=e.filter(i=>i.severity==="info"),a=[`${oe(t,"Security check:")} `+
|
|
7
|
-
`).trimEnd()}function
|
|
8
|
-
`}function
|
|
9
|
-
`).replace(/^\n+/,"").trimEnd()}function
|
|
10
|
-
`).replace(/^\n+/,"").trimEnd()}function
|
|
2
|
+
import op from"node:dns";import ct from"node:fs";import Xo from"node:path";import wl from"node:os";import Cn from"node:fs";import Cl from"node:crypto";import vr from"node:fs";import Rl from"node:os";import Y from"node:path";function Ml(){let e=process.env.OMNISH_HOME?.trim();if(e)return Y.resolve(e);let t=Rl.homedir(),n=Y.join(t,".omnish"),r=Y.join(t,".whatslive");try{if(vr.existsSync(n))return n;if(vr.existsSync(r))return r}catch{}return n}var L=Ml(),W=Y.join(L,"auth"),Te=Y.join(L,"jobs"),es=Y.join(L,"apps"),ts=Y.join(L,"logs"),pe=Y.join(ts,"gateway.log"),ee=Y.join(L,"gateway.pid"),xt=Y.join(L,"gateway-control.json"),Gt=Y.join(L,"config-ui.json"),Ne=Y.join(L,"ui.json"),wn=Y.join(L,"ui-server.json"),A=Y.join(L,"config.json"),bn=Y.join(L,"shortcuts.json"),kn=Y.join(L,"recipes.json"),Sn=Y.join(L,"recipes-user.json"),_e=Y.join(L,"cowork"),$r=Y.join(_e,"tasks.json"),Cr=Y.join(_e,"pending-runs.json"),ns=Y.join(_e,"completions.sqlite");function Jt(e){let t=Cl.createHash("sha1").update(e,"utf8").digest("hex").slice(0,8);return Y.join(es,t)}function N(e){vr.mkdirSync(e,{recursive:!0,mode:448})}function q(){N(L),N(W),N(Te),N(es),N(ts),N(_e)}var os=/^(\d+)(?::\d+)?@s\.whatsapp\.net$/i,ss=/^(\d+)@c\.us$/i,is=/^(\d+)@lid$/i;function as(e){let t=e.trim();for(;;){let n=t;if(t=t.replace(/^whatsapp:/i,"").trim(),t===n)return t}}function Tl(e){let t=as(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 Il(e){let t=e.match(os);if(t)return t[1]??null;let n=e.match(ss);if(n)return n[1]??null;let r=e.match(is);return r?r[1]??null:null}function rs(e){let t=e.replace(/\D/g,"");return t?`+${t}`:""}function xn(e){return`${e.replace(/\D/g,"")}@s.whatsapp.net`}function j(e){let t=as(e);if(!t||Tl(t))return null;if(os.test(t)||ss.test(t)||is.test(t)){let r=Il(t);if(!r)return null;let o=rs(r);return o.length>1?o:null}if(t.includes("@"))return null;let n=rs(t);return n.length>1?n:null}function vn(e){return e.map(t=>String(t).trim()).filter(t=>!!t).map(t=>t==="*"?t:j(t)).filter(t=>!!t)}function ls(e){let t=new Set;for(let n of e)n!=="*"&&t.add(n);return t}function ie(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 cs(e){let t=new Set;for(let n of e){let r=ie(String(n));r&&t.add(r)}return t}function Rr(e){let t=e.trim();if(!t)return null;let n=t.toLowerCase();if(n.startsWith("tg:")||n.startsWith("telegram:")){let o=ie(t);return o?{kind:"tg",id:o}:null}let r=j(t);return r?{kind:"wa",normalized:r}:null}var P={gatewayMode:"whatsapp",telegramBotToken:"",telegramAllowFrom:[],allowFrom:[],commandPrefix:"!",syncTimeoutMs:3e4,syncMaxBytes:32768,jobLogTailLines:80,shell:"/bin/bash",appsCols:120,appsRows:40,appsFlushMs:300,appsMinIntervalMs:800,appsMaxFlushBytes:8192,appsMaxSessions:5,appsMaxSessionsTotal:20,appsMaxWaChars:3500,appsLogTailLines:80,appsSubmitDelayMs:50,appsClearInput:!0,appsClearInputDelayMs:20,appsClearInputSequence:"^A,^K",fileSendMaxBytes:0,fileReceiveMaxBytes:0,fileInboxSubdir:"inbox",fileReceiveRootMode:"downloads",fileReceiveRootPath:"",recipesAllowDangerousBuiltins:!1,recipesMaxTaskChars:0,recipesMacroDefaultCommand:'claude -p "$OMNISH_TASK"',clusterEnabled:!1,clusterLabel:"",clusterRole:"secondary",clusterSenderBindings:{},serviceInstallFromChat:!1,updateCheckEnabled:!1,updateCheckIntervalMs:864e5,updateCheckPackageName:"omnish",updateInfoUrl:""};function Al(e){return e==="downloads"||e==="omnishData"||e==="sessionCwd"||e==="processCwd"||e==="fixed"?e:P.fileReceiveRootMode}function El(e){return e==="primary"?"primary":"secondary"}function Ol(e){if(!e||typeof e!="object")return{};let t={};for(let[n,r]of Object.entries(e)){if(typeof r!="string")continue;let o=r.trim();if(!o)continue;let s=n.trim(),a=s.toLowerCase();if(a.startsWith("wa:")){let l=j(s.slice(3));l&&(t[`wa:${l}`]=o.slice(0,64));continue}if(a.startsWith("tg:")||a.startsWith("telegram:")){let l=ie(s);l&&(t[`tg:${l}`]=o.slice(0,64));continue}let i=j(s);i&&(t[`wa:${i}`]=o.slice(0,64))}return t}function $n(e){let t=typeof e.appsFlushMs=="number"&&e.appsFlushMs>=0?e.appsFlushMs:P.appsFlushMs,n=e.gatewayMode==="telegram"||e.gatewayMode==="both"||e.gatewayMode==="whatsapp"?e.gatewayMode:P.gatewayMode,r=typeof e.telegramBotToken=="string"?e.telegramBotToken:P.telegramBotToken,o=Array.isArray(e.telegramAllowFrom)?[...new Set(e.telegramAllowFrom.map(s=>ie(String(s))).filter(s=>!!s))].sort():P.telegramAllowFrom;return{...P,...e,gatewayMode:n,telegramBotToken:r,telegramAllowFrom:o,allowFrom:Array.isArray(e.allowFrom)?vn(e.allowFrom.map(String)).filter(s=>s!=="*"):P.allowFrom,commandPrefix:(()=>{let s=typeof e.commandPrefix=="string"&&e.commandPrefix.length>0?e.commandPrefix:P.commandPrefix;return s==="! "?"!":s})(),syncTimeoutMs:typeof e.syncTimeoutMs=="number"&&e.syncTimeoutMs>0?e.syncTimeoutMs:P.syncTimeoutMs,syncMaxBytes:typeof e.syncMaxBytes=="number"&&e.syncMaxBytes>0?e.syncMaxBytes:P.syncMaxBytes,jobLogTailLines:typeof e.jobLogTailLines=="number"&&e.jobLogTailLines>0?e.jobLogTailLines:P.jobLogTailLines,shell:typeof e.shell=="string"&&e.shell.length>0?e.shell:P.shell,appsCols:typeof e.appsCols=="number"&&e.appsCols>0&&e.appsCols<=500?Math.floor(e.appsCols):P.appsCols,appsRows:typeof e.appsRows=="number"&&e.appsRows>0&&e.appsRows<=200?Math.floor(e.appsRows):P.appsRows,appsFlushMs:t,appsMinIntervalMs:typeof e.appsMinIntervalMs=="number"&&e.appsMinIntervalMs>=0?e.appsMinIntervalMs:P.appsMinIntervalMs,appsMaxFlushBytes:typeof e.appsMaxFlushBytes=="number"&&e.appsMaxFlushBytes>256?Math.floor(e.appsMaxFlushBytes):P.appsMaxFlushBytes,appsMaxSessions:typeof e.appsMaxSessions=="number"&&e.appsMaxSessions>0?Math.min(50,Math.floor(e.appsMaxSessions)):P.appsMaxSessions,appsMaxSessionsTotal:typeof e.appsMaxSessionsTotal=="number"&&e.appsMaxSessionsTotal>0?Math.min(200,Math.floor(e.appsMaxSessionsTotal)):P.appsMaxSessionsTotal,appsMaxWaChars:typeof e.appsMaxWaChars=="number"&&e.appsMaxWaChars>256?Math.floor(e.appsMaxWaChars):P.appsMaxWaChars,appsLogTailLines:typeof e.appsLogTailLines=="number"&&e.appsLogTailLines>0?Math.min(500,Math.floor(e.appsLogTailLines)):P.appsLogTailLines,appsSubmitDelayMs:typeof e.appsSubmitDelayMs=="number"&&e.appsSubmitDelayMs>=0?Math.min(500,Math.floor(e.appsSubmitDelayMs)):P.appsSubmitDelayMs,appsClearInput:typeof e.appsClearInput=="boolean"?e.appsClearInput:P.appsClearInput,appsClearInputDelayMs:typeof e.appsClearInputDelayMs=="number"&&e.appsClearInputDelayMs>=0?Math.min(200,Math.floor(e.appsClearInputDelayMs)):P.appsClearInputDelayMs,appsClearInputSequence:typeof e.appsClearInputSequence=="string"&&e.appsClearInputSequence.length>0?e.appsClearInputSequence.slice(0,200):P.appsClearInputSequence,fileSendMaxBytes:typeof e.fileSendMaxBytes=="number"&&e.fileSendMaxBytes>=0?e.fileSendMaxBytes===0?0:Math.min(2e9,Math.floor(e.fileSendMaxBytes)):P.fileSendMaxBytes,fileReceiveMaxBytes:typeof e.fileReceiveMaxBytes=="number"&&e.fileReceiveMaxBytes>=0?e.fileReceiveMaxBytes===0?0:Math.min(2e9,Math.floor(e.fileReceiveMaxBytes)):P.fileReceiveMaxBytes,fileInboxSubdir:typeof e.fileInboxSubdir=="string"&&e.fileInboxSubdir.length>0&&e.fileInboxSubdir.replace(/[/\\]/g,"").slice(0,80)||P.fileInboxSubdir,fileReceiveRootMode:Al(e.fileReceiveRootMode),fileReceiveRootPath:typeof e.fileReceiveRootPath=="string"?e.fileReceiveRootPath.trim().slice(0,4096):P.fileReceiveRootPath,recipesAllowDangerousBuiltins:typeof e.recipesAllowDangerousBuiltins=="boolean"?e.recipesAllowDangerousBuiltins:P.recipesAllowDangerousBuiltins,recipesMaxTaskChars:typeof e.recipesMaxTaskChars=="number"&&e.recipesMaxTaskChars>=0?e.recipesMaxTaskChars===0?0:Math.min(Number.MAX_SAFE_INTEGER,Math.floor(e.recipesMaxTaskChars)):P.recipesMaxTaskChars,recipesMacroDefaultCommand:typeof e.recipesMacroDefaultCommand=="string"&&e.recipesMacroDefaultCommand.trim().length>0?e.recipesMacroDefaultCommand.trim().slice(0,4096):P.recipesMacroDefaultCommand,clusterEnabled:typeof e.clusterEnabled=="boolean"?e.clusterEnabled:P.clusterEnabled,clusterLabel:typeof e.clusterLabel=="string"?e.clusterLabel.trim().slice(0,128):P.clusterLabel,clusterRole:El(e.clusterRole),clusterSenderBindings:Ol(e.clusterSenderBindings),serviceInstallFromChat:typeof e.serviceInstallFromChat=="boolean"?e.serviceInstallFromChat:P.serviceInstallFromChat,updateCheckEnabled:typeof e.updateCheckEnabled=="boolean"?e.updateCheckEnabled:P.updateCheckEnabled,updateCheckIntervalMs:typeof e.updateCheckIntervalMs=="number"&&e.updateCheckIntervalMs>0?Math.min(6048e5,Math.max(36e5,Math.floor(e.updateCheckIntervalMs))):P.updateCheckIntervalMs,updateCheckPackageName:typeof e.updateCheckPackageName=="string"&&e.updateCheckPackageName.trim().length>0?e.updateCheckPackageName.trim().slice(0,214):P.updateCheckPackageName,updateInfoUrl:typeof e.updateInfoUrl=="string"?e.updateInfoUrl.trim().slice(0,2048):P.updateInfoUrl}}function C(){if(q(),!Cn.existsSync(A)){let e=$n({});return Be(e),e}try{let e=Cn.readFileSync(A,"utf8"),t=JSON.parse(e);return $n(t)}catch{return $n({})}}function Be(e){q();let t=$n(e);Cn.writeFileSync(A,JSON.stringify(t,null,2)+`
|
|
3
|
+
`,{mode:384})}function Rn(e){let t=Rr(e);if(!t)throw new Error("Invalid entry. Use +E164 (WhatsApp) or tg:<user_id> (Telegram).");let n=C();if(t.kind==="wa"){if(t.normalized==="*")throw new Error("Wildcards are not allowed.");let r=new Set(n.allowFrom);r.add(t.normalized),n.allowFrom=[...r].sort()}else{let r=new Set(n.telegramAllowFrom);r.add(t.id),n.telegramAllowFrom=[...r].sort()}return Be(n),n}function Mn(e){let t=Rr(e);if(!t)throw new Error("Invalid entry. Use +E164 (WhatsApp) or tg:<user_id> (Telegram).");let n=C();return t.kind==="wa"?n.allowFrom=n.allowFrom.filter(r=>r!==t.normalized):n.telegramAllowFrom=n.telegramAllowFrom.filter(r=>ie(r)!==t.id),Be(n),n}function ne(e){let t=typeof process.env.TELEGRAM_BOT_TOKEN=="string"?process.env.TELEGRAM_BOT_TOKEN.trim():"";return t||(e.telegramBotToken??"").trim()}function Ze(e){let t=e.trim();return/^\d{6,}:[A-Za-z0-9_-]{20,}$/.test(t)}function vt(e){let t=C();return t.telegramBotToken=e.trim(),Be(t),C()}function Tn(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 In(e){let t=C();return t.gatewayMode=e,Be(t),C()}function An(e){let t=C();return t.clusterEnabled=e,Be(t),C()}function Ie(){try{return Cn.readdirSync(W).length>0}catch{return!1}}import us from"node:fs";import ds from"node:path";var Ll=new Set(["jpg","jpeg","png","gif","webp","bmp","tif","tiff","heic","avif"]),Pl=new Set(["mp4","mov","webm","mkv","avi","m4v","3gp"]),Fl=new Set(["mp3","ogg","opus","wav","m4a","flac","aac","wma"]),En={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 Nl(e){let t=e.replace(/^\./,"").toLowerCase();return Ll.has(t)?{category:"image",mimetype:En[t]??"image/jpeg"}:Pl.has(t)?{category:"video",mimetype:En[t]??"video/mp4"}:Fl.has(t)?{category:"audio",mimetype:En[t]??"audio/mpeg"}:{category:"document",mimetype:En[t]??"application/octet-stream"}}function et(e,t){let n;try{n=us.realpathSync(e)}catch{return{error:"File not found or unreadable."}}let r;try{r=us.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=ds.basename(n),s=ds.extname(n).slice(1),{category:a,mimetype:i}=Nl(s);return{absPath:n,category:a,mimetype:i,displayName:o}}import ps from"node:path";import{glob as _l,stat as Bl}from"node:fs/promises";function On(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 Hl(e){return/[*?[]/.test(e)}function Dl(e){return e.split(",").map(t=>t.trim()).filter(t=>t.length>0)}async function Ln(e,t){let n=Dl(t),r=new Set,o=[];for(let s of n){if(Hl(s)){for await(let i of _l(s,{cwd:e,withFileTypes:!1})){let l=ps.resolve(e,i);r.has(l)||(r.add(l),o.push(l))}continue}let a=ps.resolve(e,s);r.has(a)||(r.add(a),o.push(a))}return o}async function Pn(e){for(let t of e)try{if(!(await Bl(t)).isFile())return{ok:!1,error:`Not a file: ${t}`}}catch{return{ok:!1,error:`File not found: ${t}`}}return{ok:!0}}import Wl from"node:os";import Nn from"node:path";import Mr from"node:fs";import fs from"node:os";import tt from"node:path";var gs=tt.join(L,"sessions.json"),$t=new Map;function hs(){return{cwd:tt.resolve(fs.homedir())}}function jl(e){return e.startsWith("wa:")||e.startsWith("tg:")?e:`wa:${e}`}function Ul(){try{let e=Mr.readFileSync(gs,"utf8"),t=JSON.parse(e);for(let[n,r]of Object.entries(t)){if(!r||typeof r!="object")continue;let o=jl(n),a={cwd:typeof r.cwd=="string"&&r.cwd.length>0?tt.resolve(r.cwd):hs().cwd};r.fileReceiveRoot==="sessionCwd"&&(a.fileReceiveRoot="sessionCwd"),$t.set(o,a)}}catch{}}function ys(){N(L);let e={};for(let[t,n]of $t){let r={cwd:n.cwd};n.fileReceiveRoot==="sessionCwd"&&(r.fileReceiveRoot="sessionCwd"),e[t]=r}Mr.writeFileSync(gs,JSON.stringify(e,null,2)+`
|
|
4
|
+
`,{mode:384})}var ms=!1;function Tr(){ms||(Ul(),ms=!0)}function J(e){Tr();let t=$t.get(e);return t||(t=hs(),$t.set(e,t)),t}function Fn(e,t){Tr();let n=tt.resolve(t),r=J(e);r.cwd=n,$t.set(e,r),ys()}function ws(e){return J(e).fileReceiveRoot==="sessionCwd"?"sessionCwd":"default"}function Ir(e,t){Tr();let n=J(e);t==="default"?delete n.fileReceiveRoot:n.fileReceiveRoot="sessionCwd",$t.set(e,n),ys()}function bs(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 ks(e,t){if(t.kind==="home")return tt.resolve(fs.homedir());let n=t.value.replace(/^['"]|['"]$/g,"");return tt.isAbsolute(n)?tt.normalize(n):tt.resolve(e,n)}function Ss(e){try{return Mr.statSync(e).isDirectory()?{ok:!0}:{ok:!1,error:`Not a directory: ${e}`}}catch(t){return{ok:!1,error:String(t)}}}var Gl="Omnish";function xs(){return Nn.join(Wl.homedir(),"Downloads",Gl)}function Ct(e,t){let n=J(t);if(n.fileReceiveRoot==="sessionCwd")return n.cwd;switch(e.fileReceiveRootMode){case"downloads":return xs();case"omnishData":return Nn.join(L,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(!Nn.isAbsolute(r))throw new Error('fileReceiveRootPath must be an absolute path when fileReceiveRootMode is "fixed".');return Nn.resolve(r)}default:return xs()}}import ze from"node:fs";import $s from"node:path";import zt from"node:process";var ut="\x1B";function Jl(e){return e.replace(/\u001B\[[\d;]*m/g,"")}function _n(e){return Jl(e).length}function Ar(e,t,n,r,o=2){let s=Math.max(0,t-_n(n));return`${e}${n}${" ".repeat(s)}${" ".repeat(o)}${r}`}function Ge(e,t,n,r){if(n.length===0)return[];let o=n.map(a=>r(a.left)),s=Math.max(...o.map(_n));return n.map((a,i)=>Ar(t,s,o[i],S(e,a.right)))}var nt={primary:"#b4ff24",foreground:"#eef2f4",muted:"#8a9199",border:"#2a3139",error:"#ef4444",warn:"#a0e614",warnAlt:"#8cce04"};function zl(e){let t=e.replace(/^#/,"");return t.length!==6||!/^[0-9a-fA-F]+$/.test(t)?null:{r:parseInt(t.slice(0,2),16),g:parseInt(t.slice(2,4),16),b:parseInt(t.slice(4,6),16)}}function rt(e){let t=zl(e);return t?`${ut}[38;2;${t.r};${t.g};${t.b}m`:""}function Bn(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 Je(e,t,n){return!t||!Bn(e)?n:`${t}${n}${ut}[0m`}function V(e,t){return Je(e,`${ut}[1m`,t)}function ql(e,t){return Bn(e)?`${rt(nt.foreground)}${ut}[1m${t}${ut}[0m`:t}function re(e,t){return Je(e,`${ut}[2m`,t)}function oe(e,t){return Je(e,`${rt(nt.primary)}${ut}[1m`,t)}function K(e,t){return Je(e,rt(nt.primary),t)}function S(e,t){return Je(e,rt(nt.foreground),t)}function b(e,t){return Je(e,rt(nt.muted),t)}function Ql(e,t){return Je(e,rt(nt.border),t)}function Hn(e,t){return Je(e,rt(nt.error),t)}function we(e,t){return Je(e,rt(nt.warn),t)}function dt(e,t=60,n="\u2500"){let r=n.repeat(Math.max(1,t));return Ql(e,r)}function ce(e,t){return Bn(e)?`${`${b(e,"[")}${K(e,"omnish")}${b(e,"]")}`} ${t}`:`[omnish] ${t}`}function I(e,t){return Bn(e)?`${`${Hn(e,"[omnish]")}`} ${t}`:`[omnish] ${t}`}function vs(e,t){let n=[];for(let r of e)switch(r.kind){case"title":n.push("",oe(t,r.text),"");break;case"sub":n.push("",ql(t,r.text),"");break;case"gap":n.push("");break;case"p":n.push(S(t,r.text));break;case"bullet":n.push(`${b(t,"\u2022")} ${S(t,r.text)}`);break}return n.join(`
|
|
5
|
+
`).replace(/^\n+/,"").trimEnd()}function Dn(e){return(e&4)!==0}function Er(e){return(e&2)!==0}var Cs={error:3,warn:2,info:1};function Rs(e,t){let n=Cs[t];return e.filter(r=>Cs[r.severity]>=n)}function ot(e,t={}){let n=[],r=t.gatewayMode??e.gatewayMode,o=r==="whatsapp"||r==="both",s=r==="telegram"||r==="both";if(e.allowFrom.includes("*")&&n.push({severity:"error",code:"allow-wildcard",message:'allowFrom contains "*" \u2014 this must never be used; remove it from config immediately.',detail:`Edit ${A} and delete wildcard entries.`,fixHint:`Edit ${A} 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 ${A} 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 ${A} unless you trust every allowlisted identity.`}),!$s.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 ${A}.`});else try{ze.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 ${A} 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 ${A} points to a real file.`})}if(e.fileReceiveRootMode==="fixed"){let i=e.fileReceiveRootPath.trim();i?$s.isAbsolute(i)||n.push({severity:"error",code:"receive-fixed-not-absolute",message:`fileReceiveRootPath must be absolute when fileReceiveRootMode is "fixed": ${i}`,fixHint:`Use an absolute path for fileReceiveRootPath in ${A}.`}):n.push({severity:"error",code:"receive-fixed-empty",message:'fileReceiveRootMode is "fixed" but fileReceiveRootPath is empty.',fixHint:`Set fileReceiveRootPath to an absolute directory in ${A}, or change fileReceiveRootMode.`})}if(zt.platform!=="win32"){try{if(ze.existsSync(A)){let u=ze.statSync(A);(Dn(u.mode)||Er(u.mode))&&n.push({severity:"warn",code:"config-permissions",message:"config.json is accessible to users other than the owner (group/world).",detail:`Recommended: chmod 600 ${A}`,fixHint:`chmod 600 ${A}`})}}catch{}(typeof zt.getuid=="function"?zt.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(ze.existsSync(L)){let u=ze.statSync(L);(Dn(u.mode)||Er(u.mode))&&(l=!0,n.push({severity:"warn",code:"data-dir-permissive",message:"Omnish data directory is accessible to group or other users \u2014 auth, jobs, and logs may be exposed.",detail:`Recommended: chmod 700 ${L}`,fixHint:`chmod 700 ${L}`}))}}catch{}try{if(!l&&ze.existsSync(Te)){let u=ze.statSync(Te);(Dn(u.mode)||Er(u.mode))&&n.push({severity:"warn",code:"jobs-dir-permissive",message:"Jobs log directory is accessible to group or other users \u2014 command output may leak.",detail:`Recommended: chmod 700 ${Te}`,fixHint:`chmod 700 ${Te}`})}}catch{}try{if(ze.existsSync(W)){let u=ze.statSync(W);Dn(u.mode)&&n.push({severity:"warn",code:"auth-dir-world-readable",message:"WhatsApp auth directory is readable by others \u2014 session material may be exposed.",detail:`Recommended: chmod 700 ${W}`,fixHint:`chmod 700 ${W}`})}}catch{}}return(typeof zt.env.TELEGRAM_BOT_TOKEN=="string"?zt.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&&!ne(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 ${A} or export TELEGRAM_BOT_TOKEN=<token>.`}),e.clusterEnabled&&s&&n.push({severity:"info",code:"cluster-telegram-tokens",message:"Cluster mode with Telegram: use a distinct BotFather bot/token per omnish host if several gateways run Telegram \u2014 the same token cannot be long-polled twice.",fixHint:"Create separate bots for each machine or run Telegram on fewer hosts."}),n}function jn(e){return e.some(t=>t.severity==="error")}function Yl(e,t){switch(t){case"error":return Hn(e,"[ERROR]");case"warn":return we(e,"[WARN]");case"info":return b(e,"[INFO]")}}function Or(e,t,n,r){if(r.length!==0){t.push(V(e,`${n} (${r.length}):`));for(let o of r)t.push(` ${Yl(e,o.severity)} ${b(e,`${o.code}:`)} ${S(e,o.message)}`),o.detail&&t.push(` ${b(e,o.detail)}`),o.fixHint&&t.push(` ${b(e,`Fix: ${o.fixHint}`)}`);t.push("")}}function Lr(e,t){if(e.length===0)return[`${oe(t,"Security check:")} ${S(t,"no issues reported by automated rules.")}`,"",b(t,"Note: allowlisted remote shell access is still equivalent to sharing credentials with those identities.")].join(`
|
|
6
|
+
`);let n=e.filter(i=>i.severity==="error"),r=e.filter(i=>i.severity==="warn"),o=e.filter(i=>i.severity==="info"),a=[`${oe(t,"Security check:")} `+S(t,`${n.length} error(s), ${r.length} warning(s), ${o.length} note(s).`),""];return Or(t,a,"Errors",n),Or(t,a,"Warnings",r),Or(t,a,"Notes",o),a.push(b(t,"Allowlisted identities can run commands as this user; treat them like passwords.")),a.join(`
|
|
7
|
+
`).trimEnd()}function Pr(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 Ms(e){return`${JSON.stringify({findings:e,summary:Pr(e)},null,2)}
|
|
8
|
+
`}function Ts(e,t){let n=e.filter(i=>i.severity==="error").length,r=e.filter(i=>i.severity==="warn").length,o=e.filter(i=>i.severity==="info").length;if(n===0&&r===0&&o===0)return"security: ok (no automated findings)";let s=[];n&&s.push(`${n} error(s)`),r&&s.push(`${r} warning(s)`),o&&s.push(`${o} note(s)`);let a=t??"run `omnish security` for details";return`security: ${s.join(", ")} \u2014 ${a}`}function Is(e,t,n){let r=e.filter(l=>l.severity==="error").length,o=e.filter(l=>l.severity==="warn").length,s=e.filter(l=>l.severity==="info").length;if(r===0&&o===0&&s===0)return`${oe(t,"security:")} ${S(t,"ok (no automated findings)")}`;let a=[];r&&a.push(Hn(t,`${r} error(s)`)),o&&a.push(we(t,`${o} warning(s)`)),s&&a.push(b(t,`${s} note(s)`));let i=n??"run `omnish security` for details";return`${oe(t,"security:")} ${a.join(", ")} ${b(t,`\u2014 ${i}`)}`}var me="- ";function m(e){return{wa:e,tg:e}}function Q(e,t){return{wa:e,tg:t,tgHtml:!0}}function He(e,t){return t==="whatsapp"?{text:e.wa}:e.tgHtml?{text:e.tg,parseModeHtml:!0}:{text:e.tg}}function F(e){return e.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">")}function se(e){return e.replace(/[*_~`]/g,t=>({"*":"\u2217",_:"\uFF3F","~":"\u02DC","`":"\u2032"})[t]??t)}function Vl(e){let t=[];for(let n of e)switch(n.kind){case"title":t.push("",`*${n.text}*`,"");break;case"sub":t.push("",`_${n.text}_`,"");break;case"gap":t.push("");break;case"p":t.push(n.text);break;case"bullet":t.push(`${me}${n.text}`);break}return t.join(`
|
|
9
|
+
`).replace(/^\n+/,"").trimEnd()}function Kl(e){let t=[];for(let n of e)switch(n.kind){case"title":t.push("",`<b>${F(n.text)}</b>`,"");break;case"sub":t.push("",`<i>${F(n.text)}</i>`,"");break;case"gap":t.push("");break;case"p":t.push(F(n.text));break;case"bullet":t.push(`\u2022 ${F(n.text)}`);break}return t.join(`
|
|
10
|
+
`).replace(/^\n+/,"").trimEnd()}function B(e){return{wa:Vl(e),tg:Kl(e),tgHtml:!0}}function Rt(e){return[{kind:"title",text:"Omnish \u2014 quick help"},{kind:"p",text:"Per-chat shell cwd is stored under your data dir (see /wa help)."},{kind:"gap"},{kind:"sub",text:"Run commands"},{kind:"bullet",text:`${e.commandPrefix}<command> \u2014 sync shell in session cwd (timeout ${e.syncTimeoutMs} ms)`},{kind:"bullet",text:`${e.commandPrefix}cd <dir> \u2014 change session cwd (${e.commandPrefix}cd alone \u2192 home)`},{kind:"bullet",text:"!!start | !!stop \u2014 free shell (plain \u2192 sync shell only when no focused /apps session); space optional"},{kind:"bullet",text:"/bg <cmd> \u2014 background job; /jobs, /log, /tail, /kill"},{kind:"bullet",text:"/apps \u2026 \u2014 interactive app sessions (/apps help)"},{kind:"bullet",text:"/send selectors \u2014 host files \u2192 chat (/file); caption: selectors -- note"},{kind:"bullet",text:"/files \u2014 full help for /send and saving inbound media"},{kind:"bullet",text:"/receive \u2014 set this chat\u2019s inbound folder (e.g. session cwd)"},{kind:"bullet",text:"/computers \xB7 /pcs \xB7 /c \u2014 chat-driven cluster (/c here to take over, /c help)"},{kind:"bullet",text:"/config \u2014 view or change server settings (/config help, /config keys)"},{kind:"bullet",text:"/service \u2014 background service status, install hints, logs (/service help)"},{kind:"bullet",text:"/shortcut \u2014 this chat & shared shortcuts (/shortcut help); !name or /name expands chat override first, then shared"},{kind:"bullet",text:"/run \u2014 recipe-based task runs (/r); per-chat or gateway-shared /run add \u2014 /run list, /run help"},{kind:"bullet",text:"/cowork | /cw \u2014 scheduled & on-demand shell tasks; logs to ~/Cowork/\u2026 (/cowork help)"},{kind:"gap"},{kind:"sub",text:"Setup & gateway"},{kind:"bullet",text:"/wa help \u2014 WhatsApp link, allowlist"},{kind:"bullet",text:"/tg help \u2014 Telegram; /tg token <paste>"},{kind:"bullet",text:"/reload | /restart \u2014 apply config"},{kind:"bullet",text:"/updates \u2014 npm latest + optional notice URL; /updates cached \u2014 last snapshot"},{kind:"bullet",text:"/security \u2014 posture report; /security summary; /security tips; /security help"},{kind:"bullet",text:"/gateway | /gw | /mode \u2014 show or set gatewayMode"},{kind:"bullet",text:"/allow +E164 | /allow tg:id \u2014 allowlist; /deny \u2026; /allowlist"},{kind:"bullet",text:"/help \u2014 this message"}]}function As(e){let t=e.serviceInstallFromChat?[{kind:"bullet",text:"/service install \u2014 user-level systemd (Linux) or LaunchAgent (macOS)"},{kind:"bullet",text:"/service uninstall \u2014 remove that unit"}]:[{kind:"bullet",text:"/service install / uninstall \u2014 off by default. Enable: /config set serviceInstallFromChat true (same trust as shell)"}];return[{kind:"title",text:"Service and boot"},{kind:"p",text:"Inspect the gateway, get OS-specific install steps, or (if enabled) write the user service from chat. Same from the host terminal: omnish service help."},{kind:"bullet",text:"/service status \u2014 platform, data dir, pidfile, node + entry script"},{kind:"bullet",text:"/service instructions \u2014 copy-paste commands for this machine"},{kind:"bullet",text:"/service logs [n] \u2014 tail default gateway log (default 80 lines)"},...t,{kind:"gap"},{kind:"p",text:"See https://omnish.dev and docs/guides/background-and-boot.md."}]}function Es(){return[{kind:"title",text:"Chat config"},{kind:"p",text:`Same trust as shell \u2014 allowlisted senders can change many keys saved to ${A}.`},{kind:"gap"},{kind:"bullet",text:"/config show \u2014 full snapshot (telegramBotToken masked)"},{kind:"bullet",text:"/config get <key> \u2014 single field"},{kind:"bullet",text:"/config set <key> <value> \u2014 values can be quoted for paths with spaces"},{kind:"bullet",text:"/config keys \u2014 comma-separated whitelist of keys"},{kind:"gap"},{kind:"p",text:"After edits: /reload while omnish run is active (needed for gatewayMode and telegramBotToken)."}]}function Fr(){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: ${A}`}]}function Os(e){let t=["*Gateway status*","",`Mode: *${e.gatewayMode}*`,"- whatsapp \u2014 WhatsApp only","- telegram \u2014 Telegram only","- both \u2014 both channels","",`WhatsApp auth: ${e.authPresent?"present":"missing (omnish link)"}`,`Telegram token: ${e.tokenSet?"set":"not set"}`,`Allowlists: ${e.allowN} WA \xB7 ${e.tgAllowN} Telegram`,""];e.updateBrief&&t.push(`Updates: ${se(e.updateBrief)}`,""),t.push("Change: /gateway both \xB7 /gw tg","/updates \u2014 npm + optional notice URL",`Config: ${se(A)}`);let n=["<b>Gateway status</b>","",`<b>${F(e.gatewayMode)}</b> <i>current mode</i>`,"\u2022 whatsapp \u2014 WhatsApp only","\u2022 telegram \u2014 Telegram only","\u2022 both \u2014 both channels","",`${e.authPresent?"\u2713":"\u26A0"} WhatsApp auth: ${e.authPresent?"present":"missing \u2014 run omnish link"}`,`${e.tokenSet?"\u2713":"\u26A0"} Telegram token: ${e.tokenSet?"set":"not set"}`,`Allowlists: ${e.allowN} WA \xB7 ${e.tgAllowN} Telegram`,""];return e.updateBrief&&n.push(`<b>Updates</b> ${F(e.updateBrief)}`,""),n.push("<i>Change:</i> /gateway both \xB7 /gw tg","<i>/updates</i> \u2014 npm + optional notice URL",F(`Config: ${A}`)),Q(t.join(`
|
|
11
11
|
`),n.join(`
|
|
12
|
-
`))}function
|
|
12
|
+
`))}function qt(e){let t=["*Update check*","",`Running: *${se(e.runningVersion)}*`,`Checked (UTC): ${e.checkedAtIso}`,`npm package: ${se(e.registryPackage)}`];e.registryError?t.push("",`Registry: ${se(e.registryError)}`):e.registryLatest&&t.push("",e.updateAvailable?`*Newer version on npm:* ${se(e.registryLatest)} (upgrade when ready).`:`npm latest: ${se(e.registryLatest)} (this install is current or newer).`),e.infoError?t.push("",`Notice URL: ${se(e.infoError)}`):e.infoMessage&&(t.push("",`Notice: ${se(e.infoMessage)}`),e.infoLink&&t.push(se(e.infoLink))),t.push("","Config: updateCheckEnabled, updateCheckIntervalMs, updateCheckPackageName, updateInfoUrl");let n=["<b>Update check</b>","",`<b>Running</b> <code>${F(e.runningVersion)}</code>`,`<b>Checked (UTC)</b> ${F(e.checkedAtIso)}`,`<b>npm package</b> <code>${F(e.registryPackage)}</code>`];return e.registryError?n.push("",`<b>Registry</b> ${F(e.registryError)}`):e.registryLatest&&n.push("",e.updateAvailable?`<b>Newer on npm</b> <code>${F(e.registryLatest)}</code>`:`<b>npm latest</b> <code>${F(e.registryLatest)}</code> (current or newer here)`),e.infoError?n.push("",`<b>Notice URL</b> ${F(e.infoError)}`):e.infoMessage&&(n.push("",`<b>Notice</b> ${F(e.infoMessage)}`),e.infoLink&&n.push(F(e.infoLink))),n.push("","<i>Config keys:</i> updateCheckEnabled, updateCheckIntervalMs, updateCheckPackageName, updateInfoUrl"),Q(t.join(`
|
|
13
13
|
`),n.join(`
|
|
14
|
-
`))}function
|
|
14
|
+
`))}function Ls(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: ${L} (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 ${A}`},{kind:"bullet",text:"Run: omnish run \u2014 then use !cmd or /help here."},{kind:"gap"},{kind:"sub",text:"Troubleshooting"},{kind:"bullet",text:"Denied in logs but allowlist OK: may be LID mapping \u2014 try OMNISH_VERBOSE=1."},{kind:"bullet",text:"401 on link: omnish link --force after pnpm approve-builds && pnpm install"},{kind:"gap"},{kind:"p",text:`Current gatewayMode: ${e.gatewayMode}`}]}function Ps(e){let t=!!ne(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 ${A}, 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 ${A}; list ids in telegramAllowFrom.`},{kind:"bullet",text:"omnish run \u2014 then DM the bot with !cmd or /help."},{kind:"bullet",text:"After edits: /reload or /gateway both (saves + reloads when running)."},{kind:"gap"},{kind:"sub",text:"Notes"},{kind:"bullet",text:"Private text DMs only; groups ignored. Token stays secret."},{kind:"bullet",text:"/tg token stores token in config; chat history may retain it \u2014 prefer SSH/editor if worried."},{kind:"gap"},{kind:"p",text:`Token on host: ${t?"set (env or config)":"not set \u2014 add telegramBotToken or TELEGRAM_BOT_TOKEN"}`},{kind:"p",text:`gatewayMode: ${e.gatewayMode} \xB7 telegramAllowFrom: ${e.telegramAllowFrom.length} entries`}]}function Fs(e){let t=["*Allowlists*",""],n=["<b>Allowlists</b>",""];for(let r of e){let o=r.items.length?r.items.join(", "):"(none)";t.push(`*${r.label}* (${r.items.length})`,se(o),""),n.push(`<b>${F(r.label)}</b> (${r.items.length})`,F(o),"")}return Q(t.join(`
|
|
15
15
|
`).trimEnd(),n.join(`
|
|
16
|
-
`).trimEnd())}function
|
|
16
|
+
`).trimEnd())}function Nr(e){let t=[{label:"allowFrom (WhatsApp)",items:e.allowFrom},{label:"telegramAllowFrom",items:e.telegramAllowFrom}],n=["*Allowlist updated*","","Saved to config.",""],r=["<b>Allowlist updated</b>","","Saved to config.",""];for(let o of t){let s=o.items.length?o.items.join(", "):"(none)";n.push(`*${o.label}* (${o.items.length})`,se(s),""),r.push(`<b>${F(o.label)}</b> (${o.items.length})`,F(s),"")}return Q(n.join(`
|
|
17
17
|
`).trimEnd(),r.join(`
|
|
18
|
-
`).trimEnd())}function
|
|
19
|
-
`),n=["<b>Token saved</b>","","telegramBotToken written to config (not echoed).",F(`Config: ${
|
|
18
|
+
`).trimEnd())}function Ns(e){let t=["*Token saved*","","telegramBotToken written to config (not echoed).",`Config: ${se(A)}`,...e.flatMap(r=>["",r]),"","Send /reload so the gateway picks it up."].join(`
|
|
19
|
+
`),n=["<b>Token saved</b>","","telegramBotToken written to config (not echoed).",F(`Config: ${A}`),...e.map(r=>`<i>${F(r)}</i>`),"","Send /reload so the gateway picks it up."].join(`
|
|
20
20
|
`)+`
|
|
21
|
-
`;return
|
|
21
|
+
`;return Q(t.trimEnd(),n.trimEnd())}function _s(){return B([{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 Bs(e,t,n){let r=t?`
|
|
22
22
|
|
|
23
23
|
${t}`:n?`
|
|
24
24
|
|
|
@@ -29,77 +29,82 @@ ${F(t)}`:n?`
|
|
|
29
29
|
Start omnish run on the host to apply (or /reload from a running gateway).`:"",s=`*gatewayMode saved*
|
|
30
30
|
|
|
31
31
|
"${se(e)}"
|
|
32
|
-
${se(
|
|
32
|
+
${se(A)}${r}`,a=`<b>gatewayMode saved</b>
|
|
33
33
|
|
|
34
34
|
<code>${F(e)}</code>
|
|
35
|
-
${F(
|
|
36
|
-
`),
|
|
37
|
-
`);return
|
|
38
|
-
\u2192 ${t}
|
|
39
|
-
|
|
35
|
+
${F(A)}${o}`;return Q(s,a)}function Hs(){return B([{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 Ds(){return B([{kind:"title",text:"/deny \u2014 remove from allowlist"},{kind:"p",text:"Same forms as /allow: +E164 or tg:id"}])}function js(){return B([{kind:"title",text:"/bg \u2014 background job"},{kind:"p",text:"Usage: /bg <shell command>"},{kind:"bullet",text:"Example: /bg sleep 30 && echo done"}])}function _r(){return B([{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 Br(){return B([{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 (${A}) 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 Hr(){return B([{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 Us(e,t){let n=ws(t),r=J(t),o="";try{o=Ct(e,t)}catch(s){o=`(${String(s)})`}return B(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 Ws(){return B([{kind:"title",text:"Unknown /wa command"},{kind:"p",text:"Send /wa help for setup."}])}function Gs(){return B([{kind:"title",text:"Unknown /tg command"},{kind:"p",text:"Send /tg help or /tg token <botfather_token>."}])}function Js(){return B([{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 zs(e){return B([{kind:"title",text:"Unknown command"},{kind:"p",text:`Try /help or ${e.commandPrefix}<command>.`},{kind:"gap"},...Rt(e)])}function qs(e){return B([{kind:"title",text:"No command matched"},{kind:"p",text:`Use ${JSON.stringify(e.commandPrefix)}, a slash command, or /apps help.`},{kind:"gap"},...Rt(e)])}function Qs(){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"},...Fr()];return B(e)}function Dr(){return[{kind:"title",text:"Shortcuts (this gateway)"},{kind:"p",text:"Stored per-chat or shared for every chat on this gateway. Expansion: this chat wins, then shared. Bare token only \u2014 for task injection use /run recipes."},{kind:"p",text:"Scope flags: -g or --global = shared on this gateway; -p or --chat = private to this chat."},{kind:"gap"},{kind:"sub",text:"Manage"},{kind:"bullet",text:"/shortcut add <name> <command\u2026> \u2014 private by default; add -g|--global to share on this gateway"},{kind:"bullet",text:"/shortcut add -p|--chat <name> <command\u2026> \u2014 explicit private (this chat)"},{kind:"bullet",text:"/shortcut set <name> <command\u2026> \u2014 overwrite in chosen bucket; scope-only (same line): /shortcut set -g <name> or /shortcut set <name> -g (share); /shortcut set -p <name> or /shortcut set <name> -p (private)"},{kind:"bullet",text:"/shortcut list \u2014 merged view (/shortcuts); list --chat | -p | list --global | -g"},{kind:"bullet",text:"/shortcut show <name>; show --global|-g|--chat|-p to read one bucket"},{kind:"bullet",text:"/shortcut remove <name> \u2014 private bucket; remove --global|-g drops shared \xB7 rm, del \xB7 --chat|-p"},{kind:"bullet",text:"/alias \u2026 \u2014 same as /shortcut \u2026 (including /aliases \u2026)"},{kind:"gap"},{kind:"sub",text:"Run"},{kind:"bullet",text:"!name \u2014 expands once to the saved line (e.g. !cd \u2026 updates session cwd)"},{kind:"bullet",text:"/name \u2014 same expansion for slash-style navigation"},{kind:"p",text:"Shortcut bodies may start with !, /, etc.; nested shortcuts are not expanded."}]}function Ys(e){if(e.length===0)return m("(no shortcuts \u2014 /shortcut add <name> <command\u2026> or /shortcut add --global <name> <command\u2026>)");let t=e.every(l=>l.scope==="global"),n=e.every(l=>l.scope==="chat"),r=t?"_Shared (every chat)_":n?"_This chat only_":"_This chat + shared_",o=t?"<i>Shared (every chat)</i>":n?"<i>This chat only</i>":"<i>This chat + shared</i>",s=e.some(l=>l.scope==="chat")&&e.some(l=>l.scope==="global"),a=["*Shortcuts*",r,"",...e.map(l=>{let u=s?l.scope==="chat"?"[chat] ":"[global] ":"";return`${me}\`${u}${l.name}\` \u2192 ${l.body}`})].join(`
|
|
36
|
+
`),i=["<b>Shortcuts</b>",o,"",...e.map(l=>{let c=`${s?l.scope==="chat"?"[chat] ":"[global] ":""}${l.name}`;return`\u2022 <code>${F(c)}</code> \u2192 ${F(l.body)}`})].join(`
|
|
37
|
+
`);return Q(a,i)}function Qt(e,t,n="chat"){return m(`Shortcut saved: ${e}
|
|
38
|
+
\u2192 ${t}
|
|
39
|
+
(${n==="global"?"Shared on this gateway (every chat unless this chat overrides the name).":"Stored for this chat only."})`)}function Vs(e,t="chat"){return m(`Shortcut removed (${t==="global"?"shared":"this chat"}): ${e}`)}function Ks(e){return m(`Unknown shortcut: ${e}`)}function Xs(e,t){return m(`Unknown shortcut "${e}" in ${t==="global"?"shared shortcuts":"this chat"}.`)}function jr(e,t,n){let r=n?`
|
|
40
|
+
|
|
41
|
+
${n}`:"";return m(`${e}
|
|
42
|
+
\u2192 ${t}${r}`)}function Zs(){return[{kind:"title",text:"/run \u2014 recipe templates"},{kind:"sub",text:"Invoke"},{kind:"bullet",text:"/run <name> <task\u2026> \u2014 start attached session (plain DMs go to it until /apps detach)"},{kind:"bullet",text:"/r <name> <task\u2026> \u2014 short alias for /run"},{kind:"gap"},{kind:"sub",text:"Queue"},{kind:"bullet",text:"/run <name> -q <task\u2026> or /run <name> --queue <task\u2026> \u2014 FIFO per chat: head starts immediately; others wait in memory until the head exits cleanly (code=0, signal=0)"},{kind:"bullet",text:"Pending counts only jobs not yet started \u2014 the first queued item becomes Active right away, so /run queue can show Pending: 0 while a queued run is still executing"},{kind:"bullet",text:"/run queue \u2014 active session + recipe, numbered waiting list, paused flag"},{kind:"bullet",text:"/run queue resume \u2014 after a pause or non-clean exit, clear pause and start the next waiting item"},{kind:"gap"},{kind:"sub",text:"Discover"},{kind:"bullet",text:"/run list \u2014 featured, 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: "+kn},{kind:"bullet",text:"Built-in Claude recipes omit dangerous flags unless recipesAllowDangerousBuiltins=true."}]}function Xl(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 ei(e){let t=["*Recipes*","`/run <name> <task>`",""],n=["<b>Recipes</b>","<code>/run <name> <task></code>",""],r=(o,s)=>{let a=o.description?` \u2014 ${o.description}`:"",i=s&&o.dangerous?" [dangerous flags]":"";t.push(`${me}${o.name} \u2014 ${o.label??o.name}${a}${i}`);let l=o.description?` \u2014 ${F(o.description)}`:"",u=s&&o.dangerous?F(" [dangerous flags]"):"";n.push(`\u2022 ${F(o.name)} \u2014 ${F(o.label??o.name)}${l}${u}`)};if(e.featured.length>0){t.push("_Featured:_"),n.push("<i>Featured:</i>");for(let o of e.featured)r(o,!0);t.push(""),n.push("")}if(e.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(F("(no recipes \u2014 add host file or /run add)"))),Q(t.join(`
|
|
40
43
|
`).trimEnd(),n.join(`
|
|
41
|
-
`).trimEnd())}function
|
|
42
|
-
`))}function
|
|
43
|
-
`))}function
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
`)
|
|
47
|
-
`,{mode:384})}function
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
`)
|
|
51
|
-
|
|
44
|
+
`).trimEnd())}function Ur(e,t){let n=e.taskEnv??"OMNISH_TASK",r=[`Recipe: ${e.name}`,`Source: ${Xl(e.source)}`,`Label: ${e.label??"(none)"}`,`Task env: ${n}`,`Command: ${e.command}`];if(e.promptTemplate){r.push(`Template: ${e.promptTemplate.length} chars`);let s=e.promptTemplate.replace(/\s+/g," ").trim().slice(0,200);r.push(`Preview: ${s}${e.promptTemplate.length>200?"\u2026":""}`)}return e.category&&r.push(`Category: ${e.category}`),e.description&&r.push(`Description: ${e.description}`),e.dangerous&&r.push("Note: built-in includes gated dangerous CLI flags."),t&&r.push("",t),m(r.join(`
|
|
45
|
+
`))}function Yt(e,t,n="chat"){let r=n==="global"?"Shared on this gateway (every chat unless this chat overrides the name).":"Stored for this chat only.",o=[`Recipe saved: ${e}`,`\u2192 ${t.command}`,`(task env: ${t.taskEnv??"OMNISH_TASK"})`];return t.promptTemplate&&o.push(`Template stored: ${t.promptTemplate.length} chars`),o.push(`(${r})`),m(o.join(`
|
|
46
|
+
`))}function ti(e,t="chat"){return m(`Recipe removed (${t==="global"?"gateway-shared storage":"this chat"}): ${e}`)}function ni(e,t){return m(`Unknown recipe "${e}" in ${t==="global"?"gateway-shared storage":"this chat storage"}.`)}function ri(e,t){let n=t==="global"?"_Gateway-shared recipes_":"_This chat recipes_",r=t==="global"?"<i>Gateway-shared recipes</i>":"<i>This chat recipes</i>";if(e.length===0)return m(t==="global"?"(no gateway-shared recipes \u2014 /run add --global <name> <command\u2026>)":"(no recipes in this chat \u2014 /run add <name> <command\u2026>)");let o=["*Recipes*",n,""],s=["<b>Recipes</b>",r,""];for(let a of e){let i=a.description?` \u2014 ${a.description}`:"";o.push(`${me}${a.name} \u2014 ${a.label??a.name}${i}`);let l=a.description?` \u2014 ${F(a.description)}`:"";s.push(`\u2022 ${F(a.name)} \u2014 ${F(a.label??a.name)}${l}`)}return Q(o.join(`
|
|
47
|
+
`).trimEnd(),s.join(`
|
|
48
|
+
`).trimEnd())}function Wr(e){return m(`Unknown recipe: ${e}
|
|
49
|
+
/run list`)}function Gr(){return[{kind:"title",text:"Apps (interactive CLI)"},{kind:"sub",text:"Sessions"},{kind:"bullet",text:"/apps start <name> <command\u2026> \u2014 spawn in session cwd; auto-attaches"},{kind:"bullet",text:"/apps attach <name> | /apps detach"},{kind:"bullet",text:"/apps list | /apps info <name>"},{kind:"gap"},{kind:"sub",text:"Input"},{kind:"bullet",text:"/apps send <name> <text> \u2014 text + newline"},{kind:"bullet",text:">name text \u2014 shorthand for send"},{kind:"bullet",text:"/apps key <name> <KEY[,KEY\u2026]> \u2014 Enter, ^C, Up, Esc, \\x1b, \u2026"},{kind:"gap"},{kind:"sub",text:"Output & control"},{kind:"bullet",text:"/apps tail <name> [lines] | /apps since <name>"},{kind:"bullet",text:"/apps mute|unmute <name> | /apps raw <name> on|off"},{kind:"bullet",text:"/apps resize <name> <cols> <rows>"},{kind:"bullet",text:"/apps stop <name> | /apps kill <name> | /apps rm <name>"},{kind:"gap"},{kind:"p",text:"Attached: plain DMs go to the focused app. Escaped by ! prefix, /commands, or >other."},{kind:"p",text:"Free shell: !!start \u2014 plain DMs run as sync shell when no focused PTY; !!stop \u2014 off. Attached session wins over free shell for plain text."}]}function Zl(e){let{errors:t,warns:n,infos:r}=Pr(e),o=[{kind:"title",text:"Security check"}];if(e.length===0)return o.push({kind:"p",text:"No issues reported by automated rules."},{kind:"gap"},{kind:"p",text:"Allowlisted remote shell access is still equivalent to sharing credentials with those identities."}),o;o.push({kind:"p",text:`${t} error(s), ${n} warning(s), ${r} note(s).`}),o.push({kind:"gap"});let s=(a,i)=>{if(i.length!==0){o.push({kind:"sub",text:`${a} (${i.length})`});for(let l of i){let u=l.severity.toUpperCase();o.push({kind:"bullet",text:se(`[${u}] ${l.code}: ${l.message}`)}),l.detail&&o.push({kind:"bullet",text:se(l.detail)}),l.fixHint&&o.push({kind:"bullet",text:se(`Fix: ${l.fixHint}`)})}o.push({kind:"gap"})}};return s("Errors",e.filter(a=>a.severity==="error")),s("Warnings",e.filter(a=>a.severity==="warn")),s("Notes",e.filter(a=>a.severity==="info")),o.push({kind:"p",text:"Allowlisted identities can run commands as this user; treat them like passwords."}),o}function oi(e){return B(Zl(e))}function si(){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 ii(){return[{kind:"title",text:"/security commands"},{kind:"bullet",text:"/security \u2014 full report (same checks as omnish security)"},{kind:"bullet",text:"/security summary \u2014 one-line counts"},{kind:"bullet",text:"/security tips \u2014 short hardening checklist"},{kind:"bullet",text:"CLI: omnish security [--json] for scripts and monitoring"}]}import ci from"node:fs";import ec from"node:path";var pt="__omnish_shortcuts_global__",ai=500,ui=/^[a-zA-Z0-9][a-zA-Z0-9_-]{0,31}$/,tc=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"]),Vt=new Map,li=!1;function nc(){try{let e=ci.readFileSync(bn,"utf8"),t=JSON.parse(e);if(t&&typeof t=="object")for(let[n,r]of Object.entries(t)){if(!r||typeof r!="object")continue;let o={};for(let[s,a]of Object.entries(r)){let i=String(s).toLowerCase();typeof a=="string"&&a.length>0&&(o[i]=a.trim())}Vt.set(n,o)}}catch{}}function Un(){N(ec.dirname(bn));let e={};for(let[t,n]of Vt)Object.entries(n).length>0&&(e[t]={...n});ci.writeFileSync(bn,JSON.stringify(e,null,2)+`
|
|
50
|
+
`,{mode:384})}function Wn(){li||(nc(),li=!0)}function rc(e){return tc.has(e.trim().toLowerCase())}function mt(e){let t=e.trim();if(!t)return{ok:!1,error:"Name is empty."};let n=t.toLowerCase();return ui.test(n)?rc(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 Jr(e){let t=e.replace(/\r\n/g,`
|
|
51
|
+
`).replace(/\n/g," ").trim();return t?t.length>ai?{ok:!1,error:`Body too long (max ${ai} characters).`}:{ok:!0,body:t}:{ok:!1,error:"Body is empty."}}function De(e,t){Wn();let n=Vt.get(e);return!n&&t&&(n={},Vt.set(e,n)),n??{}}function di(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 zr(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 qr(e){let t=zr(e);return{scope:t.scope,remainder:t.remainder}}function pi(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 oc(e){let t=e.trim().toLowerCase();if(t==="--global"||t==="-g")return"global";if(t==="--chat"||t==="-p")return"chat"}function mi(e){let t=e.trim().match(/^(\S+)\s+(--global|-g|--chat|-p)\s*$/i);if(!t?.[1]||!t[2])return;let n=oc(t[2]);if(n)return{name:t[1],target:n}}function sc(e,t){Wn();let n=e,r=De(n,!1),o=De(pt,!1);if(t==="chat")return Object.entries(r).map(([i,l])=>({name:i,body:l,scope:"chat"})).sort((i,l)=>i.name.localeCompare(l.name));if(t==="global")return Object.entries(o).map(([i,l])=>({name:i,body:l,scope:"global"})).sort((i,l)=>i.name.localeCompare(l.name));let s=new Set([...Object.keys(r),...Object.keys(o)]),a=[];for(let i of[...s].sort()){if(i in r){let u=r[i];u!==void 0&&a.push({name:i,body:u,scope:"chat"});continue}let l=o[i];l!==void 0&&a.push({name:i,body:l,scope:"global"})}return a}function fi(e,t="merged"){return sc(e,t)}function Qr(e,t){let n=t.trim().toLowerCase(),r=De(e,!1)[n];return r!==void 0?r:De(pt,!1)[n]}function gi(e,t){let n=t.trim().toLowerCase(),r=De(e,!1)[n];if(r!==void 0)return{body:r,scope:"chat"};let o=De(pt,!1)[n];if(o!==void 0)return{body:o,scope:"global"}}function qe(e,t,n){let r=n.trim().toLowerCase(),o=e==="global"?pt:t;return De(o,!1)[r]}function Yr(e,t,n,r="chat"){let o=mt(t);if(!o.ok)throw new Error(o.error);let s=Jr(n);if(!s.ok)throw new Error(s.error);let a=r==="global"?pt:e,i=De(a,!0);i[o.normalized]=s.body,Un()}function hi(e,t,n="chat"){let r=t.trim().toLowerCase(),o=n==="global"?pt:e;Wn();let s=Vt.get(o);return!s||!(r in s)?!1:(delete s[r],Un(),!0)}function Vr(e,t,n){let r=mt(t);if(!r.ok)return{ok:!1,error:r.error};let o=r.normalized,s=e;Wn();let a=De(s,!0),i=De(pt,!0),l=a[o],u=i[o];if(n==="global"){if(l!==void 0){let c=Jr(l);return c.ok?(i[o]=c.body,delete a[o],Un(),{ok:!0,kind:"moved",target:"global",name:o}):{ok:!1,error:c.error}}return u!==void 0?{ok:!0,kind:"noop",message:`Shortcut "${o}" is already shared on this gateway.`}:{ok:!1,error:`No shortcut "${o}" in this chat to share. Add with /shortcut add ${o} \u2026 or make the shared copy private with /shortcut set -p ${o}.`}}if(u!==void 0){let c=Jr(u);return c.ok?(a[o]=c.body,delete i[o],Un(),{ok:!0,kind:"moved",target:"chat",name:o}):{ok:!1,error:c.error}}return l!==void 0?{ok:!0,kind:"noop",message:`Shortcut "${o}" is already private to this chat.`}:{ok:!1,error:`No shared shortcut "${o}" to make private. Add with /shortcut add --global ${o} \u2026 or share from this chat with /shortcut set -g ${o}.`}}function yi(e){let t=e.trim();return t.length>0&&!/\s/.test(t)&&ui.test(t.toLowerCase())}import ic from"node:crypto";import Zr from"node:fs";import ac from"node:path";var eo=/^[a-zA-Z0-9][a-zA-Z0-9_-]{0,31}$/,Tt="__omnish_recipes_global__",to=new Set(["help","list","show","add","set","remove","rm","del","run","r"]),Mt=new Map,wi=!1;function lc(){try{let e=Zr.readFileSync(Sn,"utf8"),t=JSON.parse(e);if(t&&typeof t=="object")for(let[n,r]of Object.entries(t)){if(!r||typeof r!="object")continue;let o={};for(let[s,a]of Object.entries(r)){let i=String(s).toLowerCase();a&&typeof a=="object"&&typeof a.command=="string"&&(o[i]=Qe(a))}Mt.set(n,o)}}catch{}}function Qe(e){let t=typeof e.taskEnv=="string"&&/^[A-Za-z_][A-Za-z0-9_]*$/.test(e.taskEnv)?e.taskEnv:"OMNISH_TASK",n=typeof e.promptTemplate=="string"&&e.promptTemplate.trim().length>0?e.promptTemplate.trim():void 0,r={command:String(e.command).trim(),taskEnv:t,label:typeof e.label=="string"?e.label.trim().slice(0,80):void 0,description:typeof e.description=="string"?e.description.trim().slice(0,200):void 0,category:typeof e.category=="string"?e.category.trim().slice(0,40):void 0,featured:typeof e.featured=="boolean"?e.featured:void 0,dangerous:typeof e.dangerous=="boolean"?e.dangerous:void 0};return n!==void 0&&(r.promptTemplate=n),r}function Jn(){N(ac.dirname(Sn));let e={};for(let[t,n]of Mt)Object.entries(n).length>0&&(e[t]={...n});Zr.writeFileSync(Sn,JSON.stringify(e,null,2)+`
|
|
52
|
+
`,{mode:384})}function zn(){wi||(lc(),wi=!0)}function bi(e){return zn(),Mt.get(e)??{}}function Kr(e){zn();let t=Mt.get(e);return t||(t={},Mt.set(e,t)),t}function cc(e,t){let n=e.taskEnv??"OMNISH_TASK",r=t.recipesMacroDefaultCommand.trim();return ft(r,n).ok?Qe({...e,command:r,promptTemplate:e.command}):e}function uc(e,t){if(e.promptTemplate||ft(e.command,t).ok||!qn(e.command,t))return!1;let n=e.command;return n.includes("```")||n.length>2e3||/^"Create\b/i.test(n)||n.includes("<<<")||n.includes(`
|
|
53
|
+
### `)}function no(e,t){let n=Qe(e),r=n.taskEnv??"OMNISH_TASK";return!n.promptTemplate&&uc(n,r)&&(n=cc(n,t)),n}function dc(e){try{let t=Zr.readFileSync(kn,"utf8"),n=JSON.parse(t);if(!n||typeof n!="object")return{};let r={};for(let[o,s]of Object.entries(n)){let a=o.trim().toLowerCase();if(!(!eo.test(a)||to.has(a))&&s&&typeof s=="object"&&typeof s.command=="string"){let l=no(s,e),u=Kt(l);if(!u.ok){console.warn(`[omnish] recipes.json: skipping "${a}": ${u.error}`);continue}r[a]=l}}return r}catch{return{}}}function pc(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 Gn(e,t,n){for(let[r,o]of Object.entries(t)){let s=r.toLowerCase();!eo.test(s)||to.has(s)||o.command.trim()&&e.set(s,{...o,name:s,source:n})}}function Xr(e,t){let n=bi(e),r={};for(let[o,s]of Object.entries(n)){let a=no(s,t);Kt(a).ok&&(r[o]=a)}return r}function ki(e,t){let n=new Map;return Gn(n,pc(t),"builtin"),Gn(n,dc(t),"global"),Gn(n,Xr(Tt,t),"shared"),Gn(n,Xr(e,t),"peer"),n}function je(e,t,n){let r=n.trim().toLowerCase();return ki(e,t).get(r)}function Si(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 ro(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 oo(e){let t=ro(e);return{scope:t.scope,remainder:t.remainder}}function xi(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 mc(e){let t=e.trim().toLowerCase();if(t==="--global"||t==="-g")return"global";if(t==="--chat"||t==="-p")return"chat"}function vi(e){let t=e.trim().match(/^(\S+)\s+(--global|-g|--chat|-p)\s*$/i);if(!t?.[1]||!t[2])return;let n=mc(t[2]);if(n)return{name:t[1],target:n}}function Ye(e,t,n,r){let o=r.trim().toLowerCase(),s=e==="global"?Tt:t,a=bi(s)[o];if(a===void 0)return;let i=no(a,n);if(Kt(i).ok)return{...i,name:o,source:e==="global"?"shared":"peer"}}function $i(e,t,n){if(n==="merged")return[];let r=n==="global"?Tt:e,o=n==="global"?"shared":"peer",s=Xr(r,t);return Object.entries(s).map(([a,i])=>({...i,name:a,source:o})).sort((a,i)=>a.name.localeCompare(i.name))}function gt(e){let t=e.trim();if(!t)return{ok:!1,error:"Name is empty."};let n=t.toLowerCase();return eo.test(n)?to.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 Ci(e,t){let n=e.replace(/\r\n/g,`
|
|
54
|
+
`).trim();return n?t>0&&n.length>t?{ok:!1,error:`Task too long (max ${t} characters).`}:{ok:!0,task:n}:{ok:!1,error:"Task is empty."}}function qn(e,t){return e.includes("$")?e.includes(t):!1}function ft(e,t){let n=e.trim();if(!n)return{ok:!1,error:"Command is empty."};if(!/^[A-Za-z_][A-Za-z0-9_]*$/.test(t))return{ok:!1,error:"Invalid taskEnv (use letters, digits, _)."};if(!qn(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 Kt(e){let t=e.taskEnv??"OMNISH_TASK",n=ft(e.command,t);return n.ok?e.promptTemplate!==void 0&&e.promptTemplate.trim().length===0?{ok:!1,error:"promptTemplate cannot be empty."}:{ok:!0}:n}function fc(e){let t=e,n=null,r=!1,o=-1,s=-1;for(let l=0;l<t.length;l+=1){let u=t[l];if(r){r=!1;continue}if(u==="\\"){r=!0;continue}if(n){u===n&&(n=null);continue}if(u==='"'||u==="'"){n=u;continue}if(u!=="-"||t[l+1]!=="-")continue;let c=l===0?"":t[l-1];if(c&&!/\s/.test(c))continue;let d=t.slice(l+2),p=/^(template|tamplate)\b/i.exec(d);if(!p)continue;let f=l+2+p[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 a=t.slice(0,o).trim(),i=t.slice(s).trim();return(i.startsWith('"')&&i.endsWith('"')&&i.length>=2||i.startsWith("'")&&i.endsWith("'")&&i.length>=2)&&(i=i.slice(1,-1)),{command:a,template:i}}function so(e,t){let n=e.replace(/\r\n/g,`
|
|
55
|
+
`).trim();if(!n)throw new Error("Recipe body is empty.");let r="OMNISH_TASK",{command:o,template:s}=fc(n);if(s!==void 0){if(!o)throw new Error("Command part (before --template) is empty.");if(!s.trim())throw new Error("Template part (after --template) is empty.");let c=ft(o,r);if(!c.ok)throw new Error(c.error);return Qe({command:o,promptTemplate:s})}let a=/\n---\n/,i=n.search(a);if(i>=0){let c=n.slice(0,i).trim(),d=n.slice(i).replace(/^\n---\n/,"").trim();if(!c)throw new Error("Command part (before ---) is empty.");if(!d)throw new Error("Template part (after ---) is empty.");let p=ft(c,r);if(!p.ok)throw new Error(p.error);return Qe({command:c,promptTemplate:d})}if(ft(n,r).ok)return Qe({command:n});let l=t.trim(),u=ft(l,r);if(!u.ok)throw new Error(`recipesMacroDefaultCommand invalid (${u.error}). Fix config or paste runner then --- then template.`);return Qe({command:l,promptTemplate:n})}function Ri(e,t,n){let r="`".repeat(3),o=`<<<${r}$${t}${r}>>>`,s=e;s.includes(o)&&(s=s.split(o).join(n));let a=`<<<${t}>>>`;s.includes(a)&&(s=s.split(a).join(n));let i=new RegExp(`\\$${t}\\b`,"g");return s.replace(i,n)}function io(e,t,n,r="chat"){let o=gt(t);if(!o.ok)throw new Error(o.error);let s=Qe({...n,command:n.command}),a=Kt(s);if(!a.ok)throw new Error(a.error);let i=r==="global"?Tt:e,l=Kr(i);l[o.normalized]=s,Jn()}function Mi(e,t,n="chat"){let r=t.trim().toLowerCase(),o=n==="global"?Tt:e;zn();let s=Mt.get(o);return!s||!(r in s)?!1:(delete s[r],Jn(),!0)}function ao(e,t,n,r){let o=gt(t);if(!o.ok)return{ok:!1,error:o.error};let s=o.normalized,a=e,i=Tt;zn();let l=Kr(a),u=Kr(i),c=l[s],d=u[s],p=f=>{let h=Qe({...f}),g=Kt(h);if(!g.ok)throw new Error(g.error);return h};try{if(n==="global"){if(c!==void 0){let g=p(c);return u[s]=g,delete l[s],Jn(),{ok:!0,kind:"moved",target:"global",name:s}}if(d!==void 0)return{ok:!0,kind:"noop",message:`Recipe "${s}" is already gateway-shared.`};let 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(d!==void 0){let h=p(d);return l[s]=h,delete u[s],Jn(),{ok:!0,kind:"moved",target:"chat",name:s}}if(c!==void 0)return{ok:!0,kind:"noop",message:`Recipe "${s}" is already stored for this chat only.`};let 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 Ti(e,t){let n=[...ki(e,t).values()],r=n.filter(i=>i.featured).sort((i,l)=>i.name.localeCompare(l.name)),o=n.filter(i=>i.source==="peer").sort((i,l)=>i.name.localeCompare(l.name)),s=n.filter(i=>i.source==="shared").sort((i,l)=>i.name.localeCompare(l.name)),a=n.filter(i=>!i.featured&&i.source!=="peer"&&i.source!=="shared").sort((i,l)=>i.name.localeCompare(l.name));return{featured:r,shared:s,yours:o,more:a}}function Qn(e){let t=ic.randomBytes(4).toString("hex");return`r-${e.replace(/[^a-zA-Z0-9_-]/g,"").slice(0,12)}-${t}`.slice(0,32)}import gc from"node:os";import*as Ai from"node-pty";function hc(e,t){return e.length<=t?e:`${e.slice(0,t)}
|
|
56
|
+
[...truncated]`}function yc(e){if(e===void 0||e===0)return null;let t=gc.constants.signals;for(let[n,r]of Object.entries(t))if(r===e)return n;return null}function Ii(e){try{process.platform==="win32"?e.kill():e.kill("SIGTERM")}catch{e.kill()}}function Yn(e,t,n){return new Promise(r=>{let o=Date.now(),s=!1,a="",i=n.cwd,l=null,u=!1,c=null,d=null,p,f=g=>{if(u)return;u=!0,p!==void 0&&clearTimeout(p),c?.dispose(),c=null,r(g);let y=d;d=null,queueMicrotask(()=>y?.dispose())},h={...process.env,TERM:"xterm-256color",COLORTERM:"truecolor",...i?{PWD:i}:{}};try{l=Ai.spawn(e,["-c",t],{name:"xterm-256color",cols:120,rows:40,cwd:i,env:h})}catch(g){f({code:null,stdout:"",stderr:String(g),durationMs:Date.now()-o,timedOut:!1,signal:null});return}p=setTimeout(()=>{s=!0,l&&Ii(l)},n.timeoutMs),c=l.onData(g=>{a+=g,a.length>n.maxBytes&&(a=hc(a,n.maxBytes),l&&Ii(l))}),d=l.onExit(g=>{f({code:g.exitCode,stdout:a,stderr:"",durationMs:Date.now()-o,timedOut:s,signal:yc(g.signal)})})})}import It from"node:fs";import po from"node:os";import Xt from"node:path";import Pi from"node:crypto";var co="\u2063omnish/c v1",wc=/([nlra])=([^\s\]]*)/g;function Ae(e){return e.replace(/-/g,"").slice(0,8)}function lo(e){return e.replace(/[\s\]\r\n]/g,"_").slice(0,64)}function bc(e){let t=lo(Ae(e.nodeId)),n=lo(e.label||""),r=e.role==="primary"?"p":"s",o=lo(e.activeNodeId?Ae(e.activeNodeId):"");return`${co} [n=${t} l=${n} r=${r} a=${o}]`}function Ei(e,t){let n=bc(t);if(e.includes(n))return e;let r=e.replace(/\s+$/,"");return r.length===0?n:`${r}
|
|
52
57
|
|
|
53
|
-
${n}`}function
|
|
54
|
-
`,{mode:384}),t}function
|
|
55
|
-
`,o=
|
|
56
|
-
`);for(let l of i){let u=l.nodeId===r,d=[(s?l.nodeId===s.nodeId:!1)?"your binding":null,u?"you":null].filter(Boolean).join(", "),
|
|
58
|
+
${n}`}function Oi(e){if(!e||!e.includes(co))return null;let t=e.indexOf(co),n=e.slice(t),r=n.indexOf("["),o=n.indexOf("]");if(r<0||o<0||o<r)return null;let s=n.slice(r+1,o),a={};for(let l of s.matchAll(wc))a[l[1]]=l[2]??"";if(!a.n)return null;let i=a.r==="p"?"primary":"secondary";return{nodeId:a.n,label:a.l??"",role:i,activeNodeId:a.a??""}}var Fi=3,kc="node-id",Sc="cluster-local.json",Li=8;function Ni(){return Xt.join(L,Sc)}function mo(){return Xt.join(L,kc)}function ve(){N(L);let e=mo();try{if(It.existsSync(e)){let n=It.readFileSync(e,"utf8").trim();if(/^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(n))return n}}catch{}let t=Pi.randomUUID();return It.writeFileSync(e,`${t}
|
|
59
|
+
`,{mode:384}),t}function _i(){return{schemaVersion:Fi,updatedAt:new Date().toISOString(),peers:[],senderBindings:{}}}function xc(e){if(!e||typeof e!="object")return{};let t={};for(let[n,r]of Object.entries(e)){if(!r||typeof r!="object")continue;let o=r;if(typeof o.nodeId!="string"||!o.nodeId)continue;let s=o.source==="config"?"config":"chat";t[n]={senderKey:typeof o.senderKey=="string"?o.senderKey:n,nodeId:o.nodeId,sinceIso:typeof o.sinceIso=="string"&&o.sinceIso?o.sinceIso:new Date(0).toISOString(),source:s}}return t}function X(){let e=Ni();try{let t=It.readFileSync(e,"utf8"),n=JSON.parse(t),r=Array.isArray(n.peers)?n.peers.filter(s=>typeof s=="object"&&s!==null&&typeof s.nodeId=="string"&&typeof s.label=="string").map(s=>({nodeId:s.nodeId,label:s.label,role:s.role==="primary"?"primary":"secondary",lastSeenIso:typeof s.lastSeenIso=="string"?s.lastSeenIso:new Date(0).toISOString()})):[],o=xc(n.senderBindings);return{schemaVersion:Fi,updatedAt:typeof n.updatedAt=="string"?n.updatedAt:new Date().toISOString(),peers:r,senderBindings:o}}catch{return _i()}}function vc(e,t){let n=Xt.dirname(e);N(n);let r=`${JSON.stringify(t,null,2)}
|
|
60
|
+
`,o=Xt.join(n,`.${Xt.basename(e)}.tmp.${process.pid}.${Pi.randomBytes(4).toString("hex")}`);It.writeFileSync(o,r,{mode:384}),It.renameSync(o,e)}function go(e){let t=Ni(),n=_i();for(let r=0;r<Li;r++){n=X(),e(n),n.updatedAt=new Date().toISOString();try{return vc(t,n),n}catch{}}throw new Error(`Could not write cluster local state after ${Li} attempts: ${t}`)}function Bi(e){return(e.clusterLabel??"").trim()||po.hostname()}function Z(e){return e.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">")}function Hi(e,t){let n=e.peers.findIndex(r=>r.nodeId===t.nodeId);n>=0?e.peers[n]=t:e.peers.push(t)}function Zt(e){return{nodeId:Ae(ve()),label:Bi(e),role:e.clusterRole,lastSeenIso:new Date().toISOString()}}function Di(e,t,n){let r=n.trim();if(!r)return{ok:!1,reason:"not-found"};let o=Zt(t),s=[o];for(let l of e.peers)l.nodeId!==o.nodeId&&s.push(l);let a=r.toLowerCase();if(/^[0-9a-f]{8}$/i.test(r)){let l=s.find(u=>u.nodeId.toLowerCase()===a);if(l)return{ok:!0,peer:l}}let i=s.filter(l=>l.label.toLowerCase()===a);return i.length===1?{ok:!0,peer:i[0]}:i.length>1?{ok:!1,reason:"ambiguous-label",matches:i}:{ok:!1,reason:"not-found"}}function Ve(e,t){let n=X(),r=n.senderBindings[t];if(r&&r.nodeId)return r;let o=e.clusterSenderBindings?.[t];if(typeof o=="string"&&o.trim()){let s=Di(n,e,o);if(s.ok)return{senderKey:t,nodeId:s.peer.nodeId,sinceIso:new Date(0).toISOString(),source:"config"}}return null}function fo(e,t,n="chat"){let r=C(),o=X(),s=Di(o,r,t);if(!s.ok)return{state:o,resolved:s};let a=new Date().toISOString();return{state:go(l=>{l.senderBindings[e]={senderKey:e,nodeId:s.peer.nodeId,sinceIso:a,source:n},Hi(l,{...s.peer,lastSeenIso:a})}),resolved:s}}function $c(e){let t=null;return{state:go(r=>{r.senderBindings[e]&&(t=r.senderBindings[e]??null,delete r.senderBindings[e])}),removed:t}}function ji(e,t){let n=Ae(ve()),r=new Date().toISOString();return go(o=>{if(Hi(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 Ui(e,t){let n=Ve(t,e);return n?n.nodeId===Ae(ve()):!1}function Cc(e,t){let n=Zt(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 Cc(e,t)===Ae(ve())}function Vn(e,t,n){let r=Ae(ve()),o=["*Computers*",""],s=n?Ve(t,n):null;n&&(s?o.push(`Your binding: \`${s.nodeId}\` (${s.source})`):o.push("Your binding: (none \u2014 send /c use <label-or-id>)"),o.push(""));let a=new Map;a.set(r,Zt(t));for(let l of e.peers)a.set(l.nodeId,l);let i=[...a.values()].sort((l,u)=>l.label.localeCompare(u.label));if(i.length===0)return o.push("(no peers observed yet \u2014 run /c status from this chat)"),o.join(`
|
|
61
|
+
`);for(let l of i){let u=l.nodeId===r,d=[(s?l.nodeId===s.nodeId:!1)?"your binding":null,u?"you":null].filter(Boolean).join(", "),p=d?` (${d})`:"";o.push(`${me}*${l.label}*${p}
|
|
57
62
|
id \`${l.nodeId}\` \xB7 role ${l.role}
|
|
58
63
|
seen ${l.lastSeenIso}`)}return o.push(""),o.push(`Updated: ${e.updatedAt}`),o.join(`
|
|
59
|
-
`)}function
|
|
60
|
-
`);for(let l of i){let u=l.nodeId===r,d=[(s?l.nodeId===s.nodeId:!1)?"your binding":null,u?"you":null].filter(Boolean).join(", "),
|
|
61
|
-
`)}function
|
|
62
|
-
`),d=["<b>This computer</b>","",`label: ${
|
|
63
|
-
`);return{wa:c,tg:d}}function
|
|
64
|
-
`),r=["<b>Computers</b> (per-sender bindings)","","Each allowlisted phone picks one machine to talk to. Other senders are not affected.","Selection persists; defaults can be set in config (clusterSenderBindings).","","Shorthand: /pcs \u2026 \xB7 /c \u2026","","\u2022 /c use <label-or-id> \u2014 bind your messages to that machine","\u2022 /c here \u2014 bind your messages to THIS machine","\u2022 /c using \u2014 show your current binding","\u2022 /c unuse \u2014 clear your chat-set binding","\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: ${
|
|
65
|
-
`);return
|
|
66
|
-
`),
|
|
67
|
-
`);return
|
|
68
|
-
`),c=["<b>Bound to this machine.</b>","","Your messages now route here. Other senders are not affected.","",
|
|
69
|
-
`);return
|
|
70
|
-
`),f=[`<b>Bound to ${
|
|
71
|
-
`);return
|
|
64
|
+
`)}function uo(e,t,n){let r=Ae(ve()),o=["<b>Computers</b>",""],s=n?Ve(t,n):null;n&&(s?o.push(`Your binding: <code>${Z(s.nodeId)}</code> (${Z(s.source)})`):o.push("Your binding: (none \u2014 send /c use <label-or-id>)"),o.push(""));let a=new Map;a.set(r,Zt(t));for(let l of e.peers)a.set(l.nodeId,l);let i=[...a.values()].sort((l,u)=>l.label.localeCompare(u.label));if(i.length===0)return o.push("(no peers observed yet \u2014 run /c status from this chat)"),o.join(`
|
|
65
|
+
`);for(let l of i){let u=l.nodeId===r,d=[(s?l.nodeId===s.nodeId:!1)?"your binding":null,u?"you":null].filter(Boolean).join(", "),p=d?` (${Z(d)})`:"";o.push(`\u2022 <b>${Z(l.label)}</b>${p}<br/> id <code>${Z(l.nodeId)}</code> \xB7 role ${Z(l.role)}<br/> seen ${Z(l.lastSeenIso)}`)}return o.push(""),o.push(`Updated: ${Z(e.updatedAt)}`),o.join(`
|
|
66
|
+
`)}function ho(e,t,n){return Vn(e,t,n??null).replace(/\*([^*]+)\*/g,"$1").replace(/`([^`]+)`/g,"$1")}function yo(e,t){let n=ve(),r=Ae(n),o=X(),s=e.clusterRole,a=Bi(e),i=t?Ve(e,t):null,l=i?i.nodeId===r:!1,u=t?i?`your binding: ${i.nodeId} (${i.source}${l?", here":""})`:"your binding: (none)":"",c=["*This computer*","",`label: ${a}`,`node id: \`${r}\` (full id in ${mo()})`,`host: ${po.hostname()}`,`clusterRole: ${s}`,u,`peers known: ${o.peers.length}`,`senderBindings (chat): ${Object.keys(o.senderBindings).length}`].filter(Boolean).join(`
|
|
67
|
+
`),d=["<b>This computer</b>","",`label: ${Z(a)}`,`node id: <code>${Z(r)}</code> (full id in ${Z(mo())})`,`host: ${Z(po.hostname())}`,`clusterRole: ${Z(s)}`,t?Z(u):"",`peers known: ${o.peers.length}`,`senderBindings (chat): ${Object.keys(o.senderBindings).length}`].filter(Boolean).join(`
|
|
68
|
+
`);return{wa:c,tg:d}}function Rc(e){let t=e.clusterRole,n=["*Computers* (per-sender bindings)","","Each allowlisted phone picks one machine to talk to. Other senders are not affected.","Selection persists; defaults can be set in config (clusterSenderBindings).","","Shorthand: /pcs \u2026 \xB7 /c \u2026","",`${me}/c use <label-or-id> \u2014 bind your messages to that machine`,`${me}/c here \u2014 bind your messages to THIS machine`,`${me}/c using \u2014 show your current binding`,`${me}/c unuse \u2014 clear your chat-set binding (config default still applies, if any)`,`${me}/c status \u2014 every online host replies with its own paragraph`,`${me}/c list \u2014 locally known roster (single responder)`,`${me}/c help \u2014 this help`,"",`clusterRole on this host: ${t}`].join(`
|
|
69
|
+
`),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 <label-or-id> \u2014 bind your messages to that machine","\u2022 /c here \u2014 bind your messages to THIS machine","\u2022 /c using \u2014 show your current binding","\u2022 /c unuse \u2014 clear your chat-set binding","\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: ${Z(t)}`].join(`
|
|
70
|
+
`);return Q(n,r)}function Mc(e,t){if(t.ok)return m("");if(t.reason==="ambiguous-label"){let n=(t.matches??[]).map(r=>`\`${r.nodeId}\` (${r.label})`).join(", ");return m(`Label "${e}" matches multiple machines: ${n}. Use the 8-character id with /c use <id>.`)}return m(`No machine matches "${e}". Send /c list to see ids and labels, or /c status to refresh the roster.`)}function Wi(e,t,n){let r=t.trim().split(/\s+/),o=r[0]?.toLowerCase()??"";if(!o||o==="help"){if(!e.clusterEnabled&&o!=="help"){let l=X();return he(l,e)?m("Cluster is disabled. Enable with: /config set clusterEnabled true (then /c use <label-or-id>). /c help for the new commands."):null}let i=X();return he(i,e)?Rc(e):null}let s=Ae(ve());if(o==="use"||o==="bind"){let i=r.slice(1).join(" ").trim();if(!n){let y=X();return he(y,e)?m("/c use is only available on chats with a known sender (allowlisted WhatsApp/Telegram)."):null}if(!i){let y=X();return he(y,e)?m("Usage: /c use <label-or-id>"):null}e.clusterEnabled||An(!0);let u=X().senderBindings[n]??null,{state:c,resolved:d}=fo(n,i,"chat");if(!d.ok)return he(c,e)?Mc(i,d):null;let p=d.peer.nodeId===s,f=u?u.nodeId===s:!1;if(!p)return null;let h=[`*Bound to ${d.peer.label}*`,`id \`${d.peer.nodeId}\``,"","Your messages now route to this machine. Other senders are not affected.","",Vn(c,e,n)].join(`
|
|
71
|
+
`),g=[`<b>Bound to ${Z(d.peer.label)}</b>`,`id <code>${Z(d.peer.nodeId)}</code>`,"","Your messages now route to this machine. Other senders are not affected.","",uo(c,e,n)].join(`
|
|
72
|
+
`);return Q(h,g)}if(o==="here"||o==="take"){if(!n){let d=X();return he(d,e)?m("/c here is only available on chats with a known sender (allowlisted WhatsApp/Telegram)."):null}e.clusterEnabled||An(!0);let{state:i,resolved:l}=fo(n,s,"chat");if(!l.ok)return he(i,e)?m("Could not bind to this machine."):null;let u=["*Bound to this machine.*","","Your messages now route here. Other senders are not affected.","",Vn(i,e,n)].join(`
|
|
73
|
+
`),c=["<b>Bound to this machine.</b>","","Your messages now route here. Other senders are not affected.","",uo(i,e,n)].join(`
|
|
74
|
+
`);return Q(u,c)}if(o==="using"){if(!n){let u=X();return he(u,e)?m("/c using is only available on chats with a known sender (allowlisted WhatsApp/Telegram)."):null}let i=Ve(e,n);if(i){if(i.nodeId!==s)return null;let d=[...X().peers,Zt(e)].find(h=>h.nodeId===i.nodeId)?.label??"(unknown label)",p=[`*Bound to ${d}*`,`id \`${i.nodeId}\` \xB7 source ${i.source}`,i.source==="chat"?`since ${i.sinceIso}`:"(from config)"].join(`
|
|
75
|
+
`),f=[`<b>Bound to ${Z(d)}</b>`,`id <code>${Z(i.nodeId)}</code> \xB7 source ${Z(i.source)}`,i.source==="chat"?`since ${Z(i.sinceIso)}`:"(from config)"].join(`
|
|
76
|
+
`);return Q(p,f)}let l=X();return he(l,e)?m("You are not bound to any machine. Send /c use <label-or-id> to bind, or set a default in config.json (clusterSenderBindings)."):null}if(o==="unuse"||o==="unbind"||o==="release"){if(!n){let p=X();return he(p,e)?m("/c unuse is only available on chats with a known sender (allowlisted WhatsApp/Telegram)."):null}let l=X().senderBindings[n]??null,{state:u}=$c(n);if(l){if(l.nodeId!==s)return null}else if(!he(u,e))return null;let c=Ve(e,n),d=c?`
|
|
72
77
|
Config default still applies: \`${c.nodeId}\`.`:`
|
|
73
|
-
No config default is set; nobody will answer until you /c use <label-or-id>.`;return
|
|
74
|
-
`)}function
|
|
75
|
-
`)}function
|
|
76
|
-
`));if(r==="show"){let a=
|
|
78
|
+
No config default is set; nobody will answer until you /c use <label-or-id>.`;return m(`Cleared your chat binding.${d}`)}if(o==="step-down"||o==="stepdown"){let i=X();return he(i,e)?m("/c step-down is no longer used. The cluster is per-sender: send /c unuse to clear your binding."):null}if(o==="status"){let i=yo(e,n);return Q(i.wa,i.tg)}if(o==="list"){let i=X(),l=n?Ve(e,n):null;if(l){if(l.nodeId!==s)return null}else if(!he(i,e))return null;return Q(Vn(i,e,n??null),uo(i,e,n??null))}let a=X();return he(a,e)?m(`Unknown /c subcommand "${o}". Try /c help`):null}function Gi(e,t){return An(!0),fo(e,t,"chat")}function ue(e){return e.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">")}function wo(e){let t=e.trim();return t?t.length<=8?"(set)":`${t.slice(0,4)}\u2026${t.slice(-4)}`:"(empty)"}function Ji(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 en(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 zi=new Set(["downloads","omnishData","sessionCwd","processCwd","fixed"]),At=["gatewayMode","clusterEnabled","clusterRole","clusterLabel","clusterSenderBindings","commandPrefix","syncTimeoutMs","syncMaxBytes","jobLogTailLines","shell","appsCols","appsRows","appsFlushMs","appsMinIntervalMs","appsMaxFlushBytes","appsMaxSessions","appsMaxSessionsTotal","appsMaxWaChars","appsLogTailLines","appsSubmitDelayMs","appsClearInput","appsClearInputDelayMs","appsClearInputSequence","fileSendMaxBytes","fileReceiveMaxBytes","fileInboxSubdir","fileReceiveRootMode","fileReceiveRootPath","recipesAllowDangerousBuiltins","recipesMaxTaskChars","recipesMacroDefaultCommand","telegramBotToken","serviceInstallFromChat","updateCheckEnabled","updateCheckIntervalMs","updateCheckPackageName","updateInfoUrl"];function qi(e){return At.includes(e)}function Tc(e){let t=ne(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: ${wo(t)}`,`telegramAllowFrom: ${e.telegramAllowFrom.length} entries`,"","*WhatsApp allowFrom*",`${e.allowFrom.length} entries`,"","*Apps*",`appsCols \xD7 appsRows: ${e.appsCols}\xD7${e.appsRows}`,`appsFlushMs / appsMinIntervalMs: ${e.appsFlushMs} / ${e.appsMinIntervalMs}`,`appsMaxFlushBytes: ${e.appsMaxFlushBytes}`,`appsMaxSessions / total: ${e.appsMaxSessions} / ${e.appsMaxSessionsTotal}`,`appsMaxWaChars: ${e.appsMaxWaChars}`,`appsLogTailLines: ${e.appsLogTailLines}`,`appsSubmitDelayMs: ${e.appsSubmitDelayMs}`,`appsClearInput: ${e.appsClearInput}`,`appsClearInputDelayMs: ${e.appsClearInputDelayMs}`,`appsClearInputSequence: ${e.appsClearInputSequence}`,"","*Files*",`fileSendMaxBytes / fileReceiveMaxBytes: ${e.fileSendMaxBytes} / ${e.fileReceiveMaxBytes}`,`fileInboxSubdir: ${e.fileInboxSubdir}`,`fileReceiveRootMode: ${e.fileReceiveRootMode}`,`fileReceiveRootPath: ${e.fileReceiveRootPath||"(empty)"}`,"","*Recipes*",`recipesAllowDangerousBuiltins: ${e.recipesAllowDangerousBuiltins}`,`recipesMaxTaskChars: ${e.recipesMaxTaskChars}`,`recipesMacroDefaultCommand: ${e.recipesMacroDefaultCommand}`,"","*Service (chat)*",`serviceInstallFromChat: ${e.serviceInstallFromChat}`,"","*Updates (optional)*",`updateCheckEnabled: ${e.updateCheckEnabled}`,`updateCheckIntervalMs: ${e.updateCheckIntervalMs}`,`updateCheckPackageName: ${e.updateCheckPackageName}`,`updateInfoUrl: ${e.updateInfoUrl?"(set)":"(empty)"}`,"",`File: ${A}`].join(`
|
|
79
|
+
`)}function Ic(e){let t=ne(e);return["<b>Config</b> (secrets masked)","",`<b>gatewayMode</b> ${ue(e.gatewayMode)}`,`<b>commandPrefix</b> ${ue(e.commandPrefix)}`,`<b>shell</b> ${ue(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: ${ue(e.clusterRole)}`,`clusterLabel: ${ue(e.clusterLabel||"(hostname)")}`,`clusterSenderBindings: ${Object.keys(e.clusterSenderBindings??{}).length} entries`,"","<b>Telegram</b>",`telegramBotToken: ${ue(wo(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>",`${ue(e.fileReceiveRootMode)} \xB7 inbox ${ue(e.fileInboxSubdir)}`,"","<b>Service (chat)</b>",`serviceInstallFromChat: ${e.serviceInstallFromChat}`,"","<b>Updates (optional)</b>",`updateCheckEnabled: ${e.updateCheckEnabled} \xB7 interval ms: ${e.updateCheckIntervalMs}`,`package: <code>${ue(e.updateCheckPackageName)}</code> \xB7 info URL: ${e.updateInfoUrl?"set":"empty"}`,"",`<code>${ue(A)}</code>`].join(`
|
|
80
|
+
`)}function Qi(e,t){if(!qi(t))return null;let n=t;if(n==="telegramBotToken")return`telegramBotToken: ${wo(ne(e))}`;let r=e[n];return`${t}: ${typeof r=="object"?JSON.stringify(r):String(r)}`}function Ac(e,t){let n=Qi(e,t);if(!n)return null;let r=n.split(": ");return r.length<2?ue(n):`<b>${ue(r[0])}</b> ${ue(r.slice(1).join(": "))}`}function Kn(e,t){let n=Ji(t),r=!1,o=!1,s=!1;if(e==="telegramBotToken"){if(!Ze(n))throw new Error("Invalid bot token format (expect digits:secret from BotFather).");return vt(n),r=!0,s=!0,{reloadSuggested:r,shellWarning:o,tokenSaved:s}}let a=C();switch(e){case"gatewayMode":{let i=Tn(n);if(!i)throw new Error("gatewayMode: use whatsapp | telegram | both (or wa, tg, b)");a.gatewayMode=i,r=!0;break}case"clusterEnabled":{let i=en(n);if(i===null)throw new Error("clusterEnabled: true or false");a.clusterEnabled=i;break}case"clusterRole":{let i=n.trim().toLowerCase();if(i!=="primary"&&i!=="secondary")throw new Error("clusterRole: primary | secondary");a.clusterRole=i;break}case"clusterLabel":a.clusterLabel=n.trim().slice(0,128);break;case"clusterSenderBindings":{let i=n.trim();if(i===""||i==="{}"||i.toLowerCase()==="clear"){a.clusterSenderBindings={};break}let l;try{l=JSON.parse(i)}catch{throw new Error('clusterSenderBindings: pass a JSON object, e.g. {"wa:+15551234567":"workshop-box"}')}if(!l||typeof l!="object"||Array.isArray(l))throw new Error("clusterSenderBindings must be a JSON object of sender \u2192 label/id");let u={};for(let[c,d]of Object.entries(l)){if(typeof d!="string"||!d.trim())throw new Error(`clusterSenderBindings.${c}: value must be a non-empty string`);u[c]=d.trim()}a.clusterSenderBindings=u;break}case"commandPrefix":if(!n.trim())throw new Error("commandPrefix cannot be empty");a.commandPrefix=n.trim().slice(0,32);break;case"syncTimeoutMs":{let i=Number.parseInt(n,10);if(!Number.isFinite(i)||i<=0)throw new Error("syncTimeoutMs: positive integer (ms)");a.syncTimeoutMs=i;break}case"syncMaxBytes":{let i=Number.parseInt(n,10);if(!Number.isFinite(i)||i<=0)throw new Error("syncMaxBytes: positive integer");a.syncMaxBytes=i;break}case"jobLogTailLines":{let i=Number.parseInt(n,10);if(!Number.isFinite(i)||i<=0)throw new Error("jobLogTailLines: positive integer");a.jobLogTailLines=i;break}case"shell":if(!n.trim())throw new Error("shell: non-empty path");a.shell=n.trim().slice(0,4096),o=!0;break;case"recipesMacroDefaultCommand":if(!n.trim())throw new Error('recipesMacroDefaultCommand: non-empty shell snippet with "$OMNISH_TASK"');a.recipesMacroDefaultCommand=Ji(n).trim().slice(0,4096);break;case"appsCols":case"appsRows":case"appsFlushMs":case"appsMinIntervalMs":case"appsMaxFlushBytes":case"appsMaxSessions":case"appsMaxSessionsTotal":case"appsMaxWaChars":case"appsLogTailLines":case"appsSubmitDelayMs":case"appsClearInputDelayMs":case"recipesMaxTaskChars":case"fileSendMaxBytes":case"fileReceiveMaxBytes":{let i=Number.parseInt(n,10);if(!Number.isFinite(i)||i<0)throw new Error(`${e}: non-negative integer`);a[e]=i;break}case"appsClearInput":{let i=en(n);if(i===null)throw new Error("appsClearInput: true or false");a.appsClearInput=i;break}case"appsClearInputSequence":a.appsClearInputSequence=n.trim().slice(0,200);break;case"fileInboxSubdir":a.fileInboxSubdir=n.trim().slice(0,80);break;case"fileReceiveRootMode":{let i=n.trim();if(!zi.has(i))throw new Error(`fileReceiveRootMode: ${[...zi].join(" | ")}`);a.fileReceiveRootMode=i;break}case"fileReceiveRootPath":a.fileReceiveRootPath=n.trim().slice(0,4096);break;case"recipesAllowDangerousBuiltins":{let i=en(n);if(i===null)throw new Error("recipesAllowDangerousBuiltins: true or false");a.recipesAllowDangerousBuiltins=i;break}case"serviceInstallFromChat":{let i=en(n);if(i===null)throw new Error("serviceInstallFromChat: true or false");a.serviceInstallFromChat=i;break}case"updateCheckEnabled":{let i=en(n);if(i===null)throw new Error("updateCheckEnabled: true or false");a.updateCheckEnabled=i;break}case"updateCheckIntervalMs":{let i=Number.parseInt(n,10);if(!Number.isFinite(i)||i<36e5)throw new Error("updateCheckIntervalMs: integer ms, minimum 3600000 (1 hour)");a.updateCheckIntervalMs=Math.min(6048e5,i);break}case"updateCheckPackageName":if(!n.trim())throw new Error("updateCheckPackageName: non-empty npm package name");a.updateCheckPackageName=n.trim().slice(0,214);break;case"updateInfoUrl":a.updateInfoUrl=n.trim().slice(0,2048);break}return Be(a),{reloadSuggested:r,shellWarning:o,tokenSaved:s}}async function Yi(e,t){let n=e.trim(),r=n.toLowerCase();if(!n||r==="help")return B(Es());if(r==="keys"||r==="help keys")return m(["*Configurable keys* (/config set)","",At.join(", "),"","Examples:","/config set clusterLabel work-laptop",'/config set fileReceiveRootPath "/path/with spaces"',"/config set gatewayMode both"].join(`
|
|
81
|
+
`));if(r==="show"){let a=C();return Q(Tc(a),Ic(a))}let o=n.match(/^get\s+(\S+)\s*$/i);if(o){let a=o[1],i=C(),l=Qi(i,a);if(!l)return m(`Unknown key "${a}". /config keys`);let u=Ac(i,a);return Q(`${l}
|
|
77
82
|
|
|
78
|
-
${
|
|
79
|
-
Reload: ${
|
|
80
|
-
Reload failed: ${
|
|
83
|
+
${A}`,`${u}<br/><br/><code>${ue(A)}</code>`)}let s=n.match(/^set\s+(\S+)\s+([\s\S]+)$/i);if(s){let a=s[1],i=s[2]??"";if(!qi(a))return m(`Unknown key "${a}". /config keys`);try{let{reloadSuggested:l,shellWarning:u,tokenSaved:c}=Kn(a,i),d="";if(t?.reload&&l){let y=await t.reload();d=y.ok?`
|
|
84
|
+
Reload: ${y.summary}`:`
|
|
85
|
+
Reload failed: ${y.error}`}else l?d=`
|
|
81
86
|
Send /reload while omnish run is active (gatewayMode / Telegram).`:d=`
|
|
82
|
-
Send /reload to pick up changes where applicable.`;let
|
|
87
|
+
Send /reload to pick up changes where applicable.`;let p=u?`
|
|
83
88
|
\u26A0 shell changed \u2014 affects all commands run via omnish.`:"",f=c?`
|
|
84
|
-
telegramBotToken saved (not echoed).`:"",
|
|
89
|
+
telegramBotToken saved (not echoed).`:"",h=`Set *${a}* (saved).${f}${p}${d}`,g=`<b>Set ${ue(a)}</b> (saved).${f?"<br/>token saved (not echoed).":""}${u?"<br/>\u26A0 shell path changed.":""}${ue(d)}`;return Q(h,g)}catch(l){return m(`Error: ${String(l)}`)}}return m("Unknown /config command. Try /config help or /config show")}import Xi from"node:fs";import Ot from"node:process";function Xn(e){let t=e.nodePath,n=e.scriptPath,r=e.omnishHome,o=["Linux (systemd --user), copy-paste on the host:","","mkdir -p ~/.config/systemd/user","# create ~/.config/systemd/user/omnish.service with ExecStart using:",`# ${t} ${n} run`,`# and Environment=OMNISH_HOME=${r}`,"# The generated unit uses Restart=on-failure and RestartSec=5 (tunable).","","systemctl --user daemon-reload","systemctl --user enable --now omnish.service","",'Optional (run without interactive login): loginctl enable-linger "$USER"',"","Full guide: docs/guides/background-and-boot.md \xB7 https://omnish.dev"].join(`
|
|
85
90
|
`),s=["macOS (LaunchAgent), copy-paste on the host:","","# Use contrib/dev.omnish.gateway.plist from the repo with paths filled as:",`# Node: ${t}`,`# Script: ${n}`,`# OMNISH_HOME: ${r}`,"# Generated plist uses KeepAlive for crash restart.","","cp \u2026/dev.omnish.gateway.plist ~/Library/LaunchAgents/","launchctl bootstrap gui/$(id -u) ~/Library/LaunchAgents/dev.omnish.gateway.plist","launchctl kickstart -k gui/$(id -u)/dev.omnish.gateway","","https://omnish.dev"].join(`
|
|
86
91
|
`),a=["Windows: Task Scheduler \u2014 Action = Start a program:","",`Program: ${t}`,`Arguments: "${n}" run`,"","Set user env OMNISH_HOME if needed. Optional XML: contrib/omnish-windows-task.xml","","https://omnish.dev"].join(`
|
|
87
92
|
`);return process.platform==="linux"?o:process.platform==="darwin"?s:process.platform==="win32"?a:[o,"","---","",s].join(`
|
|
88
|
-
`)}import{execFileSync as
|
|
93
|
+
`)}import{execFileSync as st}from"node:child_process";import Et from"node:fs";import nn from"node:os";import Ee from"node:path";import fe from"node:process";function ht(){let e=fe.execPath,t=fe.argv[1];if(!t||!t.trim())return{nodePath:e,scriptPath:"",omnishHome:L,error:"Cannot resolve gateway entry script (missing argv[1]). Run omnish from its CLI entry."};let n=Ee.resolve(t),r=fe.env.OMNISH_HOME?.trim(),o=r?Ee.resolve(r):L;return{nodePath:e,scriptPath:n,omnishHome:o}}function Vi(e){return/[ "'\\\s]/.test(e)?`"${e.replace(/\\/g,"\\\\").replace(/"/g,'\\"')}"`:e}function Ec(e){return`Environment="OMNISH_HOME=${e.replace(/\\/g,"\\\\").replace(/"/g,'\\"')}"`}function Oc(e){return`[Unit]
|
|
89
94
|
Description=omnish gateway (WhatsApp/Telegram)
|
|
90
95
|
After=network-online.target
|
|
91
96
|
Wants=network-online.target
|
|
92
97
|
|
|
93
98
|
[Service]
|
|
94
99
|
Type=simple
|
|
95
|
-
ExecStart=${`${
|
|
96
|
-
${
|
|
100
|
+
ExecStart=${`${Vi(e.nodePath)} ${Vi(e.scriptPath)} run`}
|
|
101
|
+
${Ec(e.omnishHome)}
|
|
97
102
|
Restart=on-failure
|
|
98
103
|
RestartSec=5
|
|
99
104
|
|
|
100
105
|
[Install]
|
|
101
106
|
WantedBy=default.target
|
|
102
|
-
`}function
|
|
107
|
+
`}function tn(e){return e.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""")}function Lc(e){let t=nn.homedir(),n=Ee.join(e.omnishHome,"logs","launchd-stdout.log"),r=Ee.join(e.omnishHome,"logs","launchd-stderr.log");N(Ee.dirname(n));let o=tn(e.nodePath),s=tn(e.scriptPath),a=tn(e.omnishHome),i=tn(n),l=tn(r);return`<?xml version="1.0" encoding="UTF-8"?>
|
|
103
108
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
104
109
|
<plist version="1.0">
|
|
105
110
|
<dict>
|
|
@@ -126,20 +131,20 @@ WantedBy=default.target
|
|
|
126
131
|
<string>${l}</string>
|
|
127
132
|
</dict>
|
|
128
133
|
</plist>
|
|
129
|
-
`}function
|
|
134
|
+
`}function Zn(){let e=ht();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=Ee.join(nn.homedir(),"Library/LaunchAgents/dev.omnish.gateway.plist");N(Ee.dirname(t));let n=Lc(e);Et.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{st("launchctl",["bootout",o,t],{stdio:"pipe"})}catch{}return st("launchctl",["bootstrap",o,t],{stdio:"inherit"}),st("launchctl",["kickstart","-k",`${o}/dev.omnish.gateway`],{stdio:"inherit"}),{ok:!0,detail:"LaunchAgent installed: ~/Library/LaunchAgents/dev.omnish.gateway.plist (label dev.omnish.gateway)."}}catch(t){return{ok:!1,detail:String(t)}}if(fe.platform==="linux")try{let t=Ee.join(nn.homedir(),".config","systemd","user");N(t);let n=Ee.join(t,"omnish.service"),r=Oc(e);return Et.writeFileSync(n,r,{mode:420}),st("systemctl",["--user","daemon-reload"],{stdio:"inherit"}),st("systemctl",["--user","enable","--now","omnish.service"],{stdio:"inherit"}),{ok:!0,detail:`User systemd unit installed: ${n} \u2014 enabled and started.`}}catch(t){return{ok:!1,detail:String(t)}}return{ok:!1,detail:`Unsupported platform: ${fe.platform}`}}function er(){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=Ee.join(nn.homedir(),"Library/LaunchAgents/dev.omnish.gateway.plist"),n=`gui/${typeof fe.getuid=="function"?fe.getuid():0}`;if(Et.existsSync(e)){try{st("launchctl",["bootout",n,e],{stdio:"pipe"})}catch{}Et.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=Ee.join(nn.homedir(),".config","systemd","user","omnish.service");try{st("systemctl",["--user","disable","--now","omnish.service"],{stdio:"pipe"})}catch{}Et.existsSync(e)&&Et.unlinkSync(e);try{st("systemctl",["--user","daemon-reload"],{stdio:"pipe"})}catch{}return{ok:!0,detail:"User systemd unit omnish.service removed (if it was present)."}}catch(e){return{ok:!1,detail:String(e)}}return{ok:!1,detail:`Unsupported platform: ${fe.platform}`}}import Ki from"node:fs";var tr=48e3;function nr(e,t){try{if(!Ki.existsSync(e))return"(no log file yet \u2014 start with `omnish run -d` or the systemd/LaunchAgent service.)";let n=Ki.readFileSync(e),o=(n.length>tr?n.subarray(n.length-tr):n).toString("utf8");return n.length>tr&&(o=`\u2026(truncated to last ${tr} bytes)
|
|
130
135
|
${o}`),o.split(/\r?\n/).slice(-t).join(`
|
|
131
|
-
`).trimEnd()||"(empty)"}catch(n){return`Could not read log: ${String(n)}`}}var
|
|
132
|
-
Script: ${s.scriptPath}`,c=["*Service status*","",`platform: ${
|
|
133
|
-
`),d=["<b>Service status</b>","",`<code>${
|
|
134
|
-
`);return
|
|
136
|
+
`).trimEnd()||"(empty)"}catch(n){return`Could not read log: ${String(n)}`}}var Pc=120;function Ke(e){return e.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">")}function Zi(e){let t=e.trim();return t==="/service"||t.startsWith("/service ")?t.slice(8).trim():null}async function ea(e,t){let n=t.trim().split(/\s+/),r=(n[0]??"").toLowerCase();if(!t.trim()||r==="help")return B(As(e));if(r==="status"){let s=ht(),a=(()=>{try{return Xi.existsSync(ee)?`gateway.pid: ${Xi.readFileSync(ee,"utf8").trim()}`:"gateway.pid: (missing)"}catch(p){return`gateway.pid: (read error: ${String(p)})`}})(),i=Ot.env.OMNISH_BACKGROUND_GATEWAY==="1"?"This process: background gateway (OMNISH_BACKGROUND_GATEWAY=1).":"This process: foreground gateway session.",l=typeof Ot.env.OMNISH_HOME=="string"&&Ot.env.OMNISH_HOME.trim()?`OMNISH_HOME env: ${Ot.env.OMNISH_HOME.trim()}`:"OMNISH_HOME env: (not set \u2014 using default data dir)",u=s.error?s.error:`Node: ${s.nodePath}
|
|
137
|
+
Script: ${s.scriptPath}`,c=["*Service status*","",`platform: ${Ot.platform}`,i,l,`data dir: ${L}`,a,`default log: ${pe}`,"",u,"",e.serviceInstallFromChat?"Install from chat: enabled (/service install).":"Install from chat: off \u2014 `/config set serviceInstallFromChat true` to allow /service install."].join(`
|
|
138
|
+
`),d=["<b>Service status</b>","",`<code>${Ke(Ot.platform)}</code>`,`<br/><code>${Ke(i)}</code>`,`<br/><code>${Ke(l)}</code>`,`<br/>data dir: <code>${Ke(L)}</code>`,`<br/><code>${Ke(a)}</code>`,`<br/>default log: <code>${Ke(pe)}</code>`,"",`<pre>${Ke(u)}</pre>`,"",e.serviceInstallFromChat?"Install from chat: enabled.":"Install from chat: off \u2014 <code>/config set serviceInstallFromChat true</code>."].join(`
|
|
139
|
+
`);return Q(c,d)}if(r==="instructions"){let s=ht();if(s.error)return m(s.error);let a=Xn(s);return m(`*Install hints*
|
|
135
140
|
|
|
136
|
-
${a}`)}if(r==="logs"){let s=n.length>=2?Number.parseInt(n[1],10):80,a=Number.isFinite(s)&&s>0?Math.min(s,
|
|
137
|
-
${
|
|
141
|
+
${a}`)}if(r==="logs"){let s=n.length>=2?Number.parseInt(n[1],10):80,a=Number.isFinite(s)&&s>0?Math.min(s,Pc):80,i=nr(pe,a),l=[`*Gateway log* (last ${a} lines)
|
|
142
|
+
${pe}
|
|
138
143
|
`,"```",i,"```"].join(`
|
|
139
|
-
`),u=`<b>Gateway log</b> (last ${a} lines)<br/><code>${
|
|
144
|
+
`),u=`<b>Gateway log</b> (last ${a} lines)<br/><code>${Ke(pe)}</code><pre>${Ke(i)}</pre>`;return Q(l,u)}if(r==="install"){if(!e.serviceInstallFromChat)return m("Install from chat is disabled. Same trust as shell \u2014 enable with:\n`/config set serviceInstallFromChat true`\nThen `/service install` again.");let s=Zn();return m(s.ok?`*Installed*
|
|
140
145
|
${s.detail}`:`*Install failed*
|
|
141
|
-
${s.detail}`)}if(r==="uninstall"){if(!e.serviceInstallFromChat)return
|
|
142
|
-
${s.detail}`)}return
|
|
146
|
+
${s.detail}`)}if(r==="uninstall"){if(!e.serviceInstallFromChat)return m("Uninstall from chat is disabled. Enable with `/config set serviceInstallFromChat true` or remove files on the host manually.");let s=er();return m(`*Uninstall*
|
|
147
|
+
${s.detail}`)}return m("Unknown /service command. Try /service help")}function ta(e){let t=e.trim().match(/^(\d+)\.(\d+)\.(\d+)/);return t?[Number(t[1]),Number(t[2]),Number(t[3])]:null}function na(e,t){let n=ta(e),r=ta(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 Fc="https://registry.npmjs.org",ra=null,bo=0;function rn(){return ra}function Nc(e){let t=e.trim();return t.length<1||t.length>214?!1:!/\s/.test(t)}function oa(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 _c(e){let t=e.trim(),n=`${Fc}/${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 Bc(e){try{let t=await fetch(e,{headers:{Accept:"application/json"},signal:AbortSignal.timeout(15e3)});if(!t.ok)return{error:`updateInfoUrl: HTTP ${t.status}`};let n=await t.arrayBuffer(),r=n.byteLength>65536?n.slice(0,65536):n,o=new TextDecoder("utf-8",{fatal:!1}).decode(r),s;try{s=JSON.parse(o)}catch{return{error:"updateInfoUrl: body is not JSON"}}if(!s||typeof s!="object"||Array.isArray(s))return{error:"updateInfoUrl: JSON must be an object"};let a=s,i=null;typeof a.message=="string"&&a.message.trim()&&(i=a.message.trim().slice(0,1500));let l=null,u=a.link??a.url;return typeof u=="string"&&u.trim()&&(l=oa(u)),{message:i,link:l}}catch(t){return{error:`updateInfoUrl: ${String(t)}`}}}function Hc(e){return!Number.isFinite(e)||e<36e5?36e5:e>6048e5?6048e5:Math.floor(e)}async function on(e,t){let n=Nc(t.updateCheckPackageName)?t.updateCheckPackageName.trim():"omnish",r=await _c(n),o="version"in r?r.version:null,s="error"in r?r.error:null,a=!1;o&&!s&&(a=na(e,o)<0);let i=null,l=null,u=null,c=oa(t.updateInfoUrl);if(c){let p=await Bc(c);"error"in p?u=p.error:(i=p.message,l=p.link)}let d={runningVersion:e,checkedAtIso:new Date().toISOString(),registryPackage:n,registryLatest:o,registryError:s,updateAvailable:a,infoMessage:i,infoLink:l,infoError:u};return ra=d,d}function rr(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 sa(e){let t=!1,n=async()=>{if(t)return;let s=e.getConfig();if(!s.updateCheckEnabled)return;let a=Hc(s.updateCheckIntervalMs),i=Date.now();if(!(bo!==0&&i-bo<a)){bo=i;try{let l=await on(e.getRunningVersion(),s);l.updateAvailable?e.log.info({updateAvailable:!0,running:l.runningVersion,npmLatest:l.registryLatest,pkg:l.registryPackage},"omnish update check: newer npm version available"):e.log.info({running:l.runningVersion,npmLatest:l.registryLatest,pkg:l.registryPackage},"omnish update check ok"),l.infoMessage&&e.log.info({len:l.infoMessage.length},"omnish update info message present")}catch(l){e.log.warn({err:String(l)},"omnish update check failed")}}},r=setInterval(()=>void n(),6e4),o=setTimeout(()=>void n(),3e4);return()=>{t=!0,clearInterval(r),clearTimeout(o)}}import Dc from"node:fs";import ia from"node:path";import{fileURLToPath as jc}from"node:url";var or=null;function Ue(){if(or!==null)return or;let e=ia.dirname(jc(import.meta.url)),t=ia.join(e,"..","package.json"),n=Dc.readFileSync(t,"utf8"),r=JSON.parse(n);return or=typeof r.version=="string"&&r.version.trim()?r.version.trim():"0.0.0",or}function sn(e,t,n){let r=new Date(e),o=r.getFullYear(),s=r.getMonth(),a=r.getDate(),i=new Date(o,s,a,t,n,0,0).getTime();return i<=e&&(i=new Date(o,s,a+1,t,n,0,0).getTime()),i}function Uc(e,t,n){let r=e;for(let o=0;o<14;o++){let s=sn(r,t,n),a=new Date(s).getDay();if(a>=1&&a<=5)return s;r=s}return sn(r,t,n)}function Wc(e,t,n,r){let o=e;for(let s=0;s<370;s++){let a=sn(o,n,r);if(new Date(a).getDay()===t)return a;o=a}return sn(o,n,r)}function Gc(e,t){let n=new Date(e),r=n.getFullYear(),o=n.getMonth(),s=n.getDate(),a=n.getHours(),i=new Date(r,o,s,a,t,0,0).getTime();return i<=e&&(i=new Date(r,o,s,a+1,t,0,0).getTime()),i}function Jc(e,t){return e.kind==="ondemand"?Number.POSITIVE_INFINITY:e.kind==="daily"?sn(t,e.hour,e.minute):e.kind==="weekdays"?Uc(t,e.hour,e.minute):e.kind==="hourly"?Gc(t,e.minute):Wc(t,e.weekday,e.hour,e.minute)}function aa(e,t,n,r,o=32){if(e.kind==="ondemand")return[];let s=t??n-1,a=[],i=s;for(;a.length<o;){let l=Jc(e,i);if(l>r)break;a.push(l),i=l}return a}var zc={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 ko(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 qc(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 So(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=ko(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=ko(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=qc(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=zc[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=ko(e[2]);return o?{ok:!0,schedule:{kind:"weekly",weekday:r,hour:o.hour,minute:o.minute}}:{ok:!1,error:"weekly needs HH:MM (24h) after weekday."}}return{ok:!1,error:`Unknown schedule kind "${t}".`}}function an(e){let t=r=>r<10?`0${r}`:String(r),n=(r,o)=>`${t(r)}:${t(o)}`;switch(e.kind){case"ondemand":return"ondemand";case"daily":return`daily ${n(e.hour,e.minute)}`;case"weekdays":return`weekdays ${n(e.hour,e.minute)}`;case"hourly":return e.minute===0?"hourly":`hourly :${t(e.minute)}`;case"weekly":return`weekly ${["sun","mon","tue","wed","thu","fri","sat"][e.weekday]??e.weekday} ${n(e.hour,e.minute)}`}}import Vc from"better-sqlite3";import Qc from"pino";function sr(){return process.env.OMNISH_VERBOSE==="1"||process.env.WHATSVERBOSE==="1"}var Yc=sr()?"info":"silent",E=Qc({level:Yc,base:{app:"omnish"}});function la(){return E.child({module:"baileys"})}var ca=1,it=null;function ua(){N(_e);let e=new Vc(ns);e.pragma("journal_mode = WAL"),e.exec(`
|
|
143
148
|
CREATE TABLE IF NOT EXISTS cowork_meta (
|
|
144
149
|
key TEXT PRIMARY KEY,
|
|
145
150
|
value TEXT NOT NULL
|
|
@@ -153,112 +158,119 @@ ${s.detail}`)}return p("Unknown /service command. Try /service help")}function C
|
|
|
153
158
|
PRIMARY KEY (task_id, slot_ms)
|
|
154
159
|
);
|
|
155
160
|
CREATE INDEX IF NOT EXISTS idx_cowork_completion_task ON cowork_slot_completion(task_id);
|
|
156
|
-
`);let t=e.prepare("SELECT value FROM cowork_meta WHERE key = 'schema_version'").get();return(t?Number(t.value):0)<
|
|
161
|
+
`);let t=e.prepare("SELECT value FROM cowork_meta WHERE key = 'schema_version'").get();return(t?Number(t.value):0)<ca&&e.prepare("INSERT OR REPLACE INTO cowork_meta (key, value) VALUES ('schema_version', ?)").run(String(ca)),e}function ln(e){it||(it=ua()),Xc(e)}function da(){if(it){try{it.close()}catch{}it=null}}function xo(){return it||(it=ua()),it}function Kc(e){let n=xo().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 ir(e){let t=Kc(e.id);return t??e.lastCompletedSlotMs}function ar(e,t){if(t.length===0)return;let n=xo(),r=n.prepare(`
|
|
157
162
|
INSERT INTO cowork_slot_completion (task_id, slot_ms, kind, completed_at_ms, log_path)
|
|
158
163
|
VALUES (?, ?, ?, ?, ?)
|
|
159
|
-
`);n.transaction(()=>{for(let s of t)r.run(e,s.slotMs,s.kind,s.completedAtMs,s.logPath)})()}function
|
|
160
|
-
`,{mode:384}),
|
|
161
|
-
`,{mode:384})}function
|
|
162
|
-
`))}function
|
|
163
|
-
`))}let o=n.match(/^show\s+(\S+)\s*$/i);if(o){let u=o[1],c=be();
|
|
164
|
-
`))}if(/^add$/i.test(r)){let u=n.slice(3).trim(),c=
|
|
165
|
-
`))}let s=n.match(/^run\s+(\S+)\s*$/i);if(s){let u=s[1].toLowerCase(),c=
|
|
166
|
-
`))}async function
|
|
167
|
-
${
|
|
164
|
+
`);n.transaction(()=>{for(let s of t)r.run(e,s.slotMs,s.kind,s.completedAtMs,s.logPath)})()}function Xc(e){let n=xo().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{ar(o.id,[{slotMs:o.lastCompletedSlotMs,kind:"migrated",logPath:null,completedAtMs:r}]),E.info({taskId:o.id,slotMs:o.lastCompletedSlotMs},"cowork seeded completion from tasks.json")}catch(a){E.warn({err:String(a),taskId:o.id},"cowork seed from tasks.json failed")}}import pa from"node:crypto";import cn from"node:fs";import vo from"node:os";import Lt from"node:path";var Zc=/^[a-zA-Z0-9][a-zA-Z0-9_-]{0,31}$/;function ma(e){let t=e.trim().toLowerCase();return Zc.test(t)?{ok:!0,name:t}:{ok:!1,error:"Name must be alphanumeric with _ or -, max 32 chars."}}function Pt(e,t){let n=e.trim();return n===""||n==="."?Lt.resolve(t):n==="~"?vo.homedir():n.startsWith("~/")||n.startsWith("~\\")?Lt.join(vo.homedir(),n.slice(2)):Lt.isAbsolute(n)?n:Lt.resolve(t,n)}function Co(e){return Lt.join(vo.homedir(),"Cowork",e)}function eu(e){if(!e||typeof e!="object")return null;let t=e,n=typeof t.id=="string"&&t.id.length>=4?t.id.slice(0,32):"",r=typeof t.name=="string"?t.name.trim().toLowerCase():"",o=typeof t.ownerPeerKey=="string"?t.ownerPeerKey:"",s=typeof t.command=="string"?t.command:"";if(!n||!r||!o||!s)return null;let a=typeof t.cwd=="string"?t.cwd:"",i=typeof t.outputDir=="string"&&t.outputDir.trim()?t.outputDir:Co(r),l=typeof t.enabled=="boolean"?t.enabled:!0,u=typeof t.notify=="string"?t.notify.toLowerCase():"self",c=u==="wa"||u==="whatsapp"?"wa":u==="tg"||u==="telegram"?"tg":u==="all"?"all":u==="none"?"none":"self",d={kind:"ondemand"};if(t.schedule&&typeof t.schedule=="object"){let y=t.schedule,v=typeof y.kind=="string"?y.kind:"";if(v==="ondemand")d={kind:"ondemand"};else if(v==="daily"){let k=Number(y.hour),R=Number(y.minute);Number.isFinite(k)&&Number.isFinite(R)&&(d={kind:"daily",hour:k,minute:R})}else if(v==="weekdays"){let k=Number(y.hour),R=Number(y.minute);Number.isFinite(k)&&Number.isFinite(R)&&(d={kind:"weekdays",hour:k,minute:R})}else if(v==="hourly"){let k=Number(y.minute);Number.isFinite(k)&&Number.isInteger(k)&&k>=0&&k<=59&&(d={kind:"hourly",minute:k})}else if(v==="weekly"){let k=Number(y.hour),R=Number(y.minute),_=Number(y.weekday);Number.isFinite(k)&&Number.isFinite(R)&&Number.isInteger(_)&&(d={kind:"weekly",weekday:_,hour:k,minute:R})}}let p=null;typeof t.lastCompletedSlotMs=="number"&&Number.isFinite(t.lastCompletedSlotMs)&&(p=t.lastCompletedSlotMs);let f=typeof t.createdAtMs=="number"&&Number.isFinite(t.createdAtMs)?t.createdAtMs:Date.now(),h=typeof t.attachLog=="boolean"?t.attachLog:!1,g=Array.isArray(t.attachFiles)?t.attachFiles.filter(y=>typeof y=="string"&&y.trim().length>0):[];return{id:n,name:r,ownerPeerKey:o,command:s,cwd:a,outputDir:i,schedule:d,enabled:l,notify:c,attachLog:h,attachFiles:g,lastCompletedSlotMs:p,createdAtMs:f}}function be(){try{let e=cn.readFileSync($r,"utf8"),t=JSON.parse(e);if(!t||!Array.isArray(t.tasks))return[];let n=[];for(let r of t.tasks){let o=eu(r);o&&n.push(o)}return n}catch{return[]}}function $e(e){N(_e);let t={tasks:e},n=Lt.join(_e,`.tasks.${process.pid}.${pa.randomBytes(4).toString("hex")}.tmp`);cn.writeFileSync(n,JSON.stringify(t,null,2)+`
|
|
165
|
+
`,{mode:384}),cn.renameSync(n,$r)}function Ft(e,t,n){let r=t.trim().toLowerCase();return e.find(o=>o.name===r&&o.ownerPeerKey===n)}function fa(){return pa.randomBytes(4).toString("hex")}function ga(e){N(_e),cn.writeFileSync(Cr,JSON.stringify(e,null,2)+`
|
|
166
|
+
`,{mode:384})}function ha(e){let t=$o();t.push(e),ga(t)}function $o(){try{let e=cn.readFileSync(Cr,"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 ya(e){if(e<=0)return{batch:[],remainingAfter:$o().length};let t=$o();if(t.length===0)return{batch:[],remainingAfter:0};let n=t.slice(0,e),r=t.slice(e);return ga(r),{batch:n,remainingAfter:r.length}}function tu(){return m(["Cowork \u2014 scheduled shell tasks (gateway must be running).","","add <name> <schedule> -- <command\u2026>"," schedule: ondemand | hourly [:MM] | daily HH:MM | weekdays HH:MM | weekly <mon\u2026sun> HH:MM","set <name> cmd -- <command\u2026> | schedule <\u2026> | out <path> | cwd <path>"," notify self|wa|tg|all|none"," attach on|off \u2014 send run log as a file with notify messages"," files clear | <glob/path \u2026> \u2014 optional artifacts (basename * ?); quote paths with spaces","list | show <name> | run <name> | enable <name> | disable <name> | remove <name>","","Output logs: task outputDir (default ~/Cowork/<name>). Missed times catch up when the gateway is back."].join(`
|
|
167
|
+
`))}function wa(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 nu(e){let t=" -- ",n=e.indexOf(t);if(n===-1)return{error:'Missing " -- " before the command. Example: /cowork add tick weekdays 09:00 -- date'};let r=e.slice(0,n).trim(),o=e.slice(n+t.length).trim();if(!o)return{error:"Command after -- is empty."};let s=r.split(/\s+/).filter(Boolean);if(s.length<2)return{error:"Usage: /cowork add <name> <schedule\u2026> -- <command\u2026>"};let a=s[0],i=s.slice(1);return{name:a,scheduleWords:i,command:o}}function ka(e,t){let n=e.trim();if(!n||/^help$/i.test(n))return tu();let r=n.split(/\s+/)[0].toLowerCase();if(/^list$/i.test(r)){let u=be().filter(d=>d.ownerPeerKey===t);if(u.length===0)return m("(no cowork tasks for this chat)");let c=u.map(d=>{let p=d.enabled?"":" (disabled)";return`${me}${d.name} ${an(d.schedule)} notify=${d.notify}${p}`});return m(c.join(`
|
|
168
|
+
`))}let o=n.match(/^show\s+(\S+)\s*$/i);if(o){let u=o[1],c=be();ln(c);let d=Ft(c,u,t);if(!d)return m(`Unknown task "${u}". /cowork list`);let p=ir(d),f=[`name: ${d.name}`,`id: ${d.id}`,`schedule: ${an(d.schedule)}`,`enabled: ${d.enabled}`,`notify: ${d.notify}`,`attachLog: ${d.attachLog}`,`files: ${d.attachFiles.length?d.attachFiles.join(", "):"(none)"}`,`cwd: ${d.cwd||"(session cwd)"}`,`out: ${d.outputDir}`,`cmd: ${d.command}`,`last slot: ${p?new Date(p).toLocaleString():"(never)"}`];return m(f.join(`
|
|
169
|
+
`))}if(/^add$/i.test(r)){let u=n.slice(3).trim(),c=nu(u);if("error"in c)return m(c.error);let d=ma(c.name);if(!d.ok)return m(d.error);let p=So(c.scheduleWords);if(!p.ok)return m(p.error);let f=be();if(Ft(f,d.name,t))return m(`Task "${d.name}" already exists. Remove it first or pick another name.`);let h=J(t),g=Co(d.name),y={id:fa(),name:d.name,ownerPeerKey:t,command:c.command,cwd:"",outputDir:g,schedule:p.schedule,enabled:!0,notify:"self",attachLog:!1,attachFiles:[],lastCompletedSlotMs:null,createdAtMs:Date.now()};return f.push(y),$e(f),m([`Saved cowork task "${d.name}" (${an(p.schedule)}).`,`Output: ${g}`,`Notify: self \u2014 change with /cowork set ${d.name} notify wa|tg|all|none`].join(`
|
|
170
|
+
`))}let s=n.match(/^run\s+(\S+)\s*$/i);if(s){let u=s[1].toLowerCase(),c=Ft(be(),u,t);return c?c.enabled?(ha({ownerPeerKey:t,name:c.name,at:Date.now()}),m(`On-demand run queued for "${c.name}" (runs within ~30s while omnish run is active).`)):m(`Cowork "${c.name}" is disabled. /cowork enable ${c.name}`):m(`Unknown task "${s[1]}". /cowork list`)}let a=n.match(/^(?:remove|rm|del)\s+(\S+)\s*$/i);if(a){let u=a[1],c=be(),d=c.findIndex(p=>p.name===u.toLowerCase()&&p.ownerPeerKey===t);return d===-1?m(`Unknown task "${u}".`):(c.splice(d,1),$e(c),m(`Removed cowork task "${u.toLowerCase()}".`))}let i=n.match(/^enable\s+(\S+)\s*$/i);if(i)return ba(i[1],t,!0);let l=n.match(/^disable\s+(\S+)\s*$/i);return l?ba(l[1],t,!1):/^set$/i.test(r)?su(n.slice(3).trim(),t):m("Unknown /cowork command. Try /cowork help")}function ba(e,t,n){let r=be(),o=Ft(r,e,t);return o?(o.enabled=n,$e(r),m(`Cowork "${o.name}" ${n?"enabled":"disabled"}.`)):m(`Unknown task "${e}".`)}function ru(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 ou(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 su(e,t){let n=e.match(/^(\S+)\s+([\s\S]+)$/);if(!n)return m("Usage: /cowork set <name> cmd -- \u2026 | schedule \u2026 | out <path> | cwd <path> | notify \u2026 | attach <on|off> | files \u2026");let r=n[1],o=n[2].trim(),s=be(),a=Ft(s,r,t);if(!a)return m(`Unknown task "${r}".`);if(o.toLowerCase().startsWith("cmd ")){let i=o.slice(4).trim(),l=" -- ",u=i.indexOf(l),c;if(u!==-1)c=i.slice(u+l.length).trim();else if(i.startsWith("--"))c=i.slice(2).trim();else return m("Usage: /cowork set <name> cmd -- <command\u2026>");return c?(a.command=c,$e(s),m(`Updated command for "${a.name}".`)):m("Command is empty.")}if(o.toLowerCase().startsWith("schedule ")){let i=o.slice(9).trim().split(/\s+/).filter(Boolean),l=So(i);return l.ok?(a.schedule=l.schedule,$e(s),m(`Schedule for "${a.name}": ${an(l.schedule)}`)):m(l.error)}if(o.toLowerCase().startsWith("out ")){let i=o.slice(4).trim(),l=J(t);return a.outputDir=Pt(i,l.cwd),$e(s),m(`outputDir: ${a.outputDir}`)}if(o.toLowerCase().startsWith("cwd ")){let i=o.slice(4).trim(),l=J(t);return a.cwd=i?Pt(i,l.cwd):"",$e(s),m(`cwd: ${a.cwd||"(session cwd at run time)"}`)}if(o.toLowerCase().startsWith("notify ")){let i=o.slice(7).trim(),l=ru(i);return l?(a.notify=l,$e(s),m(`notify: ${l}`)):m("notify must be self, wa, tg, all, or none.")}if(o.toLowerCase().startsWith("attach ")){let i=o.slice(7).trim(),l=wa(i);if(l.length!==1)return m("Usage: /cowork set <name> attach on|off");let u=ou(l[0]);return u===null?m("attach must be on or off"):(a.attachLog=u,$e(s),m(`attachLog: ${u}`))}if(o.toLowerCase().startsWith("files ")){let i=o.slice(6).trim(),l=wa(i);return l.length===1&&l[0].toLowerCase()==="clear"?(a.attachFiles=[],$e(s),m("files: (cleared)")):l.length===0?m("Usage: /cowork set <name> files clear | <pattern \u2026> \u2014 quote paths with spaces"):(a.attachFiles=l,$e(s),m(`files: ${a.attachFiles.join(", ")}`))}return m("Unknown set field. Try: cmd, schedule, out, cwd, notify, attach, files")}function Ro(e){let t=e.trim();if(t==="/computers"||t.startsWith("/computers "))return t.slice(10).trim();if(t==="/pcs"||t.startsWith("/pcs "))return t.slice(4).trim();let n=t.match(/^\/c(?:$|\s+(.*))/);return n?(n[1]??"").trim():null}function $(e){return{kind:"text",body:e}}function iu(e,t){return`${e}:${t}`}function au(e,t){return`${e}:apps:${t}`}async function lu(e,t){let n=e.trim();if(n===""||/^status$/i.test(n)||/^show$/i.test(n)||/^get$/i.test(n)){let i=C();return Os({gatewayMode:i.gatewayMode,authPresent:Ie(),tokenSet:!!ne(i),allowN:i.allowFrom.length,tgAllowN:i.telegramAllowFrom.length,updateBrief:rr(rn())})}if(/^help$/i.test(n))return B(Fr());let r=Tn(n);if(!r)return Qs();In(r);let o=C(),s,a=!1;if(t?.reload){let i=await t.reload();s=i.ok?i.summary:`Reload failed: ${i.error}`}else a=!0;return Bs(o.gatewayMode,s,a)}function cu(e){let t=e.trim();if(!Ze(t))return _s();let n=vt(t),r=[];return typeof process.env.TELEGRAM_BOT_TOKEN=="string"&&process.env.TELEGRAM_BOT_TOKEN.trim()&&r.push("TELEGRAM_BOT_TOKEN is set in the environment and overrides config until unset."),n.gatewayMode==="whatsapp"&&r.push('Set gatewayMode to "telegram" or "both" for Telegram to receive messages.'),Ns(r)}async function Sa(e,t,n){let r=J(t),o=bs(n);if(o!==null){let i=ks(r.cwd,o),l=Ss(i);return l.ok?(Fn(t,i),m(`cwd: ${i}`)):m(`cd: ${l.error}`)}let s=await Yn(e.shell,n,{timeoutMs:e.syncTimeoutMs,maxBytes:e.syncMaxBytes,cwd:r.cwd}),a=[];return a.push(`$ ${n}`),s.stdout.trim()&&a.push(s.stdout.trimEnd()),s.stderr.trim()&&(a.push("\u2014 stderr \u2014"),a.push(s.stderr.trimEnd())),s.timedOut?a.push(`Timed out (${Math.round(e.syncTimeoutMs/1e3)}s limit).`):s.code!==0&&s.code!==null&&a.push(`Exit ${s.code}`),m(a.join(`
|
|
171
|
+
`))}async function yt(e,t,n,r,o,s,a,i,l=null,u=!1){let c=s.text.trim(),d=s.peerKey,p=c.match(/^!!\s*(start|stop)\s*$/i);if(p)return p[1].toLowerCase()==="start"?(o.set(d,!0),$(Js())):(o.set(d,!1),$(m("Free shell mode off.")));if(c.startsWith(e.commandPrefix)){let h=c.slice(e.commandPrefix.length).trim();if(!h)return $(m(`Send ${e.commandPrefix}<command> or /help`));if(!u&&yi(h)){let g=Qr(d,h);if(g!==void 0)return await yt(e,t,n,r,o,{...s,text:g},a,i,l,!0)}return $(await Sa(e,d,h))}if(/^\/help\s+files$/i.test(c.trim()))return $(Br());if(c==="/help"||c==="help")return $(B(Rt(e)));if(c.startsWith("/")){if(/^\/files(?:\s+help)?$/i.test(c.trim()))return $(Br());let h=c.match(/^\/receive\b(?:\s+(\S+))?$/i);if(h){let w=(h[1]??"status").toLowerCase(),x=new Set(["here","cwd","session","dir"]),M=new Set(["default","global","reset"]);if(x.has(w)){Ir(d,"sessionCwd");let D=J(d).cwd;return $(m(`Inbound files will save under this chat\u2019s session folder:
|
|
172
|
+
${D}
|
|
168
173
|
(layout: \u2026/<peer>/<date>/<file> under that root).
|
|
169
174
|
|
|
170
|
-
Change folder with ${e.commandPrefix}cd \u2026 Send /receive default to use the server config again.`))}return
|
|
171
|
-
[cwd: ${
|
|
172
|
-
/log ${
|
|
173
|
-
/tail ${
|
|
174
|
-
${
|
|
175
|
+
Change folder with ${e.commandPrefix}cd \u2026 Send /receive default to use the server config again.`))}return M.has(w)?(Ir(d,"default"),$(m("Per-chat inbound folder cleared. Uploads now follow fileReceiveRootMode in config.json (/files)."))):$(w==="help"?Hr():w==="status"?Us(e,d):Hr())}if(c==="/reload"||c==="/restart"){if(!i?.reload)return $(m("Reload is only available while the omnish gateway (omnish run) is running."));let w=await i.reload();return $(m(w.ok?w.summary:`Reload failed: ${w.error}`))}if(c==="/updates"||c.startsWith("/updates ")){let w=c.slice(8).trim().toLowerCase();if(w==="cached"||w==="last"){let M=rn();return $(M?qt(M):m("No update snapshot yet. Send /updates (live check) once, or enable updateCheckEnabled and wait for the scheduled check."))}let x=await on(Ue(),C());return $(qt(x))}let g=c.match(/^\/security(?:\s+(\S+))?\s*$/i);if(g){let w=(g[1]??"").toLowerCase(),x=C(),M=ot(x);return $(w==="help"||w==="?"?B(ii()):w==="summary"||w==="brief"?m(Ts(M,"Send /security for the full report.")):w==="tips"?B(si()):w===""||w==="full"||w==="report"?oi(M):m("Unknown /security subcommand. Try /security, /security summary, /security tips, or /security help"))}let y=c.match(/^\/(gateway|gw|mode)\b(?:\s+(.*))?$/i);if(y){let w=(y[2]??"").trim();return $(await lu(w,i))}if(c==="/config"||c.startsWith("/config ")){let w=c.slice(7).trim();return $(await Yi(w,i))}let v=Zi(c);if(v!==null)return $(await ea(e,v));let k=Ro(c);if(k!==null){let w=Wi(e,k,l);return w===null?null:$(w)}let R=c.match(/^\/(send|file)\b(?:\s+([\s\S]+))?$/i);if(R){let w=(R[2]??"").trim();if(!w)return $(_r());let x=On(w);if(!x)return $(_r());let M=J(d).cwd,D=await Ln(M,x.selectorPart);if(D.length===0)return $(m(`No files matched: ${x.selectorPart}`));let H=await Pn(D);if(!H.ok)return $(m(H.error));let te=[];for(let xe of D){let Me=et(xe,e.fileSendMaxBytes);if("error"in Me)return $(m(Me.error));te.push({absPath:Me.absPath,category:Me.category,mimetype:Me.mimetype,displayName:Me.displayName,caption:x.caption})}return te.length===1?{kind:"file",spec:te[0]}:{kind:"files",specs:te}}if(c==="/allowlist"){let w=C();return $(Fs([{label:"allowFrom (WhatsApp)",items:w.allowFrom},{label:"telegramAllowFrom",items:w.telegramAllowFrom}]))}let _=c.match(/^\/allow\b\s+(.+)$/i);if(_)try{let w=Rn(_[1].trim());return $(Nr(w))}catch(w){return $(m(String(w)))}if(/^\/allow\b\s*$/i.test(c))return $(Hs());let z=c.match(/^\/deny\b\s+(.+)$/i);if(z)try{let w=Mn(z[1].trim());return $(Nr(w))}catch(w){return $(m(String(w)))}if(/^\/deny\b\s*$/i.test(c))return $(Ds());let O=c.match(/^\/(whatsapp|wa|telegram|tg)\b(?:\s+(.*))?$/i);if(O){let w=O[1].toLowerCase(),x=(O[2]??"").trim(),M=w==="whatsapp"||w==="wa";if(w==="telegram"||w==="tg"){let H=x.match(/^token\s+(\S+)\s*$/i);return H?$(cu(H[1]??"")):x===""||/^help$/i.test(x)?$(B(Ps(C()))):$(Gs())}if(M)return x===""||/^help$/i.test(x)?$(B(Ls(e))):$(Ws())}let le=c.match(/^\/(cowork|cw)\b(?:\s+([\s\S]*))?$/i);if(le){let w=(le[2]??"").trim();return $(ka(w,d))}if(c==="/apps"||c.startsWith("/apps "))return $(await fu(c,d,e,a,r));let Re=uu(c);if(Re!==null)return $(pu(Re,d,e,a));if(c.startsWith("/bg")){let w=c.slice(3).trim();if(!w)return $(js());let x=J(d).cwd,{id:M}=t.spawnJob(e.shell,w,{cwd:x});return $(m(`Job ${M} started.
|
|
176
|
+
[cwd: ${x}]
|
|
177
|
+
/log ${M}
|
|
178
|
+
/tail ${M}`))}if(c==="/jobs"){let w=t.list().slice(0,20);return w.length===0?$(m("(no jobs yet)")):$(m(w.map(x=>{let M=x.finishedAt&&x.startedAt?`${((Date.parse(x.finishedAt)-Date.parse(x.startedAt))/1e3).toFixed(1)}s`:"\u2026";return`${x.id} ${x.status} exit=${x.exitCode??"?"} ${M}
|
|
179
|
+
${x.cmd.slice(0,120)}${x.cmd.length>120?"\u2026":""}`}).join(`
|
|
175
180
|
|
|
176
|
-
`)))}let Se=c.match(/^\/log\s+([a-f0-9]{8})(?:\s+(\d+))?\s*$/i);if(Se){let
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
181
|
+
`)))}let Se=c.match(/^\/log\s+([a-f0-9]{8})(?:\s+(\d+))?\s*$/i);if(Se){let w=Se[1].toLowerCase(),x=Se[2]?Number.parseInt(Se[2],10):e.jobLogTailLines,M=Number.isFinite(x)&&x>0?Math.min(x,500):e.jobLogTailLines;return $(m(t.tailLog(w,M)))}let Le=c.match(/^\/tail\s+([a-f0-9]{8})\s*$/i);if(Le){let w=Le[1].toLowerCase(),x=iu(d,w),M=n.get(x)??0,{text:D,nextOffset:H}=t.readSince(w,M);return n.set(x,H),$(D?m(D.trimEnd()||"(no new output)"):m(`(no new output; offset ${H})`))}let We=c.match(/^\/kill\s+([a-f0-9]{8})\s*$/i);if(We){let w=We[1].toLowerCase();return $(m(t.kill(w)))}let T=c.match(/^\/(shortcut|shortcuts|alias|aliases)\b(?:\s+([\s\S]*))?$/i);if(T){let w=T[1].toLowerCase(),x=(T[2]??"").trim();return w==="shortcuts"&&!x?x="list":((w==="alias"||w==="aliases")&&!x||w==="shortcut"&&!x)&&(x="help"),$(mu(x,d))}if(!u){let w=c.trim().match(/^\/([a-zA-Z0-9][a-zA-Z0-9_-]{0,31})\s*$/);if(w){let x=w[1],M=Qr(d,x);if(M!==void 0)return await yt(e,t,n,r,o,{...s,text:M},a,i,l,!0)}}return $(zs(e))}let f=c.match(/^>(\S+)\s*(.*)$/s);if(f){let h=f[1],g=f[2]??"",y=await a.writeNamedLine(d,h,g);return y?$(m(y)):null}return await a.writeFocusedLine(d,c)?null:o.get(d)&&c?$(await Sa(e,d,c)):$(qs(e))}function uu(e){return e==="/run"||e.startsWith("/run ")?e.slice(4).trim():e==="/r"||e.startsWith("/r ")?e.slice(2).trim():null}function du(e){let t=e.trim(),n=t.match(/^(\S+)\s+(?:--queue|-q)\s+([\s\S]+)$/i);if(n)return{recipe:n[1],task:n[2],queued:!0};let r=t.match(/^(\S+)\s+([\s\S]+)$/);return r?{recipe:r[1],task:r[2],queued:!1}:null}function pu(e,t,n,r){let o=e.trim();if(!o||/^help$/i.test(o))return B(Zs());let s=/^list\b([\s\S]*)$/i.exec(o);if(s){let p=(s[1]??"").trim(),{filter:f,bad:h}=xi(p);if(h)return m(`Unknown /run list suffix: "${h}". Use: list | list --chat | list -p | list --global | list -g`);if(f==="merged")return ei(Ti(t,n));let g=$i(t,n,f);return ri(g,f)}let a=/^show\b([\s\S]*)$/i.exec(o);if(a){let p=(a[1]??"").trim(),{mode:f,remainder:h}=Si(p),g=/^(\S+)\s*$/i.exec(h);if(!g?.[1])return m("Usage: /run show <name> \u2014 or show --global|-g|--chat|-p <name> (-g shared, -p private)");let y=g[1];if(f==="resolved"){let O=je(t,n,y);return O?Ur(O):Wr(y)}let v=f==="global"?"global":"chat",k=Ye(v,t,n,y);if(k)return Ur(k,v==="global"?"From gateway-shared recipes (--global).":"From this chat only (--chat).");let R=v==="chat"&&Ye("global",t,n,y)?`
|
|
182
|
+
(Gateway-shared recipe exists: /run show --global ${y})`:"",_=v==="global"&&Ye("chat",t,n,y)?`
|
|
183
|
+
(This chat stores an override: /run show --chat ${y})`:"",z=v==="global"?`Unknown recipe "${y}" in gateway-shared storage.`:`Unknown recipe "${y}" in this chat storage.`;return m(`${z}${R}${_}`)}let i=/^add\b([\s\S]*)$/i.exec(o);if(i){let{scope:p,remainder:f}=oo((i[1]??"").trim()),h=f.match(/^(\S+)\s+([\s\S]+)$/);if(!h)return m(p==="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 g=so(h[2],n.recipesMacroDefaultCommand);io(t,h[1],g,p);let y=gt(h[1]),v=y.ok?y.normalized:h[1].toLowerCase(),k=Ye(p,t,n,v)??je(t,n,v);return k?Yt(v,k,p):m("Recipe save failed.")}catch(g){return m(String(g))}}let l=/^set\b([\s\S]*)$/i.exec(o);if(l){let{scope:p,remainder:f,explicit:h}=ro((l[1]??"").trim()),g=vi(f);if(g){if(h)return m("Cannot combine a leading scope flag (-g, --global, --chat, -p) with a trailing scope flag on the same line.");let k=gt(g.name);if(!k.ok)return m(k.error);let R=ao(t,k.normalized,g.target,n);if(!R.ok)return m(R.error);if(R.kind==="noop")return m(R.message);let _=Ye(R.target,t,n,k.normalized)??je(t,n,k.normalized);return _?Yt(k.normalized,_,R.target):m("Recipe scope update failed.")}let y=f.match(/^(\S+)\s*$/);if(y?.[1]&&h){let k=gt(y[1]);if(!k.ok)return m(k.error);let R=ao(t,k.normalized,p,n);if(!R.ok)return m(R.error);if(R.kind==="noop")return m(R.message);let _=Ye(R.target,t,n,k.normalized)??je(t,n,k.normalized);return _?Yt(k.normalized,_,R.target):m("Recipe scope update failed.")}let v=f.match(/^(\S+)\s+([\s\S]+)$/);if(!v)return m(p==="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 k=so(v[2],n.recipesMacroDefaultCommand);io(t,v[1],k,p);let R=gt(v[1]),_=R.ok?R.normalized:v[1].toLowerCase(),z=Ye(p,t,n,_)??je(t,n,_);return z?Yt(_,z,p):m("Recipe save failed.")}catch(k){return m(String(k))}}let u=/^(?:remove|rm|del)\b([\s\S]*)$/i.exec(o);if(u){let{scope:p,remainder:f}=oo((u[1]??"").trim()),h=f.match(/^(\S+)\s*$/);if(!h?.[1])return m(p==="global"?"Usage: /run remove --global <name> (aliases: rm, del)":"Usage: /run remove [--global|-g|--chat|-p] <name>");let g=h[1];return Mi(t,g,p)?ti(g,p):p==="chat"&&Ye("global",t,n,g)?m(`No recipe "${g}" in this chat. There is a gateway-shared recipe with that name \u2014 remove it with:
|
|
184
|
+
/run remove --global ${g}`):ni(g,p)}if(/^queue$/i.test(o))return m(r.runQueueStatus(t));if(/^queue\s+resume\s*$/i.test(o))return m(r.resumeRunQueue(t,n));let c=du(o);if(c){let{recipe:p,task:f,queued:h}=c,g=je(t,n,p);if(!g)return Wr(p);let y=g.taskEnv??"OMNISH_TASK";if(!qn(g.command,y))return m(`Recipe "${p}" command must reference "$${y}".`);let v=Ci(f,n.recipesMaxTaskChars);if(!v.ok)return m(v.error);let k=g.promptTemplate?Ri(g.promptTemplate,y,v.task):v.task,R={[y]:k};if(h)return m(r.enqueueQueuedRun(t,{command:g.command,extraEnv:R,recipeLabel:p},n));let _=Qn(p);return m(r.start(t,_,g.command,n,R))}let d=o.match(/^(\S+)$/);if(d){let p=d[1],f=p.toLowerCase();return f==="add"||f==="set"?m('Usage: /run add <name> cmd [--template "\u2026"] \u2014 cmd must include "$OMNISH_TASK"'):f==="show"?m("Usage: /run show <name> \u2014 optional show --global|-g|--chat|-p <name>"):f==="remove"||f==="rm"||f==="del"?m("Usage: /run remove [--global|-g|--chat|-p] <name>"):je(t,n,f)?m(`Usage: /run ${p} <task text\u2026> \u2014 replaces <<<OMNISH_TASK>>> / $OMNISH_TASK in stored templates`):m(`Unknown recipe "${p}". /run list`)}return m("/run: could not parse. /run help")}function mu(e,t){let n=e.trim();if(!n||/^help$/i.test(n))return B(Dr());let r=/^list\b([\s\S]*)$/i.exec(n);if(r){let l=(r[1]??"").trim(),{filter:u,bad:c}=pi(l);return c?m(`Unknown /shortcut list suffix: "${c}". Use: list | list --chat | list -p | list --global | list -g`):Ys(fi(t,u))}let o=/^show\b([\s\S]*)$/i.exec(n);if(o){let l=(o[1]??"").trim(),{mode:u,remainder:c}=di(l),d=/^(\S+)\s*$/i.exec(c);if(!d?.[1])return m("Usage: /shortcut show <name> \u2014 or show --global|-g|--chat|-p <name> (-g shared, -p private)");let p=d[1];if(u==="resolved"){let k=gi(t,p);if(!k)return Ks(p);let R=k.scope==="global"?"Shared shortcut (all chats use it unless this chat overrides the name).":"This chat only.";return jr(p,k.body,R)}let f=u==="global"?"global":"chat",h=qe(f,t,p);if(h!==void 0)return jr(p,h,f==="global"?"From the shared shortcut list (--global / -g).":"From this chat only (--chat / -p).");let g=f==="chat"&&qe("global",t,p)!==void 0?`
|
|
185
|
+
(Shared shortcut exists: /shortcut show --global ${p})`:"",y=f==="global"&&qe("chat",t,p)!==void 0?`
|
|
186
|
+
(This chat overrides the name: /shortcut show --chat ${p})`:"",v=f==="global"?`Unknown shortcut "${p}" in shared shortcuts.`:`Unknown shortcut "${p}" in this chat.`;return m(`${v}${g}${y}`)}let s=/^add\b([\s\S]*)$/i.exec(n);if(s){let{scope:l,remainder:u}=qr((s[1]??"").trim()),c=u.match(/^(\S+)\s+([\s\S]+)$/);if(!c)return m(l==="global"?"Usage: /shortcut add --global <name> <command\u2026> (short: add -g <name> <command\u2026>) \u2014 -g shared, -p private":"Usage: /shortcut add [--global|-g|--chat|-p] <name> <command\u2026> \u2014 -g shared, -p private");try{Yr(t,c[1],c[2],l);let d=mt(c[1]),p=d.ok?d.normalized:c[1].trim().toLowerCase(),f=qe(l,t,p)??"";return Qt(p,f,l)}catch(d){return m(String(d))}}let a=/^set\b([\s\S]*)$/i.exec(n);if(a){let{scope:l,remainder:u,explicit:c}=zr((a[1]??"").trim()),d=mi(u);if(d){if(c)return m("Cannot combine a leading scope flag (-g, --global, --chat, -p) with a trailing flag on the same line.");let h=mt(d.name);if(!h.ok)return m(h.error);let g=Vr(t,h.normalized,d.target);if(!g.ok)return m(g.error);if(g.kind==="noop")return m(g.message);let y=qe(g.target,t,h.normalized)??"";return Qt(h.normalized,y,g.target)}let p=u.match(/^(\S+)\s*$/);if(p?.[1]&&c){let h=mt(p[1]);if(!h.ok)return m(h.error);let g=Vr(t,h.normalized,l);if(!g.ok)return m(g.error);if(g.kind==="noop")return m(g.message);let y=qe(g.target,t,h.normalized)??"";return Qt(h.normalized,y,g.target)}let f=u.match(/^(\S+)\s+([\s\S]+)$/);if(!f)return m(l==="global"?"Usage: /shortcut set --global <name> <command\u2026> \u2014 or scope-only: /shortcut set -g <name> | /shortcut set <name> -g (-g shared)":"Usage: /shortcut set [--global|-g|--chat|-p] <name> <command\u2026> \u2014 or move scope: /shortcut set -g <name> | /shortcut set <name> -p (-g shared, -p private)");try{Yr(t,f[1],f[2],l);let h=mt(f[1]),g=h.ok?h.normalized:f[1].trim().toLowerCase(),y=qe(l,t,g)??"";return Qt(g,y,l)}catch(h){return m(String(h))}}let i=/^(?:remove|rm|del)\b([\s\S]*)$/i.exec(n);if(i){let{scope:l,remainder:u}=qr((i[1]??"").trim()),c=u.match(/^(\S+)\s*$/);if(!c?.[1])return m(l==="global"?"Usage: /shortcut remove --global <name> (aliases: rm, del)":"Usage: /shortcut remove [--global|-g|--chat|-p] <name>");let d=c[1];return hi(t,d,l)?Vs(d,l):l==="chat"&&qe("global",t,d)!==void 0?m(`No shortcut "${d}" in this chat. There is a shared shortcut with that name \u2014 remove it with:
|
|
187
|
+
/shortcut remove --global ${d}`):Xs(d,l)}return B(Dr())}async function fu(e,t,n,r,o){let s=e.slice(5).trim(),a=s.toLowerCase();if(!a||a==="help")return B(Gr());let i=s.match(/^(\S+)\s*(.*)$/s);if(!i)return B(Gr());let l=i[1].toLowerCase(),u=(i[2]??"").trim();switch(l){case"start":{let c=u.match(/^(\S+)\s+([\s\S]+)$/);return c?m(r.start(t,c[1],c[2],n)):m("Usage: /apps start <name> <command\u2026>")}case"attach":{let c=u.split(/\s+/)[0];return c?m(r.attach(t,c)):m("Usage: /apps attach <name>")}case"detach":return m(r.detach(t));case"list":return m(r.list(t));case"info":case"get":{let c=u.split(/\s+/)[0];return m(r.info(t,c||void 0))}case"send":{let c=u.match(/^(\S+)\s+([\s\S]+)$/);return c?m(await r.sendText(t,c[1],c[2])):m("Usage: /apps send <name> <text\u2026>")}case"key":{let c=u.match(/^(\S+)\s+([\s\S]+)$/);return c?m(r.sendKey(t,c[1],c[2].trim())):m("Usage: /apps key <name> <KEY[,KEY\u2026]>")}case"tail":{let c=u.match(/^(\S+)(?:\s+(\d+))?\s*$/);if(!c)return m("Usage: /apps tail <name> [lines]");let d=c[2]?Number.parseInt(c[2],10):n.appsLogTailLines;return m(r.tail(t,c[1],d))}case"since":{let c=u.split(/\s+/)[0];if(!c)return m("Usage: /apps since <name>");let d=au(t,c),p=o.get(d)??0,{text:f,nextOffset:h}=r.readSince(t,c,p);return o.set(d,h),m(f.trimEnd()||"(no new log bytes)")}case"mute":{let c=u.split(/\s+/)[0];return c?m(r.mute(t,c)):m("Usage: /apps mute <name>")}case"unmute":{let c=u.split(/\s+/)[0];return c?m(r.unmute(t,c)):m("Usage: /apps unmute <name>")}case"raw":{let c=u.match(/^(\S+)\s+(on|off)\s*$/i);return c?m(r.setRaw(t,c[1],c[2].toLowerCase()==="on")):m("Usage: /apps raw <name> on|off")}case"resize":{let c=u.trim().split(/\s+/).filter(Boolean);if(c.length<3)return m("Usage: /apps resize <name> <cols> <rows>");let d=c[0],p=Number(c[1]),f=Number(c[2]);return!Number.isFinite(p)||!Number.isFinite(f)?m("cols and rows must be numbers."):m(r.resize(t,d,p,f))}case"stop":{let c=u.split(/\s+/)[0];return c?m(r.stop(t,c)):m("Usage: /apps stop <name>")}case"kill":{let c=u.split(/\s+/)[0];return c?m(r.kill(t,c)):m("Usage: /apps kill <name>")}case"rm":{let c=u.split(/\s+/)[0];return c?m(r.rm(t,c)):m("Usage: /apps rm <name>")}default:return m(`Unknown /apps subcommand "${l}". /apps help`)}}function Mo(e,t,n){return!e.clusterEnabled||Ro(t.trim())!==null?!0:n?Ui(n,e):!1}function gu(e){return`wa:${e}`}function To(e){return`tg:${e}`}function xa(e){return{peerKey:gu(e.fromJid),text:e.text,waMessageId:e.messageId,mediaSavedPath:e.mediaSavedPath,mediaError:e.mediaError}}import at from"node:fs";import vu from"node:path";var va={enter:"\r",cr:"\r",lf:`
|
|
188
|
+
`,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 hu(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+=`
|
|
189
|
+
`,n++;continue}if(o==="t"){t+=" ",n++;continue}if(o==="\\"){t+="\\",n++;continue}}t+=r}return t}function yu(e){let t=e.trim();if(!t)return"";if(t.startsWith("\\"))return hu(t);let n=t.toLowerCase();if(va[n])return va[n];let r=t.match(/^\^([A-Za-z])$/);if(r){let s=r[1].toUpperCase().charCodeAt(0);if(s>=64&&s<=95)return String.fromCharCode(s-64)}let o=n.match(/^ctrl\+(.+)$/);if(o){let s=o[1];if(s.length===1){let a=s.toUpperCase().charCodeAt(0);if(a>=64&&a<=95)return String.fromCharCode(a-64)}}return t}function Io(e){let t=e.split(",").map(n=>n.trim()).filter(Boolean);return t.length===0?"":t.map(n=>yu(n)).join("")}var un="\x1B";function $a(e){let t=e;return t=t.replace(new RegExp(`${un}\\[[\\d;?]*[ -/]*[@-~]`,"g"),""),t=t.replace(new RegExp(`${un}\\][^${un}\\u0007]*(?:\\u0007|${un}\\\\)`,"g"),""),t=t.replace(new RegExp(`${un}[@-Z\\\\-_]`,"g"),""),t=t.replace(/\u0007/g,""),t}function wu(e,t){if(e.length<=t)return e?[e]:[];let n=[];for(let r=0;r<e.length;r+=t)n.push(e.slice(r,r+t));return n}function bu(e,t){return`${e}${t}`}var lr=class{constructor(t){this.opts=t}pending=new Map;timers=new Map;lastFlushEnd=new Map;flushing=new Set;closed=!1;dispose(){this.closed=!0;for(let t of this.timers.values())clearTimeout(t);this.timers.clear(),this.pending.clear(),this.lastFlushEnd.clear(),this.flushing.clear()}push(t,n,r){if(this.closed||this.opts.isMuted(t,n))return;let o=bu(t,n);this.pending.set(o,(this.pending.get(o)??"")+r);let s=this.timers.get(o);s&&clearTimeout(s);let a=this.opts.getCfg().appsFlushMs,i=setTimeout(()=>{this.timers.delete(o),this.flushNow(o,t,n)},a);this.timers.set(o,i)}async flushNow(t,n,r){if(!(this.closed||this.flushing.has(t))){this.flushing.add(t);try{let o=this.opts.getCfg(),s=o.appsMinIntervalMs,a=this.lastFlushEnd.get(t)??0,i=Math.max(0,a+s-Date.now());if(i>0&&await new Promise(f=>setTimeout(f,i)),this.closed)return;let l=this.pending.get(t)??"";if(this.pending.delete(t),!l||(this.opts.isRaw(n,r)||(l=$a(l)),!l.trim()))return;let u=o.appsMaxFlushBytes;if(l.length>u){let f=l.slice(u);l=l.slice(0,u)+`
|
|
190
|
+
[\u2026+${f.length} chars; /apps since ${r} to read more]`,this.pending.set(t,(this.pending.get(t)??"")+f)}let d=(this.opts.getRunningCount(n)>1?`[${r}] `:"")+l,p=wu(d,o.appsMaxWaChars);for(let f of p){if(this.closed)break;try{await this.opts.send(n,f)}catch{break}}this.lastFlushEnd.set(t,Date.now())}finally{this.flushing.delete(t),!this.closed&&(this.pending.get(t)??"").length>0&&queueMicrotask(()=>void this.flushNow(t,n,r))}}}};import ku from"node:fs";import Su from"node:path";import*as Ca from"node-pty";var xu=1024*1024,cr=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>xu&&this.ringChunks.length>0;){let n=this.ringChunks.shift();this.ringBytes-=n.length}}static start(t){N(Jt(t.peerKey));let n=Su.join(Jt(t.peerKey),`${t.name}.log`),r=ku.createWriteStream(n,{flags:"a",mode:384}),o={...process.env,TERM:"xterm-256color",COLORTERM:"truecolor",...t.cwd?{PWD:t.cwd}:{},...t.extraEnv??{}},s=Ca.spawn(t.shell,["-lc",t.command],{name:"xterm-256color",cols:t.cols,rows:t.rows,cwd:t.cwd,env:o}),a=new e({...t,logPath:n});return a.term=s,a.logStream=r,a.disposeData=s.onData(i=>{let l=Buffer.from(i,"utf8");a.appendRing(l),r.write(i),t.router.push(t.peerKey,t.name,i)}),a.disposeExit=s.onExit(i=>{a.exited||(a.exited=!0,a.cleanupTerm(),t.onExit({exitCode:i.exitCode,signal:i.signal}))}),a}get alive(){return this.term!==null&&!this.exited}get ringByteCount(){return this.ringBytes}write(t){this.term?.write(t)}resize(t,n){this.term?.resize(t,n)}kill(t){if(this.term)try{this.term.kill(t)}catch{}}cleanupTerm(){this.disposeData?.dispose(),this.disposeData=null,this.disposeExit?.dispose(),this.disposeExit=null,this.term=null,this.logStream?.end(),this.logStream=null}destroy(){if(this.exited){this.cleanupTerm();return}this.exited=!0,this.kill("SIGHUP"),this.cleanupTerm()}};var $u=/^[a-zA-Z0-9_-]{1,32}$/;function ye(e){return $u.test(e)?null:"Session name must be 1\u201332 chars: letters, digits, _ or -."}function ge(e,t){return`${e}:${t}`}function Cu(e){let t=e.lastIndexOf(":");return t<=0?null:{peerKey:e.slice(0,t),name:e.slice(t+1)}}function Ao(e){return new Promise(t=>setTimeout(t,e))}function Ru(e,t){return e===0&&(t===0||t==null)}async function Eo(e,t,n){let r=n.appsSubmitDelayMs,o=n.appsClearInputDelayMs,a=n.appsClearInput!==!1?Io(n.appsClearInputSequence||"^A,^K"):"",l=t.replace(/\r\n/g,`
|
|
180
191
|
`).replace(/\r/g,"").split(`
|
|
181
|
-
`);a&&(o>0&&await
|
|
182
|
-
`).trimEnd()}catch{return""}}var
|
|
183
|
-
`)}drainNextQueuedRun(t,n){let r=this.runQueue.get(t);if(!r||r.length===0)return"";let o=r.shift();r.length===0?this.runQueue.delete(t):this.runQueue.set(t,r);let s=
|
|
192
|
+
`);a&&(o>0&&await Ao(o),e.write(a));for(let u of l)u.length>0&&e.write(u),r>0&&await Ao(r),e.write("\r"),a&&(o>0&&await Ao(o),e.write(a))}function Mu(e,t){try{let r=at.readFileSync(e,"utf8").split(/\r?\n/);return r.slice(Math.max(0,r.length-t)).join(`
|
|
193
|
+
`).trimEnd()}catch{return""}}var Nt=class{constructor(t,n){this.getCfg=t;this.send=n,this.router=new lr({getCfg:()=>this.getCfg(),send:n,isMuted:(r,o)=>this.muted.has(ge(r,o)),isRaw:(r,o)=>this.rawMode.has(ge(r,o)),getRunningCount:r=>this.countPeerRunning(r)})}sessions=new Map;focus=new Map;muted=new Set;rawMode=new Set;router;killTimers=new Set;send;runQueue=new Map;activeQueuedRunHead=new Map;runQueuePaused=new Map;countPeerRunning(t){let n=`${t}:`,r=0;for(let[o,s]of this.sessions)o.startsWith(n)&&s.alive&&r++;return r}countTotalRunning(){let t=0;for(let n of this.sessions.values())n.alive&&t++;return t}dispose(){for(let t of this.killTimers)clearTimeout(t);this.killTimers.clear(),this.stopAll(),this.router.dispose()}stopAll(){for(let t of this.sessions.values())t.destroy();this.sessions.clear(),this.focus.clear(),this.runQueue.clear(),this.activeQueuedRunHead.clear(),this.runQueuePaused.clear()}getFocus(t){return this.focus.get(t)??null}logPath(t,n){return vu.join(Jt(t),`${n}.log`)}getSession(t,n){return this.sessions.get(ge(t,n))}removeSessionRecord(t,n){this.sessions.delete(ge(t,n)),this.focus.get(t)===n&&this.focus.set(t,null)}async writeFocusedLine(t,n){let r=this.focus.get(t);if(!r)return!1;let o=this.getSession(t,r);return o?.alive?(await Eo(o,n,this.getCfg()),!0):!1}async writeNamedLine(t,n,r){let o=ye(n);if(o)return o;let s=this.getSession(t,n);return s?.alive?(await Eo(s,r,this.getCfg()),null):`No app session "${n}". /apps list`}enqueueQueuedRun(t,n,r){let o=this.runQueue.get(t)??[];o.push(n),this.runQueue.set(t,o);let s=o.length,a=this.runQueuePaused.get(t)??!1,i=this.activeQueuedRunHead.get(t)??null,l=i?.sessionName??null;if(l?this.getSession(t,l)?.alive??!1:!1){let c=i?.recipeLabel?` (recipe: ${i.recipeLabel})`:"",d=s-1,p=d>0?`${d} other job(s) in line before this one.`:"Next in line after the active run finishes.";return`Queued "${n.recipeLabel}" (wait slot ${s} behind active ${l}${c}). ${p}`}return a?`Queued "${n.recipeLabel}" (position ${s}). Run queue is paused \u2014 /run queue resume to continue.`:this.drainNextQueuedRun(t,r)}resumeRunQueue(t,n){this.runQueuePaused.set(t,!1);let r=this.activeQueuedRunHead.get(t)??null,o=r?.sessionName??null;if(o?this.getSession(t,o)?.alive??!1:!1){let i=r?.recipeLabel?` (recipe: ${r.recipeLabel})`:"";return`Run queue: session "${o}"${i} is still running. Wait for exit code=0 and signal=0 before the next starts.`}return(this.runQueue.get(t)??[]).length===0?"Run queue is empty.":(this.activeQueuedRunHead.set(t,null),this.drainNextQueuedRun(t,n))}runQueueStatus(t){let n=this.runQueue.get(t)??[],r=this.activeQueuedRunHead.get(t)??null,o=r?.sessionName??null,s=this.runQueuePaused.get(t)??!1,a=o?this.getSession(t,o)?.alive??!1:!1,i=["Run queue (this chat)"];if(o&&a){let l=r?.recipeLabel?` \xB7 recipe: ${r.recipeLabel}`:"";i.push(`Active: ${o}${l}`)}else o?i.push(`Active: ${o} (exiting or stale)`):i.push("Active: (none)");if(i.push(`Pending: ${n.length} (waiting only \u2014 not counting the active run above)`),i.push(`Paused: ${s}`),n.length>0){i.push("Waiting (FIFO order):");for(let u=0;u<Math.min(n.length,20);u++)i.push(`${u+1}) ${n[u].recipeLabel}`);n.length>20&&i.push(`\u2026 and ${n.length-20} more`)}return i.push("Next auto-starts only after exit code=0 and signal=0."),i.push("Commands: /run queue resume"),i.join(`
|
|
194
|
+
`)}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=Qn(o.recipeLabel),a=this.runQueue.get(t)?.length??0,i=this.start(t,s,o.command,n,o.extraEnv);if(!i.includes("started and attached"))return r.unshift(o),this.runQueue.set(t,r),this.runQueuePaused.set(t,!0),this.activeQueuedRunHead.set(t,null),`${i}
|
|
184
195
|
Run queue paused; fix the issue, then /run queue resume.`;this.activeQueuedRunHead.set(t,{sessionName:s,recipeLabel:o.recipeLabel});let u=a>0?`
|
|
185
196
|
Run queue: started head above; ${a} job(s) still waiting in FIFO.`:`
|
|
186
197
|
Run queue: started head above; no further queued jobs.`;return`${i}${u}`}start(t,n,r,o,s){let a=ye(n);if(a)return a;if(!r.trim())return`Usage: /apps start <name> <command\u2026>
|
|
187
|
-
Example: /apps start sh bash`;if(this.sessions.has(
|
|
188
|
-
If install skipped native builds: pnpm approve-builds && pnpm install (needs @whiskeysockets/baileys, sharp, protobufjs, esbuild, node-pty).`}return this.sessions.set(
|
|
198
|
+
Example: /apps start sh bash`;if(this.sessions.has(ge(t,n)))return`Session "${n}" already exists. /apps stop ${n} or pick another name.`;if(this.countPeerRunning(t)>=o.appsMaxSessions)return`Per-chat app limit (${o.appsMaxSessions}) reached. /apps stop or /apps rm an old session.`;if(this.countTotalRunning()>=o.appsMaxSessionsTotal)return`Global app limit (${o.appsMaxSessionsTotal}) reached. Stop sessions in other chats first.`;q();let i=J(t).cwd,l={...process.env,TERM:"xterm-256color",COLORTERM:"truecolor",...i?{PWD:i}:{},...s??{}},u;try{u=cr.start({peerKey:t,name:n,shell:o.shell,command:r.trim(),cwd:i,cols:o.appsCols,rows:o.appsRows,envKeysCount:Object.keys(l).length,extraEnv:s,router:this.router,onExit:c=>{this.handleSessionExit(t,n,c)}})}catch(c){return`App spawn failed: ${String(c)}
|
|
199
|
+
If install skipped native builds: pnpm approve-builds && pnpm install (needs @whiskeysockets/baileys, sharp, protobufjs, esbuild, node-pty).`}return this.sessions.set(ge(t,n),u),this.focus.set(t,n),`App "${n}" started and attached.
|
|
189
200
|
[cwd: ${i}]
|
|
190
|
-
Plain DMs go to this session until /apps detach. Use >othername text for another session.`}async handleSessionExit(t,n,r){let o=
|
|
201
|
+
Plain DMs go to this session until /apps detach. Use >othername text for another session.`}async handleSessionExit(t,n,r){let o=ge(t,n);if(!this.sessions.has(o))return;let s=this.activeQueuedRunHead.get(t)?.sessionName===n,a=this.focus.get(t)===n;this.sessions.delete(o),a&&this.focus.set(t,null),this.muted.delete(o),this.rawMode.delete(o);let i=r.signal!=null?` signal=${r.signal}`:"",l=a?" (detached)":"",u=`[${n}] exited code=${r.exitCode}${i}${l}`;try{await this.send(t,u)}catch{}if(!s)return;this.activeQueuedRunHead.set(t,null);let c=this.getCfg(),d=Ru(r.exitCode,r.signal),p=this.runQueue.get(t)?.length??0;if(d){if(this.runQueuePaused.set(t,!1),p>0){let f=this.drainNextQueuedRun(t,c);if(f)try{await this.send(t,f)}catch{}}return}if(this.runQueuePaused.set(t,!0),p>0){let f=`Run queue paused (exit code=${r.exitCode}${r.signal!=null?` signal=${r.signal}`:""}). ${p} run(s) waiting. /run queue resume to continue.`;try{await this.send(t,f)}catch{}}}attach(t,n){let r=ye(n);return r||(this.getSession(t,n)?.alive?(this.focus.set(t,n),`Attached to "${n}". Plain messages (no ! or / or >) go to this app. /apps detach to stop.`):`No session "${n}". /apps list`)}detach(t){return this.focus.set(t,null),"Detached (attach mode off)."}list(t){let n=[];for(let[s,a]of this.sessions){let i=Cu(s);if(!i||i.peerKey!==t||!a.alive)continue;let l=this.focus.get(t)===i.name?" *":"";n.push(`${i.name}${l}`)}if(n.length===0)return"(no app sessions; /apps start <name> <cmd>)";let r=this.focus.get(t);return`${r?`attached: ${r}`:"(no focus)"}
|
|
191
202
|
App sessions:
|
|
192
203
|
${n.join(`
|
|
193
|
-
`)}`}info(t,n){let r=this.getCfg(),o=n??this.focus.get(t)??"";if(!o)return"Usage: /apps info <name> (or /apps get <name>, or attach first)";let s=ye(o);if(s)return s;let a=this.getSession(t,o),i=this.logPath(t,o),l=0;try{l=
|
|
204
|
+
`)}`}info(t,n){let r=this.getCfg(),o=n??this.focus.get(t)??"";if(!o)return"Usage: /apps info <name> (or /apps get <name>, or attach first)";let s=ye(o);if(s)return s;let a=this.getSession(t,o),i=this.logPath(t,o),l=0;try{l=at.statSync(i).size}catch{}let u=this.muted.has(ge(t,o)),c=this.rawMode.has(ge(t,o));return[`session: ${o}`,`alive: ${a?.alive??!1}`,`cmd: ${a?.command??"(unknown)"}`,`cwd: ${a?.cwd??"(unknown)"}`,`env keys: ${a?.envKeysCount??"?"}`,`terminal size: ${r.appsCols}x${r.appsRows}`,`ring bytes (approx): ${a?.ringByteCount??0}`,`log: ${i} (${l} bytes)`,`mute outbound: ${u}`,`raw outbound: ${c}`].join(`
|
|
194
205
|
`)}async sendText(t,n,r){let o=ye(n);if(o)return o;let s=this.getSession(t,n);if(!s?.alive)return`No session "${n}".`;let a=r.replace(/\r\n/g,`
|
|
195
|
-
`);return await
|
|
206
|
+
`);return await Eo(s,a,this.getCfg()),`Sent to "${n}" (${a.length} chars + Enter per line).`}sendKey(t,n,r){let o=ye(n);if(o)return o;let s=this.getSession(t,n);if(!s?.alive)return`No session "${n}".`;let a=Io(r);return a?(s.write(a),`Sent keys to "${n}".`):"Empty key sequence. Example: /apps key sh Enter or ^C,Up,Enter"}tail(t,n,r){let o=ye(n);if(o)return o;let s=this.logPath(t,n);if(!at.existsSync(s))return`(no log file for ${n})`;let a=this.getCfg(),i=Number.isFinite(r)&&r>0?Math.min(500,r):a.appsLogTailLines;return Mu(s,i)||"(empty log)"}readSince(t,n,r){let o=this.logPath(t,n);try{let a=at.statSync(o).size,i=at.openSync(o,"r");try{let l=Math.min(r,a),u=a-l,c=Buffer.alloc(u);return u>0&&at.readSync(i,c,0,u,l),{text:c.toString("utf8"),nextOffset:a}}finally{at.closeSync(i)}}catch{return{text:"",nextOffset:r}}}mute(t,n){let r=ye(n);return r||(this.muted.add(ge(t,n)),`Muted chat output for "${n}" (log still grows). /apps unmute ${n}`)}unmute(t,n){let r=ye(n);return r||(this.muted.delete(ge(t,n)),`Unmuted "${n}".`)}setRaw(t,n,r){let o=ye(n);if(o)return o;let s=ge(t,n);return r?this.rawMode.add(s):this.rawMode.delete(s),`raw chat output for "${n}": ${r?"on (ANSI kept)":"off (stripped)"}.`}resize(t,n,r,o){let s=ye(n);if(s)return s;let a=this.getSession(t,n);if(!a?.alive)return`No session "${n}".`;let i=Math.min(500,Math.max(20,Math.floor(r))),l=Math.min(200,Math.max(5,Math.floor(o)));return a.resize(i,l),`Resized "${n}" to ${i}x${l}.`}stop(t,n){let r=ye(n);if(r)return r;let o=this.getSession(t,n);if(!o?.alive)return`No running session "${n}".`;o.kill("SIGTERM");let s=ge(t,n),a=setTimeout(()=>{this.killTimers.delete(a);let i=this.getSession(t,n);i?.alive&&i.kill("SIGKILL")},5e3);return this.killTimers.add(a),`SIGTERM sent to "${n}". SIGKILL in 5s if still running.`}kill(t,n){let r=ye(n);if(r)return r;let o=this.getSession(t,n);return o?.alive?(o.kill("SIGKILL"),`SIGKILL sent to "${n}".`):`No running session "${n}".`}rm(t,n){let r=ye(n);if(r)return r;if(this.getSession(t,n)?.alive)return`Session "${n}" is still running. /apps stop ${n} first, then /apps rm ${n}.`;this.removeSessionRecord(t,n),this.muted.delete(ge(t,n)),this.rawMode.delete(ge(t,n));let s=this.logPath(t,n);try{at.rmSync(s,{force:!0})}catch{}return`Removed "${n}" metadata and log.`}};import dn from"node:fs";import pn from"node:path";function Ra(e,t,n){if(e==="none")return[];let r=new Set;if((e==="self"||e==="all")&&r.add(t),e==="wa"||e==="all")for(let o of n.allowFrom){let s=j(String(o));s&&r.add(`wa:${xn(s)}`)}if(e==="tg"||e==="all")for(let o of n.telegramAllowFrom){let s=ie(String(o));s&&r.add(`tg:${s}`)}return[...r]}var Tu=8,Ma=12e4,Ta=10,Iu=1e3;function Au(e){return e.toISOString().replace(/[:.]/g,"-")}function Eu(e){let t="";for(let n of e)n==="*"?t+="[^/\\\\]*":n==="?"?t+="[^/\\\\]":/[.+^${}()|[\]\\]/.test(n)?t+=`\\${n}`:t+=n;return new RegExp(`^${t}$`)}function Ou(e,t,n){let r=Pt(e,t),o=pn.dirname(r),s=pn.basename(r);if(o.includes("*")||o.includes("?"))return[];if(!s.includes("*")&&!s.includes("?")){try{let u=dn.statSync(r);if(u.isFile()&&u.mtimeMs>=n)return[r]}catch{}return[]}let a;try{a=dn.readdirSync(o)}catch{return[]}let i=Eu(s),l=[];for(let u of a){if(!i.test(u))continue;let c=pn.join(o,u);try{let d=dn.statSync(c);d.isFile()&&d.mtimeMs>=n&&l.push(c)}catch{}}return l}function Ia(e,t,n){let r=et(e,t.fileSendMaxBytes);return"error"in r?{ok:!1,displayName:n??pn.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 Aa(e,t,n,r){let o=J(t.ownerPeerKey),s=t.cwd.trim()?t.cwd:o.cwd,a=Pt(t.outputDir,o.cwd);try{dn.mkdirSync(a,{recursive:!0,mode:448})}catch(U){E.warn({err:String(U),outDir:a},"cowork mkdir outputDir")}let i=n.slotMs!==null?new Date(n.slotMs).toLocaleString(void 0,{dateStyle:"short",timeStyle:"short"}):"on-demand",l=n.onDemand?"on-demand":n.catchUp?"catch-up":"scheduled",u=Date.now(),c=await Yn(e.shell,t.command,{timeoutMs:e.syncTimeoutMs,maxBytes:e.syncMaxBytes,cwd:s}),d=Date.now()-u,p=`${Au(new Date)}-${t.id}-${l}.log`,f=pn.join(a,p),g=[`cowork task=${t.name} id=${t.id}`,`slot=${i} kind=${l}`,`cwd=${s}`,`exit=${c.code} timedOut=${c.timedOut} durationMs=${d}`,"---",""].join(`
|
|
196
207
|
`)+(c.stdout||"")+(c.stderr?`
|
|
197
208
|
--- stderr ---
|
|
198
|
-
${c.stderr}`:"");try{
|
|
199
|
-
`),
|
|
200
|
-
`,{mode:384})}function
|
|
201
|
-
[spawn error] ${String(
|
|
202
|
-
`)}catch{}});let f=
|
|
209
|
+
${c.stderr}`:"");try{dn.writeFileSync(f,g,{mode:384})}catch(U){E.warn({err:String(U),logPath:f},"cowork write log")}let y=c.code===0&&!c.timedOut&&c.signal===null,k=be().find(U=>U.id===t.id&&U.ownerPeerKey===t.ownerPeerKey),R=k?.notify??t.notify,_=k?.attachLog??t.attachLog,z=k?.attachFiles??t.attachFiles,O=c.timedOut?"timeout":c.signal?`signal ${c.signal}`:c.code!==0&&c.code!==null?`exit ${c.code}`:null,le=`slot: ${i} \xB7 ${l}${O?` \xB7 ${O}`:""}`,Re=(c.stdout||"").replace(/\s+$/,""),Se=(c.stderr||"").trim(),Le=Re||(Se?`(stderr) ${Se}`:"(no output)"),We=`${t.name} : ${O?`[${O}] `:""}${Le}`,T=n.onDemand?[le,`output: ${Le}`]:[We],w=[],x=[],M=u-Iu,D=[],H=new Set;if(Array.isArray(z))for(let U of z){let Pe;try{Pe=Ou(U,s,M)}catch(Fe){E.warn({err:String(Fe),pat:U},"cowork attach glob"),w.push(`attach: ${U} skipped (glob error)`);continue}if(Pe.length!==0)for(let Fe of Pe)H.has(Fe)||(H.add(Fe),D.push(Fe))}if(_){let U=Ia(f,e,`${t.name}-${l}.log`);U.ok?x.push(U.spec):w.push(`attach: ${U.displayName} skipped (${U.reason})`)}let te=0;for(let U of D){if(x.length>=Ta){te+=1;continue}let Pe=Ia(U,e);Pe.ok?x.push(Pe.spec):w.push(`attach: ${Pe.displayName} skipped (${Pe.reason})`)}te>0&&w.push(`attached: skipped ${te} file(s) over cap ${Ta}`),w.length>0&&T.push(...w);let xe=T.join(`
|
|
210
|
+
`),Me=m(xe),Xe=Ra(R,t.ownerPeerKey,e);for(let U of Xe){let Pe=He(Me,U.startsWith("tg:")?"telegram":"whatsapp").text;try{await r.sendToPeer(U,Pe)}catch(Fe){E.warn({err:String(Fe),pk:U},"cowork notify failed")}for(let Fe of x)try{await r.sendMediaToPeer(U,Fe)}catch($l){E.warn({err:String($l),pk:U,file:Fe.displayName},"cowork media notify failed")}}return{commandOk:y,logPath:f}}function Ea(e){let t=!1;ln(be());let n=async()=>{if(t)return;t=!0;let s=Date.now(),a=0,i=0,l=0,u=0,c=0;try{let d=e.getConfig(),{batch:p,remainingAfter:f}=ya(Tu);i=p.length,l=f,a=p.length+f;for(let y of p)try{let k=be().find(R=>R.name===y.name.toLowerCase()&&R.ownerPeerKey===y.ownerPeerKey);k&&k.enabled&&(u+=1,await Aa(d,k,{slotMs:null,catchUp:!1,onDemand:!0},e))}catch(v){E.warn({err:String(v),pending:y.name},"cowork on-demand run failed")}let h=be();ln(h);let g=Date.now();for(let y of h){if(!y.enabled||y.schedule.kind==="ondemand")continue;let v=ir(y),k=aa(y.schedule,v,y.createdAtMs,g);if(k.length===0)continue;let R=k[k.length-1],_=g-R>Ma;try{c+=1;let{commandOk:z,logPath:O}=await Aa(d,y,{slotMs:R,catchUp:_,onDemand:!1},e);if(z){let le=Date.now(),Re=g-R<=Ma?"on_time":"catch_up";if(k.length===1)ar(y.id,[{slotMs:R,kind:Re,logPath:O,completedAtMs:le}]);else{let Le=k.slice(0,-1).map(We=>({slotMs:We,kind:"coalesced",logPath:null,completedAtMs:le}));Le.push({slotMs:R,kind:Re,logPath:O,completedAtMs:le}),ar(y.id,Le)}}}catch(z){E.warn({err:String(z),task:y.name},"cowork scheduled run failed")}}}finally{let d=Date.now()-s;E.info({tickMs:d,pendingDepthStart:a,pendingDequeued:i,pendingRemainingAfter:l,pendingRunsStarted:u,scheduledRan:c},"cowork tick"),t=!1}},r=setInterval(()=>void n(),3e4),o=setTimeout(()=>void n(),5e3);return()=>{clearInterval(r),clearTimeout(o),da()}}import{spawn as Lu}from"node:child_process";import Pu from"node:crypto";import ke from"node:fs";import Oo from"node:path";function _t(e,t){ke.writeFileSync(e,JSON.stringify(t,null,2)+`
|
|
211
|
+
`,{mode:384})}function mn(e){try{return JSON.parse(ke.readFileSync(e,"utf8"))}catch{return null}}var wt=class{running=new Map;metaPath(t){return Oo.join(Te,`${t}.meta.json`)}logPath(t){return Oo.join(Te,`${t}.log`)}spawnJob(t,n,r={}){q();let o=Pu.randomBytes(4).toString("hex"),s=this.logPath(o),a=this.metaPath(o);ke.writeFileSync(s,"",{flag:"w",mode:384});let i=ke.createWriteStream(s,{flags:"a"}),l=new Date().toISOString(),u=r.cwd,c=Lu(t,["-c",n],{stdio:["ignore","pipe","pipe"],env:{...process.env,TERM:"dumb",...u?{PWD:u}:{}},...u?{cwd:u}:{}});this.running.set(o,c);let d={id:o,cmd:n,pid:c.pid??null,startedAt:l,status:"running",exitCode:null,signal:null};return _t(a,d),c.stdout?.on("data",p=>{i.write(p)}),c.stderr?.on("data",p=>{i.write(p)}),c.on("close",(p,f)=>{this.running.delete(o),i.end();let h=mn(a)??d,g={...h,status:h.status==="killed"?"killed":"done",exitCode:p,signal:f??null,finishedAt:new Date().toISOString()};_t(a,g)}),c.on("error",p=>{this.running.delete(o),i.end(()=>{try{ke.appendFileSync(s,`
|
|
212
|
+
[spawn error] ${String(p)}
|
|
213
|
+
`)}catch{}});let f=mn(a)??d;_t(a,{...f,status:"done",exitCode:null,signal:null,finishedAt:new Date().toISOString()})}),{id:o,meta:d}}list(){q();let t=[];try{t=ke.readdirSync(Te)}catch{return[]}let n=[];for(let r of t){if(!r.endsWith(".meta.json"))continue;let o=mn(Oo.join(Te,r));o&&n.push(o)}return n.sort((r,o)=>r.startedAt<o.startedAt?1:-1),n}tailLog(t,n){let r=this.logPath(t);if(!ke.existsSync(r))return"(no log file)";let s=ke.readFileSync(r,"utf8").split(`
|
|
203
214
|
`);return s.slice(Math.max(0,s.length-n)).join(`
|
|
204
|
-
`).trimEnd()||"(empty log)"}readSince(t,n){let r=this.logPath(t);if(!ke.existsSync(r))return{text:"",nextOffset:n};let s=ke.statSync(r).size;if(n>=s)return{text:"",nextOffset:s};let a=Buffer.alloc(s-n),i=ke.openSync(r,"r");try{ke.readSync(i,a,0,a.length,n)}finally{ke.closeSync(i)}return{text:a.toString("utf8"),nextOffset:s}}kill(t){let n=this.metaPath(t),r=
|
|
205
|
-
`).trim()}function
|
|
215
|
+
`).trimEnd()||"(empty log)"}readSince(t,n){let r=this.logPath(t);if(!ke.existsSync(r))return{text:"",nextOffset:n};let s=ke.statSync(r).size;if(n>=s)return{text:"",nextOffset:s};let a=Buffer.alloc(s-n),i=ke.openSync(r,"r");try{ke.readSync(i,a,0,a.length,n)}finally{ke.closeSync(i)}return{text:a.toString("utf8"),nextOffset:s}}kill(t){let n=this.metaPath(t),r=mn(n);if(!r)return`Unknown job id: ${t}`;let o=this.running.get(t);if(o&&!o.killed)return o.kill("SIGTERM"),_t(n,{...r,status:"killed",signal:"SIGTERM"}),`Sent SIGTERM to job ${t} (pid ${r.pid??"?"})`;if(r.pid)try{return process.kill(r.pid,"SIGTERM"),_t(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=mn(this.metaPath(t));r&&_t(this.metaPath(t),{...r,status:"killed",signal:"SIGTERM"})}this.running.clear()}};import dd from"node:fs";import{Bot as pd,InputFile as md}from"grammy";import Oa from"node:fs";import Bt from"node:path";function Fu(e){return e.replace(/[^a-zA-Z0-9._-]+/g,"_").slice(0,120)||"unknown"}function Nu(e){let r=Bt.basename(e.trim()||"file").replace(/[^a-zA-Z0-9._-]+/g,"_").replace(/^\.+/,"").slice(0,180);return r.length>0?r:"file"}function _u(e,t){let n=new Date().toISOString().slice(0,10),r=Fu(t);return Bt.join(e,r,n)}function ur(e,t,n){let r=_u(e,t);N(r);let o=Nu(n),s=Bt.join(r,o);if(!Oa.existsSync(s))return s;let a=Bt.extname(o),i=a?o.slice(0,-a.length):o;for(let l=1;l<1e4;l+=1){let u=`${i}-${l}${a}`;if(s=Bt.join(r,u),!Oa.existsSync(s))return s}return Bt.join(r,`${i}-${Date.now()}${a}`)}import Bu from"node:dns";import Hu from"node:https";import{URL as Du}from"node:url";function Ht(e){if(e instanceof Error){let t=e.cause;return t!=null?`${e.message} (${String(t)})`:e.message}return String(e)}function La(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 ju(e,t){let r=t.split("/").filter(o=>o.length>0).map(encodeURIComponent).join("/");return`https://api.telegram.org/file/bot${e}/${r}`}var Pa="omnish (Telegram bot; https://github.com/labKnowledge/whatsLive)";function Uu(e){let t=new Du(e);return new Promise((n,r)=>{let o=Hu.request({hostname:t.hostname,port:t.port||443,path:t.pathname+t.search,method:"GET",headers:{"User-Agent":Pa,Accept:"*/*"},lookup(s,a,i){Bu.lookup(s,{family:4},i)}},s=>{let a=s.statusCode??0,i=[];s.on("data",l=>i.push(l)),s.on("end",()=>n({status:a,buffer:Buffer.concat(i)})),s.on("error",r)});o.on("error",r),o.end()})}async function Fa(e,t,n){let r;try{r=await e.api.getFile(t)}catch(a){return E.warn({err:String(a),fileId:t},"telegram getFile failed"),{error:"Could not resolve file on Telegram."}}if(!r.file_path)return{error:"Telegram did not return a file path."};let o=ju(e.token,r.file_path),s;try{let a=await fetch(o,{headers:{"User-Agent":Pa,Accept:"*/*"}});if(!a.ok)return E.warn({status:a.status,statusText:a.statusText,fileId:t},"telegram file fetch HTTP error"),{error:`Could not download file from Telegram (HTTP ${a.status}${a.statusText?` ${a.statusText}`:""}).`};try{s=Buffer.from(await a.arrayBuffer())}catch(i){return E.warn({err:String(i),fileId:t},"telegram file read body failed"),{error:`Could not read file from Telegram (${String(i)}).`}}}catch(a){E.warn({err:String(a),fileId:t},"telegram file fetch failed; retrying via HTTPS IPv4");try{let i=await Uu(o);if(i.status<200||i.status>=300)return E.warn({status:i.status,fileId:t},"telegram HTTPS IPv4 fallback HTTP error"),{error:`Could not download file from Telegram (HTTP ${i.status}).`};s=i.buffer}catch(i){return E.warn({err:String(a),err2:String(i),fileId:t},"telegram file download failed (fetch and IPv4 fallback)"),{error:`Could not download file from Telegram (network: ${Ht(a)}; IPv4 fallback: ${Ht(i)}).`}}}return n>0&&s.byteLength>n?{error:`Media too large (max ${n} bytes).`}:{buffer:s}}import{downloadMediaMessage as Wu,extensionForMediaMessage as Gu,getContentType as dr,isJidGroup as Ba,isLidUser as Ju}from"@whiskeysockets/baileys";import zu from"node:fs";function qu(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 Qu(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 Ha(e){return[qu(e),Qu(e)].filter(n=>n.trim().length>0).join(`
|
|
216
|
+
`).trim()}function Lo(e){return e.replace(/[\u0000-\u0008\u000B\u000C\u000E-\u001F\u007F]/g,"").replace(/\uFEFF/g,"").trim()}function Da(e){let t=dr(e??void 0);return t==="imageMessage"||t==="videoMessage"||t==="audioMessage"||t==="documentMessage"||t==="stickerMessage"}function Yu(e){let t=dr(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 Na(e){try{if(!e)return"file.bin";let t=Gu(e);return`file${t&&t.length>0?t.startsWith(".")?t:`.${t}`:".bin"}`}catch{return`${(dr(e??void 0)??"file").replace("Message","")||"file"}.bin`}}function Vu(e){let t=dr(e??void 0);if(!t)return Na(e);let n=e?.[t];return n?.fileName&&typeof n.fileName=="string"&&n.fileName.trim()?n.fileName.trim():Na(e)}async function _a(e,t,n,r){let o=t.message??void 0;if(!Da(o))return{};let s=r.fileReceiveMaxBytes,a=Yu(o);if(s>0&&a!==void 0&&a>s)return{error:`Media too large (max ${s} bytes).`};let i;try{i=await Wu(t,"buffer",{},{logger:e.logger,reuploadRequest:e.updateMediaMessage})}catch(d){return E.warn({err:String(d),detail:Ht(d)},"whatsapp downloadMediaMessage failed"),{error:`Could not download media from WhatsApp (${Ht(d)}).`}}if(s>0&&i.length>s)return{error:`Media too large (max ${s} bytes).`};let l;try{l=Ct(r,n)}catch(d){return{error:String(d)}}let u=Vu(o),c=ur(l,n,u);try{zu.writeFileSync(c,i,{mode:384})}catch{return{error:"Could not write media to inbox."}}return{path:c}}async function Ku(e,t){let n=t.remoteJid??"";if(!n)return{fromJid:"",fromE164:""};if(t.remoteJidAlt){let r=j(t.remoteJidAlt)??j(n)??"";return{fromJid:n,fromE164:r}}if(Ju(n))try{let r=await e.signalRepository?.lidMapping?.getPNForLID?.(n);if(r){let o=j(r)??"";if(o)return{fromJid:n,fromE164:o}}}catch{}return{fromJid:n,fromE164:j(n)??""}}function Xu(e){try{if(!C().clusterEnabled)return;let n=Ha(e.message??void 0);if(!n)return;let r=Oi(n);if(!r)return;let o=e.key?.remoteJid??"",s=null;if(o&&!Ba(o)){let a=j(o);a&&(s=`wa:${a}`)}ji(r,s)}catch(t){E.warn({err:String(t)},"cluster footer observation failed")}}function ja(e,t){let n=r=>{r.type==="notify"&&(async()=>{for(let o of r.messages){let s=o.key;if(!s)continue;if(s.fromMe){Xu(o);continue}let a=s.remoteJid;if(!a||Ba(a)||a.toLowerCase().endsWith("@status")||a==="status@broadcast")continue;let l=Lo(Ha(o.message??void 0)),{fromJid:u,fromE164:c}=await Ku(e,s),d=`wa:${u}`,p=Da(o.message??void 0);if(p&&!l){(async()=>{try{let g=C(),y=await _a(e,o,d,g);await t({fromJid:u,fromE164:c,text:"",messageId:s.id??void 0,mediaSavedPath:y.path,mediaError:y.error})}catch(g){E.error({err:String(g),fromJid:u},"whatsapp media-only background task failed")}})();continue}let f,h;if(p){let g=C(),y=await _a(e,o,d,g);f=y.path,h=y.error}!l&&!f&&!h||await t({fromJid:u,fromE164:c,text:l,messageId:s.id??void 0,mediaSavedPath:f,mediaError:h})}})()};return e.ev.on("messages.upsert",n),()=>{e.ev.off("messages.upsert",n)}}import id from"node:fs";import Zu from"node:process";import ed,{DisconnectReason as bt,fetchLatestBaileysVersion as td,makeCacheableSignalKeyStore as nd,useMultiFileAuthState as rd}from"@whiskeysockets/baileys";import od from"qrcode-terminal";var sd="0.1.0";function Po(e){return e?.error?.output?.statusCode}async function pr(e={}){q();let t=e.authDir??W,n=e.verbose===!0,r=la(),{state:o,saveCreds:s}=await rd(t),{version:a}=await td(),i=ed({version:a,logger:r,printQRInTerminal:!1,browser:["omnish","cli",sd],auth:{creds:o.creds,keys:nd(o.keys,r)},syncFullHistory:!1,markOnlineOnConnect:!1});return i.ev.on("creds.update",s),i.ev.on("connection.update",l=>{let{connection:u,lastDisconnect:c,qr:d}=l;if(d&&(e.onQr?.(d),e.printQr)){let p=Zu.stdout,f=b(p,"\xB7".repeat(42));console.log(re(p,"Scan with WhatsApp \u2192 Linked devices")),console.log(f),od.generate(d,{small:!0}),console.log(f)}if(u==="close"){let p=Po(c);n&&p===bt.loggedOut&&r.warn("WhatsApp session logged out (401).")}u==="open"&&n&&r.info("WhatsApp Web connected.")}),i.ws&&typeof i.ws.on=="function"&&i.ws.on("error",l=>{r.error({err:String(l)},"WebSocket error")}),i}function Dt(e){try{e.end(new Error("omnish: socket closed"))}catch{e.ws?.close()}}function Fo(e){return e?.output?.statusCode??e?.status??e?.error?.output?.statusCode}function mr(e){return e.user?.id?Promise.resolve():new Promise((t,n)=>{let r=o=>{if(o.connection==="open"){e.ev.off("connection.update",r),t();return}if(o.connection==="close"){e.ev.off("connection.update",r);let a=o.lastDisconnect?.error??o.lastDisconnect??new Error("Connection closed");n(a)}};e.ev.on("connection.update",r)})}function kt(e,t,n){return new Promise((r,o)=>{let s=setTimeout(()=>o(new Error(n)),t);e.then(a=>{clearTimeout(s),r(a)},a=>{clearTimeout(s),o(a)})})}var za=3500,ad=400,Ua=18e4,Wa=9e4,Ga=3e5;function No(e,t=za){if(e.length<=t)return[e];let n=[],r=0;for(;r<e.length;){let o=Math.min(r+t,e.length);if(o<e.length){let a=e.slice(r,o).lastIndexOf(`
|
|
206
217
|
|
|
207
|
-
`);a>Math.floor(t*.35)&&(o=r+a+2)}n.push(e.slice(r,o)),r=o}return n}function
|
|
208
|
-
`)),
|
|
209
|
-
${oe(n,"omnish link")} ${
|
|
210
|
-
`),await
|
|
211
|
-
${
|
|
212
|
-
`)}})}async function
|
|
213
|
-
`));for(let r=1;r<=2;r++)try{await
|
|
214
|
-
${K(
|
|
215
|
-
`);return}catch(o){if(r===1&&
|
|
216
|
-
${
|
|
217
|
-
${
|
|
218
|
-
`),
|
|
219
|
-
${
|
|
220
|
-
${
|
|
218
|
+
`);a>Math.floor(t*.35)&&(o=r+a+2)}n.push(e.slice(r,o)),r=o}return n}function fr(e){return new Promise(t=>setTimeout(t,e))}async function Ja(e,t){await Promise.race([e,fr(Ua).then(()=>{E.warn({jid:t,ms:Ua},"whatsapp outbound self-heal: prior send chain waited too long; continuing")})])}async function ld(e,t,n,r=3){let o;for(let s=1;s<=r;s+=1)try{await kt(e.sendMessage(t,{text:n}),Wa,`whatsapp sendMessage timed out after ${Wa}ms`);return}catch(a){o=a;let i=String(a);if(/not connected|closed|timed out|timeout/i.test(i)&&s<r){await fr(800*s);continue}throw a}throw o}function cd(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 ud(e,t,n,r=3){let o;for(let s=1;s<=r;s+=1)try{await kt(e.sendMessage(t,n),Ga,`whatsapp sendMedia timed out after ${Ga}ms`);return}catch(a){o=a;let i=String(a);if(/not connected|closed|timed out|timeout/i.test(i)&&s<r){await fr(800*s);continue}throw a}throw o}function qa(e,t={}){let n=new Map,r=t.decorate??(o=>o);return{async sendText(o,s){let a=r(s,o),i=n.get(o)??Promise.resolve(),l=Ja(i,o).then(async()=>{let u=No(a,za);for(let c=0;c<u.length;c+=1)await ld(e,o,u[c]??""),c<u.length-1&&await fr(ad)}).catch(u=>{E.error({err:String(u),jid:o},"sendText failed")});n.set(o,l),await l.finally(()=>{n.get(o)===l&&n.delete(o)})},async sendMedia(o,s){let a=id.readFileSync(s.absPath),i=cd(s,a),l=n.get(o)??Promise.resolve(),u=Ja(l,o).then(async()=>{await ud(e,o,i)}).catch(c=>{E.error({err:String(c),jid:o},"sendMedia failed")});n.set(o,u),await u.finally(()=>{n.get(o)===u&&n.delete(o)})}}}var fd=400;async function Qa(e,t,n,r){let o=t(),s=await Fa(e,r.fileId,o.fileReceiveMaxBytes);if("error"in s)return{mediaError:s.error};let a;try{a=Ct(o,n)}catch(l){return{mediaError:String(l)}}let i=ur(a,n,r.baseName);try{return dd.writeFileSync(i,s.buffer,{mode:384}),{mediaSavedPath:i}}catch{return{mediaError:"Could not write media to inbox."}}}function gd(e){return new Promise(t=>setTimeout(t,e))}async function _o(e,t,n,r={}){let o=new pd(e);await o.api.deleteWebhook({drop_pending_updates:!1});let s=new Map,a=r.decorate??(c=>c);async function i(c,d){let p=Math.min(t().appsMaxWaChars,4096),f=He(d,"telegram"),h=a(f.text,To(c)),g=f.parseModeHtml,v=(s.get(c)??Promise.resolve()).then(async()=>{let k=No(h,p);for(let R=0;R<k.length;R+=1){let _=k[R]??"",z=g?{parse_mode:"HTML"}:void 0;try{await o.api.sendMessage(c,_,z)}catch(O){if(g){E.warn({err:String(O),chatId:c},"telegram HTML send failed; retrying plain");let le=_.replace(/<[^>]+>/g,"");await o.api.sendMessage(c,le)}else throw O}R<k.length-1&&await gd(fd)}}).catch(k=>{E.error({err:String(k),chatId:c},"telegram sendText failed")});s.set(c,v),await v.finally(()=>{s.get(c)===v&&s.delete(c)})}async function l(c,d){let p=new md(d.absPath,d.displayName),f=d.caption,g=(s.get(c)??Promise.resolve()).then(async()=>{switch(d.category){case"image":await o.api.sendPhoto(c,p,f?{caption:f}:void 0);break;case"video":await o.api.sendVideo(c,p,f?{caption:f}:void 0);break;case"audio":await o.api.sendAudio(c,p,f?{caption:f}:void 0);break;case"document":await o.api.sendDocument(c,p,f?{caption:f}:void 0);break}}).catch(y=>{E.error({err:String(y),chatId:c},"telegram sendMedia failed")});s.set(c,g),await g.finally(()=>{s.get(c)===g&&s.delete(c)})}o.on("message",async c=>{let d=c.chat,p=c.message;if(!d||d.type!=="private"||!c.from||!p)return;let f="text"in p&&p.text?p.text:"",h="caption"in p&&p.caption?p.caption:"",g=Lo([f,h].filter(Boolean).join(`
|
|
219
|
+
`)),y=La(p),v=To(d.id),k=d.id,R=async O=>{if(O.kind==="file")await l(k,O.spec);else if(O.kind==="files")for(let le of O.specs)await l(k,le);else await i(k,O.body)};if(y&&!g){(async()=>{try{let O=await Qa(o,t,v,y);if(!O.mediaSavedPath&&!O.mediaError)return;await n({peerKey:v,text:"",tgChatId:d.id,tgReplyToMessageId:p.message_id,mediaSavedPath:O.mediaSavedPath,mediaError:O.mediaError},R)}catch(O){E.error({err:String(O),chatId:k},"telegram media-only background task failed")}})();return}let _,z;if(y){let O=await Qa(o,t,v,y);_=O.mediaSavedPath,z=O.mediaError}!g&&!_&&!z||await n({peerKey:v,text:g,tgChatId:d.id,tgReplyToMessageId:p.message_id,mediaSavedPath:_,mediaError:z},R)}),o.catch(c=>{E.error({err:String(c)},"telegram bot error")});let u=o.start();return{bot:o,sendText:i,sendMedia:l,stop:async()=>{await o.stop(),await u.catch(()=>{})}}}import hd from"node:fs";import yd from"node:path";function wd(e){let t=yd.join(e,"creds.json");try{let n=hd.readFileSync(t,"utf8"),r=JSON.parse(n);if(!r.me||typeof r.me!="object"||r.me===null)return null;let o=r.me,s=typeof o.id=="string"?o.id:void 0,a=typeof o.phoneNumber=="string"?o.phoneNumber:void 0;return!s&&!a?null:{id:s,phoneNumber:a}}catch{return null}}function Ya(e){let t=wd(e);if(!t)return null;let n=t.phoneNumber?.trim();if(n){let s=j(n);return s?`phone ${s}`:`phone ${n}`}let r=t.id?.trim();if(!r)return null;let o=r.toLowerCase();if(o.endsWith("@s.whatsapp.net")||o.endsWith("@c.us")){let s=j(r);return s?`phone ${s}`:`id ${r}`}return o.endsWith("@lid")?`device ${r} (LID \u2014 not an E.164; your number may appear after the gateway runs)`:`id ${r}`}import hr from"node:fs";import Ce from"node:process";import Va from"node:fs";var bd="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 gr(e){let t=String(e).toLowerCase();return t.includes("401")||t.includes("logged out")?!0:Fo(e)===bt.loggedOut}function kd(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 Bo(e){let t=!1;for(let n=0;n<3;n++){if(e.signal?.aborted)throw new Error("Pairing cancelled.");let r=await pr({authDir:e.authDir,printQr:e.printQr===!0,verbose:e.verbose===!0,onQr:e.onQr});e.onSocketReady?.(r);try{let o=kt(mr(r),6e5,bd);e.signal?await Promise.race([o,kd(e.signal)]):await o;return}catch(o){if(o instanceof Error&&o.message==="Pairing cancelled.")throw o;if(Fo(o)===bt.restartRequired&&!t){t=!0,e.onRestart515?.(),await new Promise(a=>setTimeout(a,1500));continue}throw o}finally{e.onSocketClosed?.(),Dt(r)}}throw new Error("Pairing failed after restart (515) retries.")}async function Ka(e){let t=e.authDir??W;q();for(let n=1;n<=2;n++)try{await Bo({...e,authDir:t});return}catch(r){if(r instanceof Error&&r.message==="Pairing cancelled.")throw r;if(n===1&&gr(r)){Va.rmSync(t,{recursive:!0,force:!0}),Va.mkdirSync(t,{recursive:!0,mode:448});continue}throw r}}async function Sd(e,t){let n=Ce.stdout;console.log(`
|
|
220
|
+
${oe(n,"omnish link")} ${b(n,"\u2014 QR appears below; scan from WhatsApp \u2192 Linked devices.")}
|
|
221
|
+
`),await Bo({authDir:e,verbose:t,printQr:!0,onRestart515:()=>{console.warn(`
|
|
222
|
+
${ce(Ce.stderr,"WhatsApp requested a restart after pairing (code 515). This is normal. Opening a new connection\u2026")}
|
|
223
|
+
`)}})}async function Xa(e={}){let t=e.authDir??W,n=e.verbose===!0;q(),e.force&&(hr.rmSync(t,{recursive:!0,force:!0}),hr.mkdirSync(t,{recursive:!0,mode:448}),console.log(`${we(Ce.stdout,"Cleared saved session (--force).")} ${b(Ce.stdout,"Requesting a new QR\u2026")}
|
|
224
|
+
`));for(let r=1;r<=2;r++)try{await Sd(t,n),console.log(`
|
|
225
|
+
${K(Ce.stdout,"Linked.")} ${S(Ce.stdout,"Session saved. You can run")} ${K(Ce.stdout,"omnish run")} ${S(Ce.stdout,"now.")}
|
|
226
|
+
`);return}catch(o){if(r===1&&gr(o)){console.warn(`
|
|
227
|
+
${ce(Ce.stderr,"WhatsApp returned logged-out (401). This often happens after Ctrl+C during link or corrupt auth files.")}
|
|
228
|
+
${ce(Ce.stderr,"Clearing auth dir and retrying once with a fresh QR\u2026")}
|
|
229
|
+
`),hr.rmSync(t,{recursive:!0,force:!0}),hr.mkdirSync(t,{recursive:!0,mode:448});continue}throw gr(o)&&console.error(`
|
|
230
|
+
${I(Ce.stderr,"Still failing after a clean auth directory. Try:")}
|
|
231
|
+
${b(Ce.stderr,` pnpm approve-builds && pnpm install
|
|
221
232
|
(pnpm may have skipped Baileys/sharp/protobuf build scripts.)
|
|
222
233
|
Then: omnish link --force
|
|
223
|
-
`)}`),o}}import{spawn as
|
|
224
|
-
`,{mode:384})}function
|
|
225
|
-
`);if(a===-1)return;let i=o.slice(0,a).trim();o=o.slice(a+1);let l=
|
|
226
|
-
`),r.end()})}),r.on("error",()=>{})});n.listen(0,"127.0.0.1",()=>{let r=n.address();if(!r||typeof r=="string"){
|
|
227
|
-
config: ${
|
|
234
|
+
`)}`),o}}import{spawn as xd,spawnSync as vd}from"node:child_process";import jt from"node:fs";import $d from"node:path";import Ut from"node:process";function yr(e){q(),N($d.dirname(e));let t=Ut.argv[1];if(!t)return{ok:!1,message:"Cannot resolve entry script; invoke via node path/to/dist/index.js."};let n=jt.openSync(e,"a"),r=xd(Ut.execPath,[t,"run"],{detached:!0,stdio:["ignore",n,n],env:{...Ut.env,OMNISH_BACKGROUND_GATEWAY:"1"}});return jt.closeSync(n),r.unref(),r.pid?{ok:!0,pid:r.pid}:{ok:!1,message:"Failed to start background gateway."}}function wr(){if(q(),!jt.existsSync(ee))return{outcome:"no_pidfile"};let e=jt.readFileSync(ee,"utf8").trim(),t=Number(e);if(!Number.isFinite(t)||t<=0){try{jt.unlinkSync(ee)}catch{}return{outcome:"invalid_pidfile"}}try{Ut.kill(t,0)}catch{try{jt.unlinkSync(ee)}catch{}return{outcome:"stale_cleaned",pid:t}}try{return Ut.kill(t,"SIGTERM"),{outcome:"sent_signal",pid:t}}catch(n){return Ut.platform==="win32"&&vd("taskkill",["/PID",String(t),"/T"],{windowsHide:!0}).status===0?{outcome:"taskkill_ok",pid:t}:{outcome:"failed",message:`could not signal process: ${String(n)}`}}}import Cd from"node:crypto";import Ho from"node:fs";import Rd from"node:net";var fn=null;function Md(){try{let e=Ho.readFileSync(xt,"utf8"),t=JSON.parse(e);return typeof t.token=="string"?t.token:""}catch{return""}}function Td(e){Ho.writeFileSync(xt,JSON.stringify(e,null,2)+`
|
|
235
|
+
`,{mode:384})}function Id(){try{Ho.unlinkSync(xt)}catch{}}async function Ad(e,t,n){let r;try{r=JSON.parse(e)}catch{return{ok:!1,error:"Invalid JSON."}}if(!r||typeof r!="object"||r.op!=="sendMedia")return{ok:!1,error:"Unsupported operation."};if(typeof r.token!="string"||!n||r.token!==n)return{ok:!1,error:"Unauthorized."};let o=t.getCfg(),s=typeof r.absPath=="string"?r.absPath.trim():"";if(!s)return{ok:!1,error:"Missing absPath."};let a=typeof r.caption=="string"&&r.caption.length>0?r.caption:void 0,i=et(s,o.fileSendMaxBytes);if("error"in i)return{ok:!1,error:i.error};let l={absPath:i.absPath,category:i.category,mimetype:i.mimetype,displayName:i.displayName,caption:a};if(r.channel==="whatsapp"){let u=t.getWaOutbound();if(!u)return{ok:!1,error:"WhatsApp outbound is not connected."};let c=o.gatewayMode;if(c!=="whatsapp"&&c!=="both")return{ok:!1,error:"gatewayMode does not include WhatsApp."};let d=typeof r.e164=="string"?r.e164.trim():"";if(!d.startsWith("+"))return{ok:!1,error:"WhatsApp destination must be E.164 (e.g. +15551234567)."};let p=xn(d);try{return await u.sendMedia(p,l),{ok:!0}}catch(f){return{ok:!1,error:String(f)}}}if(r.channel==="telegram"){let u=t.getTgSendMedia();if(!u)return{ok:!1,error:"Telegram outbound is not connected."};let c=o.gatewayMode;if(c!=="telegram"&&c!=="both")return{ok:!1,error:"gatewayMode does not include Telegram."};if(!Number.isFinite(r.chatId))return{ok:!1,error:"Invalid Telegram chat id."};try{return await u(r.chatId,l),{ok:!0}}catch(d){return{ok:!1,error:String(d)}}}return{ok:!1,error:"Unknown channel."}}function Za(e){if(fn)return;let t=Cd.randomBytes(32).toString("hex"),n=Rd.createServer(r=>{let o="";r.setTimeout(12e4),r.on("data",s=>{o+=s.toString("utf8");let a=o.indexOf(`
|
|
236
|
+
`);if(a===-1)return;let i=o.slice(0,a).trim();o=o.slice(a+1);let l=Md();Ad(i,e,l).then(u=>{r.write(`${JSON.stringify(u)}
|
|
237
|
+
`),r.end()})}),r.on("error",()=>{})});n.listen(0,"127.0.0.1",()=>{let r=n.address();if(!r||typeof r=="string"){E.error("gateway control: could not read listen address");return}let o={token:t,host:r.address,port:r.port};Td(o),E.info({port:r.port},"gateway control listening")}),n.on("error",r=>{E.error({err:String(r)},"gateway control server error")}),fn=n}function br(){if(fn){try{fn.close()}catch{}fn=null,Id()}}function kr(){let e=C(),t=e.gatewayMode,n=t==="whatsapp"||t==="both",r=t==="telegram"||t==="both",o=ne(e);if(r&&!o)return{ok:!1,message:`Telegram enabled (gatewayMode) but no bot token. Set telegramBotToken in config or TELEGRAM_BOT_TOKEN.
|
|
238
|
+
config: ${A}`};if(n&&!Ie())return{ok:!1,message:"WhatsApp enabled but no session. Run `omnish link` first."};let s=ot(e);return jn(s)?{ok:!1,message:`Fix security errors before starting the gateway (or change gatewayMode / token).
|
|
228
239
|
|
|
229
240
|
${s.filter(l=>l.severity==="error").map(l=>{let u=[l.message];return l.detail&&u.push(l.detail),l.fixHint&&u.push(`Fix: ${l.fixHint}`),u.join(`
|
|
230
241
|
`)}).join(`
|
|
231
242
|
|
|
232
|
-
`)}`}:{ok:!0}}import
|
|
233
|
-
`,{mode:384});try{
|
|
234
|
-
`,{mode:384});try{
|
|
235
|
-
`,{mode:384}),r}import
|
|
236
|
-
`,{mode:384})}function
|
|
243
|
+
`)}`}:{ok:!0}}import nl from"node:crypto";import St from"node:fs";import rl from"node:path";function Ed(){return nl.randomBytes(24).toString("hex")}function el(){return nl.randomBytes(32).toString("hex")}function tl(e){try{let t=St.readFileSync(e,"utf8"),n=JSON.parse(t);if(typeof n.token=="string"&&n.token.length>=16&&typeof n.secret=="string"&&n.secret.length>=16)return{token:n.token,secret:n.secret}}catch{}return null}function Od(){let e=tl(Ne);if(e)return e;if(e=tl(Gt),e){N(rl.dirname(Ne)),St.writeFileSync(Ne,JSON.stringify(e,null,2)+`
|
|
244
|
+
`,{mode:384});try{St.unlinkSync(Gt)}catch{}return e}return null}function ol(e){N(rl.dirname(Ne));let t=Od(),n=(e??"").trim();if(n){let o={token:n,secret:t?.secret??el()};St.writeFileSync(Ne,JSON.stringify(o,null,2)+`
|
|
245
|
+
`,{mode:384});try{St.existsSync(Gt)&&St.unlinkSync(Gt)}catch{}return o}if(t)return t;let r={token:Ed(),secret:el()};return St.writeFileSync(Ne,JSON.stringify(r,null,2)+`
|
|
246
|
+
`,{mode:384}),r}import lt from"node:fs";import Bd from"node:http";import ae from"node:path";import Oe from"node:process";import{fileURLToPath as Hd}from"node:url";import Dd from"node:os";import Do from"node:crypto";var jo="omnish_cfg_sess",sl=7*24*60*60*1e3;function Uo(e){let t=Date.now()+sl,n=Buffer.from(JSON.stringify({exp:t}),"utf8").toString("base64url"),r=Do.createHmac("sha256",e).update(n).digest("hex");return`${n}.${r}`}function il(e,t){let n=Ld(t??"")[jo];if(!n||!n.includes("."))return!1;let r=n.lastIndexOf("."),o=n.slice(0,r),s=n.slice(r+1),a=Do.createHmac("sha256",e).update(o).digest("hex");try{let i=Buffer.from(s,"hex"),l=Buffer.from(a,"hex");if(i.length!==l.length||!Do.timingSafeEqual(i,l))return!1}catch{return!1}try{let i=JSON.parse(Buffer.from(o,"base64url").toString("utf8"));return!(typeof i.exp!="number"||i.exp<Date.now())}catch{return!1}}function Wo(e){let t=Math.floor(sl/1e3);return`${jo}=${e}; HttpOnly; Path=/; SameSite=Lax; Max-Age=${t}`}function al(){return`${jo}=; HttpOnly; Path=/; SameSite=Lax; Max-Age=0`}function Ld(e){let t={};for(let n of e.split(";")){let r=n.indexOf("=");if(r===-1)continue;let o=n.slice(0,r).trim(),s=n.slice(r+1).trim();o&&(t[o]=decodeURIComponent(s))}return t}import Go from"node:fs";import Sr from"node:process";function ll(e){try{return Sr.kill(e,0),!0}catch{return!1}}function cl(e){let t=Date.now()+e;for(;Date.now()<t;);}function Pd(){try{let e=Go.readFileSync(wn,"utf8"),t=JSON.parse(e);if(!t||typeof t!="object")return null;let n=t,r=typeof n.pid=="number"?n.pid:Number(n.pid),o=typeof n.port=="number"?n.port:Number(n.port),s=typeof n.host=="string"?n.host:"",a=typeof n.startedAt=="string"?n.startedAt:"";return!Number.isFinite(r)||r<=0||!Number.isFinite(o)||o<=0||o>65535?null:{pid:r,port:o,host:s,startedAt:a}}catch{return null}}function ul(e){Go.writeFileSync(wn,`${JSON.stringify(e)}
|
|
247
|
+
`,{mode:384})}function gn(){try{Go.unlinkSync(wn)}catch{}}function dl(e){let t=Pd();if(t&&t.port===e&&t.pid!==Sr.pid){if(!ll(t.pid)){gn();return}try{Sr.kill(t.pid,"SIGTERM")}catch{}if(cl(350),ll(t.pid)){try{Sr.kill(t.pid,"SIGKILL")}catch{}cl(100)}gn()}}import Jo from"node:fs";import Fd from"node:process";function Nd(e){try{return Fd.kill(e,0),!0}catch{return!1}}function _d(){try{let e=Jo.readFileSync(ee,"utf8").trim(),t=Number.parseInt(e,10);return Number.isFinite(t)&&t>0?t:null}catch{return null}}function xr(){let e=_d();return e===null?!1:Nd(e)}var zo=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&&(Dt(this.currentSock),this.currentSock=null)}beginPairing(t){if(this.busy)throw new Error("Pairing already in progress.");if(xr())throw new Error("Gateway appears to be running (gateway.pid). Stop `omnish run` or your service before pairing from the browser.");if(!t.force&&Ie())throw new Error("WhatsApp session already linked. Use \u201CReplace session\u201D or run `omnish link --force` from the CLI.");q(),t.force&&(Jo.rmSync(W,{recursive:!0,force:!0}),Jo.mkdirSync(W,{recursive:!0,mode:448})),this.busy=!0,this.abort=new AbortController;let n=this.abort.signal;(async()=>{try{await Ka({authDir:W,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}})()}},hn=new zo;var G="application/json; charset=utf-8",yn=null;function jd(){gn();let e=yn;yn=null,e?e.close(()=>{Oe.exit(0)}):Oe.exit(0),setTimeout(()=>Oe.exit(0),4e3).unref()}function Ud(){let e=Oe.env.OMNISH_CONFIG_UI_STATIC?.trim(),t=Oe.env.OMNISH_UI_STATIC?.trim()||e;if(t&<.existsSync(ae.join(t,"index.html")))return t;let n=ae.dirname(Hd(import.meta.url)),r=ae.join(n,"ui");if(lt.existsSync(ae.join(r,"index.html")))return r;let o=ae.join(n,"..","..","dist","ui");if(lt.existsSync(ae.join(o,"index.html")))return o;let s=ae.join(Oe.cwd(),"dist","ui");if(lt.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 Wd(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 Gd(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 qo(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 Jd(e){return At.includes(e)}function Qo(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:!!ne(e),telegramBotTokenEnvOverride:typeof Oe.env.TELEGRAM_BOT_TOKEN=="string"&&Oe.env.TELEGRAM_BOT_TOKEN.trim().length>0}}function zd(){let e=C();return{version:Ue(),dataDir:L,configPath:A,waAuthDir:W,whatsappLinked:Ie(),gatewayPidHint:lt.existsSync(ae.join(L,"gateway.pid")),gatewayRunning:xr(),gatewayLogFile:pe,gatewayMode:e.gatewayMode,telegramBotTokenMasked:ne(e).length===0?"":Qo(e).telegramBotToken,telegramBotTokenEnvOverride:typeof Oe.env.TELEGRAM_BOT_TOKEN=="string"&&Oe.env.TELEGRAM_BOT_TOKEN.trim().length>0}}function qd(e,t){if(!Array.isArray(e)||!Array.isArray(t))throw new Error("allowFrom and telegramAllowFrom must be arrays of strings.");let n=e.map(i=>String(i).trim()).filter(Boolean),r=t.map(i=>String(i).trim()).filter(Boolean),o=vn(n).filter(i=>i!=="*"),s=[];for(let i of r){let l=ie(i);if(!l)throw new Error(`Invalid Telegram allow entry: ${i}`);s.push(l)}let a=C();a.allowFrom=o.sort(),a.telegramAllowFrom=[...new Set(s)].sort(),Be(a)}function Qd(e){return typeof e=="string"?e:typeof e=="boolean"||typeof e=="number"?String(e):JSON.stringify(e)}async function pl(e){let t=Ud(),{meta:n}=e;dl(e.port);let r=Bd.createServer(async(o,s)=>{try{let a=new URL(o.url??"/",`http://${o.headers.host??"localhost"}`),i=a.pathname;if(o.method==="GET"&&i==="/"){let p=a.searchParams.get("token")?.trim();if(p&&p===n.token){let f=Uo(n.secret);s.statusCode=302,s.setHeader("Location","/"),s.setHeader("Set-Cookie",Wo(f)),s.end();return}}if(i.startsWith("/api/")){let p=o.headers.cookie,f=il(n.secret,p);if(o.method==="POST"&&i==="/api/session"){let h=await qo(o),g=typeof h?.token=="string"?h.token.trim():"";if(!g||g!==n.token){s.statusCode=401,s.setHeader("Content-Type",G),s.end(JSON.stringify({ok:!1,error:"Invalid token."}));return}let y=Uo(n.secret);s.statusCode=200,s.setHeader("Content-Type",G),s.setHeader("Set-Cookie",Wo(y)),s.end(JSON.stringify({ok:!0}));return}if(o.method==="GET"&&i==="/api/me"){s.statusCode=f?200:401,s.setHeader("Content-Type",G),s.end(JSON.stringify({ok:f}));return}if(!f){s.statusCode=401,s.setHeader("Content-Type",G),s.end(JSON.stringify({ok:!1,error:"Unauthorized."}));return}if(o.method==="GET"&&i==="/api/status"){s.statusCode=200,s.setHeader("Content-Type",G),s.end(JSON.stringify({ok:!0,...zd()}));return}if(o.method==="GET"&&i==="/api/config"){s.statusCode=200,s.setHeader("Content-Type",G),s.end(JSON.stringify({ok:!0,config:Qo(C())}));return}if(o.method==="PUT"&&i==="/api/config"){let h=await qo(o);if(!h||typeof h!="object"){s.statusCode=400,s.setHeader("Content-Type",G),s.end(JSON.stringify({ok:!1,error:"Expected JSON object."}));return}let g=h;for(let[y,v]of Object.entries(g))if(v!==void 0&&!(y==="allowFrom"||y==="telegramAllowFrom")){if(y==="telegramBotToken"){let k=typeof v=="string"?v.trim():"";if(!k||k==="(set)"||k.includes("\u2026"))continue;if(!Ze(k))throw new Error("Invalid Telegram bot token format.");Kn("telegramBotToken",k);continue}if(!Jd(y))throw new Error(`Unknown or unsupported config key: ${y}`);Kn(y,Qd(v))}if("allowFrom"in g||"telegramAllowFrom"in g){let y=C();qd("allowFrom"in g?g.allowFrom:y.allowFrom,"telegramAllowFrom"in g?g.telegramAllowFrom:y.telegramAllowFrom)}s.statusCode=200,s.setHeader("Content-Type",G),s.end(JSON.stringify({ok:!0,config:Qo(C())}));return}if(o.method==="POST"&&i==="/api/logout"){s.statusCode=200,s.setHeader("Content-Type",G),s.setHeader("Set-Cookie",al()),s.end(JSON.stringify({ok:!0}));return}if(o.method==="POST"&&i==="/api/shutdown"){s.statusCode=200,s.setHeader("Content-Type",G),s.end(JSON.stringify({ok:!0})),setImmediate(()=>{hn.requestCancel(),jd()});return}if(o.method==="POST"&&i==="/api/gateway/start"){if(xr()){s.statusCode=409,s.setHeader("Content-Type",G),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=kr();if(!h.ok){s.statusCode=400,s.setHeader("Content-Type",G),s.end(JSON.stringify({ok:!1,error:h.message}));return}let g=pe,y=yr(g);if(!y.ok){s.statusCode=500,s.setHeader("Content-Type",G),s.end(JSON.stringify({ok:!1,error:y.message}));return}s.statusCode=200,s.setHeader("Content-Type",G),s.end(JSON.stringify({ok:!0,pid:y.pid,logFile:g}));return}if(o.method==="POST"&&i==="/api/gateway/stop"){let h=wr();switch(h.outcome){case"no_pidfile":s.statusCode=400,s.setHeader("Content-Type",G),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",G),s.end(JSON.stringify({ok:!1,error:"Invalid pidfile (removed)."}));return;case"stale_cleaned":s.statusCode=200,s.setHeader("Content-Type",G),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",G),s.end(JSON.stringify({ok:!0,pid:h.pid}));return;case"taskkill_ok":s.statusCode=200,s.setHeader("Content-Type",G),s.end(JSON.stringify({ok:!0,pid:h.pid,taskkill:!0}));return;case"failed":s.statusCode=500,s.setHeader("Content-Type",G),s.end(JSON.stringify({ok:!1,error:h.message}));return}}if(o.method==="GET"&&i==="/api/wa/link/events"){s.writeHead(200,{"Content-Type":"text/event-stream; charset=utf-8","Cache-Control":"no-store",Connection:"keep-alive"}),s.write(`: connected
|
|
237
248
|
|
|
238
|
-
`);let
|
|
249
|
+
`);let h=hn.subscribe(g=>{s.write(`data: ${JSON.stringify(g)}
|
|
239
250
|
|
|
240
|
-
`)});o.on("close",()=>{
|
|
241
|
-
`;return new Promise(o=>{let s=!1,a="";function i(u){s||(s=!0,o(u))}let l=
|
|
242
|
-
`);if(c>=0){let d=a.slice(0,c).trim();try{let
|
|
243
|
-
`)}var
|
|
244
|
-
`))}async function
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
`)
|
|
248
|
-
`)
|
|
249
|
-
`)}),
|
|
250
|
-
`))}function
|
|
251
|
-
`))}function
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
`))}function
|
|
255
|
-
|
|
256
|
-
`),console.
|
|
257
|
-
|
|
258
|
-
|
|
251
|
+
`)});o.on("close",()=>{h()});return}if(o.method==="POST"&&i==="/api/wa/link/start"){let g=(await qo(o))?.force===!0;try{hn.beginPairing({force:g})}catch(y){let v=String(y),k=v.includes("already in progress")||v.includes("Gateway appears")?409:400;s.statusCode=k,s.setHeader("Content-Type",G),s.end(JSON.stringify({ok:!1,error:v}));return}s.statusCode=202,s.setHeader("Content-Type",G),s.end(JSON.stringify({ok:!0}));return}if(o.method==="POST"&&i==="/api/wa/link/cancel"){hn.requestCancel(),s.statusCode=200,s.setHeader("Content-Type",G),s.end(JSON.stringify({ok:!0}));return}s.statusCode=404,s.setHeader("Content-Type",G),s.end(JSON.stringify({ok:!1,error:"Not found."}));return}let l=i==="/"?"index.html":i.replace(/^\/+/,""),u=Gd(t,l),c=ae.join(t,"index.html");u&<.existsSync(u)&<.statSync(u).isFile()&&(c=u);let d=lt.readFileSync(c);s.statusCode=200,s.setHeader("Content-Type",Wd(c)),s.setHeader("Cache-Control",ae.basename(c)==="index.html"?"no-store":"public, max-age=3600"),s.end(d)}catch(a){s.statusCode=500,s.setHeader("Content-Type",G),s.end(JSON.stringify({ok:!1,error:String(a)}))}});await new Promise((o,s)=>{r.once("error",s),r.listen(e.port,e.host,()=>o())}),yn=r,ul({pid:Oe.pid,port:e.port,host:e.host,startedAt:new Date().toISOString()}),r.on("close",()=>{gn(),yn===r&&(yn=null)})}function ml(e){let t=Dd.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 Xd from"node:readline";import gl from"node:path";import de from"node:process";import Yd from"node:net";import Vd from"node:fs";function Kd(){try{let e=Vd.readFileSync(xt,"utf8"),t=JSON.parse(e);return typeof t.host!="string"||typeof t.port!="number"||typeof t.token!="string"||!Number.isFinite(t.port)?null:{host:t.host,port:t.port,token:t.token}}catch{return null}}async function Yo(e){let t=Kd();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)}
|
|
252
|
+
`;return new Promise(o=>{let s=!1,a="";function i(u){s||(s=!0,o(u))}let l=Yd.connect({host:t.host,port:t.port},()=>{l.write(r)});l.setTimeout(6e5),l.on("data",u=>{a+=u.toString("utf8");let c=a.indexOf(`
|
|
253
|
+
`);if(c>=0){let d=a.slice(0,c).trim();try{let p=JSON.parse(d);p.ok?i(null):i(p.error||"Unknown error from gateway control.")}catch{i("Invalid response from gateway control.")}l.destroy()}}),l.on("error",u=>{i(`Control connection failed: ${String(u)}`)}),l.on("timeout",()=>{l.destroy(),i("Gateway control timed out.")}),l.on("close",()=>{!s&&a.trim()===""&&i("Gateway closed the control connection without a response.")})})}function fl(e){let t=e.trim();if(!/^\/sendto(\s|$)/i.test(t))return null;let n=t.replace(/^\/sendto\s+/i,"").trim();if(!n)return null;let r=n.indexOf(" ");if(r===-1)return null;let o=n.slice(0,r).trim(),s=n.slice(r+1).trim();if(!s)return null;let a=On(s);if(!a)return null;let{selectorPart:i,caption:l}=a,u=o.toLowerCase();if(u==="*"||u==="all")return{channel:"all",selectorPart:i,caption:l};if(u==="wa"||u==="whatsapp"||u==="wa:all"||u==="whatsapp:all")return{channel:"whatsapp-all",selectorPart:i,caption:l};if(u==="tg"||u==="telegram"||u==="tg:all"||u==="telegram:all")return{channel:"telegram-all",selectorPart:i,caption:l};if(u.startsWith("tg:")||u.startsWith("telegram:")){let c=ie(o);if(!c)return null;let d=Number(c);return Number.isFinite(d)?{channel:"telegram",chatId:d,selectorPart:i,caption:l}:null}if(u.startsWith("wa:")){let c=o.slice(3).trim();if(!c)return null;let d=c.split(",").map(p=>j(p.trim())).filter(p=>!!p);return d.length===0?null:{channel:"whatsapp",e164s:d,selectorPart:i,caption:l}}if(o.includes(",")){let c=o.split(",").map(d=>j(d.trim())).filter(d=>!!d);return c.length===0?null:{channel:"whatsapp",e164s:c,selectorPart:i,caption:l}}if(o.startsWith("+")){let c=j(o);return c?{channel:"whatsapp",e164s:[c],selectorPart:i,caption:l}:null}return null}function Vo(){return["/sendto wa|tg|* <selectors> [-- caption]","/sendto +E164 or +E164,+E164 <selectors> [-- caption]","/sendto tg:<chat_id> <selectors> [-- caption] (compat: single Telegram id)","selectors: file1,file2 or *.mp4 or **/*.mp4 (from current session cwd)","examples: /sendto wa ./promo.mp4 | /sendto * **/*.mp4 -- Daily clips","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(`
|
|
254
|
+
`)}var Wt="wa:cli:interactive";function Zd(e){let t=e.trim();if(!t)return null;let n=t.toLowerCase();if(n.startsWith("tg:")||n.startsWith("telegram:")){let s=ie(t);return s?`tg:${s}`:null}let r=n.startsWith("wa:")?t.slice(3):t,o=j(r);return o?`wa:${o}`:null}function ep(e){let t=null,n=null;for(let r=0;r<e.length;){let o=e[r]??"";if(o==="--as"){let s=e[r+1];if(!s||s.startsWith("-"))return{opts:{senderKey:null,oneShot:null},error:"--as requires a sender (wa:+E164 or tg:<id>)."};let a=Zd(s);if(!a)return{opts:{senderKey:null,oneShot:null},error:`Could not parse sender "${s}". Use +E164, wa:+E164, or tg:<user_id>.`};t=a,r+=2;continue}if(o==="-c"||o==="--command"){let s=e[r+1];if(typeof s!="string")return{opts:{senderKey:null,oneShot:null},error:`${o} requires a command string.`};n=s,r+=2;continue}return o==="--help"||o==="-h"?{opts:{senderKey:null,oneShot:null},error:"help"}:{opts:{senderKey:null,oneShot:null},error:`Unknown argument: ${o}`}}return{opts:{senderKey:t,oneShot:n},error:null}}function Ko(e){let t=de.cwd(),n=[`${K(e,"omnish i")} ${b(e,"[options]")}`,re(e,"Interactive shell \u2014 same commands as WhatsApp/Telegram chat."),"",V(e,"Usage:"),` ${S(e,"omnish i [options]")}`,` ${S(e,"omnish interactive [options]")}`,"",V(e,"Options:"),...Ge(e," ",[{left:"--as <sender>",right:"Sender key for cluster commands (wa:+E164 or tg:id). Default: synthetic wa:cli:interactive."},{left:"-c, --command <line>",right:"Run one line and exit (non-interactive)."},{left:"-h, --help",right:"Show this help."}],r=>b(e,r)),"",`${b(e,"Trust:")} ${S(e,"Full local access like your shell; not gated by inbox allowlist.")}`,`${b(e,"Jobs:")} ${S(e,"/bg and /jobs apply only to this REPL session (not the gateway process).")}`,`${b(e,"Files:")} ${S(e,"Use /sendto to push a host file through the gateway; plain /send needs a chat peer.")}`,`${b(e,"Gateway:")} ${S(e,"/reload and /updates require omnish run; /sendto requires omnish run for WA/TG delivery.")}`,"",Vo(),"",`${b(e,"cwd:")} ${S(e,`session starts at ${t} (change with !cd or ${C().commandPrefix}cd).`)}`];console.log(n.join(`
|
|
255
|
+
`))}async function tp(e){let t=C(),n=J(Wt).cwd,r=await Ln(n,e.selectorPart);if(r.length===0)return{error:`No files matched: ${e.selectorPart}`};let o=await Pn(r);if(!o.ok)return{error:o.error};let s=r.map(d=>et(d,t.fileSendMaxBytes));for(let d of s)if("error"in d)return{error:d.error};let a=new Set,i=new Set;if(e.channel==="whatsapp-all"||e.channel==="all"){let d=new Set;for(let p of t.allowFrom){let f=j(String(p));f&&!d.has(f)&&(d.add(f),a.add(f))}}if(e.channel==="telegram-all"||e.channel==="all")for(let d of t.telegramAllowFrom){let p=ie(String(d));if(!p)continue;let f=Number(p);Number.isFinite(f)&&i.add(f)}if(e.channel==="whatsapp")for(let d of e.e164s)a.add(d);if(e.channel==="telegram"&&i.add(e.chatId),a.size===0&&i.size===0)return{error:"No recipients matched the requested /sendto destination."};let l=[];for(let d of a)for(let p of s){if("error"in p)continue;let f=await Yo({op:"sendMedia",channel:"whatsapp",e164:d,absPath:p.absPath,caption:e.caption});f&&l.push(`[wa:${d}] ${p.displayName}: ${f}`)}for(let d of i)for(let p of s){if("error"in p)continue;let f=await Yo({op:"sendMedia",channel:"telegram",chatId:d,absPath:p.absPath,caption:e.caption});f&&l.push(`[tg:${d}] ${p.displayName}: ${f}`)}if(l.length>0)return{error:l.join(`
|
|
256
|
+
`)};let u=a.size+i.size,c=s.length;return{error:null,recipientsSent:u,filesSent:c,messagesSent:u*c}}async function np(e,t,n,r,o,s,a){let i=e.trim();if(!i)return;let l=fl(i);if(l!==null||/^\/sendto(\s|$)/i.test(i)){if(l===null){console.log(I(de.stderr,`Invalid /sendto.
|
|
257
|
+
`+Vo()));return}let{error:d,recipientsSent:p,filesSent:f,messagesSent:h}=await tp(l);if(d)console.log(I(de.stderr,d));else{let g=p!==void 0&&f!==void 0&&h!==void 0?`Sent ${f} file(s) to ${p} recipient(s) (${h} message(s)).`:"Sent.";console.log(ce(de.stdout,g))}return}let u={peerKey:Wt,text:e},c=await yt(C(),t,n,r,o,u,s,void 0,a);c!==null&&await rp(c)}async function rp(e){if(e===null)return;if(e.kind==="file"||e.kind==="files"){console.log(ce(de.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(`
|
|
258
|
+
`)));return}let t=He(e.body,"whatsapp").text;t.trim()&&console.log(t)}async function hl(e){let t=ep(e);if(t.error==="help"){Ko(de.stdout);return}if(t.error&&t.error!==null){console.error(I(de.stderr,t.error)),console.error(b(de.stderr,"Try: omnish i --help")),de.exitCode=1;return}let{senderKey:n,oneShot:r}=t.opts,o=n??Wt;q(),Fn(Wt,de.cwd());let s=new wt,a=new Map,i=new Map,l=new Map,u=new Nt(()=>C(),async(f,h)=>{de.stdout.write(h),h.endsWith(`
|
|
259
|
+
`)||de.stdout.write(`
|
|
260
|
+
`)}),c=async f=>{try{await np(f,s,a,i,l,u,o)}catch(h){console.error(I(de.stderr,String(h)))}};if(r!==null){await c(r),u.dispose(),s.killAllRunning();return}let d=Xd.createInterface({input:de.stdin,output:de.stdout}),p=gl.basename(J(Wt).cwd);d.setPrompt(`${p}> `),d.on("line",f=>{c(f).then(()=>{let h=gl.basename(J(Wt).cwd);d.setPrompt(`${h}> `),d.prompt()})}),d.on("close",()=>{u.dispose(),s.killAllRunning(),de.stdout.write(`
|
|
261
|
+
`)}),d.prompt()}op.setDefaultResultOrder("ipv4first");function bl(){let e=process.stdout,t=[`${oe(e,"omnish run")} ${b(e,"[options]")}`,re(e,"Listen for DMs and run shell commands from allowlisted chats."),"",V(e,"Usage:"),` ${S(e,"omnish run [options]")}`,"",V(e,"Options:"),...Ge(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: ${pe}).`},{left:"-h, --help",right:"Show this help."}],n=>b(e,n)),"",`${S(e,"Config reload:")} ${b(e,"while the gateway runs, edit config then send /reload or /restart from an allowlisted chat (no restart needed for many keys). /updates checks npm (and optional updateInfoUrl).")}`,""];console.log(t.join(`
|
|
262
|
+
`))}function kl(){let e=process.stdout,t=[{left:"omnish link [--force]",right:"WhatsApp: scan QR (Linked devices). --force wipes session first."},{left:"omnish link --tg <bot_token>",right:"Telegram: save token to config; gatewayMode telegram or both if WhatsApp is linked."}],n=t.map(a=>b(e,a.left)),r=Math.max(...n.map(_n)),o=t.map((a,i)=>Ar(" ",r,n[i],S(e,a.right))),s=[`${oe(e,"omnish link")} ${b(e,"[options]")}`,re(e,"Connect WhatsApp (QR) or save a Telegram bot token."),"",V(e,"Usage:"),` ${S(e,"omnish link [--force]")}`,` ${S(e,"omnish link --tg <bot_token>")}`,"",V(e,"Modes:"),...o,` ${re(e,"Do not combine --tg with --force.")}`,"",V(e,"Options:"),...Ge(e," ",[{left:"-f, --force",right:"WhatsApp only: delete saved session before pairing."},{left:"-h, --help",right:"Show this help."}],a=>b(e,a)),"",`${S(e,"Next:")} ${K(e,"omnish allow tg:<your_user_id>")} ${b(e,"then")} ${K(e,"omnish run")}`,`${b(e,"Config:")} ${S(e,A)}`,""];console.log(s.join(`
|
|
263
|
+
`))}function sp(e){let t=!1,n=null;for(let r=0;r<e.length;r++){let o=e[r]??"";if(o==="--help"||o==="-h")return{kind:"help"};if(o==="--force"||o==="-f"){t=!0;continue}if(o==="--tg"||o==="--telegram"){let s=e[r+1];if(!s||s.startsWith("-"))return{kind:"error",message:"[omnish] --tg requires a bot token (from @BotFather)."};n=s,r++;continue}if(o.startsWith("--tg=")||o.startsWith("--telegram=")){let s=o.indexOf("="),a=o.slice(s+1).trim();if(!a)return{kind:"error",message:"[omnish] --tg= requires a non-empty bot token."};n=a;continue}return{kind:"error",message:`[omnish] unknown link argument: ${o}
|
|
264
|
+
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 Zo(){let e=process.stdout,t=`${oe(e,"omnish")} ${b(e,`v${Ue()}`)}`,n=[{left:"link [--force] [--tg <token>]",right:"WhatsApp (QR) or Telegram bot token \u2014 omnish link --help"},{left:"run [options]",right:"Listen for DMs (WhatsApp and/or Telegram \u2014 see gatewayMode in config)"},{left:"stop",right:`Stop background gateway (pidfile: ${ee})`},{left:"service <subcommand>",right:"Boot install hints, logs, systemd/LaunchAgent \u2014 omnish service help"},{left:"logout",right:"Delete saved WhatsApp session"},{left:"allow +<E164> | tg:<id>",right:"Add allowlist entry"},{left:"deny +<E164> | tg:<id>",right:"Remove allowlist entry"},{left:"status [--check-updates]",right:"Channels, identity, allowlists, jobs, security, cluster (if enabled)"},{left:"commands",right:"Chat commands for allowlisted users (same as /help)"},{left:"security [--json]",right:"Configuration security report (JSON for scripts)"},{left:"cluster [status | use <sender> <label-or-id>]",right:"Per-sender machine bindings"},{left:"i | interactive [options]",right:"Local REPL (chat commands; /sendto needs omnish run)"},{left:"ui [options]",right:"Browser setup UI on LAN (token-gated) \u2014 omnish ui --help"}],r=[{left:"-v, --version",right:"Print version and exit."},{left:"-h, --help",right:"Show this help (same as omnish help)."}],o=[t,re(e,"Allowlisted inbox \u2192 your real shell. No AI."),"",V(e,"Usage:"),` ${S(e,"omnish [options] <command> [args...]")}`,"",V(e,"Options:"),...Ge(e," ",r,s=>b(e,s)),"",V(e,"Commands:"),...Ge(e," ",n,s=>b(e,s)),"",`${b(e,"Config:")} ${S(e,`${A} \u2014 gatewayMode: "whatsapp" | "telegram" | "both"`)}`,`${b(e,"Env:")} ${S(e,"OMNISH_VERBOSE=1 (Baileys; legacy WHATSVERBOSE=1 still works), TELEGRAM_BOT_TOKEN (optional)")}`,`${b(e,"Data:")} ${S(e,"~/.omnish by default; ~/.whatslive reused if it already exists. OMNISH_HOME overrides.")}`,`${b(e,"See also:")} ${S(e,"https://omnish.dev")}`,""];console.log(o.join(`
|
|
265
|
+
`))}function ip(e){let t=(e??"").trim().toLowerCase(),n=process.stdout;if(!t){Zo();return}switch(t){case"link":kl();return;case"run":bl();return;case"service":xl();return;case"i":case"interactive":Ko(n);return;case"ui":vl();return;default:console.error(I(process.stderr,`No detailed help for "${e}". Try: omnish help`)),process.exitCode=1}}function ap(e){let t=!1,n="",r=!1;for(let s=0;s<e.length;s++){let a=e[s]??"";if(a==="-d"||a==="--background")t=!0;else if(a==="--log-file"||a==="--log"){let i=e[++s];i||(console.error(I(process.stderr,"--log-file requires a path.")),process.exit(1)),n=i}else if(a==="--help"||a==="-h")r=!0;else{let i=process.stderr;console.error(I(i,`unknown run option: ${a}`)),console.error(I(i,"Try: omnish run --help")),process.exit(1)}}let o=n.trim()!==""?Xo.isAbsolute(n)?n:Xo.resolve(process.cwd(),n):pe;return{background:t,logFile:o,help:r}}function lp(e){let t=yr(e);t.ok||(console.error(I(process.stderr,t.message)),process.exit(1));let n=process.stdout;console.log(`${ce(n,`gateway started in background (pid ${t.pid}).`)} ${b(n,`Log: ${e}`)}`)}function cp(){let e=wr();switch(e.outcome){case"no_pidfile":console.error(I(process.stderr,`no pidfile at ${ee} \u2014 is a background gateway running?`)),process.exitCode=1;return;case"invalid_pidfile":console.error(I(process.stderr,"invalid pidfile.")),process.exitCode=1;return;case"stale_cleaned":console.log(ce(process.stdout,`process ${e.pid} is not running; removing stale pidfile.`));return;case"sent_signal":console.log(ce(process.stdout,`sent SIGTERM to gateway (pid ${e.pid}).`));return;case"taskkill_ok":console.log(ce(process.stdout,`stopped gateway (pid ${e.pid}) using taskkill.`));return;case"failed":console.error(I(process.stderr,e.message)),process.exitCode=1;return}}function yl(){if(process.env.OMNISH_BACKGROUND_GATEWAY==="1")try{ct.readFileSync(ee,"utf8").trim()===String(process.pid)&&ct.unlinkSync(ee)}catch{}}function up(e){return e.length<=8?"(set)":`${e.slice(0,4)}\u2026${e.slice(-4)}`}function dp(){if(!ct.existsSync(ee))return"gateway process: not running (no pid file)";let e=ct.readFileSync(ee,"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 Sl=120;function xl(){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 ${Sl}).`},{left:"install",right:"Write user systemd / LaunchAgent unit (needs serviceInstallFromChat=true)."},{left:"uninstall",right:"Remove that unit (same gate as install)."}],n=[`${oe(e,"omnish service")} ${b(e,"<subcommand>")}`,re(e,"Boot integration, crash restart policy, and logs (same ideas as /service in chat)."),"",V(e,"Usage:"),` ${S(e,"omnish service <subcommand>")}`,"",V(e,"Subcommands:"),...Ge(e," ",t,r=>b(e,r)),"",V(e,"Restart and reload:"),` ${S(e,"Process")} ${b(e,"\u2014 Linux user unit: Restart=on-failure, RestartSec=5. macOS: KeepAlive. Full restart loads config from disk.")}`,` ${S(e,"Config live")} ${b(e,"\u2014 allowlisted chat while gateway runs: /reload or /restart")}`,"",`${b(e,"Docs:")} ${S(e,"docs/guides/background-and-boot.md")} ${re(e,"\xB7")} https://omnish.dev`,""];console.log(n.join(`
|
|
266
|
+
`))}function pp(e){let t=process.stdout,n=process.stderr,r=(e[0]??"help").toLowerCase();if(r==="help"||r==="--help"||r==="-h"){xl();return}if(r==="instructions"){let o=ht();if(o.error){console.error(I(n,o.error)),process.exitCode=1;return}console.log(Xn(o));return}if(r==="status"){let o=ht(),s=(()=>{try{return ct.existsSync(ee)?`gateway.pid: ${ct.readFileSync(ee,"utf8").trim()}`:"gateway.pid: (missing)"}catch(d){return`gateway.pid: (read error: ${String(d)})`}})(),a=process.env.OMNISH_BACKGROUND_GATEWAY==="1"?"This process: background gateway (OMNISH_BACKGROUND_GATEWAY=1).":"This process: CLI (not the gateway \u2014 run omnish service status on the host where the gateway runs for live pid info).",i=typeof process.env.OMNISH_HOME=="string"&&process.env.OMNISH_HOME.trim()?`OMNISH_HOME env: ${process.env.OMNISH_HOME.trim()}`:"OMNISH_HOME env: (not set \u2014 using default data dir)",l=o.error?o.error:`Node: ${o.nodePath}
|
|
267
|
+
Script: ${o.scriptPath}`,c=C().serviceInstallFromChat?"Install from CLI/chat: enabled (omnish service install / /service install).":"Install from CLI/chat: off \u2014 set serviceInstallFromChat true in config (same trust as shell).";console.log(oe(t,"omnish service status")),console.log(""),console.log(`${b(t,"platform:")} ${S(t,process.platform)}`),console.log(`${b(t,"session:")} ${S(t,a)}`),console.log(`${b(t,"env:")} ${S(t,i)}`),console.log(`${b(t,"data dir:")} ${S(t,L)}`),console.log(`${b(t,"pidfile:")} ${S(t,s)}`),console.log(`${b(t,"default log:")} ${S(t,pe)}`),console.log(""),console.log(l),console.log(""),console.log(S(t,c));return}if(r==="logs"){let o=e.length>=2?Number.parseInt(e[1],10):80,s=Number.isFinite(o)&&o>0?Math.min(o,Sl):80,a=nr(pe,s);console.log(`${b(t,"file:")} ${S(t,pe)}`),console.log(`${b(t,"lines:")} ${S(t,String(s))}`),console.log(""),console.log(a);return}if(r==="install"){if(!C().serviceInstallFromChat){console.error(I(n,"Install is disabled. Set serviceInstallFromChat to true in config (same trust as shell), then run again.")),process.exitCode=1;return}let s=Zn();s.ok?console.log(ce(t,s.detail)):(console.error(I(n,s.detail)),process.exitCode=1);return}if(r==="uninstall"){if(!C().serviceInstallFromChat){console.error(I(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=er();s.ok?console.log(ce(t,s.detail)):(console.error(I(n,s.detail)),process.exitCode=1);return}console.error(I(n,`Unknown subcommand "${r}". Try: omnish service help`)),process.exitCode=1}function mp(e){let t=e.trim();if(!t)return null;let n=t.toLowerCase();if(n.startsWith("tg:")||n.startsWith("telegram:")){let s=ie(t);return s?`tg:${s}`:null}let r=n.startsWith("wa:")?t.slice(3):t,o=j(r);return o?`wa:${o}`:null}async function fp(){let e=null,t=kr();t.ok||(console.error(I(process.stderr,t.message)),process.exit(1));let n=C(),r=n.gatewayMode,o=r==="whatsapp"||r==="both",s=r==="telegram"||r==="both",a=ne(n),i=ot(n),l=process.stderr,u=Rs(i,"warn");if(u.length>0&&(console.warn(`${ce(l,`Security (${u.length} finding(s)):`)}
|
|
268
|
+
`),console.warn(Lr(u,l))),process.env.OMNISH_BACKGROUND_GATEWAY==="1")try{ct.writeFileSync(ee,`${process.pid}
|
|
269
|
+
`,{mode:384})}catch(T){E.warn({err:String(T)},"could not write gateway pidfile")}let c=(T,w)=>{let x=C();if(!x.clusterEnabled)return T;let M=ve(),D=(x.clusterLabel??"").trim()||wl.hostname(),H=null;if(w.startsWith("tg:"))H=w;else if(w){let xe=j(w);xe&&(H=`wa:${xe}`)}let te=H?Ve(x,H):null;return Ei(T,{nodeId:M,label:D,role:x.clusterRole,activeNodeId:te?.nodeId??""})},d=new wt,p=new Map,f=new Map,h=new Map,g=null,y={stop:null,sendText:null,sendMedia:null},v=null,k=!1,R=async(T,w)=>{if(T.startsWith("wa:")){let x=T.slice(3);g&&await g.sendText(x,w)}else if(T.startsWith("tg:")){let x=Number(T.slice(3));y.sendText&&Number.isFinite(x)&&await y.sendText(x,m(w))}},_=async(T,w)=>{if(T.startsWith("wa:")){let x=T.slice(3);g&&await g.sendMedia(x,w)}else if(T.startsWith("tg:")){let x=Number(T.slice(3));y.sendMedia&&Number.isFinite(x)&&await y.sendMedia(x,w)}},z=()=>new Nt(()=>C(),R),O=z();v=O;let le,Re={async reload(){try{E.info("gateway reload requested from chat"),await y.stop?.().catch(()=>{}),y.stop=null,y.sendText=null,y.sendMedia=null;let T=C(),w=T.gatewayMode==="telegram"||T.gatewayMode==="both",x=ne(T);if(w&&x){let H=await _o(x,()=>C(),le,{decorate:c});y.sendText=H.sendText,y.sendMedia=H.sendMedia,y.stop=H.stop}let M=["Reload complete.",`gatewayMode: ${T.gatewayMode}`,w&&x?"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(`
|
|
270
|
+
`),D=rr(rn());return{ok:!0,summary:D?`${M}
|
|
259
271
|
|
|
260
|
-
Updates (last check): ${
|
|
261
|
-
${
|
|
262
|
-
`))}async function
|
|
263
|
-
`));return}await
|
|
264
|
-
`)),o){let
|
|
272
|
+
Updates (last check): ${D}`:M}}catch(T){return{ok:!1,error:String(T)}}}};if(le=async(T,w)=>{let x=C(),M=cs(x.telegramAllowFrom),D=T.peerKey.startsWith("tg:")?T.peerKey.slice(3):"";if(!D||!M.has(D)){E.warn({denied:T.peerKey,uid:D},"telegram denied");return}try{let H=T.peerKey;if(!Mo(x,T.text,H))return;if(T.mediaError&&await w({kind:"text",body:m(T.mediaError)}),T.mediaSavedPath&&await w({kind:"text",body:m(`Saved: ${T.mediaSavedPath}`)}),T.text.trim()){let te=await yt(x,d,p,f,h,T,O,Re,H);te!==null&&await w(te)}}catch(H){E.error({err:String(H)},"telegram handler error"),await w({kind:"text",body:m(`Error: ${String(H)}`)}).catch(()=>{})}},s){let T=await _o(a,()=>C(),le,{decorate:c});y.sendText=T.sendText,y.sendMedia=T.sendMedia,y.stop=T.stop}Za({getCfg:()=>C(),getWaOutbound:()=>g,getTgSendMedia:()=>y.sendMedia}),e=sa({getRunningVersion:Ue,getConfig:C,log:E});let Se=Ea({getConfig:C,sendToPeer:R,sendMediaToPeer:_}),Le=!s,We=()=>{k=!0,Se(),e?.(),e=null,yl(),br(),y.stop?.().catch(()=>{}),v?.dispose(),d.killAllRunning(),console.error(`
|
|
273
|
+
${I(process.stderr,"shutting down\u2026")}`),process.exit(0)};if(process.on("SIGINT",We),process.on("SIGTERM",We),o)for(;!k;){let T=!1,w;try{w=await pr({printQr:!1,verbose:sr()}),await kt(mr(w),3e5,"Gateway: timed out waiting for WhatsApp connection (5 min).")}catch(M){console.error(I(process.stderr,`connect failed: ${String(M)}`)),await new Promise(D=>setTimeout(D,5e3));continue}g=qa(w,{decorate:c});let x=ja(w,async M=>{let D=C(),H=ls(D.allowFrom),te=M.fromE164||j(M.fromJid)||"";if(!te||!H.has(te)){E.warn({denied:M.fromJid,phone:te},"denied");return}try{let xe=xa(M),Me=`wa:${te}`;if(!Mo(D,xe.text,Me))return;if(M.mediaError&&await g.sendText(M.fromJid,M.mediaError),M.mediaSavedPath&&await g.sendText(M.fromJid,`Saved: ${M.mediaSavedPath}`),M.text.trim()){let Xe=await yt(D,d,p,f,h,xe,O,Re,Me);if(Xe!==null)if(Xe.kind==="file")await g.sendMedia(M.fromJid,Xe.spec);else if(Xe.kind==="files")for(let U of Xe.specs)await g.sendMedia(M.fromJid,U);else await g.sendText(M.fromJid,He(Xe.body,"whatsapp").text)}}catch(xe){E.error({err:String(xe)},"handler error"),await g.sendText(M.fromJid,He(m(`Error: ${String(xe)}`),"whatsapp").text).catch(()=>{})}});if(await new Promise(M=>{let D=H=>{H.connection==="close"&&(Po(H.lastDisconnect)===bt.loggedOut&&(T=!0),w.ev.off("connection.update",D),M())};w.ev.on("connection.update",D)}),x(),Le&&(O.dispose(),O=z(),v=O),Dt(w),g=null,T&&(console.error(I(process.stderr,"session logged out. Run `omnish link` again.")),Se(),e?.(),e=null,yl(),br(),y.stop?.().catch(()=>{}),process.exit(1)),k)break;await new Promise(M=>setTimeout(M,3e3))}else for(;!k;)await new Promise(T=>setTimeout(T,500));Se(),e?.(),br(),y.stop?.().catch(()=>{}),O.dispose()}function gp(e){let t=!1,n="0.0.0.0",r=3789,o;for(let s=0;s<e.length;s++){let a=e[s];if(a==="--help"||a==="-h"){t=!0;continue}if(a==="--host"||a==="-H"){let l=e[++s];l||(console.error(I(process.stderr,"--host requires an address (e.g. 127.0.0.1).")),process.exit(1)),n=l;continue}if(a==="--port"||a==="-p"){let l=e[++s],u=Number.parseInt(l??"",10);Number.isFinite(u)||(console.error(I(process.stderr,"--port requires a number.")),process.exit(1)),r=u;continue}if(a==="--token"||a==="-t"){let l=e[++s];(!l||l.startsWith("-"))&&(console.error(I(process.stderr,"--token requires a secret string.")),process.exit(1)),o=l;continue}let i=process.stderr;console.error(I(i,`unknown ui argument: ${a}`)),console.error(I(i,"Try: omnish ui --help")),process.exit(1)}return{help:t,host:n,port:r,token:o}}function vl(){let e=process.stdout,t=[`${oe(e,"omnish ui")} ${b(e,"[options]")}`,re(e,"Serve the browser setup panel on your LAN (token-gated). Same trust as editing config on disk."),"",V(e,"Usage:"),` ${S(e,"omnish ui [options]")}`,"",V(e,"Options:"),...Ge(e," ",[{left:"--host <addr>",right:"Bind address (default 0.0.0.0 \u2014 reachable on LAN). Use 127.0.0.1 for loopback only."},{left:"--port <n>",right:"TCP port (default 3789)."},{left:"--token <secret>",right:`Set or rotate setup token (saved to ${Ne}).`},{left:"-h, --help",right:"This help."}],n=>b(e,n)),"",`${b(e,"Warning:")} ${we(e,"Listening on all interfaces exposes a remote control panel on your network \u2014 use a trusted LAN.")}`,""];console.log(t.join(`
|
|
274
|
+
`))}async function hp(){let[,,e,...t]=process.argv;if(e==="--version"||e==="-v"||e==="-V"){let n=process.stdout;console.log(`${oe(n,"omnish")} ${b(n,Ue())}`);return}if(e==="--help"||e==="-h"){Zo();return}if(e==="help"){ip(t[0]);return}switch(q(),e){case"link":{let n=sp(t);if(n.kind==="help"){kl();return}if(n.kind==="error"){let r=process.stderr,o=n.message.replace(/^\[omnish\]\s*/,"");console.error(I(r,o)),process.exitCode=1;return}if(n.kind==="tg"){let r=n.token.trim(),o=process.stderr,s=process.stdout;if(!Ze(r)){console.error(I(o,"That does not look like a Telegram bot token (expect digits:secret from @BotFather).")),process.exitCode=1;return}vt(r);let a=Ie()?"both":"telegram";In(a),console.log([`${K(s,"Telegram")} ${S(s,"bot token saved to")} ${b(s,A)}`,`${b(s,"gatewayMode:")} ${K(s,a)}`,"",S(s,"Next:"),` ${b(s,"1.")} ${S(s,"Find your numeric user id (e.g. t.me/userinfobot), then:")} ${K(s,"omnish allow tg:<id>")}`,` ${b(s,"2.")} ${K(s,"omnish run")}`,""].join(`
|
|
275
|
+
`));return}await Xa({verbose:sr(),force:n.force});return}case"run":{let n=ap(t);if(n.help){bl();return}if(n.background){lp(n.logFile);return}await fp();return}case"stop":cp();return;case"logout":{try{ct.rmSync(W,{recursive:!0,force:!0}),console.log(ce(process.stdout,"Session removed. Run `omnish link` to pair again."))}catch(n){console.error(I(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(I(o,"Usage: omnish allow +<E164> or omnish allow tg:<user_id>")),process.exitCode=1;return}try{let s=Rn(n);console.log(`${b(r,"allowFrom:")} ${S(r,s.allowFrom.join(", ")||"(empty)")}`),console.log(`${b(r,"telegramAllowFrom:")} ${S(r,s.telegramAllowFrom.join(", ")||"(empty)")}`)}catch(s){console.error(I(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(I(o,"Usage: omnish deny +<E164> or omnish deny tg:<user_id>")),process.exitCode=1;return}try{let s=Mn(n);console.log(`${b(r,"allowFrom:")} ${S(r,s.allowFrom.join(", ")||"(empty)")}`),console.log(`${b(r,"telegramAllowFrom:")} ${S(r,s.telegramAllowFrom.join(", ")||"(empty)")}`)}catch(s){console.error(I(o,String(s).replace(/^\[omnish\]\s*/,""))),process.exitCode=1}return}case"i":case"interactive":{await hl(t);return}case"status":{let n=process.stdout,r=C(),o=t.includes("--check-updates"),s=new wt().list(),a=s.filter(h=>h.status==="running").length,i=ne(r),l=ot(r),u=r.gatewayMode==="whatsapp"||r.gatewayMode==="both",c=r.gatewayMode==="telegram"||r.gatewayMode==="both",d=Ie(),p=d?Ya(W):null,f=[];if(f.push(`${oe(n,"omnish")} ${b(n,Ue())}`,`${b(n,"gatewayMode:")} ${S(n,r.gatewayMode)}`,`${b(n,"data dir:")} ${S(n,Xo.dirname(W))}`,"",`${b(n,"gateway process:")} ${S(n,dp().replace(/^gateway process: /,""))}`,"",dt(n),K(n,"whatsapp"),` ${b(n,"in use:")} ${u?S(n,"yes"):we(n,"no (gatewayMode is telegram-only)")}`),u){let h=d?S(n,`linked (${W})`):we(n,"missing \u2014 run omnish link");f.push(` ${b(n,"session:")} ${h}`),d&&p&&f.push(` ${b(n,"linked as:")} ${S(n,p)}`),d&&!p&&f.push(` ${b(n,"linked as:")} ${re(n,"(not in creds yet \u2014 try again after omnish link completes)")}`)}if(f.push(` ${V(n,"Allowed")}`),r.allowFrom.length===0)f.push(` ${re(n,"(none)")}`);else for(let h of r.allowFrom)f.push(` ${b(n,"whatsapp:")} ${S(n,h)}`);if(f.push("",dt(n),K(n,"telegram")),f.push(` ${b(n,"in use:")} ${c?S(n,"yes"):we(n,"no (gatewayMode is whatsapp-only)")}`),c){let h=i?S(n,up(i)):we(n,"(none) \u2014 omnish link --tg <token> or TELEGRAM_BOT_TOKEN");f.push(` ${b(n,"bot token:")} ${h}`)}if(f.push(` ${V(n,"Allowed")}`),r.telegramAllowFrom.length===0)f.push(` ${re(n,"(none)")}`);else for(let h of r.telegramAllowFrom)f.push(` ${b(n,"telegram:")} ${S(n,h)}`);if(f.push("",dt(n),`${K(n,"jobs")} ${b(n,`(recent): ${s.length} total, ${a} running`)}`,Is(l,n)),console.log(f.join(`
|
|
276
|
+
`)),o){let h=await on(Ue(),r),g=qt(h);console.log(""),console.log(dt(n)),console.log(He(g,"whatsapp").text)}if(r.clusterEnabled){let h=ve(),g=X(),y=Object.keys(g.senderBindings).length,v=Object.keys(r.clusterSenderBindings??{}).length;console.log(""),console.log(dt(n)),console.log(K(n,"cluster")),console.log(` ${b(n,"\xB7")} ${S(n,`enabled \xB7 label ${r.clusterLabel||wl.hostname()} \xB7 bindings ${y} chat / ${v} default`)}`),console.log(` ${b(n,"node:")} ${S(n,`${h.slice(0,8)}\u2026`)}`)}return}case"commands":{let n=process.stdout,r=C();console.log(vs(Rt(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(b(n,At.join(", "))),console.log(`${b(n,"See also:")} ${S(n,A)}`);return}case"cluster":{let n=process.stdout,r=process.stderr,o=(t[0]??"status").toLowerCase();if(o==="status"){let s=C(),a=ve();if(console.log(`${b(n,"clusterEnabled:")} ${S(n,String(s.clusterEnabled))}`),console.log(`${b(n,"clusterLabel:")} ${S(n,s.clusterLabel||"(hostname)")}`),console.log(`${b(n,"clusterRole:")} ${S(n,`${s.clusterRole} (informational; no longer used to gate traffic)`)}`),console.log(`${b(n,"node id:")} ${K(n,a)}`),s.clusterEnabled){let i=X(),l=yo(s,null);console.log(""),console.log(S(n,l.wa.replace(/\*([^*]+)\*/g,"$1").replace(/`([^`]+)`/g,"$1"))),console.log(""),console.log(ho(i,s,null));let u=Object.keys(i.senderBindings).length,c=Object.entries(s.clusterSenderBindings??{});if(u>0){console.log(""),console.log(K(n,"Chat bindings (cluster-local.json)"));for(let[d,p]of Object.entries(i.senderBindings))console.log(` ${b(n,d)} ${re(n,"->")} ${S(n,`${p.nodeId} (${p.source}, since ${p.sinceIso})`)}`)}if(c.length>0){console.log(""),console.log(K(n,"Config defaults (clusterSenderBindings)"));for(let[d,p]of c)console.log(` ${b(n,d)} ${re(n,"->")} ${S(n,p)}`)}}else console.log(""),console.log(we(n,"(cluster disabled \u2014 /config set clusterEnabled true to enable, then /c use <label-or-id> from each sender)"));return}if(o==="use"||o==="bind"){let s=t[1],a=t.slice(2).join(" ").trim();if(!s||!a){console.error(I(r,"Usage: omnish cluster use <senderE164|tg:id> <label-or-id>")),process.exitCode=1;return}let i=mp(s);if(!i){console.error(I(r,`Could not parse sender "${s}". Use +E164 (WhatsApp) or tg:<user_id> (Telegram).`)),process.exitCode=1;return}let{state:l,resolved:u}=Gi(i,a);if(!u.ok){if(u.reason==="ambiguous-label"){let d=(u.matches??[]).map(p=>`${p.nodeId}(${p.label})`).join(", ");console.error(I(r,`Label "${a}" matches multiple machines: ${d}. Use the 8-character id.`))}else console.error(I(r,`No machine matches "${a}". Run /c status from the chat first to populate the roster, or pass an 8-character node id.`));process.exitCode=1;return}console.log(`${K(n,"cluster:")} ${S(n,`${i} -> ${u.peer.nodeId} (${u.peer.label}).`)}`);let c=C();console.log(""),console.log(ho(l,c,i));return}if(o==="here"){console.error(I(r,"omnish cluster here is no longer available. Use: omnish cluster use <senderE164|tg:id> <label-or-id>")),console.error(I(r,"Or send /c here from the controller's chat on the machine you want to bind.")),process.exitCode=1;return}console.error(I(r,"Usage: omnish cluster [status | use <sender> <label-or-id>]")),process.exitCode=1;return}case"security":{let n=C(),r=ot(n),o=t.includes("--json");console.log(o?Ms(r):Lr(r,process.stdout)),jn(r)&&(process.exitCode=1);return}case"service":{pp(t);return}case"ui":{let n=gp(t);if(n.help){vl();return}if(!Number.isFinite(n.port)||n.port<1||n.port>65535){console.error(I(process.stderr,"port must be between 1 and 65535.")),process.exitCode=1;return}let r=ol(n.token);await pl({host:n.host,port:n.port,meta:r});let o=process.stdout,s=ml(n.port);console.log(""),console.log(`${oe(o,"ui")} ${b(o,"listening")}`),console.log(`${b(o,"bind:")} ${S(o,`${n.host}:${n.port}`)}`),console.log(`${b(o,"setup token:")} ${S(o,r.token)}`),console.log(`${b(o,"token file:")} ${re(o,Ne)}`),console.log(""),console.log(we(o,"Anyone on your network who can reach this port needs the token \u2014 do not expose to untrusted Wi\u2011Fi.")),console.log(""),console.log(`${b(o,"Open:")}`),console.log(` ${S(o,`http://127.0.0.1:${n.port}/`)}`);for(let a of s)console.log(` ${S(o,a)}`);console.log(""),console.log(`${b(o,"Quick link (same Wi\u2011Fi):")} ${S(o,`http://127.0.0.1:${n.port}/?token=${encodeURIComponent(r.token)}`)}`),console.log("");return}default:Zo(),e&&(process.exitCode=1)}}hp().catch(e=>{console.error(I(process.stderr,String(e))),process.exit(1)});
|