omnish 1.2.4 → 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +83 -0
- package/README.md +13 -2
- package/config.example.json +15 -4
- package/dist/index.js +290 -195
- package/package.json +26 -12
package/dist/index.js
CHANGED
|
@@ -1,115 +1,130 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import Fp from"node:dns";import ct from"node:fs";import ns from"node:path";import Al from"node:os";import En from"node:fs";import Bl from"node:crypto";import Mr from"node:fs";import Hl from"node:os";import K from"node:path";function Dl(){let e=process.env.OMNISH_HOME?.trim();if(e)return K.resolve(e);let t=Hl.homedir(),n=K.join(t,".omnish"),r=K.join(t,".whatslive");try{if(Mr.existsSync(n))return n;if(Mr.existsSync(r))return r}catch{}return n}var N=Dl(),J=K.join(N,"auth"),Ae=K.join(N,"jobs"),os=K.join(N,"apps"),ss=K.join(N,"logs"),fe=K.join(ss,"gateway.log"),ne=K.join(N,"gateway.pid"),xt=K.join(N,"gateway-control.json"),zt=K.join(N,"config-ui.json"),_e=K.join(N,"ui.json"),$n=K.join(N,"ui-server.json"),P=K.join(N,"config.json"),Cn=K.join(N,"shortcuts.json"),Rn=K.join(N,"recipes.json"),Mn=K.join(N,"recipes-user.json"),Be=K.join(N,"cowork"),Tr=K.join(Be,"tasks.json"),Ir=K.join(Be,"pending-runs.json"),is=K.join(Be,"completions.sqlite");function qt(e){let t=Bl.createHash("sha1").update(e,"utf8").digest("hex").slice(0,8);return K.join(os,t)}function H(e){Mr.mkdirSync(e,{recursive:!0,mode:448})}function z(){H(N),H(J),H(Ae),H(os),H(ss),H(Be)}var ls=/^(\d+)(?::\d+)?@s\.whatsapp\.net$/i,cs=/^(\d+)@c\.us$/i,us=/^(\d+)@lid$/i;function ds(e){let t=e.trim();for(;;){let n=t;if(t=t.replace(/^whatsapp:/i,"").trim(),t===n)return t}}function jl(e){let t=ds(e);if(!t.toLowerCase().endsWith("@g.us"))return!1;let r=t.slice(0,t.length-5);return!r||r.includes("@")?!1:/^[0-9]+(-[0-9]+)*$/.test(r)}function Ul(e){let t=e.match(ls);if(t)return t[1]??null;let n=e.match(cs);if(n)return n[1]??null;let r=e.match(us);return r?r[1]??null:null}function as(e){let t=e.replace(/\D/g,"");return t?`+${t}`:""}function Qt(e){return`${e.replace(/\D/g,"")}@s.whatsapp.net`}function G(e){let t=ds(e);if(!t||jl(t))return null;if(ls.test(t)||cs.test(t)||us.test(t)){let r=Ul(t);if(!r)return null;let o=as(r);return o.length>1?o:null}if(t.includes("@"))return null;let n=as(t);return n.length>1?n:null}function Tn(e){return e.map(t=>String(t).trim()).filter(t=>!!t).map(t=>t==="*"?t:G(t)).filter(t=>!!t)}function ps(e){let t=new Set;for(let n of e)n!=="*"&&t.add(n);return t}function ue(e){let t=e.trim();for(;;){let n=t.toLowerCase();if(n.startsWith("tg:")){t=t.slice(3).trimStart();continue}if(n.startsWith("telegram:")){t=t.slice(9).trimStart();continue}break}return/^\d+$/.test(t)?t:null}function ms(e){let t=new Set;for(let n of e){let r=ue(String(n));r&&t.add(r)}return t}function Er(e){let t=e.trim();if(!t)return null;let n=t.toLowerCase();if(n.startsWith("tg:")||n.startsWith("telegram:")){let o=ue(t);return o?{kind:"tg",id:o}:null}let r=G(t);return r?{kind:"wa",normalized:r}:null}var F={gatewayMode:"whatsapp",telegramBotToken:"",telegramAllowFrom:[],allowFrom:[],commandPrefix:"!",syncTimeoutMs:3e4,syncMaxBytes:32768,jobLogTailLines:80,shell:"/bin/bash",appsCols:120,appsRows:40,appsFlushMs:300,appsMinIntervalMs:800,appsMaxFlushBytes:8192,appsMaxSessions:5,appsMaxSessionsTotal:20,appsMaxWaChars:3500,appsLogTailLines:80,appsSubmitDelayMs:50,appsClearInput:!0,appsClearInputDelayMs:20,appsClearInputSequence:"^A,^K",fileSendMaxBytes:0,fileReceiveMaxBytes:0,fileInboxSubdir:"inbox",fileReceiveRootMode:"downloads",fileReceiveRootPath:"",recipesAllowDangerousBuiltins:!1,recipesMaxTaskChars:0,recipesMacroDefaultCommand:'claude -p "$OMNISH_TASK"',clusterEnabled:!1,clusterLabel:"",clusterRole:"secondary",clusterSenderBindings:{},serviceInstallFromChat:!1,updateCheckEnabled:!1,updateCheckIntervalMs:864e5,updateCheckPackageName:"omnish",updateInfoUrl:"",chatLlmFallbackEnabled:!1,chatLlmShellCommand:"",chatLlmTimeoutMs:12e4,chatLlmMaxInputChars:16e3,chatLlmMaxOutputChars:24e3,chatLlmNeedsTty:!1,chatLlmWorkDir:""};function Wl(e){return e==="downloads"||e==="omnishData"||e==="sessionCwd"||e==="processCwd"||e==="fixed"?e:F.fileReceiveRootMode}function Gl(e){return e==="primary"?"primary":"secondary"}function Jl(e){if(!e||typeof e!="object")return{};let t={};for(let[n,r]of Object.entries(e)){if(typeof r!="string")continue;let o=r.trim();if(!o)continue;let s=n.trim(),i=s.toLowerCase();if(i.startsWith("wa:")){let l=G(s.slice(3));l&&(t[`wa:${l}`]=o.slice(0,64));continue}if(i.startsWith("tg:")||i.startsWith("telegram:")){let l=ue(s);l&&(t[`tg:${l}`]=o.slice(0,64));continue}let a=G(s);a&&(t[`wa:${a}`]=o.slice(0,64))}return t}function In(e){let t=typeof e.appsFlushMs=="number"&&e.appsFlushMs>=0?e.appsFlushMs:F.appsFlushMs,n=e.gatewayMode==="telegram"||e.gatewayMode==="both"||e.gatewayMode==="whatsapp"?e.gatewayMode:F.gatewayMode,r=typeof e.telegramBotToken=="string"?e.telegramBotToken:F.telegramBotToken,o=Array.isArray(e.telegramAllowFrom)?[...new Set(e.telegramAllowFrom.map(s=>ue(String(s))).filter(s=>!!s))].sort():F.telegramAllowFrom;return{...F,...e,gatewayMode:n,telegramBotToken:r,telegramAllowFrom:o,allowFrom:Array.isArray(e.allowFrom)?Tn(e.allowFrom.map(String)).filter(s=>s!=="*"):F.allowFrom,commandPrefix:(()=>{let s=typeof e.commandPrefix=="string"&&e.commandPrefix.length>0?e.commandPrefix:F.commandPrefix;return s==="! "?"!":s})(),syncTimeoutMs:typeof e.syncTimeoutMs=="number"&&e.syncTimeoutMs>0?e.syncTimeoutMs:F.syncTimeoutMs,syncMaxBytes:typeof e.syncMaxBytes=="number"&&e.syncMaxBytes>0?e.syncMaxBytes:F.syncMaxBytes,jobLogTailLines:typeof e.jobLogTailLines=="number"&&e.jobLogTailLines>0?e.jobLogTailLines:F.jobLogTailLines,shell:typeof e.shell=="string"&&e.shell.length>0?e.shell:F.shell,appsCols:typeof e.appsCols=="number"&&e.appsCols>0&&e.appsCols<=500?Math.floor(e.appsCols):F.appsCols,appsRows:typeof e.appsRows=="number"&&e.appsRows>0&&e.appsRows<=200?Math.floor(e.appsRows):F.appsRows,appsFlushMs:t,appsMinIntervalMs:typeof e.appsMinIntervalMs=="number"&&e.appsMinIntervalMs>=0?e.appsMinIntervalMs:F.appsMinIntervalMs,appsMaxFlushBytes:typeof e.appsMaxFlushBytes=="number"&&e.appsMaxFlushBytes>256?Math.floor(e.appsMaxFlushBytes):F.appsMaxFlushBytes,appsMaxSessions:typeof e.appsMaxSessions=="number"&&e.appsMaxSessions>0?Math.min(50,Math.floor(e.appsMaxSessions)):F.appsMaxSessions,appsMaxSessionsTotal:typeof e.appsMaxSessionsTotal=="number"&&e.appsMaxSessionsTotal>0?Math.min(200,Math.floor(e.appsMaxSessionsTotal)):F.appsMaxSessionsTotal,appsMaxWaChars:typeof e.appsMaxWaChars=="number"&&e.appsMaxWaChars>256?Math.floor(e.appsMaxWaChars):F.appsMaxWaChars,appsLogTailLines:typeof e.appsLogTailLines=="number"&&e.appsLogTailLines>0?Math.min(500,Math.floor(e.appsLogTailLines)):F.appsLogTailLines,appsSubmitDelayMs:typeof e.appsSubmitDelayMs=="number"&&e.appsSubmitDelayMs>=0?Math.min(500,Math.floor(e.appsSubmitDelayMs)):F.appsSubmitDelayMs,appsClearInput:typeof e.appsClearInput=="boolean"?e.appsClearInput:F.appsClearInput,appsClearInputDelayMs:typeof e.appsClearInputDelayMs=="number"&&e.appsClearInputDelayMs>=0?Math.min(200,Math.floor(e.appsClearInputDelayMs)):F.appsClearInputDelayMs,appsClearInputSequence:typeof e.appsClearInputSequence=="string"&&e.appsClearInputSequence.length>0?e.appsClearInputSequence.slice(0,200):F.appsClearInputSequence,fileSendMaxBytes:typeof e.fileSendMaxBytes=="number"&&e.fileSendMaxBytes>=0?e.fileSendMaxBytes===0?0:Math.min(2e9,Math.floor(e.fileSendMaxBytes)):F.fileSendMaxBytes,fileReceiveMaxBytes:typeof e.fileReceiveMaxBytes=="number"&&e.fileReceiveMaxBytes>=0?e.fileReceiveMaxBytes===0?0:Math.min(2e9,Math.floor(e.fileReceiveMaxBytes)):F.fileReceiveMaxBytes,fileInboxSubdir:typeof e.fileInboxSubdir=="string"&&e.fileInboxSubdir.length>0&&e.fileInboxSubdir.replace(/[/\\]/g,"").slice(0,80)||F.fileInboxSubdir,fileReceiveRootMode:Wl(e.fileReceiveRootMode),fileReceiveRootPath:typeof e.fileReceiveRootPath=="string"?e.fileReceiveRootPath.trim().slice(0,4096):F.fileReceiveRootPath,recipesAllowDangerousBuiltins:typeof e.recipesAllowDangerousBuiltins=="boolean"?e.recipesAllowDangerousBuiltins:F.recipesAllowDangerousBuiltins,recipesMaxTaskChars:typeof e.recipesMaxTaskChars=="number"&&e.recipesMaxTaskChars>=0?e.recipesMaxTaskChars===0?0:Math.min(Number.MAX_SAFE_INTEGER,Math.floor(e.recipesMaxTaskChars)):F.recipesMaxTaskChars,recipesMacroDefaultCommand:typeof e.recipesMacroDefaultCommand=="string"&&e.recipesMacroDefaultCommand.trim().length>0?e.recipesMacroDefaultCommand.trim().slice(0,4096):F.recipesMacroDefaultCommand,clusterEnabled:typeof e.clusterEnabled=="boolean"?e.clusterEnabled:F.clusterEnabled,clusterLabel:typeof e.clusterLabel=="string"?e.clusterLabel.trim().slice(0,128):F.clusterLabel,clusterRole:Gl(e.clusterRole),clusterSenderBindings:Jl(e.clusterSenderBindings),serviceInstallFromChat:typeof e.serviceInstallFromChat=="boolean"?e.serviceInstallFromChat:F.serviceInstallFromChat,updateCheckEnabled:typeof e.updateCheckEnabled=="boolean"?e.updateCheckEnabled:F.updateCheckEnabled,updateCheckIntervalMs:typeof e.updateCheckIntervalMs=="number"&&e.updateCheckIntervalMs>0?Math.min(6048e5,Math.max(36e5,Math.floor(e.updateCheckIntervalMs))):F.updateCheckIntervalMs,updateCheckPackageName:typeof e.updateCheckPackageName=="string"&&e.updateCheckPackageName.trim().length>0?e.updateCheckPackageName.trim().slice(0,214):F.updateCheckPackageName,updateInfoUrl:typeof e.updateInfoUrl=="string"?e.updateInfoUrl.trim().slice(0,2048):F.updateInfoUrl,chatLlmFallbackEnabled:typeof e.chatLlmFallbackEnabled=="boolean"?e.chatLlmFallbackEnabled:F.chatLlmFallbackEnabled,chatLlmShellCommand:typeof e.chatLlmShellCommand=="string"?e.chatLlmShellCommand.trim().slice(0,8192):F.chatLlmShellCommand,chatLlmTimeoutMs:typeof e.chatLlmTimeoutMs=="number"&&e.chatLlmTimeoutMs>0?Math.min(9e5,Math.floor(e.chatLlmTimeoutMs)):F.chatLlmTimeoutMs,chatLlmMaxInputChars:typeof e.chatLlmMaxInputChars=="number"&&e.chatLlmMaxInputChars>0?Math.min(5e5,Math.floor(e.chatLlmMaxInputChars)):F.chatLlmMaxInputChars,chatLlmMaxOutputChars:typeof e.chatLlmMaxOutputChars=="number"&&e.chatLlmMaxOutputChars>0?Math.min(2e6,Math.floor(e.chatLlmMaxOutputChars)):F.chatLlmMaxOutputChars,chatLlmNeedsTty:typeof e.chatLlmNeedsTty=="boolean"?e.chatLlmNeedsTty:F.chatLlmNeedsTty,chatLlmWorkDir:typeof e.chatLlmWorkDir=="string"?e.chatLlmWorkDir.trim().slice(0,4096):F.chatLlmWorkDir}}function R(){if(z(),!En.existsSync(P)){let e=In({});return He(e),e}try{let e=En.readFileSync(P,"utf8"),t=JSON.parse(e);return In(t)}catch{return In({})}}function He(e){z();let t=In(e);En.writeFileSync(P,JSON.stringify(t,null,2)+`
|
|
3
|
-
`,{mode:384})}function
|
|
4
|
-
`,{mode:384})
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
2
|
+
var xm=Object.defineProperty;var Zn=(e,t)=>()=>(e&&(t=e(e=0)),t);var $m=(e,t)=>{for(var n in t)xm(e,n,{get:t[n],enumerable:!0})};import Rm from"node:crypto";import _s from"node:fs";import Cm from"node:os";import re from"node:path";function Mm(){let e=process.env.OMNISH_HOME?.trim();if(e)return re.resolve(e);let t=Cm.homedir(),n=re.join(t,".omnish"),r=re.join(t,".whatslive");try{if(_s.existsSync(n))return n;if(_s.existsSync(r))return r}catch{}return n}function nr(e){let t=Rm.createHash("sha1").update(e,"utf8").digest("hex").slice(0,8);return re.join(qa,t)}function j(e){_s.mkdirSync(e,{recursive:!0,mode:448})}function te(){j(H),j(ne),j(tt),j(qa),j(Ya),j(ut),j(bt)}var H,ne,tt,qa,Ya,Ne,le,hn,er,ct,_r,Fr,_,Wr,Ur,Dr,ut,Fs,Ws,Qa,bt,tr,Hr,G=Zn(()=>{"use strict";H=Mm(),ne=re.join(H,"auth"),tt=re.join(H,"jobs"),qa=re.join(H,"apps"),Ya=re.join(H,"logs"),Ne=re.join(Ya,"gateway.log"),le=re.join(H,"gateway.pid"),hn=re.join(H,"gateway-control.json"),er=re.join(H,"config-ui.json"),ct=re.join(H,"ui.json"),_r=re.join(H,"tunnel-auth.json"),Fr=re.join(H,"ui-server.json"),_=re.join(H,"config.json"),Wr=re.join(H,"shortcuts.json"),Ur=re.join(H,"recipes.json"),Dr=re.join(H,"recipes-user.json"),ut=re.join(H,"cowork"),Fs=re.join(ut,"tasks.json"),Ws=re.join(ut,"pending-runs.json"),Qa=re.join(ut,"completions.sqlite"),bt=re.join(H,"watch"),tr=re.join(bt,"rules.json"),Hr=re.join(bt,"events.sqlite")});function tl(e){let t=e.trim();for(;;){let n=t;if(t=t.replace(/^whatsapp:/i,"").trim(),t===n)return t}}function Tm(e){let t=tl(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 Em(e){let t=e.match(Xa);if(t)return t[1]??null;let n=e.match(Za);if(n)return n[1]??null;let r=e.match(el);return r?r[1]??null:null}function Va(e){let t=e.replace(/\D/g,"");return t?`+${t}`:""}function It(e){return`${e.replace(/\D/g,"")}@s.whatsapp.net`}function ee(e){let t=tl(e);if(!t||Tm(t))return null;if(Xa.test(t)||Za.test(t)||el.test(t)){let r=Em(t);if(!r)return null;let o=Va(r);return o.length>1?o:null}if(t.includes("@"))return null;let n=Va(t);return n.length>1?n:null}function Br(e){return e.map(t=>String(t).trim()).filter(t=>!!t).map(t=>t==="*"?t:ee(t)).filter(t=>!!t)}function jr(e){let t=new Set;for(let n of e){if(n==="*")continue;let r=ee(String(n));r&&t.add(r)}return t}function Ee(e){let t=e.trim();for(;;){let n=t.toLowerCase();if(n.startsWith("tg:")){t=t.slice(3).trimStart();continue}if(n.startsWith("telegram:")){t=t.slice(9).trimStart();continue}break}return/^\d+$/.test(t)?t:null}function Gr(e){let t=new Set;for(let n of e){let r=Ee(String(n));r&&t.add(r)}return t}function Us(e){let t=e.trim();if(!t)return null;let n=t.toLowerCase();if(n.startsWith("tg:")||n.startsWith("telegram:")){let o=Ee(t);return o?{kind:"tg",id:o}:null}let r=ee(t);return r?{kind:"wa",normalized:r}:null}function nl(e,t){let n=ee(t);return n?e.has(n):!1}var Xa,Za,el,qe=Zn(()=>{"use strict";Xa=/^(\d+)(?::\d+)?@s\.whatsapp\.net$/i,Za=/^(\d+)@c\.us$/i,el=/^(\d+)@lid$/i});import Jr from"node:fs";function Pm(e){return e==="downloads"||e==="omnishData"||e==="sessionCwd"||e==="processCwd"||e==="fixed"?e:N.fileReceiveRootMode}function Am(e){return e==="primary"?"primary":"secondary"}function Im(e){if(!e||typeof e!="object")return{};let t={};for(let[n,r]of Object.entries(e)){if(typeof r!="string")continue;let o=r.trim();if(!o)continue;let s=n.trim(),i=s.toLowerCase();if(i.startsWith("wa:")){let l=ee(s.slice(3));l&&(t[`wa:${l}`]=o.slice(0,64));continue}if(i.startsWith("tg:")||i.startsWith("telegram:")){let l=Ee(s);l&&(t[`tg:${l}`]=o.slice(0,64));continue}let a=ee(s);a&&(t[`wa:${a}`]=o.slice(0,64))}return t}function rr(e){let t=typeof e.appsFlushMs=="number"&&e.appsFlushMs>=0?e.appsFlushMs:N.appsFlushMs,n=e.gatewayMode==="telegram"||e.gatewayMode==="both"||e.gatewayMode==="whatsapp"?e.gatewayMode:N.gatewayMode,r=typeof e.telegramBotToken=="string"?e.telegramBotToken:N.telegramBotToken,o=Array.isArray(e.telegramAllowFrom)?[...new Set(e.telegramAllowFrom.map(s=>Ee(String(s))).filter(s=>!!s))].sort():N.telegramAllowFrom;return{...N,...e,gatewayMode:n,telegramBotToken:r,telegramAllowFrom:o,allowFrom:Array.isArray(e.allowFrom)?Br(e.allowFrom.map(String)).filter(s=>s!=="*"):N.allowFrom,commandPrefix:(()=>{let s=typeof e.commandPrefix=="string"&&e.commandPrefix.length>0?e.commandPrefix:N.commandPrefix;return s==="! "?"!":s})(),syncTimeoutMs:typeof e.syncTimeoutMs=="number"&&e.syncTimeoutMs>0?e.syncTimeoutMs:N.syncTimeoutMs,syncMaxBytes:typeof e.syncMaxBytes=="number"&&e.syncMaxBytes>0?e.syncMaxBytes:N.syncMaxBytes,jobLogTailLines:typeof e.jobLogTailLines=="number"&&e.jobLogTailLines>0?e.jobLogTailLines:N.jobLogTailLines,shell:typeof e.shell=="string"&&e.shell.length>0?e.shell:N.shell,appsCols:typeof e.appsCols=="number"&&e.appsCols>0&&e.appsCols<=500?Math.floor(e.appsCols):N.appsCols,appsRows:typeof e.appsRows=="number"&&e.appsRows>0&&e.appsRows<=200?Math.floor(e.appsRows):N.appsRows,appsFlushMs:t,appsMinIntervalMs:typeof e.appsMinIntervalMs=="number"&&e.appsMinIntervalMs>=0?e.appsMinIntervalMs:N.appsMinIntervalMs,appsMaxFlushBytes:typeof e.appsMaxFlushBytes=="number"&&e.appsMaxFlushBytes>256?Math.floor(e.appsMaxFlushBytes):N.appsMaxFlushBytes,appsMaxSessions:typeof e.appsMaxSessions=="number"&&e.appsMaxSessions>0?Math.min(50,Math.floor(e.appsMaxSessions)):N.appsMaxSessions,appsMaxSessionsTotal:typeof e.appsMaxSessionsTotal=="number"&&e.appsMaxSessionsTotal>0?Math.min(200,Math.floor(e.appsMaxSessionsTotal)):N.appsMaxSessionsTotal,appsMaxWaChars:typeof e.appsMaxWaChars=="number"&&e.appsMaxWaChars>256?Math.floor(e.appsMaxWaChars):N.appsMaxWaChars,appsLogTailLines:typeof e.appsLogTailLines=="number"&&e.appsLogTailLines>0?Math.min(500,Math.floor(e.appsLogTailLines)):N.appsLogTailLines,appsSubmitDelayMs:typeof e.appsSubmitDelayMs=="number"&&e.appsSubmitDelayMs>=0?Math.min(500,Math.floor(e.appsSubmitDelayMs)):N.appsSubmitDelayMs,appsClearInput:typeof e.appsClearInput=="boolean"?e.appsClearInput:N.appsClearInput,appsClearInputDelayMs:typeof e.appsClearInputDelayMs=="number"&&e.appsClearInputDelayMs>=0?Math.min(200,Math.floor(e.appsClearInputDelayMs)):N.appsClearInputDelayMs,appsClearInputSequence:typeof e.appsClearInputSequence=="string"&&e.appsClearInputSequence.length>0?e.appsClearInputSequence.slice(0,200):N.appsClearInputSequence,appsSkipClearOnPasswordPrompt:typeof e.appsSkipClearOnPasswordPrompt=="boolean"?e.appsSkipClearOnPasswordPrompt:N.appsSkipClearOnPasswordPrompt,appsPasswordPromptHint:typeof e.appsPasswordPromptHint=="boolean"?e.appsPasswordPromptHint:N.appsPasswordPromptHint,fileSendMaxBytes:typeof e.fileSendMaxBytes=="number"&&e.fileSendMaxBytes>=0?e.fileSendMaxBytes===0?0:Math.min(2e9,Math.floor(e.fileSendMaxBytes)):N.fileSendMaxBytes,fileReceiveMaxBytes:typeof e.fileReceiveMaxBytes=="number"&&e.fileReceiveMaxBytes>=0?e.fileReceiveMaxBytes===0?0:Math.min(2e9,Math.floor(e.fileReceiveMaxBytes)):N.fileReceiveMaxBytes,fileInboxSubdir:typeof e.fileInboxSubdir=="string"&&e.fileInboxSubdir.length>0&&e.fileInboxSubdir.replace(/[/\\]/g,"").slice(0,80)||N.fileInboxSubdir,fileReceiveRootMode:Pm(e.fileReceiveRootMode),fileReceiveRootPath:typeof e.fileReceiveRootPath=="string"?e.fileReceiveRootPath.trim().slice(0,4096):N.fileReceiveRootPath,recipesAllowDangerousBuiltins:typeof e.recipesAllowDangerousBuiltins=="boolean"?e.recipesAllowDangerousBuiltins:N.recipesAllowDangerousBuiltins,recipesMaxTaskChars:typeof e.recipesMaxTaskChars=="number"&&e.recipesMaxTaskChars>=0?e.recipesMaxTaskChars===0?0:Math.min(Number.MAX_SAFE_INTEGER,Math.floor(e.recipesMaxTaskChars)):N.recipesMaxTaskChars,recipesMacroDefaultCommand:typeof e.recipesMacroDefaultCommand=="string"&&e.recipesMacroDefaultCommand.trim().length>0?e.recipesMacroDefaultCommand.trim().slice(0,4096):N.recipesMacroDefaultCommand,clusterEnabled:typeof e.clusterEnabled=="boolean"?e.clusterEnabled:N.clusterEnabled,clusterLabel:typeof e.clusterLabel=="string"?e.clusterLabel.trim().slice(0,128):N.clusterLabel,clusterRole:Am(e.clusterRole),clusterSenderBindings:Im(e.clusterSenderBindings),serviceInstallFromChat:typeof e.serviceInstallFromChat=="boolean"?e.serviceInstallFromChat:N.serviceInstallFromChat,updateCheckEnabled:typeof e.updateCheckEnabled=="boolean"?e.updateCheckEnabled:N.updateCheckEnabled,updateCheckIntervalMs:typeof e.updateCheckIntervalMs=="number"&&e.updateCheckIntervalMs>0?Math.min(6048e5,Math.max(36e5,Math.floor(e.updateCheckIntervalMs))):N.updateCheckIntervalMs,updateCheckPackageName:typeof e.updateCheckPackageName=="string"&&e.updateCheckPackageName.trim().length>0?e.updateCheckPackageName.trim().slice(0,214):N.updateCheckPackageName,updateInfoUrl:typeof e.updateInfoUrl=="string"?e.updateInfoUrl.trim().slice(0,2048):N.updateInfoUrl,chatLlmFallbackEnabled:typeof e.chatLlmFallbackEnabled=="boolean"?e.chatLlmFallbackEnabled:N.chatLlmFallbackEnabled,chatLlmShellCommand:typeof e.chatLlmShellCommand=="string"?e.chatLlmShellCommand.trim().slice(0,8192):N.chatLlmShellCommand,chatLlmTimeoutMs:typeof e.chatLlmTimeoutMs=="number"&&e.chatLlmTimeoutMs>0?Math.min(9e5,Math.floor(e.chatLlmTimeoutMs)):N.chatLlmTimeoutMs,chatLlmMaxInputChars:typeof e.chatLlmMaxInputChars=="number"&&e.chatLlmMaxInputChars>0?Math.min(5e5,Math.floor(e.chatLlmMaxInputChars)):N.chatLlmMaxInputChars,chatLlmMaxOutputChars:typeof e.chatLlmMaxOutputChars=="number"&&e.chatLlmMaxOutputChars>0?Math.min(2e6,Math.floor(e.chatLlmMaxOutputChars)):N.chatLlmMaxOutputChars,chatLlmNeedsTty:typeof e.chatLlmNeedsTty=="boolean"?e.chatLlmNeedsTty:N.chatLlmNeedsTty,chatLlmWorkDir:typeof e.chatLlmWorkDir=="string"?e.chatLlmWorkDir.trim().slice(0,4096):N.chatLlmWorkDir,tunnelEnabled:typeof e.tunnelEnabled=="boolean"?e.tunnelEnabled:N.tunnelEnabled,tunnelRelayUrl:typeof e.tunnelRelayUrl=="string"&&e.tunnelRelayUrl.trim().length>0?e.tunnelRelayUrl.trim().slice(0,2048):N.tunnelRelayUrl,tunnelMaxActive:typeof e.tunnelMaxActive=="number"&&e.tunnelMaxActive>0?Math.min(50,Math.floor(e.tunnelMaxActive)):N.tunnelMaxActive,platformToken:typeof e.platformToken=="string"?e.platformToken.trim():N.platformToken,platformDeviceId:typeof e.platformDeviceId=="string"?e.platformDeviceId.trim().slice(0,128):N.platformDeviceId,webhookEnabled:typeof e.webhookEnabled=="boolean"?e.webhookEnabled:N.webhookEnabled,webhookPort:typeof e.webhookPort=="number"&&e.webhookPort>=0?Math.min(65535,Math.floor(e.webhookPort)):N.webhookPort,webhookHost:typeof e.webhookHost=="string"&&e.webhookHost.trim().length>0?e.webhookHost.trim():N.webhookHost,webhookToken:typeof e.webhookToken=="string"?e.webhookToken.trim():N.webhookToken,watchEnabled:typeof e.watchEnabled=="boolean"?e.watchEnabled:N.watchEnabled,watchDebounceMs:typeof e.watchDebounceMs=="number"&&Number.isFinite(e.watchDebounceMs)?Math.max(500,Math.min(6e4,Math.floor(e.watchDebounceMs))):N.watchDebounceMs,watchMaxEventsPerMinute:typeof e.watchMaxEventsPerMinute=="number"&&Number.isFinite(e.watchMaxEventsPerMinute)?Math.max(1,Math.min(120,Math.floor(e.watchMaxEventsPerMinute))):N.watchMaxEventsPerMinute,watchAutoRestore:typeof e.watchAutoRestore=="boolean"?e.watchAutoRestore:N.watchAutoRestore}}function W(e){let t=$(),n=rr({...t,...e});return _e(n),n}function $(){if(te(),!Jr.existsSync(_)){let e=rr({});return _e(e),e}try{let e=Jr.readFileSync(_,"utf8"),t=JSON.parse(e);return rr(t)}catch{return rr({})}}function _e(e){te();let t=rr(e);Jr.writeFileSync(_,JSON.stringify(t,null,2)+`
|
|
3
|
+
`,{mode:384})}function Kr(e){let t=Us(e);if(!t)throw new Error("Invalid entry. Use +E164 (WhatsApp) or tg:<user_id> (Telegram).");let n=$();if(t.kind==="wa"){if(t.normalized==="*")throw new Error("Wildcards are not allowed.");let r=new Set(n.allowFrom);r.add(t.normalized),n.allowFrom=[...r].sort()}else{let r=new Set(n.telegramAllowFrom);r.add(t.id),n.telegramAllowFrom=[...r].sort()}return _e(n),n}function zr(e){let t=Us(e);if(!t)throw new Error("Invalid entry. Use +E164 (WhatsApp) or tg:<user_id> (Telegram).");let n=$();return t.kind==="wa"?n.allowFrom=n.allowFrom.filter(r=>r!==t.normalized):n.telegramAllowFrom=n.telegramAllowFrom.filter(r=>Ee(r)!==t.id),_e(n),n}function ve(e){let t=typeof process.env.TELEGRAM_BOT_TOKEN=="string"?process.env.TELEGRAM_BOT_TOKEN.trim():"";return t||(e.telegramBotToken??"").trim()}function dt(e){let t=e.trim();return/^\d{6,}:[A-Za-z0-9_-]{20,}$/.test(t)}function Lt(e){let t=$();return t.telegramBotToken=e.trim(),_e(t),$()}function gn(e){let t=e.trim().toLowerCase();return t==="whatsapp"||t==="wa"||t==="w"?"whatsapp":t==="telegram"||t==="tg"||t==="t"?"telegram":t==="both"||t==="all"||t==="b"?"both":null}function qr(e){let t=$();return t.gatewayMode=e,_e(t),$()}function Yr(e){let t=$();return t.clusterEnabled=e,_e(t),$()}function nt(){try{return Jr.readdirSync(ne).length>0}catch{return!1}}var N,pe=Zn(()=>{"use strict";G();qe();N={gatewayMode:"whatsapp",telegramBotToken:"",telegramAllowFrom:[],allowFrom:[],commandPrefix:"!",syncTimeoutMs:3e4,syncMaxBytes:32768,jobLogTailLines:80,shell:"/bin/bash",appsCols:120,appsRows:40,appsFlushMs:300,appsMinIntervalMs:800,appsMaxFlushBytes:8192,appsMaxSessions:5,appsMaxSessionsTotal:20,appsMaxWaChars:3500,appsLogTailLines:80,appsSubmitDelayMs:50,appsClearInput:!0,appsClearInputDelayMs:20,appsClearInputSequence:"^A,^K",appsSkipClearOnPasswordPrompt:!0,appsPasswordPromptHint:!0,fileSendMaxBytes:0,fileReceiveMaxBytes:0,fileInboxSubdir:"inbox",fileReceiveRootMode:"downloads",fileReceiveRootPath:"",recipesAllowDangerousBuiltins:!1,recipesMaxTaskChars:0,recipesMacroDefaultCommand:'claude -p "$OMNISH_TASK"',clusterEnabled:!1,clusterLabel:"",clusterRole:"secondary",clusterSenderBindings:{},serviceInstallFromChat:!1,updateCheckEnabled:!1,updateCheckIntervalMs:864e5,updateCheckPackageName:"omnish",updateInfoUrl:"",chatLlmFallbackEnabled:!1,chatLlmShellCommand:"",chatLlmTimeoutMs:12e4,chatLlmMaxInputChars:16e3,chatLlmMaxOutputChars:24e3,chatLlmNeedsTty:!1,chatLlmWorkDir:"",tunnelEnabled:!1,tunnelRelayUrl:"https://tunnel.omnish.dev",platformToken:"",platformDeviceId:"",tunnelMaxActive:5,webhookEnabled:!1,webhookPort:0,webhookHost:"127.0.0.1",webhookToken:"",watchEnabled:!1,watchDebounceMs:2e3,watchMaxEventsPerMinute:30,watchAutoRestore:!0}});import Lm from"pino";function yn(){return process.env.OMNISH_VERBOSE==="1"||process.env.WHATSVERBOSE==="1"}function rl(){return T.child({module:"baileys"})}var Om,T,be=Zn(()=>{"use strict";Om=yn()?"info":"silent",T=Lm({level:Om,base:{app:"omnish"}})});var hd={};$m(hd,{fetchPlatformAccount:()=>Bn,getAttachedConfig:()=>ea,getAttachedPlatformSnapshot:()=>Sy,mergeAttachedPlatformPolicy:()=>fd,parsePlatformMeResponse:()=>pd,setAttachedPlatformSnapshot:()=>Xi,setPlatformDefaultDevice:()=>ta,snapshotFromRegisteredAccount:()=>md,syncAttachedPlatformPolicy:()=>ns,updatePlatformAllowlists:()=>ts});function Zi(e){let t=e.replace(/\/$/,"");return/^https?:\/\//i.test(t)?t:`http://${t}`}function by(e){return e==="telegram"||e==="both"||e==="whatsapp"?e:"whatsapp"}function ky(e){if(!e||typeof e!="object")return{};let t={};for(let[n,r]of Object.entries(e)){if(!r||typeof r!="object")continue;let o=r,s=typeof o.status=="string"?o.status:"idle";t[n]={status:s,linked:o.linked===!0||s==="linked",...typeof o.tokenConfigured=="boolean"?{tokenConfigured:o.tokenConfigured}:{}}}return t}function pd(e){let t=e.routing,n=t&&typeof t=="object"?t:null,r=Array.isArray(n?.onlineDeviceIds)?n.onlineDeviceIds.map(String):[],o=n?.defaultDeviceId!=null?String(n.defaultDeviceId):e.defaultDeviceId!=null?String(e.defaultDeviceId):null;return{allowFrom:Array.isArray(e.allowFrom)?e.allowFrom.map(String):[],telegramAllowFrom:Array.isArray(e.telegramAllowFrom)?e.telegramAllowFrom.map(String):[],gatewayMode:by(e.gatewayMode),connectors:ky(e.connectors),defaultDeviceId:o,routing:{defaultDeviceId:o,onlineDeviceIds:r,onlineCount:typeof n?.onlineCount=="number"?n.onlineCount:r.length}}}function md(e,t=[],n=[]){let r=e.defaultDeviceId??null;return{allowFrom:t,telegramAllowFrom:n,gatewayMode:e.gatewayMode,connectors:e.connectors,defaultDeviceId:r,routing:{defaultDeviceId:r,onlineDeviceIds:[],onlineCount:0}}}function fd(e,t){return{...e,allowFrom:[...t.allowFrom],telegramAllowFrom:[...t.telegramAllowFrom],gatewayMode:t.gatewayMode}}function Xi(e){es=e}function Sy(){return es}function ea(){let e=$();return es?fd(e,es):e}async function Bn(e){let t=await fetch(`${Zi(e.platformUrl)}/v1/me`,{headers:{Authorization:`Bearer ${e.token}`}});if(!t.ok)throw new Error(`Platform GET /v1/me failed: HTTP ${t.status}`);let n=await t.json();return pd(n)}async function ts(e,t){let n=await fetch(`${Zi(e.platformUrl)}/v1/me/allowlists`,{method:"PUT",headers:{"content-type":"application/json",Authorization:`Bearer ${e.token}`},body:JSON.stringify(t)}),r=await n.json().catch(()=>({}));if(!n.ok)throw new Error(r.error||`Platform PUT /v1/me/allowlists failed: HTTP ${n.status}`);return{allowFrom:Array.isArray(r.allowFrom)?r.allowFrom.map(String):[],telegramAllowFrom:Array.isArray(r.telegramAllowFrom)?r.telegramAllowFrom.map(String):[]}}async function ta(e,t){let n=await fetch(`${Zi(e.platformUrl)}/v1/me/default-device`,{method:"PUT",headers:{"content-type":"application/json",Authorization:`Bearer ${e.token}`},body:JSON.stringify({deviceId:t})});if(!n.ok){let r=await n.json().catch(()=>({}));throw new Error(r.error||`Platform PUT /v1/me/default-device failed: HTTP ${n.status}`)}}async function ns(e,t){try{let n=await Bn(e);return Xi(n),T.info({gatewayMode:n.gatewayMode,waLinked:n.connectors.whatsapp?.linked===!0,tgLinked:n.connectors.telegram?.linked===!0,allowFromCount:n.allowFrom.length,telegramAllowFromCount:n.telegramAllowFrom.length},"attached platform policy loaded"),n}catch(n){if(t){let r=md(t);return Xi(r),T.warn({err:String(n)},"platform GET /v1/me failed; using register ack for gatewayMode/connectors only (allowlists from local config until /v1/me works)"),r}return T.warn({err:String(n)},"platform GET /v1/me failed; attached inbound uses local config.json allowlists"),null}}var es,rs=Zn(()=>{"use strict";pe();be();es=null});pe();import Jb from"node:dns";import Kb from"node:crypto";import Xt from"node:fs";import Ka from"node:path";import gm from"node:os";pe();be();import Sf from"node:os";import vf from"node:fs";G();import ol from"node:crypto";import or from"node:fs";import Ds from"node:os";import wn from"node:path";var Nm=/^[a-zA-Z0-9][a-zA-Z0-9_-]{0,31}$/;function Bs(e){let t=e.trim().toLowerCase();return Nm.test(t)?{ok:!0,name:t}:{ok:!1,error:"Name must be alphanumeric with _ or -, max 32 chars."}}function Ge(e,t){let n=e.trim();return n===""||n==="."?wn.resolve(t):n==="~"?Ds.homedir():n.startsWith("~/")||n.startsWith("~\\")?wn.join(Ds.homedir(),n.slice(2)):wn.isAbsolute(n)?n:wn.resolve(t,n)}function Qr(e){return wn.join(Ds.homedir(),"Cowork",e)}function _m(e){if(!e||typeof e!="object")return null;let t=e,n=typeof t.id=="string"&&t.id.length>=4?t.id.slice(0,32):"",r=typeof t.name=="string"?t.name.trim().toLowerCase():"",o=typeof t.ownerPeerKey=="string"?t.ownerPeerKey:"",s=typeof t.command=="string"?t.command:"";if(!n||!r||!o||!s)return null;let i=typeof t.cwd=="string"?t.cwd:"",a=typeof t.outputDir=="string"&&t.outputDir.trim()?t.outputDir:Qr(r),l=typeof t.enabled=="boolean"?t.enabled:!0,d=typeof t.notify=="string"?t.notify.toLowerCase():"self",u=d==="wa"||d==="whatsapp"?"wa":d==="tg"||d==="telegram"?"tg":d==="all"?"all":d==="none"?"none":"self",c={kind:"ondemand"};if(t.schedule&&typeof t.schedule=="object"){let v=t.schedule,E=typeof v.kind=="string"?v.kind:"";if(E==="ondemand")c={kind:"ondemand"};else if(E==="daily"){let R=Number(v.hour),P=Number(v.minute);Number.isFinite(R)&&Number.isFinite(P)&&(c={kind:"daily",hour:R,minute:P})}else if(E==="weekdays"){let R=Number(v.hour),P=Number(v.minute);Number.isFinite(R)&&Number.isFinite(P)&&(c={kind:"weekdays",hour:R,minute:P})}else if(E==="hourly"){let R=Number(v.minute);Number.isFinite(R)&&Number.isInteger(R)&&R>=0&&R<=59&&(c={kind:"hourly",minute:R})}else if(E==="weekly"){let R=Number(v.hour),P=Number(v.minute),M=Number(v.weekday);Number.isFinite(R)&&Number.isFinite(P)&&Number.isInteger(M)&&(c={kind:"weekly",weekday:M,hour:R,minute:P})}else if(E==="heartbeat"){let R=Number(v.intervalMs),P=Number(v.graceMs);Number.isFinite(R)&&R>0&&Number.isFinite(P)&&P>0&&(c={kind:"heartbeat",intervalMs:R,graceMs:P})}}let m=null;typeof t.lastCompletedSlotMs=="number"&&Number.isFinite(t.lastCompletedSlotMs)&&(m=t.lastCompletedSlotMs);let f=typeof t.createdAtMs=="number"&&Number.isFinite(t.createdAtMs)?t.createdAtMs:Date.now(),h=typeof t.attachLog=="boolean"?t.attachLog:!1,g=Array.isArray(t.attachFiles)?t.attachFiles.filter(v=>typeof v=="string"&&v.trim().length>0):[],y=typeof t.notifyWhen=="string"?t.notifyWhen.toLowerCase():"always";return{id:n,name:r,ownerPeerKey:o,command:s,cwd:i,outputDir:a,schedule:c,enabled:l,notify:u,notifyWhen:y==="failure"?"failure":y==="state-change"?"state-change":"always",attachLog:h,attachFiles:g,lastCompletedSlotMs:m,createdAtMs:f}}function xe(){try{let e=or.readFileSync(Fs,"utf8"),t=JSON.parse(e);if(!t||!Array.isArray(t.tasks))return[];let n=[];for(let r of t.tasks){let o=_m(r);o&&n.push(o)}return n}catch{return[]}}function Le(e){j(ut);let t={tasks:e},n=wn.join(ut,`.tasks.${process.pid}.${ol.randomBytes(4).toString("hex")}.tmp`);or.writeFileSync(n,JSON.stringify(t,null,2)+`
|
|
4
|
+
`,{mode:384}),or.renameSync(n,Fs)}function Ye(e,t,n){let r=t.trim().toLowerCase();return e.find(o=>o.name===r&&o.ownerPeerKey===n)}function sr(){return ol.randomBytes(4).toString("hex")}function sl(e){j(ut),or.writeFileSync(Ws,JSON.stringify(e,null,2)+`
|
|
5
|
+
`,{mode:384})}function il(e){let t=Hs();t.push(e),sl(t)}function Hs(){try{let e=or.readFileSync(Ws,"utf8"),t=JSON.parse(e);return Array.isArray(t)?t.filter(n=>n&&typeof n=="object"&&typeof n.ownerPeerKey=="string"&&typeof n.name=="string"):[]}catch{return[]}}function al(e){if(e<=0)return{batch:[],remainingAfter:Hs().length};let t=Hs();if(t.length===0)return{batch:[],remainingAfter:0};let n=t.slice(0,e),r=t.slice(e);return sl(r),{batch:n,remainingAfter:r.length}}G();G();import Fm from"better-sqlite3";var ll=1,cl=200,bn=null;function Wm(){j(bt);let e=new Fm(Hr);e.pragma("journal_mode = WAL"),e.exec(`
|
|
6
|
+
CREATE TABLE IF NOT EXISTS watch_meta (
|
|
7
|
+
key TEXT PRIMARY KEY,
|
|
8
|
+
value TEXT NOT NULL
|
|
9
|
+
);
|
|
10
|
+
CREATE TABLE IF NOT EXISTS watch_recent (
|
|
11
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
12
|
+
rule_id TEXT NOT NULL,
|
|
13
|
+
rule_name TEXT NOT NULL,
|
|
14
|
+
kind TEXT NOT NULL,
|
|
15
|
+
summary TEXT NOT NULL,
|
|
16
|
+
ts_ms INTEGER NOT NULL
|
|
17
|
+
);
|
|
18
|
+
CREATE INDEX IF NOT EXISTS idx_watch_recent_ts ON watch_recent(ts_ms DESC);
|
|
19
|
+
CREATE TABLE IF NOT EXISTS watch_rule_state (
|
|
20
|
+
rule_id TEXT PRIMARY KEY,
|
|
21
|
+
state_key TEXT NOT NULL,
|
|
22
|
+
updated_at_ms INTEGER NOT NULL
|
|
23
|
+
);
|
|
24
|
+
`);let t=e.prepare("SELECT value FROM watch_meta WHERE key = 'schema_version'").get();return(t?Number(t.value):0)<ll&&e.prepare("INSERT OR REPLACE INTO watch_meta (key, value) VALUES ('schema_version', ?)").run(String(ll)),e}function ir(){return bn||(bn=Wm()),bn}function Vr(){ir()}function ul(){if(bn){try{bn.close()}catch{}bn=null}}function dl(e){let t=ir();t.prepare("INSERT INTO watch_recent (rule_id, rule_name, kind, summary, ts_ms) VALUES (?, ?, ?, ?, ?)").run(e.ruleId,e.ruleName,e.kind,e.summary.slice(0,2e3),e.tsMs);let n=t.prepare("SELECT COUNT(*) AS c FROM watch_recent").get();n.c>cl&&t.prepare(`DELETE FROM watch_recent WHERE id IN (
|
|
25
|
+
SELECT id FROM watch_recent ORDER BY ts_ms ASC LIMIT ?
|
|
26
|
+
)`).run(n.c-cl)}function pl(e,t){let n=ir(),r=Math.min(Math.max(1,e),50),o=n.prepare("SELECT rule_id, rule_name, kind, summary, ts_ms FROM watch_recent ORDER BY ts_ms DESC LIMIT ?").all(r*3),s=[];for(let i of o)if(!(t&&!t.has(i.rule_id))&&(s.push({ruleId:i.rule_id,ruleName:i.rule_name,kind:i.kind,stateKey:"",summary:i.summary,tsMs:i.ts_ms}),s.length>=r))break;return s}function ml(e){return ir().prepare("SELECT state_key FROM watch_rule_state WHERE rule_id = ?").get(e)?.state_key??null}function fl(e,t,n){ir().prepare("INSERT OR REPLACE INTO watch_rule_state (rule_id, state_key, updated_at_ms) VALUES (?, ?, ?)").run(e,t,n)}import bl from"node:path";import kt from"node:path";function Um(e,t){let n=kt.normalize(e);for(let r of t){let o=kt.normalize(r);if(n===o||n.startsWith(o+kt.sep))return!0}return!1}function Dm(e,t,n){let r=kt.relative(t,e);if(r.startsWith("..")||kt.isAbsolute(r))return!1;let o=r.split(kt.sep).join("/"),s=n.replace(/\\/g,"/").replace(/^\//,"");if(s.includes("**")){let i=s.replace(/[.+^${}()|[\]\\]/g,"\\$&").replace(/\*\*/g,"___GLOBSTAR___").replace(/\*/g,"[^/]*").replace(/___GLOBSTAR___/g,".*");try{if(new RegExp(`^${i}$`).test(o))return!0;if(s.startsWith("**/")){let l=s.slice(3);if(l&&!l.includes("*"))return o.split("/").includes(l)}return!1}catch{return!1}}if(s.includes("*")){let i=s.replace(/[.+^${}()|[\]\\]/g,"\\$&").replace(/\*/g,"[^/]*");try{return new RegExp(`^${i}$`).test(o)}catch{return!1}}return o===s||o.endsWith("/"+s)||o.includes("/"+s+"/")}function Xr(e,t){if(t.excludePaths.length&&Um(e,t.excludePaths))return!0;let n=t.path;if(n&&t.excludeGlobs.length){for(let r of t.excludeGlobs)if(Dm(e,n,r))return!0}return!1}function hl(e){let t=[];for(let n of e.excludeGlobs){let r=n.replace(/\\/g,"/");r.startsWith("**/")?t.push(r):r.includes("*")?t.push(`**/${r}`):t.push(`**/${r}/**`)}for(let n of e.excludePaths)if(e.path)try{let r=kt.relative(e.path,n);!r.startsWith("..")&&!kt.isAbsolute(r)&&t.push(r.split(kt.sep).join("/")+"/**")}catch{}return t}qe();function kn(e,t,n){if(e==="none")return[];let r=new Set;if((e==="self"||e==="all")&&r.add(t),e==="wa"||e==="all")for(let o of n.allowFrom){let s=ee(String(o));s&&r.add(`wa:${It(s)}`)}if(e==="tg"||e==="all")for(let o of n.telegramAllowFrom){let s=Ee(String(o));s&&r.add(`tg:${s}`)}return[...r]}function gl(e,t,n){return kn(e,t,n)}be();G();import Hm from"node:crypto";import js from"node:fs";var Bm=/^[a-zA-Z0-9][a-zA-Z0-9_-]{0,31}$/,jm=1,Gs=20;function Zr(e){let t=e.trim().toLowerCase();return Bm.test(t)?{ok:!0,name:t}:{ok:!1,error:"Name must be alphanumeric with _ or -, max 32 chars."}}function eo(){return Hm.randomBytes(8).toString("hex")}function Gm(e){if(!Array.isArray(e)||e.length===0)return["create","delete","rename"];let t=new Set(["create","delete","rename","update"]),n=[];for(let r of e)typeof r=="string"&&t.has(r)&&n.push(r);return n.length?n:["create","delete","rename"]}function Jm(e){if(!e||typeof e!="object")return null;let t=e,n=typeof t.id=="string"&&t.id.length>=4?t.id.slice(0,32):"",r=typeof t.name=="string"?t.name.trim().toLowerCase():"",o=typeof t.ownerPeerKey=="string"?t.ownerPeerKey:"",s=typeof t.kind=="string"?t.kind:"",i=s==="fs"||s==="pkg"||s==="svc"?s:null;if(!n||!r||!o||!i)return null;let a=typeof t.notify=="string"?t.notify.toLowerCase():"self",l=a==="wa"||a==="whatsapp"?"wa":a==="tg"||a==="telegram"?"tg":a==="all"?"all":a==="none"?"none":"self",d=typeof t.notifyWhen=="string"?t.notifyWhen:"always",u=d==="failure"||d==="state-change"?d:"always",c=Array.isArray(t.units)?t.units.filter(h=>typeof h=="string"&&h.trim().length>0).map(h=>h.trim()):[],m=Array.isArray(t.excludePaths)?t.excludePaths.filter(h=>typeof h=="string"&&h.trim().length>0):[],f=Array.isArray(t.excludeGlobs)?t.excludeGlobs.filter(h=>typeof h=="string"&&h.trim().length>0):[];return{id:n,name:r,ownerPeerKey:o,kind:i,enabled:typeof t.enabled=="boolean"?t.enabled:!0,paused:typeof t.paused=="boolean"?t.paused:!1,notify:l,notifyWhen:u,path:typeof t.path=="string"?t.path:"",events:Gm(t.events),units:c,excludePaths:m,excludeGlobs:f,adapterStatus:typeof t.adapterStatus=="string"?t.adapterStatus:"",createdAtMs:typeof t.createdAtMs=="number"&&Number.isFinite(t.createdAtMs)?t.createdAtMs:Date.now()}}function Km(e){let t=JSON.parse(e);return!t||!Array.isArray(t.rules)?[]:t.rules.map(Jm).filter(n=>n!==null)}function zm(e){let t=[...e].sort((s,i)=>s.createdAtMs-i.createdAtMs),n=new Set,r=!1,o=[];for(let s of t){if(!n.has(s.name)){n.add(s.name),o.push(s);continue}let i=s.id.slice(0,8),a=`${s.name}-${i}`;a.length>32&&(a=`${s.name.slice(0,32-i.length-1)}-${i}`);let l=0;for(;n.has(a);){l+=1;let d=String(l);a=`${s.name.slice(0,Math.max(1,32-i.length-d.length-1))}-${i}${d}`}n.add(a),T.warn({oldName:s.name,newName:a,id:s.id},"watch: renamed duplicate rule for device-wide namespace"),o.push({...s,name:a}),r=!0}return{rules:o,changed:r}}function Oe(){try{let e=js.readFileSync(tr,"utf8"),t=Km(e),{rules:n,changed:r}=zm(t);return r&&pt(n),n}catch{return[]}}function qm(e){let t=0,n=0,r=0;for(let o of e)o.enabled?o.paused?n+=1:t+=1:r+=1;return{total:e.length,active:t,paused:n,disabled:r}}function ar(){let e=Oe();return{rules:e,summary:qm(e)}}function pt(e){j(bt);let n=`${JSON.stringify({version:jm,rules:e},null,2)}
|
|
27
|
+
`,r=`${tr}.tmp`;js.writeFileSync(r,n,{mode:384}),js.renameSync(r,tr)}function Js(){return tr}function Zt(e,t){let n=t.trim().toLowerCase();return e.find(r=>r.name===n)}function yl(e,t){return e.find(n=>n.id===t)}function Sn(e,t){let n=e.findIndex(r=>r.id===t.id);if(n>=0){let r=[...e];return r[n]=t,r}return[...e,t]}function wl(e,t){let n=t.trim().toLowerCase();return e.filter(r=>r.name!==n)}import Ym from"node:os";import mt from"node:path";var Qm=new Set([".env","id_rsa","id_ed25519","id_ecdsa","known_hosts","credentials.json","secrets.json",".netrc","shadow","passwd"]);function Qe(e){let t=mt.normalize(e),n=t.toLowerCase(),r=Ym.homedir().toLowerCase();if(n.includes(`${mt.sep}.ssh${mt.sep}`)||n.endsWith(`${mt.sep}.ssh`)||n.includes(`${mt.sep}browser${mt.sep}`)&&n.includes("profile")||r&&(n===`${r}/.gnupg`||n.startsWith(`${r}/.gnupg${mt.sep}`))||n.includes(`${mt.sep}keychains${mt.sep}`))return!0;let o=mt.basename(t);return!!(Qm.has(o)||o.startsWith(".env.")||o.endsWith(".pem")||o.endsWith(".key"))}var Vm=["node_modules",".git",".svn",".hg","__pycache__",".cache",".next","dist","build"],Xm=[".tmp",".swp",".swx","~",".part"];function kl(e,t){let n=new Map,r=new Map;function o(u,c){let m=u.split(bl.sep);for(let h of m)if(Vm.includes(h))return!0;let f=bl.basename(u);for(let h of Xm)if(f.endsWith(h))return!0;return!!Xr(u,c)}function s(u){let c=yl(Oe(),u);return!c||!c.enabled||c.paused?null:c}function i(u){let c=Date.now(),m=r.get(u);return!m||c-m.windowStart>=6e4?(r.set(u,{windowStart:c,count:1}),!1):m.count>=t.maxEventsPerMinute?!0:(m.count+=1,!1)}async function a(u,c){let m=e.getConfig();if(!m.watchEnabled)return;let f=s(u.id);if(!f||c.kind==="fs"&&c.meta?.path&&(Qe(c.meta.path)||o(c.meta.path,f)))return;if(dl(c),(f.notifyWhen??"always")==="state-change"){if(ml(f.id)===c.stateKey)return;fl(f.id,c.stateKey,c.tsMs)}let g=gl(f.notify,f.ownerPeerKey,m);if(g.length===0)return;let y=`[watch:${f.name}] ${c.summary}`;await Promise.all(g.map(b=>e.sendToPeer(b,y).catch(()=>{})))}function l(u){let c=n.get(u);if(!c)return;n.delete(u);let m=s(c.ruleId);m&&(i(c.ruleId)||a(m,c.event))}function d(u){for(let[c,m]of n)m.ruleId===u&&(clearTimeout(m.timer),n.delete(c))}return{ingest(u,c){if(!u.enabled||u.paused||c.meta?.path&&(Qe(c.meta.path)||o(c.meta.path,u)))return;let m=`${u.id}:${c.stateKey||c.summary}`,f=n.get(m);f&&clearTimeout(f.timer);let h=setTimeout(()=>l(m),t.debounceMs);h.unref?.(),n.set(m,{timer:h,event:c,ruleId:u.id})},cancelForRule:d,dispose(){for(let u of n.values())clearTimeout(u.timer);n.clear(),r.clear()}}}function Sl(e,t){let n=e.toLowerCase();return!!(n==="create"&&t.has("create")||n==="delete"&&t.has("delete")||(n==="update"||n==="rename")&&(t.has("update")||t.has("rename")))}be();import vl from"node:fs";import Zm from"node:path";import{subscribe as ef}from"@parcel/watcher";var tf=["**/node_modules/**","**/.git/**","**/.svn/**","**/.hg/**","**/__pycache__/**","**/.cache/**"];function nf(e){return e==="create"?"create":e==="delete"?"delete":e==="update"?"update":e}function rf(e,t){try{let n=vl.statSync(t),r=n.isFile()&&n.size<1024*1024?` (${n.size<1024?`${n.size} B`:`${(n.size/1024).toFixed(1)} KB`})`:"";return`fs: ${e} ${t}${r}`}catch{return`fs: ${e} ${t}`}}async function xl(e,t){let n=e.path;if(!n||!vl.existsSync(n))return{stop:async()=>{},status:()=>"error: path missing or not found"};if(Qe(n))return{stop:async()=>{},status:()=>"error: sensitive path denied"};let r=new Set(e.events.map(i=>i.toLowerCase())),o="ok",s=null;try{s=await ef(n,(i,a)=>{if(i){o=`error: ${i.message}`,T.warn({rule:e.name,err:i.message},"watch fs adapter");return}for(let l of a){let d=nf(l.type);if(!Sl(d,r))continue;let u=Zm.resolve(l.path);if(!u.startsWith(n)||Qe(u)||Xr(u,e))continue;let c=rf(d,u);t({ruleId:e.id,ruleName:e.name,kind:"fs",stateKey:`${d}:${u}`,summary:c,tsMs:Date.now(),meta:{path:u,type:d}})}},{ignore:[...tf,...hl(e)]})}catch(i){o=`error: ${String(i)}`,T.warn({rule:e.name,err:String(i)},"watch fs subscribe failed")}return{stop:async()=>{s&&(await s.unsubscribe().catch(()=>{}),s=null)},status:()=>o}}import pf from"node:os";be();import Ot from"node:fs";function of(e,t,n){let r=n?.pollMs??2e3,o=0,s=!1,i=null,a=null,l="starting";function d(){if(!s)try{let u=Ot.statSync(e);if(u.size<o&&(o=0),u.size<=o)return;let c=Ot.openSync(e,"r");try{let m=u.size-o,f=Buffer.alloc(m);Ot.readSync(c,f,0,m,o),o=u.size;let h=f.toString("utf8");for(let g of h.split(`
|
|
28
|
+
`)){let y=g.trim();y&&t(y)}}finally{Ot.closeSync(c)}l="ok"}catch(u){l=`error: ${String(u)}`}}try{Ot.existsSync(e)&&(o=Ot.statSync(e).size),i=Ot.watch(e,()=>d()),i.on("error",()=>{i?.close(),i=null}),l="ok (watch)"}catch{l="ok (poll)",a=setInterval(d,r),a.unref?.(),d()}return{stop(){s=!0,i?.close(),a&&clearInterval(a)},status:()=>l}}function to(e,t){for(let n of e)try{if(Ot.existsSync(n))return of(n,t)}catch(r){T.debug({path:n,err:String(r)},"watch log-tail skip path")}return null}var sf=["/var/log/install.log"];function af(e){let t=e.toLowerCase();return t.includes("installed")||t.includes("upgraded")||t.includes("removed")?e.length>240?`pkg: ${e.slice(0,240)}\u2026`:`pkg: ${e}`:null}function $l(e,t){return to(sf,n=>{let r=af(n);r&&t({ruleId:e.id,ruleName:e.name,kind:"pkg",stateKey:r.slice(0,200),summary:r,tsMs:Date.now()})})}var lf=["/var/log/dpkg.log","/var/log/apt/history.log"];function cf(e){if(e.includes("status installed")){let t=e.match(/install\s+(\S+):/);if(t)return`pkg: installed ${t[1]} (dpkg)`}if(e.includes("status removed")||e.includes("remove ")){let t=e.match(/remove\s+(\S+):/);if(t)return`pkg: removed ${t[1]} (dpkg)`}return null}function uf(e){return e.startsWith("Install:")||e.startsWith("Upgrade:")?`pkg: ${e.slice(0,120)} (apt)`:e.startsWith("Remove:")?`pkg: ${e.slice(0,120)} (apt)`:null}function Rl(e,t){return to(lf,n=>{let r=cf(n)??uf(n);r&&t({ruleId:e.id,ruleName:e.name,kind:"pkg",stateKey:r,summary:r,tsMs:Date.now()})})}import{spawn as df}from"node:child_process";function Cl(e,t){let n=!1,r="ok",o=0,s=()=>{if(n)return;let a=df("powershell.exe",["-NoProfile","-NonInteractive","-Command","Get-WinEvent -FilterHashtable @{LogName='Application'; ProviderName=@('MsiInstaller','Windows Installer')} -MaxEvents 5 -ErrorAction SilentlyContinue | Select-Object -Property RecordId,Message | ConvertTo-Json -Compress"],{windowsHide:!0}),l="";a.stdout?.on("data",d=>{l+=String(d)}),a.on("close",d=>{if(n||d!==0||!l.trim()){d!==0&&(r="ok (no events or access denied)"),i();return}try{let u=JSON.parse(l),c=Array.isArray(u)?u:[u];for(let m of c.sort((f,h)=>f.RecordId-h.RecordId)){if(m.RecordId<=o)continue;o=m.RecordId;let f=(m.Message??"").replace(/\s+/g," ").trim().slice(0,300);if(!f)continue;let h=`pkg: ${f}`;t({ruleId:e.id,ruleName:e.name,kind:"pkg",stateKey:`win:${m.RecordId}`,summary:h,tsMs:Date.now()})}r="ok"}catch(u){r=`parse error: ${String(u)}`}i()}),a.on("error",d=>{r=`error: ${String(d)}`,i()})},i=()=>{n||setTimeout(s,3e4).unref?.()};return s(),{stop(){n=!0},status:()=>r}}function Ml(e,t){let n=pf.platform();if(n==="win32"){let o=Cl(e,t);return{stop:()=>o.stop(),status:o.status}}if(n==="darwin"){let o=$l(e,t);return o?{stop:()=>o.stop(),status:o.status}:{stop:()=>{},status:()=>"error: cannot read /var/log/install.log (try sudo or adm group)"}}let r=Rl(e,t);return r?{stop:()=>r.stop(),status:r.status}:{stop:()=>{},status:()=>"error: cannot read dpkg/apt logs (add user to adm or run gateway with read access)"}}import kf from"node:os";import{spawnSync as Tl}from"node:child_process";var mf=3e4;function ff(e){let t=new Map;for(let n of e){let r=Tl("launchctl",["print",`system/${n}`],{encoding:"utf8",timeout:1e4});if(r.status!==0){let s=process.getuid?.()??501,i=Tl("launchctl",["print",`gui/${s}/${n}`],{encoding:"utf8",timeout:1e4});if(i.status!==0){t.set(n,"not-found");continue}let a=(i.stdout??"").includes("state = running")?"running":"stopped";t.set(n,a);continue}let o=(r.stdout??"").includes("state = running")?"running":"stopped";t.set(n,o)}return t}function El(e,t){let n=e.units,r=!1,o="ok",s=new Map,i=()=>{if(r)return;let a=ff(n);if(a.size===0)o="error: no launchd labels configured";else{o="ok";for(let[l,d]of a){let u=s.get(l);if(u!==void 0&&u!==d){let c=`svc: ${l} ${u} \u2192 ${d}`;t({ruleId:e.id,ruleName:e.name,kind:"svc",stateKey:`${l}:${d}`,summary:c,tsMs:Date.now(),meta:{unit:l,state:d}})}}s=a}r||setTimeout(i,mf).unref?.()};return i(),{stop(){r=!0},status:()=>o}}import{spawnSync as Pl}from"node:child_process";var hf=3e4;function gf(e){let t=new Map;if(e.length===0)return t;let n=["show",...e,"--property=ActiveState,SubState,UnitFileState","--no-pager"],r=Pl("systemctl",n,{encoding:"utf8",timeout:15e3});if(r.status!==0)return t;let o="";for(let s of(r.stdout??"").split(`
|
|
29
|
+
`)){let i=s.match(/^Unit=(.+)$/);if(i){o=i[1];continue}let a=s.match(/^ActiveState=(.+)$/);a&&o&&t.set(o,a[1].trim())}if(t.size===0)for(let s of e){let i=s.endsWith(".service")?s:`${s}.service`,a=Pl("systemctl",["is-active",i],{encoding:"utf8",timeout:5e3}),l=(a.stdout??a.stderr??"unknown").trim();t.set(i,l)}return t}function Al(e,t){let n=e.units.map(a=>a.endsWith(".service")?a:`${a}.service`),r=!1,o="ok",s=new Map,i=()=>{if(r)return;let a=gf(n);if(a.size===0)o="error: systemctl unavailable or units not found";else{o="ok";for(let[l,d]of a){let u=s.get(l);if(u!==void 0&&u!==d){let c=`svc: ${l} ${u} \u2192 ${d}`;t({ruleId:e.id,ruleName:e.name,kind:"svc",stateKey:`${l}:${d}`,summary:c,tsMs:Date.now(),meta:{unit:l,state:d}})}}s=a}r||setTimeout(i,hf).unref?.()};return i(),{stop(){r=!0},status:()=>o}}import{spawnSync as yf}from"node:child_process";var wf=3e4;function bf(e){let t=new Map;for(let n of e){let r=yf("sc",["query",n],{encoding:"utf8",timeout:1e4,windowsHide:!0});if(r.status!==0){t.set(n,"not-found");continue}let o=r.stdout??"",s="unknown";o.includes("RUNNING")?s="running":o.includes("STOPPED")?s="stopped":o.includes("START_PENDING")?s="start-pending":o.includes("STOP_PENDING")&&(s="stop-pending"),t.set(n,s)}return t}function Il(e,t){let n=e.units,r=!1,o="ok",s=new Map,i=()=>{if(r)return;let a=bf(n);if(a.size===0)o="error: no service names configured";else{o="ok";for(let[l,d]of a){let u=s.get(l);if(u!==void 0&&u!==d){let c=`svc: ${l} ${u} \u2192 ${d}`;t({ruleId:e.id,ruleName:e.name,kind:"svc",stateKey:`${l}:${d}`,summary:c,tsMs:Date.now(),meta:{unit:l,state:d}})}}s=a}r||setTimeout(i,wf).unref?.()};return i(),{stop(){r=!0},status:()=>o}}function Ll(e,t){let n=kf.platform();return n==="win32"?Il(e,t):n==="darwin"?El(e,t):Al(e,t)}var Ve=null,Nt=null;function Ol(e){let t=e.getConfig();return kl(e,{debounceMs:Math.max(500,t.watchDebounceMs??2e3),maxEventsPerMinute:Math.max(1,t.watchMaxEventsPerMinute??30)})}function xf(e){return e.watchEnabled&&e.watchAutoRestore}var Ks=class{deps;pipeline;runtimes=new Map;stopped=!1;constructor(t){this.deps=t,this.pipeline=Ol(t)}onEvent=(t,n)=>{this.pipeline.ingest(t,n)};cancelPendingForRule(t){this.pipeline.cancelForRule(t)}async reload(){if(this.pipeline.dispose(),this.pipeline=Ol(this.deps),await this.stopAdapters(),this.stopped||!this.deps.getConfig().watchEnabled)return 0;let n=Oe().filter(r=>r.enabled&&!r.paused);for(let r of n)await this.startRule(r);return this.runtimes.size}async startRule(t){try{let n;if(t.kind==="fs"){let r=Ge(t.path,Sf.homedir()),o={...t,path:r};if(Qe(r)){t.adapterStatus="denied: sensitive path",this.persistRuleStatus(t);return}if(!vf.existsSync(r)){t.adapterStatus="error: path not found",this.persistRuleStatus(t);return}let s=await xl(o,i=>this.onEvent(o,i));n={stop:()=>s.stop(),status:s.status}}else if(t.kind==="pkg")n=Ml(t,o=>this.onEvent(t,o));else if(t.kind==="svc"){if(t.units.length===0){t.adapterStatus="error: no units",this.persistRuleStatus(t);return}n=Ll(t,o=>this.onEvent(t,o))}else return;t.adapterStatus=n.status(),this.persistRuleStatus(t),this.runtimes.set(t.id,{rule:t,adapter:n})}catch(n){t.adapterStatus=`error: ${String(n)}`,this.persistRuleStatus(t),T.warn({rule:t.name,err:String(n)},"watch rule start failed")}}persistRuleStatus(t){let n=Oe(),r=n.findIndex(o=>o.id===t.id);r>=0&&(n[r]={...n[r],adapterStatus:t.adapterStatus},pt(n))}async stopAdapters(){for(let t of this.runtimes.values())try{await t.adapter.stop()}catch{}this.runtimes.clear()}async stop(){this.stopped=!0,await this.stopAdapters(),this.pipeline.dispose(),ul()}getStatusLines(){let t=Oe();return t.length===0?["(no watch rules)"]:t.map(n=>{let o=this.runtimes.get(n.id)?.adapter.status()??n.adapterStatus,s=n.enabled?n.paused?"paused":"on":"disabled",i=n.kind==="fs"?n.path:n.kind==="svc"?n.units.join(","):"system",a=n.kind==="fs"&&(n.excludePaths.length||n.excludeGlobs.length)?` excludes:${n.excludePaths.length+n.excludeGlobs.length}`:"";return`${n.name} [${n.kind}] ${s} notify=${n.notify} when=${n.notifyWhen} \u2014 ${i}${a} \u2014 ${o}`})}getRunningCount(){return this.runtimes.size}isRuleRunning(t){return this.runtimes.has(t)}};function Nl(){return Ve}function zs(e){Ve?.cancelPendingForRule(e)}function $f(){j(bt),Vr()}function qs(){Nt&&(Ve||(Ve=new Ks(Nt)),Ve.reload())}function no(e){Nt=e,$f();let t=e.getConfig(),{summary:n}=ar();return n.total>0&&T.info({rules:n.total,active:n.active,paused:n.paused,disabled:n.disabled},"watch rules loaded from disk"),xf(t)&&qs(),()=>{Ve?.stop(),Ve=null,Nt=null}}async function _l(){return Nt?Nt.getConfig().watchEnabled?(qs(),Ve?.getRunningCount()??0):(Ve?.stop(),Ve=null,-1):-1}function De(){if(!Nt)return;if(!Nt.getConfig().watchEnabled){Ve?.stop(),Ve=null;return}qs()}G();pe();G();import Mf from"node:os";import oo from"node:path";G();import Ys from"node:fs";import Wl from"node:os";import _t from"node:path";var Ul=_t.join(H,"sessions.json"),vn=new Map;function Dl(){return{cwd:_t.resolve(Wl.homedir())}}function Rf(e){return e.startsWith("wa:")||e.startsWith("tg:")?e:`wa:${e}`}function Cf(){try{let e=Ys.readFileSync(Ul,"utf8"),t=JSON.parse(e);for(let[n,r]of Object.entries(t)){if(!r||typeof r!="object")continue;let o=Rf(n),i={cwd:typeof r.cwd=="string"&&r.cwd.length>0?_t.resolve(r.cwd):Dl().cwd};r.fileReceiveRoot==="sessionCwd"&&(i.fileReceiveRoot="sessionCwd"),vn.set(o,i)}}catch{}}function Hl(){j(H);let e={};for(let[t,n]of vn){let r={cwd:n.cwd};n.fileReceiveRoot==="sessionCwd"&&(r.fileReceiveRoot="sessionCwd"),e[t]=r}Ys.writeFileSync(Ul,JSON.stringify(e,null,2)+`
|
|
30
|
+
`,{mode:384})}var Fl=!1;function Qs(){Fl||(Cf(),Fl=!0)}function oe(e){Qs();let t=vn.get(e);return t||(t=Dl(),vn.set(e,t)),t}function ro(e,t){Qs();let n=_t.resolve(t),r=oe(e);r.cwd=n,vn.set(e,r),Hl()}function Bl(e){return oe(e).fileReceiveRoot==="sessionCwd"?"sessionCwd":"default"}function Vs(e,t){Qs();let n=oe(e);t==="default"?delete n.fileReceiveRoot:n.fileReceiveRoot="sessionCwd",vn.set(e,n),Hl()}function jl(e){let t=e.trim();if(/[&|;\n]/.test(t)||!t.startsWith("cd"))return null;if(t==="cd")return{kind:"home"};if(t.length>2&&t[2]!==" "&&t[2]!==" ")return null;let n=t.slice(2).trimStart();return n?{kind:"path",value:n}:{kind:"home"}}function Gl(e,t){if(t.kind==="home")return _t.resolve(Wl.homedir());let n=t.value.replace(/^['"]|['"]$/g,"");return _t.isAbsolute(n)?_t.normalize(n):_t.resolve(e,n)}function Jl(e){try{return Ys.statSync(e).isDirectory()?{ok:!0}:{ok:!1,error:`Not a directory: ${e}`}}catch(t){return{ok:!1,error:String(t)}}}var Tf="Omnish";function Kl(){return oo.join(Mf.homedir(),"Downloads",Tf)}function Ft(e,t){let n=oe(t);if(n.fileReceiveRoot==="sessionCwd")return n.cwd;switch(e.fileReceiveRootMode){case"downloads":return Kl();case"omnishData":return oo.join(H,e.fileInboxSubdir);case"sessionCwd":return n.cwd;case"processCwd":return process.cwd();case"fixed":{let r=(e.fileReceiveRootPath??"").trim();if(!r)throw new Error('fileReceiveRootPath is required when fileReceiveRootMode is "fixed".');if(!oo.isAbsolute(r))throw new Error('fileReceiveRootPath must be an absolute path when fileReceiveRootMode is "fixed".');return oo.resolve(r)}default:return Kl()}}G();import $t from"node:fs";import ql from"node:path";import Ht from"node:process";var en="\x1B";function Ef(e){return e.replace(/\u001B\[[\d;]*m/g,"")}function so(e){return Ef(e).length}function Xs(e,t,n,r,o=2){let s=Math.max(0,t-so(n));return`${e}${n}${" ".repeat(s)}${" ".repeat(o)}${r}`}function St(e,t,n,r){if(n.length===0)return[];let o=n.map(i=>r(i.left)),s=Math.max(...o.map(so));return n.map((i,a)=>Xs(t,s,o[a],S(e,i.right)))}var Wt={primary:"#b4ff24",foreground:"#eef2f4",muted:"#8a9199",border:"#2a3139",error:"#ef4444",warn:"#a0e614",warnAlt:"#8cce04"};function Pf(e){let t=e.replace(/^#/,"");return t.length!==6||!/^[0-9a-fA-F]+$/.test(t)?null:{r:parseInt(t.slice(0,2),16),g:parseInt(t.slice(2,4),16),b:parseInt(t.slice(4,6),16)}}function Ut(e){let t=Pf(e);return t?`${en}[38;2;${t.r};${t.g};${t.b}m`:""}function io(e){if(process.env.NO_COLOR!==void 0&&process.env.NO_COLOR!=="")return!1;let t=process.env.FORCE_COLOR;return t==="1"||t==="true"?!0:t==="0"||t==="false"?!1:e.isTTY===!0}function vt(e,t,n){return!t||!io(e)?n:`${t}${n}${en}[0m`}function J(e,t){return vt(e,`${en}[1m`,t)}function Af(e,t){return io(e)?`${Ut(Wt.foreground)}${en}[1m${t}${en}[0m`:t}function V(e,t){return vt(e,`${en}[2m`,t)}function Ce(e,t){return vt(e,`${Ut(Wt.primary)}${en}[1m`,t)}function ce(e,t){return vt(e,Ut(Wt.primary),t)}function S(e,t){return vt(e,Ut(Wt.foreground),t)}function w(e,t){return vt(e,Ut(Wt.muted),t)}function If(e,t){return vt(e,Ut(Wt.border),t)}function ao(e,t){return vt(e,Ut(Wt.error),t)}function me(e,t){return vt(e,Ut(Wt.warn),t)}function Dt(e,t=60,n="\u2500"){let r=n.repeat(Math.max(1,t));return If(e,r)}function B(e,t){return io(e)?`${`${w(e,"[")}${ce(e,"omnish")}${w(e,"]")}`} ${t}`:`[omnish] ${t}`}function C(e,t){return io(e)?`${`${ao(e,"[omnish]")}`} ${t}`:`[omnish] ${t}`}function zl(e,t){let n=[];for(let r of e)switch(r.kind){case"title":n.push("",Ce(t,r.text),"");break;case"sub":n.push("",Af(t,r.text),"");break;case"gap":n.push("");break;case"p":n.push(S(t,r.text));break;case"bullet":n.push(`${w(t,"\u2022")} ${S(t,r.text)}`);break}return n.join(`
|
|
31
|
+
`).replace(/^\n+/,"").trimEnd()}pe();G();pe();G();import Zs from"node:fs";function xt(){try{let e=Zs.readFileSync(_r,"utf8"),t=JSON.parse(e);return!t||typeof t.token!="string"||!t.token.trim()?null:{token:t.token.trim(),relayUrl:typeof t.relayUrl=="string"?t.relayUrl.trim():void 0}}catch{return null}}function ft(e){te(),Zs.writeFileSync(_r,JSON.stringify({token:e.token.trim(),...e.relayUrl?{relayUrl:e.relayUrl.trim()}:{}},null,2)+`
|
|
32
|
+
`,{mode:384})}function lo(){try{Zs.unlinkSync(_r)}catch{}}function Lf(){return process.env.OMNISH_TOKEN?.trim()||process.env.OMNISH_TUNNEL_TOKEN?.trim()||process.env.OMNISH_DEVICE_TOKEN?.trim()||""}function ht(){let e=Lf();if(e)return e;let t=$().platformToken.trim();return t||(xt()?.token??"")}function ei(e){let t=process.env.OMNISH_PLATFORM_URL?.trim()||process.env.OMNISH_COMM_LAYER_URL?.trim()||process.env.OMNISH_TUNNEL_RELAY?.trim();if(t)return t.replace(/\/$/,"");let n=$().tunnelRelayUrl.trim();if(n)return n.replace(/\/$/,"");let r=xt()?.relayUrl?.trim();return r?r.replace(/\/$/,""):e.replace(/\/$/,"")}function rt(e){return ei(e)}var $e="https://tunnel.omnish.dev";function co(e){return(e&4)!==0}function ti(e){return(e&2)!==0}var Yl={error:3,warn:2,info:1};function Ql(e,t){let n=Yl[t];return e.filter(r=>Yl[r.severity]>=n)}function Rt(e,t={}){let n=[],r=t.gatewayMode??e.gatewayMode,o=r==="whatsapp"||r==="both",s=r==="telegram"||r==="both";if(e.allowFrom.includes("*")&&n.push({severity:"error",code:"allow-wildcard",message:'allowFrom contains "*" \u2014 this must never be used; remove it from config immediately.',detail:`Edit ${_} and delete wildcard entries.`,fixHint:`Edit ${_} and remove any "*" entries from allowFrom.`}),o&&e.allowFrom.length===0&&n.push({severity:"warn",code:"allow-wa-empty",message:"allowFrom is empty \u2014 no WhatsApp identity can run commands.",detail:"Add your number: omnish allow +<E164>",fixHint:"omnish allow +<your_E164_number>"}),s&&e.telegramAllowFrom.length===0&&n.push({severity:"warn",code:"allow-tg-empty",message:"telegramAllowFrom is empty \u2014 no Telegram user can run commands.",detail:"Add your user id: omnish allow tg:<id>",fixHint:"omnish allow tg:<your_telegram_user_id>"}),e.recipesAllowDangerousBuiltins&&n.push({severity:"warn",code:"recipes-dangerous",message:"recipesAllowDangerousBuiltins is enabled \u2014 built-in recipe helpers may add permissive Claude Code flags.",fixHint:`Set recipesAllowDangerousBuiltins to false in ${_} unless you trust every recipe.`}),e.serviceInstallFromChat&&n.push({severity:"warn",code:"service-install-from-chat",message:"serviceInstallFromChat is enabled \u2014 allowlisted users can run /service install and write user-level systemd or LaunchAgent units.",fixHint:`Set serviceInstallFromChat to false in ${_} unless you trust every allowlisted identity.`}),!ql.isAbsolute(e.shell))n.push({severity:"warn",code:"shell-not-absolute",message:`Shell path is not absolute: ${e.shell}`,fixHint:`Set "shell" to an absolute path (e.g. /bin/bash) in ${_}.`});else try{$t.existsSync(e.shell)||n.push({severity:"error",code:"shell-missing",message:`Configured shell does not exist: ${e.shell}`,fixHint:`Install the shell or update "shell" in ${_} to a valid binary.`})}catch{n.push({severity:"warn",code:"shell-stat-failed",message:`Could not verify shell path: ${e.shell}`,fixHint:`Check permissions and that "shell" in ${_} points to a real file.`})}if(e.fileReceiveRootMode==="fixed"){let a=e.fileReceiveRootPath.trim();a?ql.isAbsolute(a)||n.push({severity:"error",code:"receive-fixed-not-absolute",message:`fileReceiveRootPath must be absolute when fileReceiveRootMode is "fixed": ${a}`,fixHint:`Use an absolute path for fileReceiveRootPath in ${_}.`}):n.push({severity:"error",code:"receive-fixed-empty",message:'fileReceiveRootMode is "fixed" but fileReceiveRootPath is empty.',fixHint:`Set fileReceiveRootPath to an absolute directory in ${_}, or change fileReceiveRootMode.`})}if(Ht.platform!=="win32"){try{if($t.existsSync(_)){let d=$t.statSync(_);(co(d.mode)||ti(d.mode))&&n.push({severity:"warn",code:"config-permissions",message:"config.json is accessible to users other than the owner (group/world).",detail:`Recommended: chmod 600 ${_}`,fixHint:`chmod 600 ${_}`})}}catch{}(typeof Ht.getuid=="function"?Ht.getuid():null)===0&&n.push({severity:"warn",code:"run-as-root",message:"Process is running as root \u2014 allowlisted remote access has full system privileges.",fixHint:"Run omnish as a non-root user (systemd user service, container user, etc.)."});let l=!1;try{if($t.existsSync(H)){let d=$t.statSync(H);(co(d.mode)||ti(d.mode))&&(l=!0,n.push({severity:"warn",code:"data-dir-permissive",message:"Omnish data directory is accessible to group or other users \u2014 auth, jobs, and logs may be exposed.",detail:`Recommended: chmod 700 ${H}`,fixHint:`chmod 700 ${H}`}))}}catch{}try{if(!l&&$t.existsSync(tt)){let d=$t.statSync(tt);(co(d.mode)||ti(d.mode))&&n.push({severity:"warn",code:"jobs-dir-permissive",message:"Jobs log directory is accessible to group or other users \u2014 command output may leak.",detail:`Recommended: chmod 700 ${tt}`,fixHint:`chmod 700 ${tt}`})}}catch{}try{if($t.existsSync(ne)){let d=$t.statSync(ne);co(d.mode)&&n.push({severity:"warn",code:"auth-dir-world-readable",message:"WhatsApp auth directory is readable by others \u2014 session material may be exposed.",detail:`Recommended: chmod 700 ${ne}`,fixHint:`chmod 700 ${ne}`})}}catch{}}return(typeof Ht.env.TELEGRAM_BOT_TOKEN=="string"?Ht.env.TELEGRAM_BOT_TOKEN.trim():"")&&n.push({severity:"info",code:"telegram-token-env",message:"TELEGRAM_BOT_TOKEN is set in the environment; it overrides config.json until unset.",fixHint:"Unset TELEGRAM_BOT_TOKEN in the shell/service env if you want config.json to apply."}),s&&!ve(e)&&n.push({severity:"error",code:"telegram-no-token",message:"Telegram is enabled for this gateway but no bot token is configured.",detail:"Set telegramBotToken in config or TELEGRAM_BOT_TOKEN.",fixHint:`Set telegramBotToken in ${_} or export TELEGRAM_BOT_TOKEN=<token>.`}),e.clusterEnabled&&s&&n.push({severity:"info",code:"cluster-telegram-tokens",message:"Cluster mode with Telegram: use a distinct BotFather bot/token per omnish host if several gateways run Telegram \u2014 the same token cannot be long-polled twice.",fixHint:"Create separate bots for each machine or run Telegram on fewer hosts."}),e.platformToken.trim()&&n.push({severity:"info",code:"platform-token-config",message:"platformToken is stored in config.json (same trust boundary as your shell account).",fixHint:"Use OMNISH_TOKEN in the environment for CI/ephemeral hosts; env overrides config."}),(Ht.env.OMNISH_TOKEN?.trim()||Ht.env.OMNISH_TUNNEL_TOKEN?.trim()||Ht.env.OMNISH_DEVICE_TOKEN?.trim())&&n.push({severity:"info",code:"platform-token-env",message:"OMNISH_TOKEN (or OMNISH_TUNNEL_TOKEN / OMNISH_DEVICE_TOKEN) is set in the environment.",fixHint:"Unset platform token env vars if you want config.json / tunnel-auth.json to apply."}),e.tunnelEnabled&&(n.push({severity:"warn",code:"tunnel-enabled",message:"Chat tunneling is enabled \u2014 allowlisted users can publish public URLs to local HTTP/TCP services.",fixHint:"Disable tunnelEnabled or restrict allowlists if you do not want remote tunnel control."}),ht()||n.push({severity:"warn",code:"tunnel-no-token",message:"Chat tunneling is enabled but no tunnel token is configured.",fixHint:"Run `omnish tunnel login` or set OMNISH_TUNNEL_TOKEN for the gateway process."})),e.tunnelRelayUrl.trim()&&e.tunnelRelayUrl.trim()!==$e&&n.push({severity:"info",code:"tunnel-relay-custom",message:`Custom tunnel relay configured: ${e.tunnelRelayUrl.trim()}`,fixHint:"Use the default relay only if you trust the operator of that endpoint."}),n}function lr(e){return e.some(t=>t.severity==="error")}function Of(e,t){switch(t){case"error":return ao(e,"[ERROR]");case"warn":return me(e,"[WARN]");case"info":return w(e,"[INFO]")}}function ni(e,t,n,r){if(r.length!==0){t.push(J(e,`${n} (${r.length}):`));for(let o of r)t.push(` ${Of(e,o.severity)} ${w(e,`${o.code}:`)} ${S(e,o.message)}`),o.detail&&t.push(` ${w(e,o.detail)}`),o.fixHint&&t.push(` ${w(e,`Fix: ${o.fixHint}`)}`);t.push("")}}function ri(e,t){if(e.length===0)return[`${Ce(t,"Security check:")} ${S(t,"no issues reported by automated rules.")}`,"",w(t,"Note: allowlisted remote shell access is still equivalent to sharing credentials with those identities.")].join(`
|
|
33
|
+
`);let n=e.filter(a=>a.severity==="error"),r=e.filter(a=>a.severity==="warn"),o=e.filter(a=>a.severity==="info"),i=[`${Ce(t,"Security check:")} `+S(t,`${n.length} error(s), ${r.length} warning(s), ${o.length} note(s).`),""];return ni(t,i,"Errors",n),ni(t,i,"Warnings",r),ni(t,i,"Notes",o),i.push(w(t,"Allowlisted identities can run commands as this user; treat them like passwords.")),i.join(`
|
|
34
|
+
`).trimEnd()}function oi(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 Vl(e){return`${JSON.stringify({findings:e,summary:oi(e)},null,2)}
|
|
35
|
+
`}function Xl(e,t){let n=e.filter(a=>a.severity==="error").length,r=e.filter(a=>a.severity==="warn").length,o=e.filter(a=>a.severity==="info").length;if(n===0&&r===0&&o===0)return"security: ok (no automated findings)";let s=[];n&&s.push(`${n} error(s)`),r&&s.push(`${r} warning(s)`),o&&s.push(`${o} note(s)`);let i=t??"run `omnish security` for details";return`security: ${s.join(", ")} \u2014 ${i}`}function Zl(e,t,n){let r=e.filter(l=>l.severity==="error").length,o=e.filter(l=>l.severity==="warn").length,s=e.filter(l=>l.severity==="info").length;if(r===0&&o===0&&s===0)return`${Ce(t,"security:")} ${S(t,"ok (no automated findings)")}`;let i=[];r&&i.push(ao(t,`${r} error(s)`)),o&&i.push(me(t,`${o} warning(s)`)),s&&i.push(w(t,`${s} note(s)`));let a=n??"run `omnish security` for details";return`${Ce(t,"security:")} ${i.join(", ")} ${w(t,`\u2014 ${a}`)}`}var Re="- ";function p(e){return{wa:e,tg:e}}function ue(e,t){return{wa:e,tg:t,tgHtml:!0}}function ke(e,t){return t==="whatsapp"?{text:e.wa}:e.tgHtml?{text:e.tg,parseModeHtml:!0}:{text:e.tg}}function q(e){return e.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">")}function Me(e){return e.replace(/[*_~`]/g,t=>({"*":"\u2217",_:"\uFF3F","~":"\u02DC","`":"\u2032"})[t]??t)}function Nf(e){let t=[];for(let n of e)switch(n.kind){case"title":t.push("",`*${n.text}*`,"");break;case"sub":t.push("",`_${n.text}_`,"");break;case"gap":t.push("");break;case"p":t.push(n.text);break;case"bullet":t.push(`${Re}${n.text}`);break}return t.join(`
|
|
36
|
+
`).replace(/^\n+/,"").trimEnd()}function _f(e){let t=[];for(let n of e)switch(n.kind){case"title":t.push("",`<b>${q(n.text)}</b>`,"");break;case"sub":t.push("",`<i>${q(n.text)}</i>`,"");break;case"gap":t.push("");break;case"p":t.push(q(n.text));break;case"bullet":t.push(`\u2022 ${q(n.text)}`);break}return t.join(`
|
|
37
|
+
`).replace(/^\n+/,"").trimEnd()}function Z(e){return{wa:Nf(e),tg:_f(e),tgHtml:!0}}function xn(e){return[{kind:"title",text:"Omnish \u2014 quick help"},{kind:"p",text:"Per-chat shell cwd is stored under your data dir (see /wa help)."},{kind:"gap"},{kind:"sub",text:"Run commands"},{kind:"bullet",text:`${e.commandPrefix}<command> \u2014 sync shell in session cwd (timeout ${e.syncTimeoutMs} ms)`},{kind:"bullet",text:`${e.commandPrefix}cd <dir> \u2014 change session cwd (${e.commandPrefix}cd alone \u2192 home)`},{kind:"bullet",text:"!!start | !!stop \u2014 free shell (plain \u2192 sync shell only when no focused /apps session); space optional"},{kind:"bullet",text:"/bg <cmd> \u2014 background job; optional -n name; /jobs, /log, /tail, /kill (id or name)"},{kind:"bullet",text:e.tunnelEnabled?"/tunnel login|logout|status|http|tcp \u2014 login/status anytime; /tunnels needs tunnelEnabled":"/tunnel expose/list off until tunnelEnabled true (login/status still work)"},{kind:"bullet",text:"/apps \u2026 \u2014 interactive app sessions (/apps help)"},{kind:"bullet",text:"/send selectors \u2014 host files \u2192 chat (/file); caption: selectors -- note"},{kind:"bullet",text:"/files \u2014 full help for /send and saving inbound media"},{kind:"bullet",text:"/receive \u2014 set this chat\u2019s inbound folder (e.g. session cwd)"},{kind:"bullet",text:"/computers \xB7 /pcs \xB7 /c \u2014 chat-driven cluster (/c here to take over, /c help)"},{kind:"bullet",text:"/config \u2014 view or change server settings (/config help, /config keys)"},{kind:"bullet",text:"/service \u2014 background service status, install hints, logs (/service help)"},{kind:"bullet",text:"/shortcut \u2014 this chat & shared shortcuts (/shortcut help); !name or /name expands chat override first, then shared"},{kind:"bullet",text:"/run \u2014 recipe-based task runs (/r); per-chat or gateway-shared /run add \u2014 /run list, /run help"},{kind:"bullet",text:"/cowork | /cw \u2014 scheduled & on-demand shell tasks; logs to ~/Cowork/\u2026 (/cowork help)"},{kind:"bullet",text:"/watch \u2014 OS event eye: fs/pkg/svc alerts; pause/stop/rm, excludes (/watch help; docs/features/watch.md)"},{kind:"gap"},{kind:"sub",text:"Setup & gateway"},{kind:"bullet",text:"/wa help \u2014 WhatsApp link, allowlist"},{kind:"bullet",text:"/tg help \u2014 Telegram; /tg token <paste>"},{kind:"bullet",text:"/reload | /restart \u2014 apply config"},{kind:"bullet",text:"/updates \u2014 npm latest + optional notice URL; /updates cached \u2014 last snapshot"},{kind:"bullet",text:"/security \u2014 posture report; /security summary; /security tips; /security help"},{kind:"bullet",text:"/gateway | /gw | /mode \u2014 show or set gatewayMode"},{kind:"bullet",text:"/allow +E164 | /allow tg:id \u2014 allowlist; /deny \u2026; /allowlist"},{kind:"bullet",text:"/help \u2014 this message"},{kind:"gap"},{kind:"sub",text:"Terms"},{kind:"bullet",text:"Gateway = omnish on this host; shell runs here. Standalone = messengers on this host; attached = communication layer routes chat here."},{kind:"bullet",text:'Communication layer (when shipped) = link chats once, device token on this CLI; shell stays here. Standalone = no layer. /service "platform" = OS.'}]}function ec(e){let t=e.serviceInstallFromChat?[{kind:"bullet",text:"/service install \u2014 user-level systemd (Linux) or LaunchAgent (macOS)"},{kind:"bullet",text:"/service uninstall \u2014 remove that unit"}]:[{kind:"bullet",text:"/service install / uninstall \u2014 off by default. Enable: /config set serviceInstallFromChat true (same trust as shell)"}];return[{kind:"title",text:"Service and boot"},{kind:"p",text:"Inspect the gateway, get OS-specific install steps, or (if enabled) write the user service from chat. Same from the host terminal: omnish service help."},{kind:"p",text:'"platform" in status means your OS (Linux/macOS/Windows), not a hosted omnish account.'},{kind:"bullet",text:"/service status \u2014 platform, data dir, pidfile, node + entry script"},{kind:"bullet",text:"/service instructions \u2014 copy-paste commands for this machine"},{kind:"bullet",text:"/service logs [n] \u2014 tail default gateway log (default 80 lines)"},...t,{kind:"gap"},{kind:"p",text:"See https://omnish.dev and docs/guides/background-and-boot.md."}]}function tc(){return[{kind:"title",text:"Chat config"},{kind:"p",text:`Same trust as shell \u2014 allowlisted senders can change many keys saved to ${_}.`},{kind:"gap"},{kind:"bullet",text:"/config show \u2014 full snapshot (telegramBotToken masked)"},{kind:"bullet",text:"/config get <key> \u2014 single field"},{kind:"bullet",text:"/config set <key> <value> \u2014 values can be quoted for paths with spaces"},{kind:"bullet",text:"/config keys \u2014 allowed keys for /config set (includes tunnel*, webhook*, watch*, chatLlm*, cluster*, \u2026)"},{kind:"gap"},{kind:"p",text:"After edits: /reload while omnish run is active (needed for gatewayMode and telegramBotToken)."}]}function si(){return[{kind:"title",text:"Gateway mode"},{kind:"p",text:"Which transports omnish uses:"},{kind:"bullet",text:"whatsapp (wa, w) \u2014 WhatsApp DMs only"},{kind:"bullet",text:"telegram (tg, t) \u2014 Telegram bot only"},{kind:"bullet",text:"both (all, b) \u2014 WhatsApp + Telegram"},{kind:"gap"},{kind:"sub",text:"Commands"},{kind:"bullet",text:"/gateway \u2014 current mode + hints (includes last update snapshot if any)"},{kind:"bullet",text:"/gateway telegram \u2014 save (auto-reloads Telegram if gateway is up)"},{kind:"bullet",text:"/gw both \u2014 short form; /mode wa \u2014 same"},{kind:"gap"},{kind:"p",text:`Config: ${_}`}]}function nc(e){let t=["*Gateway status*","",`Mode: *${e.gatewayMode}*`,"- whatsapp \u2014 WhatsApp only","- telegram \u2014 Telegram only","- both \u2014 both channels","",`WhatsApp auth: ${e.authPresent?"present":"missing (omnish link)"}`,`Telegram token: ${e.tokenSet?"set":"not set"}`,`Allowlists: ${e.allowN} WA \xB7 ${e.tgAllowN} Telegram`,""];e.updateBrief&&t.push(`Updates: ${Me(e.updateBrief)}`,""),t.push("Change: /gateway both \xB7 /gw tg","/updates \u2014 npm + optional notice URL",`Config: ${Me(_)}`);let n=["<b>Gateway status</b>","",`<b>${q(e.gatewayMode)}</b> <i>current mode</i>`,"\u2022 whatsapp \u2014 WhatsApp only","\u2022 telegram \u2014 Telegram only","\u2022 both \u2014 both channels","",`${e.authPresent?"\u2713":"\u26A0"} WhatsApp auth: ${e.authPresent?"present":"missing \u2014 run omnish link"}`,`${e.tokenSet?"\u2713":"\u26A0"} Telegram token: ${e.tokenSet?"set":"not set"}`,`Allowlists: ${e.allowN} WA \xB7 ${e.tgAllowN} Telegram`,""];return e.updateBrief&&n.push(`<b>Updates</b> ${q(e.updateBrief)}`,""),n.push("<i>Change:</i> /gateway both \xB7 /gw tg","<i>/updates</i> \u2014 npm + optional notice URL",q(`Config: ${_}`)),ue(t.join(`
|
|
11
38
|
`),n.join(`
|
|
12
|
-
`))}function
|
|
39
|
+
`))}function cr(e){let t=["*Update check*","",`Running: *${Me(e.runningVersion)}*`,`Checked (UTC): ${e.checkedAtIso}`,`npm package: ${Me(e.registryPackage)}`];e.registryError?t.push("",`Registry: ${Me(e.registryError)}`):e.registryLatest&&t.push("",e.updateAvailable?`*Newer version on npm:* ${Me(e.registryLatest)} (upgrade when ready).`:`npm latest: ${Me(e.registryLatest)} (this install is current or newer).`),e.infoError?t.push("",`Notice URL: ${Me(e.infoError)}`):e.infoMessage&&(t.push("",`Notice: ${Me(e.infoMessage)}`),e.infoLink&&t.push(Me(e.infoLink))),t.push("","Config: updateCheckEnabled, updateCheckIntervalMs, updateCheckPackageName, updateInfoUrl");let n=["<b>Update check</b>","",`<b>Running</b> <code>${q(e.runningVersion)}</code>`,`<b>Checked (UTC)</b> ${q(e.checkedAtIso)}`,`<b>npm package</b> <code>${q(e.registryPackage)}</code>`];return e.registryError?n.push("",`<b>Registry</b> ${q(e.registryError)}`):e.registryLatest&&n.push("",e.updateAvailable?`<b>Newer on npm</b> <code>${q(e.registryLatest)}</code>`:`<b>npm latest</b> <code>${q(e.registryLatest)}</code> (current or newer here)`),e.infoError?n.push("",`<b>Notice URL</b> ${q(e.infoError)}`):e.infoMessage&&(n.push("",`<b>Notice</b> ${q(e.infoMessage)}`),e.infoLink&&n.push(q(e.infoLink))),n.push("","<i>Config keys:</i> updateCheckEnabled, updateCheckIntervalMs, updateCheckPackageName, updateInfoUrl"),ue(t.join(`
|
|
13
40
|
`),n.join(`
|
|
14
|
-
`))}function
|
|
41
|
+
`))}function rc(e){return[{kind:"title",text:"WhatsApp setup"},{kind:"p",text:"Do these on the machine running omnish (QR is shown in the terminal, not in chat)."},{kind:"gap"},{kind:"sub",text:"Steps"},{kind:"bullet",text:"Install omnish; build native deps (node-pty, Baileys)."},{kind:"bullet",text:`Data dir: ${H} (override: OMNISH_HOME). Auth: <data-dir>/auth/`},{kind:"bullet",text:"Run: omnish link \u2014 scan QR in WhatsApp \u2192 Linked devices."},{kind:"bullet",text:"Allow your number: omnish allow +<E164>"},{kind:"bullet",text:`Set gatewayMode to "whatsapp" or "both" in ${_}`},{kind:"bullet",text:"Run: omnish run \u2014 then use !cmd or /help here."},{kind:"gap"},{kind:"sub",text:"Troubleshooting"},{kind:"bullet",text:"Denied in logs but allowlist OK: may be LID mapping \u2014 try OMNISH_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 oc(e){let t=!!ve(e);return[{kind:"title",text:"Telegram setup"},{kind:"p",text:"Configure on the host that runs omnish."},{kind:"gap"},{kind:"sub",text:"Steps"},{kind:"bullet",text:"@BotFather \u2192 /newbot \u2014 copy the API token."},{kind:"bullet",text:`Token: /tg token <paste>, or ${_}, or TELEGRAM_BOT_TOKEN env (env wins).`},{kind:"bullet",text:'Your numeric id: @userinfobot / @getidsbot, or logs on "telegram denied".'},{kind:"bullet",text:"Allow: omnish allow tg:<user_id>"},{kind:"bullet",text:`gatewayMode: "telegram" or "both" in ${_}; list ids in telegramAllowFrom.`},{kind:"bullet",text:"omnish run \u2014 then DM the bot with !cmd or /help."},{kind:"bullet",text:"After edits: /reload or /gateway both (saves + reloads when running)."},{kind:"gap"},{kind:"sub",text:"Notes"},{kind:"bullet",text:"Private text DMs only; groups ignored. Token stays secret."},{kind:"bullet",text:"/tg token stores token in config; chat history may retain it \u2014 prefer SSH/editor if worried."},{kind:"gap"},{kind:"p",text:`Token on host: ${t?"set (env or config)":"not set \u2014 add telegramBotToken or TELEGRAM_BOT_TOKEN"}`},{kind:"p",text:`gatewayMode: ${e.gatewayMode} \xB7 telegramAllowFrom: ${e.telegramAllowFrom.length} entries`}]}function sc(e){let t=["*Allowlists*",""],n=["<b>Allowlists</b>",""];for(let r of e){let o=r.items.length?r.items.join(", "):"(none)";t.push(`*${r.label}* (${r.items.length})`,Me(o),""),n.push(`<b>${q(r.label)}</b> (${r.items.length})`,q(o),"")}return ue(t.join(`
|
|
15
42
|
`).trimEnd(),n.join(`
|
|
16
|
-
`).trimEnd())}function
|
|
43
|
+
`).trimEnd())}function ii(e){let t=[{label:"allowFrom (WhatsApp)",items:e.allowFrom},{label:"telegramAllowFrom",items:e.telegramAllowFrom}],n=["*Allowlist updated*","","Saved to config.",""],r=["<b>Allowlist updated</b>","","Saved to config.",""];for(let o of t){let s=o.items.length?o.items.join(", "):"(none)";n.push(`*${o.label}* (${o.items.length})`,Me(s),""),r.push(`<b>${q(o.label)}</b> (${o.items.length})`,q(s),"")}return ue(n.join(`
|
|
17
44
|
`).trimEnd(),r.join(`
|
|
18
|
-
`).trimEnd())}function
|
|
19
|
-
`),n=["<b>Token saved</b>","","telegramBotToken written to config (not echoed).",
|
|
45
|
+
`).trimEnd())}function ic(e){let t=["*Token saved*","","telegramBotToken written to config (not echoed).",`Config: ${Me(_)}`,...e.flatMap(r=>["",r]),"","Send /reload so the gateway picks it up."].join(`
|
|
46
|
+
`),n=["<b>Token saved</b>","","telegramBotToken written to config (not echoed).",q(`Config: ${_}`),...e.map(r=>`<i>${q(r)}</i>`),"","Send /reload so the gateway picks it up."].join(`
|
|
20
47
|
`)+`
|
|
21
|
-
`;return
|
|
48
|
+
`;return ue(t.trimEnd(),n.trimEnd())}function ac(){return Z([{kind:"title",text:"That does not look like a bot token"},{kind:"p",text:"Expected from @BotFather: one token, no spaces, like 123456789:AA_heG\u2026"},{kind:"bullet",text:"Example: /tg token 123456789:AAAbcd\u2026"}])}function lc(e,t,n){let r=t?`
|
|
22
49
|
|
|
23
50
|
${t}`:n?`
|
|
24
51
|
|
|
25
52
|
Start omnish run on the host to apply (or /reload from a running gateway).`:"",o=t?`
|
|
26
53
|
|
|
27
|
-
${
|
|
54
|
+
${q(t)}`:n?`
|
|
28
55
|
|
|
29
56
|
Start omnish run on the host to apply (or /reload from a running gateway).`:"",s=`*gatewayMode saved*
|
|
30
57
|
|
|
31
|
-
"${
|
|
32
|
-
${
|
|
58
|
+
"${Me(e)}"
|
|
59
|
+
${Me(_)}${r}`,i=`<b>gatewayMode saved</b>
|
|
33
60
|
|
|
34
|
-
<code>${
|
|
35
|
-
${
|
|
36
|
-
`),a=["<b>Shortcuts</b>",o,"",...e.map(l=>{let
|
|
37
|
-
`);return
|
|
61
|
+
<code>${q(e)}</code>
|
|
62
|
+
${q(_)}${o}`;return ue(s,i)}function cc(){return Z([{kind:"title",text:"/allow \u2014 add to allowlist"},{kind:"bullet",text:"/allow +<E164> \u2014 WhatsApp (country code, no spaces)"},{kind:"bullet",text:"/allow tg:<user_id> \u2014 Telegram numeric id"},{kind:"gap"},{kind:"p",text:"Examples: /allow +15551234567 \xB7 /allow tg:987654321"}])}function uc(){return Z([{kind:"title",text:"/deny \u2014 remove from allowlist"},{kind:"p",text:"Same forms as /allow: +E164 or tg:id"}])}function dc(){return Z([{kind:"title",text:"/bg \u2014 background job"},{kind:"p",text:"Usage: /bg [flags] <shell command>"},{kind:"bullet",text:"Optional name: /bg -n mybuild npm run build \xB7 /bg --name mybuild \u2026 \xB7 /bg --name=mybuild \u2026"},{kind:"bullet",text:"Notify on completion: /bg --notify <cmd> or /bg -N <cmd> \u2014 sends a message when the job finishes (with exit status)."},{kind:"bullet",text:"Combine flags: /bg -N -n deploy git pull && docker compose up -d"},{kind:"bullet",text:"Then use /log mybuild, /tail mybuild, /kill mybuild (or the 8-char id). Names: letters, digits, . _ - up to 64 chars."},{kind:"bullet",text:"Example: /bg sleep 30 && echo done"}])}function ai(){return Z([{kind:"title",text:"/send \u2014 push a host file to this chat"},{kind:"p",text:"Usage: /send <selectors> [-- caption] \u2014 selectors resolve from session cwd."},{kind:"bullet",text:"/send ./photo.png"},{kind:"bullet",text:"/send ./clip1.mp4,./clip2.mp4 -- Launch set"},{kind:"bullet",text:"/send **/*.mp4"},{kind:"bullet",text:"/send /abs/path/report.pdf -- Q4 draft"},{kind:"bullet",text:"Selectors: file1,file2 | *.ext | **/*.ext"},{kind:"bullet",text:"Alias: /file \u2026"},{kind:"gap"},{kind:"p",text:"Caps: fileSendMaxBytes / fileReceiveMaxBytes (0 = no omnish cap). Where inbound media is saved: fileReceiveRootMode + fileReceiveRootPath / fileInboxSubdir \u2014 send /files for details."}])}function li(){return Z([{kind:"title",text:"Files \u2014 send & receive"},{kind:"sub",text:"Host \u2192 chat"},{kind:"bullet",text:"/send <selectors> or /file \u2026 \u2014 selectors support file1,file2, *.ext, **/*.ext from this chat\u2019s session cwd (same as ! shell); optional caption after -- "},{kind:"bullet",text:"fileSendMaxBytes in config caps outbound size (0 = no omnish cap)."},{kind:"gap"},{kind:"sub",text:"Chat \u2192 host"},{kind:"bullet",text:"Send a photo, video, document, audio, etc. in this DM; omnish saves it and replies with Saved: <path> (or an error)."},{kind:"bullet",text:"Folders: <root>/<peer>/<YYYY-MM-DD>/<filename> \u2014 root comes from config below."},{kind:"bullet",text:"fileReceiveRootMode: downloads (home/Downloads/Omnish) \xB7 omnishData (data dir + fileInboxSubdir) \xB7 sessionCwd \xB7 processCwd \xB7 fixed (needs absolute fileReceiveRootPath)."},{kind:"bullet",text:"fileReceiveMaxBytes \u2014 inbound size cap (0 = no omnish cap)."},{kind:"gap"},{kind:"p",text:`Edit config on the host (${_}) then use /reload. Tip: omnish status shows the data directory.`},{kind:"gap"},{kind:"p",text:"Per-chat folder: /receive here saves inbound files under your session cwd (!cd); /receive default clears."}])}function ci(){return Z([{kind:"title",text:"/receive \u2014 inbound files for this chat"},{kind:"p",text:"Stored on the host with your session (sessions.json). Does not edit config.json."},{kind:"bullet",text:"/receive \u2014 show current setting and resolved save folder"},{kind:"bullet",text:"/receive here \u2014 save uploads under this chat\u2019s session cwd (same as !cd); subfolders: peer / date / file"},{kind:"bullet",text:"/receive default \u2014 clear per-chat rule; use global fileReceiveRootMode (/files, config.json)"},{kind:"gap"},{kind:"p",text:"Aliases: here = cwd = session = dir \xB7 default = global = reset"}])}function pc(e,t){let n=Bl(t),r=oe(t),o="";try{o=Ft(e,t)}catch(s){o=`(${String(s)})`}return Z(n==="sessionCwd"?[{kind:"title",text:"/receive \u2014 this chat"},{kind:"p",text:"Per-chat: inbound media saves under your session folder (updated when you !cd)."},{kind:"bullet",text:`Session cwd: ${r.cwd}`},{kind:"bullet",text:`Next file root: ${o}`},{kind:"p",text:"Send /receive default to follow server config instead."}]:[{kind:"title",text:"/receive \u2014 this chat"},{kind:"p",text:"Per-chat override: off \u2014 using global fileReceiveRootMode from config."},{kind:"bullet",text:`Global mode: ${e.fileReceiveRootMode}`},{kind:"bullet",text:`Next file root: ${o}`},{kind:"p",text:"Send /receive here to pin saves to your current session folder."}])}function mc(){return Z([{kind:"title",text:"Unknown /wa command"},{kind:"p",text:"Send /wa help for setup."}])}function fc(){return Z([{kind:"title",text:"Unknown /tg command"},{kind:"p",text:"Send /tg help or /tg token <botfather_token>."}])}function hc(){return Z([{kind:"title",text:"Free shell mode on"},{kind:"p",text:"Plain messages run as sync shell (no command prefix)."},{kind:"bullet",text:"Send !!stop to turn off."},{kind:"bullet",text:"Apps: plain DMs no longer go to the focused app \u2014 use >name text or /apps send."}])}function gc(e){return Z([{kind:"title",text:"Unknown command"},{kind:"p",text:`Try /help or ${e.commandPrefix}<command>.`},{kind:"gap"},...xn(e)])}function yc(e){return Z([{kind:"title",text:"No command matched"},{kind:"p",text:`Use ${JSON.stringify(e.commandPrefix)}, a slash command, or /apps help.`},{kind:"gap"},...xn(e)])}function wc(){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"},...si()];return Z(e)}function ui(){return[{kind:"title",text:"Shortcuts (this gateway)"},{kind:"p",text:"Stored per-chat or shared for every chat on this gateway. Expansion: this chat wins, then shared. Bare token only \u2014 for task injection use /run recipes."},{kind:"p",text:"Scope flags: -g or --global = shared on this gateway; -p or --chat = private to this chat."},{kind:"gap"},{kind:"sub",text:"Manage"},{kind:"bullet",text:"/shortcut add <name> <command\u2026> \u2014 private by default; add -g|--global to share on this gateway"},{kind:"bullet",text:"/shortcut add -p|--chat <name> <command\u2026> \u2014 explicit private (this chat)"},{kind:"bullet",text:"/shortcut set <name> <command\u2026> \u2014 overwrite in chosen bucket; scope-only (same line): /shortcut set -g <name> or /shortcut set <name> -g (share); /shortcut set -p <name> or /shortcut set <name> -p (private)"},{kind:"bullet",text:"/shortcut list \u2014 merged view (/shortcuts); list --chat | -p | list --global | -g"},{kind:"bullet",text:"/shortcut show <name>; show --global|-g|--chat|-p to read one bucket"},{kind:"bullet",text:"/shortcut remove <name> \u2014 private bucket; remove --global|-g drops shared \xB7 rm, del \xB7 --chat|-p"},{kind:"bullet",text:"/shortcut <name> publish \u2014 share to online catalog (platform login)"},{kind:"bullet",text:"/shortcut online trending | show <publicId> | <publicId> download \u2014 shortcuts only"},{kind:"bullet",text:"/alias \u2026 \u2014 same as /shortcut \u2026 (including /aliases \u2026)"},{kind:"gap"},{kind:"sub",text:"Run"},{kind:"bullet",text:"!name \u2014 expands once to the saved line (e.g. !cd \u2026 updates session cwd)"},{kind:"bullet",text:"/name \u2014 same expansion for slash-style navigation"},{kind:"p",text:"Shortcut bodies may start with !, /, etc.; nested shortcuts are not expanded."}]}function bc(e){if(e.length===0)return p("(no shortcuts \u2014 /shortcut add <name> <command\u2026> or /shortcut add --global <name> <command\u2026>)");let t=e.every(l=>l.scope==="global"),n=e.every(l=>l.scope==="chat"),r=t?"_Shared (every chat)_":n?"_This chat only_":"_This chat + shared_",o=t?"<i>Shared (every chat)</i>":n?"<i>This chat only</i>":"<i>This chat + shared</i>",s=e.some(l=>l.scope==="chat")&&e.some(l=>l.scope==="global"),i=["*Shortcuts*",r,"",...e.map(l=>{let d=s?l.scope==="chat"?"[chat] ":"[global] ":"";return`${Re}\`${d}${l.name}\` \u2192 ${l.body}`})].join(`
|
|
63
|
+
`),a=["<b>Shortcuts</b>",o,"",...e.map(l=>{let u=`${s?l.scope==="chat"?"[chat] ":"[global] ":""}${l.name}`;return`\u2022 <code>${q(u)}</code> \u2192 ${q(l.body)}`})].join(`
|
|
64
|
+
`);return ue(i,a)}function ur(e,t,n="chat"){return p(`Shortcut saved: ${e}
|
|
38
65
|
\u2192 ${t}
|
|
39
|
-
(${n==="global"?"Shared on this gateway (every chat unless this chat overrides the name).":"Stored for this chat only."})`)}function
|
|
66
|
+
(${n==="global"?"Shared on this gateway (every chat unless this chat overrides the name).":"Stored for this chat only."})`)}function kc(e,t="chat"){return p(`Shortcut removed (${t==="global"?"shared":"this chat"}): ${e}`)}function Sc(e){return p(`Unknown shortcut: ${e}`)}function vc(e,t){return p(`Unknown shortcut "${e}" in ${t==="global"?"shared shortcuts":"this chat"}.`)}function di(e,t,n){let r=n?`
|
|
40
67
|
|
|
41
68
|
${n}`:"";return p(`${e}
|
|
42
|
-
\u2192 ${t}${r}`)}function
|
|
69
|
+
\u2192 ${t}${r}`)}function xc(){return[{kind:"title",text:"/run \u2014 recipe templates"},{kind:"sub",text:"Invoke"},{kind:"bullet",text:"/run <name> <task\u2026> \u2014 start attached session (plain DMs go to it until /apps detach)"},{kind:"bullet",text:"/r <name> <task\u2026> \u2014 short alias for /run"},{kind:"gap"},{kind:"sub",text:"Queue"},{kind:"bullet",text:"/run <name> -q <task\u2026> or /run <name> --queue <task\u2026> \u2014 FIFO per chat: head starts immediately; others wait in memory until the head exits cleanly (code=0, signal=0)"},{kind:"bullet",text:"Pending counts only jobs not yet started \u2014 the first queued item becomes Active right away, so /run queue can show Pending: 0 while a queued run is still executing"},{kind:"bullet",text:"/run queue \u2014 active session + recipe, numbered waiting list, paused flag"},{kind:"bullet",text:"/run queue resume \u2014 after a pause or non-clean exit, clear pause and start the next waiting item"},{kind:"bullet",text:"/run queue load <file.json> \u2014 enqueue jobs from JSON (paths relative to session cwd); /run queue load json [\u2026] \u2014 same payload inline; attach a file with caption /run queue load"},{kind:"bullet",text:'Queue JSON: [ { "recipe": "<name>", "task": "<text>" }, \u2026 ] or { "tasks": [ \u2026 ] } (max 64 jobs per load; same rules as /run <name> -q)'},{kind:"gap"},{kind:"sub",text:"Discover"},{kind:"bullet",text:"/run list \u2014 featured, gateway-shared, this chat, host templates; list --chat | -p | list --global | -g"},{kind:"bullet",text:"/run show <name> \u2014 merged resolution; show --global|-g|--chat|-p reads one user bucket"},{kind:"gap"},{kind:"sub",text:"Manage"},{kind:"p",text:"Recipe scope flags: -g or --global = shared on this gateway; -p or --chat = private to this chat."},{kind:"bullet",text:'/run add <name> <command\u2026> [--template "\u2026"] \u2014 this chat; add --global|-g share on this gateway'},{kind:"bullet",text:"/run set <name> <command\u2026> \u2014 overwrite in chosen bucket (--global|-g|--chat|-p); scope-only (same body): /run set -g <name> or /run set <name> -g (share); /run set -p <name> or /run set <name> -p (this chat only)"},{kind:"bullet",text:"/run remove <name> \u2014 this chat storage; remove --global|-g clears gateway-shared \xB7 rm, del \xB7 --chat|-p"},{kind:"gap"},{kind:"sub",text:"Notes"},{kind:"bullet",text:"Commands must reference the task env variable (default `$OMNISH_TASK`)."},{kind:"bullet",text:"Template placeholders `<<<OMNISH_TASK>>>`, `<<<```$OMNISH_TASK```>>>`, and `$OMNISH_TASK` are replaced with task text."},{kind:"bullet",text:"Host overrides file: "+Ur},{kind:"bullet",text:"Built-in Claude recipes omit dangerous flags unless recipesAllowDangerousBuiltins=true."},{kind:"gap"},{kind:"sub",text:"Online catalog"},{kind:"bullet",text:"/run online trending | search <query> | show <publicId> | <publicId> download \u2014 all kinds"},{kind:"bullet",text:"/run <recipe> publish \u2014 share to platform (requires omnish platform login)"}]}function Ff(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 $c(e){let t=["*Recipes*","`/run <name> <task>`",""],n=["<b>Recipes</b>","<code>/run <name> <task></code>",""],r=(o,s)=>{let i=o.description?` \u2014 ${o.description}`:"",a=s&&o.dangerous?" [dangerous flags]":"";t.push(`${Re}${o.name} \u2014 ${o.label??o.name}${i}${a}`);let l=o.description?` \u2014 ${q(o.description)}`:"",d=s&&o.dangerous?q(" [dangerous flags]"):"";n.push(`\u2022 ${q(o.name)} \u2014 ${q(o.label??o.name)}${l}${d}`)};if(e.featured.length>0){t.push("_Featured:_"),n.push("<i>Featured:</i>");for(let o of e.featured)r(o,!0);t.push(""),n.push("")}if(e.shared.length>0){t.push("_Shared (every chat):_"),n.push("<i>Shared (every chat):</i>");for(let o of e.shared)r(o,!1);t.push(""),n.push("")}if(e.yours.length>0){t.push("_This chat:_"),n.push("<i>This chat:</i>");for(let o of e.yours)r(o,!1);t.push(""),n.push("")}if(e.more.length>0){t.push("_More:_"),n.push("<i>More:</i>");for(let o of e.more)r(o,!1)}return e.featured.length===0&&e.shared.length===0&&e.yours.length===0&&e.more.length===0&&(t.push("(no recipes \u2014 add host file or /run add)"),n.push(q("(no recipes \u2014 add host file or /run add)"))),ue(t.join(`
|
|
43
70
|
`).trimEnd(),n.join(`
|
|
44
|
-
`).trimEnd())}function
|
|
45
|
-
`))}function
|
|
46
|
-
`))}function
|
|
71
|
+
`).trimEnd())}function pi(e,t){let n=e.taskEnv??"OMNISH_TASK",r=[`Recipe: ${e.name}`,`Source: ${Ff(e.source)}`,`Label: ${e.label??"(none)"}`];if(e.steps&&e.steps.length>0){r.push(`Type: runbook (${e.steps.length} steps)`);for(let o=0;o<e.steps.length;o++){let s=e.steps[o],i=s.label?` (${s.label})`:"",a=s.continueOnFail?" [continue-on-fail]":"";r.push(` ${o+1}. ${s.cmd}${i}${a}`)}}else r.push(`Task env: ${n}`),r.push(`Command: ${e.command}`);if(e.promptTemplate){r.push(`Template: ${e.promptTemplate.length} chars`);let s=e.promptTemplate.replace(/\s+/g," ").trim().slice(0,200);r.push(`Preview: ${s}${e.promptTemplate.length>200?"\u2026":""}`)}return e.category&&r.push(`Category: ${e.category}`),e.description&&r.push(`Description: ${e.description}`),e.dangerous&&r.push("Note: built-in includes gated dangerous CLI flags."),t&&r.push("",t),p(r.join(`
|
|
72
|
+
`))}function dr(e,t,n="chat"){let r=n==="global"?"Shared on this gateway (every chat unless this chat overrides the name).":"Stored for this chat only.",o=[`Recipe saved: ${e}`,`\u2192 ${t.command}`,`(task env: ${t.taskEnv??"OMNISH_TASK"})`];return t.promptTemplate&&o.push(`Template stored: ${t.promptTemplate.length} chars`),o.push(`(${r})`),p(o.join(`
|
|
73
|
+
`))}function Rc(e,t="chat"){return p(`Recipe removed (${t==="global"?"gateway-shared storage":"this chat"}): ${e}`)}function Cc(e,t){return p(`Unknown recipe "${e}" in ${t==="global"?"gateway-shared storage":"this chat storage"}.`)}function Mc(e,t){let n=t==="global"?"_Gateway-shared recipes_":"_This chat recipes_",r=t==="global"?"<i>Gateway-shared recipes</i>":"<i>This chat recipes</i>";if(e.length===0)return p(t==="global"?"(no gateway-shared recipes \u2014 /run add --global <name> <command\u2026>)":"(no recipes in this chat \u2014 /run add <name> <command\u2026>)");let o=["*Recipes*",n,""],s=["<b>Recipes</b>",r,""];for(let i of e){let a=i.description?` \u2014 ${i.description}`:"";o.push(`${Re}${i.name} \u2014 ${i.label??i.name}${a}`);let l=i.description?` \u2014 ${q(i.description)}`:"";s.push(`\u2022 ${q(i.name)} \u2014 ${q(i.label??i.name)}${l}`)}return ue(o.join(`
|
|
47
74
|
`).trimEnd(),s.join(`
|
|
48
|
-
`).trimEnd())}function
|
|
49
|
-
/run list`)}function
|
|
50
|
-
|
|
51
|
-
`)
|
|
52
|
-
|
|
53
|
-
### `)}function io(e,t){let n=qe(e),r=n.taskEnv??"OMNISH_TASK";return!n.promptTemplate&&vc(n,r)&&(n=xc(n,t)),n}function $c(e){try{let t=ro.readFileSync(Rn,"utf8"),n=JSON.parse(t);if(!n||typeof n!="object")return{};let r={};for(let[o,s]of Object.entries(n)){let i=o.trim().toLowerCase();if(!(!oo.test(i)||so.has(i))&&s&&typeof s=="object"&&typeof s.command=="string"){let l=io(s,e),u=nn(l);if(!u.ok){console.warn(`[omnish] recipes.json: skipping "${i}": ${u.error}`);continue}r[i]=l}}return r}catch{return{}}}function Cc(e){return{claude:{command:'claude -p "$OMNISH_TASK" --allowedTools all --dangerously-skip-permissions',taskEnv:"OMNISH_TASK",label:"Claude Code",description:"Anthropic Claude Code CLI (requires `claude` on PATH)",category:"agents",featured:!0,dangerous:e.recipesAllowDangerousBuiltins},codex:{command:'codex "$OMNISH_TASK"',taskEnv:"OMNISH_TASK",label:"Codex CLI",description:"OpenAI Codex CLI if installed; override in recipes.json",category:"agents",featured:!1},gemini:{command:'gemini -p "$OMNISH_TASK"',taskEnv:"OMNISH_TASK",label:"Gemini CLI",description:"Google Gemini CLI if installed; override in recipes.json",category:"agents",featured:!1},cursor:{command:"agent --yolo --force -p ```Before writing any code:\n1. Analyze the codebase and state your full implementation plan\n2. List every file you will touch\n3. Identify any risks or ambiguities\n4. Then execute the plan step by step, <task> $OMNISH_TASK </task>```",taskEnv:"OMNISH_TASK",label:"Cursor Agent",description:"Cursor Agent if installed; override in recipes.json",category:"agents",featured:!0}}}function qn(e,t,n){for(let[r,o]of Object.entries(t)){let s=r.toLowerCase();!oo.test(s)||so.has(s)||o.command.trim()&&e.set(s,{...o,name:s,source:n})}}function no(e,t){let n=vi(e),r={};for(let[o,s]of Object.entries(n)){let i=io(s,t);nn(i).ok&&(r[o]=i)}return r}function $i(e,t){let n=new Map;return qn(n,Cc(t),"builtin"),qn(n,$c(t),"global"),qn(n,no(Tt,t),"shared"),qn(n,no(e,t),"peer"),n}function Re(e,t,n){let r=n.trim().toLowerCase();return $i(e,t).get(r)}function Ci(e){let t=e.trim();if(/^--global$/i.test(t))return{mode:"global",remainder:""};if(/^-g$/i.test(t))return{mode:"global",remainder:""};if(/^--chat$/i.test(t))return{mode:"chat",remainder:""};if(/^-p$/i.test(t))return{mode:"chat",remainder:""};let n=/^--global\s+/i.exec(t);if(n)return{mode:"global",remainder:t.slice(n[0].length).trimStart()};let r=/^-g\s+/i.exec(t);if(r)return{mode:"global",remainder:t.slice(r[0].length).trimStart()};let o=/^--chat\s+/i.exec(t);if(o)return{mode:"chat",remainder:t.slice(o[0].length).trimStart()};let s=/^-p\s+/i.exec(t);return s?{mode:"chat",remainder:t.slice(s[0].length).trimStart()}:{mode:"resolved",remainder:t}}function ao(e){let t=e.trim();if(/^--global$/i.test(t))return{scope:"global",remainder:"",explicit:!0};if(/^-g$/i.test(t))return{scope:"global",remainder:"",explicit:!0};if(/^--chat$/i.test(t))return{scope:"chat",remainder:"",explicit:!0};if(/^-p$/i.test(t))return{scope:"chat",remainder:"",explicit:!0};let n=/^--global\s+([\s\S]*)$/i.exec(t);if(n?.[1]!==void 0)return{scope:"global",remainder:n[1].trimStart(),explicit:!0};let r=/^-g\s+([\s\S]*)$/i.exec(t);if(r?.[1]!==void 0)return{scope:"global",remainder:r[1].trimStart(),explicit:!0};let o=/^--chat\s+([\s\S]*)$/i.exec(t);if(o?.[1]!==void 0)return{scope:"chat",remainder:o[1].trimStart(),explicit:!0};let s=/^-p\s+([\s\S]*)$/i.exec(t);return s?.[1]!==void 0?{scope:"chat",remainder:s[1].trimStart(),explicit:!0}:{scope:"chat",remainder:t,explicit:!1}}function lo(e){let t=ao(e);return{scope:t.scope,remainder:t.remainder}}function Ri(e){let t=e.trim();return!t||/^-+$/i.test(t)?{filter:"merged"}:/^(?:--global|-g)$/i.test(t)?{filter:"global"}:/^(?:--chat|-p)$/i.test(t)?{filter:"chat"}:{filter:"merged",bad:t}}function Rc(e){let t=e.trim().toLowerCase();if(t==="--global"||t==="-g")return"global";if(t==="--chat"||t==="-p")return"chat"}function Mi(e){let t=e.trim().match(/^(\S+)\s+(--global|-g|--chat|-p)\s*$/i);if(!t?.[1]||!t[2])return;let n=Rc(t[2]);if(n)return{name:t[1],target:n}}function Qe(e,t,n,r){let o=r.trim().toLowerCase(),s=e==="global"?Tt:t,i=vi(s)[o];if(i===void 0)return;let a=io(i,n);if(nn(a).ok)return{...a,name:o,source:e==="global"?"shared":"peer"}}function Ti(e,t,n){if(n==="merged")return[];let r=n==="global"?Tt:e,o=n==="global"?"shared":"peer",s=no(r,t);return Object.entries(s).map(([i,a])=>({...a,name:i,source:o})).sort((i,a)=>i.name.localeCompare(a.name))}function gt(e){let t=e.trim();if(!t)return{ok:!1,error:"Name is empty."};let n=t.toLowerCase();return oo.test(n)?so.has(n)?{ok:!1,error:`Reserved name: ${n}`}:{ok:!0,normalized:n}:{ok:!1,error:`Invalid name (use letters, digits, _ or -; max 32 chars): ${t}`}}function Vn(e,t){let n=e.replace(/\r\n/g,`
|
|
54
|
-
`).trim();return n?t>0&&n.length>t?{ok:!1,error:`Task too long (max ${t} characters).`}:{ok:!0,task:n}:{ok:!1,error:"Task is empty."}}function It(e,t){return e.includes("$")?e.includes(t):!1}function ft(e,t){let n=e.trim();if(!n)return{ok:!1,error:"Command is empty."};if(!/^[A-Za-z_][A-Za-z0-9_]*$/.test(t))return{ok:!1,error:"Invalid taskEnv (use letters, digits, _)."};if(!It(n,t))return{ok:!1,error:`Command must reference "$${t}" so the task is passed via the environment.`};let r=`"$${t}"`;return n.includes(r)?n.includes("```")?{ok:!1,error:"Recipe command must not contain markdown code fences (```). Use promptTemplate or split with a --- line (see /run help)."}:{ok:!0}:{ok:!1,error:`Command must include ${r} (quoted).`}}function nn(e){let t=e.taskEnv??"OMNISH_TASK",n=ft(e.command,t);return n.ok?e.promptTemplate!==void 0&&e.promptTemplate.trim().length===0?{ok:!1,error:"promptTemplate cannot be empty."}:{ok:!0}:n}function Mc(e){let t=e,n=null,r=!1,o=-1,s=-1;for(let l=0;l<t.length;l+=1){let u=t[l];if(r){r=!1;continue}if(u==="\\"){r=!0;continue}if(n){u===n&&(n=null);continue}if(u==='"'||u==="'"){n=u;continue}if(u!=="-"||t[l+1]!=="-")continue;let c=l===0?"":t[l-1];if(c&&!/\s/.test(c))continue;let d=t.slice(l+2),m=/^(template|tamplate)\b/i.exec(d);if(!m)continue;let y=l+2+m[0].length,g=t[y]??"";if(!g||!/\s/.test(g))continue;o=l;let f=y;for(;f<t.length&&/\s/.test(t[f]);)f+=1;s=f;break}if(o<0||s<0)return{command:t.trim()};let i=t.slice(0,o).trim(),a=t.slice(s).trim();return(a.startsWith('"')&&a.endsWith('"')&&a.length>=2||a.startsWith("'")&&a.endsWith("'")&&a.length>=2)&&(a=a.slice(1,-1)),{command:i,template:a}}function co(e,t){let n=e.replace(/\r\n/g,`
|
|
55
|
-
`).trim();if(!n)throw new Error("Recipe body is empty.");let r="OMNISH_TASK",{command:o,template:s}=Mc(n);if(s!==void 0){if(!o)throw new Error("Command part (before --template) is empty.");if(!s.trim())throw new Error("Template part (after --template) is empty.");let c=ft(o,r);if(!c.ok)throw new Error(c.error);return qe({command:o,promptTemplate:s})}let i=/\n---\n/,a=n.search(i);if(a>=0){let c=n.slice(0,a).trim(),d=n.slice(a).replace(/^\n---\n/,"").trim();if(!c)throw new Error("Command part (before ---) is empty.");if(!d)throw new Error("Template part (after ---) is empty.");let m=ft(c,r);if(!m.ok)throw new Error(m.error);return qe({command:c,promptTemplate:d})}if(ft(n,r).ok)return qe({command:n});let l=t.trim(),u=ft(l,r);if(!u.ok)throw new Error(`recipesMacroDefaultCommand invalid (${u.error}). Fix config or paste runner then --- then template.`);return qe({command:l,promptTemplate:n})}function Kn(e,t,n){let r="`".repeat(3),o=`<<<${r}$${t}${r}>>>`,s=e;s.includes(o)&&(s=s.split(o).join(n));let i=`<<<${t}>>>`;s.includes(i)&&(s=s.split(i).join(n));let a=new RegExp(`\\$${t}\\b`,"g");return s.replace(a,n)}function uo(e,t,n,r="chat"){let o=gt(t);if(!o.ok)throw new Error(o.error);let s=qe({...n,command:n.command}),i=nn(s);if(!i.ok)throw new Error(i.error);let a=r==="global"?Tt:e,l=to(a);l[o.normalized]=s,Qn()}function Ii(e,t,n="chat"){let r=t.trim().toLowerCase(),o=n==="global"?Tt:e;Yn();let s=Mt.get(o);return!s||!(r in s)?!1:(delete s[r],Qn(),!0)}function po(e,t,n,r){let o=gt(t);if(!o.ok)return{ok:!1,error:o.error};let s=o.normalized,i=e,a=Tt;Yn();let l=to(i),u=to(a),c=l[s],d=u[s],m=y=>{let g=qe({...y}),f=nn(g);if(!f.ok)throw new Error(f.error);return g};try{if(n==="global"){if(c!==void 0){let f=m(c);return u[s]=f,delete l[s],Qn(),{ok:!0,kind:"moved",target:"global",name:s}}if(d!==void 0)return{ok:!0,kind:"noop",message:`Recipe "${s}" is already gateway-shared.`};let g=Re(e,r,s);return g?.source==="builtin"||g?.source==="global"?{ok:!1,error:`Recipe "${s}" is built-in or from host recipes.json \u2014 not moved via /run set. Edit recipes.json or use a different name.`}:{ok:!1,error:`No user recipe "${s}" in this chat to promote. Add with /run add ${s} \u2026 or demote from global with /run set -p ${s}.`}}if(d!==void 0){let g=m(d);return l[s]=g,delete u[s],Qn(),{ok:!0,kind:"moved",target:"chat",name:s}}if(c!==void 0)return{ok:!0,kind:"noop",message:`Recipe "${s}" is already stored for this chat only.`};let y=Re(e,r,s);return y?.source==="builtin"||y?.source==="global"?{ok:!1,error:`Recipe "${s}" is built-in or from host recipes.json \u2014 not moved via /run set.`}:{ok:!1,error:`No gateway-shared user recipe "${s}" to demote. Add with /run add --global ${s} \u2026 or promote from this chat with /run set -g ${s}.`}}catch(y){return{ok:!1,error:String(y)}}}function Ei(e,t){let n=[...$i(e,t).values()],r=n.filter(a=>a.featured).sort((a,l)=>a.name.localeCompare(l.name)),o=n.filter(a=>a.source==="peer").sort((a,l)=>a.name.localeCompare(l.name)),s=n.filter(a=>a.source==="shared").sort((a,l)=>a.name.localeCompare(l.name)),i=n.filter(a=>!a.featured&&a.source!=="peer"&&a.source!=="shared").sort((a,l)=>a.name.localeCompare(l.name));return{featured:r,shared:s,yours:o,more:i}}function Xn(e){let t=bc.randomBytes(4).toString("hex");return`r-${e.replace(/[^a-zA-Z0-9_-]/g,"").slice(0,12)}-${t}`.slice(0,32)}import Ai from"node:fs";var Li=64,Tc=1024*1024;function Oi(e){return e.fileReceiveMaxBytes>0?e.fileReceiveMaxBytes:Tc}function Pi(e){let t;try{t=JSON.parse(e)}catch(o){return{ok:!1,error:`Invalid JSON: ${String(o)}`}}let n;if(Array.isArray(t))n=t;else if(t&&typeof t=="object"&&!Array.isArray(t)){let o=t,s=Object.keys(o);if(s.length!==1||s[0]!=="tasks")return{ok:!1,error:'JSON must be an array or a single-key object: { "tasks": [ \u2026 ] }.'};let i=o.tasks;if(!Array.isArray(i))return{ok:!1,error:'"tasks" must be an array.'};n=i}else return{ok:!1,error:'JSON must be an array or { "tasks": [ \u2026 ] }.'};if(n.length===0)return{ok:!1,error:"Queue JSON must contain at least one job."};if(n.length>Li)return{ok:!1,error:`Too many jobs (max ${Li}).`};let r=[];for(let o=0;o<n.length;o++){let s=n[o];if(!s||typeof s!="object"||Array.isArray(s))return{ok:!1,error:`Job ${o+1}: must be an object with "recipe" and "task".`};let i=s;if(Object.keys(i).length!==2||typeof i.recipe!="string"||typeof i.task!="string")return{ok:!1,error:`Job ${o+1}: must contain only "recipe" and "task" string fields.`};r.push({recipe:i.recipe,task:i.task})}return{ok:!0,jobs:r}}function Fi(e,t,n){let r=[];for(let o=0;o<n.length;o++){let{recipe:s,task:i}=n[o],a=Re(e,t,s);if(!a)return{ok:!1,error:`Job ${o+1}: unknown recipe "${s}".`};let l=a.taskEnv??"OMNISH_TASK";if(!It(a.command,l))return{ok:!1,error:`Job ${o+1}: recipe "${s}" command must reference "$${l}".`};let u=Vn(i,t.recipesMaxTaskChars);if(!u.ok)return{ok:!1,error:`Job ${o+1}: ${u.error}`};let c=a.promptTemplate?Kn(a.promptTemplate,l,u.task):u.task,d={[l]:c};r.push({command:a.command,extraEnv:d,recipeLabel:s})}return{ok:!0,items:r}}function mo(e,t){let n;try{n=Ai.statSync(e)}catch{return{ok:!1,error:`Cannot read file: ${e}`}}if(!n.isFile())return{ok:!1,error:`Not a file: ${e}`};if(n.size>t)return{ok:!1,error:`File too large (max ${t} bytes for queue load).`};try{return{ok:!0,text:Ai.readFileSync(e,"utf8")}}catch(r){return{ok:!1,error:String(r)}}}import Ic from"node:os";import*as _i from"node-pty";function Ec(e,t){return e.length<=t?e:`${e.slice(0,t)}
|
|
56
|
-
[...truncated]`}function Ac(e){if(e===void 0||e===0)return null;let t=Ic.constants.signals;for(let[n,r]of Object.entries(t))if(r===e)return n;return null}function Ni(e){try{process.platform==="win32"?e.kill():e.kill("SIGTERM")}catch{e.kill()}}function Et(e,t,n){return new Promise(r=>{let o=Date.now(),s=!1,i="",a=n.cwd,l=null,u=!1,c=null,d=null,m,y=h=>{if(u)return;u=!0,m!==void 0&&clearTimeout(m),c?.dispose(),c=null,r(h);let x=d;d=null,queueMicrotask(()=>x?.dispose())},f={...n.env??process.env,TERM:"xterm-256color",COLORTERM:"truecolor",...a?{PWD:a}:{}};try{l=_i.spawn(e,["-c",t],{name:"xterm-256color",cols:120,rows:40,cwd:a,env:f})}catch(h){y({code:null,stdout:"",stderr:String(h),durationMs:Date.now()-o,timedOut:!1,signal:null});return}m=setTimeout(()=>{s=!0,l&&Ni(l)},n.timeoutMs),c=l.onData(h=>{i+=h,i.length>n.maxBytes&&(i=Ec(i,n.maxBytes),l&&Ni(l))}),d=l.onExit(h=>{y({code:h.exitCode,stdout:i,stderr:"",durationMs:Date.now()-o,timedOut:s,signal:Ac(h.signal)})})})}import{spawn as Lc}from"node:child_process";import Oc from"node:crypto";import ve from"node:fs";import fo from"node:path";var Bi=64,Pc=/^[a-zA-Z0-9._-]+$/;function Fc(e){let t=e.trim();return t?t.length>Bi?`Job name must be at most ${Bi} characters.`:Pc.test(t)?null:"Job name may only use letters, digits, and . _ -":"Job name must not be empty."}function Hi(e){let t=e.trim();if(!t)return{error:"empty"};let n=null,r=t,o=[/^--name\s*=\s*(\S+)\s+([\s\S]+)$/i,/^--name\s+(\S+)\s+([\s\S]+)$/i,/^-n\s+(\S+)\s+([\s\S]+)$/i];for(let s of o){let i=t.match(s);if(i){n=i[1],r=i[2].trim();break}}if(n===null&&[/^-n\s+\S+\s*$/i,/^--name\s+\S+\s*$/i,/^--name=\S+\s*$/i,/^-n\s*$/i,/^--name\s*$/i].some(i=>i.test(t)))return{error:"Add a shell command after the name."};if(n!==null){let s=Fc(n);if(s)return{error:s};if(!r)return{error:"Add a shell command after the name."}}return{cmd:r,name:n}}function Nc(e,t){let n=t.trim();if(!n)return{ok:!1,error:"Missing job id or name."};let r=n.toLowerCase(),o=/^[a-f0-9]{8}$/;if(o.test(r)&&e.find(i=>i.id===r))return{ok:!0,id:r};for(let s of e)if(s.name&&s.name.toLowerCase()===r)return{ok:!0,id:s.id};return o.test(r)?{ok:!1,error:`Unknown job id: ${r}`}:{ok:!1,error:`Unknown job name: ${n}`}}function At(e,t){ve.writeFileSync(e,JSON.stringify(t,null,2)+`
|
|
57
|
-
`,{mode:384})}function rn(e){try{return JSON.parse(ve.readFileSync(e,"utf8"))}catch{return null}}var ht=class{running=new Map;metaPath(t){return fo.join(Ae,`${t}.meta.json`)}logPath(t){return fo.join(Ae,`${t}.log`)}spawnJob(t,n,r={}){z();let o=Oc.randomBytes(4).toString("hex"),s=this.logPath(o),i=this.metaPath(o);ve.writeFileSync(s,"",{flag:"w",mode:384});let a=ve.createWriteStream(s,{flags:"a"}),l=new Date().toISOString(),u=r.cwd,c=Lc(t,["-c",n],{stdio:["ignore","pipe","pipe"],env:{...process.env,TERM:"dumb",...u?{PWD:u}:{}},...u?{cwd:u}:{}});this.running.set(o,c);let d={id:o,cmd:n,...r.name?{name:r.name}:{},pid:c.pid??null,startedAt:l,status:"running",exitCode:null,signal:null};return At(i,d),c.stdout?.on("data",m=>{a.write(m)}),c.stderr?.on("data",m=>{a.write(m)}),c.on("close",(m,y)=>{this.running.delete(o),a.end();let g=rn(i)??d,f={...g,status:g.status==="killed"?"killed":"done",exitCode:m,signal:y??null,finishedAt:new Date().toISOString()};At(i,f)}),c.on("error",m=>{this.running.delete(o),a.end(()=>{try{ve.appendFileSync(s,`
|
|
58
|
-
[spawn error] ${String(m)}
|
|
59
|
-
`)}catch{}});let y=rn(i)??d;At(i,{...y,status:"done",exitCode:null,signal:null,finishedAt:new Date().toISOString()})}),{id:o,meta:d}}list(){z();let t=[];try{t=ve.readdirSync(Ae)}catch{return[]}let n=[];for(let r of t){if(!r.endsWith(".meta.json"))continue;let o=rn(fo.join(Ae,r));o&&n.push(o)}return n.sort((r,o)=>r.startedAt<o.startedAt?1:-1),n}tailLog(t,n){let r=this.logPath(t);if(!ve.existsSync(r))return"(no log file)";let s=ve.readFileSync(r,"utf8").split(`
|
|
60
|
-
`);return s.slice(Math.max(0,s.length-n)).join(`
|
|
61
|
-
`).trimEnd()||"(empty log)"}readSince(t,n){let r=this.logPath(t);if(!ve.existsSync(r))return{text:"",nextOffset:n};let s=ve.statSync(r).size;if(n>=s)return{text:"",nextOffset:s};let i=Buffer.alloc(s-n),a=ve.openSync(r,"r");try{ve.readSync(a,i,0,i.length,n)}finally{ve.closeSync(a)}return{text:i.toString("utf8"),nextOffset:s}}resolveJobRef(t){return Nc(this.list(),t)}kill(t){let n=this.metaPath(t),r=rn(n);if(!r)return`Unknown job id: ${t}`;let o=this.running.get(t);if(o&&!o.killed)return o.kill("SIGTERM"),At(n,{...r,status:"killed",signal:"SIGTERM"}),`Sent SIGTERM to job ${t} (pid ${r.pid??"?"})`;if(r.pid)try{return process.kill(r.pid,"SIGTERM"),At(n,{...r,status:"killed",signal:"SIGTERM"}),`Sent SIGTERM to pid ${r.pid} (${t})`}catch{return`Job ${t} is not running (no live process).`}return`Job ${t} is not running.`}killAllRunning(){for(let[t,n]of this.running){n.killed||n.kill("SIGTERM");let r=rn(this.metaPath(t));r&&At(this.metaPath(t),{...r,status:"killed",signal:"SIGTERM"})}this.running.clear()}};import Lt from"node:fs";import wo from"node:os";import on from"node:path";import Wi from"node:crypto";var ho="\u2063omnish/c v1",_c=/([nlra])=([^\s\]]*)/g;function Oe(e){return e.replace(/-/g,"").slice(0,8)}function go(e){return e.replace(/[\s\]\r\n]/g,"_").slice(0,64)}function Bc(e){let t=go(Oe(e.nodeId)),n=go(e.label||""),r=e.role==="primary"?"p":"s",o=go(e.activeNodeId?Oe(e.activeNodeId):"");return`${ho} [n=${t} l=${n} r=${r} a=${o}]`}function Di(e,t){let n=Bc(t);if(e.includes(n))return e;let r=e.replace(/\s+$/,"");return r.length===0?n:`${r}
|
|
75
|
+
`).trimEnd())}function mi(e){return p(`Unknown recipe: ${e}
|
|
76
|
+
/run list`)}function fi(){return[{kind:"title",text:"Apps (interactive CLI)"},{kind:"sub",text:"Sessions"},{kind:"bullet",text:"/apps start <name> <command\u2026> \u2014 spawn in session cwd; auto-attaches"},{kind:"bullet",text:"/apps attach <name> | /apps detach"},{kind:"bullet",text:"/apps list | /apps info <name>"},{kind:"gap"},{kind:"sub",text:"Input"},{kind:"bullet",text:"/apps send <name> <text> \u2014 text + newline"},{kind:"bullet",text:">name text \u2014 shorthand for send"},{kind:"bullet",text:"/apps key <name> <KEY[,KEY\u2026]> \u2014 Enter, ^C, Up, Esc, \\x1b, \u2026"},{kind:"gap"},{kind:"sub",text:"Output & control"},{kind:"bullet",text:"/apps tail <name> [lines] | /apps since <name>"},{kind:"bullet",text:"/apps mute|unmute <name> | /apps raw <name> on|off"},{kind:"bullet",text:"/apps resize <name> <cols> <rows>"},{kind:"bullet",text:"/apps stop <name> | /apps kill <name> | /apps rm <name>"},{kind:"bullet",text:"/apps <session> publish \u2014 share session command to online catalog"},{kind:"bullet",text:"/apps online trending | show <publicId> | <publicId> download \u2014 browse app templates only"},{kind:"gap"},{kind:"p",text:"Attached: plain DMs go to the focused app. Escaped by ! prefix, /commands, or >other."},{kind:"p",text:"Free shell: !!start \u2014 plain DMs run as sync shell when no focused PTY; !!stop \u2014 off. Attached session wins over free shell for plain text."}]}function Wf(e){let{errors:t,warns:n,infos:r}=oi(e),o=[{kind:"title",text:"Security check"}];if(e.length===0)return o.push({kind:"p",text:"No issues reported by automated rules."},{kind:"gap"},{kind:"p",text:"Allowlisted remote shell access is still equivalent to sharing credentials with those identities."}),o;o.push({kind:"p",text:`${t} error(s), ${n} warning(s), ${r} note(s).`}),o.push({kind:"gap"});let s=(i,a)=>{if(a.length!==0){o.push({kind:"sub",text:`${i} (${a.length})`});for(let l of a){let d=l.severity.toUpperCase();o.push({kind:"bullet",text:Me(`[${d}] ${l.code}: ${l.message}`)}),l.detail&&o.push({kind:"bullet",text:Me(l.detail)}),l.fixHint&&o.push({kind:"bullet",text:Me(`Fix: ${l.fixHint}`)})}o.push({kind:"gap"})}};return s("Errors",e.filter(i=>i.severity==="error")),s("Warnings",e.filter(i=>i.severity==="warn")),s("Notes",e.filter(i=>i.severity==="info")),o.push({kind:"p",text:"Allowlisted identities can run commands as this user; treat them like passwords."}),o}function Tc(e){return Z(Wf(e))}function Ec(){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 Pc(){return[{kind:"title",text:"/security commands"},{kind:"bullet",text:"/security \u2014 full report (same checks as omnish security)"},{kind:"bullet",text:"/security summary \u2014 one-line counts"},{kind:"bullet",text:"/security tips \u2014 short hardening checklist"},{kind:"bullet",text:"CLI: omnish security [--json] for scripts and monitoring"}]}function Se(e){return e.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">")}function $n(e){let t=e.trim();return t?t.length<=8?"(set)":`${t.slice(0,4)}\u2026${t.slice(-4)}`:"(empty)"}function Ac(e){let t=e.trim();return(t.startsWith('"')&&t.endsWith('"')&&t.length>=2||t.startsWith("'")&&t.endsWith("'")&&t.length>=2)&&(t=t.slice(1,-1)),t}function Je(e){let t=e.trim().toLowerCase();return t==="true"||t==="1"||t==="yes"||t==="on"?!0:t==="false"||t==="0"||t==="no"||t==="off"?!1:null}var Ic=new Set(["downloads","omnishData","sessionCwd","processCwd","fixed"]),Bt=["gatewayMode","clusterEnabled","clusterRole","clusterLabel","clusterSenderBindings","commandPrefix","syncTimeoutMs","syncMaxBytes","jobLogTailLines","shell","appsCols","appsRows","appsFlushMs","appsMinIntervalMs","appsMaxFlushBytes","appsMaxSessions","appsMaxSessionsTotal","appsMaxWaChars","appsLogTailLines","appsSubmitDelayMs","appsClearInput","appsClearInputDelayMs","appsClearInputSequence","appsSkipClearOnPasswordPrompt","appsPasswordPromptHint","fileSendMaxBytes","fileReceiveMaxBytes","fileInboxSubdir","fileReceiveRootMode","fileReceiveRootPath","recipesAllowDangerousBuiltins","recipesMaxTaskChars","recipesMacroDefaultCommand","telegramBotToken","serviceInstallFromChat","updateCheckEnabled","updateCheckIntervalMs","updateCheckPackageName","updateInfoUrl","chatLlmFallbackEnabled","chatLlmShellCommand","chatLlmTimeoutMs","chatLlmMaxInputChars","chatLlmMaxOutputChars","chatLlmNeedsTty","chatLlmWorkDir","tunnelEnabled","tunnelRelayUrl","tunnelMaxActive","webhookEnabled","webhookPort","webhookHost","webhookToken","watchEnabled","watchDebounceMs","watchMaxEventsPerMinute","watchAutoRestore"];function Lc(e){return Bt.includes(e)}function Uf(e){let t=ve(e);return["*Config* (secrets masked)","",`gatewayMode: ${e.gatewayMode}`,`commandPrefix: ${e.commandPrefix}`,`shell: ${e.shell}`,`syncTimeoutMs: ${e.syncTimeoutMs}`,`syncMaxBytes: ${e.syncMaxBytes}`,`jobLogTailLines: ${e.jobLogTailLines}`,"","*Cluster*",`clusterEnabled: ${e.clusterEnabled}`,`clusterRole: ${e.clusterRole}`,`clusterLabel: ${e.clusterLabel||"(hostname)"}`,`clusterSenderBindings: ${Object.keys(e.clusterSenderBindings??{}).length} entries`,"","*Telegram*",`telegramBotToken: ${$n(t)}`,`telegramAllowFrom: ${e.telegramAllowFrom.length} entries`,"","*WhatsApp allowFrom*",`${e.allowFrom.length} entries`,"","*Apps*",`appsCols \xD7 appsRows: ${e.appsCols}\xD7${e.appsRows}`,`appsFlushMs / appsMinIntervalMs: ${e.appsFlushMs} / ${e.appsMinIntervalMs}`,`appsMaxFlushBytes: ${e.appsMaxFlushBytes}`,`appsMaxSessions / total: ${e.appsMaxSessions} / ${e.appsMaxSessionsTotal}`,`appsMaxWaChars: ${e.appsMaxWaChars}`,`appsLogTailLines: ${e.appsLogTailLines}`,`appsSubmitDelayMs: ${e.appsSubmitDelayMs}`,`appsClearInput: ${e.appsClearInput}`,`appsClearInputDelayMs: ${e.appsClearInputDelayMs}`,`appsClearInputSequence: ${e.appsClearInputSequence}`,`appsSkipClearOnPasswordPrompt: ${e.appsSkipClearOnPasswordPrompt}`,`appsPasswordPromptHint: ${e.appsPasswordPromptHint}`,"","*Files*",`fileSendMaxBytes / fileReceiveMaxBytes: ${e.fileSendMaxBytes} / ${e.fileReceiveMaxBytes}`,`fileInboxSubdir: ${e.fileInboxSubdir}`,`fileReceiveRootMode: ${e.fileReceiveRootMode}`,`fileReceiveRootPath: ${e.fileReceiveRootPath||"(empty)"}`,"","*Recipes*",`recipesAllowDangerousBuiltins: ${e.recipesAllowDangerousBuiltins}`,`recipesMaxTaskChars: ${e.recipesMaxTaskChars}`,`recipesMacroDefaultCommand: ${e.recipesMacroDefaultCommand}`,"","*Service (chat)*",`serviceInstallFromChat: ${e.serviceInstallFromChat}`,"","*Updates (optional)*",`updateCheckEnabled: ${e.updateCheckEnabled}`,`updateCheckIntervalMs: ${e.updateCheckIntervalMs}`,`updateCheckPackageName: ${e.updateCheckPackageName}`,`updateInfoUrl: ${e.updateInfoUrl?"(set)":"(empty)"}`,"","*Tunneling (chat /tunnel)*",`tunnelEnabled: ${e.tunnelEnabled}`,`tunnelRelayUrl: ${e.tunnelRelayUrl}`,`tunnelMaxActive: ${e.tunnelMaxActive}`,"","*Chat LLM fallback (optional)*",`chatLlmFallbackEnabled: ${e.chatLlmFallbackEnabled}`,`chatLlmShellCommand: ${e.chatLlmShellCommand?"(set)":"(empty)"}`,`chatLlmTimeoutMs: ${e.chatLlmTimeoutMs}`,`chatLlmMaxInputChars / chatLlmMaxOutputChars: ${e.chatLlmMaxInputChars} / ${e.chatLlmMaxOutputChars}`,`chatLlmNeedsTty: ${e.chatLlmNeedsTty}`,`chatLlmWorkDir: ${e.chatLlmWorkDir||"(empty \u2014 temp dir per run)"}`,"","*Webhook receiver (optional)*",`webhookEnabled: ${e.webhookEnabled}`,`webhookPort: ${e.webhookPort} (0 = random)`,`webhookHost: ${e.webhookHost}`,`webhookToken: ${$n(e.webhookToken)}`,"","*Watch (OS event eye)*",`watchEnabled: ${e.watchEnabled}`,`watchDebounceMs: ${e.watchDebounceMs}`,`watchMaxEventsPerMinute: ${e.watchMaxEventsPerMinute}`,`watchAutoRestore: ${e.watchAutoRestore}`,"",`File: ${_}`].join(`
|
|
77
|
+
`)}function Df(e){let t=ve(e);return["<b>Config</b> (secrets masked)","",`<b>gatewayMode</b> ${Se(e.gatewayMode)}`,`<b>commandPrefix</b> ${Se(e.commandPrefix)}`,`<b>shell</b> ${Se(e.shell)}`,`<b>syncTimeoutMs</b> ${e.syncTimeoutMs}`,`<b>syncMaxBytes</b> ${e.syncMaxBytes}`,`<b>jobLogTailLines</b> ${e.jobLogTailLines}`,"","<b>Cluster</b>",`clusterEnabled: ${e.clusterEnabled} \xB7 clusterRole: ${Se(e.clusterRole)}`,`clusterLabel: ${Se(e.clusterLabel||"(hostname)")}`,`clusterSenderBindings: ${Object.keys(e.clusterSenderBindings??{}).length} entries`,"","<b>Telegram</b>",`telegramBotToken: ${Se($n(t))}`,`telegramAllowFrom: ${e.telegramAllowFrom.length} entries`,"",`<b>WhatsApp allowFrom</b> ${e.allowFrom.length} entries`,"","<b>Apps</b>",`${e.appsCols}\xD7${e.appsRows} flush ${e.appsFlushMs}/${e.appsMinIntervalMs} ms \u2026`,"","<b>Files</b>",`${Se(e.fileReceiveRootMode)} \xB7 inbox ${Se(e.fileInboxSubdir)}`,"","<b>Service (chat)</b>",`serviceInstallFromChat: ${e.serviceInstallFromChat}`,"","<b>Updates (optional)</b>",`updateCheckEnabled: ${e.updateCheckEnabled} \xB7 interval ms: ${e.updateCheckIntervalMs}`,`package: <code>${Se(e.updateCheckPackageName)}</code> \xB7 info URL: ${e.updateInfoUrl?"set":"empty"}`,"","<b>Tunneling (chat /tunnel)</b>",`tunnelEnabled: ${e.tunnelEnabled} \xB7 relay <code>${Se(e.tunnelRelayUrl)}</code> \xB7 max active: ${e.tunnelMaxActive}`,"","<b>Chat LLM fallback</b>",`enabled: ${e.chatLlmFallbackEnabled} \xB7 command: ${e.chatLlmShellCommand?"set":"empty"}`,`timeout ms: ${e.chatLlmTimeoutMs} \xB7 in/out chars: ${e.chatLlmMaxInputChars} / ${e.chatLlmMaxOutputChars}`,`needsTty: ${e.chatLlmNeedsTty} \xB7 workDir: ${Se(e.chatLlmWorkDir||"(empty)")}`,"","<b>Webhook receiver</b>",`enabled: ${e.webhookEnabled} \xB7 ${Se(e.webhookHost)}:${e.webhookPort} \xB7 token: ${Se($n(e.webhookToken))}`,"","<b>Watch (OS event eye)</b>",`enabled: ${e.watchEnabled} \xB7 debounce ms: ${e.watchDebounceMs} \xB7 max/min: ${e.watchMaxEventsPerMinute} \xB7 autoRestore: ${e.watchAutoRestore}`,"",`<code>${Se(_)}</code>`].join(`
|
|
78
|
+
`)}function Oc(e,t){if(!Lc(t))return null;let n=t;if(n==="telegramBotToken")return`telegramBotToken: ${$n(ve(e))}`;if(n==="webhookToken")return`webhookToken: ${$n(e.webhookToken)}`;let r=e[n];return`${t}: ${typeof r=="object"?JSON.stringify(r):String(r)}`}function Hf(e,t){let n=Oc(e,t);if(!n)return null;let r=n.split(": ");return r.length<2?Se(n):`<b>${Se(r[0])}</b> ${Se(r.slice(1).join(": "))}`}function uo(e,t){let n=Ac(t),r=!1,o=!1,s=!1;if(e==="telegramBotToken"){if(!dt(n))throw new Error("Invalid bot token format (expect digits:secret from BotFather).");return Lt(n),r=!0,s=!0,{reloadSuggested:r,shellWarning:o,tokenSaved:s}}let i=$();switch(e){case"gatewayMode":{let a=gn(n);if(!a)throw new Error("gatewayMode: use whatsapp | telegram | both (or wa, tg, b)");i.gatewayMode=a,r=!0;break}case"clusterEnabled":{let a=Je(n);if(a===null)throw new Error("clusterEnabled: true or false");i.clusterEnabled=a;break}case"clusterRole":{let a=n.trim().toLowerCase();if(a!=="primary"&&a!=="secondary")throw new Error("clusterRole: primary | secondary");i.clusterRole=a;break}case"clusterLabel":i.clusterLabel=n.trim().slice(0,128);break;case"clusterSenderBindings":{let a=n.trim();if(a===""||a==="{}"||a.toLowerCase()==="clear"){i.clusterSenderBindings={};break}let l;try{l=JSON.parse(a)}catch{throw new Error('clusterSenderBindings: pass a JSON object, e.g. {"wa:+15551234567":"workshop-box"}')}if(!l||typeof l!="object"||Array.isArray(l))throw new Error("clusterSenderBindings must be a JSON object of sender \u2192 label/id");let d={};for(let[u,c]of Object.entries(l)){if(typeof c!="string"||!c.trim())throw new Error(`clusterSenderBindings.${u}: value must be a non-empty string`);d[u]=c.trim()}i.clusterSenderBindings=d;break}case"commandPrefix":if(!n.trim())throw new Error("commandPrefix cannot be empty");i.commandPrefix=n.trim().slice(0,32);break;case"syncTimeoutMs":{let a=Number.parseInt(n,10);if(!Number.isFinite(a)||a<=0)throw new Error("syncTimeoutMs: positive integer (ms)");i.syncTimeoutMs=a;break}case"syncMaxBytes":{let a=Number.parseInt(n,10);if(!Number.isFinite(a)||a<=0)throw new Error("syncMaxBytes: positive integer");i.syncMaxBytes=a;break}case"jobLogTailLines":{let a=Number.parseInt(n,10);if(!Number.isFinite(a)||a<=0)throw new Error("jobLogTailLines: positive integer");i.jobLogTailLines=a;break}case"shell":if(!n.trim())throw new Error("shell: non-empty path");i.shell=n.trim().slice(0,4096),o=!0;break;case"recipesMacroDefaultCommand":if(!n.trim())throw new Error('recipesMacroDefaultCommand: non-empty shell snippet with "$OMNISH_TASK"');i.recipesMacroDefaultCommand=Ac(n).trim().slice(0,4096);break;case"appsCols":case"appsRows":case"appsFlushMs":case"appsMinIntervalMs":case"appsMaxFlushBytes":case"appsMaxSessions":case"appsMaxSessionsTotal":case"appsMaxWaChars":case"appsLogTailLines":case"appsSubmitDelayMs":case"appsClearInputDelayMs":case"recipesMaxTaskChars":case"fileSendMaxBytes":case"fileReceiveMaxBytes":{let a=Number.parseInt(n,10);if(!Number.isFinite(a)||a<0)throw new Error(`${e}: non-negative integer`);i[e]=a;break}case"appsClearInput":{let a=Je(n);if(a===null)throw new Error("appsClearInput: true or false");i.appsClearInput=a;break}case"appsClearInputSequence":i.appsClearInputSequence=n.trim().slice(0,200);break;case"appsSkipClearOnPasswordPrompt":{let a=Je(n);if(a===null)throw new Error("appsSkipClearOnPasswordPrompt: true or false");i.appsSkipClearOnPasswordPrompt=a;break}case"appsPasswordPromptHint":{let a=Je(n);if(a===null)throw new Error("appsPasswordPromptHint: true or false");i.appsPasswordPromptHint=a;break}case"fileInboxSubdir":i.fileInboxSubdir=n.trim().slice(0,80);break;case"fileReceiveRootMode":{let a=n.trim();if(!Ic.has(a))throw new Error(`fileReceiveRootMode: ${[...Ic].join(" | ")}`);i.fileReceiveRootMode=a;break}case"fileReceiveRootPath":i.fileReceiveRootPath=n.trim().slice(0,4096);break;case"recipesAllowDangerousBuiltins":{let a=Je(n);if(a===null)throw new Error("recipesAllowDangerousBuiltins: true or false");i.recipesAllowDangerousBuiltins=a;break}case"serviceInstallFromChat":{let a=Je(n);if(a===null)throw new Error("serviceInstallFromChat: true or false");i.serviceInstallFromChat=a;break}case"updateCheckEnabled":{let a=Je(n);if(a===null)throw new Error("updateCheckEnabled: true or false");i.updateCheckEnabled=a;break}case"updateCheckIntervalMs":{let a=Number.parseInt(n,10);if(!Number.isFinite(a)||a<36e5)throw new Error("updateCheckIntervalMs: integer ms, minimum 3600000 (1 hour)");i.updateCheckIntervalMs=Math.min(6048e5,a);break}case"updateCheckPackageName":if(!n.trim())throw new Error("updateCheckPackageName: non-empty npm package name");i.updateCheckPackageName=n.trim().slice(0,214);break;case"updateInfoUrl":i.updateInfoUrl=n.trim().slice(0,2048);break;case"tunnelEnabled":{let a=Je(n);if(a===null)throw new Error("tunnelEnabled: true or false");return W({tunnelEnabled:a}),{reloadSuggested:r,shellWarning:o,tokenSaved:s}}case"tunnelRelayUrl":{let a=n.trim();if(!a)throw new Error("tunnelRelayUrl: non-empty URL");let l;try{l=new URL(a)}catch{throw new Error("tunnelRelayUrl: invalid URL")}if(l.protocol!=="http:"&&l.protocol!=="https:")throw new Error("tunnelRelayUrl: use http:// or https://");return W({tunnelRelayUrl:a}),{reloadSuggested:r,shellWarning:o,tokenSaved:s}}case"tunnelMaxActive":{let a=Number.parseInt(n.trim(),10);if(!Number.isFinite(a)||a<1||a>50)throw new Error("tunnelMaxActive: integer 1\u201350");return W({tunnelMaxActive:a}),{reloadSuggested:r,shellWarning:o,tokenSaved:s}}case"chatLlmFallbackEnabled":{let a=Je(n);if(a===null)throw new Error("chatLlmFallbackEnabled: true or false");return W({chatLlmFallbackEnabled:a}),{reloadSuggested:r,shellWarning:o,tokenSaved:s}}case"chatLlmShellCommand":return W({chatLlmShellCommand:n}),{reloadSuggested:r,shellWarning:o,tokenSaved:s};case"chatLlmTimeoutMs":{let a=Number.parseInt(n.trim(),10);if(!Number.isFinite(a)||a<=0)throw new Error("chatLlmTimeoutMs: positive integer (ms)");return W({chatLlmTimeoutMs:a}),{reloadSuggested:r,shellWarning:o,tokenSaved:s}}case"chatLlmMaxInputChars":{let a=Number.parseInt(n.trim(),10);if(!Number.isFinite(a)||a<=0)throw new Error("chatLlmMaxInputChars: positive integer");return W({chatLlmMaxInputChars:a}),{reloadSuggested:r,shellWarning:o,tokenSaved:s}}case"chatLlmMaxOutputChars":{let a=Number.parseInt(n.trim(),10);if(!Number.isFinite(a)||a<=0)throw new Error("chatLlmMaxOutputChars: positive integer");return W({chatLlmMaxOutputChars:a}),{reloadSuggested:r,shellWarning:o,tokenSaved:s}}case"chatLlmNeedsTty":{let a=Je(n);if(a===null)throw new Error("chatLlmNeedsTty: true or false");return W({chatLlmNeedsTty:a}),{reloadSuggested:r,shellWarning:o,tokenSaved:s}}case"chatLlmWorkDir":return W({chatLlmWorkDir:n.trim()}),{reloadSuggested:r,shellWarning:o,tokenSaved:s};case"webhookEnabled":{let a=Je(n);if(a===null)throw new Error("webhookEnabled: true or false");return W({webhookEnabled:a}),r=!0,{reloadSuggested:r,shellWarning:o,tokenSaved:s}}case"webhookPort":{let a=Number.parseInt(n.trim(),10);if(!Number.isFinite(a)||a<0||a>65535)throw new Error("webhookPort: integer 0\u201365535 (0 = random free port)");return W({webhookPort:a}),r=!0,{reloadSuggested:r,shellWarning:o,tokenSaved:s}}case"webhookHost":if(!n.trim())throw new Error("webhookHost: non-empty bind address");return W({webhookHost:n.trim().slice(0,256)}),r=!0,{reloadSuggested:r,shellWarning:o,tokenSaved:s};case"webhookToken":return W({webhookToken:n.trim()}),r=!0,{reloadSuggested:r,shellWarning:o,tokenSaved:s};case"watchEnabled":{let a=Je(n);if(a===null)throw new Error("watchEnabled: true or false");return W({watchEnabled:a}),De(),{reloadSuggested:r,shellWarning:o,tokenSaved:s}}case"watchDebounceMs":{let a=Number.parseInt(n.trim(),10);if(!Number.isFinite(a)||a<500||a>6e4)throw new Error("watchDebounceMs: integer 500\u201360000");return W({watchDebounceMs:a}),De(),{reloadSuggested:r,shellWarning:o,tokenSaved:s}}case"watchMaxEventsPerMinute":{let a=Number.parseInt(n.trim(),10);if(!Number.isFinite(a)||a<1||a>120)throw new Error("watchMaxEventsPerMinute: integer 1\u2013120");return W({watchMaxEventsPerMinute:a}),De(),{reloadSuggested:r,shellWarning:o,tokenSaved:s}}case"watchAutoRestore":{let a=Je(n);if(a===null)throw new Error("watchAutoRestore: true or false");return W({watchAutoRestore:a}),De(),{reloadSuggested:r,shellWarning:o,tokenSaved:s}}}return _e(i),{reloadSuggested:r,shellWarning:o,tokenSaved:s}}async function Nc(e,t){let n=e.trim(),r=n.toLowerCase();if(!n||r==="help")return Z(tc());if(r==="keys"||r==="help keys")return p(["*Configurable keys* (/config set)","",Bt.join(", "),"","Examples:","/config set clusterLabel work-laptop",'/config set fileReceiveRootPath "/path/with spaces"',"/config set gatewayMode both","/config set tunnelEnabled true","/config set chatLlmFallbackEnabled false","/config set webhookEnabled true","/config set watchEnabled true"].join(`
|
|
79
|
+
`));if(r==="show"){let i=$();return ue(Uf(i),Df(i))}let o=n.match(/^get\s+(\S+)\s*$/i);if(o){let i=o[1],a=$(),l=Oc(a,i);if(!l)return p(`Unknown key "${i}". /config keys`);let d=Hf(a,i);return ue(`${l}
|
|
62
80
|
|
|
63
|
-
${
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
81
|
+
${_}`,`${d}<br/><br/><code>${Se(_)}</code>`)}let s=n.match(/^set\s+(\S+)\s+([\s\S]+)$/i);if(s){let i=s[1],a=s[2]??"";if(!Lc(i))return p(`Unknown key "${i}". /config keys`);try{let{reloadSuggested:l,shellWarning:d,tokenSaved:u}=uo(i,a),c="";if(t?.reload&&l){let y=await t.reload();c=y.ok?`
|
|
82
|
+
Reload: ${y.summary}`:`
|
|
83
|
+
Reload failed: ${y.error}`}else l?c=`
|
|
84
|
+
Send /reload while omnish run is active (gatewayMode / Telegram).`:c=`
|
|
85
|
+
Send /reload to pick up changes where applicable.`;let m=d?`
|
|
86
|
+
\u26A0 shell changed \u2014 affects all commands run via omnish.`:"",f=u?`
|
|
87
|
+
telegramBotToken saved (not echoed).`:"",h=`Set *${i}* (saved).${f}${m}${c}`,g=`<b>Set ${Se(i)}</b> (saved).${f?"<br/>token saved (not echoed).":""}${d?"<br/>\u26A0 shell path changed.":""}${Se(c)}`;return ue(h,g)}catch(l){return p(`Error: ${String(l)}`)}}return p("Unknown /config command. Try /config help or /config show")}pe();var hi=[...Bt,"platformToken","platformDeviceId"],gi={platform_url:"tunnelRelayUrl",tunnel_relay_url:"tunnelRelayUrl",platform_token:"platformToken",token:"platformToken",omnish_token:"platformToken",platform_device_id:"platformDeviceId",device_id:"platformDeviceId"};for(let e of hi)gi[e]=e;function po(e){let t=e.trim().toLowerCase().replace(/-/g,"_");return gi[t]??null}function _c(){return Object.keys(gi).sort()}function Ke(e){let t=e.trim().toLowerCase();return t==="true"||t==="1"||t==="yes"||t==="on"?!0:t==="false"||t==="0"||t==="no"||t==="off"?!1:null}function Bf(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 jf(e){return N[e]}function Rn(e,t){let n=Bf(t),r=$();switch(e){case"platformToken":return W({platformToken:n});case"platformDeviceId":return W({platformDeviceId:n});case"gatewayMode":{let o=gn(n);if(!o)throw new Error('gatewayMode: "whatsapp", "telegram", or "both"');return r.gatewayMode=o,_e(r),r}case"telegramBotToken":if(!dt(n))throw new Error("telegramBotToken: invalid bot token format");return Lt(n);case"tunnelRelayUrl":{let o=n.trim();if(!o)throw new Error("tunnelRelayUrl: non-empty URL");let s;try{s=new URL(o)}catch{throw new Error("tunnelRelayUrl: invalid URL")}if(s.protocol!=="http:"&&s.protocol!=="https:")throw new Error("tunnelRelayUrl: use http:// or https://");return W({tunnelRelayUrl:o})}case"tunnelEnabled":{let o=Ke(n);if(o===null)throw new Error("tunnelEnabled: true or false");return W({tunnelEnabled:o})}case"tunnelMaxActive":{let o=Number.parseInt(n,10);if(!Number.isFinite(o)||o<1||o>50)throw new Error("tunnelMaxActive: integer 1\u201350");return W({tunnelMaxActive:o})}case"clusterSenderBindings":{let o;try{o=JSON.parse(n)}catch{throw new Error("clusterSenderBindings: valid JSON object")}if(!o||typeof o!="object"||Array.isArray(o))throw new Error("clusterSenderBindings: JSON object required");return W({clusterSenderBindings:o})}default:break}if(e==="clusterEnabled"){let o=Ke(n);if(o===null)throw new Error("clusterEnabled: true or false");r.clusterEnabled=o}else if(e==="clusterRole"){if(n!=="primary"&&n!=="secondary")throw new Error('clusterRole: "primary" or "secondary"');r.clusterRole=n}else if(e==="clusterLabel")r.clusterLabel=n.trim().slice(0,64);else if(e==="commandPrefix"){if(!n)throw new Error("commandPrefix: non-empty");r.commandPrefix=n}else if(e==="shell"){if(!n)throw new Error("shell: non-empty path");r.shell=n}else if(e==="syncTimeoutMs"){let o=Number.parseInt(n,10);if(!Number.isFinite(o)||o<=0)throw new Error("syncTimeoutMs: positive integer");r.syncTimeoutMs=o}else if(e==="syncMaxBytes"){let o=Number.parseInt(n,10);if(!Number.isFinite(o)||o<=0)throw new Error("syncMaxBytes: positive integer");r.syncMaxBytes=o}else if(e==="jobLogTailLines"){let o=Number.parseInt(n,10);if(!Number.isFinite(o)||o<=0)throw new Error("jobLogTailLines: positive integer");r.jobLogTailLines=o}else if(e==="appsCols"||e==="appsRows"){let o=Number.parseInt(n,10);if(!Number.isFinite(o)||o<20)throw new Error(`${e}: integer >= 20`);r[e]=o}else if(e==="appsFlushMs"||e==="appsMinIntervalMs"||e==="appsMaxFlushBytes"||e==="appsMaxWaChars"||e==="appsLogTailLines"||e==="appsSubmitDelayMs"||e==="appsClearInputDelayMs"){let o=Number.parseInt(n,10);if(!Number.isFinite(o)||o<0)throw new Error(`${e}: non-negative integer`);r[e]=o}else if(e==="appsMaxSessions"||e==="appsMaxSessionsTotal"){let o=Number.parseInt(n,10);if(!Number.isFinite(o)||o<1)throw new Error(`${e}: positive integer`);r[e]=o}else if(e==="appsClearInput"){let o=Ke(n);if(o===null)throw new Error("appsClearInput: true or false");r.appsClearInput=o}else if(e==="appsClearInputSequence")r.appsClearInputSequence=n;else if(e==="appsSkipClearOnPasswordPrompt"){let o=Ke(n);if(o===null)throw new Error("appsSkipClearOnPasswordPrompt: true or false");r.appsSkipClearOnPasswordPrompt=o}else if(e==="appsPasswordPromptHint"){let o=Ke(n);if(o===null)throw new Error("appsPasswordPromptHint: true or false");r.appsPasswordPromptHint=o}else if(e==="fileSendMaxBytes"||e==="fileReceiveMaxBytes"){let o=Number.parseInt(n,10);if(!Number.isFinite(o)||o<0)throw new Error(`${e}: non-negative integer`);r[e]=o}else if(e==="fileInboxSubdir")r.fileInboxSubdir=n.trim().slice(0,128);else if(e==="fileReceiveRootMode"){if(!new Set(["downloads","omnishData","sessionCwd","processCwd","fixed"]).has(n))throw new Error("fileReceiveRootMode: downloads|omnishData|sessionCwd|processCwd|fixed");r.fileReceiveRootMode=n}else if(e==="fileReceiveRootPath")r.fileReceiveRootPath=n.trim().slice(0,4096);else if(e==="recipesAllowDangerousBuiltins"){let o=Ke(n);if(o===null)throw new Error("recipesAllowDangerousBuiltins: true or false");r.recipesAllowDangerousBuiltins=o}else if(e==="recipesMaxTaskChars"){let o=Number.parseInt(n,10);if(!Number.isFinite(o)||o<0)throw new Error("recipesMaxTaskChars: non-negative integer");r.recipesMaxTaskChars=o}else if(e==="recipesMacroDefaultCommand"){if(!n.includes("$OMNISH_TASK"))throw new Error('recipesMacroDefaultCommand: must include "$OMNISH_TASK"');r.recipesMacroDefaultCommand=n}else if(e==="serviceInstallFromChat"){let o=Ke(n);if(o===null)throw new Error("serviceInstallFromChat: true or false");r.serviceInstallFromChat=o}else if(e==="updateCheckEnabled"){let o=Ke(n);if(o===null)throw new Error("updateCheckEnabled: true or false");r.updateCheckEnabled=o}else if(e==="updateCheckIntervalMs"){let o=Number.parseInt(n,10);if(!Number.isFinite(o)||o<36e5)throw new Error("updateCheckIntervalMs: min 3600000");r.updateCheckIntervalMs=Math.min(6048e5,o)}else if(e==="updateCheckPackageName"){if(!n.trim())throw new Error("updateCheckPackageName: non-empty");r.updateCheckPackageName=n.trim().slice(0,214)}else if(e==="updateInfoUrl")r.updateInfoUrl=n.trim().slice(0,2048);else if(e==="chatLlmFallbackEnabled"){let o=Ke(n);if(o===null)throw new Error("chatLlmFallbackEnabled: true or false");return W({chatLlmFallbackEnabled:o})}else{if(e==="chatLlmShellCommand")return W({chatLlmShellCommand:n});if(e==="chatLlmTimeoutMs"){let o=Number.parseInt(n,10);if(!Number.isFinite(o)||o<=0)throw new Error("chatLlmTimeoutMs: positive integer");return W({chatLlmTimeoutMs:o})}else if(e==="chatLlmMaxInputChars"){let o=Number.parseInt(n,10);if(!Number.isFinite(o)||o<=0)throw new Error("chatLlmMaxInputChars: positive integer");return W({chatLlmMaxInputChars:o})}else if(e==="chatLlmMaxOutputChars"){let o=Number.parseInt(n,10);if(!Number.isFinite(o)||o<=0)throw new Error("chatLlmMaxOutputChars: positive integer");return W({chatLlmMaxOutputChars:o})}else if(e==="chatLlmNeedsTty"){let o=Ke(n);if(o===null)throw new Error("chatLlmNeedsTty: true or false");return W({chatLlmNeedsTty:o})}else{if(e==="chatLlmWorkDir")return W({chatLlmWorkDir:n.trim()});if(e==="webhookEnabled"){let o=Ke(n);if(o===null)throw new Error("webhookEnabled: true or false");return W({webhookEnabled:o})}else if(e==="webhookPort"){let o=Number.parseInt(n,10);if(!Number.isFinite(o)||o<0||o>65535)throw new Error("webhookPort: integer 0\u201365535");return W({webhookPort:o})}else if(e==="webhookHost"){if(!n.trim())throw new Error("webhookHost: non-empty");return W({webhookHost:n.trim().slice(0,256)})}else{if(e==="webhookToken")return W({webhookToken:n.trim()});if(e==="watchEnabled"){let o=Ke(n);if(o===null)throw new Error("watchEnabled: true or false");return W({watchEnabled:o})}else if(e==="watchDebounceMs"){let o=Number.parseInt(n,10);if(!Number.isFinite(o)||o<500||o>6e4)throw new Error("watchDebounceMs: integer 500\u201360000");return W({watchDebounceMs:o})}else if(e==="watchMaxEventsPerMinute"){let o=Number.parseInt(n,10);if(!Number.isFinite(o)||o<1||o>120)throw new Error("watchMaxEventsPerMinute: integer 1\u2013120");return W({watchMaxEventsPerMinute:o})}else if(e==="watchAutoRestore"){let o=Ke(n);if(o===null)throw new Error("watchAutoRestore: true or false");return W({watchAutoRestore:o})}else throw new Error(`Unsupported key: ${e}`)}}}return _e(r),r}function Fc(e){let t=jf(e);if(e==="platformToken"||e==="platformDeviceId")return W({[e]:t});if(e==="tunnelRelayUrl")return W({tunnelRelayUrl:t});let n=$();return n[e]=t,_e(n),n}function tn(e,t){if(e==="platformToken"||e==="telegramBotToken"||e==="webhookToken"){let n=t.trim();return n?n.length<=8?"(set)":`${n.slice(0,4)}\u2026${n.slice(-4)}`:"(empty)"}return t}pe();G();import Cn from"node:fs";import ki from"node:os";import pr from"node:path";import Hc from"node:crypto";var wi="\u2063omnish/c v1",Gf=/([nlra])=([^\s\]]*)/g;function ot(e){return e.replace(/-/g,"").slice(0,8)}function yi(e){return e.replace(/[\s\]\r\n]/g,"_").slice(0,64)}function Jf(e){let t=yi(ot(e.nodeId)),n=yi(e.label||""),r=e.role==="primary"?"p":"s",o=yi(e.activeNodeId?ot(e.activeNodeId):"");return`${wi} [n=${t} l=${n} r=${r} a=${o}]`}function Wc(e,t){let n=Jf(t);if(e.includes(n))return e;let r=e.replace(/\s+$/,"");return r.length===0?n:`${r}
|
|
88
|
+
|
|
89
|
+
${n}`}function Uc(e){if(!e||!e.includes(wi))return null;let t=e.indexOf(wi),n=e.slice(t),r=n.indexOf("["),o=n.indexOf("]");if(r<0||o<0||o<r)return null;let s=n.slice(r+1,o),i={};for(let l of s.matchAll(Gf))i[l[1]]=l[2]??"";if(!i.n)return null;let a=i.r==="p"?"primary":"secondary";return{nodeId:i.n,label:i.l??"",role:a,activeNodeId:i.a??""}}var Bc=3,Kf="node-id",zf="cluster-local.json",Dc=8;function jc(){return pr.join(H,zf)}function Si(){return pr.join(H,Kf)}function Xe(){j(H);let e=Si();try{if(Cn.existsSync(e)){let n=Cn.readFileSync(e,"utf8").trim();if(/^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(n))return n}}catch{}let t=Hc.randomUUID();return Cn.writeFileSync(e,`${t}
|
|
90
|
+
`,{mode:384}),t}function Gc(){return{schemaVersion:Bc,updatedAt:new Date().toISOString(),peers:[],senderBindings:{}}}function qf(e){if(!e||typeof e!="object")return{};let t={};for(let[n,r]of Object.entries(e)){if(!r||typeof r!="object")continue;let o=r;if(typeof o.nodeId!="string"||!o.nodeId)continue;let s=o.source==="config"?"config":"chat";t[n]={senderKey:typeof o.senderKey=="string"?o.senderKey:n,nodeId:o.nodeId,sinceIso:typeof o.sinceIso=="string"&&o.sinceIso?o.sinceIso:new Date(0).toISOString(),source:s}}return t}function ge(){let e=jc();try{let t=Cn.readFileSync(e,"utf8"),n=JSON.parse(t),r=Array.isArray(n.peers)?n.peers.filter(s=>typeof s=="object"&&s!==null&&typeof s.nodeId=="string"&&typeof s.label=="string").map(s=>({nodeId:s.nodeId,label:s.label,role:s.role==="primary"?"primary":"secondary",lastSeenIso:typeof s.lastSeenIso=="string"?s.lastSeenIso:new Date(0).toISOString()})):[],o=qf(n.senderBindings);return{schemaVersion:Bc,updatedAt:typeof n.updatedAt=="string"?n.updatedAt:new Date().toISOString(),peers:r,senderBindings:o}}catch{return Gc()}}function Yf(e,t){let n=pr.dirname(e);j(n);let r=`${JSON.stringify(t,null,2)}
|
|
91
|
+
`,o=pr.join(n,`.${pr.basename(e)}.tmp.${process.pid}.${Hc.randomBytes(4).toString("hex")}`);Cn.writeFileSync(o,r,{mode:384}),Cn.renameSync(o,e)}function xi(e){let t=jc(),n=Gc();for(let r=0;r<Dc;r++){n=ge(),e(n),n.updatedAt=new Date().toISOString();try{return Yf(t,n),n}catch{}}throw new Error(`Could not write cluster local state after ${Dc} attempts: ${t}`)}function Jc(e){return(e.clusterLabel??"").trim()||ki.hostname()}function ye(e){return e.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">")}function Kc(e,t){let n=e.peers.findIndex(r=>r.nodeId===t.nodeId);n>=0?e.peers[n]=t:e.peers.push(t)}function mr(e){return{nodeId:ot(Xe()),label:Jc(e),role:e.clusterRole,lastSeenIso:new Date().toISOString()}}function zc(e,t,n){let r=n.trim();if(!r)return{ok:!1,reason:"not-found"};let o=mr(t),s=[o];for(let l of e.peers)l.nodeId!==o.nodeId&&s.push(l);let i=r.toLowerCase();if(/^[0-9a-f]{8}$/i.test(r)){let l=s.find(d=>d.nodeId.toLowerCase()===i);if(l)return{ok:!0,peer:l}}let a=s.filter(l=>l.label.toLowerCase()===i);return a.length===1?{ok:!0,peer:a[0]}:a.length>1?{ok:!1,reason:"ambiguous-label",matches:a}:{ok:!1,reason:"not-found"}}function Ct(e,t){let n=ge(),r=n.senderBindings[t];if(r&&r.nodeId)return r;let o=e.clusterSenderBindings?.[t];if(typeof o=="string"&&o.trim()){let s=zc(n,e,o);if(s.ok)return{senderKey:t,nodeId:s.peer.nodeId,sinceIso:new Date(0).toISOString(),source:"config"}}return null}function vi(e,t,n="chat"){let r=$(),o=ge(),s=zc(o,r,t);if(!s.ok)return{state:o,resolved:s};let i=new Date().toISOString();return{state:xi(l=>{l.senderBindings[e]={senderKey:e,nodeId:s.peer.nodeId,sinceIso:i,source:n},Kc(l,{...s.peer,lastSeenIso:i})}),resolved:s}}function Qf(e){let t=null;return{state:xi(r=>{r.senderBindings[e]&&(t=r.senderBindings[e]??null,delete r.senderBindings[e])}),removed:t}}function qc(e,t){let n=ot(Xe()),r=new Date().toISOString();return xi(o=>{if(Kc(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 Yc(e,t){let n=Ct(t,e);return n?n.nodeId===ot(Xe()):!1}function Vf(e,t){let n=mr(t).nodeId,r=new Set([n]);for(let s of e.peers)r.add(s.nodeId);return[...r].sort()[0]??n}function He(e,t){return Vf(e,t)===ot(Xe())}function mo(e,t,n){let r=ot(Xe()),o=["*Computers*",""],s=n?Ct(t,n):null;n&&(s?o.push(`Your binding: \`${s.nodeId}\` (${s.source})`):o.push("Your binding: (none \u2014 send /c use <label-or-id>)"),o.push(""));let i=new Map;i.set(r,mr(t));for(let l of e.peers)i.set(l.nodeId,l);let a=[...i.values()].sort((l,d)=>l.label.localeCompare(d.label));if(a.length===0)return o.push("(no peers observed yet \u2014 run /c status from this chat)"),o.join(`
|
|
92
|
+
`);for(let l of a){let d=l.nodeId===r,c=[(s?l.nodeId===s.nodeId:!1)?"your binding":null,d?"you":null].filter(Boolean).join(", "),m=c?` (${c})`:"";o.push(`${Re}*${l.label}*${m}
|
|
67
93
|
id \`${l.nodeId}\` \xB7 role ${l.role}
|
|
68
94
|
seen ${l.lastSeenIso}`)}return o.push(""),o.push(`Updated: ${e.updatedAt}`),o.join(`
|
|
69
|
-
`)}function
|
|
70
|
-
`);for(let l of a){let
|
|
71
|
-
`)}function
|
|
72
|
-
`),
|
|
73
|
-
`);return{wa:
|
|
74
|
-
`),r=["<b>Computers</b> (per-sender bindings)","","Each allowlisted phone picks one machine to talk to. Other senders are not affected.","Selection persists; defaults can be set in config (clusterSenderBindings).","","Shorthand: /pcs \u2026 \xB7 /c \u2026","","\u2022 /c use <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: ${
|
|
75
|
-
`);return
|
|
76
|
-
`),
|
|
77
|
-
`);return
|
|
78
|
-
`),
|
|
79
|
-
`);return
|
|
80
|
-
`),
|
|
81
|
-
`);return
|
|
82
|
-
Config default still applies: \`${
|
|
83
|
-
No config default is set; nobody will answer until you /c use <label-or-id>.`;return p(`Cleared your chat binding.${
|
|
84
|
-
`)}function Qc(e){let t=oe(e);return["<b>Config</b> (secrets masked)","",`<b>gatewayMode</b> ${pe(e.gatewayMode)}`,`<b>commandPrefix</b> ${pe(e.commandPrefix)}`,`<b>shell</b> ${pe(e.shell)}`,`<b>syncTimeoutMs</b> ${e.syncTimeoutMs}`,`<b>syncMaxBytes</b> ${e.syncMaxBytes}`,`<b>jobLogTailLines</b> ${e.jobLogTailLines}`,"","<b>Cluster</b>",`clusterEnabled: ${e.clusterEnabled} \xB7 clusterRole: ${pe(e.clusterRole)}`,`clusterLabel: ${pe(e.clusterLabel||"(hostname)")}`,`clusterSenderBindings: ${Object.keys(e.clusterSenderBindings??{}).length} entries`,"","<b>Telegram</b>",`telegramBotToken: ${pe($o(t))}`,`telegramAllowFrom: ${e.telegramAllowFrom.length} entries`,"",`<b>WhatsApp allowFrom</b> ${e.allowFrom.length} entries`,"","<b>Apps</b>",`${e.appsCols}\xD7${e.appsRows} flush ${e.appsFlushMs}/${e.appsMinIntervalMs} ms \u2026`,"","<b>Files</b>",`${pe(e.fileReceiveRootMode)} \xB7 inbox ${pe(e.fileInboxSubdir)}`,"","<b>Service (chat)</b>",`serviceInstallFromChat: ${e.serviceInstallFromChat}`,"","<b>Updates (optional)</b>",`updateCheckEnabled: ${e.updateCheckEnabled} \xB7 interval ms: ${e.updateCheckIntervalMs}`,`package: <code>${pe(e.updateCheckPackageName)}</code> \xB7 info URL: ${e.updateInfoUrl?"set":"empty"}`,"",`<code>${pe(P)}</code>`].join(`
|
|
85
|
-
`)}function ra(e,t){if(!na(t))return null;let n=t;if(n==="telegramBotToken")return`telegramBotToken: ${$o(oe(e))}`;let r=e[n];return`${t}: ${typeof r=="object"?JSON.stringify(r):String(r)}`}function Yc(e,t){let n=ra(e,t);if(!n)return null;let r=n.split(": ");return r.length<2?pe(n):`<b>${pe(r[0])}</b> ${pe(r.slice(1).join(": "))}`}function er(e,t){let n=ea(t),r=!1,o=!1,s=!1;if(e==="telegramBotToken"){if(!Ze(n))throw new Error("Invalid bot token format (expect digits:secret from BotFather).");return vt(n),r=!0,s=!0,{reloadSuggested:r,shellWarning:o,tokenSaved:s}}let i=R();switch(e){case"gatewayMode":{let a=On(n);if(!a)throw new Error("gatewayMode: use whatsapp | telegram | both (or wa, tg, b)");i.gatewayMode=a,r=!0;break}case"clusterEnabled":{let a=an(n);if(a===null)throw new Error("clusterEnabled: true or false");i.clusterEnabled=a;break}case"clusterRole":{let a=n.trim().toLowerCase();if(a!=="primary"&&a!=="secondary")throw new Error("clusterRole: primary | secondary");i.clusterRole=a;break}case"clusterLabel":i.clusterLabel=n.trim().slice(0,128);break;case"clusterSenderBindings":{let a=n.trim();if(a===""||a==="{}"||a.toLowerCase()==="clear"){i.clusterSenderBindings={};break}let l;try{l=JSON.parse(a)}catch{throw new Error('clusterSenderBindings: pass a JSON object, e.g. {"wa:+15551234567":"workshop-box"}')}if(!l||typeof l!="object"||Array.isArray(l))throw new Error("clusterSenderBindings must be a JSON object of sender \u2192 label/id");let u={};for(let[c,d]of Object.entries(l)){if(typeof d!="string"||!d.trim())throw new Error(`clusterSenderBindings.${c}: value must be a non-empty string`);u[c]=d.trim()}i.clusterSenderBindings=u;break}case"commandPrefix":if(!n.trim())throw new Error("commandPrefix cannot be empty");i.commandPrefix=n.trim().slice(0,32);break;case"syncTimeoutMs":{let a=Number.parseInt(n,10);if(!Number.isFinite(a)||a<=0)throw new Error("syncTimeoutMs: positive integer (ms)");i.syncTimeoutMs=a;break}case"syncMaxBytes":{let a=Number.parseInt(n,10);if(!Number.isFinite(a)||a<=0)throw new Error("syncMaxBytes: positive integer");i.syncMaxBytes=a;break}case"jobLogTailLines":{let a=Number.parseInt(n,10);if(!Number.isFinite(a)||a<=0)throw new Error("jobLogTailLines: positive integer");i.jobLogTailLines=a;break}case"shell":if(!n.trim())throw new Error("shell: non-empty path");i.shell=n.trim().slice(0,4096),o=!0;break;case"recipesMacroDefaultCommand":if(!n.trim())throw new Error('recipesMacroDefaultCommand: non-empty shell snippet with "$OMNISH_TASK"');i.recipesMacroDefaultCommand=ea(n).trim().slice(0,4096);break;case"appsCols":case"appsRows":case"appsFlushMs":case"appsMinIntervalMs":case"appsMaxFlushBytes":case"appsMaxSessions":case"appsMaxSessionsTotal":case"appsMaxWaChars":case"appsLogTailLines":case"appsSubmitDelayMs":case"appsClearInputDelayMs":case"recipesMaxTaskChars":case"fileSendMaxBytes":case"fileReceiveMaxBytes":{let a=Number.parseInt(n,10);if(!Number.isFinite(a)||a<0)throw new Error(`${e}: non-negative integer`);i[e]=a;break}case"appsClearInput":{let a=an(n);if(a===null)throw new Error("appsClearInput: true or false");i.appsClearInput=a;break}case"appsClearInputSequence":i.appsClearInputSequence=n.trim().slice(0,200);break;case"fileInboxSubdir":i.fileInboxSubdir=n.trim().slice(0,80);break;case"fileReceiveRootMode":{let a=n.trim();if(!ta.has(a))throw new Error(`fileReceiveRootMode: ${[...ta].join(" | ")}`);i.fileReceiveRootMode=a;break}case"fileReceiveRootPath":i.fileReceiveRootPath=n.trim().slice(0,4096);break;case"recipesAllowDangerousBuiltins":{let a=an(n);if(a===null)throw new Error("recipesAllowDangerousBuiltins: true or false");i.recipesAllowDangerousBuiltins=a;break}case"serviceInstallFromChat":{let a=an(n);if(a===null)throw new Error("serviceInstallFromChat: true or false");i.serviceInstallFromChat=a;break}case"updateCheckEnabled":{let a=an(n);if(a===null)throw new Error("updateCheckEnabled: true or false");i.updateCheckEnabled=a;break}case"updateCheckIntervalMs":{let a=Number.parseInt(n,10);if(!Number.isFinite(a)||a<36e5)throw new Error("updateCheckIntervalMs: integer ms, minimum 3600000 (1 hour)");i.updateCheckIntervalMs=Math.min(6048e5,a);break}case"updateCheckPackageName":if(!n.trim())throw new Error("updateCheckPackageName: non-empty npm package name");i.updateCheckPackageName=n.trim().slice(0,214);break;case"updateInfoUrl":i.updateInfoUrl=n.trim().slice(0,2048);break}return He(i),{reloadSuggested:r,shellWarning:o,tokenSaved:s}}async function oa(e,t){let n=e.trim(),r=n.toLowerCase();if(!n||r==="help")return j(Fs());if(r==="keys"||r==="help keys")return p(["*Configurable keys* (/config set)","",Ot.join(", "),"","Examples:","/config set clusterLabel work-laptop",'/config set fileReceiveRootPath "/path/with spaces"',"/config set gatewayMode both"].join(`
|
|
86
|
-
`));if(r==="show"){let i=R();return Y(qc(i),Qc(i))}let o=n.match(/^get\s+(\S+)\s*$/i);if(o){let i=o[1],a=R(),l=ra(a,i);if(!l)return p(`Unknown key "${i}". /config keys`);let u=Yc(a,i);return Y(`${l}
|
|
87
|
-
|
|
88
|
-
${P}`,`${u}<br/><br/><code>${pe(P)}</code>`)}let s=n.match(/^set\s+(\S+)\s+([\s\S]+)$/i);if(s){let i=s[1],a=s[2]??"";if(!na(i))return p(`Unknown key "${i}". /config keys`);try{let{reloadSuggested:l,shellWarning:u,tokenSaved:c}=er(i,a),d="";if(t?.reload&&l){let h=await t.reload();d=h.ok?`
|
|
89
|
-
Reload: ${h.summary}`:`
|
|
90
|
-
Reload failed: ${h.error}`}else l?d=`
|
|
91
|
-
Send /reload while omnish run is active (gatewayMode / Telegram).`:d=`
|
|
92
|
-
Send /reload to pick up changes where applicable.`;let m=u?`
|
|
93
|
-
\u26A0 shell changed \u2014 affects all commands run via omnish.`:"",y=c?`
|
|
94
|
-
telegramBotToken saved (not echoed).`:"",g=`Set *${i}* (saved).${y}${m}${d}`,f=`<b>Set ${pe(i)}</b> (saved).${y?"<br/>token saved (not echoed).":""}${u?"<br/>\u26A0 shell path changed.":""}${pe(d)}`;return Y(g,f)}catch(l){return p(`Error: ${String(l)}`)}}return p("Unknown /config command. Try /config help or /config show")}import aa from"node:fs";import Ft from"node:process";function tr(e){let t=e.nodePath,n=e.scriptPath,r=e.omnishHome,o=["Linux (systemd --user), copy-paste on the host:","","mkdir -p ~/.config/systemd/user","# create ~/.config/systemd/user/omnish.service with ExecStart using:",`# ${t} ${n} run`,`# and Environment=OMNISH_HOME=${r}`,"# The generated unit uses Restart=on-failure and RestartSec=5 (tunable).","","systemctl --user daemon-reload","systemctl --user enable --now omnish.service","",'Optional (run without interactive login): loginctl enable-linger "$USER"',"","Full guide: docs/guides/background-and-boot.md \xB7 https://omnish.dev"].join(`
|
|
95
|
+
`)}function bi(e,t,n){let r=ot(Xe()),o=["<b>Computers</b>",""],s=n?Ct(t,n):null;n&&(s?o.push(`Your binding: <code>${ye(s.nodeId)}</code> (${ye(s.source)})`):o.push("Your binding: (none \u2014 send /c use <label-or-id>)"),o.push(""));let i=new Map;i.set(r,mr(t));for(let l of e.peers)i.set(l.nodeId,l);let a=[...i.values()].sort((l,d)=>l.label.localeCompare(d.label));if(a.length===0)return o.push("(no peers observed yet \u2014 run /c status from this chat)"),o.join(`
|
|
96
|
+
`);for(let l of a){let d=l.nodeId===r,c=[(s?l.nodeId===s.nodeId:!1)?"your binding":null,d?"you":null].filter(Boolean).join(", "),m=c?` (${ye(c)})`:"";o.push(`\u2022 <b>${ye(l.label)}</b>${m}<br/> id <code>${ye(l.nodeId)}</code> \xB7 role ${ye(l.role)}<br/> seen ${ye(l.lastSeenIso)}`)}return o.push(""),o.push(`Updated: ${ye(e.updatedAt)}`),o.join(`
|
|
97
|
+
`)}function $i(e,t,n){return mo(e,t,n??null).replace(/\*([^*]+)\*/g,"$1").replace(/`([^`]+)`/g,"$1")}function Ri(e,t){let n=Xe(),r=ot(n),o=ge(),s=e.clusterRole,i=Jc(e),a=t?Ct(e,t):null,l=a?a.nodeId===r:!1,d=t?a?`your binding: ${a.nodeId} (${a.source}${l?", here":""})`:"your binding: (none)":"",u=["*This computer*","",`label: ${i}`,`node id: \`${r}\` (full id in ${Si()})`,`host: ${ki.hostname()}`,`clusterRole: ${s}`,d,`peers known: ${o.peers.length}`,`senderBindings (chat): ${Object.keys(o.senderBindings).length}`].filter(Boolean).join(`
|
|
98
|
+
`),c=["<b>This computer</b>","",`label: ${ye(i)}`,`node id: <code>${ye(r)}</code> (full id in ${ye(Si())})`,`host: ${ye(ki.hostname())}`,`clusterRole: ${ye(s)}`,t?ye(d):"",`peers known: ${o.peers.length}`,`senderBindings (chat): ${Object.keys(o.senderBindings).length}`].filter(Boolean).join(`
|
|
99
|
+
`);return{wa:u,tg:c}}function Xf(e){let t=e.clusterRole,n=["*Computers* (per-sender bindings)","","Each allowlisted phone picks one machine to talk to. Other senders are not affected.","Selection persists; defaults can be set in config (clusterSenderBindings).","","Shorthand: /pcs \u2026 \xB7 /c \u2026","",`${Re}/c use <label-or-id> \u2014 bind your messages to that machine`,`${Re}/c here \u2014 bind your messages to THIS machine`,`${Re}/c using \u2014 show your current binding`,`${Re}/c unuse \u2014 clear your chat-set binding (config default still applies, if any)`,`${Re}/c status \u2014 every online host replies with its own paragraph`,`${Re}/c list \u2014 locally known roster (single responder)`,`${Re}/c help \u2014 this help`,"",`clusterRole on this host: ${t}`].join(`
|
|
100
|
+
`),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: ${ye(t)}`].join(`
|
|
101
|
+
`);return ue(n,r)}function Zf(e,t){if(t.ok)return p("");if(t.reason==="ambiguous-label"){let n=(t.matches??[]).map(r=>`\`${r.nodeId}\` (${r.label})`).join(", ");return p(`Label "${e}" matches multiple machines: ${n}. Use the 8-character id with /c use <id>.`)}return p(`No machine matches "${e}". Send /c list to see ids and labels, or /c status to refresh the roster.`)}function Qc(e,t,n){let r=t.trim().split(/\s+/),o=r[0]?.toLowerCase()??"";if(!o||o==="help"){if(!e.clusterEnabled&&o!=="help"){let l=ge();return He(l,e)?p("Cluster is disabled. Enable with: /config set clusterEnabled true (then /c use <label-or-id>). /c help for the new commands."):null}let a=ge();return He(a,e)?Xf(e):null}let s=ot(Xe());if(o==="use"||o==="bind"){let a=r.slice(1).join(" ").trim();if(!n){let y=ge();return He(y,e)?p("/c use is only available on chats with a known sender (allowlisted WhatsApp/Telegram)."):null}if(!a){let y=ge();return He(y,e)?p("Usage: /c use <label-or-id>"):null}e.clusterEnabled||Yr(!0);let d=ge().senderBindings[n]??null,{state:u,resolved:c}=vi(n,a,"chat");if(!c.ok)return He(u,e)?Zf(a,c):null;let m=c.peer.nodeId===s,f=d?d.nodeId===s:!1;if(!m)return null;let h=[`*Bound to ${c.peer.label}*`,`id \`${c.peer.nodeId}\``,"","Your messages now route to this machine. Other senders are not affected.","",mo(u,e,n)].join(`
|
|
102
|
+
`),g=[`<b>Bound to ${ye(c.peer.label)}</b>`,`id <code>${ye(c.peer.nodeId)}</code>`,"","Your messages now route to this machine. Other senders are not affected.","",bi(u,e,n)].join(`
|
|
103
|
+
`);return ue(h,g)}if(o==="here"||o==="take"){if(!n){let c=ge();return He(c,e)?p("/c here is only available on chats with a known sender (allowlisted WhatsApp/Telegram)."):null}e.clusterEnabled||Yr(!0);let{state:a,resolved:l}=vi(n,s,"chat");if(!l.ok)return He(a,e)?p("Could not bind to this machine."):null;let d=["*Bound to this machine.*","","Your messages now route here. Other senders are not affected.","",mo(a,e,n)].join(`
|
|
104
|
+
`),u=["<b>Bound to this machine.</b>","","Your messages now route here. Other senders are not affected.","",bi(a,e,n)].join(`
|
|
105
|
+
`);return ue(d,u)}if(o==="using"){if(!n){let d=ge();return He(d,e)?p("/c using is only available on chats with a known sender (allowlisted WhatsApp/Telegram)."):null}let a=Ct(e,n);if(a){if(a.nodeId!==s)return null;let c=[...ge().peers,mr(e)].find(h=>h.nodeId===a.nodeId)?.label??"(unknown label)",m=[`*Bound to ${c}*`,`id \`${a.nodeId}\` \xB7 source ${a.source}`,a.source==="chat"?`since ${a.sinceIso}`:"(from config)"].join(`
|
|
106
|
+
`),f=[`<b>Bound to ${ye(c)}</b>`,`id <code>${ye(a.nodeId)}</code> \xB7 source ${ye(a.source)}`,a.source==="chat"?`since ${ye(a.sinceIso)}`:"(from config)"].join(`
|
|
107
|
+
`);return ue(m,f)}let l=ge();return He(l,e)?p("You are not bound to any machine. Send /c use <label-or-id> to bind, or set a default in config.json (clusterSenderBindings)."):null}if(o==="unuse"||o==="unbind"||o==="release"){if(!n){let m=ge();return He(m,e)?p("/c unuse is only available on chats with a known sender (allowlisted WhatsApp/Telegram)."):null}let l=ge().senderBindings[n]??null,{state:d}=Qf(n);if(l){if(l.nodeId!==s)return null}else if(!He(d,e))return null;let u=Ct(e,n),c=u?`
|
|
108
|
+
Config default still applies: \`${u.nodeId}\`.`:`
|
|
109
|
+
No config default is set; nobody will answer until you /c use <label-or-id>.`;return p(`Cleared your chat binding.${c}`)}if(o==="step-down"||o==="stepdown"){let a=ge();return He(a,e)?p("/c step-down is no longer used. The cluster is per-sender: send /c unuse to clear your binding."):null}if(o==="status"){let a=Ri(e,n);return ue(a.wa,a.tg)}if(o==="list"){let a=ge(),l=n?Ct(e,n):null;if(l){if(l.nodeId!==s)return null}else if(!He(a,e))return null;return ue(mo(a,e,n??null),bi(a,e,n??null))}let i=ge();return He(i,e)?p(`Unknown /c subcommand "${o}". Try /c help`):null}function Vc(e,t){return Yr(!0),vi(e,t,"chat")}qe();be();function eh(e){return`wa:${e}`}function Ci(e){return`tg:${e}`}function Xc(e){return{peerKey:eh(e.fromJid),text:e.text,waMessageId:e.messageId,mediaSavedPath:e.mediaSavedPath,mediaError:e.mediaError}}G();function fo(e){let t=e.nodePath,n=e.scriptPath,r=e.omnishHome,o=["Linux (systemd --user), copy-paste on the host:","","mkdir -p ~/.config/systemd/user","# create ~/.config/systemd/user/omnish.service with ExecStart using:",`# ${t} ${n} run`,`# and Environment=OMNISH_HOME=${r}`,"# The generated unit uses Restart=on-failure and RestartSec=5 (tunable).","","systemctl --user daemon-reload","systemctl --user enable --now omnish.service","",'Optional (run without interactive login): loginctl enable-linger "$USER"',"","Full guide: docs/guides/background-and-boot.md \xB7 https://omnish.dev"].join(`
|
|
95
110
|
`),s=["macOS (LaunchAgent), copy-paste on the host:","","# Use contrib/dev.omnish.gateway.plist from the repo with paths filled as:",`# Node: ${t}`,`# Script: ${n}`,`# OMNISH_HOME: ${r}`,"# Generated plist uses KeepAlive for crash restart.","","cp \u2026/dev.omnish.gateway.plist ~/Library/LaunchAgents/","launchctl bootstrap gui/$(id -u) ~/Library/LaunchAgents/dev.omnish.gateway.plist","launchctl kickstart -k gui/$(id -u)/dev.omnish.gateway","","https://omnish.dev"].join(`
|
|
96
111
|
`),i=["Windows: Task Scheduler \u2014 Action = Start a program:","",`Program: ${t}`,`Arguments: "${n}" run`,"","Set user env OMNISH_HOME if needed. Optional XML: contrib/omnish-windows-task.xml","","https://omnish.dev"].join(`
|
|
97
112
|
`);return process.platform==="linux"?o:process.platform==="darwin"?s:process.platform==="win32"?i:[o,"","---","",s].join(`
|
|
98
|
-
`)}import{execFileSync as
|
|
113
|
+
`)}G();import{execFileSync as jt}from"node:child_process";import Mn from"node:fs";import hr from"node:os";import st from"node:path";import Fe from"node:process";function nn(){let e=Fe.execPath,t=Fe.argv[1];if(!t||!t.trim())return{nodePath:e,scriptPath:"",omnishHome:H,error:"Cannot resolve gateway entry script (missing argv[1]). Run omnish from its CLI entry."};let n=st.resolve(t),r=Fe.env.OMNISH_HOME?.trim(),o=r?st.resolve(r):H;return{nodePath:e,scriptPath:n,omnishHome:o}}function Zc(e){return/[ "'\\\s]/.test(e)?`"${e.replace(/\\/g,"\\\\").replace(/"/g,'\\"')}"`:e}function th(e){return`Environment="OMNISH_HOME=${e.replace(/\\/g,"\\\\").replace(/"/g,'\\"')}"`}function nh(e){return`[Unit]
|
|
99
114
|
Description=omnish gateway (WhatsApp/Telegram)
|
|
100
115
|
After=network-online.target
|
|
101
116
|
Wants=network-online.target
|
|
102
117
|
|
|
103
118
|
[Service]
|
|
104
119
|
Type=simple
|
|
105
|
-
ExecStart=${`${
|
|
106
|
-
${
|
|
120
|
+
ExecStart=${`${Zc(e.nodePath)} ${Zc(e.scriptPath)} run`}
|
|
121
|
+
${th(e.omnishHome)}
|
|
107
122
|
Restart=on-failure
|
|
108
123
|
RestartSec=5
|
|
109
124
|
|
|
110
125
|
[Install]
|
|
111
126
|
WantedBy=default.target
|
|
112
|
-
`}function
|
|
127
|
+
`}function fr(e){return e.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""")}function rh(e){let t=hr.homedir(),n=st.join(e.omnishHome,"logs","launchd-stdout.log"),r=st.join(e.omnishHome,"logs","launchd-stderr.log");j(st.dirname(n));let o=fr(e.nodePath),s=fr(e.scriptPath),i=fr(e.omnishHome),a=fr(n),l=fr(r);return`<?xml version="1.0" encoding="UTF-8"?>
|
|
113
128
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
114
129
|
<plist version="1.0">
|
|
115
130
|
<dict>
|
|
@@ -136,20 +151,34 @@ WantedBy=default.target
|
|
|
136
151
|
<string>${l}</string>
|
|
137
152
|
</dict>
|
|
138
153
|
</plist>
|
|
139
|
-
`}function
|
|
154
|
+
`}function ho(){let e=nn();if(e.error)return{ok:!1,detail:e.error};if(Fe.platform==="win32")return{ok:!1,detail:"Automatic install is not supported on Windows from chat. Use Task Scheduler (see /service instructions) or omnish-windows-task.xml in the repo contrib folder."};if(Fe.platform==="darwin")try{let t=st.join(hr.homedir(),"Library/LaunchAgents/dev.omnish.gateway.plist");j(st.dirname(t));let n=rh(e);Mn.writeFileSync(t,n,{mode:384});let r=typeof Fe.getuid=="function"?Fe.getuid():null;if(r===null||r<0)return{ok:!1,detail:"Could not read user id for launchctl."};let o=`gui/${r}`;try{jt("launchctl",["bootout",o,t],{stdio:"pipe"})}catch{}return jt("launchctl",["bootstrap",o,t],{stdio:"inherit"}),jt("launchctl",["kickstart","-k",`${o}/dev.omnish.gateway`],{stdio:"inherit"}),{ok:!0,detail:"LaunchAgent installed: ~/Library/LaunchAgents/dev.omnish.gateway.plist (label dev.omnish.gateway)."}}catch(t){return{ok:!1,detail:String(t)}}if(Fe.platform==="linux")try{let t=st.join(hr.homedir(),".config","systemd","user");j(t);let n=st.join(t,"omnish.service"),r=nh(e);return Mn.writeFileSync(n,r,{mode:420}),jt("systemctl",["--user","daemon-reload"],{stdio:"inherit"}),jt("systemctl",["--user","enable","--now","omnish.service"],{stdio:"inherit"}),{ok:!0,detail:`User systemd unit installed: ${n} \u2014 enabled and started.`}}catch(t){return{ok:!1,detail:String(t)}}return{ok:!1,detail:`Unsupported platform: ${Fe.platform}`}}function go(){if(Fe.platform==="win32")return{ok:!0,detail:"Windows: remove the omnish task from Task Scheduler manually on this PC. See /service instructions."};if(Fe.platform==="darwin")try{let e=st.join(hr.homedir(),"Library/LaunchAgents/dev.omnish.gateway.plist"),n=`gui/${typeof Fe.getuid=="function"?Fe.getuid():0}`;if(Mn.existsSync(e)){try{jt("launchctl",["bootout",n,e],{stdio:"pipe"})}catch{}Mn.unlinkSync(e)}return{ok:!0,detail:"LaunchAgent dev.omnish.gateway removed (if it was present)."}}catch(e){return{ok:!1,detail:String(e)}}if(Fe.platform==="linux")try{let e=st.join(hr.homedir(),".config","systemd","user","omnish.service");try{jt("systemctl",["--user","disable","--now","omnish.service"],{stdio:"pipe"})}catch{}Mn.existsSync(e)&&Mn.unlinkSync(e);try{jt("systemctl",["--user","daemon-reload"],{stdio:"pipe"})}catch{}return{ok:!0,detail:"User systemd unit omnish.service removed (if it was present)."}}catch(e){return{ok:!1,detail:String(e)}}return{ok:!1,detail:`Unsupported platform: ${Fe.platform}`}}import eu from"node:fs";var yo=48e3;function wo(e,t){try{if(!eu.existsSync(e))return"(no log file yet \u2014 start with `omnish run -d` or the systemd/LaunchAgent service.)";let n=eu.readFileSync(e),o=(n.length>yo?n.subarray(n.length-yo):n).toString("utf8");return n.length>yo&&(o=`\u2026(truncated to last ${yo} bytes)
|
|
140
155
|
${o}`),o.split(/\r?\n/).slice(-t).join(`
|
|
141
|
-
`).trimEnd()||"(empty)"}catch(n){return`Could not read log: ${String(n)}`}}var
|
|
142
|
-
|
|
143
|
-
`),d=["<
|
|
144
|
-
`);return
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
`,"
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
156
|
+
`).trimEnd()||"(empty)"}catch(n){return`Could not read log: ${String(n)}`}}import Gt from"node:fs";import $h from"node:path";G();import oh from"node:crypto";import Ei from"node:fs";import sh from"node:path";var Pi=/^[a-zA-Z0-9][a-zA-Z0-9_-]{0,31}$/,En="__omnish_recipes_global__",Ai=new Set(["help","list","show","add","set","remove","rm","del","run","r","online"]),Tn=new Map,tu=!1;function ih(){try{let e=Ei.readFileSync(Dr,"utf8"),t=JSON.parse(e);if(t&&typeof t=="object")for(let[n,r]of Object.entries(t)){if(!r||typeof r!="object")continue;let o={};for(let[s,i]of Object.entries(r)){let a=String(s).toLowerCase();i&&typeof i=="object"&&typeof i.command=="string"&&(o[a]=Be(i))}Tn.set(n,o)}}catch{}}function ah(e){if(!Array.isArray(e)||e.length===0)return;let t=[];for(let n of e)if(typeof n=="string"&&n.trim())t.push({cmd:n.trim()});else if(n&&typeof n=="object"){let r=n,o=typeof r.cmd=="string"?r.cmd.trim():"";if(!o)continue;t.push({cmd:o,label:typeof r.label=="string"?r.label.trim().slice(0,80):void 0,continueOnFail:typeof r.continueOnFail=="boolean"?r.continueOnFail:void 0})}return t.length>0?t:void 0}function Be(e){let t=typeof e.taskEnv=="string"&&/^[A-Za-z_][A-Za-z0-9_]*$/.test(e.taskEnv)?e.taskEnv:"OMNISH_TASK",n=typeof e.promptTemplate=="string"&&e.promptTemplate.trim().length>0?e.promptTemplate.trim():void 0,r=ah(e.steps),o={command:String(e.command).trim(),taskEnv:t,label:typeof e.label=="string"?e.label.trim().slice(0,80):void 0,description:typeof e.description=="string"?e.description.trim().slice(0,200):void 0,category:typeof e.category=="string"?e.category.trim().slice(0,40):void 0,featured:typeof e.featured=="boolean"?e.featured:void 0,dangerous:typeof e.dangerous=="boolean"?e.dangerous:void 0};return n!==void 0&&(o.promptTemplate=n),r!==void 0&&(o.steps=r),o}function ko(){j(sh.dirname(Dr));let e={};for(let[t,n]of Tn)Object.entries(n).length>0&&(e[t]={...n});Ei.writeFileSync(Dr,JSON.stringify(e,null,2)+`
|
|
157
|
+
`,{mode:384})}function So(){tu||(ih(),tu=!0)}function nu(e){return So(),Tn.get(e)??{}}function Mi(e){So();let t=Tn.get(e);return t||(t={},Tn.set(e,t)),t}function lh(e,t){let n=e.taskEnv??"OMNISH_TASK",r=t.recipesMacroDefaultCommand.trim();return rn(r,n).ok?Be({...e,command:r,promptTemplate:e.command}):e}function ch(e,t){if(e.promptTemplate||rn(e.command,t).ok||!Pn(e.command,t))return!1;let n=e.command;return n.includes("```")||n.length>2e3||/^"Create\b/i.test(n)||n.includes("<<<")||n.includes(`
|
|
158
|
+
### `)}function Ii(e,t){let n=Be(e),r=n.taskEnv??"OMNISH_TASK";return!n.promptTemplate&&ch(n,r)&&(n=lh(n,t)),n}function uh(e){try{let t=Ei.readFileSync(Ur,"utf8"),n=JSON.parse(t);if(!n||typeof n!="object")return{};let r={};for(let[o,s]of Object.entries(n)){let i=o.trim().toLowerCase();if(!(!Pi.test(i)||Ai.has(i))&&s&&typeof s=="object"&&typeof s.command=="string"){let l=Ii(s,e),d=on(l);if(!d.ok){console.warn(`[omnish] recipes.json: skipping "${i}": ${d.error}`);continue}r[i]=l}}return r}catch{return{}}}function dh(e){return{claude:{command:'claude -p "$OMNISH_TASK" --allowedTools all --dangerously-skip-permissions',taskEnv:"OMNISH_TASK",label:"Claude Code",description:"Anthropic Claude Code CLI (requires `claude` on PATH)",category:"agents",featured:!0,dangerous:e.recipesAllowDangerousBuiltins},codex:{command:'codex "$OMNISH_TASK"',taskEnv:"OMNISH_TASK",label:"Codex CLI",description:"OpenAI Codex CLI if installed; override in recipes.json",category:"agents",featured:!1},gemini:{command:'gemini -p "$OMNISH_TASK"',taskEnv:"OMNISH_TASK",label:"Gemini CLI",description:"Google Gemini CLI if installed; override in recipes.json",category:"agents",featured:!1},cursor:{command:"agent --yolo --force -p ```Before writing any code:\n1. Analyze the codebase and state your full implementation plan\n2. List every file you will touch\n3. Identify any risks or ambiguities\n4. Then execute the plan step by step, <task> $OMNISH_TASK </task>```",taskEnv:"OMNISH_TASK",label:"Cursor Agent",description:"Cursor Agent if installed; override in recipes.json",category:"agents",featured:!0}}}function bo(e,t,n){for(let[r,o]of Object.entries(t)){let s=r.toLowerCase();!Pi.test(s)||Ai.has(s)||o.command.trim()&&e.set(s,{...o,name:s,source:n})}}function Ti(e,t){let n=nu(e),r={};for(let[o,s]of Object.entries(n)){let i=Ii(s,t);on(i).ok&&(r[o]=i)}return r}function ru(e,t){let n=new Map;return bo(n,dh(t),"builtin"),bo(n,uh(t),"global"),bo(n,Ti(En,t),"shared"),bo(n,Ti(e,t),"peer"),n}function je(e,t,n){let r=n.trim().toLowerCase();return ru(e,t).get(r)}function ou(e){let t=e.trim();if(/^--global$/i.test(t))return{mode:"global",remainder:""};if(/^-g$/i.test(t))return{mode:"global",remainder:""};if(/^--chat$/i.test(t))return{mode:"chat",remainder:""};if(/^-p$/i.test(t))return{mode:"chat",remainder:""};let n=/^--global\s+/i.exec(t);if(n)return{mode:"global",remainder:t.slice(n[0].length).trimStart()};let r=/^-g\s+/i.exec(t);if(r)return{mode:"global",remainder:t.slice(r[0].length).trimStart()};let o=/^--chat\s+/i.exec(t);if(o)return{mode:"chat",remainder:t.slice(o[0].length).trimStart()};let s=/^-p\s+/i.exec(t);return s?{mode:"chat",remainder:t.slice(s[0].length).trimStart()}:{mode:"resolved",remainder:t}}function Li(e){let t=e.trim();if(/^--global$/i.test(t))return{scope:"global",remainder:"",explicit:!0};if(/^-g$/i.test(t))return{scope:"global",remainder:"",explicit:!0};if(/^--chat$/i.test(t))return{scope:"chat",remainder:"",explicit:!0};if(/^-p$/i.test(t))return{scope:"chat",remainder:"",explicit:!0};let n=/^--global\s+([\s\S]*)$/i.exec(t);if(n?.[1]!==void 0)return{scope:"global",remainder:n[1].trimStart(),explicit:!0};let r=/^-g\s+([\s\S]*)$/i.exec(t);if(r?.[1]!==void 0)return{scope:"global",remainder:r[1].trimStart(),explicit:!0};let o=/^--chat\s+([\s\S]*)$/i.exec(t);if(o?.[1]!==void 0)return{scope:"chat",remainder:o[1].trimStart(),explicit:!0};let s=/^-p\s+([\s\S]*)$/i.exec(t);return s?.[1]!==void 0?{scope:"chat",remainder:s[1].trimStart(),explicit:!0}:{scope:"chat",remainder:t,explicit:!1}}function Oi(e){let t=Li(e);return{scope:t.scope,remainder:t.remainder}}function su(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 ph(e){let t=e.trim().toLowerCase();if(t==="--global"||t==="-g")return"global";if(t==="--chat"||t==="-p")return"chat"}function iu(e){let t=e.trim().match(/^(\S+)\s+(--global|-g|--chat|-p)\s*$/i);if(!t?.[1]||!t[2])return;let n=ph(t[2]);if(n)return{name:t[1],target:n}}function Mt(e,t,n,r){let o=r.trim().toLowerCase(),s=e==="global"?En:t,i=nu(s)[o];if(i===void 0)return;let a=Ii(i,n);if(on(a).ok)return{...a,name:o,source:e==="global"?"shared":"peer"}}function au(e,t,n){if(n==="merged")return[];let r=n==="global"?En:e,o=n==="global"?"shared":"peer",s=Ti(r,t);return Object.entries(s).map(([i,a])=>({...a,name:i,source:o})).sort((i,a)=>i.name.localeCompare(a.name))}function gt(e){let t=e.trim();if(!t)return{ok:!1,error:"Name is empty."};let n=t.toLowerCase();return Pi.test(n)?Ai.has(n)?{ok:!1,error:`Reserved name: ${n}`}:{ok:!0,normalized:n}:{ok:!1,error:`Invalid name (use letters, digits, _ or -; max 32 chars): ${t}`}}function vo(e,t){let n=e.replace(/\r\n/g,`
|
|
159
|
+
`).trim();return n?t>0&&n.length>t?{ok:!1,error:`Task too long (max ${t} characters).`}:{ok:!0,task:n}:{ok:!1,error:"Task is empty."}}function Pn(e,t){return e.includes("$")?e.includes(t):!1}function rn(e,t){let n=e.trim();if(!n)return{ok:!1,error:"Command is empty."};if(!/^[A-Za-z_][A-Za-z0-9_]*$/.test(t))return{ok:!1,error:"Invalid taskEnv (use letters, digits, _)."};if(!Pn(n,t))return{ok:!1,error:`Command must reference "$${t}" so the task is passed via the environment.`};let r=`"$${t}"`;return n.includes(r)?n.includes("```")?{ok:!1,error:"Recipe command must not contain markdown code fences (```). Use promptTemplate or split with a --- line (see /run help)."}:{ok:!0}:{ok:!1,error:`Command must include ${r} (quoted).`}}function on(e){let t=e.taskEnv??"OMNISH_TASK",n=rn(e.command,t);return n.ok?e.promptTemplate!==void 0&&e.promptTemplate.trim().length===0?{ok:!1,error:"promptTemplate cannot be empty."}:{ok:!0}:n}function mh(e){let t=e,n=null,r=!1,o=-1,s=-1;for(let l=0;l<t.length;l+=1){let d=t[l];if(r){r=!1;continue}if(d==="\\"){r=!0;continue}if(n){d===n&&(n=null);continue}if(d==='"'||d==="'"){n=d;continue}if(d!=="-"||t[l+1]!=="-")continue;let u=l===0?"":t[l-1];if(u&&!/\s/.test(u))continue;let c=t.slice(l+2),m=/^(template|tamplate)\b/i.exec(c);if(!m)continue;let f=l+2+m[0].length,h=t[f]??"";if(!h||!/\s/.test(h))continue;o=l;let g=f;for(;g<t.length&&/\s/.test(t[g]);)g+=1;s=g;break}if(o<0||s<0)return{command:t.trim()};let i=t.slice(0,o).trim(),a=t.slice(s).trim();return(a.startsWith('"')&&a.endsWith('"')&&a.length>=2||a.startsWith("'")&&a.endsWith("'")&&a.length>=2)&&(a=a.slice(1,-1)),{command:i,template:a}}function Ni(e,t){let n=e.replace(/\r\n/g,`
|
|
160
|
+
`).trim();if(!n)throw new Error("Recipe body is empty.");let r=n.match(/^--steps\s+([\s\S]+)$/i);if(r){let m=r[1].trim().split(/\s*;\s*|\n/).filter(h=>h.trim());if(m.length===0)throw new Error("No steps found. Separate steps with ; or newlines.");let f=m.map(h=>{let g=h.startsWith("+"),y=g?h.slice(1).trim():h.trim();if(!y)throw new Error("Empty step in recipe.");return{cmd:y,...g?{continueOnFail:!0}:{}}});return Be({command:f[0].cmd,steps:f})}let o="OMNISH_TASK",{command:s,template:i}=mh(n);if(i!==void 0){if(!s)throw new Error("Command part (before --template) is empty.");if(!i.trim())throw new Error("Template part (after --template) is empty.");let c=rn(s,o);if(!c.ok)throw new Error(c.error);return Be({command:s,promptTemplate:i})}let a=/\n---\n/,l=n.search(a);if(l>=0){let c=n.slice(0,l).trim(),m=n.slice(l).replace(/^\n---\n/,"").trim();if(!c)throw new Error("Command part (before ---) is empty.");if(!m)throw new Error("Template part (after ---) is empty.");let f=rn(c,o);if(!f.ok)throw new Error(f.error);return Be({command:c,promptTemplate:m})}if(rn(n,o).ok)return Be({command:n});let d=t.trim(),u=rn(d,o);if(!u.ok)throw new Error(`recipesMacroDefaultCommand invalid (${u.error}). Fix config or paste runner then --- then template.`);return Be({command:d,promptTemplate:n})}function xo(e,t,n){let r="`".repeat(3),o=`<<<${r}$${t}${r}>>>`,s=e;s.includes(o)&&(s=s.split(o).join(n));let i=`<<<${t}>>>`;s.includes(i)&&(s=s.split(i).join(n));let a=new RegExp(`\\$${t}\\b`,"g");return s.replace(a,n)}function An(e,t,n,r="chat",o){let s=gt(t);if(!s.ok)throw new Error(s.error);let i=Be({...n,command:n.command});if(o?.skipCommandValidation){if(!i.command.trim())throw new Error("Command is empty.")}else{let d=on(i);if(!d.ok)throw new Error(d.error)}let a=r==="global"?En:e,l=Mi(a);l[s.normalized]=i,ko()}function lu(e,t,n="chat"){let r=t.trim().toLowerCase(),o=n==="global"?En:e;So();let s=Tn.get(o);return!s||!(r in s)?!1:(delete s[r],ko(),!0)}function _i(e,t,n,r){let o=gt(t);if(!o.ok)return{ok:!1,error:o.error};let s=o.normalized,i=e,a=En;So();let l=Mi(i),d=Mi(a),u=l[s],c=d[s],m=f=>{let h=Be({...f}),g=on(h);if(!g.ok)throw new Error(g.error);return h};try{if(n==="global"){if(u!==void 0){let g=m(u);return d[s]=g,delete l[s],ko(),{ok:!0,kind:"moved",target:"global",name:s}}if(c!==void 0)return{ok:!0,kind:"noop",message:`Recipe "${s}" is already gateway-shared.`};let h=je(e,r,s);return h?.source==="builtin"||h?.source==="global"?{ok:!1,error:`Recipe "${s}" is built-in or from host recipes.json \u2014 not moved via /run set. Edit recipes.json or use a different name.`}:{ok:!1,error:`No user recipe "${s}" in this chat to promote. Add with /run add ${s} \u2026 or demote from global with /run set -p ${s}.`}}if(c!==void 0){let h=m(c);return l[s]=h,delete d[s],ko(),{ok:!0,kind:"moved",target:"chat",name:s}}if(u!==void 0)return{ok:!0,kind:"noop",message:`Recipe "${s}" is already stored for this chat only.`};let f=je(e,r,s);return f?.source==="builtin"||f?.source==="global"?{ok:!1,error:`Recipe "${s}" is built-in or from host recipes.json \u2014 not moved via /run set.`}:{ok:!1,error:`No gateway-shared user recipe "${s}" to demote. Add with /run add --global ${s} \u2026 or promote from this chat with /run set -g ${s}.`}}catch(f){return{ok:!1,error:String(f)}}}function cu(e,t){let n=[...ru(e,t).values()],r=n.filter(a=>a.featured).sort((a,l)=>a.name.localeCompare(l.name)),o=n.filter(a=>a.source==="peer").sort((a,l)=>a.name.localeCompare(l.name)),s=n.filter(a=>a.source==="shared").sort((a,l)=>a.name.localeCompare(l.name)),i=n.filter(a=>!a.featured&&a.source!=="peer"&&a.source!=="shared").sort((a,l)=>a.name.localeCompare(l.name));return{featured:r,shared:s,yours:o,more:i}}function $o(e){let t=oh.randomBytes(4).toString("hex");return`r-${e.replace(/[^a-zA-Z0-9_-]/g,"").slice(0,12)}-${t}`.slice(0,32)}function uu(e,t){let n=[`runbook: ${e}`];for(let o of t){let s=o.skipped?"skip":o.exitCode===0?"ok":"FAIL",i=o.label||`step ${o.index+1}`;if(o.skipped)n.push(`[${s}] ${i}`);else{n.push(`[${s}] ${i}${o.timedOut?" (timeout)":o.exitCode!==0?` (exit ${o.exitCode})`:""}`);let a=o.output.trim();if(a){let l=a.length>300?a.slice(0,300)+"\u2026":a;n.push(l)}}}let r=t.every(o=>o.skipped||o.exitCode===0);return n.push(r?"All steps completed successfully.":"Runbook stopped on failure."),n.join(`
|
|
161
|
+
`)}G();var du={enter:"\r",cr:"\r",lf:`
|
|
162
|
+
`,return:"\r",tab:" ",esc:"\x1B",escape:"\x1B",space:" ",backspace:"\x7F",bs:"\x7F",delete:"\x1B[3~",up:"\x1B[A",down:"\x1B[B",right:"\x1B[C",left:"\x1B[D",home:"\x1B[H",end:"\x1B[F",pageup:"\x1B[5~",pagedown:"\x1B[6~","ctrl+c":"","ctrl+d":"","ctrl+z":"","ctrl+l":"\f","ctrl+u":"","ctrl+k":"\v"};function fh(e){let t="";for(let n=0;n<e.length;n++){let r=e[n];if(r==="\\"&&n+1<e.length){let o=e[n+1];if(o==="x"&&n+3<e.length){let s=e.slice(n+2,n+4);if(/^[0-9a-fA-F]{2}$/.test(s)){t+=String.fromCharCode(Number.parseInt(s,16)),n+=3;continue}}if(o==="r"){t+="\r",n++;continue}if(o==="n"){t+=`
|
|
163
|
+
`,n++;continue}if(o==="t"){t+=" ",n++;continue}if(o==="\\"){t+="\\",n++;continue}}t+=r}return t}function hh(e){let t=e.trim();if(!t)return"";if(t.startsWith("\\"))return fh(t);let n=t.toLowerCase();if(du[n])return du[n];let r=t.match(/^\^([A-Za-z])$/);if(r){let s=r[1].toUpperCase().charCodeAt(0);if(s>=64&&s<=95)return String.fromCharCode(s-64)}let o=n.match(/^ctrl\+(.+)$/);if(o){let s=o[1];if(s.length===1){let i=s.toUpperCase().charCodeAt(0);if(i>=64&&i<=95)return String.fromCharCode(i-64)}}return t}function Ro(e){let t=e.split(",").map(n=>n.trim()).filter(Boolean);return t.length===0?"":t.map(n=>hh(n)).join("")}var gr="\x1B";function Co(e){let t=e;return t=t.replace(new RegExp(`${gr}\\[[\\d;?]*[ -/]*[@-~]`,"g"),""),t=t.replace(new RegExp(`${gr}\\][^${gr}\\u0007]*(?:\\u0007|${gr}\\\\)`,"g"),""),t=t.replace(new RegExp(`${gr}[@-Z\\\\-_]`,"g"),""),t=t.replace(/\u0007/g,""),t}function gh(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 yh(e,t){return`${e}${t}`}var Mo=class{constructor(t){this.opts=t}pending=new Map;timers=new Map;lastFlushEnd=new Map;flushing=new Set;closed=!1;dispose(){this.closed=!0;for(let t of this.timers.values())clearTimeout(t);this.timers.clear(),this.pending.clear(),this.lastFlushEnd.clear(),this.flushing.clear()}push(t,n,r){if(this.closed||this.opts.isMuted(t,n))return;let o=yh(t,n);this.pending.set(o,(this.pending.get(o)??"")+r);let s=this.timers.get(o);s&&clearTimeout(s);let i=this.opts.getCfg().appsFlushMs,a=setTimeout(()=>{this.timers.delete(o),this.flushNow(o,t,n)},i);this.timers.set(o,a)}async flushNow(t,n,r){if(!(this.closed||this.flushing.has(t))){this.flushing.add(t);try{let o=this.opts.getCfg(),s=o.appsMinIntervalMs,i=this.lastFlushEnd.get(t)??0,a=Math.max(0,i+s-Date.now());if(a>0&&await new Promise(f=>setTimeout(f,a)),this.closed)return;let l=this.pending.get(t)??"";if(this.pending.delete(t),!l||(this.opts.isRaw(n,r)||(l=Co(l)),!l.trim()))return;let d=o.appsMaxFlushBytes;if(l.length>d){let f=l.slice(d);l=l.slice(0,d)+`
|
|
164
|
+
[\u2026+${f.length} chars; /apps since ${r} to read more]`,this.pending.set(t,(this.pending.get(t)??"")+f)}let c=(this.opts.getRunningCount(n)>1?`[${r}] `:"")+l,m=gh(c,o.appsMaxWaChars);for(let f of m){if(this.closed)break;try{await this.opts.send(n,f)}catch{break}}this.lastFlushEnd.set(t,Date.now())}finally{this.flushing.delete(t),!this.closed&&(this.pending.get(t)??"").length>0&&queueMicrotask(()=>void this.flushNow(t,n,r))}}}};var To=4096,wh=/\[sudo\]\s+password\s+for\s+/i,bh=/passphrase\s+for\s+/i,kh=/(?:^|\n)\s*(?:Password|password):\s*$/;function Eo(e){let n=Co(e).replace(/\r/g,"").slice(-512);return!!(wh.test(n)||bh.test(n)||kh.test(n))}G();import Sh from"node:fs";import vh from"node:path";import*as pu from"node-pty";var xh=1024*1024,Po=class e{peerKey;name;command;cwd;envKeysCount;logPath;term=null;logStream=null;disposeData=null;disposeExit=null;exited=!1;ringChunks=[];ringBytes=0;constructor(t){this.peerKey=t.peerKey,this.name=t.name,this.command=t.command,this.cwd=t.cwd,this.envKeysCount=t.envKeysCount,this.logPath=t.logPath}appendRing(t){for(this.ringChunks.push(t),this.ringBytes+=t.length;this.ringBytes>xh&&this.ringChunks.length>0;){let n=this.ringChunks.shift();this.ringBytes-=n.length}}static start(t){j(nr(t.peerKey));let n=vh.join(nr(t.peerKey),`${t.name}.log`),r=Sh.createWriteStream(n,{flags:"a",mode:384}),o={...process.env,TERM:"xterm-256color",COLORTERM:"truecolor",...t.cwd?{PWD:t.cwd}:{},...t.extraEnv??{}},s=pu.spawn(t.shell,["-lc",t.command],{name:"xterm-256color",cols:t.cols,rows:t.rows,cwd:t.cwd,env:o}),i=new e({...t,logPath:n});return i.term=s,i.logStream=r,i.disposeData=s.onData(a=>{let l=Buffer.from(a,"utf8");i.appendRing(l),r.write(a),t.onOutput?.(a),t.router.push(t.peerKey,t.name,a)}),i.disposeExit=s.onExit(a=>{i.exited||(i.exited=!0,i.cleanupTerm(),t.onExit({exitCode:a.exitCode,signal:a.signal}))}),i}get alive(){return this.term!==null&&!this.exited}get ringByteCount(){return this.ringBytes}recentOutputTail(t=4096){if(this.ringBytes===0)return"";let n=Math.max(1,t),r=[],o=Math.min(n,this.ringBytes);for(let s=this.ringChunks.length-1;s>=0&&o>0;s--){let i=this.ringChunks[s];i.length<=o?(r.unshift(i),o-=i.length):(r.unshift(i.subarray(i.length-o)),o=0)}return Buffer.concat(r).toString("utf8")}write(t){this.term?.write(t)}resize(t,n){this.term?.resize(t,n)}kill(t){if(this.term)try{this.term.kill(t)}catch{}}cleanupTerm(){this.disposeData?.dispose(),this.disposeData=null,this.disposeExit?.dispose(),this.disposeExit=null,this.term=null,this.logStream?.end(),this.logStream=null}destroy(){if(this.exited){this.cleanupTerm();return}this.exited=!0,this.kill("SIGHUP"),this.cleanupTerm()}};function Fi(e){return new Promise(t=>setTimeout(t,e))}async function Ao(e,t,n){let r=n.appsSubmitDelayMs,o=n.appsClearInputDelayMs,s=n.appsClearInput!==!1,a=n.appsSkipClearOnPasswordPrompt!==!1&&Eo(e.recentOutputTail(To)),l=s&&!a?Ro(n.appsClearInputSequence||"^A,^K"):"",u=t.replace(/\r\n/g,`
|
|
165
|
+
`).replace(/\r/g,"").split(`
|
|
166
|
+
`);l&&(o>0&&await Fi(o),e.write(l));for(let c of u)c.length>0&&e.write(c),r>0&&await Fi(r),e.write("\r"),l&&(o>0&&await Fi(o),e.write(l))}var Rh=/^[a-zA-Z0-9_-]{1,32}$/;function We(e){return Rh.test(e)?null:"Session name must be 1\u201332 chars: letters, digits, _ or -."}function Pe(e,t){return`${e}:${t}`}function Ch(e){let t=e.lastIndexOf(":");return t<=0?null:{peerKey:e.slice(0,t),name:e.slice(t+1)}}function Mh(e,t){return e===0&&(t===0||t==null)}function Th(e,t){try{let r=Gt.readFileSync(e,"utf8").split(/\r?\n/);return r.slice(Math.max(0,r.length-t)).join(`
|
|
167
|
+
`).trimEnd()}catch{return""}}var Jt=class{constructor(t,n){this.getCfg=t;this.send=n,this.router=new Mo({getCfg:()=>this.getCfg(),send:n,isMuted:(r,o)=>this.muted.has(Pe(r,o)),isRaw:(r,o)=>this.rawMode.has(Pe(r,o)),getRunningCount:r=>this.countPeerRunning(r)})}sessions=new Map;focus=new Map;muted=new Set;rawMode=new Set;router;killTimers=new Set;send;runQueue=new Map;activeQueuedRunHead=new Map;runQueuePaused=new Map;passwordHintSent=new Set;countPeerRunning(t){let n=`${t}:`,r=0;for(let[o,s]of this.sessions)o.startsWith(n)&&s.alive&&r++;return r}countTotalRunning(){let t=0;for(let n of this.sessions.values())n.alive&&t++;return t}dispose(){for(let t of this.killTimers)clearTimeout(t);this.killTimers.clear(),this.stopAll(),this.router.dispose()}stopAll(){for(let t of this.sessions.values())t.destroy();this.sessions.clear(),this.focus.clear(),this.runQueue.clear(),this.activeQueuedRunHead.clear(),this.runQueuePaused.clear()}getFocus(t){return this.focus.get(t)??null}logPath(t,n){return $h.join(nr(t),`${n}.log`)}getSession(t,n){return this.sessions.get(Pe(t,n))}removeSessionRecord(t,n){let r=Pe(t,n);this.sessions.delete(r),this.passwordHintSent.delete(r),this.focus.get(t)===n&&this.focus.set(t,null)}onSessionOutput(t,n){let r=Pe(t,n),o=this.getSession(t,n);if(!o?.alive)return;let s=this.getCfg(),i=o.recentOutputTail(To);if(!Eo(i)){this.passwordHintSent.delete(r);return}if(s.appsPasswordPromptHint===!1||this.passwordHintSent.has(r))return;this.passwordHintSent.add(r);let l=`Password prompt in app "${n}" \u2014 reply with your password only (no line-clear keys). It is not echoed in chat but is written to the session log.`;this.send(t,l).catch(()=>{})}async writeFocusedLine(t,n){let r=this.focus.get(t);if(!r)return!1;let o=this.getSession(t,r);return o?.alive?(await Ao(o,n,this.getCfg()),!0):!1}async writeNamedLine(t,n,r){let o=We(n);if(o)return o;let s=this.getSession(t,n);return s?.alive?(await Ao(s,r,this.getCfg()),null):`No app session "${n}". /apps list`}enqueueQueuedRun(t,n,r){let o=this.runQueue.get(t)??[];o.push(n),this.runQueue.set(t,o);let s=o.length,i=this.runQueuePaused.get(t)??!1,a=this.activeQueuedRunHead.get(t)??null,l=a?.sessionName??null;if(l?this.getSession(t,l)?.alive??!1:!1){let u=a?.recipeLabel?` (recipe: ${a.recipeLabel})`:"",c=s-1,m=c>0?`${c} other job(s) in line before this one.`:"Next in line after the active run finishes.";return`Queued "${n.recipeLabel}" (wait slot ${s} behind active ${l}${u}). ${m}`}return i?`Queued "${n.recipeLabel}" (position ${s}). Run queue is paused \u2014 /run queue resume to continue.`:this.drainNextQueuedRun(t,r)}resumeRunQueue(t,n){this.runQueuePaused.set(t,!1);let r=this.activeQueuedRunHead.get(t)??null,o=r?.sessionName??null;if(o?this.getSession(t,o)?.alive??!1:!1){let a=r?.recipeLabel?` (recipe: ${r.recipeLabel})`:"";return`Run queue: session "${o}"${a} is still running. Wait for exit code=0 and signal=0 before the next starts.`}return(this.runQueue.get(t)??[]).length===0?"Run queue is empty.":(this.activeQueuedRunHead.set(t,null),this.drainNextQueuedRun(t,n))}runQueueStatus(t){let n=this.runQueue.get(t)??[],r=this.activeQueuedRunHead.get(t)??null,o=r?.sessionName??null,s=this.runQueuePaused.get(t)??!1,i=o?this.getSession(t,o)?.alive??!1:!1,a=["Run queue (this chat)"];if(o&&i){let l=r?.recipeLabel?` \xB7 recipe: ${r.recipeLabel}`:"";a.push(`Active: ${o}${l}`)}else o?a.push(`Active: ${o} (exiting or stale)`):a.push("Active: (none)");if(a.push(`Pending: ${n.length} (waiting only \u2014 not counting the active run above)`),a.push(`Paused: ${s}`),n.length>0){a.push("Waiting (FIFO order):");for(let d=0;d<Math.min(n.length,20);d++)a.push(`${d+1}) ${n[d].recipeLabel}`);n.length>20&&a.push(`\u2026 and ${n.length-20} more`)}return a.push("Next auto-starts only after exit code=0 and signal=0."),a.push("Commands: /run queue resume"),a.join(`
|
|
168
|
+
`)}drainNextQueuedRun(t,n){let r=this.runQueue.get(t);if(!r||r.length===0)return"";let o=r.shift();r.length===0?this.runQueue.delete(t):this.runQueue.set(t,r);let s=$o(o.recipeLabel),i=this.runQueue.get(t)?.length??0,a=this.start(t,s,o.command,n,o.extraEnv);if(!a.includes("started and attached"))return r.unshift(o),this.runQueue.set(t,r),this.runQueuePaused.set(t,!0),this.activeQueuedRunHead.set(t,null),`${a}
|
|
169
|
+
Run queue paused; fix the issue, then /run queue resume.`;this.activeQueuedRunHead.set(t,{sessionName:s,recipeLabel:o.recipeLabel});let d=i>0?`
|
|
170
|
+
Run queue: started head above; ${i} job(s) still waiting in FIFO.`:`
|
|
171
|
+
Run queue: started head above; no further queued jobs.`;return`${a}${d}`}start(t,n,r,o,s){let i=We(n);if(i)return i;if(!r.trim())return`Usage: /apps start <name> <command\u2026>
|
|
172
|
+
Example: /apps start sh bash`;if(this.sessions.has(Pe(t,n)))return`Session "${n}" already exists. /apps stop ${n} or pick another name.`;if(this.countPeerRunning(t)>=o.appsMaxSessions)return`Per-chat app limit (${o.appsMaxSessions}) reached. /apps stop or /apps rm an old session.`;if(this.countTotalRunning()>=o.appsMaxSessionsTotal)return`Global app limit (${o.appsMaxSessionsTotal}) reached. Stop sessions in other chats first.`;te();let a=oe(t).cwd,l={...process.env,TERM:"xterm-256color",COLORTERM:"truecolor",...a?{PWD:a}:{},...s??{}},d;try{d=Po.start({peerKey:t,name:n,shell:o.shell,command:r.trim(),cwd:a,cols:o.appsCols,rows:o.appsRows,envKeysCount:Object.keys(l).length,extraEnv:s,router:this.router,onOutput:()=>{this.onSessionOutput(t,n)},onExit:u=>{this.handleSessionExit(t,n,u)}})}catch(u){return`App spawn failed: ${String(u)}
|
|
173
|
+
If install skipped native builds: pnpm approve-builds && pnpm install (needs @whiskeysockets/baileys, sharp, protobufjs, esbuild, node-pty).`}return this.sessions.set(Pe(t,n),d),this.focus.set(t,n),`App "${n}" started and attached.
|
|
174
|
+
[cwd: ${a}]
|
|
175
|
+
Plain DMs go to this session until /apps detach. Use >othername text for another session.`}async handleSessionExit(t,n,r){let o=Pe(t,n);if(!this.sessions.has(o))return;let s=this.activeQueuedRunHead.get(t)?.sessionName===n,i=this.focus.get(t)===n;this.sessions.delete(o),i&&this.focus.set(t,null),this.muted.delete(o),this.rawMode.delete(o),this.passwordHintSent.delete(o);let a=r.signal!=null?` signal=${r.signal}`:"",l=i?" (detached)":"",d=`[${n}] exited code=${r.exitCode}${a}${l}`;try{await this.send(t,d)}catch{}if(!s)return;this.activeQueuedRunHead.set(t,null);let u=this.getCfg(),c=Mh(r.exitCode,r.signal),m=this.runQueue.get(t)?.length??0;if(c){if(this.runQueuePaused.set(t,!1),m>0){let f=this.drainNextQueuedRun(t,u);if(f)try{await this.send(t,f)}catch{}}return}if(this.runQueuePaused.set(t,!0),m>0){let f=`Run queue paused (exit code=${r.exitCode}${r.signal!=null?` signal=${r.signal}`:""}). ${m} run(s) waiting. /run queue resume to continue.`;try{await this.send(t,f)}catch{}}}attach(t,n){let r=We(n);return r||(this.getSession(t,n)?.alive?(this.focus.set(t,n),`Attached to "${n}". Plain messages (no ! or / or >) go to this app. /apps detach to stop.`):`No session "${n}". /apps list`)}detach(t){return this.focus.set(t,null),"Detached (attach mode off)."}list(t){let n=[];for(let[s,i]of this.sessions){let a=Ch(s);if(!a||a.peerKey!==t||!i.alive)continue;let l=this.focus.get(t)===a.name?" *":"";n.push(`${a.name}${l}`)}if(n.length===0)return"(no app sessions; /apps start <name> <cmd>)";let r=this.focus.get(t);return`${r?`attached: ${r}`:"(no focus)"}
|
|
176
|
+
App sessions:
|
|
177
|
+
${n.join(`
|
|
178
|
+
`)}`}getSessionCommand(t,n){if(We(n))return null;let o=this.getSession(t,n);return o?.command?.trim()?o.command.trim():null}info(t,n){let r=this.getCfg(),o=n??this.focus.get(t)??"";if(!o)return"Usage: /apps info <name> (or /apps get <name>, or attach first)";let s=We(o);if(s)return s;let i=this.getSession(t,o),a=this.logPath(t,o),l=0;try{l=Gt.statSync(a).size}catch{}let d=this.muted.has(Pe(t,o)),u=this.rawMode.has(Pe(t,o));return[`session: ${o}`,`alive: ${i?.alive??!1}`,`cmd: ${i?.command??"(unknown)"}`,`cwd: ${i?.cwd??"(unknown)"}`,`env keys: ${i?.envKeysCount??"?"}`,`terminal size: ${r.appsCols}x${r.appsRows}`,`ring bytes (approx): ${i?.ringByteCount??0}`,`log: ${a} (${l} bytes)`,`mute outbound: ${d}`,`raw outbound: ${u}`].join(`
|
|
179
|
+
`)}async sendText(t,n,r){let o=We(n);if(o)return o;let s=this.getSession(t,n);if(!s?.alive)return`No session "${n}".`;let i=r.replace(/\r\n/g,`
|
|
180
|
+
`);return await Ao(s,i,this.getCfg()),`Sent to "${n}" (${i.length} chars + Enter per line).`}sendKey(t,n,r){let o=We(n);if(o)return o;let s=this.getSession(t,n);if(!s?.alive)return`No session "${n}".`;let i=Ro(r);return i?(s.write(i),`Sent keys to "${n}".`):"Empty key sequence. Example: /apps key sh Enter or ^C,Up,Enter"}tail(t,n,r){let o=We(n);if(o)return o;let s=this.logPath(t,n);if(!Gt.existsSync(s))return`(no log file for ${n})`;let i=this.getCfg(),a=Number.isFinite(r)&&r>0?Math.min(500,r):i.appsLogTailLines;return Th(s,a)||"(empty log)"}readSince(t,n,r){let o=this.logPath(t,n);try{let i=Gt.statSync(o).size,a=Gt.openSync(o,"r");try{let l=Math.min(r,i),d=i-l,u=Buffer.alloc(d);return d>0&&Gt.readSync(a,u,0,d,l),{text:u.toString("utf8"),nextOffset:i}}finally{Gt.closeSync(a)}}catch{return{text:"",nextOffset:r}}}mute(t,n){let r=We(n);return r||(this.muted.add(Pe(t,n)),`Muted chat output for "${n}" (log still grows). /apps unmute ${n}`)}unmute(t,n){let r=We(n);return r||(this.muted.delete(Pe(t,n)),`Unmuted "${n}".`)}setRaw(t,n,r){let o=We(n);if(o)return o;let s=Pe(t,n);return r?this.rawMode.add(s):this.rawMode.delete(s),`raw chat output for "${n}": ${r?"on (ANSI kept)":"off (stripped)"}.`}resize(t,n,r,o){let s=We(n);if(s)return s;let i=this.getSession(t,n);if(!i?.alive)return`No session "${n}".`;let a=Math.min(500,Math.max(20,Math.floor(r))),l=Math.min(200,Math.max(5,Math.floor(o)));return i.resize(a,l),`Resized "${n}" to ${a}x${l}.`}stop(t,n){let r=We(n);if(r)return r;let o=this.getSession(t,n);if(!o?.alive)return`No running session "${n}".`;o.kill("SIGTERM");let s=Pe(t,n),i=setTimeout(()=>{this.killTimers.delete(i);let a=this.getSession(t,n);a?.alive&&a.kill("SIGKILL")},5e3);return this.killTimers.add(i),`SIGTERM sent to "${n}". SIGKILL in 5s if still running.`}kill(t,n){let r=We(n);if(r)return r;let o=this.getSession(t,n);return o?.alive?(o.kill("SIGKILL"),`SIGKILL sent to "${n}".`):`No running session "${n}".`}rm(t,n){let r=We(n);if(r)return r;if(this.getSession(t,n)?.alive)return`Session "${n}" is still running. /apps stop ${n} first, then /apps rm ${n}.`;this.removeSessionRecord(t,n),this.muted.delete(Pe(t,n)),this.rawMode.delete(Pe(t,n)),this.passwordHintSent.delete(Pe(t,n));let s=this.logPath(t,n);try{Gt.rmSync(s,{force:!0})}catch{}return`Removed "${n}" metadata and log.`}};qe();be();import wr from"node:fs";import br from"node:path";import mu from"node:fs";import fu from"node:path";var Eh=new Set(["jpg","jpeg","png","gif","webp","bmp","tif","tiff","heic","avif"]),Ph=new Set(["mp4","mov","webm","mkv","avi","m4v","3gp"]),Ah=new Set(["mp3","ogg","opus","wav","m4a","flac","aac","wma"]),Io={jpg:"image/jpeg",jpeg:"image/jpeg",png:"image/png",gif:"image/gif",webp:"image/webp",bmp:"image/bmp",tif:"image/tiff",tiff:"image/tiff",heic:"image/heic",avif:"image/avif",mp4:"video/mp4",mov:"video/quicktime",webm:"video/webm",mkv:"video/x-matroska",avi:"video/x-msvideo",m4v:"video/x-m4v","3gp":"video/3gpp",mp3:"audio/mpeg",ogg:"audio/ogg",opus:"audio/opus",wav:"audio/wav",m4a:"audio/mp4",flac:"audio/flac",aac:"audio/aac",wma:"audio/x-ms-wma",pdf:"application/pdf",zip:"application/zip",gz:"application/gzip",txt:"text/plain",json:"application/json",xml:"application/xml",csv:"text/csv"};function Ih(e){let t=e.replace(/^\./,"").toLowerCase();return Eh.has(t)?{category:"image",mimetype:Io[t]??"image/jpeg"}:Ph.has(t)?{category:"video",mimetype:Io[t]??"video/mp4"}:Ah.has(t)?{category:"audio",mimetype:Io[t]??"audio/mpeg"}:{category:"document",mimetype:Io[t]??"application/octet-stream"}}function Kt(e,t){let n;try{n=mu.realpathSync(e)}catch{return{error:"File not found or unreadable."}}let r;try{r=mu.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=fu.basename(n),s=fu.extname(n).slice(1),{category:i,mimetype:a}=Ih(s);return{absPath:n,category:i,mimetype:a,displayName:o}}import Lh from"node:os";import*as gu from"node-pty";function Oh(e,t){return e.length<=t?e:`${e.slice(0,t)}
|
|
181
|
+
[...truncated]`}function Nh(e){if(e===void 0||e===0)return null;let t=Lh.constants.signals;for(let[n,r]of Object.entries(t))if(r===e)return n;return null}function hu(e){try{process.platform==="win32"?e.kill():e.kill("SIGTERM")}catch{e.kill()}}function sn(e,t,n){return new Promise(r=>{let o=Date.now(),s=!1,i="",a=n.cwd,l=null,d=!1,u=null,c=null,m,f=y=>{if(d)return;d=!0,m!==void 0&&clearTimeout(m),u?.dispose(),u=null,r(y);let b=c;c=null,queueMicrotask(()=>b?.dispose())},g={...n.env??process.env,TERM:"xterm-256color",COLORTERM:"truecolor",...a?{PWD:a}:{}};try{l=gu.spawn(e,["-c",t],{name:"xterm-256color",cols:120,rows:40,cwd:a,env:g})}catch(y){f({code:null,stdout:"",stderr:String(y),durationMs:Date.now()-o,timedOut:!1,signal:null});return}m=setTimeout(()=>{s=!0,l&&hu(l)},n.timeoutMs),u=l.onData(y=>{i+=y,i.length>n.maxBytes&&(i=Oh(i,n.maxBytes),l&&hu(l))}),c=l.onExit(y=>{f({code:y.exitCode,stdout:i,stderr:"",durationMs:Date.now()-o,timedOut:s,signal:Nh(y.signal)})})})}function yr(e,t,n){let r=new Date(e),o=r.getFullYear(),s=r.getMonth(),i=r.getDate(),a=new Date(o,s,i,t,n,0,0).getTime();return a<=e&&(a=new Date(o,s,i+1,t,n,0,0).getTime()),a}function _h(e,t,n){let r=e;for(let o=0;o<14;o++){let s=yr(r,t,n),i=new Date(s).getDay();if(i>=1&&i<=5)return s;r=s}return yr(r,t,n)}function Fh(e,t,n,r){let o=e;for(let s=0;s<370;s++){let i=yr(o,n,r);if(new Date(i).getDay()===t)return i;o=i}return yr(o,n,r)}function Wh(e,t){let n=new Date(e),r=n.getFullYear(),o=n.getMonth(),s=n.getDate(),i=n.getHours(),a=new Date(r,o,s,i,t,0,0).getTime();return a<=e&&(a=new Date(r,o,s,i+1,t,0,0).getTime()),a}function Uh(e,t){return e.kind==="ondemand"||e.kind==="heartbeat"?Number.POSITIVE_INFINITY:e.kind==="daily"?yr(t,e.hour,e.minute):e.kind==="weekdays"?_h(t,e.hour,e.minute):e.kind==="hourly"?Wh(t,e.minute):Fh(t,e.weekday,e.hour,e.minute)}function bu(e,t,n,r,o=32){if(e.kind==="ondemand"||e.kind==="heartbeat")return[];let s=t??n-1,i=[],a=s;for(;i.length<o;){let l=Uh(e,a);if(l>r)break;i.push(l),a=l}return i}var Dh={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 Wi(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 Hh(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 Lo(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=Wi(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=Wi(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=Hh(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=Dh[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=Wi(e[2]);return o?{ok:!0,schedule:{kind:"weekly",weekday:r,hour:o.hour,minute:o.minute}}:{ok:!1,error:"weekly needs HH:MM (24h) after weekday."}}if(t==="heartbeat"){if(e.length<2||e.length>3)return{ok:!1,error:"Usage: heartbeat <interval> [grace] \u2014 e.g. heartbeat 1h, heartbeat 30m 10m"};let n=yu(e[1]);if(n===null||n<6e4)return{ok:!1,error:"heartbeat interval must be >= 1m. Use 5m, 1h, 2h, 1d, etc."};let r=Math.floor(n*.5);if(e.length===3){let o=yu(e[2]);if(o===null||o<3e4)return{ok:!1,error:"heartbeat grace must be >= 30s. Use 1m, 5m, etc."};r=o}return{ok:!0,schedule:{kind:"heartbeat",intervalMs:n,graceMs:r}}}return{ok:!1,error:`Unknown schedule kind "${t}".`}}function yu(e){let t=e.trim().toLowerCase();if(!t)return null;let n=t.match(/^(?:(\d+)d)?(?:(\d+)h)?(?:(\d+)m)?(?:(\d+)s)?$/);if(!n||t===""||!n[1]&&!n[2]&&!n[3]&&!n[4])return null;let r=n[1]?Number(n[1]):0,o=n[2]?Number(n[2]):0,s=n[3]?Number(n[3]):0,i=n[4]?Number(n[4]):0;return((r*24+o)*60+s)*6e4+i*1e3}function wu(e){let t=Math.floor(e/1e3),n=Math.floor(t/86400),r=Math.floor(t%86400/3600),o=Math.floor(t%3600/60),s=[];return n>0&&s.push(`${n}d`),r>0&&s.push(`${r}h`),o>0&&s.push(`${o}m`),s.length>0?s.join(""):`${t}s`}function In(e){let t=r=>r<10?`0${r}`:String(r),n=(r,o)=>`${t(r)}:${t(o)}`;switch(e.kind){case"ondemand":return"ondemand";case"daily":return`daily ${n(e.hour,e.minute)}`;case"weekdays":return`weekdays ${n(e.hour,e.minute)}`;case"hourly":return e.minute===0?"hourly":`hourly :${t(e.minute)}`;case"weekly":return`weekly ${["sun","mon","tue","wed","thu","fri","sat"][e.weekday]??e.weekday} ${n(e.hour,e.minute)}`;case"heartbeat":return`heartbeat every ${wu(e.intervalMs)} grace ${wu(e.graceMs)}`}}G();be();import Bh from"better-sqlite3";var ku=2,zt=null;function Su(){j(ut);let e=new Bh(Qa);e.pragma("journal_mode = WAL"),e.exec(`
|
|
153
182
|
CREATE TABLE IF NOT EXISTS cowork_meta (
|
|
154
183
|
key TEXT PRIMARY KEY,
|
|
155
184
|
value TEXT NOT NULL
|
|
@@ -163,124 +192,190 @@ ${s.detail}`)}return p("Unknown /service command. Try /service help")}function u
|
|
|
163
192
|
PRIMARY KEY (task_id, slot_ms)
|
|
164
193
|
);
|
|
165
194
|
CREATE INDEX IF NOT EXISTS idx_cowork_completion_task ON cowork_slot_completion(task_id);
|
|
166
|
-
|
|
195
|
+
CREATE TABLE IF NOT EXISTS cowork_task_state (
|
|
196
|
+
task_id TEXT PRIMARY KEY,
|
|
197
|
+
last_ok INTEGER NOT NULL,
|
|
198
|
+
updated_at_ms INTEGER NOT NULL
|
|
199
|
+
);
|
|
200
|
+
`);let t=e.prepare("SELECT value FROM cowork_meta WHERE key = 'schema_version'").get(),n=t?Number(t.value):0;return n<ku&&(n<2&&e.exec(`
|
|
201
|
+
CREATE TABLE IF NOT EXISTS cowork_task_state (
|
|
202
|
+
task_id TEXT PRIMARY KEY,
|
|
203
|
+
last_ok INTEGER NOT NULL,
|
|
204
|
+
updated_at_ms INTEGER NOT NULL
|
|
205
|
+
);
|
|
206
|
+
`),e.prepare("INSERT OR REPLACE INTO cowork_meta (key, value) VALUES ('schema_version', ?)").run(String(ku))),e}function an(e){zt||(zt=Su()),Gh(e)}function vu(){if(zt){try{zt.close()}catch{}zt=null}}function ln(){return zt||(zt=Su()),zt}function jh(e){let n=ln().prepare("SELECT MAX(slot_ms) AS m FROM cowork_slot_completion WHERE task_id = ?").get(e)?.m;return n==null||!Number.isFinite(n)?null:n}function Oo(e){let t=jh(e.id);return t??e.lastCompletedSlotMs}function No(e,t){if(t.length===0)return;let n=ln(),r=n.prepare(`
|
|
167
207
|
INSERT INTO cowork_slot_completion (task_id, slot_ms, kind, completed_at_ms, log_path)
|
|
168
208
|
VALUES (?, ?, ?, ?, ?)
|
|
169
|
-
`);n.transaction(()=>{for(let s of t)r.run(e,s.slotMs,s.kind,s.completedAtMs,s.logPath)})()}function
|
|
170
|
-
|
|
171
|
-
`,{mode:384})}function Ca(e){let t=Eo();t.push(e),$a(t)}function Eo(){try{let e=gn.readFileSync(Ir,"utf8"),t=JSON.parse(e);return Array.isArray(t)?t.filter(n=>n&&typeof n=="object"&&typeof n.ownerPeerKey=="string"&&typeof n.name=="string"):[]}catch{return[]}}function Ra(e){if(e<=0)return{batch:[],remainingAfter:Eo().length};let t=Eo();if(t.length===0)return{batch:[],remainingAfter:0};let n=t.slice(0,e),r=t.slice(e);return $a(r),{batch:n,remainingAfter:r.length}}function ku(){return p(["Cowork \u2014 scheduled shell tasks (gateway must be running).","","add <name> <schedule> -- <command\u2026>"," schedule: ondemand | hourly [:MM] | daily HH:MM | weekdays HH:MM | weekly <mon\u2026sun> HH:MM","set <name> cmd -- <command\u2026> | schedule <\u2026> | out <path> | cwd <path>"," notify self|wa|tg|all|none"," attach on|off \u2014 send run log as a file with notify messages"," files clear | <glob/path \u2026> \u2014 optional artifacts (basename * ?); quote paths with spaces","list | show <name> | run <name> | enable <name> | disable <name> | remove <name>","","Output logs: task outputDir (default ~/Cowork/<name>). Missed times catch up when the gateway is back."].join(`
|
|
172
|
-
`))}function Ma(e){let t=[],n=0;for(;n<e.length;){for(;n<e.length&&/\s/.test(e[n]);)n+=1;if(n>=e.length)break;if(e[n]==='"'){let o=n+1,s=e.indexOf('"',o);if(s===-1){t.push(e.slice(o));break}t.push(e.slice(o,s)),n=s+1;continue}let r=n;for(;r<e.length&&!/\s/.test(e[r]);)r+=1;t.push(e.slice(n,r)),n=r}return t}function Su(e){let t=" -- ",n=e.indexOf(t);if(n===-1)return{error:'Missing " -- " before the command. Example: /cowork add tick weekdays 09:00 -- date'};let r=e.slice(0,n).trim(),o=e.slice(n+t.length).trim();if(!o)return{error:"Command after -- is empty."};let s=r.split(/\s+/).filter(Boolean);if(s.length<2)return{error:"Usage: /cowork add <name> <schedule\u2026> -- <command\u2026>"};let i=s[0],a=s.slice(1);return{name:i,scheduleWords:a,command:o}}function Ia(e,t){let n=e.trim();if(!n||/^help$/i.test(n))return ku();let r=n.split(/\s+/)[0].toLowerCase();if(/^list$/i.test(r)){let u=$e().filter(d=>d.ownerPeerKey===t);if(u.length===0)return p("(no cowork tasks for this chat)");let c=u.map(d=>{let m=d.enabled?"":" (disabled)";return`${ge}${d.name} ${mn(d.schedule)} notify=${d.notify}${m}`});return p(c.join(`
|
|
173
|
-
`))}let o=n.match(/^show\s+(\S+)\s*$/i);if(o){let u=o[1],c=$e();fn(c);let d=Bt(c,u,t);if(!d)return p(`Unknown task "${u}". /cowork list`);let m=cr(d),y=[`name: ${d.name}`,`id: ${d.id}`,`schedule: ${mn(d.schedule)}`,`enabled: ${d.enabled}`,`notify: ${d.notify}`,`attachLog: ${d.attachLog}`,`files: ${d.attachFiles.length?d.attachFiles.join(", "):"(none)"}`,`cwd: ${d.cwd||"(session cwd)"}`,`out: ${d.outputDir}`,`cmd: ${d.command}`,`last slot: ${m?new Date(m).toLocaleString():"(never)"}`];return p(y.join(`
|
|
174
|
-
`))}if(/^add$/i.test(r)){let u=n.slice(3).trim(),c=Su(u);if("error"in c)return p(c.error);let d=xa(c.name);if(!d.ok)return p(d.error);let m=Mo(c.scheduleWords);if(!m.ok)return p(m.error);let y=$e();if(Bt(y,d.name,t))return p(`Task "${d.name}" already exists. Remove it first or pick another name.`);let g=q(t),f=Ao(d.name),h={id:va(),name:d.name,ownerPeerKey:t,command:c.command,cwd:"",outputDir:f,schedule:m.schedule,enabled:!0,notify:"self",attachLog:!1,attachFiles:[],lastCompletedSlotMs:null,createdAtMs:Date.now()};return y.push(h),Te(y),p([`Saved cowork task "${d.name}" (${mn(m.schedule)}).`,`Output: ${f}`,`Notify: self \u2014 change with /cowork set ${d.name} notify wa|tg|all|none`].join(`
|
|
175
|
-
`))}let s=n.match(/^run\s+(\S+)\s*$/i);if(s){let u=s[1].toLowerCase(),c=Bt($e(),u,t);return c?c.enabled?(Ca({ownerPeerKey:t,name:c.name,at:Date.now()}),p(`On-demand run queued for "${c.name}" (runs within ~30s while omnish run is active).`)):p(`Cowork "${c.name}" is disabled. /cowork enable ${c.name}`):p(`Unknown task "${s[1]}". /cowork list`)}let i=n.match(/^(?:remove|rm|del)\s+(\S+)\s*$/i);if(i){let u=i[1],c=$e(),d=c.findIndex(m=>m.name===u.toLowerCase()&&m.ownerPeerKey===t);return d===-1?p(`Unknown task "${u}".`):(c.splice(d,1),Te(c),p(`Removed cowork task "${u.toLowerCase()}".`))}let a=n.match(/^enable\s+(\S+)\s*$/i);if(a)return Ta(a[1],t,!0);let l=n.match(/^disable\s+(\S+)\s*$/i);return l?Ta(l[1],t,!1):/^set$/i.test(r)?$u(n.slice(3).trim(),t):p("Unknown /cowork command. Try /cowork help")}function Ta(e,t,n){let r=$e(),o=Bt(r,e,t);return o?(o.enabled=n,Te(r),p(`Cowork "${o.name}" ${n?"enabled":"disabled"}.`)):p(`Unknown task "${e}".`)}function xu(e){let t=e.toLowerCase();return t==="self"?"self":t==="wa"||t==="whatsapp"?"wa":t==="tg"||t==="telegram"?"tg":t==="all"?"all":t==="none"?"none":null}function vu(e){let t=e.toLowerCase();return t==="on"||t==="true"||t==="1"||t==="yes"?!0:t==="off"||t==="false"||t==="0"||t==="no"?!1:null}function $u(e,t){let n=e.match(/^(\S+)\s+([\s\S]+)$/);if(!n)return p("Usage: /cowork set <name> cmd -- \u2026 | schedule \u2026 | out <path> | cwd <path> | notify \u2026 | attach <on|off> | files \u2026");let r=n[1],o=n[2].trim(),s=$e(),i=Bt(s,r,t);if(!i)return p(`Unknown task "${r}".`);if(o.toLowerCase().startsWith("cmd ")){let a=o.slice(4).trim(),l=" -- ",u=a.indexOf(l),c;if(u!==-1)c=a.slice(u+l.length).trim();else if(a.startsWith("--"))c=a.slice(2).trim();else return p("Usage: /cowork set <name> cmd -- <command\u2026>");return c?(i.command=c,Te(s),p(`Updated command for "${i.name}".`)):p("Command is empty.")}if(o.toLowerCase().startsWith("schedule ")){let a=o.slice(9).trim().split(/\s+/).filter(Boolean),l=Mo(a);return l.ok?(i.schedule=l.schedule,Te(s),p(`Schedule for "${i.name}": ${mn(l.schedule)}`)):p(l.error)}if(o.toLowerCase().startsWith("out ")){let a=o.slice(4).trim(),l=q(t);return i.outputDir=_t(a,l.cwd),Te(s),p(`outputDir: ${i.outputDir}`)}if(o.toLowerCase().startsWith("cwd ")){let a=o.slice(4).trim(),l=q(t);return i.cwd=a?_t(a,l.cwd):"",Te(s),p(`cwd: ${i.cwd||"(session cwd at run time)"}`)}if(o.toLowerCase().startsWith("notify ")){let a=o.slice(7).trim(),l=xu(a);return l?(i.notify=l,Te(s),p(`notify: ${l}`)):p("notify must be self, wa, tg, all, or none.")}if(o.toLowerCase().startsWith("attach ")){let a=o.slice(7).trim(),l=Ma(a);if(l.length!==1)return p("Usage: /cowork set <name> attach on|off");let u=vu(l[0]);return u===null?p("attach must be on or off"):(i.attachLog=u,Te(s),p(`attachLog: ${u}`))}if(o.toLowerCase().startsWith("files ")){let a=o.slice(6).trim(),l=Ma(a);return l.length===1&&l[0].toLowerCase()==="clear"?(i.attachFiles=[],Te(s),p("files: (cleared)")):l.length===0?p("Usage: /cowork set <name> files clear | <pattern \u2026> \u2014 quote paths with spaces"):(i.attachFiles=l,Te(s),p(`files: ${i.attachFiles.join(", ")}`))}return p("Unknown set field. Try: cmd, schedule, out, cwd, notify, attach, files")}function Lo(e){let t=e.trim();if(t==="/computers"||t.startsWith("/computers "))return t.slice(10).trim();if(t==="/pcs"||t.startsWith("/pcs "))return t.slice(4).trim();let n=t.match(/^\/c(?:$|\s+(.*))/);return n?(n[1]??"").trim():null}function C(e){return{kind:"text",body:e}}function Cu(e,t){return`${e}:${t}`}function Ru(e,t){return`${e}:apps:${t}`}async function Mu(e,t){let n=e.trim();if(n===""||/^status$/i.test(n)||/^show$/i.test(n)||/^get$/i.test(n)){let a=R();return Ns({gatewayMode:a.gatewayMode,authPresent:Le(),tokenSet:!!oe(a),allowN:a.allowFrom.length,tgAllowN:a.telegramAllowFrom.length,updateBrief:ir(un())})}if(/^help$/i.test(n))return j(Hr());let r=On(n);if(!r)return Xs();Pn(r);let o=R(),s,i=!1;if(t?.reload){let a=await t.reload();s=a.ok?a.summary:`Reload failed: ${a.error}`}else i=!0;return Us(o.gatewayMode,s,i)}function Tu(e){let t=e.trim();if(!Ze(t))return js();let n=vt(t),r=[];return typeof process.env.TELEGRAM_BOT_TOKEN=="string"&&process.env.TELEGRAM_BOT_TOKEN.trim()&&r.push("TELEGRAM_BOT_TOKEN is set in the environment and overrides config until unset."),n.gatewayMode==="whatsapp"&&r.push('Set gatewayMode to "telegram" or "both" for Telegram to receive messages.'),Ds(r)}async function Ea(e,t,n){let r=q(t),o=vs(n);if(o!==null){let a=$s(r.cwd,o),l=Cs(a);return l.ok?(Bn(t,a),p(`cwd: ${a}`)):p(`cd: ${l.error}`)}let s=await Et(e.shell,n,{timeoutMs:e.syncTimeoutMs,maxBytes:e.syncMaxBytes,cwd:r.cwd}),i=[];return i.push(`$ ${n}`),s.stdout.trim()&&i.push(s.stdout.trimEnd()),s.stderr.trim()&&(i.push("\u2014 stderr \u2014"),i.push(s.stderr.trimEnd())),s.timedOut?i.push(`Timed out (${Math.round(e.syncTimeoutMs/1e3)}s limit).`):s.code!==0&&s.code!==null&&i.push(`Exit ${s.code}`),p(i.join(`
|
|
176
|
-
`))}async function wt(e,t,n,r,o,s,i,a,l=null,u=!1,c){let d=s.text.trim(),m=s.peerKey,y=d.match(/^!!\s*(start|stop)\s*$/i);if(y)return y[1].toLowerCase()==="start"?(o.set(m,!0),C(Ys())):(o.set(m,!1),C(p("Free shell mode off.")));if(d.startsWith(e.commandPrefix)){let f=d.slice(e.commandPrefix.length).trim();if(!f)return C(p(`Send ${e.commandPrefix}<command> or /help`));if(!u&&Si(f)){let h=Xr(m,f);if(h!==void 0)return await wt(e,t,n,r,o,{...s,text:h},i,a,l,!0,c)}return C(await Ea(e,m,f))}if(/^\/help\s+files$/i.test(d.trim()))return C(Ur());if(d==="/help"||d==="help")return C(j(Rt(e)));if(d.startsWith("/")){if(/^\/files(?:\s+help)?$/i.test(d.trim()))return C(Ur());let f=d.match(/^\/receive\b(?:\s+(\S+))?$/i);if(f){let w=(f[1]??"status").toLowerCase(),S=new Set(["here","cwd","session","dir"]),I=new Set(["default","global","reset"]);if(S.has(w)){Or(m,"sessionCwd");let _=q(m).cwd;return C(p(`Inbound files will save under this chat\u2019s session folder:
|
|
177
|
-
${_}
|
|
178
|
-
(layout: \u2026/<peer>/<date>/<file> under that root).
|
|
179
|
-
|
|
180
|
-
Change folder with ${e.commandPrefix}cd \u2026 Send /receive default to use the server config again.`))}return I.has(w)?(Or(m,"default"),C(p("Per-chat inbound folder cleared. Uploads now follow fileReceiveRootMode in config.json (/files)."))):C(w==="help"?Wr():w==="status"?zs(e,m):Wr())}if(d==="/reload"||d==="/restart"){if(!a?.reload)return C(p("Reload is only available while the omnish gateway (omnish run) is running."));let w=await a.reload();return C(p(w.ok?w.summary:`Reload failed: ${w.error}`))}if(d==="/updates"||d.startsWith("/updates ")){let w=d.slice(8).trim().toLowerCase();if(w==="cached"||w==="last"){let I=un();return C(I?Xt(I):p("No update snapshot yet. Send /updates (live check) once, or enable updateCheckEnabled and wait for the scheduled check."))}let S=await dn(Ue(),R());return C(Xt(S))}let h=d.match(/^\/security(?:\s+(\S+))?\s*$/i);if(h){let w=(h[1]??"").toLowerCase(),S=R(),I=ot(S);return C(w==="help"||w==="?"?j(ui()):w==="summary"||w==="brief"?p(Ls(I,"Send /security for the full report.")):w==="tips"?j(ci()):w===""||w==="full"||w==="report"?li(I):p("Unknown /security subcommand. Try /security, /security summary, /security tips, or /security help"))}let x=d.match(/^\/(gateway|gw|mode)\b(?:\s+(.*))?$/i);if(x){let w=(x[2]??"").trim();return C(await Mu(w,a))}if(d==="/config"||d.startsWith("/config ")){let w=d.slice(7).trim();return C(await oa(w,a))}let k=la(d);if(k!==null)return C(await ca(e,k));let $=Lo(d);if($!==null){let w=Xi(e,$,l);return w===null?null:C(w)}let A=d.match(/^\/(send|file)\b(?:\s+([\s\S]+))?$/i);if(A){let w=(A[2]??"").trim();if(!w)return C(jr());let S=_n(w);if(!S)return C(jr());let I=q(m).cwd,_=await Yt(I,S.selectorPart);if(_.length===0)return C(p(`No files matched: ${S.selectorPart}`));let B=await Vt(_);if(!B.ok)return C(p(B.error));let V=[];for(let me of _){let Ee=et(me,e.fileSendMaxBytes);if("error"in Ee)return C(p(Ee.error));V.push({absPath:Ee.absPath,category:Ee.category,mimetype:Ee.mimetype,displayName:Ee.displayName,caption:S.caption})}return V.length===1?{kind:"file",spec:V[0]}:{kind:"files",specs:V}}if(d==="/allowlist"){let w=R();return C(Hs([{label:"allowFrom (WhatsApp)",items:w.allowFrom},{label:"telegramAllowFrom",items:w.telegramAllowFrom}]))}let M=d.match(/^\/allow\b\s+(.+)$/i);if(M)try{let w=An(M[1].trim());return C(Dr(w))}catch(w){return C(p(String(w)))}if(/^\/allow\b\s*$/i.test(d))return C(Ws());let L=d.match(/^\/deny\b\s+(.+)$/i);if(L)try{let w=Ln(L[1].trim());return C(Dr(w))}catch(w){return C(p(String(w)))}if(/^\/deny\b\s*$/i.test(d))return C(Gs());let U=d.match(/^\/(whatsapp|wa|telegram|tg)\b(?:\s+(.*))?$/i);if(U){let w=U[1].toLowerCase(),S=(U[2]??"").trim(),I=w==="whatsapp"||w==="wa";if(w==="telegram"||w==="tg"){let B=S.match(/^token\s+(\S+)\s*$/i);return B?C(Tu(B[1]??"")):S===""||/^help$/i.test(S)?C(j(Bs(R()))):C(Qs())}if(I)return S===""||/^help$/i.test(S)?C(j(_s(e))):C(qs())}let re=d.match(/^\/(cowork|cw)\b(?:\s+([\s\S]*))?$/i);if(re){let w=(re[2]??"").trim();return C(Ia(w,m))}if(d==="/apps"||d.startsWith("/apps "))return C(await Ou(d,m,e,i,r));let Se=Iu(d);if(Se!==null)return C(await Au(Se,m,e,i,s.mediaSavedPath));if(d.startsWith("/bg")){let w=d.slice(3).trim();if(!w)return C(Js());let S=Hi(w);if("error"in S)return C(p(S.error==="empty"?"Usage: /bg <command> or /bg -n <name> <command>.":S.error));let I=q(m).cwd,{id:_,meta:B}=t.spawnJob(e.shell,S.cmd,{cwd:I,name:S.name}),V=B.name?`${_} (${B.name})`:_;return C(p(`Job ${V} started.
|
|
181
|
-
[cwd: ${I}]
|
|
182
|
-
/log ${B.name??_}
|
|
183
|
-
/tail ${B.name??_}`))}if(d==="/jobs"){let w=t.list().slice(0,20);return w.length===0?C(p("(no jobs yet)")):C(p(w.map(S=>{let I=S.finishedAt&&S.startedAt?`${((Date.parse(S.finishedAt)-Date.parse(S.startedAt))/1e3).toFixed(1)}s`:"\u2026";return`${S.name?`${S.id} ${S.name}`:S.id} ${S.status} exit=${S.exitCode??"?"} ${I}
|
|
184
|
-
${S.cmd.slice(0,120)}${S.cmd.length>120?"\u2026":""}`}).join(`
|
|
185
|
-
|
|
186
|
-
`)))}let we=d.match(/^\/log\s+(\S+)(?:\s+(\d+))?\s*$/i);if(we){let w=we[1],S=t.resolveJobRef(w);if(!S.ok)return C(p(S.error));let I=S.id,_=we[2]?Number.parseInt(we[2],10):e.jobLogTailLines,B=Number.isFinite(_)&&_>0?Math.min(_,500):e.jobLogTailLines;return C(p(t.tailLog(I,B)))}let Ke=d.match(/^\/tail\s+(\S+)\s*$/i);if(Ke){let w=Ke[1],S=t.resolveJobRef(w);if(!S.ok)return C(p(S.error));let I=S.id,_=Cu(m,I),B=n.get(_)??0,{text:V,nextOffset:me}=t.readSince(I,B);return n.set(_,me),C(V?p(V.trimEnd()||"(no new output)"):p(`(no new output; offset ${me})`))}let Xe=d.match(/^\/kill\s+(\S+)\s*$/i);if(Xe){let w=Xe[1],S=t.resolveJobRef(w);return S.ok?C(p(t.kill(S.id))):C(p(S.error))}let T=d.match(/^\/(shortcut|shortcuts|alias|aliases)\b(?:\s+([\s\S]*))?$/i);if(T){let w=T[1].toLowerCase(),S=(T[2]??"").trim();return w==="shortcuts"&&!S?S="list":((w==="alias"||w==="aliases")&&!S||w==="shortcut"&&!S)&&(S="help"),C(Lu(S,m))}if(!u){let w=d.trim().match(/^\/([a-zA-Z0-9][a-zA-Z0-9_-]{0,31})\s*$/);if(w){let S=w[1],I=Xr(m,S);if(I!==void 0)return await wt(e,t,n,r,o,{...s,text:I},i,a,l,!0,c)}}return C(Vs(e))}let g=d.match(/^>(\S+)\s*(.*)$/s);if(g){let f=g[1],h=g[2]??"",x=await i.writeNamedLine(m,f,h);return x?C(p(x)):null}return await i.writeFocusedLine(m,d)?null:o.get(m)&&d?C(await Ea(e,m,d)):e.chatLlmFallbackEnabled&&e.chatLlmShellCommand.trim().length>0&&c?.onPlainTextLlmFallback?(c.onPlainTextLlmFallback(m,d),null):C(Ks(e))}function Iu(e){return e==="/run"||e.startsWith("/run ")?e.slice(4).trim():e==="/r"||e.startsWith("/r ")?e.slice(2).trim():null}function Eu(e){let t=e.trim(),n=t.match(/^(\S+)\s+(?:--queue|-q)\s+([\s\S]+)$/i);if(n)return{recipe:n[1],task:n[2],queued:!0};let r=t.match(/^(\S+)\s+([\s\S]+)$/);return r?{recipe:r[1],task:r[2],queued:!1}:null}async function Au(e,t,n,r,o){let s=e.trim();if(!s||/^help$/i.test(s))return j(ri());let i=/^list\b([\s\S]*)$/i.exec(s);if(i){let g=(i[1]??"").trim(),{filter:f,bad:h}=Ri(g);if(h)return p(`Unknown /run list suffix: "${h}". Use: list | list --chat | list -p | list --global | list -g`);if(f==="merged")return oi(Ei(t,n));let x=Ti(t,n,f);return ai(x,f)}let a=/^show\b([\s\S]*)$/i.exec(s);if(a){let g=(a[1]??"").trim(),{mode:f,remainder:h}=Ci(g),x=/^(\S+)\s*$/i.exec(h);if(!x?.[1])return p("Usage: /run show <name> \u2014 or show --global|-g|--chat|-p <name> (-g shared, -p private)");let k=x[1];if(f==="resolved"){let re=Re(t,n,k);return re?zr(re):qr(k)}let $=f==="global"?"global":"chat",A=Qe($,t,n,k);if(A)return zr(A,$==="global"?"From gateway-shared recipes (--global).":"From this chat only (--chat).");let M=$==="chat"&&Qe("global",t,n,k)?`
|
|
187
|
-
(Gateway-shared recipe exists: /run show --global ${k})`:"",L=$==="global"&&Qe("chat",t,n,k)?`
|
|
188
|
-
(This chat stores an override: /run show --chat ${k})`:"",U=$==="global"?`Unknown recipe "${k}" in gateway-shared storage.`:`Unknown recipe "${k}" in this chat storage.`;return p(`${U}${M}${L}`)}let l=/^add\b([\s\S]*)$/i.exec(s);if(l){let{scope:g,remainder:f}=lo((l[1]??"").trim()),h=f.match(/^(\S+)\s+([\s\S]+)$/);if(!h)return p(g==="global"?'Usage: /run add --global <name> <command\u2026> [--template "\u2026"] \u2014 cmd must reference "$OMNISH_TASK"':"Usage: /run add [--global|-g|--chat|-p] <name> <command\u2026> \u2014 see /run help");try{let x=co(h[2],n.recipesMacroDefaultCommand);uo(t,h[1],x,g);let k=gt(h[1]),$=k.ok?k.normalized:h[1].toLowerCase(),A=Qe(g,t,n,$)??Re(t,n,$);return A?en($,A,g):p("Recipe save failed.")}catch(x){return p(String(x))}}let u=/^set\b([\s\S]*)$/i.exec(s);if(u){let{scope:g,remainder:f,explicit:h}=ao((u[1]??"").trim()),x=Mi(f);if(x){if(h)return p("Cannot combine a leading scope flag (-g, --global, --chat, -p) with a trailing scope flag on the same line.");let A=gt(x.name);if(!A.ok)return p(A.error);let M=po(t,A.normalized,x.target,n);if(!M.ok)return p(M.error);if(M.kind==="noop")return p(M.message);let L=Qe(M.target,t,n,A.normalized)??Re(t,n,A.normalized);return L?en(A.normalized,L,M.target):p("Recipe scope update failed.")}let k=f.match(/^(\S+)\s*$/);if(k?.[1]&&h){let A=gt(k[1]);if(!A.ok)return p(A.error);let M=po(t,A.normalized,g,n);if(!M.ok)return p(M.error);if(M.kind==="noop")return p(M.message);let L=Qe(M.target,t,n,A.normalized)??Re(t,n,A.normalized);return L?en(A.normalized,L,M.target):p("Recipe scope update failed.")}let $=f.match(/^(\S+)\s+([\s\S]+)$/);if(!$)return p(g==="global"?'Usage: /run set --global <name> <command\u2026> [--template "\u2026"] \u2014 or scope-only: /run set --global <name> | /run set <name> -g':"Usage: /run set [--global|-g|--chat|-p] <name> <command\u2026> \u2014 or move scope without changing the body: /run set -g <name> | /run set <name> -p \u2014 see /run help");try{let A=co($[2],n.recipesMacroDefaultCommand);uo(t,$[1],A,g);let M=gt($[1]),L=M.ok?M.normalized:$[1].toLowerCase(),U=Qe(g,t,n,L)??Re(t,n,L);return U?en(L,U,g):p("Recipe save failed.")}catch(A){return p(String(A))}}let c=/^(?:remove|rm|del)\b([\s\S]*)$/i.exec(s);if(c){let{scope:g,remainder:f}=lo((c[1]??"").trim()),h=f.match(/^(\S+)\s*$/);if(!h?.[1])return p(g==="global"?"Usage: /run remove --global <name> (aliases: rm, del)":"Usage: /run remove [--global|-g|--chat|-p] <name>");let x=h[1];return Ii(t,x,g)?si(x,g):g==="chat"&&Qe("global",t,n,x)?p(`No recipe "${x}" in this chat. There is a gateway-shared recipe with that name \u2014 remove it with:
|
|
189
|
-
/run remove --global ${x}`):ii(x,g)}let d=/^queue\s+load\b([\s\S]*)$/i.exec(s);if(d){let g=(d[1]??"").trim(),f=Oi(n),h=null,x=/^json(?:\s+([\s\S]+))?$/i.exec(g);if(x){let M=(x[1]??"").trim();if(!M)return p('Usage: /run queue load json [{"recipe":"\u2026","task":"\u2026"}, \u2026] \u2014 or { "tasks": [ \u2026 ] }');h=M}else if(g.length>0){let M=g;(M.startsWith('"')&&M.endsWith('"')||M.startsWith("'")&&M.endsWith("'"))&&(M=M.slice(1,-1));let L=q(t).cwd,U=await Yt(L,M);if(U.length===0)return p(`No files matched: ${M}`);if(U.length>1)return p("Queue load: specify a single JSON file.");let re=await Vt(U);if(!re.ok)return p(re.error);let Se=mo(U[0],f);if(!Se.ok)return p(Se.error);h=Se.text}else if(o){let M=mo(o,f);if(!M.ok)return p(M.error);h=M.text}else return p("Usage: /run queue load <file.json> \u2014 or /run queue load json [\u2026] \u2014 or attach a file with caption /run queue load");let k=Pi(h);if(!k.ok)return p(k.error);let $=Fi(t,n,k.jobs);if(!$.ok)return p($.error);let A=[];for(let M of $.items)A.push(r.enqueueQueuedRun(t,M,n));return p(A.join(`
|
|
190
|
-
`))}if(/^queue$/i.test(s))return p(r.runQueueStatus(t));if(/^queue\s+resume\s*$/i.test(s))return p(r.resumeRunQueue(t,n));let m=Eu(s);if(m){let{recipe:g,task:f,queued:h}=m,x=Re(t,n,g);if(!x)return qr(g);let k=x.taskEnv??"OMNISH_TASK";if(!It(x.command,k))return p(`Recipe "${g}" command must reference "$${k}".`);let $=Vn(f,n.recipesMaxTaskChars);if(!$.ok)return p($.error);let A=x.promptTemplate?Kn(x.promptTemplate,k,$.task):$.task,M={[k]:A};if(h)return p(r.enqueueQueuedRun(t,{command:x.command,extraEnv:M,recipeLabel:g},n));let L=Xn(g);return p(r.start(t,L,x.command,n,M))}let y=s.match(/^(\S+)$/);if(y){let g=y[1],f=g.toLowerCase();return f==="add"||f==="set"?p('Usage: /run add <name> cmd [--template "\u2026"] \u2014 cmd must include "$OMNISH_TASK"'):f==="show"?p("Usage: /run show <name> \u2014 optional show --global|-g|--chat|-p <name>"):f==="remove"||f==="rm"||f==="del"?p("Usage: /run remove [--global|-g|--chat|-p] <name>"):Re(t,n,f)?p(`Usage: /run ${g} <task text\u2026> \u2014 replaces <<<OMNISH_TASK>>> / $OMNISH_TASK in stored templates`):p(`Unknown recipe "${g}". /run list`)}return p("/run: could not parse. /run help")}function Lu(e,t){let n=e.trim();if(!n||/^help$/i.test(n))return j(Gr());let r=/^list\b([\s\S]*)$/i.exec(n);if(r){let l=(r[1]??"").trim(),{filter:u,bad:c}=hi(l);return c?p(`Unknown /shortcut list suffix: "${c}". Use: list | list --chat | list -p | list --global | list -g`):Zs(wi(t,u))}let o=/^show\b([\s\S]*)$/i.exec(n);if(o){let l=(o[1]??"").trim(),{mode:u,remainder:c}=gi(l),d=/^(\S+)\s*$/i.exec(c);if(!d?.[1])return p("Usage: /shortcut show <name> \u2014 or show --global|-g|--chat|-p <name> (-g shared, -p private)");let m=d[1];if(u==="resolved"){let k=bi(t,m);if(!k)return ti(m);let $=k.scope==="global"?"Shared shortcut (all chats use it unless this chat overrides the name).":"This chat only.";return Jr(m,k.body,$)}let y=u==="global"?"global":"chat",g=ze(y,t,m);if(g!==void 0)return Jr(m,g,y==="global"?"From the shared shortcut list (--global / -g).":"From this chat only (--chat / -p).");let f=y==="chat"&&ze("global",t,m)!==void 0?`
|
|
191
|
-
(Shared shortcut exists: /shortcut show --global ${m})`:"",h=y==="global"&&ze("chat",t,m)!==void 0?`
|
|
192
|
-
(This chat overrides the name: /shortcut show --chat ${m})`:"",x=y==="global"?`Unknown shortcut "${m}" in shared shortcuts.`:`Unknown shortcut "${m}" in this chat.`;return p(`${x}${f}${h}`)}let s=/^add\b([\s\S]*)$/i.exec(n);if(s){let{scope:l,remainder:u}=Kr((s[1]??"").trim()),c=u.match(/^(\S+)\s+([\s\S]+)$/);if(!c)return p(l==="global"?"Usage: /shortcut add --global <name> <command\u2026> (short: add -g <name> <command\u2026>) \u2014 -g shared, -p private":"Usage: /shortcut add [--global|-g|--chat|-p] <name> <command\u2026> \u2014 -g shared, -p private");try{Zr(t,c[1],c[2],l);let d=mt(c[1]),m=d.ok?d.normalized:c[1].trim().toLowerCase(),y=ze(l,t,m)??"";return Zt(m,y,l)}catch(d){return p(String(d))}}let i=/^set\b([\s\S]*)$/i.exec(n);if(i){let{scope:l,remainder:u,explicit:c}=Vr((i[1]??"").trim()),d=yi(u);if(d){if(c)return p("Cannot combine a leading scope flag (-g, --global, --chat, -p) with a trailing flag on the same line.");let g=mt(d.name);if(!g.ok)return p(g.error);let f=eo(t,g.normalized,d.target);if(!f.ok)return p(f.error);if(f.kind==="noop")return p(f.message);let h=ze(f.target,t,g.normalized)??"";return Zt(g.normalized,h,f.target)}let m=u.match(/^(\S+)\s*$/);if(m?.[1]&&c){let g=mt(m[1]);if(!g.ok)return p(g.error);let f=eo(t,g.normalized,l);if(!f.ok)return p(f.error);if(f.kind==="noop")return p(f.message);let h=ze(f.target,t,g.normalized)??"";return Zt(g.normalized,h,f.target)}let y=u.match(/^(\S+)\s+([\s\S]+)$/);if(!y)return p(l==="global"?"Usage: /shortcut set --global <name> <command\u2026> \u2014 or scope-only: /shortcut set -g <name> | /shortcut set <name> -g (-g shared)":"Usage: /shortcut set [--global|-g|--chat|-p] <name> <command\u2026> \u2014 or move scope: /shortcut set -g <name> | /shortcut set <name> -p (-g shared, -p private)");try{Zr(t,y[1],y[2],l);let g=mt(y[1]),f=g.ok?g.normalized:y[1].trim().toLowerCase(),h=ze(l,t,f)??"";return Zt(f,h,l)}catch(g){return p(String(g))}}let a=/^(?:remove|rm|del)\b([\s\S]*)$/i.exec(n);if(a){let{scope:l,remainder:u}=Kr((a[1]??"").trim()),c=u.match(/^(\S+)\s*$/);if(!c?.[1])return p(l==="global"?"Usage: /shortcut remove --global <name> (aliases: rm, del)":"Usage: /shortcut remove [--global|-g|--chat|-p] <name>");let d=c[1];return ki(t,d,l)?ei(d,l):l==="chat"&&ze("global",t,d)!==void 0?p(`No shortcut "${d}" in this chat. There is a shared shortcut with that name \u2014 remove it with:
|
|
193
|
-
/shortcut remove --global ${d}`):ni(d,l)}return j(Gr())}async function Ou(e,t,n,r,o){let s=e.slice(5).trim(),i=s.toLowerCase();if(!i||i==="help")return j(Qr());let a=s.match(/^(\S+)\s*(.*)$/s);if(!a)return j(Qr());let l=a[1].toLowerCase(),u=(a[2]??"").trim();switch(l){case"start":{let c=u.match(/^(\S+)\s+([\s\S]+)$/);return c?p(r.start(t,c[1],c[2],n)):p("Usage: /apps start <name> <command\u2026>")}case"attach":{let c=u.split(/\s+/)[0];return c?p(r.attach(t,c)):p("Usage: /apps attach <name>")}case"detach":return p(r.detach(t));case"list":return p(r.list(t));case"info":case"get":{let c=u.split(/\s+/)[0];return p(r.info(t,c||void 0))}case"send":{let c=u.match(/^(\S+)\s+([\s\S]+)$/);return c?p(await r.sendText(t,c[1],c[2])):p("Usage: /apps send <name> <text\u2026>")}case"key":{let c=u.match(/^(\S+)\s+([\s\S]+)$/);return c?p(r.sendKey(t,c[1],c[2].trim())):p("Usage: /apps key <name> <KEY[,KEY\u2026]>")}case"tail":{let c=u.match(/^(\S+)(?:\s+(\d+))?\s*$/);if(!c)return p("Usage: /apps tail <name> [lines]");let d=c[2]?Number.parseInt(c[2],10):n.appsLogTailLines;return p(r.tail(t,c[1],d))}case"since":{let c=u.split(/\s+/)[0];if(!c)return p("Usage: /apps since <name>");let d=Ru(t,c),m=o.get(d)??0,{text:y,nextOffset:g}=r.readSince(t,c,m);return o.set(d,g),p(y.trimEnd()||"(no new log bytes)")}case"mute":{let c=u.split(/\s+/)[0];return c?p(r.mute(t,c)):p("Usage: /apps mute <name>")}case"unmute":{let c=u.split(/\s+/)[0];return c?p(r.unmute(t,c)):p("Usage: /apps unmute <name>")}case"raw":{let c=u.match(/^(\S+)\s+(on|off)\s*$/i);return c?p(r.setRaw(t,c[1],c[2].toLowerCase()==="on")):p("Usage: /apps raw <name> on|off")}case"resize":{let c=u.trim().split(/\s+/).filter(Boolean);if(c.length<3)return p("Usage: /apps resize <name> <cols> <rows>");let d=c[0],m=Number(c[1]),y=Number(c[2]);return!Number.isFinite(m)||!Number.isFinite(y)?p("cols and rows must be numbers."):p(r.resize(t,d,m,y))}case"stop":{let c=u.split(/\s+/)[0];return c?p(r.stop(t,c)):p("Usage: /apps stop <name>")}case"kill":{let c=u.split(/\s+/)[0];return c?p(r.kill(t,c)):p("Usage: /apps kill <name>")}case"rm":{let c=u.split(/\s+/)[0];return c?p(r.rm(t,c)):p("Usage: /apps rm <name>")}default:return p(`Unknown /apps subcommand "${l}". /apps help`)}}function Oo(e,t,n){return!e.clusterEnabled||Lo(t.trim())!==null?!0:n?Ki(n,e):!1}function Pu(e){return`wa:${e}`}function Po(e){return`tg:${e}`}function Aa(e){return{peerKey:Pu(e.fromJid),text:e.text,waMessageId:e.messageId,mediaSavedPath:e.mediaSavedPath,mediaError:e.mediaError}}import at from"node:fs";import Uu from"node:path";var La={enter:"\r",cr:"\r",lf:`
|
|
194
|
-
`,return:"\r",tab:" ",esc:"\x1B",escape:"\x1B",space:" ",backspace:"\x7F",bs:"\x7F",delete:"\x1B[3~",up:"\x1B[A",down:"\x1B[B",right:"\x1B[C",left:"\x1B[D",home:"\x1B[H",end:"\x1B[F",pageup:"\x1B[5~",pagedown:"\x1B[6~","ctrl+c":"","ctrl+d":"","ctrl+z":"","ctrl+l":"\f","ctrl+u":"","ctrl+k":"\v"};function Fu(e){let t="";for(let n=0;n<e.length;n++){let r=e[n];if(r==="\\"&&n+1<e.length){let o=e[n+1];if(o==="x"&&n+3<e.length){let s=e.slice(n+2,n+4);if(/^[0-9a-fA-F]{2}$/.test(s)){t+=String.fromCharCode(Number.parseInt(s,16)),n+=3;continue}}if(o==="r"){t+="\r",n++;continue}if(o==="n"){t+=`
|
|
195
|
-
`,n++;continue}if(o==="t"){t+=" ",n++;continue}if(o==="\\"){t+="\\",n++;continue}}t+=r}return t}function Nu(e){let t=e.trim();if(!t)return"";if(t.startsWith("\\"))return Fu(t);let n=t.toLowerCase();if(La[n])return La[n];let r=t.match(/^\^([A-Za-z])$/);if(r){let s=r[1].toUpperCase().charCodeAt(0);if(s>=64&&s<=95)return String.fromCharCode(s-64)}let o=n.match(/^ctrl\+(.+)$/);if(o){let s=o[1];if(s.length===1){let i=s.toUpperCase().charCodeAt(0);if(i>=64&&i<=95)return String.fromCharCode(i-64)}}return t}function Fo(e){let t=e.split(",").map(n=>n.trim()).filter(Boolean);return t.length===0?"":t.map(n=>Nu(n)).join("")}var hn="\x1B";function Oa(e){let t=e;return t=t.replace(new RegExp(`${hn}\\[[\\d;?]*[ -/]*[@-~]`,"g"),""),t=t.replace(new RegExp(`${hn}\\][^${hn}\\u0007]*(?:\\u0007|${hn}\\\\)`,"g"),""),t=t.replace(new RegExp(`${hn}[@-Z\\\\-_]`,"g"),""),t=t.replace(/\u0007/g,""),t}function _u(e,t){if(e.length<=t)return e?[e]:[];let n=[];for(let r=0;r<e.length;r+=t)n.push(e.slice(r,r+t));return n}function Bu(e,t){return`${e}${t}`}var dr=class{constructor(t){this.opts=t}pending=new Map;timers=new Map;lastFlushEnd=new Map;flushing=new Set;closed=!1;dispose(){this.closed=!0;for(let t of this.timers.values())clearTimeout(t);this.timers.clear(),this.pending.clear(),this.lastFlushEnd.clear(),this.flushing.clear()}push(t,n,r){if(this.closed||this.opts.isMuted(t,n))return;let o=Bu(t,n);this.pending.set(o,(this.pending.get(o)??"")+r);let s=this.timers.get(o);s&&clearTimeout(s);let i=this.opts.getCfg().appsFlushMs,a=setTimeout(()=>{this.timers.delete(o),this.flushNow(o,t,n)},i);this.timers.set(o,a)}async flushNow(t,n,r){if(!(this.closed||this.flushing.has(t))){this.flushing.add(t);try{let o=this.opts.getCfg(),s=o.appsMinIntervalMs,i=this.lastFlushEnd.get(t)??0,a=Math.max(0,i+s-Date.now());if(a>0&&await new Promise(y=>setTimeout(y,a)),this.closed)return;let l=this.pending.get(t)??"";if(this.pending.delete(t),!l||(this.opts.isRaw(n,r)||(l=Oa(l)),!l.trim()))return;let u=o.appsMaxFlushBytes;if(l.length>u){let y=l.slice(u);l=l.slice(0,u)+`
|
|
196
|
-
[\u2026+${y.length} chars; /apps since ${r} to read more]`,this.pending.set(t,(this.pending.get(t)??"")+y)}let d=(this.opts.getRunningCount(n)>1?`[${r}] `:"")+l,m=_u(d,o.appsMaxWaChars);for(let y of m){if(this.closed)break;try{await this.opts.send(n,y)}catch{break}}this.lastFlushEnd.set(t,Date.now())}finally{this.flushing.delete(t),!this.closed&&(this.pending.get(t)??"").length>0&&queueMicrotask(()=>void this.flushNow(t,n,r))}}}};import Hu from"node:fs";import Du from"node:path";import*as Pa from"node-pty";var ju=1024*1024,pr=class e{peerKey;name;command;cwd;envKeysCount;logPath;term=null;logStream=null;disposeData=null;disposeExit=null;exited=!1;ringChunks=[];ringBytes=0;constructor(t){this.peerKey=t.peerKey,this.name=t.name,this.command=t.command,this.cwd=t.cwd,this.envKeysCount=t.envKeysCount,this.logPath=t.logPath}appendRing(t){for(this.ringChunks.push(t),this.ringBytes+=t.length;this.ringBytes>ju&&this.ringChunks.length>0;){let n=this.ringChunks.shift();this.ringBytes-=n.length}}static start(t){H(qt(t.peerKey));let n=Du.join(qt(t.peerKey),`${t.name}.log`),r=Hu.createWriteStream(n,{flags:"a",mode:384}),o={...process.env,TERM:"xterm-256color",COLORTERM:"truecolor",...t.cwd?{PWD:t.cwd}:{},...t.extraEnv??{}},s=Pa.spawn(t.shell,["-lc",t.command],{name:"xterm-256color",cols:t.cols,rows:t.rows,cwd:t.cwd,env:o}),i=new e({...t,logPath:n});return i.term=s,i.logStream=r,i.disposeData=s.onData(a=>{let l=Buffer.from(a,"utf8");i.appendRing(l),r.write(a),t.router.push(t.peerKey,t.name,a)}),i.disposeExit=s.onExit(a=>{i.exited||(i.exited=!0,i.cleanupTerm(),t.onExit({exitCode:a.exitCode,signal:a.signal}))}),i}get alive(){return this.term!==null&&!this.exited}get ringByteCount(){return this.ringBytes}write(t){this.term?.write(t)}resize(t,n){this.term?.resize(t,n)}kill(t){if(this.term)try{this.term.kill(t)}catch{}}cleanupTerm(){this.disposeData?.dispose(),this.disposeData=null,this.disposeExit?.dispose(),this.disposeExit=null,this.term=null,this.logStream?.end(),this.logStream=null}destroy(){if(this.exited){this.cleanupTerm();return}this.exited=!0,this.kill("SIGHUP"),this.cleanupTerm()}};var Wu=/^[a-zA-Z0-9_-]{1,32}$/;function ke(e){return Wu.test(e)?null:"Session name must be 1\u201332 chars: letters, digits, _ or -."}function ye(e,t){return`${e}:${t}`}function Gu(e){let t=e.lastIndexOf(":");return t<=0?null:{peerKey:e.slice(0,t),name:e.slice(t+1)}}function No(e){return new Promise(t=>setTimeout(t,e))}function Ju(e,t){return e===0&&(t===0||t==null)}async function _o(e,t,n){let r=n.appsSubmitDelayMs,o=n.appsClearInputDelayMs,i=n.appsClearInput!==!1?Fo(n.appsClearInputSequence||"^A,^K"):"",l=t.replace(/\r\n/g,`
|
|
197
|
-
`).replace(/\r/g,"").split(`
|
|
198
|
-
`);i&&(o>0&&await No(o),e.write(i));for(let u of l)u.length>0&&e.write(u),r>0&&await No(r),e.write("\r"),i&&(o>0&&await No(o),e.write(i))}function zu(e,t){try{let r=at.readFileSync(e,"utf8").split(/\r?\n/);return r.slice(Math.max(0,r.length-t)).join(`
|
|
199
|
-
`).trimEnd()}catch{return""}}var Ht=class{constructor(t,n){this.getCfg=t;this.send=n,this.router=new dr({getCfg:()=>this.getCfg(),send:n,isMuted:(r,o)=>this.muted.has(ye(r,o)),isRaw:(r,o)=>this.rawMode.has(ye(r,o)),getRunningCount:r=>this.countPeerRunning(r)})}sessions=new Map;focus=new Map;muted=new Set;rawMode=new Set;router;killTimers=new Set;send;runQueue=new Map;activeQueuedRunHead=new Map;runQueuePaused=new Map;countPeerRunning(t){let n=`${t}:`,r=0;for(let[o,s]of this.sessions)o.startsWith(n)&&s.alive&&r++;return r}countTotalRunning(){let t=0;for(let n of this.sessions.values())n.alive&&t++;return t}dispose(){for(let t of this.killTimers)clearTimeout(t);this.killTimers.clear(),this.stopAll(),this.router.dispose()}stopAll(){for(let t of this.sessions.values())t.destroy();this.sessions.clear(),this.focus.clear(),this.runQueue.clear(),this.activeQueuedRunHead.clear(),this.runQueuePaused.clear()}getFocus(t){return this.focus.get(t)??null}logPath(t,n){return Uu.join(qt(t),`${n}.log`)}getSession(t,n){return this.sessions.get(ye(t,n))}removeSessionRecord(t,n){this.sessions.delete(ye(t,n)),this.focus.get(t)===n&&this.focus.set(t,null)}async writeFocusedLine(t,n){let r=this.focus.get(t);if(!r)return!1;let o=this.getSession(t,r);return o?.alive?(await _o(o,n,this.getCfg()),!0):!1}async writeNamedLine(t,n,r){let o=ke(n);if(o)return o;let s=this.getSession(t,n);return s?.alive?(await _o(s,r,this.getCfg()),null):`No app session "${n}". /apps list`}enqueueQueuedRun(t,n,r){let o=this.runQueue.get(t)??[];o.push(n),this.runQueue.set(t,o);let s=o.length,i=this.runQueuePaused.get(t)??!1,a=this.activeQueuedRunHead.get(t)??null,l=a?.sessionName??null;if(l?this.getSession(t,l)?.alive??!1:!1){let c=a?.recipeLabel?` (recipe: ${a.recipeLabel})`:"",d=s-1,m=d>0?`${d} other job(s) in line before this one.`:"Next in line after the active run finishes.";return`Queued "${n.recipeLabel}" (wait slot ${s} behind active ${l}${c}). ${m}`}return i?`Queued "${n.recipeLabel}" (position ${s}). Run queue is paused \u2014 /run queue resume to continue.`:this.drainNextQueuedRun(t,r)}resumeRunQueue(t,n){this.runQueuePaused.set(t,!1);let r=this.activeQueuedRunHead.get(t)??null,o=r?.sessionName??null;if(o?this.getSession(t,o)?.alive??!1:!1){let a=r?.recipeLabel?` (recipe: ${r.recipeLabel})`:"";return`Run queue: session "${o}"${a} is still running. Wait for exit code=0 and signal=0 before the next starts.`}return(this.runQueue.get(t)??[]).length===0?"Run queue is empty.":(this.activeQueuedRunHead.set(t,null),this.drainNextQueuedRun(t,n))}runQueueStatus(t){let n=this.runQueue.get(t)??[],r=this.activeQueuedRunHead.get(t)??null,o=r?.sessionName??null,s=this.runQueuePaused.get(t)??!1,i=o?this.getSession(t,o)?.alive??!1:!1,a=["Run queue (this chat)"];if(o&&i){let l=r?.recipeLabel?` \xB7 recipe: ${r.recipeLabel}`:"";a.push(`Active: ${o}${l}`)}else o?a.push(`Active: ${o} (exiting or stale)`):a.push("Active: (none)");if(a.push(`Pending: ${n.length} (waiting only \u2014 not counting the active run above)`),a.push(`Paused: ${s}`),n.length>0){a.push("Waiting (FIFO order):");for(let u=0;u<Math.min(n.length,20);u++)a.push(`${u+1}) ${n[u].recipeLabel}`);n.length>20&&a.push(`\u2026 and ${n.length-20} more`)}return a.push("Next auto-starts only after exit code=0 and signal=0."),a.push("Commands: /run queue resume"),a.join(`
|
|
200
|
-
`)}drainNextQueuedRun(t,n){let r=this.runQueue.get(t);if(!r||r.length===0)return"";let o=r.shift();r.length===0?this.runQueue.delete(t):this.runQueue.set(t,r);let s=Xn(o.recipeLabel),i=this.runQueue.get(t)?.length??0,a=this.start(t,s,o.command,n,o.extraEnv);if(!a.includes("started and attached"))return r.unshift(o),this.runQueue.set(t,r),this.runQueuePaused.set(t,!0),this.activeQueuedRunHead.set(t,null),`${a}
|
|
201
|
-
Run queue paused; fix the issue, then /run queue resume.`;this.activeQueuedRunHead.set(t,{sessionName:s,recipeLabel:o.recipeLabel});let u=i>0?`
|
|
202
|
-
Run queue: started head above; ${i} job(s) still waiting in FIFO.`:`
|
|
203
|
-
Run queue: started head above; no further queued jobs.`;return`${a}${u}`}start(t,n,r,o,s){let i=ke(n);if(i)return i;if(!r.trim())return`Usage: /apps start <name> <command\u2026>
|
|
204
|
-
Example: /apps start sh bash`;if(this.sessions.has(ye(t,n)))return`Session "${n}" already exists. /apps stop ${n} or pick another name.`;if(this.countPeerRunning(t)>=o.appsMaxSessions)return`Per-chat app limit (${o.appsMaxSessions}) reached. /apps stop or /apps rm an old session.`;if(this.countTotalRunning()>=o.appsMaxSessionsTotal)return`Global app limit (${o.appsMaxSessionsTotal}) reached. Stop sessions in other chats first.`;z();let a=q(t).cwd,l={...process.env,TERM:"xterm-256color",COLORTERM:"truecolor",...a?{PWD:a}:{},...s??{}},u;try{u=pr.start({peerKey:t,name:n,shell:o.shell,command:r.trim(),cwd:a,cols:o.appsCols,rows:o.appsRows,envKeysCount:Object.keys(l).length,extraEnv:s,router:this.router,onExit:c=>{this.handleSessionExit(t,n,c)}})}catch(c){return`App spawn failed: ${String(c)}
|
|
205
|
-
If install skipped native builds: pnpm approve-builds && pnpm install (needs @whiskeysockets/baileys, sharp, protobufjs, esbuild, node-pty).`}return this.sessions.set(ye(t,n),u),this.focus.set(t,n),`App "${n}" started and attached.
|
|
206
|
-
[cwd: ${a}]
|
|
207
|
-
Plain DMs go to this session until /apps detach. Use >othername text for another session.`}async handleSessionExit(t,n,r){let o=ye(t,n);if(!this.sessions.has(o))return;let s=this.activeQueuedRunHead.get(t)?.sessionName===n,i=this.focus.get(t)===n;this.sessions.delete(o),i&&this.focus.set(t,null),this.muted.delete(o),this.rawMode.delete(o);let a=r.signal!=null?` signal=${r.signal}`:"",l=i?" (detached)":"",u=`[${n}] exited code=${r.exitCode}${a}${l}`;try{await this.send(t,u)}catch{}if(!s)return;this.activeQueuedRunHead.set(t,null);let c=this.getCfg(),d=Ju(r.exitCode,r.signal),m=this.runQueue.get(t)?.length??0;if(d){if(this.runQueuePaused.set(t,!1),m>0){let y=this.drainNextQueuedRun(t,c);if(y)try{await this.send(t,y)}catch{}}return}if(this.runQueuePaused.set(t,!0),m>0){let y=`Run queue paused (exit code=${r.exitCode}${r.signal!=null?` signal=${r.signal}`:""}). ${m} run(s) waiting. /run queue resume to continue.`;try{await this.send(t,y)}catch{}}}attach(t,n){let r=ke(n);return r||(this.getSession(t,n)?.alive?(this.focus.set(t,n),`Attached to "${n}". Plain messages (no ! or / or >) go to this app. /apps detach to stop.`):`No session "${n}". /apps list`)}detach(t){return this.focus.set(t,null),"Detached (attach mode off)."}list(t){let n=[];for(let[s,i]of this.sessions){let a=Gu(s);if(!a||a.peerKey!==t||!i.alive)continue;let l=this.focus.get(t)===a.name?" *":"";n.push(`${a.name}${l}`)}if(n.length===0)return"(no app sessions; /apps start <name> <cmd>)";let r=this.focus.get(t);return`${r?`attached: ${r}`:"(no focus)"}
|
|
208
|
-
App sessions:
|
|
209
|
-
${n.join(`
|
|
210
|
-
`)}`}info(t,n){let r=this.getCfg(),o=n??this.focus.get(t)??"";if(!o)return"Usage: /apps info <name> (or /apps get <name>, or attach first)";let s=ke(o);if(s)return s;let i=this.getSession(t,o),a=this.logPath(t,o),l=0;try{l=at.statSync(a).size}catch{}let u=this.muted.has(ye(t,o)),c=this.rawMode.has(ye(t,o));return[`session: ${o}`,`alive: ${i?.alive??!1}`,`cmd: ${i?.command??"(unknown)"}`,`cwd: ${i?.cwd??"(unknown)"}`,`env keys: ${i?.envKeysCount??"?"}`,`terminal size: ${r.appsCols}x${r.appsRows}`,`ring bytes (approx): ${i?.ringByteCount??0}`,`log: ${a} (${l} bytes)`,`mute outbound: ${u}`,`raw outbound: ${c}`].join(`
|
|
211
|
-
`)}async sendText(t,n,r){let o=ke(n);if(o)return o;let s=this.getSession(t,n);if(!s?.alive)return`No session "${n}".`;let i=r.replace(/\r\n/g,`
|
|
212
|
-
`);return await _o(s,i,this.getCfg()),`Sent to "${n}" (${i.length} chars + Enter per line).`}sendKey(t,n,r){let o=ke(n);if(o)return o;let s=this.getSession(t,n);if(!s?.alive)return`No session "${n}".`;let i=Fo(r);return i?(s.write(i),`Sent keys to "${n}".`):"Empty key sequence. Example: /apps key sh Enter or ^C,Up,Enter"}tail(t,n,r){let o=ke(n);if(o)return o;let s=this.logPath(t,n);if(!at.existsSync(s))return`(no log file for ${n})`;let i=this.getCfg(),a=Number.isFinite(r)&&r>0?Math.min(500,r):i.appsLogTailLines;return zu(s,a)||"(empty log)"}readSince(t,n,r){let o=this.logPath(t,n);try{let i=at.statSync(o).size,a=at.openSync(o,"r");try{let l=Math.min(r,i),u=i-l,c=Buffer.alloc(u);return u>0&&at.readSync(a,c,0,u,l),{text:c.toString("utf8"),nextOffset:i}}finally{at.closeSync(a)}}catch{return{text:"",nextOffset:r}}}mute(t,n){let r=ke(n);return r||(this.muted.add(ye(t,n)),`Muted chat output for "${n}" (log still grows). /apps unmute ${n}`)}unmute(t,n){let r=ke(n);return r||(this.muted.delete(ye(t,n)),`Unmuted "${n}".`)}setRaw(t,n,r){let o=ke(n);if(o)return o;let s=ye(t,n);return r?this.rawMode.add(s):this.rawMode.delete(s),`raw chat output for "${n}": ${r?"on (ANSI kept)":"off (stripped)"}.`}resize(t,n,r,o){let s=ke(n);if(s)return s;let i=this.getSession(t,n);if(!i?.alive)return`No session "${n}".`;let a=Math.min(500,Math.max(20,Math.floor(r))),l=Math.min(200,Math.max(5,Math.floor(o)));return i.resize(a,l),`Resized "${n}" to ${a}x${l}.`}stop(t,n){let r=ke(n);if(r)return r;let o=this.getSession(t,n);if(!o?.alive)return`No running session "${n}".`;o.kill("SIGTERM");let s=ye(t,n),i=setTimeout(()=>{this.killTimers.delete(i);let a=this.getSession(t,n);a?.alive&&a.kill("SIGKILL")},5e3);return this.killTimers.add(i),`SIGTERM sent to "${n}". SIGKILL in 5s if still running.`}kill(t,n){let r=ke(n);if(r)return r;let o=this.getSession(t,n);return o?.alive?(o.kill("SIGKILL"),`SIGKILL sent to "${n}".`):`No running session "${n}".`}rm(t,n){let r=ke(n);if(r)return r;if(this.getSession(t,n)?.alive)return`Session "${n}" is still running. /apps stop ${n} first, then /apps rm ${n}.`;this.removeSessionRecord(t,n),this.muted.delete(ye(t,n)),this.rawMode.delete(ye(t,n));let s=this.logPath(t,n);try{at.rmSync(s,{force:!0})}catch{}return`Removed "${n}" metadata and log.`}};import yn from"node:fs";import wn from"node:path";function Fa(e,t,n){if(e==="none")return[];let r=new Set;if((e==="self"||e==="all")&&r.add(t),e==="wa"||e==="all")for(let o of n.allowFrom){let s=G(String(o));s&&r.add(`wa:${Qt(s)}`)}if(e==="tg"||e==="all")for(let o of n.telegramAllowFrom){let s=ue(String(o));s&&r.add(`tg:${s}`)}return[...r]}var qu=8,Na=12e4,_a=10,Qu=1e3;function Yu(e){return e.toISOString().replace(/[:.]/g,"-")}function Vu(e){let t="";for(let n of e)n==="*"?t+="[^/\\\\]*":n==="?"?t+="[^/\\\\]":/[.+^${}()|[\]\\]/.test(n)?t+=`\\${n}`:t+=n;return new RegExp(`^${t}$`)}function Ku(e,t,n){let r=_t(e,t),o=wn.dirname(r),s=wn.basename(r);if(o.includes("*")||o.includes("?"))return[];if(!s.includes("*")&&!s.includes("?")){try{let u=yn.statSync(r);if(u.isFile()&&u.mtimeMs>=n)return[r]}catch{}return[]}let i;try{i=yn.readdirSync(o)}catch{return[]}let a=Vu(s),l=[];for(let u of i){if(!a.test(u))continue;let c=wn.join(o,u);try{let d=yn.statSync(c);d.isFile()&&d.mtimeMs>=n&&l.push(c)}catch{}}return l}function Ba(e,t,n){let r=et(e,t.fileSendMaxBytes);return"error"in r?{ok:!1,displayName:n??wn.basename(e),reason:r.error.replace(/\.$/,"").toLowerCase()}:{ok:!0,spec:{absPath:r.absPath,category:r.category,mimetype:r.mimetype,displayName:n??r.displayName}}}async function Ha(e,t,n,r){let o=q(t.ownerPeerKey),s=t.cwd.trim()?t.cwd:o.cwd,i=_t(t.outputDir,o.cwd);try{yn.mkdirSync(i,{recursive:!0,mode:448})}catch(W){E.warn({err:String(W),outDir:i},"cowork mkdir outputDir")}let a=n.slotMs!==null?new Date(n.slotMs).toLocaleString(void 0,{dateStyle:"short",timeStyle:"short"}):"on-demand",l=n.onDemand?"on-demand":n.catchUp?"catch-up":"scheduled",u=Date.now(),c=await Et(e.shell,t.command,{timeoutMs:e.syncTimeoutMs,maxBytes:e.syncMaxBytes,cwd:s}),d=Date.now()-u,m=`${Yu(new Date)}-${t.id}-${l}.log`,y=wn.join(i,m),f=[`cowork task=${t.name} id=${t.id}`,`slot=${a} kind=${l}`,`cwd=${s}`,`exit=${c.code} timedOut=${c.timedOut} durationMs=${d}`,"---",""].join(`
|
|
213
|
-
`)+(c.stdout||"")+(c.stderr?`
|
|
209
|
+
`);n.transaction(()=>{for(let s of t)r.run(e,s.slotMs,s.kind,s.completedAtMs,s.logPath)})()}function Ui(e){ln().prepare("INSERT OR REPLACE INTO cowork_task_state (task_id, last_ok, updated_at_ms) VALUES (?, 1, ?)").run(e,Date.now())}function _o(e){return ln().prepare("SELECT updated_at_ms FROM cowork_task_state WHERE task_id = ?").get(e)?.updated_at_ms??null}function Fo(e){let t=ln().prepare("SELECT last_ok FROM cowork_task_state WHERE task_id = ?").get(e);return t==null?null:t.last_ok===1}function Wo(e,t){ln().prepare("INSERT OR REPLACE INTO cowork_task_state (task_id, last_ok, updated_at_ms) VALUES (?, ?, ?)").run(e,t?1:0,Date.now())}function Gh(e){let n=ln().prepare("SELECT COUNT(*) AS c FROM cowork_slot_completion WHERE task_id = ?"),r=Date.now();for(let o of e)if(!(o.lastCompletedSlotMs==null||!Number.isFinite(o.lastCompletedSlotMs)||n.get(o.id).c>0))try{No(o.id,[{slotMs:o.lastCompletedSlotMs,kind:"migrated",logPath:null,completedAtMs:r}]),T.info({taskId:o.id,slotMs:o.lastCompletedSlotMs},"cowork seeded completion from tasks.json")}catch(i){T.warn({err:String(i),taskId:o.id},"cowork seed from tasks.json failed")}}function Di(e,t){if(e.startsWith("tg:")){let n=e.slice(3);return Gr(t.telegramAllowFrom).has(n)}return nl(jr(t.allowFrom),e.replace(/^wa:/,""))}var Jh=8,xu=12e4,$u=10,Kh=1e3;function zh(e){return e.toISOString().replace(/[:.]/g,"-")}function qh(e){let t="";for(let n of e)n==="*"?t+="[^/\\\\]*":n==="?"?t+="[^/\\\\]":/[.+^${}()|[\]\\]/.test(n)?t+=`\\${n}`:t+=n;return new RegExp(`^${t}$`)}function Yh(e,t,n){let r=Ge(e,t),o=br.dirname(r),s=br.basename(r);if(o.includes("*")||o.includes("?"))return[];if(!s.includes("*")&&!s.includes("?")){try{let d=wr.statSync(r);if(d.isFile()&&d.mtimeMs>=n)return[r]}catch{}return[]}let i;try{i=wr.readdirSync(o)}catch{return[]}let a=qh(s),l=[];for(let d of i){if(!a.test(d))continue;let u=br.join(o,d);try{let c=wr.statSync(u);c.isFile()&&c.mtimeMs>=n&&l.push(u)}catch{}}return l}function Ru(e,t,n){let r=Kt(e,t.fileSendMaxBytes);return"error"in r?{ok:!1,displayName:n??br.basename(e),reason:r.error.replace(/\.$/,"").toLowerCase()}:{ok:!0,spec:{absPath:r.absPath,category:r.category,mimetype:r.mimetype,displayName:n??r.displayName}}}async function Cu(e,t,n,r){let o=oe(t.ownerPeerKey),s=t.cwd.trim()?t.cwd:o.cwd,i=Ge(t.outputDir,o.cwd);try{wr.mkdirSync(i,{recursive:!0,mode:448})}catch(k){T.warn({err:String(k),outDir:i},"cowork mkdir outputDir")}let a=n.slotMs!==null?new Date(n.slotMs).toLocaleString(void 0,{dateStyle:"short",timeStyle:"short"}):"on-demand",l=n.onDemand?"on-demand":n.catchUp?"catch-up":"scheduled",d=Date.now(),u=await sn(e.shell,t.command,{timeoutMs:e.syncTimeoutMs,maxBytes:e.syncMaxBytes,cwd:s}),c=Date.now()-d,m=`${zh(new Date)}-${t.id}-${l}.log`,f=br.join(i,m),g=[`cowork task=${t.name} id=${t.id}`,`slot=${a} kind=${l}`,`cwd=${s}`,`exit=${u.code} timedOut=${u.timedOut} durationMs=${c}`,"---",""].join(`
|
|
210
|
+
`)+(u.stdout||"")+(u.stderr?`
|
|
214
211
|
--- stderr ---
|
|
215
|
-
${
|
|
216
|
-
`),
|
|
217
|
-
[...input truncated]`}function
|
|
212
|
+
${u.stderr}`:"");try{wr.writeFileSync(f,g,{mode:384})}catch(k){T.warn({err:String(k),logPath:f},"cowork write log")}let y=u.code===0&&!u.timedOut&&u.signal===null,v=xe().find(k=>k.id===t.id&&k.ownerPeerKey===t.ownerPeerKey),E=v?.notify??t.notify,R=v?.notifyWhen??t.notifyWhen??"always",P=v?.attachLog??t.attachLog,M=v?.attachFiles??t.attachFiles,L=!0;if(R==="failure")L=!y;else if(R==="state-change"){let k=Fo(t.id);L=k===null||k!==y}Wo(t.id,y);let A=u.timedOut?"timeout":u.signal?`signal ${u.signal}`:u.code!==0&&u.code!==null?`exit ${u.code}`:null,K=`slot: ${a} \xB7 ${l}${A?` \xB7 ${A}`:""}`,X=(u.stdout||"").replace(/\s+$/,""),se=(u.stderr||"").trim(),he=X||(se?`(stderr) ${se}`:"(no output)"),D=R==="state-change"?y?" [recovered]":" [failing]":"",ae=`${t.name}${D} : ${A?`[${A}] `:""}${he}`,Y=n.onDemand?[K,`output: ${he}`]:[ae];if(L){let k=[],x=[],O=d-Kh,F=[],Q=new Set;if(Array.isArray(M))for(let de of M){let at;try{at=Yh(de,s,O)}catch(lt){T.warn({err:String(lt),pat:de},"cowork attach glob"),k.push(`attach: ${de} skipped (glob error)`);continue}if(at.length!==0)for(let lt of at)Q.has(lt)||(Q.add(lt),F.push(lt))}if(P){let de=Ru(f,e,`${t.name}-${l}.log`);de.ok?x.push(de.spec):k.push(`attach: ${de.displayName} skipped (${de.reason})`)}let z=0;for(let de of F){if(x.length>=$u){z+=1;continue}let at=Ru(de,e);at.ok?x.push(at.spec):k.push(`attach: ${at.displayName} skipped (${at.reason})`)}z>0&&k.push(`attached: skipped ${z} file(s) over cap ${$u}`),k.length>0&&Y.push(...k);let we=Y.join(`
|
|
213
|
+
`),Ue=p(we),Ie=kn(E,t.ownerPeerKey,e);for(let de of Ie){let at=ke(Ue,de.startsWith("tg:")?"telegram":"whatsapp").text;try{await r.sendToPeer(de,at)}catch(lt){T.warn({err:String(lt),pk:de},"cowork notify failed")}for(let lt of x)try{await r.sendMediaToPeer(de,lt)}catch(vm){T.warn({err:String(vm),pk:de,file:lt.displayName},"cowork media notify failed")}}}return{commandOk:y,logPath:f}}function Uo(e){let t=!1;an(xe());let n=async()=>{if(t)return;t=!0;let s=Date.now(),i=0,a=0,l=0,d=0,u=0;try{let c=e.getConfig(),{batch:m,remainingAfter:f}=al(Jh);a=m.length,l=f,i=m.length+f;for(let y of m)try{let v=xe().find(E=>E.name===y.name.toLowerCase()&&E.ownerPeerKey===y.ownerPeerKey);if(v&&v.enabled){if(!Di(v.ownerPeerKey,c)){T.warn({task:v.name,peer:v.ownerPeerKey},"cowork: skipping on-demand run \u2014 owner no longer on allowlist");continue}d+=1,await Cu(c,v,{slotMs:null,catchUp:!1,onDemand:!0},e)}}catch(b){T.warn({err:String(b),pending:y.name},"cowork on-demand run failed")}let h=xe();an(h);let g=Date.now();for(let y of h){if(!y.enabled||y.schedule.kind!=="heartbeat"||!Di(y.ownerPeerKey,c))continue;let b=_o(y.id);if(b===null)continue;let v=b+y.schedule.intervalMs+y.schedule.graceMs;if(g>v){if(Fo(y.id)===!1)continue;Wo(y.id,!1);let R=Math.round((g-b)/6e4),P=`${y.name} [heartbeat missed] \u2014 last check-in ${R}m ago`,M=y.notify,L=kn(M,y.ownerPeerKey,c);for(let A of L)try{await e.sendToPeer(A,P)}catch(K){T.warn({err:String(K),pk:A},"cowork heartbeat notify failed")}}else if(g<=v&&Fo(y.id)===!1){Wo(y.id,!0);let R=`${y.name} [heartbeat recovered]`,P=y.notify,M=kn(P,y.ownerPeerKey,c);for(let L of M)try{await e.sendToPeer(L,R)}catch(A){T.warn({err:String(A),pk:L},"cowork heartbeat recovery notify failed")}}}for(let y of h){if(!y.enabled||y.schedule.kind==="ondemand"||y.schedule.kind==="heartbeat")continue;if(!Di(y.ownerPeerKey,c)){T.warn({task:y.name,peer:y.ownerPeerKey},"cowork: skipping scheduled run \u2014 owner no longer on allowlist");continue}let b=Oo(y),v=bu(y.schedule,b,y.createdAtMs,g);if(v.length===0)continue;let E=v[v.length-1],R=g-E>xu;try{u+=1;let{commandOk:P,logPath:M}=await Cu(c,y,{slotMs:E,catchUp:R,onDemand:!1},e);if(P){let L=Date.now(),A=g-E<=xu?"on_time":"catch_up";if(v.length===1)No(y.id,[{slotMs:E,kind:A,logPath:M,completedAtMs:L}]);else{let X=v.slice(0,-1).map(se=>({slotMs:se,kind:"coalesced",logPath:null,completedAtMs:L}));X.push({slotMs:E,kind:A,logPath:M,completedAtMs:L}),No(y.id,X)}}}catch(P){T.warn({err:String(P),task:y.name},"cowork scheduled run failed")}}}finally{let c=Date.now()-s;T.info({tickMs:c,pendingDepthStart:i,pendingDequeued:a,pendingRemainingAfter:l,pendingRunsStarted:d,scheduledRan:u},"cowork tick"),t=!1}},r=setInterval(()=>void n(),3e4),o=setTimeout(()=>void n(),5e3);return()=>{clearInterval(r),clearTimeout(o),vu()}}import Qh from"node:fs";import Mu from"node:path";import{fileURLToPath as Vh}from"node:url";var Do=null;function Ze(){if(Do!==null)return Do;let e=Mu.dirname(Vh(import.meta.url)),t=Mu.join(e,"..","package.json"),n=Qh.readFileSync(t,"utf8"),r=JSON.parse(n);return Do=typeof r.version=="string"&&r.version.trim()?r.version.trim():"0.0.0",Do}be();G();import{spawn as Xh}from"node:child_process";import Tu from"node:fs";import Eu from"node:path";var Zh=new Set(["PATH","HOME","USER","LOGNAME","SHELL","LANG","LC_ALL","LC_CTYPE","LC_MESSAGES","TMPDIR","TZ"]),eg=new Set(["OPENAI_API_KEY","ANTHROPIC_API_KEY","GOOGLE_API_KEY","GEMINI_API_KEY","MISTRAL_API_KEY","GROQ_API_KEY","COHERE_API_KEY","HUGGINGFACE_TOKEN","TOGETHER_API_KEY","FIREWORKS_API_KEY","PERPLEXITY_API_KEY","DEEPSEEK_API_KEY","XAI_API_KEY","CURSOR_API_KEY"]);function tg(e,t){let n={OMNISH_PEER_KEY:e,OMNISH_CHAT_MESSAGE:t};for(let r of Zh){let o=process.env[r];o!==void 0&&(n[r]=o)}for(let[r,o]of Object.entries(process.env))o&&(r.startsWith("OMNISH_")||eg.has(r))&&(n[r]=o);return n}var Pu=new Map;function ng(e,t){let r=(Pu.get(e)??Promise.resolve()).then(t).catch(o=>{T.warn({peerKey:e,err:String(o)},"chat LLM fallback queue task failed")});Pu.set(e,r)}function rg(e){te();let t=e.chatLlmWorkDir.trim();if(t){let r=Eu.resolve(t);return j(r),{cwd:r,cleanup:()=>{}}}let n=Tu.mkdtempSync(Eu.join(H,"chat-llm-"));return{cwd:n,cleanup:()=>{try{Tu.rmSync(n,{recursive:!0,force:!0})}catch{}}}}function og(e,t){return e.length<=t?e:`${e.slice(0,t)}
|
|
214
|
+
[...input truncated]`}function sg(e,t){let n=[],r=e.stdout.trimEnd(),o=e.stderr.trimEnd();return r&&n.push(r.length>t?`${r.slice(0,t)}
|
|
218
215
|
[...truncated]`:r),o&&(n.push("\u2014 stderr \u2014"),n.push(o.length>t?`${o.slice(0,t)}
|
|
219
216
|
[...truncated]`:o)),n.length===0&&n.push("(no output)"),e.timedOut?n.push(`(timed out after ${Math.round(e.durationMs/1e3)}s)`):e.code!==0&&e.code!==null?n.push(`(exit ${e.code})`):e.signal&&n.push(`(signal ${e.signal})`),n.join(`
|
|
220
|
-
`)}function
|
|
221
|
-
[...truncated]`;else{let
|
|
222
|
-
[...truncated]`}try{
|
|
223
|
-
${String(
|
|
224
|
-
${String(
|
|
225
|
-
|
|
217
|
+
`)}function Ho(e){let t=e&&typeof e=="object"&&"code"in e?String(e.code):"";return t==="EPIPE"||t==="EOF"}function Au(e){try{e?.stdin?.end()}catch(t){if(!Ho(t))throw t}}function ig(e,t,n,r){return new Promise(o=>{let s=Date.now(),i=!1,a="",l="",d=!1,u=null,c,m=b=>{d||(d=!0,clearTimeout(c),o(b))},f=r.maxOutChars,h=(b,v)=>{b==="out"?a+=v:l+=v;let E=a.length+l.length;if(E>f){let R=E-f;if(l.length>=R)l=`${l.slice(0,Math.max(0,l.length-R))}
|
|
218
|
+
[...truncated]`;else{let P=R-l.length;l="",a=`${a.slice(0,Math.max(0,a.length-P))}
|
|
219
|
+
[...truncated]`}try{u?.kill("SIGTERM")}catch{}}};try{u=Xh(e,["-c",t],{cwd:r.cwd,env:r.env,stdio:["pipe","pipe","pipe"]})}catch(b){m({code:null,stdout:"",stderr:String(b),durationMs:Date.now()-s,timedOut:!1,signal:null});return}c=setTimeout(()=>{i=!0;try{u?.kill("SIGTERM")}catch{}},r.timeoutMs),u.stdout?.setEncoding("utf8"),u.stderr?.setEncoding("utf8"),u.stdout?.on("data",b=>h("out",b)),u.stderr?.on("data",b=>h("err",b));let g=u.stdin;g&&g.on("error",b=>{Ho(b)||m({code:null,stdout:a,stderr:`${l}
|
|
220
|
+
${String(b)}`,durationMs:Date.now()-s,timedOut:i,signal:null})}),(()=>{if(!(!g||d))try{g.write(n,"utf8",b=>{if(b&&!Ho(b)){try{u?.kill("SIGTERM")}catch{}m({code:null,stdout:a,stderr:`${l}
|
|
221
|
+
${String(b)}`,durationMs:Date.now()-s,timedOut:i,signal:null});return}Au(u)})}catch(b){if(!Ho(b)){m({code:null,stdout:a,stderr:`${l}
|
|
222
|
+
${String(b)}`,durationMs:Date.now()-s,timedOut:i,signal:null});return}Au(u)}})(),u.on("error",b=>{m({code:null,stdout:a,stderr:`${l}
|
|
223
|
+
${String(b)}`,durationMs:Date.now()-s,timedOut:i,signal:null})}),u.on("close",(b,v)=>{m({code:b,stdout:a,stderr:l,durationMs:Date.now()-s,timedOut:i,signal:v??null})})})}async function ag(e,t,n,r){let o=e.chatLlmShellCommand.trim();if(!o){await r("(chat LLM fallback: chatLlmShellCommand is empty)");return}let{cwd:s,cleanup:i}=rg(e);try{let a=og(n,e.chatLlmMaxInputChars),l=tg(t,a),d=e.chatLlmMaxOutputChars,u;e.chatLlmNeedsTty?u=await sn(e.shell,o,{cwd:s,timeoutMs:e.chatLlmTimeoutMs,maxBytes:d,env:l}):u=await ig(e.shell,o,a,{cwd:s,timeoutMs:e.chatLlmTimeoutMs,maxOutChars:d,env:l});let c=sg(u,d);await r(c)}catch(a){T.warn({peerKey:t,err:String(a)},"chat LLM fallback run failed"),await r(`(assistant error) ${String(a)}`)}finally{i()}}function Ln(e,t,n,r){T.info({peerKey:t,len:n.length},"chat LLM fallback enqueued"),ng(t,async()=>{await ag(e,t,n,r),T.info({peerKey:t},"chat LLM fallback completed")})}G();import{spawn as lg}from"node:child_process";import cg from"node:crypto";import ze from"node:fs";import Hi from"node:path";var Iu=64,ug=/^[a-zA-Z0-9._-]+$/;function dg(e){let t=e.trim();return t?t.length>Iu?`Job name must be at most ${Iu} characters.`:ug.test(t)?null:"Job name may only use letters, digits, and . _ -":"Job name must not be empty."}function Lu(e){let t=e.trim();if(!t)return{error:"empty"};let n=null,r=!1,o=!0;for(;o;){o=!1;let i=t.trim();if(/^--notify(?:\s|$)/i.test(i)){r=!0,t=i.slice(8).trim(),o=!0;continue}if(/^-N(?:\s|$)/.test(i)){r=!0,t=i.slice(2).trim(),o=!0;continue}let a=[[/^--name\s*=\s*(\S+)\s+([\s\S]+)$/i,1,2],[/^--name\s+(\S+)\s+([\s\S]+)$/i,1,2],[/^-n\s+(\S+)\s+([\s\S]+)$/i,1,2]];for(let[l,d,u]of a){let c=i.match(l);if(c){n=c[d],t=c[u].trim(),o=!0;break}}}let s=t.trim();if(n===null&&[/^-n\s+\S+\s*$/i,/^--name\s+\S+\s*$/i,/^--name=\S+\s*$/i,/^-n\s*$/i,/^--name\s*$/i].some(a=>a.test(s)))return{error:"Add a shell command after the name."};if(n!==null){let i=dg(n);if(i)return{error:i}}return s?{cmd:s,name:n,notify:r}:{error:"Add a shell command after the flags."}}function pg(e,t){let n=t.trim();if(!n)return{ok:!1,error:"Missing job id or name."};let r=n.toLowerCase(),o=/^[a-f0-9]{8}$/;if(o.test(r)&&e.find(i=>i.id===r))return{ok:!0,id:r};for(let s of e)if(s.name&&s.name.toLowerCase()===r)return{ok:!0,id:s.id};return o.test(r)?{ok:!1,error:`Unknown job id: ${r}`}:{ok:!1,error:`Unknown job name: ${n}`}}function On(e,t){ze.writeFileSync(e,JSON.stringify(t,null,2)+`
|
|
224
|
+
`,{mode:384})}function kr(e){try{return JSON.parse(ze.readFileSync(e,"utf8"))}catch{return null}}function Ou(e){let t=e.name?`${e.id} (${e.name})`:e.id,n=e.finishedAt&&e.startedAt?Date.parse(e.finishedAt)-Date.parse(e.startedAt):null,r=n!==null?`${(n/1e3).toFixed(1)}s`:"?",o=e.exitCode===0&&!e.signal,s=e.status==="killed"?`killed (signal ${e.signal??"?"})`:o?"completed successfully":e.signal?`signal ${e.signal}`:`exit ${e.exitCode??"?"}`,i=e.cmd.length>100?`${e.cmd.slice(0,100)}\u2026`:e.cmd;return[`Job ${t} ${s}`,`duration: ${r}`,`$ ${i}`,`/log ${e.name??e.id}`].join(`
|
|
225
|
+
`)}var Tt=class{running=new Map;metaPath(t){return Hi.join(tt,`${t}.meta.json`)}logPath(t){return Hi.join(tt,`${t}.log`)}spawnJob(t,n,r={}){te();let o=cg.randomBytes(4).toString("hex"),s=this.logPath(o),i=this.metaPath(o);ze.writeFileSync(s,"",{flag:"w",mode:384});let a=ze.createWriteStream(s,{flags:"a"}),l=new Date().toISOString(),d=r.cwd,u=lg(t,["-c",n],{stdio:["ignore","pipe","pipe"],env:{...process.env,TERM:"dumb",...d?{PWD:d}:{}},...d?{cwd:d}:{}});this.running.set(o,u);let c={id:o,cmd:n,...r.name?{name:r.name}:{},pid:u.pid??null,startedAt:l,status:"running",exitCode:null,signal:null,...r.notifyPeerKey?{notifyPeerKey:r.notifyPeerKey}:{}};return On(i,c),u.stdout?.on("data",m=>{a.write(m)}),u.stderr?.on("data",m=>{a.write(m)}),u.on("close",(m,f)=>{this.running.delete(o),a.end();let h=kr(i)??c,g={...h,status:h.status==="killed"?"killed":"done",exitCode:m,signal:f??null,finishedAt:new Date().toISOString()};On(i,g),r.onComplete&&r.onComplete(g)}),u.on("error",m=>{this.running.delete(o),a.end(()=>{try{ze.appendFileSync(s,`
|
|
226
|
+
[spawn error] ${String(m)}
|
|
227
|
+
`)}catch{}});let h={...kr(i)??c,status:"done",exitCode:null,signal:null,finishedAt:new Date().toISOString()};On(i,h),r.onComplete&&r.onComplete(h)}),{id:o,meta:c}}list(){te();let t=[];try{t=ze.readdirSync(tt)}catch{return[]}let n=[];for(let r of t){if(!r.endsWith(".meta.json"))continue;let o=kr(Hi.join(tt,r));o&&n.push(o)}return n.sort((r,o)=>r.startedAt<o.startedAt?1:-1),n}tailLog(t,n){let r=this.logPath(t);if(!ze.existsSync(r))return"(no log file)";let s=ze.readFileSync(r,"utf8").split(`
|
|
228
|
+
`);return s.slice(Math.max(0,s.length-n)).join(`
|
|
229
|
+
`).trimEnd()||"(empty log)"}readSince(t,n){let r=this.logPath(t);if(!ze.existsSync(r))return{text:"",nextOffset:n};let s=ze.statSync(r).size;if(n>=s)return{text:"",nextOffset:s};let i=Buffer.alloc(s-n),a=ze.openSync(r,"r");try{ze.readSync(a,i,0,i.length,n)}finally{ze.closeSync(a)}return{text:i.toString("utf8"),nextOffset:s}}resolveJobRef(t){return pg(this.list(),t)}kill(t){let n=this.metaPath(t),r=kr(n);if(!r)return`Unknown job id: ${t}`;let o=this.running.get(t);if(o&&!o.killed)return o.kill("SIGTERM"),On(n,{...r,status:"killed",signal:"SIGTERM"}),`Sent SIGTERM to job ${t} (pid ${r.pid??"?"})`;if(r.pid)try{return process.kill(r.pid,"SIGTERM"),On(n,{...r,status:"killed",signal:"SIGTERM"}),`Sent SIGTERM to pid ${r.pid} (${t})`}catch{return`Job ${t} is not running (no live process).`}return`Job ${t} is not running.`}killAllRunning(){for(let[t,n]of this.running){n.killed||n.kill("SIGTERM");let r=kr(this.metaPath(t));r&&On(this.metaPath(t),{...r,status:"killed",signal:"SIGTERM"})}this.running.clear()}};import Kg from"node:fs";import{Bot as zg,InputFile as qg}from"grammy";G();import Nu from"node:fs";import Nn from"node:path";function mg(e){return e.replace(/[^a-zA-Z0-9._-]+/g,"_").slice(0,120)||"unknown"}function fg(e){let r=Nn.basename(e.trim()||"file").replace(/[^a-zA-Z0-9._-]+/g,"_").replace(/^\.+/,"").slice(0,180);return r.length>0?r:"file"}function hg(e,t){let n=new Date().toISOString().slice(0,10),r=mg(t);return Nn.join(e,r,n)}function _n(e,t,n){let r=hg(e,t);j(r);let o=fg(n),s=Nn.join(r,o);if(!Nu.existsSync(s))return s;let i=Nn.extname(o),a=i?o.slice(0,-i.length):o;for(let l=1;l<1e4;l+=1){let d=`${a}-${l}${i}`;if(s=Nn.join(r,d),!Nu.existsSync(s))return s}return Nn.join(r,`${a}-${Date.now()}${i}`)}be();import gg from"node:dns";import yg from"node:https";import{URL as wg}from"node:url";function Fn(e){if(e instanceof Error){let t=e.cause;return t!=null?`${e.message} (${String(t)})`:e.message}return String(e)}be();function _u(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 bg(e,t){let r=t.split("/").filter(o=>o.length>0).map(encodeURIComponent).join("/");return`https://api.telegram.org/file/bot${e}/${r}`}var Fu="omnish (Telegram bot; https://github.com/labKnowledge/whatsLive)";function kg(e){let t=new wg(e);return new Promise((n,r)=>{let o=yg.request({hostname:t.hostname,port:t.port||443,path:t.pathname+t.search,method:"GET",headers:{"User-Agent":Fu,Accept:"*/*"},lookup(s,i,a){gg.lookup(s,{family:4},a)}},s=>{let i=s.statusCode??0,a=[];s.on("data",l=>a.push(l)),s.on("end",()=>n({status:i,buffer:Buffer.concat(a)})),s.on("error",r)});o.on("error",r),o.end()})}async function Wu(e,t,n){let r;try{r=await e.api.getFile(t)}catch(i){return T.warn({err:String(i),fileId:t},"telegram getFile failed"),{error:"Could not resolve file on Telegram."}}if(!r.file_path)return{error:"Telegram did not return a file path."};let o=bg(e.token,r.file_path),s;try{let i=await fetch(o,{headers:{"User-Agent":Fu,Accept:"*/*"}});if(!i.ok)return T.warn({status:i.status,statusText:i.statusText,fileId:t},"telegram file fetch HTTP error"),{error:`Could not download file from Telegram (HTTP ${i.status}${i.statusText?` ${i.statusText}`:""}).`};try{s=Buffer.from(await i.arrayBuffer())}catch(a){return T.warn({err:String(a),fileId:t},"telegram file read body failed"),{error:`Could not read file from Telegram (${String(a)}).`}}}catch(i){T.warn({err:String(i),fileId:t},"telegram file fetch failed; retrying via HTTPS IPv4");try{let a=await kg(o);if(a.status<200||a.status>=300)return T.warn({status:a.status,fileId:t},"telegram HTTPS IPv4 fallback HTTP error"),{error:`Could not download file from Telegram (HTTP ${a.status}).`};s=a.buffer}catch(a){return T.warn({err:String(i),err2:String(a),fileId:t},"telegram file download failed (fetch and IPv4 fallback)"),{error:`Could not download file from Telegram (network: ${Fn(i)}; IPv4 fallback: ${Fn(a)}).`}}}return n>0&&s.byteLength>n?{error:`Media too large (max ${n} bytes).`}:{buffer:s}}pe();qe();import{downloadMediaMessage as $g,extensionForMediaMessage as Rg,getContentType as Bo,isJidGroup as Bu,isLidUser as Cg}from"@whiskeysockets/baileys";import Mg from"node:fs";be();var qt=new Map;function Sg(){let e=Date.now();for(let[t,n]of qt)e-n>6e5&&qt.delete(t);for(;qt.size>500;){let t=qt.keys().next().value;if(t===void 0)break;qt.delete(t)}}function vg(e){!e||typeof e!="string"||(Sg(),qt.set(e,Date.now()))}function Bi(e){vg(e?.key?.id??void 0)}function xg(e){if(!e)return!1;let t=qt.get(e);return t===void 0?!1:Date.now()-t>6e5?(qt.delete(e),!1):!0}function Uu(e){return!xg(e)}function Tg(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 Eg(e){if(!e||typeof e!="object")return"";let t=e;for(let n of["imageMessage","videoMessage","documentMessage","audioMessage","stickerMessage"]){let r=t[n];if(r&&typeof r.caption=="string"&&r.caption.trim())return r.caption}return""}function ju(e){return[Tg(e),Eg(e)].filter(n=>n.trim().length>0).join(`
|
|
230
|
+
`).trim()}function ji(e){return e.replace(/[\u0000-\u0008\u000B\u000C\u000E-\u001F\u007F]/g,"").replace(/\uFEFF/g,"").trim()}function Gu(e){let t=Bo(e??void 0);return t==="imageMessage"||t==="videoMessage"||t==="audioMessage"||t==="documentMessage"||t==="stickerMessage"}function Pg(e){let t=Bo(e??void 0);if(!t)return;let r=e?.[t]?.fileLength;if(typeof r=="number"&&Number.isFinite(r))return r;if(r&&typeof r=="object"&&"toNumber"in r&&typeof r.toNumber=="function")try{let o=r.toNumber();return Number.isFinite(o)?o:void 0}catch{return}}function Du(e){try{if(!e)return"file.bin";let t=Rg(e);return`file${t&&t.length>0?t.startsWith(".")?t:`.${t}`:".bin"}`}catch{return`${(Bo(e??void 0)??"file").replace("Message","")||"file"}.bin`}}function Ag(e){let t=Bo(e??void 0);if(!t)return Du(e);let n=e?.[t];return n?.fileName&&typeof n.fileName=="string"&&n.fileName.trim()?n.fileName.trim():Du(e)}async function Hu(e,t,n,r){let o=t.message??void 0;if(!Gu(o))return{};let s=r.fileReceiveMaxBytes,i=Pg(o);if(s>0&&i!==void 0&&i>s)return{error:`Media too large (max ${s} bytes).`};let a;try{a=await $g(t,"buffer",{},{logger:e.logger,reuploadRequest:e.updateMediaMessage})}catch(c){return T.warn({err:String(c),detail:Fn(c)},"whatsapp downloadMediaMessage failed"),{error:`Could not download media from WhatsApp (${Fn(c)}).`}}if(s>0&&a.length>s)return{error:`Media too large (max ${s} bytes).`};let l;try{l=Ft(r,n)}catch(c){return{error:String(c)}}let d=Ag(o),u=_n(l,n,d);try{Mg.writeFileSync(u,a,{mode:384})}catch{return{error:"Could not write media to inbox."}}return{path:u}}async function Ig(e,t){let n=t.remoteJid??"";if(!n)return{fromJid:"",fromE164:""};if(t.remoteJidAlt){let r=ee(t.remoteJidAlt)??ee(n)??"";return{fromJid:n,fromE164:r}}if(Cg(n))try{let r=await e.signalRepository?.lidMapping?.getPNForLID?.(n);if(r){let o=ee(r)??"";if(o)return{fromJid:n,fromE164:o}}}catch{}return{fromJid:n,fromE164:ee(n)??""}}function Lg(e){try{if(!$().clusterEnabled)return;let n=ju(e.message??void 0);if(!n)return;let r=Uc(n);if(!r)return;let o=e.key?.remoteJid??"",s=null;if(o&&!Bu(o)){let i=ee(o);i&&(s=`wa:${i}`)}qc(r,s)}catch(t){T.warn({err:String(t)},"cluster footer observation failed")}}function Ju(e,t){let n=r=>{r.type==="notify"&&(async()=>{for(let o of r.messages){let s=o.key;if(!s||s.fromMe&&(Lg(o),!Uu(s.id)))continue;let i=s.remoteJid;if(!i||Bu(i)||i.toLowerCase().endsWith("@status")||i==="status@broadcast")continue;let l=ji(ju(o.message??void 0)),{fromJid:d,fromE164:u}=await Ig(e,s),c=`wa:${d}`,m=Gu(o.message??void 0);if(m&&!l){(async()=>{try{let g=$(),y=await Hu(e,o,c,g);await t({fromJid:d,fromE164:u,text:"",messageId:s.id??void 0,mediaSavedPath:y.path,mediaError:y.error})}catch(g){T.error({err:String(g),fromJid:d},"whatsapp media-only background task failed")}})();continue}let f,h;if(m){let g=$(),y=await Hu(e,o,c,g);f=y.path,h=y.error}!l&&!f&&!h||await t({fromJid:d,fromE164:u,text:l,messageId:s.id??void 0,mediaSavedPath:f,mediaError:h})}})()};return e.ev.on("messages.upsert",n),()=>{e.ev.off("messages.upsert",n)}}be();import Hg from"node:fs";import Og from"node:process";import Ng,{DisconnectReason as cn,fetchLatestBaileysVersion as _g,makeCacheableSignalKeyStore as Fg,useMultiFileAuthState as Wg}from"@whiskeysockets/baileys";import Ug from"qrcode-terminal";G();be();var Dg="0.1.0";function Gi(e){return e?.error?.output?.statusCode}async function jo(e={}){te();let t=e.authDir??ne,n=e.verbose===!0,r=rl(),{state:o,saveCreds:s}=await Wg(t),{version:i}=await _g(),a=Ng({version:i,logger:r,printQRInTerminal:!1,browser:["omnish","cli",Dg],auth:{creds:o.creds,keys:Fg(o.keys,r)},syncFullHistory:!1,markOnlineOnConnect:!1});return a.ev.on("creds.update",s),a.ev.on("connection.update",l=>{let{connection:d,lastDisconnect:u,qr:c}=l;if(c&&(e.onQr?.(c),e.printQr)){let m=Og.stdout,f=w(m,"\xB7".repeat(42));console.log(V(m,"Scan with WhatsApp \u2192 Linked devices")),console.log(f),Ug.generate(c,{small:!0}),console.log(f)}if(d==="close"){let m=Gi(u);n&&m===cn.loggedOut&&r.warn("WhatsApp session logged out (401).")}d==="open"&&n&&r.info("WhatsApp Web connected.")}),a.ws&&typeof a.ws.on=="function"&&a.ws.on("error",l=>{r.error({err:String(l)},"WebSocket error")}),a}function Wn(e){try{e.end(new Error("omnish: socket closed"))}catch{e.ws?.close()}}function Ji(e){return e?.output?.statusCode??e?.status??e?.error?.output?.statusCode}function Go(e){return e.user?.id?Promise.resolve():new Promise((t,n)=>{let r=o=>{if(o.connection==="open"){e.ev.off("connection.update",r),t();return}if(o.connection==="close"){e.ev.off("connection.update",r);let i=o.lastDisconnect?.error??o.lastDisconnect??new Error("Connection closed");n(i)}};e.ev.on("connection.update",r)})}function un(e,t,n){return new Promise((r,o)=>{let s=setTimeout(()=>o(new Error(n)),t);e.then(i=>{clearTimeout(s),r(i)},i=>{clearTimeout(s),o(i)})})}var Qu=3500,Bg=400,Ku=18e4,zu=9e4,qu=3e5;function Ki(e,t=Qu){if(e.length<=t)return[e];let n=[],r=0;for(;r<e.length;){let o=Math.min(r+t,e.length);if(o<e.length){let i=e.slice(r,o).lastIndexOf(`
|
|
226
231
|
|
|
227
|
-
`);i>Math.floor(t*.35)&&(o=r+i+2)}n.push(e.slice(r,o)),r=o}return n}function
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
`),await
|
|
231
|
-
${
|
|
232
|
-
`)
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
`);
|
|
236
|
-
${
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
${
|
|
240
|
-
|
|
232
|
+
`);i>Math.floor(t*.35)&&(o=r+i+2)}n.push(e.slice(r,o)),r=o}return n}function Jo(e){return new Promise(t=>setTimeout(t,e))}async function Yu(e,t){await Promise.race([e,Jo(Ku).then(()=>{T.warn({jid:t,ms:Ku},"whatsapp outbound self-heal: prior send chain waited too long; continuing")})])}async function jg(e,t,n,r=3){let o;for(let s=1;s<=r;s+=1)try{let i=await un(e.sendMessage(t,{text:n}),zu,`whatsapp sendMessage timed out after ${zu}ms`);Bi(i);return}catch(i){o=i;let a=String(i);if(/not connected|closed|timed out|timeout/i.test(a)&&s<r){await Jo(800*s);continue}throw i}throw o}function Gg(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 Jg(e,t,n,r=3){let o;for(let s=1;s<=r;s+=1)try{let i=await un(e.sendMessage(t,n),qu,`whatsapp sendMedia timed out after ${qu}ms`);Bi(i);return}catch(i){o=i;let a=String(i);if(/not connected|closed|timed out|timeout/i.test(a)&&s<r){await Jo(800*s);continue}throw i}throw o}function Vu(e,t={}){let n=new Map,r=t.decorate??(o=>o);return{async sendText(o,s){let i=r(s,o),a=n.get(o)??Promise.resolve(),l=Yu(a,o).then(async()=>{let d=Ki(i,Qu);for(let u=0;u<d.length;u+=1)await jg(e,o,d[u]??""),u<d.length-1&&await Jo(Bg)}).catch(d=>{T.error({err:String(d),jid:o},"sendText failed")});n.set(o,l),await l.finally(()=>{n.get(o)===l&&n.delete(o)})},async sendMedia(o,s){let i=Hg.readFileSync(s.absPath),a=Gg(s,i),l=n.get(o)??Promise.resolve(),d=Yu(l,o).then(async()=>{await Jg(e,o,a)}).catch(u=>{T.error({err:String(u),jid:o},"sendMedia failed")});n.set(o,d),await d.finally(()=>{n.get(o)===d&&n.delete(o)})}}}function Xu(e){let t=e.trim();return t?/^\/id(?:@[\w_]+)?$/i.test(t):!1}function Zu(e){let t=String(e).replace(/\D/g,"");return`Your Telegram user id: ${t}
|
|
233
|
+
Add to allowlist on this gateway host:
|
|
234
|
+
omnish allow tg:${t}`}var Yg=400;async function ed(e,t,n,r){let o=t(),s=await Wu(e,r.fileId,o.fileReceiveMaxBytes);if("error"in s)return{mediaError:s.error};let i;try{i=Ft(o,n)}catch(l){return{mediaError:String(l)}}let a=_n(i,n,r.baseName);try{return Kg.writeFileSync(a,s.buffer,{mode:384}),{mediaSavedPath:a}}catch{return{mediaError:"Could not write media to inbox."}}}function Qg(e){return new Promise(t=>setTimeout(t,e))}async function zi(e,t,n,r={}){let o=new zg(e);await o.api.deleteWebhook({drop_pending_updates:!1});let s=new Map,i=r.decorate??(u=>u);async function a(u,c){let m=Math.min(t().appsMaxWaChars,4096),f=ke(c,"telegram"),h=i(f.text,Ci(u)),g=f.parseModeHtml,b=(s.get(u)??Promise.resolve()).then(async()=>{let v=Ki(h,m);for(let E=0;E<v.length;E+=1){let R=v[E]??"",P=g?{parse_mode:"HTML"}:void 0;try{await o.api.sendMessage(u,R,P)}catch(M){if(g){T.warn({err:String(M),chatId:u},"telegram HTML send failed; retrying plain");let L=R.replace(/<[^>]+>/g,"");await o.api.sendMessage(u,L)}else throw M}E<v.length-1&&await Qg(Yg)}}).catch(v=>{T.error({err:String(v),chatId:u},"telegram sendText failed")});s.set(u,b),await b.finally(()=>{s.get(u)===b&&s.delete(u)})}async function l(u,c){let m=new qg(c.absPath,c.displayName),f=c.caption,g=(s.get(u)??Promise.resolve()).then(async()=>{switch(c.category){case"image":await o.api.sendPhoto(u,m,f?{caption:f}:void 0);break;case"video":await o.api.sendVideo(u,m,f?{caption:f}:void 0);break;case"audio":await o.api.sendAudio(u,m,f?{caption:f}:void 0);break;case"document":await o.api.sendDocument(u,m,f?{caption:f}:void 0);break}}).catch(y=>{T.error({err:String(y),chatId:u},"telegram sendMedia failed")});s.set(u,g),await g.finally(()=>{s.get(u)===g&&s.delete(u)})}o.on("message",async u=>{let c=u.chat,m=u.message;if(!c||c.type!=="private"||!u.from||!m)return;let f="text"in m&&m.text?m.text:"",h="caption"in m&&m.caption?m.caption:"",g=ji([f,h].filter(Boolean).join(`
|
|
235
|
+
`));if(g&&Xu(g)){let M=u.from.id;await o.api.sendMessage(c.id,Zu(M));return}let y=_u(m),b=Ci(c.id),v=c.id,E=async M=>{if(M.kind==="file")await l(v,M.spec);else if(M.kind==="files")for(let L of M.specs)await l(v,L);else if(M.kind==="texts")for(let L of M.bodies)await a(v,L);else await a(v,M.body)};if(y&&!g){(async()=>{try{let M=await ed(o,t,b,y);if(!M.mediaSavedPath&&!M.mediaError)return;await n({peerKey:b,text:"",tgChatId:c.id,tgReplyToMessageId:m.message_id,mediaSavedPath:M.mediaSavedPath,mediaError:M.mediaError},E)}catch(M){T.error({err:String(M),chatId:v},"telegram media-only background task failed")}})();return}let R,P;if(y){let M=await ed(o,t,b,y);R=M.mediaSavedPath,P=M.mediaError}!g&&!R&&!P||await n({peerKey:b,text:g,tgChatId:c.id,tgReplyToMessageId:m.message_id,mediaSavedPath:R,mediaError:P},E)}),o.catch(u=>{T.error({err:String(u)},"telegram bot error")});let d=o.start();return{bot:o,sendText:a,sendMedia:l,stop:async()=>{await o.stop(),await d.catch(()=>{})}}}qe();import Vg from"node:fs";import Xg from"node:path";function Zg(e){let t=Xg.join(e,"creds.json");try{let n=Vg.readFileSync(t,"utf8"),r=JSON.parse(n);if(!r.me||typeof r.me!="object"||r.me===null)return null;let o=r.me,s=typeof o.id=="string"?o.id:void 0,i=typeof o.phoneNumber=="string"?o.phoneNumber:void 0;return!s&&!i?null:{id:s,phoneNumber:i}}catch{return null}}function td(e){let t=Zg(e);if(!t)return null;let n=t.phoneNumber?.trim();if(n){let s=ee(n);return s?`phone ${s}`:`phone ${n}`}let r=t.id?.trim();if(!r)return null;let o=r.toLowerCase();if(o.endsWith("@s.whatsapp.net")||o.endsWith("@c.us")){let s=ee(r);return s?`phone ${s}`:`id ${r}`}return o.endsWith("@lid")?`device ${r} (LID \u2014 not an E.164; your number may appear after the gateway runs)`:`id ${r}`}import zo from"node:fs";import et from"node:process";G();G();import nd from"node:fs";var ey="Timed out after 10 minutes. Phone stuck on \u201CLogging in\u201D usually means: run `pnpm approve-builds && pnpm install`, check network/firewall, then try `omnish link --force` again.";function Ko(e){let t=String(e).toLowerCase();return t.includes("401")||t.includes("logged out")?!0:Ji(e)===cn.loggedOut}function ty(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 qi(e){let t=!1;for(let n=0;n<3;n++){if(e.signal?.aborted)throw new Error("Pairing cancelled.");let r=await jo({authDir:e.authDir,printQr:e.printQr===!0,verbose:e.verbose===!0,onQr:e.onQr});e.onSocketReady?.(r);try{let o=un(Go(r),6e5,ey);e.signal?await Promise.race([o,ty(e.signal)]):await o;return}catch(o){if(o instanceof Error&&o.message==="Pairing cancelled.")throw o;if(Ji(o)===cn.restartRequired&&!t){t=!0,e.onRestart515?.(),await new Promise(i=>setTimeout(i,1500));continue}throw o}finally{e.onSocketClosed?.(),Wn(r)}}throw new Error("Pairing failed after restart (515) retries.")}async function rd(e){let t=e.authDir??ne;te();for(let n=1;n<=2;n++)try{await qi({...e,authDir:t});return}catch(r){if(r instanceof Error&&r.message==="Pairing cancelled.")throw r;if(n===1&&Ko(r)){nd.rmSync(t,{recursive:!0,force:!0}),nd.mkdirSync(t,{recursive:!0,mode:448});continue}throw r}}async function ny(e,t){let n=et.stdout;console.log(`
|
|
236
|
+
${Ce(n,"omnish link")} ${w(n,"\u2014 QR appears below; scan from WhatsApp \u2192 Linked devices.")}
|
|
237
|
+
`),await qi({authDir:e,verbose:t,printQr:!0,onRestart515:()=>{console.warn(`
|
|
238
|
+
${B(et.stderr,"WhatsApp requested a restart after pairing (code 515). This is normal. Opening a new connection\u2026")}
|
|
239
|
+
`)}})}async function od(e={}){let t=e.authDir??ne,n=e.verbose===!0;te(),e.force&&(zo.rmSync(t,{recursive:!0,force:!0}),zo.mkdirSync(t,{recursive:!0,mode:448}),console.log(`${me(et.stdout,"Cleared saved session (--force).")} ${w(et.stdout,"Requesting a new QR\u2026")}
|
|
240
|
+
`));for(let r=1;r<=2;r++)try{await ny(t,n),console.log(`
|
|
241
|
+
${ce(et.stdout,"Linked.")} ${S(et.stdout,"Session saved. You can run")} ${ce(et.stdout,"omnish run")} ${S(et.stdout,"now.")}
|
|
242
|
+
`);return}catch(o){if(r===1&&Ko(o)){console.warn(`
|
|
243
|
+
${B(et.stderr,"WhatsApp returned logged-out (401). This often happens after Ctrl+C during link or corrupt auth files.")}
|
|
244
|
+
${B(et.stderr,"Clearing auth dir and retrying once with a fresh QR\u2026")}
|
|
245
|
+
`),zo.rmSync(t,{recursive:!0,force:!0}),zo.mkdirSync(t,{recursive:!0,mode:448});continue}throw Ko(o)&&console.error(`
|
|
246
|
+
${C(et.stderr,"Still failing after a clean auth directory. Try:")}
|
|
247
|
+
${w(et.stderr,` pnpm approve-builds && pnpm install
|
|
241
248
|
(pnpm may have skipped Baileys/sharp/protobuf build scripts.)
|
|
242
249
|
Then: omnish link --force
|
|
243
|
-
`)}`),o}}import{spawn as
|
|
244
|
-
|
|
245
|
-
`)
|
|
246
|
-
|
|
247
|
-
|
|
250
|
+
`)}`),o}}G();import{spawn as ry,spawnSync as oy}from"node:child_process";import Un from"node:fs";import sy from"node:path";import Dn from"node:process";function qo(e){te(),j(sy.dirname(e));let t=Dn.argv[1];if(!t)return{ok:!1,message:"Cannot resolve entry script; invoke via node path/to/dist/index.js."};let n=Un.openSync(e,"a"),r=ry(Dn.execPath,[t,"run"],{detached:!0,stdio:["ignore",n,n],env:{...Dn.env,OMNISH_BACKGROUND_GATEWAY:"1"}});return Un.closeSync(n),r.unref(),r.pid?{ok:!0,pid:r.pid}:{ok:!1,message:"Failed to start background gateway."}}function Yo(){if(te(),!Un.existsSync(le))return{outcome:"no_pidfile"};let e=Un.readFileSync(le,"utf8").trim(),t=Number(e);if(!Number.isFinite(t)||t<=0){try{Un.unlinkSync(le)}catch{}return{outcome:"invalid_pidfile"}}try{Dn.kill(t,0)}catch{try{Un.unlinkSync(le)}catch{}return{outcome:"stale_cleaned",pid:t}}try{return Dn.kill(t,"SIGTERM"),{outcome:"sent_signal",pid:t}}catch(n){return Dn.platform==="win32"&&oy("taskkill",["/PID",String(t),"/T"],{windowsHide:!0}).status===0?{outcome:"taskkill_ok",pid:t}:{outcome:"failed",message:`could not signal process: ${String(n)}`}}}import ud from"node:crypto";import Qi from"node:fs";import iy from"node:net";qe();be();G();import ld from"node:fs";var sd=Number(process.env.PLATFORM_INBOUND_MEDIA_MAX_BYTES)>0?Number(process.env.PLATFORM_INBOUND_MEDIA_MAX_BYTES):54525952,id=50;function ad(e){try{return JSON.parse(e)}catch{return null}}function Vo(e){return e.fileSendMaxBytes<=0?8388608:Math.min(e.fileSendMaxBytes,8388608)}function cd(e){if(ld.statSync(e.absPath).size>8388608)throw new Error(`File too large for attached mode (max ${8388608} bytes).`);let n=ld.readFileSync(e.absPath).toString("base64");return{name:e.displayName,mimetype:e.mimetype,category:e.category,dataBase64:n,...e.caption?{caption:e.caption}:{}}}function Yi(e,t,n,r){return t.kind==="texts"?{body:t.bodies.map(s=>ke(s,r).text).filter(s=>s.trim()).join(`
|
|
251
|
+
|
|
252
|
+
`),...n?{messageId:n}:{}}:t.kind==="text"?{body:ke(t.body,r).text,...n?{messageId:n}:{}}:t.kind==="file"?{...n?{messageId:n}:{},files:[cd(t.spec)]}:{...n?{messageId:n}:{},files:t.specs.map(cd)}}var Sr=null;function ay(){try{let e=Qi.readFileSync(hn,"utf8"),t=JSON.parse(e);return typeof t.token=="string"?t.token:""}catch{return""}}function ly(e){Qi.writeFileSync(hn,JSON.stringify(e,null,2)+`
|
|
253
|
+
`,{mode:384})}function cy(){try{Qi.unlinkSync(hn)}catch{}}async function uy(e,t){let n=t.getCfg(),r=typeof e.absPath=="string"?e.absPath.trim():"";if(!r)return{ok:!1,error:"Missing absPath."};let o=typeof e.caption=="string"&&e.caption.length>0?e.caption:void 0,s=t.sendPlatformMedia&&!t.getWaOutbound()&&!t.getTgSendMedia()?Vo(n):n.fileSendMaxBytes,i=Kt(r,s);if("error"in i)return{ok:!1,error:i.error};let a={absPath:i.absPath,category:i.category,mimetype:i.mimetype,displayName:i.displayName,caption:o};if(e.channel==="whatsapp"){let l=t.getWaOutbound(),d=typeof e.e164=="string"?e.e164.trim():"";if(!d.startsWith("+"))return{ok:!1,error:"WhatsApp destination must be E.164 (e.g. +15551234567)."};let u=It(d);if(!l){let m=t.sendPlatformMedia;if(!m)return{ok:!1,error:"WhatsApp outbound is not connected."};try{return await m(`wa:${u}`,a),{ok:!0}}catch(f){return{ok:!1,error:String(f)}}}let c=n.gatewayMode;if(c!=="whatsapp"&&c!=="both")return{ok:!1,error:"gatewayMode does not include WhatsApp."};try{return await l.sendMedia(u,a),{ok:!0}}catch(m){return{ok:!1,error:String(m)}}}if(e.channel==="telegram"){let l=t.getTgSendMedia();if(!l){let u=t.sendPlatformMedia;if(!u)return{ok:!1,error:"Telegram outbound is not connected."};if(!Number.isFinite(e.chatId))return{ok:!1,error:"Invalid Telegram chat id."};let c=`tg:${e.chatId}`;try{return await u(c,a),{ok:!0}}catch(m){return{ok:!1,error:String(m)}}}let d=n.gatewayMode;if(d!=="telegram"&&d!=="both")return{ok:!1,error:"gatewayMode does not include Telegram."};if(!Number.isFinite(e.chatId))return{ok:!1,error:"Invalid Telegram chat id."};try{return await l(e.chatId,a),{ok:!0}}catch(u){return{ok:!1,error:String(u)}}}return{ok:!1,error:"Unknown channel."}}async function dy(e,t){let n=t.getCfg(),r=typeof e.text=="string"?e.text.trim():"";if(!r)return{ok:!1,error:"Missing or empty text."};if(e.channel==="whatsapp"){let o=t.getWaOutbound();if(!o){let l=t.sendPlatformText;if(!l)return{ok:!1,error:"WhatsApp outbound is not connected."};let d=typeof e.e164=="string"?e.e164.trim():"";if(!d.startsWith("+"))return{ok:!1,error:"WhatsApp destination must be E.164 (e.g. +15551234567)."};let u=It(d);try{return await l(`wa:${u}`,r),{ok:!0}}catch(c){return{ok:!1,error:String(c)}}}let s=n.gatewayMode;if(s!=="whatsapp"&&s!=="both")return{ok:!1,error:"gatewayMode does not include WhatsApp."};let i=typeof e.e164=="string"?e.e164.trim():"";if(!i.startsWith("+"))return{ok:!1,error:"WhatsApp destination must be E.164 (e.g. +15551234567)."};let a=It(i);try{return await o.sendText(a,r),{ok:!0}}catch(l){return{ok:!1,error:String(l)}}}if(e.channel==="telegram"){let o=t.getTgSendText();if(!o){let i=t.sendPlatformText;if(!i)return{ok:!1,error:"Telegram outbound is not connected."};if(!Number.isFinite(e.chatId))return{ok:!1,error:"Invalid Telegram chat id."};try{return await i(`tg:${e.chatId}`,r),{ok:!0}}catch(a){return{ok:!1,error:String(a)}}}let s=n.gatewayMode;if(s!=="telegram"&&s!=="both")return{ok:!1,error:"gatewayMode does not include Telegram."};if(!Number.isFinite(e.chatId))return{ok:!1,error:"Invalid Telegram chat id."};try{return await o(e.chatId,p(r)),{ok:!0}}catch(i){return{ok:!1,error:String(i)}}}return{ok:!1,error:"Unknown channel."}}async function py(e,t,n){let r;try{r=JSON.parse(e)}catch{return{ok:!1,error:"Invalid JSON."}}if(!r||typeof r!="object")return{ok:!1,error:"Invalid request."};if(typeof r.token!="string"||!n)return{ok:!1,error:"Unauthorized."};try{let o=Buffer.from(r.token,"utf8"),s=Buffer.from(n,"utf8");if(o.length!==s.length||!ud.timingSafeEqual(o,s))return{ok:!1,error:"Unauthorized."}}catch{return{ok:!1,error:"Unauthorized."}}return r.op==="sendMedia"?uy(r,t):r.op==="sendText"?dy(r,t):{ok:!1,error:"Unsupported operation."}}function Xo(e){if(Sr)return;let t=ud.randomBytes(32).toString("hex"),n=iy.createServer(r=>{let o="";r.setTimeout(12e4),r.on("data",s=>{o+=s.toString("utf8");let i=o.indexOf(`
|
|
254
|
+
`);if(i===-1)return;let a=o.slice(0,i).trim();o=o.slice(i+1);let l=ay();py(a,e,l).then(d=>{r.write(`${JSON.stringify(d)}
|
|
255
|
+
`),r.end()})}),r.on("error",()=>{})});n.listen(0,"127.0.0.1",()=>{let r=n.address();if(!r||typeof r=="string"){T.error("gateway control: could not read listen address");return}let o={token:t,host:r.address,port:r.port};ly(o),T.info({port:r.port},"gateway control listening")}),n.on("error",r=>{T.error({err:String(r)},"gateway control server error")}),Sr=n}function Hn(){if(Sr){try{Sr.close()}catch{}Sr=null,cy()}}be();import my from"node:crypto";import fy from"node:http";var hy=256*1024;function gy(e,t){if(!e||!t)return!1;try{let n=Buffer.from(e,"utf8"),r=Buffer.from(t,"utf8");return n.length===r.length&&my.timingSafeEqual(n,r)}catch{return!1}}function yy(e){let t=typeof e.source=="string"?e.source:"webhook";if(e.action==="completed"&&e.workflow_run&&typeof e.workflow_run=="object"){let a=e.workflow_run,l=a.name??"?",d=a.conclusion??"?",u=a.head_branch??"?",c=a.html_url??"",m=e.repository?.full_name??"?";return`[${t}] ${m} \u2014 ${l}
|
|
256
|
+
result: ${d}
|
|
257
|
+
branch: ${u}${c?`
|
|
258
|
+
${c}`:""}`}if(e.object_kind==="pipeline"&&e.object_attributes&&typeof e.object_attributes=="object"){let a=e.object_attributes,l=a.status??"?",d=a.ref??"?",u=a.id??"?",c=e.project?.path_with_namespace??"?";return`[${t}] ${c} \u2014 pipeline #${u}
|
|
259
|
+
status: ${l}
|
|
260
|
+
ref: ${d}`}let n=typeof e.text=="string"?e.text:null,r=typeof e.message=="string"?e.message:null,o=typeof e.title=="string"?e.title:null,s=typeof e.status=="string"?e.status:null;if(n)return`[${t}] ${n}`;let i=[`[${t}]`];if(o&&i.push(o),r&&i.push(r),s&&i.push(`status: ${s}`),i.length===1){let a=JSON.stringify(e).slice(0,500);i.push(a)}return i.join(`
|
|
261
|
+
`)}var Vi=null;function Zo(e,t){if(Vi)return{stop:()=>{}};let n=fy.createServer((r,o)=>{if(r.method!=="POST"){o.writeHead(405,{"Content-Type":"application/json"}),o.end(JSON.stringify({ok:!1,error:"Method not allowed"}));return}let s=r.headers.authorization,i=s?.startsWith("Bearer ")?s.slice(7):void 0,a=new URL(r.url??"/",`http://${r.headers.host??"localhost"}`).searchParams.get("token")??void 0;if(!gy(i??a,e.token)){o.writeHead(401,{"Content-Type":"application/json"}),o.end(JSON.stringify({ok:!1,error:"Unauthorized"}));return}let d="",u=0;r.on("data",c=>{if(u+=c.length,u>hy){o.writeHead(413,{"Content-Type":"application/json"}),o.end(JSON.stringify({ok:!1,error:"Payload too large"})),r.destroy();return}d+=c.toString("utf8")}),r.on("end",()=>{let c;try{c=JSON.parse(d)}catch{o.writeHead(400,{"Content-Type":"application/json"}),o.end(JSON.stringify({ok:!1,error:"Invalid JSON"}));return}let f=new URL(r.url??"/",`http://${r.headers.host??"localhost"}`).searchParams.get("source")??r.headers["x-webhook-source"]??void 0;f&&(c.source=f);let h=typeof c.peerKey=="string"&&c.peerKey||t.getDefaultPeerKey();if(!h){o.writeHead(400,{"Content-Type":"application/json"}),o.end(JSON.stringify({ok:!1,error:"No target peer. Set peerKey in body or configure an allowlisted identity."}));return}let g=yy(c);t.sendToPeer(h,g).then(()=>{o.writeHead(200,{"Content-Type":"application/json"}),o.end(JSON.stringify({ok:!0}))},y=>{T.warn({err:String(y)},"webhook: sendToPeer failed"),o.writeHead(502,{"Content-Type":"application/json"}),o.end(JSON.stringify({ok:!1,error:"Failed to deliver message"}))})})});return n.listen(e.port,e.host,()=>{let r=n.address(),o=typeof r=="object"&&r?r.port:e.port;T.info({port:o,host:e.host},"webhook receiver listening")}),n.on("error",r=>{T.error({err:String(r)},"webhook receiver error")}),Vi=n,{stop:()=>{try{n.close()}catch{}Vi=null}}}pe();import qw from"node:crypto";import Ea from"node:fs";be();import wy from"node:fs";function dd(e,t,n){let r=e.fileReceiveMaxBytes,o;try{o=Buffer.from(n.dataBase64,"base64")}catch{return{mediaError:"Invalid inbound media payload."}}if(r>0&&o.length>r)return{mediaError:`Media too large (max ${r} bytes).`};let s;try{s=Ft(e,t)}catch(a){return{mediaError:String(a)}}let i=_n(s,t,n.name);try{return wy.writeFileSync(i,o,{mode:384}),{mediaSavedPath:i}}catch{return{mediaError:"Could not write media to inbox."}}}G();rs();be();import na from"ws";import xy from"ws";var vy=["/platform/device","/control/device"];function os(e){let t=e.replace(/\/$/,"");return vy.map(n=>{let r=new URL(n,t);return r.protocol=r.protocol==="https:"?"wss:":"ws:",r.toString()})}var $y=Math.ceil(sd*1.4)+512*1024;function Ry(e){let t=String(e instanceof Error?e.message:e);return/Unexpected server response:\s*400/.test(t)||/Unexpected server response:\s*404/.test(t)}async function gd(e,t){let n=os(e),r=null;for(let s of n)try{return{ws:await new Promise((a,l)=>{let d=new xy(s,{headers:{Authorization:`Bearer ${t}`},maxPayload:$y});d.once("open",()=>a(d)),d.once("error",l)}),pathname:new URL(s).pathname}}catch(i){if(r=i instanceof Error?i:new Error(String(i)),Ry(i))continue;throw r}let o="Could not open a device WebSocket. Redeploy the relay (for /control/device fallback) and ensure /platform/* or /control/* route to port 8788.";throw r&&/Unexpected server response:\s*400/.test(r.message)?new Error(`${r.message} \u2014 ${o}`):new Error(r?`${r.message} \u2014 ${o}`:o)}var Cy=1e3,My=6e4,ss=class{constructor(t){this.options=t}ws=null;stopped=!1;pingTimer=null;registerResolve=null;registerReject=null;registeredAccount=null;registeredDeviceId=null;reconnectAttempt=0;reconnectTimer=null;outboundQueue=[];getRegisteredAccount(){return this.registeredAccount}async connect(){return this.connectOnce()}async connectOnce(){let{env:t}=this.options,n=new Promise((a,l)=>{this.registerResolve=a,this.registerReject=l}),{ws:r,pathname:o}=await gd(t.platformUrl,t.token);this.ws=r,o!=="/platform/device"&&T.info({pathname:o},"platform device websocket connected via fallback path"),r.on("message",a=>{this.handleMessage(a.toString())}),r.on("close",()=>{this.stopped||(T.warn("platform device websocket closed; scheduling reconnect"),this.scheduleReconnect())}),r.on("error",a=>{T.warn({err:String(a)},"platform device websocket error")});let s=setTimeout(()=>{this.registerReject?.(new Error("Platform device register timeout (15s)"))},15e3);this.sendRaw({type:"register",...t.deviceId?{deviceId:t.deviceId}:{},label:process.env.OMNISH_DEVICE_LABEL?.trim()||"default"});let i=await n;return clearTimeout(s),this.registeredDeviceId=i.deviceId,this.reconnectAttempt=0,this.pingTimer&&clearInterval(this.pingTimer),this.pingTimer=setInterval(()=>{this.sendRaw({type:"ping"})},3e4),this.flushOutboundQueue(),i}scheduleReconnect(){if(this.stopped||this.reconnectTimer)return;this.pingTimer&&(clearInterval(this.pingTimer),this.pingTimer=null),this.ws=null;let t=Math.min(Cy*2**this.reconnectAttempt,My);this.reconnectAttempt+=1,this.reconnectTimer=setTimeout(()=>{this.reconnectTimer=null,!this.stopped&&this.connectOnce().catch(n=>{T.warn({err:String(n)},"platform device reconnect failed"),this.scheduleReconnect()})},t),this.reconnectTimer.unref?.()}async handleMessage(t){let n=ad(t);if(n){if(n.type==="registered"&&"deviceId"in n){n.account&&(this.registeredAccount=n.account),this.registerResolve?.({deviceId:n.deviceId,...n.account?{account:n.account}:{}}),this.registerResolve=null,this.registerReject=null;return}if(n.type==="message"){await this.options.onMessage(n);return}if(n.type==="reply_error"){await this.options.onReplyError?.(n.peerKey,n.error,n.messageId);return}n.type==="error"&&(T.warn({message:n.message},"platform error"),this.registerReject?.(new Error(n.message)),this.registerResolve=null,this.registerReject=null)}}sendReply(t,n,r,o){this.enqueue({type:"reply",peerKey:t,...n?{body:n}:{},...r?{messageId:r}:{},...o?.length?{files:o}:{}})}sendRoutedReply(t,n){this.enqueue({type:"reply",peerKey:t,...n})}enqueue(t){if(this.ws?.readyState===na.OPEN){this.sendRaw(t);return}if(this.outboundQueue.length>=id){T.warn("platform outbound queue full; dropping reply");return}this.outboundQueue.push(t)}flushOutboundQueue(){for(;this.outboundQueue.length>0&&this.ws?.readyState===na.OPEN;){let t=this.outboundQueue.shift();t&&this.sendRaw(t)}}sendRaw(t){this.ws?.readyState===na.OPEN&&this.ws.send(JSON.stringify(t))}stop(){this.stopped=!0,this.reconnectTimer&&(clearTimeout(this.reconnectTimer),this.reconnectTimer=null),this.pingTimer&&(clearInterval(this.pingTimer),this.pingTimer=null);try{this.ws?.close()}catch{}this.ws=null,this.outboundQueue.length=0}};import ia from"node:readline/promises";import{stdin as aa,stdout as ps}from"node:process";pe();var Ty=/^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$/i;function wd(e){let t=e.trim().toLowerCase();return t?t.length>63?"Tunnel name must be at most 63 characters.":Ty.test(t)?null:"Tunnel name may only use letters, digits, and hyphens.":"Tunnel name must not be empty."}function bd(e){let t=Number.parseInt(e,10);return!Number.isInteger(t)||t<1||t>65535?null:t}function yd(e,t){let n="127.0.0.1",r,o,s=!1,i=[];for(let d=0;d<t.length;d++){let u=t[d];if(u==="--host"){let c=t[++d];if(!c)return{kind:"error",message:"--host requires an address."};n=c;continue}if(u.startsWith("--host=")){n=u.slice(7);continue}if(u==="--relay"){let c=t[++d];if(!c)return{kind:"error",message:"--relay requires a URL."};r=c;continue}if(u.startsWith("--relay=")){r=u.slice(8);continue}if(u==="--name"){let c=t[++d];if(!c)return{kind:"error",message:"--name requires a slug."};o=c;continue}if(u.startsWith("--name=")){o=u.slice(7);continue}if(u==="--background"||u==="-b"){s=!0;continue}i.push(u)}let a=i[0];if(!a)return{kind:"error",message:`Usage: omnish tunnel ${e} <port> [--host <addr>] [--name <slug>] [--relay <url>] [--background]`};let l=bd(a);if(l===null)return{kind:"error",message:"Port must be an integer between 1 and 65535."};if(o){let d=wd(o);if(d)return{kind:"error",message:d}}return{kind:"expose",options:{kind:e,port:l,host:n,relayUrl:r??"",name:o,background:s}}}function ra(e){let[t,...n]=e,r=(t??"").trim().toLowerCase();if(!r||r==="help"||r==="-h"||r==="--help")return{kind:"help"};if(r==="signup"){let o,s,i,a;for(let l=0;l<n.length;l++){let d=n[l];if(d==="--email"){o=n[++l];continue}if(d.startsWith("--email=")){o=d.slice(8);continue}if(d==="--phone"){s=n[++l];continue}if(d.startsWith("--phone=")){s=d.slice(8);continue}if(d==="--password"){i=n[++l];continue}if(d.startsWith("--password=")){i=d.slice(11);continue}if(d==="--relay"){a=n[++l];continue}if(d.startsWith("--relay=")){a=d.slice(8);continue}}return{kind:"signup",email:o,phone:s,password:i,relayUrl:a}}if(r==="login"){let o,s,i,a,l;for(let d=0;d<n.length;d++){let u=n[d];if(u==="--token"){o=n[++d];continue}if(u.startsWith("--token=")){o=u.slice(8);continue}if(u==="--email"){s=n[++d];continue}if(u.startsWith("--email=")){s=u.slice(8);continue}if(u==="--phone"){i=n[++d];continue}if(u.startsWith("--phone=")){i=u.slice(8);continue}if(u==="--password"){a=n[++d];continue}if(u.startsWith("--password=")){a=u.slice(11);continue}if(u==="--relay"){l=n[++d];continue}if(u.startsWith("--relay=")){l=u.slice(8);continue}o||(o=u)}return{kind:"login",token:o,email:s,phone:i,password:a,relayUrl:l}}if(r==="logout")return{kind:"logout"};if(r==="list")return{kind:"list"};if(r==="status"){let o;for(let s=0;s<n.length;s++){let i=n[s];if(i==="--relay"){o=n[s+1],s++;continue}i.startsWith("--relay=")&&(o=i.slice(8))}return{kind:"status",relayUrl:o}}if(r==="stop"){let o=n[0]?.trim();return o?{kind:"stop",target:o}:{kind:"error",message:"Usage: omnish tunnel stop <id|slug>"}}return r==="http"?yd("http",n):r==="tcp"?yd("tcp",n):{kind:"error",message:`Unknown tunnel subcommand "${t}". Try: omnish tunnel help`}}function Ey(e){let t=[],n="",r=null;for(let o=0;o<e.length;o++){let s=e[o];if(r){s===r?r=null:n+=s;continue}if(s==='"'||s==="'"){r=s;continue}if(/\s/.test(s)){n.length&&(t.push(n),n="");continue}n+=s}return n.length&&t.push(n),t}function kd(e){let t=e.trim();if(!t||t==="help")return{kind:"help"};let n=Ey(t),r=n[0]?.toLowerCase();if(r==="login"||r==="logout"||r==="status"||r==="signup")return ra(n);if(t.toLowerCase()==="list"||t.toLowerCase()==="ls")return{kind:"list"};let o=t.match(/^stop\s+(\S+)\s*$/i);if(o)return{kind:"stop",target:o[1]};let s=t.match(/^(http|tcp)\s+(\d+)(?:\s+--name\s+(\S+))?(?:\s+--host\s+(\S+))?\s*$/i);if(s){let i=s[1].toLowerCase(),a=bd(s[2]);if(a===null)return{kind:"error",message:"Port must be an integer between 1 and 65535."};let l=s[3],d=s[4]??"127.0.0.1";if(l){let u=wd(l);if(u)return{kind:"error",message:u}}return{kind:"expose",options:{kind:i,port:a,host:d,relayUrl:"",name:l,background:!0}}}return{kind:"error",message:"Usage: /tunnel login \u2026 | /tunnel status | /tunnel http <port> | /tunnel tcp <port> | /tunnels | /tunnel stop <id>"}}import Py from"ws";import{URL as Sd}from"node:url";function jn(e){let t=new Sd(e);return t.protocol=t.protocol==="https:"?"wss:":"ws:",(!t.pathname||t.pathname==="/")&&(t.pathname="/control"),t.toString()}function vd(e){return new Sd("/health",e).toString()}async function is(e,t,n=1e4){let r=t.trim();if(!r)return{ok:!1,healthOk:!1,controlOk:!1,error:"Tunnel token is missing."};let o=vd(e),s=!1,i;try{let d=await fetch(o,{method:"GET",signal:AbortSignal.timeout(n)});if(!d.ok)return{ok:!1,healthOk:!1,controlOk:!1,error:`Health HTTP ${d.status} (${o})`};let u=await d.json();if(!u?.ok)return{ok:!1,healthOk:!1,controlOk:!1,error:"Health JSON missing ok:true"};s=!0,typeof u.version=="string"&&(i=u.version)}catch(d){return{ok:!1,healthOk:!1,controlOk:!1,error:`Health fetch failed: ${String(d)}`}}let a=jn(e),l=!1;try{await new Promise((d,u)=>{let c=new Py(a,{headers:{Authorization:`Bearer ${r}`}}),m=setTimeout(()=>{c.terminate(),u(new Error("WSS auth timeout"))},n);c.once("message",f=>{clearTimeout(m);try{let h=JSON.parse(f.toString());if(h.type!=="auth_ok"){u(new Error(`Expected auth_ok, got ${h.type??"?"}`));return}c.close(),d()}catch(h){u(h)}}),c.once("error",f=>{clearTimeout(m),u(f)})}),l=!0}catch(d){return{ok:!1,healthOk:!0,healthVersion:i,controlOk:!1,error:`Control WebSocket: ${String(d)}`}}return{ok:!0,healthOk:s,healthVersion:i,controlOk:!0}}import Rd from"node:crypto";import Ay from"node:http";import Iy from"node:net";import Yt from"ws";function as(e,t,n=Buffer.alloc(0)){let r=Buffer.allocUnsafe(9+n.length);return r.writeUInt8(e,0),r.writeUInt32BE(t,1),r.writeUInt32BE(n.length,5),n.copy(r,9),r}function xd(e){if(e.length<9)return null;let t=e.readUInt8(0),n=e.readUInt32BE(1),r=e.readUInt32BE(5);return e.length<9+r?null:{frameType:t,streamId:n,payload:e.subarray(9,9+r)}}function oa(e){try{let t=JSON.parse(e);return!t||typeof t!="object"||typeof t.type!="string"?null:t}catch{return null}}function Gn(e){return JSON.stringify(e)}function Ly(e){let t=Rd.createHash("sha1").update(e,"utf8").digest("hex").slice(0,8);return Number.parseInt(t,16)>>>0}var ls=class{constructor(t){this.opts=t;let n=Rd.randomBytes(4).toString("hex");this.record={id:n,kind:t.expose.kind,slug:t.expose.name?.trim().toLowerCase()??"",localHost:t.expose.host,localPort:t.expose.port,publicUrl:"",status:"connecting",startedAt:new Date().toISOString()}}ws=null;record;tcpStreams=new Map;tcpStreamIds=new Map;stopped=!1;pingTimer=null;getRecord(){return{...this.record}}setStatus(t,n){this.record.status=t,this.record.error=n,this.opts.onStatus?.(this.getRecord())}async start(){if(this.stopped)throw new Error("Tunnel client already stopped.");let t=jn(this.opts.relayUrl),n=new Yt(t,{headers:{Authorization:`Bearer ${this.opts.token}`}});this.ws=n,await new Promise((o,s)=>{let i=d=>{l(),s(d)},a=()=>{l(),o()},l=()=>{n.off("error",i),n.off("open",a)};n.once("error",i),n.once("open",a)}),n.on("message",(o,s)=>{if(s){this.handleBinary(Buffer.isBuffer(o)?o:Buffer.from(o));return}let i=typeof o=="string"?o:o.toString("utf8");this.handleControl(i)}),n.on("close",()=>{this.stopped||this.setStatus("error","Relay connection closed."),this.cleanupTcpStreams()}),n.on("error",o=>{this.stopped||this.setStatus("error",String(o))}),this.pingTimer=setInterval(()=>{n.readyState===Yt.OPEN&&n.send(Gn({type:"ping"}))},3e4),n.send(Gn({type:"register",id:this.record.id,kind:this.opts.expose.kind,localHost:this.opts.expose.host,localPort:this.opts.expose.port,...this.opts.expose.name?{name:this.opts.expose.name}:{}}));let r=await this.waitForRegistered();return this.record.slug=r.slug,this.record.publicUrl=r.publicUrl,this.setStatus("active"),this.getRecord()}waitForRegistered(){let t=this.ws;return t?new Promise((n,r)=>{let o=s=>{if(typeof s!="string"&&!Buffer.isBuffer(s))return;let i=typeof s=="string"?s:s.toString("utf8"),a=oa(i);if(a){if(a.type==="registered"&&a.id===this.record.id){t.off("message",o),n(a);return}a.type==="error"&&(t.off("message",o),r(new Error(a.message)))}};t.on("message",o)}):Promise.reject(new Error("WebSocket not connected."))}async handleControl(t){let n=oa(t);if(n&&!(n.type==="pong"||n.type==="auth_ok")){if(n.type==="error"){this.setStatus("error",n.message);return}if(n.type==="http"){await this.handleHttpRequest(n);return}if(n.type==="tcp_open"){await this.handleTcpOpen(n.streamId);return}n.type==="tcp_close"&&this.closeTcpStream(n.streamId)}}handleBinary(t){let n=xd(t);if(!n)return;let r=[...this.tcpStreamIds.entries()].find(([,s])=>s===n.streamId)?.[0];if(!r)return;let o=this.tcpStreams.get(r);if(o){if(n.frameType===2){o.write(n.payload);return}n.frameType===3&&(o.end(),this.tcpStreams.delete(r),this.tcpStreamIds.delete(r))}}async handleHttpRequest(t){let n=this.ws;if(!n||n.readyState!==Yt.OPEN)return;let r=t.bodyBase64?Buffer.from(t.bodyBase64,"base64"):void 0,o={host:this.opts.expose.host,port:this.opts.expose.port,method:t.method,path:t.path,headers:{...t.headers}};await new Promise(s=>{let i=Ay.request(o,a=>{let l=[];a.on("data",d=>l.push(Buffer.isBuffer(d)?d:Buffer.from(d))),a.on("end",()=>{let d=Buffer.concat(l);n.send(Gn({type:"http_res",requestId:t.requestId,status:a.statusCode??502,headers:a.headers,...d.length>0?{bodyBase64:d.toString("base64")}:{}})),s()})});i.on("error",a=>{n.send(Gn({type:"http_res",requestId:t.requestId,status:502,headers:{"content-type":"text/plain"},bodyBase64:Buffer.from(String(a)).toString("base64")})),s()}),r&&r.length>0&&i.write(r),i.end()})}async handleTcpOpen(t){let n=this.ws;if(!n||n.readyState!==Yt.OPEN)return;let r=Ly(t);this.tcpStreamIds.set(t,r);let o=Iy.connect({host:this.opts.expose.host,port:this.opts.expose.port});this.tcpStreams.set(t,o),o.on("data",s=>{n.readyState===Yt.OPEN&&n.send(as(2,r,Buffer.isBuffer(s)?s:Buffer.from(s)))}),o.on("close",()=>{n.readyState===Yt.OPEN&&n.send(as(3,r)),this.tcpStreams.delete(t),this.tcpStreamIds.delete(t)}),o.on("error",()=>{n.readyState===Yt.OPEN&&n.send(as(3,r)),this.tcpStreams.delete(t),this.tcpStreamIds.delete(t)})}closeTcpStream(t){let n=this.tcpStreams.get(t);n&&n.destroy(),this.tcpStreams.delete(t),this.tcpStreamIds.delete(t)}cleanupTcpStreams(){for(let t of this.tcpStreams.values())t.destroy();this.tcpStreams.clear(),this.tcpStreamIds.clear()}async stop(){if(this.stopped)return;this.stopped=!0,this.pingTimer&&(clearInterval(this.pingTimer),this.pingTimer=null);let t=this.ws;t&&t.readyState===Yt.OPEN&&(t.send(Gn({type:"unregister",id:this.record.id})),t.close()),this.cleanupTcpStreams(),this.setStatus("stopped")}};var cs=class{clients=new Map;list(){return[...new Set(this.clients.values())].map(t=>t.getRecord())}getActiveCount(){return[...new Set(this.clients.values())].filter(t=>t.getRecord().status==="active").length}async expose(t,n){let r=ht();if(!r)throw new Error("No tunnel token configured. Run `omnish tunnel login` or set OMNISH_TUNNEL_TOKEN.");let o=t.tunnelMaxActive>0?t.tunnelMaxActive:5;if(this.getActiveCount()>=o)throw new Error(`Active tunnel limit reached (${o}). Stop one with \`omnish tunnel stop <id>\`.`);let s=n.relayUrl||rt(t.tunnelRelayUrl||$e),i=new ls({relayUrl:s,token:r,expose:n,onStatus:l=>{(l.status==="stopped"||l.status==="error")&&this.clients.delete(l.id)}}),a=await i.start();return this.clients.set(a.id,i),a.slug&&this.clients.set(a.slug,i),a}async stop(t){let n=t.trim().toLowerCase(),r=this.clients.get(n)??this.clients.get(t.trim());if(!r)return null;let o=r.getRecord();return await r.stop(),this.clients.delete(o.id),o.slug&&this.clients.delete(o.slug),r.getRecord()}async stopAll(){let t=[...new Set(this.clients.values())];await Promise.all(t.map(n=>n.stop())),this.clients.clear()}};async function Cd(e,t){let n=await fetch(e,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(t),signal:AbortSignal.timeout(15e3)}),r=await n.json();return!n.ok||!r.token?{ok:!1,error:r.error??`HTTP ${n.status}`}:{ok:!0,token:r.token}}function us(e,t){let n=new URL("/auth/signup",e).toString();return Cd(n,{...t.email?{email:t.email}:{},...t.phone?{phone:t.phone}:{},password:t.password})}function ds(e,t){let n=new URL("/auth/login",e).toString();return Cd(n,{...t.email?{email:t.email}:{},...t.phone?{phone:t.phone}:{},password:t.password})}var Jn=new cs;function dn(){return Jn}function Oy(e){let t=[J(e,"omnish tunnel"),w(e,"Expose local HTTP or TCP ports through the omnish relay."),"",J(e,"Usage:"),` ${S(e,"omnish tunnel signup [--email <email>] [--phone <phone>] [--password <pass>] [--relay <url>]")}`,` ${S(e,"omnish tunnel login [--token <token>] [--relay <url>]")}`,` ${S(e,"omnish tunnel login --email <email> --password <pass> [--relay <url>]")}`,` ${S(e,"omnish tunnel logout")}`,` ${S(e,"omnish tunnel status [--relay <url>]")}`,` ${S(e,"omnish tunnel http <port> [--host <addr>] [--name <slug>] [--relay <url>] [--background]")}`,` ${S(e,"omnish tunnel tcp <port> [--host <addr>] [--name <slug>] [--relay <url>] [--background]")}`,` ${S(e,"omnish tunnel list")}`,` ${S(e,"omnish tunnel stop <id|slug>")}`,"",w(e,"Secrets live in ~/.omnish/tunnel-auth.json or OMNISH_TUNNEL_TOKEN."),w(e,`Default relay: ${$e}`),""];console.log(t.join(`
|
|
262
|
+
`))}async function Ny(){let e=ia.createInterface({input:aa,output:ps});try{return(await e.question("Tunnel token: ")).trim()}finally{e.close()}}async function Md(e){let t=ra(e),n=ps,r=process.stderr;if(t.kind==="help"){Oy(n);return}if(t.kind==="error"){console.error(C(r,t.message)),process.exitCode=1;return}let o=$();if(t.kind==="signup"){let s=t.relayUrl||rt(o.tunnelRelayUrl||$e),i=ia.createInterface({input:aa,output:ps});try{let a=t.email?.trim()||(await i.question("Email (or leave empty for phone): ")).trim(),l=t.phone?.trim()||"";if(a||(l=l||(await i.question("Phone: ")).trim()),!a&&!l){console.error(C(r,"Email or phone is required.")),process.exitCode=1;return}let d=t.password||(await i.question("Password (min 8 chars): ")).trim();if(d.length<8){console.error(C(r,"Password must be at least 8 characters.")),process.exitCode=1;return}let u=await us(s,{...a?{email:a}:{},...l?{phone:l}:{},password:d});if(!u.ok){console.error(C(r,u.error)),process.exitCode=1;return}ft({token:u.token,...t.relayUrl?{relayUrl:t.relayUrl}:{}}),console.log(B(n,"Account created. Token saved."))}finally{i.close()}return}if(t.kind==="login"){if(t.email||t.phone){let a=t.relayUrl||rt(o.tunnelRelayUrl||$e),l=ia.createInterface({input:aa,output:ps});try{let d=t.password||(await l.question("Password: ")).trim(),u=await ds(a,{...t.email?{email:t.email}:{},...t.phone?{phone:t.phone}:{},password:d});if(!u.ok){console.error(C(r,u.error)),process.exitCode=1;return}ft({token:u.token,...t.relayUrl?{relayUrl:t.relayUrl}:{}}),console.log(B(n,"Logged in. Token saved."))}finally{l.close()}return}let i=t.token?.trim()||await Ny();if(!i){console.error(C(r,"Tunnel token is required.")),process.exitCode=1;return}ft({token:i,...t.relayUrl?{relayUrl:t.relayUrl}:{}}),console.log(B(n,"Tunnel token saved."));return}if(t.kind==="logout"){lo(),console.log(B(n,"Tunnel token removed."));return}if(t.kind==="status"){let s=t.relayUrl||rt(o.tunnelRelayUrl||$e),i=ht(),a=!!process.env.OMNISH_TUNNEL_TOKEN?.trim(),l=await is(s,i);console.log(`${w(n,"relay:")} ${S(n,s)}`),console.log(`${w(n,"token:")} ${S(n,i?`configured${a?" (OMNISH_TUNNEL_TOKEN)":""}`:C(r,"missing"))}`),console.log(`${w(n,"health:")} ${l.healthOk?S(n,`ok${l.healthVersion?` (${l.healthVersion})`:""}`):C(r,"fail")}`),console.log(`${w(n,"control:")} ${l.controlOk?S(n,"auth ok"):C(r,"fail")}${l.error&&!l.ok?` \u2014 ${l.error}`:""}`),console.log(`${w(n,"active:")} ${S(n,String(Jn.getActiveCount()))}`);return}if(t.kind==="list"){let s=Jn.list();if(s.length===0){console.log(B(n,"(no active tunnels)"));return}for(let i of s)console.log(`${S(n,i.id)} ${i.kind} ${i.status} ${i.publicUrl||"(pending)"}
|
|
263
|
+
${w(n,`${i.localHost}:${i.localPort}`)}`);return}if(t.kind==="stop"){let s=await Jn.stop(t.target);if(!s){console.error(C(r,`No active tunnel matched "${t.target}".`)),process.exitCode=1;return}console.log(B(n,`Stopped tunnel ${s.id}.`));return}if(t.kind==="expose"){let s=t.options.relayUrl||rt(o.tunnelRelayUrl||$e),i=await Jn.expose(o,{...t.options,relayUrl:s});console.log(B(n,`${i.kind.toUpperCase()} tunnel active`)),console.log(`${w(n,"public:")} ${S(n,i.publicUrl)}`),console.log(`${w(n,"local:")} ${S(n,`${i.localHost}:${i.localPort}`)}`),console.log(`${w(n,"id:")} ${S(n,i.id)}`),t.options.background||(console.log(w(n,"Press Ctrl+C to stop.")),await new Promise(a=>{let l=async()=>{await Jn.stop(i.id),a()};process.once("SIGINT",l),process.once("SIGTERM",l)}))}}function Td(e){let t=e.trim().match(/^(\d+)\.(\d+)\.(\d+)/);return t?[Number(t[1]),Number(t[2]),Number(t[3])]:null}function Ed(e,t){let n=Td(e),r=Td(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 _y="https://registry.npmjs.org",Pd=null,la=0;function vr(){return Pd}function Fy(e){let t=e.trim();return t.length<1||t.length>214?!1:!/\s/.test(t)}function Ad(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 Wy(e){let t=e.trim(),n=`${_y}/${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 Uy(e){try{let t=await fetch(e,{headers:{Accept:"application/json"},signal:AbortSignal.timeout(15e3)});if(!t.ok)return{error:`updateInfoUrl: HTTP ${t.status}`};let n=await t.arrayBuffer(),r=n.byteLength>65536?n.slice(0,65536):n,o=new TextDecoder("utf-8",{fatal:!1}).decode(r),s;try{s=JSON.parse(o)}catch{return{error:"updateInfoUrl: body is not JSON"}}if(!s||typeof s!="object"||Array.isArray(s))return{error:"updateInfoUrl: JSON must be an object"};let i=s,a=null;typeof i.message=="string"&&i.message.trim()&&(a=i.message.trim().slice(0,1500));let l=null,d=i.link??i.url;return typeof d=="string"&&d.trim()&&(l=Ad(d)),{message:a,link:l}}catch(t){return{error:`updateInfoUrl: ${String(t)}`}}}function Dy(e){return!Number.isFinite(e)||e<36e5?36e5:e>6048e5?6048e5:Math.floor(e)}async function xr(e,t){let n=Fy(t.updateCheckPackageName)?t.updateCheckPackageName.trim():"omnish",r=await Wy(n),o="version"in r?r.version:null,s="error"in r?r.error:null,i=!1;o&&!s&&(i=Ed(e,o)<0);let a=null,l=null,d=null,u=Ad(t.updateInfoUrl);if(u){let m=await Uy(u);"error"in m?d=m.error:(a=m.message,l=m.link)}let c={runningVersion:e,checkedAtIso:new Date().toISOString(),registryPackage:n,registryLatest:o,registryError:s,updateAvailable:i,infoMessage:a,infoLink:l,infoError:d};return Pd=c,c}function ms(e){if(!e)return;let t=[];if(e.registryError?t.push(`npm check: ${e.registryError}`):e.registryLatest&&t.push(e.updateAvailable?`npm latest *${e.registryLatest}* (running ${e.runningVersion})`:`npm latest ${e.registryLatest} (up to date)`),e.infoError?t.push(`info URL: ${e.infoError}`):e.infoMessage&&t.push(`notice: ${e.infoMessage}`),t.length!==0)return t.join(" \xB7 ")}function fs(e){let t=!1,n=async()=>{if(t)return;let s=e.getConfig();if(!s.updateCheckEnabled)return;let i=Dy(s.updateCheckIntervalMs),a=Date.now();if(!(la!==0&&a-la<i)){la=a;try{let l=await xr(e.getRunningVersion(),s);l.updateAvailable?e.log.info({updateAvailable:!0,running:l.runningVersion,npmLatest:l.registryLatest,pkg:l.registryPackage},"omnish update check: newer npm version available"):e.log.info({running:l.runningVersion,npmLatest:l.registryLatest,pkg:l.registryPackage},"omnish update check ok"),l.infoMessage&&e.log.info({len:l.infoMessage.length},"omnish update info message present")}catch(l){e.log.warn({err:String(l)},"omnish update check failed")}}},r=setInterval(()=>void n(),6e4),o=setTimeout(()=>void n(),3e4);return()=>{t=!0,clearInterval(r),clearTimeout(o)}}pe();pe();function Qt(e){return(e??"").trim()}function Hy(){return Qt(process.env.OMNISH_PLATFORM_URL)||Qt(process.env.OMNISH_COMM_LAYER_URL)||Qt(process.env.OMNISH_TUNNEL_RELAY)}function By(){return Qt(process.env.OMNISH_TOKEN)||Qt(process.env.OMNISH_DEVICE_TOKEN)||Qt(process.env.OMNISH_TUNNEL_TOKEN)}function ca(){return ht()}function Kn(){return By()?"env":$().platformToken.trim()?"config":xt()?.token?"file":"default"}function $r(){let e=$();return ei(e.tunnelRelayUrl||$e)}function zn(){return Hy()?"env":$().tunnelRelayUrl.trim()?"config":xt()?.relayUrl?.trim()?"file":"default"}function ua(){let e=Qt(process.env.OMNISH_DEVICE_ID);if(e)return e;let t=$().platformDeviceId.trim();if(t)return t}function da(){return Qt(process.env.OMNISH_DEVICE_ID)?"env":$().platformDeviceId.trim()?"config":"default"}function fe(){let e=$r(),t=ca();if(!e||!t)return null;let n=ua();return{platformUrl:e.replace(/\/$/,""),token:t,deviceId:n}}import Id from"node:path";import{glob as jy,stat as Gy}from"node:fs/promises";function hs(e){let t=e.trim();if(!t||t.startsWith("-- ")||t==="--")return null;let n=t.indexOf(" -- "),r,o;return n!==-1?(r=t.slice(0,n).trim(),o=t.slice(n+4).trim()||void 0):r=t,(r.startsWith('"')&&r.endsWith('"')||r.startsWith("'")&&r.endsWith("'"))&&(r=r.slice(1,-1)),r.trim()?{selectorPart:r,caption:o}:null}function Jy(e){return/[*?[]/.test(e)}function Ky(e){return e.split(",").map(t=>t.trim()).filter(t=>t.length>0)}async function Rr(e,t){let n=Ky(t),r=new Set,o=[];for(let s of n){if(Jy(s)){for await(let a of jy(s,{cwd:e,withFileTypes:!1})){let l=Id.resolve(e,a);r.has(l)||(r.add(l),o.push(l))}continue}let i=Id.resolve(e,s);r.has(i)||(r.add(i),o.push(i))}return o}async function Cr(e){for(let t of e)try{if(!(await Gy(t)).isFile())return{ok:!1,error:`Not a file: ${t}`}}catch{return{ok:!1,error:`File not found: ${t}`}}return{ok:!0}}G();import Nd from"node:fs";import zy from"node:path";var pn="__omnish_shortcuts_global__",Ld=500,_d=/^[a-zA-Z0-9][a-zA-Z0-9_-]{0,31}$/,qy=new Set(["help","apps","bg","jobs","log","tail","kill","send","file","files","receive","reload","restart","updates","gateway","gw","mode","allow","deny","allowlist","whatsapp","wa","telegram","tg","cowork","cw","shortcut","shortcuts","alias","aliases"]),Mr=new Map,Od=!1;function Yy(){try{let e=Nd.readFileSync(Wr,"utf8"),t=JSON.parse(e);if(t&&typeof t=="object")for(let[n,r]of Object.entries(t)){if(!r||typeof r!="object")continue;let o={};for(let[s,i]of Object.entries(r)){let a=String(s).toLowerCase();typeof i=="string"&&i.length>0&&(o[a]=i.trim())}Mr.set(n,o)}}catch{}}function gs(){j(zy.dirname(Wr));let e={};for(let[t,n]of Mr)Object.entries(n).length>0&&(e[t]={...n});Nd.writeFileSync(Wr,JSON.stringify(e,null,2)+`
|
|
264
|
+
`,{mode:384})}function ys(){Od||(Yy(),Od=!0)}function Qy(e){return qy.has(e.trim().toLowerCase())}function wt(e){let t=e.trim();if(!t)return{ok:!1,error:"Name is empty."};let n=t.toLowerCase();return _d.test(n)?Qy(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 pa(e){let t=e.replace(/\r\n/g,`
|
|
265
|
+
`).replace(/\n/g," ").trim();return t?t.length>Ld?{ok:!1,error:`Body too long (max ${Ld} characters).`}:{ok:!0,body:t}:{ok:!1,error:"Body is empty."}}function yt(e,t){ys();let n=Mr.get(e);return!n&&t&&(n={},Mr.set(e,n)),n??{}}function Fd(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 ma(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 fa(e){let t=ma(e);return{scope:t.scope,remainder:t.remainder}}function Wd(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 Vy(e){let t=e.trim().toLowerCase();if(t==="--global"||t==="-g")return"global";if(t==="--chat"||t==="-p")return"chat"}function Ud(e){let t=e.trim().match(/^(\S+)\s+(--global|-g|--chat|-p)\s*$/i);if(!t?.[1]||!t[2])return;let n=Vy(t[2]);if(n)return{name:t[1],target:n}}function Xy(e,t){ys();let n=e,r=yt(n,!1),o=yt(pn,!1);if(t==="chat")return Object.entries(r).map(([a,l])=>({name:a,body:l,scope:"chat"})).sort((a,l)=>a.name.localeCompare(l.name));if(t==="global")return Object.entries(o).map(([a,l])=>({name:a,body:l,scope:"global"})).sort((a,l)=>a.name.localeCompare(l.name));let s=new Set([...Object.keys(r),...Object.keys(o)]),i=[];for(let a of[...s].sort()){if(a in r){let d=r[a];d!==void 0&&i.push({name:a,body:d,scope:"chat"});continue}let l=o[a];l!==void 0&&i.push({name:a,body:l,scope:"global"})}return i}function Dd(e,t="merged"){return Xy(e,t)}function ha(e,t){let n=t.trim().toLowerCase(),r=yt(e,!1)[n];return r!==void 0?r:yt(pn,!1)[n]}function ws(e,t){let n=wt(t);if(!n.ok)return;let r=n.normalized,o=yt(e,!1)[r];if(o!==void 0)return{name:r,body:o,scope:"chat"};let s=yt(pn,!1)[r];if(s!==void 0)return{name:r,body:s,scope:"global"}}function Et(e,t,n){let r=n.trim().toLowerCase(),o=e==="global"?pn:t;return yt(o,!1)[r]}function Tr(e,t,n,r="chat"){let o=wt(t);if(!o.ok)throw new Error(o.error);let s=pa(n);if(!s.ok)throw new Error(s.error);let i=r==="global"?pn:e,a=yt(i,!0);a[o.normalized]=s.body,gs()}function Hd(e,t,n="chat"){let r=t.trim().toLowerCase(),o=n==="global"?pn:e;ys();let s=Mr.get(o);return!s||!(r in s)?!1:(delete s[r],gs(),!0)}function ga(e,t,n){let r=wt(t);if(!r.ok)return{ok:!1,error:r.error};let o=r.normalized,s=e;ys();let i=yt(s,!0),a=yt(pn,!0),l=i[o],d=a[o];if(n==="global"){if(l!==void 0){let u=pa(l);return u.ok?(a[o]=u.body,delete i[o],gs(),{ok:!0,kind:"moved",target:"global",name:o}):{ok:!1,error:u.error}}return d!==void 0?{ok:!0,kind:"noop",message:`Shortcut "${o}" is already shared on this gateway.`}:{ok:!1,error:`No shortcut "${o}" in this chat to share. Add with /shortcut add ${o} \u2026 or make the shared copy private with /shortcut set -p ${o}.`}}if(d!==void 0){let u=pa(d);return u.ok?(i[o]=u.body,delete a[o],gs(),{ok:!0,kind:"moved",target:"chat",name:o}):{ok:!1,error:u.error}}return l!==void 0?{ok:!0,kind:"noop",message:`Shortcut "${o}" is already private to this chat.`}:{ok:!1,error:`No shared shortcut "${o}" to make private. Add with /shortcut add --global ${o} \u2026 or share from this chat with /shortcut set -g ${o}.`}}function Bd(e){let t=e.trim();return t.length>0&&!/\s/.test(t)&&_d.test(t.toLowerCase())}import jd from"node:fs";var Gd=64,Zy=1024*1024;function Jd(e){return e.fileReceiveMaxBytes>0?e.fileReceiveMaxBytes:Zy}function Kd(e){let t;try{t=JSON.parse(e)}catch(o){return{ok:!1,error:`Invalid JSON: ${String(o)}`}}let n;if(Array.isArray(t))n=t;else if(t&&typeof t=="object"&&!Array.isArray(t)){let o=t,s=Object.keys(o);if(s.length!==1||s[0]!=="tasks")return{ok:!1,error:'JSON must be an array or a single-key object: { "tasks": [ \u2026 ] }.'};let i=o.tasks;if(!Array.isArray(i))return{ok:!1,error:'"tasks" must be an array.'};n=i}else return{ok:!1,error:'JSON must be an array or { "tasks": [ \u2026 ] }.'};if(n.length===0)return{ok:!1,error:"Queue JSON must contain at least one job."};if(n.length>Gd)return{ok:!1,error:`Too many jobs (max ${Gd}).`};let r=[];for(let o=0;o<n.length;o++){let s=n[o];if(!s||typeof s!="object"||Array.isArray(s))return{ok:!1,error:`Job ${o+1}: must be an object with "recipe" and "task".`};let i=s;if(Object.keys(i).length!==2||typeof i.recipe!="string"||typeof i.task!="string")return{ok:!1,error:`Job ${o+1}: must contain only "recipe" and "task" string fields.`};r.push({recipe:i.recipe,task:i.task})}return{ok:!0,jobs:r}}function zd(e,t,n){let r=[];for(let o=0;o<n.length;o++){let{recipe:s,task:i}=n[o],a=je(e,t,s);if(!a)return{ok:!1,error:`Job ${o+1}: unknown recipe "${s}".`};let l=a.taskEnv??"OMNISH_TASK";if(!Pn(a.command,l))return{ok:!1,error:`Job ${o+1}: recipe "${s}" command must reference "$${l}".`};let d=vo(i,t.recipesMaxTaskChars);if(!d.ok)return{ok:!1,error:`Job ${o+1}: ${d.error}`};let u=a.promptTemplate?xo(a.promptTemplate,l,d.task):d.task,c={[l]:u};r.push({command:a.command,extraEnv:c,recipeLabel:s})}return{ok:!0,items:r}}function ya(e,t){let n;try{n=jd.statSync(e)}catch{return{ok:!1,error:`Cannot read file: ${e}`}}if(!n.isFile())return{ok:!1,error:`Not a file: ${e}`};if(n.size>t)return{ok:!1,error:`File too large (max ${t} bytes for queue load).`};try{return{ok:!0,text:jd.readFileSync(e,"utf8")}}catch(r){return{ok:!1,error:String(r)}}}G();import qd from"node:fs";import qn from"node:process";var ew=120;function Pt(e){return e.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">")}function Yd(e){let t=e.trim();return t==="/service"||t.startsWith("/service ")?t.slice(8).trim():null}async function Qd(e,t){let n=t.trim().split(/\s+/),r=(n[0]??"").toLowerCase();if(!t.trim()||r==="help")return Z(ec(e));if(r==="status"){let s=nn(),i=(()=>{try{return qd.existsSync(le)?`gateway.pid: ${qd.readFileSync(le,"utf8").trim()}`:"gateway.pid: (missing)"}catch(m){return`gateway.pid: (read error: ${String(m)})`}})(),a=qn.env.OMNISH_BACKGROUND_GATEWAY==="1"?"This process: background gateway (OMNISH_BACKGROUND_GATEWAY=1).":"This process: foreground gateway session.",l=typeof qn.env.OMNISH_HOME=="string"&&qn.env.OMNISH_HOME.trim()?`OMNISH_HOME env: ${qn.env.OMNISH_HOME.trim()}`:"OMNISH_HOME env: (not set \u2014 using default data dir)",d=s.error?s.error:`Node: ${s.nodePath}
|
|
266
|
+
Script: ${s.scriptPath}`,u=["*Service status*","",`platform: ${qn.platform}`,a,l,`data dir: ${H}`,i,`default log: ${Ne}`,"",d,"",e.serviceInstallFromChat?"Install from chat: enabled (/service install).":"Install from chat: off \u2014 `/config set serviceInstallFromChat true` to allow /service install."].join(`
|
|
267
|
+
`),c=["<b>Service status</b>","",`<code>${Pt(qn.platform)}</code>`,`<br/><code>${Pt(a)}</code>`,`<br/><code>${Pt(l)}</code>`,`<br/>data dir: <code>${Pt(H)}</code>`,`<br/><code>${Pt(i)}</code>`,`<br/>default log: <code>${Pt(Ne)}</code>`,"",`<pre>${Pt(d)}</pre>`,"",e.serviceInstallFromChat?"Install from chat: enabled.":"Install from chat: off \u2014 <code>/config set serviceInstallFromChat true</code>."].join(`
|
|
268
|
+
`);return ue(u,c)}if(r==="instructions"){let s=nn();if(s.error)return p(s.error);let i=fo(s);return p(`*Install hints*
|
|
269
|
+
|
|
270
|
+
${i}`)}if(r==="logs"){let s=n.length>=2?Number.parseInt(n[1],10):80,i=Number.isFinite(s)&&s>0?Math.min(s,ew):80,a=wo(Ne,i),l=[`*Gateway log* (last ${i} lines)
|
|
271
|
+
${Ne}
|
|
272
|
+
`,"```",a,"```"].join(`
|
|
273
|
+
`),d=`<b>Gateway log</b> (last ${i} lines)<br/><code>${Pt(Ne)}</code><pre>${Pt(a)}</pre>`;return ue(l,d)}if(r==="install"){if(!e.serviceInstallFromChat)return p("Install from chat is disabled. Same trust as shell \u2014 enable with:\n`/config set serviceInstallFromChat true`\nThen `/service install` again.");let s=ho();return p(s.ok?`*Installed*
|
|
274
|
+
${s.detail}`:`*Install failed*
|
|
275
|
+
${s.detail}`)}if(r==="uninstall"){if(!e.serviceInstallFromChat)return p("Uninstall from chat is disabled. Enable with `/config set serviceInstallFromChat true` or remove files on the host manually.");let s=go();return p(`*Uninstall*
|
|
276
|
+
${s.detail}`)}return p("Unknown /service command. Try /service help")}pe();function bs(){let e=fe();return e?.platformUrl?e.platformUrl.replace(/\/$/,""):($().tunnelRelayUrl||$e).trim().replace(/\/$/,"")}function ks(){let e=fe();return e?.token?{Authorization:`Bearer ${e.token}`}:{}}async function Er(e){try{return await e.json()}catch{return{error:`HTTP ${e.status}`}}}function tw(){return`Set platform URL: omnish config add tunnelRelayUrl ${$e} \u2014 publish: omnish platform login`}function wa(){return fe()?.token?{ok:!0}:{ok:!1,error:`Publish requires a platform account token. ${tw()}`}}async function Vd(e){let t=new URLSearchParams;e?.kind&&t.set("kind",e.kind),e?.limit&&t.set("limit",String(e.limit));let n=t.toString(),r=`${bs()}/v1/catalog/trending${n?`?${n}`:""}`,o=await fetch(r,{headers:ks()}),s=await Er(o);return o.ok?s:{error:s.error??`HTTP ${o.status}`}}async function Xd(e,t){let n=new URLSearchParams;n.set("q",e),t?.kind&&n.set("kind",t.kind),t?.category&&n.set("category",t.category),t?.limit&&n.set("limit",String(t.limit));let r=`${bs()}/v1/catalog/search?${n}`,o=await fetch(r,{headers:ks()}),s=await Er(o);return o.ok?s:{error:s.error??`HTTP ${o.status}`}}async function Zd(e){let t=encodeURIComponent(e.trim()),n=`${bs()}/v1/catalog/${t}`,r=await fetch(n,{headers:ks()}),o=await Er(r);return r.ok?o:{error:o.error??`HTTP ${r.status}`}}async function ep(e){let t=encodeURIComponent(e.trim()),n=`${bs()}/v1/catalog/${t}/download`,r=await fetch(n,{method:"POST",headers:ks()}),o=await Er(r);return r.ok?o:{error:o.error??`HTTP ${r.status}`}}async function tp(e){let t=wa();if(!t.ok)return{error:t.error};let n=fe(),r=`${n.platformUrl.replace(/\/$/,"")}/v1/catalog`,o=await fetch(r,{method:"POST",headers:{"content-type":"application/json",Authorization:`Bearer ${n.token}`},body:JSON.stringify(e)}),s=await Er(o);return o.ok?s:{error:s.error??`HTTP ${o.status}`}}function np(e){return e==="global"?"global":"chat"}function rp(e,t,n,r="global"){switch(e.kind){case"recipe":{let o=e.payload,s=Be({...o,dangerous:void 0}),i=on(s);if(!i.ok)return{ok:!1,error:i.error};let a=gt(e.name);if(!a.ok)return{ok:!1,error:a.error};try{An(t,a.normalized,s,np(r))}catch(d){return{ok:!1,error:String(d.message??d)}}let l=r==="global"?`/run ${a.normalized} <task>`:`/run ${a.normalized} <task> (this chat)`;return{ok:!0,message:`Installed recipe "${a.normalized}" (${r}). ${l}`}}case"app":{let o=e.payload;if(!o.command?.trim())return{ok:!1,error:"App payload has no command."};let s=Be({command:o.command.trim(),label:o.label??e.title,description:o.description??e.description,category:e.category||"app"}),i=gt(e.name);if(!i.ok)return{ok:!1,error:i.error};try{An(t,i.normalized,s,np(r),{skipCommandValidation:!0})}catch(a){return{ok:!1,error:String(a.message??a)}}return{ok:!0,message:`Installed app template "${i.normalized}" (${r}). /apps start <session> ${o.command.trim()}`}}case"shortcut":{let o=e.payload,s=wt(e.name);if(!s.ok)return{ok:!1,error:s.error};try{Tr(t,s.normalized,o.body,r==="global"?"global":"chat")}catch(i){return{ok:!1,error:String(i.message??i)}}return{ok:!0,message:`Installed shortcut "${s.normalized}" (${r}). Type ${s.normalized} to expand.`}}case"cowork":{let o=e.payload,s=xe();if(Ye(s,e.name,t))return{ok:!1,error:`Cowork task "${e.name}" already exists for this chat. Remove it first or rename.`};let i=Date.now(),a={id:sr(),name:e.name,ownerPeerKey:t,command:o.command,cwd:o.cwd??"",outputDir:o.outputDir??"",schedule:o.schedule,enabled:o.enabled??!0,notify:o.notify??"self",notifyWhen:o.notifyWhen??"always",attachLog:o.attachLog??!1,attachFiles:o.attachFiles??[],lastCompletedSlotMs:null,createdAtMs:i};return s.push(a),Le(s),{ok:!0,message:`Installed cowork task "${e.name}" for this chat. /cowork show ${e.name}`}}default:return{ok:!1,error:"Unknown catalog kind."}}}function op(e){return{name:e.name,command:e.command,cwd:e.cwd,outputDir:e.outputDir,schedule:e.schedule,enabled:e.enabled,notify:e.notify,notifyWhen:e.notifyWhen,attachLog:e.attachLog,attachFiles:e.attachFiles}}function sp(e){let{dangerous:t,...n}=e;return Be(n)}var ba=new Map;function ka(e,t){return`${e}:${t}`}function Sa(e,t,n){ba.set(ka(e,t),n)}function ip(e,t){return ba.get(ka(e,t))}function Ss(e,t,n){let r=Number.parseInt(n,10);if(!Number.isFinite(r)||r<1)return null;let o=ba.get(ka(e,t));return!o||r>o.length?null:o[r-1].publicId}function mn(e){return!!(e&&typeof e=="object"&&typeof e.error=="string")}function vs(e,t){return`${e.commandPrefix} online ${t}`}function ap(e){let t=o=>vs(e,o),n=e.defaultKind===void 0?" [recipe|app|cowork|shortcut]":` (${e.defaultKind} only)`,r=[`Online catalog \u2014 ${e.defaultKind??"all kinds"}`,"",t(`trending${e.defaultKind===void 0?" [kind]":""}`),t(`search <query>${n}`),t("show <publicId>"),t("<publicId> download [--chat|-p for this chat only]"),t("trending <n> download \u2014 install #n from last list"),t("list \u2014 repeat last trending/search list")];return e.commandPrefix==="/run"&&r.push("","Also: /apps online \u2026 \xB7 /cowork online \u2026 \xB7 /shortcut online \u2026","","Publish (platform account):","/run <recipe> publish [--title \u2026] [--category \u2026]","/apps <session> publish","/cowork <name> publish","/shortcut <name> publish"),p(r.join(`
|
|
277
|
+
`))}function xs(e,t,n){if(e.length===0)return p(`${t}
|
|
278
|
+
(no results)`);let r=[t,""];return e.forEach((o,s)=>{let i=o.category?` \xB7 ${o.category}`:"";r.push(`${s+1}. ${o.publicId} (${o.kind}${i}) \u2193${o.downloadCount} \u2014 ${o.title||o.name}`),o.description&&r.push(` ${o.description.slice(0,120)}`)}),r.push("",`Install: ${vs(n,"<publicId> download")} \u2014 or ${vs(n,"trending <n> download")}`),p(r.join(`
|
|
279
|
+
`))}function lp(e,t){let n=[`${e.publicId} (${e.kind})`,`title: ${e.title||e.name}`,e.description?`description: ${e.description}`:"",e.category?`category: ${e.category}`:"",e.tags?.length?`tags: ${e.tags.join(", ")}`:"",`downloads: ${e.downloadCount}`,`author: ${e.authorLabel||"(unknown)"}`,"","payload:"].filter(Boolean),r=e.payload;if(e.kind==="recipe"||e.kind==="app"){let o=r;if(n.push(` command: ${o.command??""}`),o.promptTemplate&&n.push(` template: ${o.promptTemplate.slice(0,400)}${o.promptTemplate.length>400?"\u2026":""}`),Array.isArray(o.steps)&&o.steps.length>0){n.push(` steps: ${o.steps.length}`);for(let[s,i]of o.steps.entries()){let a=typeof i=="string"?i:i.cmd;n.push(` ${s+1}. ${a??""}`)}}}else if(e.kind==="shortcut")n.push(` body: ${r.body}`);else if(e.kind==="cowork"){let o=r;n.push(` command: ${o.command??""}`),n.push(` schedule: ${o.schedule?.kind??"?"}`)}return n.push("",vs(t,`${e.publicId} download`)),p(n.join(`
|
|
280
|
+
`))}var va=new Set(["recipe","app","cowork","shortcut"]),nw={commandPrefix:"/run",listScope:"run"},cp={commandPrefix:"/apps",defaultKind:"app",listScope:"apps"},up={commandPrefix:"/cowork",defaultKind:"cowork",listScope:"cowork"},dp={commandPrefix:"/shortcut",defaultKind:"shortcut",listScope:"shortcut"};function rw(e){if(!e)return;let t=e.toLowerCase();return va.has(t)?t:void 0}function At(e,t){return`${e.commandPrefix} online ${t}`}function ow(e,t){if(e.defaultKind)return t&&t.toLowerCase()!==e.defaultKind?{error:`This command only lists ${e.defaultKind} entries. Omit the kind suffix.`}:e.defaultKind;let n=rw(t);return t&&!n?{error:`Unknown kind "${t}". Use: recipe, app, cowork, shortcut`}:n}function sw(e,t){if(e.defaultKind){if(t.length>1&&va.has(t[t.length-1].toLowerCase())){if(t[t.length-1].toLowerCase()!==e.defaultKind)return{error:`This command only searches ${e.defaultKind} entries. Omit the kind suffix.`};t.pop()}return{kind:e.defaultKind,query:t.join(" ")}}let n;return t.length>1&&va.has(t[t.length-1].toLowerCase())&&(n=t.pop().toLowerCase()),{kind:n,query:t.join(" ")}}async function Yn(e,t,n,r){let o=e.trim();if(!o||/^help$/i.test(o))return ap(r);let s=o.match(/^(.+?)\s+download(?:\s+(.*))?$/i);if(s){let d=s[1].trim(),u=(s[2]??"").trim(),c=/--chat|-p/i.test(u)?"chat":"global",m=null,f=/^trending\s+(\d+)$/i.exec(d),h=/^search\s+(\S+)\s+(\d+)$/i.exec(d);if(f){if(m=Ss(t,r.listScope,f[1]),!m)return p(`No trending list in this chat \u2014 run ${At(r,"trending")} first, or use ${At(r,"<publicId> download")}.`)}else if(h){if(m=Ss(t,r.listScope,h[2]),!m)return p(`No search list in this chat \u2014 run ${At(r,"search <query>")} first, or use ${At(r,"<publicId> download")}.`)}else if(/^\d+$/.test(d)){if(m=Ss(t,r.listScope,d),!m)return p(`No list item #${d}. Run trending or search first, or use ${At(r,"<publicId> download")}.`)}else m=d;let g=await ep(m);if(mn(g))return p(`Download failed: ${g.error}`);if(r.defaultKind&&g.kind!==r.defaultKind)return p(`Entry "${m}" is kind=${g.kind}, not ${r.defaultKind}. Use /run online show ${m} from /run.`);let y=rp(g,t,n,c);return y.ok?p(y.message):p(`Download failed: ${y.error}`)}let i=/^show\s+(\S+)\s*$/i.exec(o);if(i){let d=await Zd(i[1]);return mn(d)?p(`Not found: ${d.error}`):r.defaultKind&&d.kind!==r.defaultKind?p(`Entry "${i[1]}" is kind=${d.kind}, not ${r.defaultKind}. Use /run online show ${i[1]}.`):lp(d,r)}let a=/^trending(?:\s+(\S+))?\s*$/i.exec(o);if(a){let d=ow(r,a[1]);if(typeof d=="object"&&"error"in d)return p(d.error);let u=await Vd(d?{kind:d}:void 0);if(mn(u))return p(`Trending failed: ${u.error}`);Sa(t,r.listScope,u.items);let c=r.defaultKind?`Trending (${r.defaultKind})`:"Trending";return xs(u.items,c,r)}let l=/^search\s+([\s\S]+)$/i.exec(o);if(l){let d=l[1].trim().split(/\s+/),u=sw(r,d);if("error"in u)return p(u.error);let{kind:c,query:m}=u;if(!m)return p(`Usage: ${At(r,"search <query>")}`);let f=await Xd(m,c?{kind:c}:void 0);return mn(f)?p(`Search failed: ${f.error}`):(Sa(t,r.listScope,f.items),xs(f.items,`Search: ${m}`,r))}if(/^list\s*$/i.test(o)){let d=ip(t,r.listScope);return d?.length?xs(d,"Last list",r):p(`No cached list. ${At(r,"trending")} or ${At(r,"search <query>")}`)}return p(`Unknown ${r.commandPrefix} online command. ${At(r,"help")}`)}async function pp(e,t,n){return Yn(e,t,n,nw)}function Pr(e){let t=e.trim(),n=[],r,o,s,i=a=>{let l=t.match(a);if(!l?.[1])return;let d=l[1].trim();if(t=(t.slice(0,l.index)+t.slice(l.index+l[0].length)).trim(),a.source.includes("title"))r=d.slice(0,120);else if(a.source.includes("description"))o=d.slice(0,500);else if(a.source.includes("category"))s=d.slice(0,40);else if(a.source.includes("tag"))for(let u of d.split(/[,;]/)){let c=u.trim();c&&n.push(c.slice(0,32))}};for(let a=0;a<8;a+=1){let l=t;if(i(/--title\s+"([^"]+)"/i),i(/--title\s+(\S+)/i),i(/--description\s+"([^"]+)"/i),i(/--description\s+(\S+)/i),i(/--category\s+"([^"]+)"/i),i(/--category\s+(\S+)/i),i(/--tag\s+"([^"]+)"/i),i(/--tag\s+(\S+)/i),t===l)break}return{title:r,description:o,category:s,tags:n,remainder:t.trim()}}function iw(e,t,n){return{kind:"recipe",name:e,title:n.title??t.label??e,description:n.description??t.description,category:n.category??t.category,tags:n.tags,payload:sp(t)}}function mp(e,t,n,r){let o=Pr(r),s=n.trim().toLowerCase(),i=je(e,t,s);return i?i.source==="builtin"?{ok:!1,error:"Built-in recipes cannot be published. Copy with /run add first."}:{ok:!0,body:iw(i.name,i,o)}:{ok:!1,error:`Unknown recipe "${n}".`}}function fp(e,t,n){let r=Pr(n),o=ws(e,t);return o?{ok:!0,body:{kind:"shortcut",name:o.name,title:r.title??o.name,description:r.description,category:r.category??"shortcut",tags:r.tags,payload:{body:o.body}}}:{ok:!1,error:`Unknown shortcut "${t}".`}}function hp(e,t,n){let r=Pr(n),o=xe(),s=Ye(o,t,e);return s?{ok:!0,body:{kind:"cowork",name:s.name,title:r.title??s.name,description:r.description,category:r.category??"cowork",tags:r.tags,payload:op(s)}}:{ok:!1,error:`Unknown cowork task "${t}" for this chat.`}}function gp(e,t,n){let r=Pr(n),o=e.trim().toLowerCase();return t.trim()?{ok:!0,body:{kind:"app",name:o,title:r.title??o,description:r.description,category:r.category??"app",tags:r.tags,payload:{command:t.trim(),label:r.title}}}:{ok:!1,error:"Session has no command to publish."}}async function Qn(e){let t=wa();if(!t.ok)return{ok:!1,error:t.error};let n=await tp(e);if(mn(n))return{ok:!1,error:n.error};let r=n;return{ok:!0,message:`${r.updated?"Updated":"Published"}: ${r.publicId} (${e.kind}). Others: /run online ${r.publicId} download`}}function aw(){return p(["Cowork \u2014 scheduled shell tasks (gateway must be running).","","add <name> <schedule> -- <command\u2026>"," schedule: ondemand | hourly [:MM] | daily HH:MM | weekdays HH:MM | weekly <mon\u2026sun> HH:MM","add <name> heartbeat <interval> [grace] \u2014 dead-man's-switch (no command needed)"," e.g. /cowork add backup heartbeat 1h 10m","set <name> cmd -- <command\u2026> | schedule <\u2026> | out <path> | cwd <path>"," notify self|wa|tg|all|none"," when always|failure|state-change \u2014 notify condition (default: always)"," attach on|off \u2014 send run log as a file with notify messages"," files clear | <glob/path \u2026> \u2014 optional artifacts (basename * ?); quote paths with spaces","list | show <name> | run <name> | checkin <name> | enable <name> | disable <name> | remove <name>","publish <name> \u2014 share task to online catalog (platform login)","online trending | show <publicId> | <publicId> download \u2014 cowork tasks only","","Output logs: task outputDir (default ~/Cowork/<name>). Missed times catch up when the gateway is back."].join(`
|
|
281
|
+
`))}function yp(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 lw(e){let t=" -- ",n=e.indexOf(t);if(n===-1)return{error:'Missing " -- " before the command. Example: /cowork add tick weekdays 09:00 -- date'};let r=e.slice(0,n).trim(),o=e.slice(n+t.length).trim();if(!o)return{error:"Command after -- is empty."};let s=r.split(/\s+/).filter(Boolean);if(s.length<2)return{error:"Usage: /cowork add <name> <schedule\u2026> -- <command\u2026>"};let i=s[0],a=s.slice(1);return{name:i,scheduleWords:a,command:o}}async function bp(e,t,n){let r=e.trim();if(!r||/^help$/i.test(r))return aw();let o=/^online\b([\s\S]*)$/i.exec(r);if(o)return Yn((o[1]??"").trim(),t,n,up);let s=/^(\S+)\s+publish\b([\s\S]*)$/i.exec(r);if(s){let f=hp(t,s[1],s[2]??"");if(!f.ok)return p(f.error);let h=await Qn(f.body);return p(h.ok?h.message:h.error)}let i=r.split(/\s+/)[0].toLowerCase();if(/^list$/i.test(i)){let f=xe().filter(g=>g.ownerPeerKey===t);if(f.length===0)return p("(no cowork tasks for this chat)");let h=f.map(g=>{let y=g.enabled?"":" (disabled)",b=g.notifyWhen&&g.notifyWhen!=="always"?` when=${g.notifyWhen}`:"";return`${Re}${g.name} ${In(g.schedule)} notify=${g.notify}${b}${y}`});return p(h.join(`
|
|
282
|
+
`))}let a=r.match(/^show\s+(\S+)\s*$/i);if(a){let f=a[1],h=xe();an(h);let g=Ye(h,f,t);if(!g)return p(`Unknown task "${f}". /cowork list`);let y=Oo(g),b=[`name: ${g.name}`,`id: ${g.id}`,`schedule: ${In(g.schedule)}`,`enabled: ${g.enabled}`,`notify: ${g.notify}`,`notifyWhen: ${g.notifyWhen??"always"}`];if(g.schedule.kind==="heartbeat"){let v=_o(g.id);b.push(`lastCheckin: ${v?new Date(v).toLocaleString():"(never \u2014 send /cowork checkin "+g.name+")"}`)}else b.push(`attachLog: ${g.attachLog}`),b.push(`files: ${g.attachFiles.length?g.attachFiles.join(", "):"(none)"}`),b.push(`cwd: ${g.cwd||"(session cwd)"}`),b.push(`out: ${g.outputDir}`),b.push(`cmd: ${g.command}`),b.push(`last slot: ${y?new Date(y).toLocaleString():"(never)"}`);return p(b.join(`
|
|
283
|
+
`))}if(/^add$/i.test(i)){let f=r.slice(3).trim(),h=f.match(/^(\S+)\s+(heartbeat\s+.+)$/i);if(h){let M=Bs(h[1]);if(!M.ok)return p(M.error);let L=h[2].split(/\s+/).filter(Boolean),A=Lo(L);if(!A.ok)return p(A.error);let K=xe();if(Ye(K,M.name,t))return p(`Task "${M.name}" already exists. Remove it first or pick another name.`);let X=Qr(M.name),se={id:sr(),name:M.name,ownerPeerKey:t,command:"",cwd:"",outputDir:X,schedule:A.schedule,enabled:!0,notify:"self",notifyWhen:"always",attachLog:!1,attachFiles:[],lastCompletedSlotMs:null,createdAtMs:Date.now()};return K.push(se),Le(K),an(K),Ui(se.id),p([`Saved heartbeat task "${M.name}" (${In(A.schedule)}).`,`Send /cowork checkin ${M.name} to record a heartbeat.`,"Alerts if no check-in arrives within the expected interval + grace period."].join(`
|
|
284
|
+
`))}let g=lw(f);if("error"in g)return p(g.error);let y=Bs(g.name);if(!y.ok)return p(y.error);let b=Lo(g.scheduleWords);if(!b.ok)return p(b.error);let v=xe();if(Ye(v,y.name,t))return p(`Task "${y.name}" already exists. Remove it first or pick another name.`);let E=oe(t),R=Qr(y.name),P={id:sr(),name:y.name,ownerPeerKey:t,command:g.command,cwd:"",outputDir:R,schedule:b.schedule,enabled:!0,notify:"self",notifyWhen:"always",attachLog:!1,attachFiles:[],lastCompletedSlotMs:null,createdAtMs:Date.now()};return v.push(P),Le(v),p([`Saved cowork task "${y.name}" (${In(b.schedule)}).`,`Output: ${R}`,`Notify: self \u2014 change with /cowork set ${y.name} notify wa|tg|all|none`].join(`
|
|
285
|
+
`))}let l=r.match(/^run\s+(\S+)\s*$/i);if(l){let f=l[1].toLowerCase(),h=Ye(xe(),f,t);return h?h.enabled?(il({ownerPeerKey:t,name:h.name,at:Date.now()}),p(`On-demand run queued for "${h.name}" (runs within ~30s while omnish run is active).`)):p(`Cowork "${h.name}" is disabled. /cowork enable ${h.name}`):p(`Unknown task "${l[1]}". /cowork list`)}let d=r.match(/^checkin\s+(\S+)\s*$/i);if(d){let f=d[1].toLowerCase(),h=xe();an(h);let g=Ye(h,f,t);return g?g.schedule.kind!=="heartbeat"?p(`"${g.name}" is not a heartbeat task.`):(Ui(g.id),p(`Heartbeat recorded for "${g.name}".`)):p(`Unknown task "${d[1]}". /cowork list`)}let u=r.match(/^(?:remove|rm|del)\s+(\S+)\s*$/i);if(u){let f=u[1],h=xe(),g=h.findIndex(y=>y.name===f.toLowerCase()&&y.ownerPeerKey===t);return g===-1?p(`Unknown task "${f}".`):(h.splice(g,1),Le(h),p(`Removed cowork task "${f.toLowerCase()}".`))}let c=r.match(/^enable\s+(\S+)\s*$/i);if(c)return wp(c[1],t,!0);let m=r.match(/^disable\s+(\S+)\s*$/i);return m?wp(m[1],t,!1):/^set$/i.test(i)?pw(r.slice(3).trim(),t):p("Unknown /cowork command. Try /cowork help")}function wp(e,t,n){let r=xe(),o=Ye(r,e,t);return o?(o.enabled=n,Le(r),p(`Cowork "${o.name}" ${n?"enabled":"disabled"}.`)):p(`Unknown task "${e}".`)}function cw(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 uw(e){let t=e.toLowerCase();return t==="always"||t==="all"?"always":t==="failure"||t==="fail"||t==="failures"?"failure":t==="state-change"||t==="statechange"||t==="change"||t==="transition"?"state-change":null}function dw(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 pw(e,t){let n=e.match(/^(\S+)\s+([\s\S]+)$/);if(!n)return p("Usage: /cowork set <name> cmd -- \u2026 | schedule \u2026 | out <path> | cwd <path> | notify \u2026 | when \u2026 | attach <on|off> | files \u2026");let r=n[1],o=n[2].trim(),s=xe(),i=Ye(s,r,t);if(!i)return p(`Unknown task "${r}".`);if(o.toLowerCase().startsWith("cmd ")){let a=o.slice(4).trim(),l=" -- ",d=a.indexOf(l),u;if(d!==-1)u=a.slice(d+l.length).trim();else if(a.startsWith("--"))u=a.slice(2).trim();else return p("Usage: /cowork set <name> cmd -- <command\u2026>");return u?(i.command=u,Le(s),p(`Updated command for "${i.name}".`)):p("Command is empty.")}if(o.toLowerCase().startsWith("schedule ")){let a=o.slice(9).trim().split(/\s+/).filter(Boolean),l=Lo(a);return l.ok?(i.schedule=l.schedule,Le(s),p(`Schedule for "${i.name}": ${In(l.schedule)}`)):p(l.error)}if(o.toLowerCase().startsWith("out ")){let a=o.slice(4).trim(),l=oe(t);return i.outputDir=Ge(a,l.cwd),Le(s),p(`outputDir: ${i.outputDir}`)}if(o.toLowerCase().startsWith("cwd ")){let a=o.slice(4).trim(),l=oe(t);return i.cwd=a?Ge(a,l.cwd):"",Le(s),p(`cwd: ${i.cwd||"(session cwd at run time)"}`)}if(o.toLowerCase().startsWith("notify ")){let a=o.slice(7).trim(),l=cw(a);return l?(i.notify=l,Le(s),p(`notify: ${l}`)):p("notify must be self, wa, tg, all, or none.")}if(o.toLowerCase().startsWith("when ")){let a=o.slice(5).trim(),l=uw(a);return l?(i.notifyWhen=l,Le(s),p(`notifyWhen: ${l}`)):p("when must be always, failure, or state-change.")}if(o.toLowerCase().startsWith("attach ")){let a=o.slice(7).trim(),l=yp(a);if(l.length!==1)return p("Usage: /cowork set <name> attach on|off");let d=dw(l[0]);return d===null?p("attach must be on or off"):(i.attachLog=d,Le(s),p(`attachLog: ${d}`))}if(o.toLowerCase().startsWith("files ")){let a=o.slice(6).trim(),l=yp(a);return l.length===1&&l[0].toLowerCase()==="clear"?(i.attachFiles=[],Le(s),p("files: (cleared)")):l.length===0?p("Usage: /cowork set <name> files clear | <pattern \u2026> \u2014 quote paths with spaces"):(i.attachFiles=l,Le(s),p(`files: ${i.attachFiles.join(", ")}`))}return p("Unknown set field. Try: cmd, schedule, out, cwd, notify, when, attach, files")}pe();import Lw from"node:fs";G();import kp from"node:os";import mw from"node:path";var fw=new Set(["create","delete","rename","update"]);function hw(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 gw(e){let t=e.split(",").map(n=>n.trim().toLowerCase());return t.length===0?!1:t.every(n=>fw.has(n))}function yw(e){return e.split(",").map(t=>t.trim().toLowerCase())}function xa(e,t,n){let r=e.trim();if(r.startsWith("-")&&(r=r.slice(1)),!r)return{};let o=Ge(r,n);return r.includes("*")||r.includes("?")?{glob:r.replace(/\\/g,"/")}:mw.isAbsolute(o)||r.startsWith("~")||r.startsWith("./")||r.startsWith("../")?{path:o}:r.startsWith("/")?{path:o}:{glob:r.includes("/")?r:`**/${r}`}}function Sp(e,t=kp.homedir()){let n=e.replace(/\s+&&\s+/g," ").trim(),r=hw(n);if(r.length===0)return{ok:!1,error:"Usage: /watch add fs <name> <path> [events] [-exclude \u2026] [--exclude \u2026]"};let o=0,s=r[o];o+=1;let i=["create","delete","rename"],a=[],l=[];for(;o<r.length;){let u=r[o];if(u==="--exclude"){if(o+=1,o>=r.length)return{ok:!1,error:"--exclude requires a pattern."};let c=r[o];o+=1;let m=Ge(s,t),f=xa(c.startsWith("-")?c:`-${c}`,m,t);f.path?a.push(f.path):f.glob&&l.push(f.glob);continue}if(u.startsWith("--"))return{ok:!1,error:`Unknown flag: ${u}`};if(u.startsWith("-")){let c=Ge(s,t),m=xa(u,c,t);m.path?a.push(m.path):m.glob&&l.push(m.glob),o+=1;continue}if(gw(u)){i=yw(u),o+=1;continue}return{ok:!1,error:`Unexpected token "${u}". Put path first, then events or -excludes.`}}return{ok:!0,value:{rootPath:Ge(s,t),events:i,excludePaths:a,excludeGlobs:l}}}function $a(e,t,n=kp.homedir()){let r=xa(e.startsWith("-")?e:`-${e}`,t,n);return!r.path&&!r.glob?{error:"Empty exclude pattern."}:r}import{spawnSync as $s}from"node:child_process";import vp from"node:fs";import ww from"node:os";import bw from"node:path";var kw=40,Sw=10,Rs=15e3,vw=["~/Projects","~/deploy","~/Downloads","~/src","/var/www","/srv"],xp={linux:"/var/log/dpkg.log (also /var/log/apt/history.log)",darwin:"/var/log/install.log",win32:"Windows Application event log (install/remove)"};function xw(e){return e.startsWith("~/")?bw.join(ww.homedir(),e.slice(2)):e}function $w(e,t){return t?.trim()?e.toLowerCase().includes(t.trim().toLowerCase()):!0}function Rw(e){let t=n=>n==="running"||n==="active";return[...e].sort((n,r)=>{let o=t(n.state),s=t(r.state);return o!==s?o?-1:1:n.name.localeCompare(r.name)})}function Cw(e){let t=[];for(let n of e.split(`
|
|
286
|
+
`)){let r=n.trim();if(!r)continue;let o=r.split(/\s+/);if(o.length<4)continue;let s=o[0];if(!s.endsWith(".service"))continue;let i=o[2].toLowerCase(),a=s.slice(0,-8);t.push({name:a,state:i})}return t}function Mw(e){let t=[],n=e.split(`
|
|
287
|
+
`);for(let r=0;r<n.length;r++){let o=n[r].trim();if(!o||o.startsWith("PID"))continue;let s=o.split(/\s+/);if(s.length<3)continue;let i=s[s.length-1];if(!i.includes("."))continue;let l=s[0]==="-"?"stopped":"running";t.push({name:i,state:l})}return t}function Tw(e){let t=[],n="";for(let r of e.split(`
|
|
288
|
+
`)){let o=r.match(/SERVICE_NAME:\s*(.+)/i);if(o){n=o[1].trim();continue}let s=r.match(/^\s*STATE\s*:\s*\d+\s+(\S+)/i);if(s&&n){let i=s[1].toUpperCase(),a=i.toLowerCase();i==="RUNNING"?a="running":i==="STOPPED"&&(a="stopped"),t.push({name:n,state:a}),n=""}}return t}function Ew(){let e=$s("systemctl",["list-units","--type=service","--all","--no-pager","--plain","--no-legend"],{encoding:"utf8",timeout:Rs});return e.error||e.status!==0?{entries:[],error:"systemctl unavailable \u2014 install systemd or run on Linux."}:{entries:Cw(e.stdout??"")}}function Pw(){let e=$s("launchctl",["list"],{encoding:"utf8",timeout:Rs});return e.error||e.status!==0?{entries:[],error:"launchctl unavailable."}:{entries:Mw(e.stdout??"")}}function Aw(){let e=$s("sc",["query","type=","service","state=","all"],{encoding:"utf8",timeout:Rs,windowsHide:!0});if(!e.error&&e.status===0&&(e.stdout??"").includes("SERVICE_NAME"))return{entries:Tw(e.stdout??"")};let t=$s("powershell",["-NoProfile","-Command","Get-Service | ForEach-Object { $_.Name + ' ' + $_.Status }"],{encoding:"utf8",timeout:Rs,windowsHide:!0});if(t.error||t.status!==0)return{entries:[],error:"sc query and Get-Service unavailable."};let n=[];for(let r of(t.stdout??"").split(`
|
|
289
|
+
`)){let o=r.trim();if(!o)continue;let s=o.lastIndexOf(" ");if(s<=0)continue;let i=o.slice(0,s).trim(),a=o.slice(s+1).trim().toLowerCase();n.push({name:i,state:a})}return{entries:n}}function Cs(e){let t=e?.limit??kw,n=e?.filter,r,o=process.platform;if(o==="linux")r=Ew();else if(o==="darwin")r=Pw();else if(o==="win32")r=Aw();else return{entries:[],truncated:!1,totalMatched:0,error:`Service discovery not supported on ${o}.`};if(r.error)return{entries:[],truncated:!1,totalMatched:0,error:r.error};let s=Rw(r.entries.filter(l=>$w(l.name,n))),i=s.length,a=i>t;return{entries:s.slice(0,t),truncated:a,totalMatched:i}}function Iw(e,t=Sw){let n=[];for(let r=0;r<e.length;r+=t)n.push(e.slice(r,r+t));return n}function Ra(e,t){if(e.error)return p(e.error);if(e.entries.length===0){let o=t?.trim()?`No services match "${t}". Try /watch svc list without a filter.`:"No services found on this host.";return p(o)}let n=e.entries.map(o=>`${Re}${o.name} \u2014 ${o.state}`),r=t?.trim()?`Services matching "${t}" (${e.entries.length} shown):`:`Services on ${process.platform} (${e.entries.length} shown):`;if(n.unshift(r),e.truncated){let o=e.totalMatched-e.entries.length;n.push("",`${o} more \u2014 narrow with /watch svc list <filter>`)}return n.push("","Copy-paste templates follow in the next message."),p(n.join(`
|
|
290
|
+
`))}function Ca(e,t){let n=t?.trim()||"<name>";if(e.length===0)return p("No services to template \u2014 run /watch svc list first.");let r=e.map(i=>i.name),o=Iw(r),s=[t?.trim()?`Copy-paste (/watch add svc ${n} \u2026):`:"Copy-paste (replace <name>):",""];for(let i of o)s.push(`/watch add svc ${n} ${i.join(" ")}`);return p(s.join(`
|
|
291
|
+
`))}function $p(){let e=process.platform,t=e==="linux"?"linux":e==="darwin"?"darwin":"win32",n=["Watch hints",""];n.push("Package log (pkg watches):"),n.push(` ${xp[t]??xp.linux}`),n.push("","Filesystem roots that exist on this host:");let r=[];for(let o of vw){let s=xw(o);try{vp.existsSync(s)&&vp.statSync(s).isDirectory()&&r.push(o)}catch{}}if(r.length===0)n.push(" (none of the usual paths found \u2014 use any directory you own)");else for(let o of r)n.push(` ${o}`),n.push(` /watch add fs <name> ${o} create,delete,rename`);return n.push("","Service names: /watch svc list [filter]"),p(n.join(`
|
|
292
|
+
`))}function U(e){return{replies:[e]}}function Ow(...e){return{replies:e}}function Nw(e){let t=e.trim();if(!t)return{};let n=t.split(/\s+/);if(n.length>=2){let s=Zr(n[0]);return s.ok?{ruleName:s.name,filter:n.slice(1).join(" ")}:{filter:t}}let r=n[0],o=Zr(r);if(o.ok){let s=Cs({filter:r,limit:1});if(s.totalMatched===0&&!s.error)return{ruleName:o.name}}return{filter:r}}function _w(){return p(["Watch \u2014 OS event eye (watchEnabled + omnish run). Docs: docs/features/watch.md","","add fs <name> <path> [events] [-/exclude \u2026] [--exclude glob]",' e.g. /watch add fs home ~/Projects create,delete -~/Projects/tmp --exclude "**/.keyfolder"',"add pkg <name> | add svc <name> <unit\u2026>","svc list [filter] \u2014 services on this host (+ templates in next message)","svc templates [ruleName] [filter] \u2014 copy-paste /watch add svc lines only","hints \u2014 suggested FS paths and package log location","list | status | reload | show <name> | recent [N]","pause|stop <name> \u2014 stop alerts, keep rule","resume <name> | enable <name> | disable <name> | rm <name>","exclude <name> list|add <pattern>|rm <pattern>","set <name> notify self|wa|tg|all|none","set <name> when always|state-change","on | off \u2014 global watchEnabled in config","Rules are device-wide (~/.omnish/watch/rules.json); any allowlisted peer can manage.","notify:self alerts the peer who created the rule."].join(`
|
|
293
|
+
`))}function Rp(){return{excludePaths:[],excludeGlobs:[]}}function Fw(e){return e.notify==="self"?`notify=self (${e.ownerPeerKey})`:`notify=${e.notify}`}function Ww(e,t){zs(e.id),t==="pause"||t==="stop"?e.paused=!0:t==="resume"?e.paused=!1:t==="enable"?(e.enabled=!0,e.paused=!1):t==="disable"&&(e.enabled=!1,e.paused=!1)}function Cp(e,t){let n=e.trim();if(!n||/^help$/i.test(n))return U(_w());let r=n.split(/\s+/)[0].toLowerCase();if(Vr(),/^hints$/i.test(r))return U($p());if(/^svc$/i.test(r)){let u=n.slice(3).trim(),m=(u.split(/\s+/)[0]??"").toLowerCase();if(m==="list"){let f=u.slice(4).trim()||void 0,h=Cs({filter:f});return h.error?U(p(h.error)):h.entries.length===0?U(Ra(h,f)):Ow(Ra(h,f),Ca(h.entries))}if(m==="templates"){let f=u.slice(9).trim(),{ruleName:h,filter:g}=Nw(f),y=Cs({filter:g});return y.error?U(p(y.error)):U(Ca(y.entries,h))}return U(p("svc subcommands: list [filter] | templates [ruleName] [filter]"))}if(/^status$/i.test(r)){let u=$(),{summary:c}=ar(),m=[`watchEnabled: ${u.watchEnabled} watchAutoRestore: ${u.watchAutoRestore}`,`debounce: ${u.watchDebounceMs}ms max/min: ${u.watchMaxEventsPerMinute}`,`rules file: ${Js()} (${c.total} saved, ${c.active} active, ${c.paused} paused, ${c.disabled} disabled)`,`events db: ${Hr}`];c.total>0&&!u.watchEnabled?m.push("Rules are on disk; adapters stopped \u2014 /watch on to start."):c.total>0&&u.watchEnabled&&!u.watchAutoRestore?m.push("watchAutoRestore is off \u2014 /watch reload to start adapters without restarting gateway."):c.paused>0&&m.push(`${c.paused} paused rule(s) stay stopped until /watch resume <name>.`);let f=Nl();return f?(m.push("","Adapters:"),m.push(...f.getStatusLines())):u.watchEnabled&&u.watchAutoRestore?m.push("","(manager starting \u2014 try /watch status again)"):u.watchEnabled?m.push("","(manager idle \u2014 /watch reload)"):m.push("","(manager off \u2014 /watch on)"),U(p(m.join(`
|
|
294
|
+
`)))}if(/^reload$/i.test(r)){let{summary:u}=ar();return $().watchEnabled?(_l(),U(p(`Reloading from ${Js()}: ${u.total} rule(s) on disk, ${u.active} eligible to run. /watch status for adapters.`))):U(p(`Rules on disk: ${u.total} (${u.active} active). watchEnabled is false \u2014 /watch on first.`))}if(/^list$/i.test(r)){let u=Oe();if(u.length===0)return U(p("(no watch rules on this device)"));let c=u.map(m=>{let f=m.enabled?m.paused?"paused":"on":"disabled",h=m.kind==="fs"?m.path:m.kind==="svc"?m.units.join(","):"system";return`${Re}${m.name} [${m.kind}] ${f} ${Fw(m)} when=${m.notifyWhen} \u2014 ${h}`});return U(p(c.join(`
|
|
295
|
+
`)))}let o=n.match(/^recent(?:\s+(\d+))?\s*$/i);if(o){let u=o[1]?Number(o[1]):15,c=Oe(),m=new Set(c.map(g=>g.id)),f=pl(u,m);if(f.length===0)return U(p("(no recent watch events)"));let h=f.map(g=>`${new Date(g.tsMs).toLocaleString()} ${g.ruleName} ${g.summary}`);return U(p(h.join(`
|
|
296
|
+
`)))}let s=n.match(/^show\s+(\S+)\s*$/i);if(s){let u=Zt(Oe(),s[1]);if(!u)return U(p(`Unknown rule "${s[1]}". /watch list`));let c=[`name: ${u.name}`,`kind: ${u.kind}`,`creator: ${u.ownerPeerKey}`,`enabled: ${u.enabled}`,`paused: ${u.paused}`,`notify: ${u.notify}`,`notifyWhen: ${u.notifyWhen}`,`adapter: ${u.adapterStatus||"(unknown)"}`];return u.kind==="fs"&&(c.push(`path: ${u.path}`),c.push(`events: ${u.events.join(",")}`),c.push(`excludePaths: ${u.excludePaths.length?u.excludePaths.join(", "):"(none)"}`),c.push(`excludeGlobs: ${u.excludeGlobs.length?u.excludeGlobs.join(", "):"(none)"}`)),u.kind==="svc"&&c.push(`units: ${u.units.join(", ")||"(none)"}`),U(p(c.join(`
|
|
297
|
+
`)))}if(/^on$/i.test(r))return W({watchEnabled:!0}),De(),U(p("watchEnabled: true (gateway picks up watchers when running)."));if(/^off$/i.test(r))return W({watchEnabled:!1}),De(),U(p("watchEnabled: false \u2014 all adapters stopped."));let i=n.match(/^exclude\s+(\S+)\s+(\S+)(?:\s+([\s\S]+))?\s*$/i);if(i){let u=i[1],c=i[2].toLowerCase(),m=(i[3]??"").trim(),f=Oe(),h=Zt(f,u);if(!h)return U(p(`Unknown rule "${u}".`));if(h.kind!=="fs")return U(p("exclude applies to filesystem rules only."));if(c==="list"){let g=[`excludePaths: ${h.excludePaths.length?h.excludePaths.join(`
|
|
298
|
+
`):"(none)"}`,`excludeGlobs: ${h.excludeGlobs.length?h.excludeGlobs.join(`
|
|
299
|
+
`):"(none)"}`];return U(p(g.join(`
|
|
300
|
+
`)))}if(c==="add"){if(!m)return U(p("Usage: /watch exclude <name> add <pattern>"));let g=$a(m,h.path);if("error"in g)return U(p(g.error));if(g.path){if(Qe(g.path))return U(p("That path is blocked."));h.excludePaths.includes(g.path)||h.excludePaths.push(g.path)}return g.glob&&!h.excludeGlobs.includes(g.glob)&&h.excludeGlobs.push(g.glob),pt(Sn(f,h)),De(),U(p(`Added exclude to "${h.name}".`))}if(c==="rm"||c==="remove"){if(!m)return U(p("Usage: /watch exclude <name> rm <pattern>"));let g=$a(m,h.path);return"error"in g?U(p(g.error)):(g.path&&(h.excludePaths=h.excludePaths.filter(y=>y!==g.path)),g.glob&&(h.excludeGlobs=h.excludeGlobs.filter(y=>y!==g.glob)),pt(Sn(f,h)),De(),U(p(`Removed exclude from "${h.name}".`)))}return U(p("exclude subcommands: list | add | rm"))}if(/^add$/i.test(r)){let u=n.slice(3).trim(),c=u.split(/\s+/);if(c.length<2)return U(p("Usage: /watch add fs|pkg|svc <name> \u2026"));let m=c[0].toLowerCase(),f=m==="fs"||m==="pkg"||m==="svc"?m:null;if(!f)return U(p("Kind must be fs, pkg, or svc."));let h=c[1],g=Zr(h);if(!g.ok)return U(p(g.error));let y=Oe();if(y.length>=Gs)return U(p(`Max ${Gs} watch rules on this device.`));if(Zt(y,g.name))return U(p(`Rule "${g.name}" already exists.`));let b;if(f==="fs"){let R=u.slice(m.length+1+h.length).trim(),P=Sp(R);if(!P.ok)return U(p(P.error));let{rootPath:M,events:L,excludePaths:A,excludeGlobs:K}=P.value;if(Qe(M))return U(p("That path is blocked (sensitive). Choose another directory."));if(!Lw.existsSync(M))return U(p(`Path not found: ${M}`));for(let X of A)if(Qe(X))return U(p(`Excluded path blocked: ${X}`));b={id:eo(),name:g.name,ownerPeerKey:t,kind:"fs",enabled:!0,paused:!1,notify:"self",notifyWhen:"always",path:M,events:L,units:[],excludePaths:A,excludeGlobs:K,adapterStatus:"",createdAtMs:Date.now()}}else if(f==="pkg")b={id:eo(),name:g.name,ownerPeerKey:t,kind:"pkg",enabled:!0,paused:!1,notify:"self",notifyWhen:"always",path:"",events:[],units:[],...Rp(),adapterStatus:"",createdAtMs:Date.now()};else{let R=c.slice(2);if(R.length===0)return U(p("Usage: /watch add svc <name> <unit\u2026>"));b={id:eo(),name:g.name,ownerPeerKey:t,kind:"svc",enabled:!0,paused:!1,notify:"self",notifyWhen:"always",path:"",events:[],units:R,...Rp(),adapterStatus:"",createdAtMs:Date.now()}}pt(Sn(y,b)),De();let E=$().watchEnabled?"":" Rule saved to disk. /watch on to start adapters.";return U(p(`Watch rule "${b.name}" added (${b.kind}).${E}`))}let a=n.match(/^set\s+(\S+)\s+(\S+)\s+(\S+)\s*$/i);if(a){let[,u,c,m]=a,f=Oe(),h=Zt(f,u);if(!h)return U(p(`Unknown rule "${u}".`));let g=c.toLowerCase(),y=m.toLowerCase();if(g==="notify"){let b=y==="wa"||y==="whatsapp"?"wa":y==="tg"||y==="telegram"?"tg":y==="all"?"all":y==="none"?"none":y==="self"?"self":null;if(!b)return U(p("notify must be self, wa, tg, all, or none."));h.notify=b}else if(g==="when"){let b=y==="state-change"?"state-change":y==="always"?"always":null;if(!b)return U(p("when must be always or state-change."));h.notifyWhen=b}else return U(p("set supports: notify, when"));return pt(Sn(f,h)),De(),U(p(`Updated ${h.name} ${g}=${y}.`))}let l=n.match(/^(?:rm|remove)\s+(\S+)\s*$/i);if(l){let u=l[1],c=Oe(),m=Zt(c,u);return m?(zs(m.id),pt(wl(c,u)),De(),U(p(`Removed watch rule "${u}".`))):U(p(`Unknown rule "${u}".`))}let d=n.match(/^(pause|stop|resume|enable|disable)\s+(\S+)\s*$/i);if(d){let u=d[1].toLowerCase(),c=d[2],m=Oe(),f=Zt(m,c);if(!f)return U(p(`Unknown rule "${c}".`));Ww(f,u==="stop"?"pause":u),pt(Sn(m,f)),De();let g=u==="stop"?"paused (stop)":`${u}d`;return U(p(`${f.name}: ${g}.`))}return U(p("Unknown /watch command. Try /watch help"))}pe();function Mp(){return p(["Tunnel commands:",'/tunnel signup --email "you@example.com" --password "yourpass"','/tunnel signup --phone "+1234567890" --password "yourpass"','/tunnel login --email "you@example.com" --password "yourpass"','/tunnel login --token "<token>" [--relay <url>]',"/tunnel logout","/tunnel status [--relay <url>]","/tunnel http <port> [--name <slug>] [--host <addr>]","/tunnel tcp <port> [--name <slug>] [--host <addr>]","/tunnels","/tunnel stop <id|slug>"].join(`
|
|
301
|
+
`))}async function Ma(e,t,n){let r=kd(e);if(r.kind==="help")return Mp();if(r.kind==="error")return p(r.message);if(r.kind==="signup"){let o=r.email?.trim()||"",s=r.phone?.trim()||"",i=r.password||"";if(!o&&!s)return p('Usage: /tunnel signup --email "you@example.com" --password "yourpass"');if(i.length<8)return p("Password must be at least 8 characters.");let a=$(),l=r.relayUrl?.trim()||rt(a.tunnelRelayUrl||$e),d=await us(l,{...o?{email:o}:{},...s?{phone:s}:{},password:i});return d.ok?(ft({token:d.token,...r.relayUrl?.trim()?{relayUrl:r.relayUrl.trim()}:{}}),p("Account created. Token saved.")):p(`Signup failed: ${d.error}`)}if(r.kind==="login"){if(r.email||r.phone){let i=r.password||"";if(!i)return p('Usage: /tunnel login --email "you@example.com" --password "yourpass"');let a=$(),l=r.relayUrl?.trim()||rt(a.tunnelRelayUrl||$e),d=await ds(l,{...r.email?{email:r.email}:{},...r.phone?{phone:r.phone}:{},password:i});return d.ok?(ft({token:d.token,...r.relayUrl?.trim()?{relayUrl:r.relayUrl.trim()}:{}}),p("Logged in. Token saved.")):p(`Login failed: ${d.error}`)}let s=r.token?.trim();return s?(ft({token:s,...r.relayUrl?.trim()?{relayUrl:r.relayUrl.trim()}:{}}),p("Tunnel token saved on this host (not shown). If OMNISH_TUNNEL_TOKEN is set in the gateway environment, it overrides the file until unset.")):p('Usage: /tunnel login --token "<token>" [--relay <url>]')}if(r.kind==="logout")return lo(),p("Tunnel token file removed. Unset OMNISH_TUNNEL_TOKEN in the gateway environment if you rely on it.");if(r.kind==="status"){let o=$(),s=r.relayUrl?.trim()||rt(o.tunnelRelayUrl||$e),i=ht(),a=!!process.env.OMNISH_TUNNEL_TOKEN?.trim(),d=!!xt()?.token?.trim(),u=await is(s,i),c=[`Relay: ${s}`,`Token: ${i?"configured":"missing"}${a?" (OMNISH_TUNNEL_TOKEN)":d?" (tunnel-auth.json)":""}`,`Health: ${u.healthOk?`ok${u.healthVersion?` (version ${u.healthVersion})`:""}`:"fail"}`,`Control (WSS): ${u.controlOk?"auth ok":"fail"}`,`Active tunnels: ${n.getActiveCount()}`];return!u.ok&&u.error&&c.push(`Detail: ${u.error}`),p(c.join(`
|
|
302
|
+
`))}if(!t.tunnelEnabled)return p("Chat tunneling is disabled. Use /config set tunnelEnabled true (then expose with /tunnel http \u2026).");if(r.kind==="list"){let o=n.list();return o.length===0?p("(no active tunnels)"):p(o.map(s=>`${s.id} ${s.kind} ${s.status}
|
|
303
|
+
${s.publicUrl||"(pending)"}
|
|
304
|
+
${s.localHost}:${s.localPort}`).join(`
|
|
305
|
+
|
|
306
|
+
`))}if(r.kind==="stop"){let o=await n.stop(r.target);return o?p(`Stopped tunnel ${o.id}.`):p(`No active tunnel matched "${r.target}".`)}if(r.kind==="expose"){let o=await n.expose(t,r.options);return p(`${o.kind.toUpperCase()} tunnel active
|
|
307
|
+
public: ${o.publicUrl}
|
|
308
|
+
local: ${o.localHost}:${o.localPort}
|
|
309
|
+
id: ${o.id}`)}return Mp()}function Ta(e){let t=e.trim();if(t==="/computers"||t.startsWith("/computers "))return t.slice(10).trim();if(t==="/pcs"||t.startsWith("/pcs "))return t.slice(4).trim();let n=t.match(/^\/c(?:$|\s+(.*))/);return n?(n[1]??"").trim():null}function I(e){return{kind:"text",body:e}}function Uw(e,t){return`${e}:${t}`}function Dw(e,t){return`${e}:apps:${t}`}async function Hw(e,t){let n=e.trim();if(n===""||/^status$/i.test(n)||/^show$/i.test(n)||/^get$/i.test(n)){let a=$();return nc({gatewayMode:a.gatewayMode,authPresent:nt(),tokenSet:!!ve(a),allowN:a.allowFrom.length,tgAllowN:a.telegramAllowFrom.length,updateBrief:ms(vr())})}if(/^help$/i.test(n))return Z(si());let r=gn(n);if(!r)return wc();qr(r);let o=$(),s,i=!1;if(t?.reload){let a=await t.reload();s=a.ok?a.summary:`Reload failed: ${a.error}`}else i=!0;return lc(o.gatewayMode,s,i)}function Bw(e){let t=e.trim();if(!dt(t))return ac();let n=Lt(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.'),ic(r)}async function Tp(e,t,n){let r=oe(t),o=jl(n);if(o!==null){let a=Gl(r.cwd,o),l=Jl(a);return l.ok?(ro(t,a),p(`cwd: ${a}`)):p(`cd: ${l.error}`)}let s=await sn(e.shell,n,{timeoutMs:e.syncTimeoutMs,maxBytes:e.syncMaxBytes,cwd:r.cwd}),i=[];return i.push(`$ ${n}`),s.stdout.trim()&&i.push(s.stdout.trimEnd()),s.stderr.trim()&&(i.push("\u2014 stderr \u2014"),i.push(s.stderr.trimEnd())),s.timedOut?i.push(`Timed out (${Math.round(e.syncTimeoutMs/1e3)}s limit).`):s.code!==0&&s.code!==null&&i.push(`Exit ${s.code}`),p(i.join(`
|
|
310
|
+
`))}async function Vn(e,t,n,r,o,s,i,a,l=null,d=!1,u){let c=s.text.trim(),m=s.peerKey,f=c.match(/^!!\s*(start|stop)\s*$/i);if(f)return f[1].toLowerCase()==="start"?(o.set(m,!0),I(hc())):(o.set(m,!1),I(p("Free shell mode off.")));if(c.startsWith(e.commandPrefix)){let g=c.slice(e.commandPrefix.length).trim();if(!g)return I(p(`Send ${e.commandPrefix}<command> or /help`));if(!d&&Bd(g)){let y=ha(m,g);if(y!==void 0)return await Vn(e,t,n,r,o,{...s,text:y},i,a,l,!0,u)}return I(await Tp(e,m,g))}if(/^\/help\s+files$/i.test(c.trim()))return I(li());if(c==="/help"||c==="help")return I(Z(xn(e)));if(c.startsWith("/")){if(/^\/files(?:\s+help)?$/i.test(c.trim()))return I(li());let g=c.match(/^\/receive\b(?:\s+(\S+))?$/i);if(g){let k=(g[1]??"status").toLowerCase(),x=new Set(["here","cwd","session","dir"]),O=new Set(["default","global","reset"]);if(x.has(k)){Vs(m,"sessionCwd");let F=oe(m).cwd;return I(p(`Inbound files will save under this chat\u2019s session folder:
|
|
311
|
+
${F}
|
|
312
|
+
(layout: \u2026/<peer>/<date>/<file> under that root).
|
|
313
|
+
|
|
314
|
+
Change folder with ${e.commandPrefix}cd \u2026 Send /receive default to use the server config again.`))}return O.has(k)?(Vs(m,"default"),I(p("Per-chat inbound folder cleared. Uploads now follow fileReceiveRootMode in config.json (/files)."))):I(k==="help"?ci():k==="status"?pc(e,m):ci())}if(c==="/reload"||c==="/restart"){if(!a?.reload)return I(p("Reload is only available while the omnish gateway (omnish run) is running."));let k=await a.reload();return I(p(k.ok?k.summary:`Reload failed: ${k.error}`))}if(c==="/updates"||c.startsWith("/updates ")){let k=c.slice(8).trim().toLowerCase();if(k==="cached"||k==="last"){let O=vr();return I(O?cr(O):p("No update snapshot yet. Send /updates (live check) once, or enable updateCheckEnabled and wait for the scheduled check."))}let x=await xr(Ze(),$());return I(cr(x))}let y=c.match(/^\/security(?:\s+(\S+))?\s*$/i);if(y){let k=(y[1]??"").toLowerCase(),x=$(),O=Rt(x);return I(k==="help"||k==="?"?Z(Pc()):k==="summary"||k==="brief"?p(Xl(O,"Send /security for the full report.")):k==="tips"?Z(Ec()):k===""||k==="full"||k==="report"?Tc(O):p("Unknown /security subcommand. Try /security, /security summary, /security tips, or /security help"))}let b=c.match(/^\/(gateway|gw|mode)\b(?:\s+(.*))?$/i);if(b){let k=(b[2]??"").trim();return I(await Hw(k,a))}if(c==="/config"||c.startsWith("/config ")){let k=c.slice(7).trim();return I(await Nc(k,a))}let v=Yd(c);if(v!==null)return I(await Qd(e,v));let E=Ta(c);if(E!==null){let k=Qc(e,E,l);return k===null?null:I(k)}let R=c.match(/^\/(send|file)\b(?:\s+([\s\S]+))?$/i);if(R){let k=(R[2]??"").trim();if(!k)return I(ai());let x=hs(k);if(!x)return I(ai());let O=oe(m).cwd,F=await Rr(O,x.selectorPart);if(F.length===0)return I(p(`No files matched: ${x.selectorPart}`));let Q=await Cr(F);if(!Q.ok)return I(p(Q.error));let z=fe()?Vo(e):e.fileSendMaxBytes,we=[];for(let Ue of F){let Ie=Kt(Ue,z);if("error"in Ie)return I(p(Ie.error));we.push({absPath:Ie.absPath,category:Ie.category,mimetype:Ie.mimetype,displayName:Ie.displayName,caption:x.caption})}return we.length===1?{kind:"file",spec:we[0]}:{kind:"files",specs:we}}if(c==="/allowlist"){let k=$();return I(sc([{label:"allowFrom (WhatsApp)",items:k.allowFrom},{label:"telegramAllowFrom",items:k.telegramAllowFrom}]))}let P=c.match(/^\/allow\b\s+(.+)$/i);if(P)try{let k=Kr(P[1].trim());return I(ii(k))}catch(k){return I(p(String(k)))}if(/^\/allow\b\s*$/i.test(c))return I(cc());let M=c.match(/^\/deny\b\s+(.+)$/i);if(M)try{let k=zr(M[1].trim());return I(ii(k))}catch(k){return I(p(String(k)))}if(/^\/deny\b\s*$/i.test(c))return I(uc());let L=c.match(/^\/(whatsapp|wa|telegram|tg)\b(?:\s+(.*))?$/i);if(L){let k=L[1].toLowerCase(),x=(L[2]??"").trim(),O=k==="whatsapp"||k==="wa";if(k==="telegram"||k==="tg"){let Q=x.match(/^token\s+(\S+)\s*$/i);return Q?I(Bw(Q[1]??"")):x===""||/^help$/i.test(x)?I(Z(oc($()))):I(fc())}if(O)return x===""||/^help$/i.test(x)?I(Z(rc(e))):I(mc())}let A=c.match(/^\/(cowork|cw)\b(?:\s+([\s\S]*))?$/i);if(A){let k=(A[2]??"").trim();return I(await bp(k,m,e))}let K=c.match(/^\/watch\b(?:\s+([\s\S]*))?$/i);if(K){let k=(K[1]??"").trim(),x=Cp(k,m);return x.replies.length===1?I(x.replies[0]):{kind:"texts",bodies:x.replies}}if(c==="/apps"||c.startsWith("/apps "))return I(await zw(c,m,e,i,r));let X=jw(c);if(X!==null)return I(await Jw(X,m,e,i,s.mediaSavedPath,u));if(c.startsWith("/bg")){let k=c.slice(3).trim();if(!k)return I(dc());let x=Lu(k);if("error"in x)return I(p(x.error==="empty"?"Usage: /bg <command> or /bg -n <name> <command>.":x.error));let O=oe(m).cwd,F=x.notify&&u?.sendToPeer?Ie=>{let de=Ou(Ie);u.sendToPeer(m,de).catch(()=>{})}:void 0,{id:Q,meta:z}=t.spawnJob(e.shell,x.cmd,{cwd:O,name:x.name,notifyPeerKey:x.notify?m:null,onComplete:F}),we=z.name?`${Q} (${z.name})`:Q,Ue=x.notify?`
|
|
315
|
+
Notify on completion: on`:"";return I(p(`Job ${we} started.
|
|
316
|
+
[cwd: ${O}]
|
|
317
|
+
/log ${z.name??Q}
|
|
318
|
+
/tail ${z.name??Q}${Ue}`))}if(c==="/tunnels")return I(await Ma("list",e,dn()));let se=c.match(/^\/tunnel\b(?:\s+([\s\S]*))?$/i);if(se){let k=(se[1]??"").trim();return I(await Ma(k||"help",e,dn()))}if(c==="/jobs"){let k=t.list().slice(0,20);return k.length===0?I(p("(no jobs yet)")):I(p(k.map(x=>{let O=x.finishedAt&&x.startedAt?`${((Date.parse(x.finishedAt)-Date.parse(x.startedAt))/1e3).toFixed(1)}s`:"\u2026";return`${x.name?`${x.id} ${x.name}`:x.id} ${x.status} exit=${x.exitCode??"?"} ${O}
|
|
319
|
+
${x.cmd.slice(0,120)}${x.cmd.length>120?"\u2026":""}`}).join(`
|
|
320
|
+
|
|
321
|
+
`)))}let he=c.match(/^\/log\s+(\S+)(?:\s+(\d+))?\s*$/i);if(he){let k=he[1],x=t.resolveJobRef(k);if(!x.ok)return I(p(x.error));let O=x.id,F=he[2]?Number.parseInt(he[2],10):e.jobLogTailLines,Q=Number.isFinite(F)&&F>0?Math.min(F,500):e.jobLogTailLines;return I(p(t.tailLog(O,Q)))}let D=c.match(/^\/tail\s+(\S+)\s*$/i);if(D){let k=D[1],x=t.resolveJobRef(k);if(!x.ok)return I(p(x.error));let O=x.id,F=Uw(m,O),Q=n.get(F)??0,{text:z,nextOffset:we}=t.readSince(O,Q);return n.set(F,we),I(z?p(z.trimEnd()||"(no new output)"):p(`(no new output; offset ${we})`))}let ae=c.match(/^\/kill\s+(\S+)\s*$/i);if(ae){let k=ae[1],x=t.resolveJobRef(k);return x.ok?I(p(t.kill(x.id))):I(p(x.error))}let Y=c.match(/^\/(shortcut|shortcuts|alias|aliases)\b(?:\s+([\s\S]*))?$/i);if(Y){let k=Y[1].toLowerCase(),x=(Y[2]??"").trim();return k==="shortcuts"&&!x?x="list":((k==="alias"||k==="aliases")&&!x||k==="shortcut"&&!x)&&(x="help"),I(await Kw(x,m,e))}if(!d){let k=c.trim().match(/^\/([a-zA-Z0-9][a-zA-Z0-9_-]{0,31})\s*$/);if(k){let x=k[1],O=ha(m,x);if(O!==void 0)return await Vn(e,t,n,r,o,{...s,text:O},i,a,l,!0,u)}}return I(gc(e))}let h=c.match(/^>(\S+)\s*(.*)$/s);if(h){let g=h[1],y=h[2]??"",b=await i.writeNamedLine(m,g,y);return b?I(p(b)):null}return await i.writeFocusedLine(m,c)?null:o.get(m)&&c?I(await Tp(e,m,c)):e.chatLlmFallbackEnabled&&e.chatLlmShellCommand.trim().length>0&&u?.onPlainTextLlmFallback?(u.onPlainTextLlmFallback(m,c),null):I(yc(e))}function jw(e){return e==="/run"||e.startsWith("/run ")?e.slice(4).trim():e==="/r"||e.startsWith("/r ")?e.slice(2).trim():null}function Gw(e){let t=e.trim(),n=t.match(/^(\S+)\s+(?:--queue|-q)\s+([\s\S]+)$/i);if(n)return{recipe:n[1],task:n[2],queued:!0};let r=t.match(/^(\S+)\s+([\s\S]+)$/);return r?{recipe:r[1],task:r[2],queued:!1}:null}async function Jw(e,t,n,r,o,s){let i=e.trim();if(!i||/^help$/i.test(i))return Z(xc());let a=/^online\b([\s\S]*)$/i.exec(i);if(a)return pp((a[1]??"").trim(),t,n);let l=/^(\S+)\s+publish\b([\s\S]*)$/i.exec(i);if(l){let b=mp(t,n,l[1],l[2]??"");if(!b.ok)return p(b.error);let v=await Qn(b.body);return p(v.ok?v.message:v.error)}let d=/^list\b([\s\S]*)$/i.exec(i);if(d){let b=(d[1]??"").trim(),{filter:v,bad:E}=su(b);if(E)return p(`Unknown /run list suffix: "${E}". Use: list | list --chat | list -p | list --global | list -g`);if(v==="merged")return $c(cu(t,n));let R=au(t,n,v);return Mc(R,v)}let u=/^show\b([\s\S]*)$/i.exec(i);if(u){let b=(u[1]??"").trim(),{mode:v,remainder:E}=ou(b),R=/^(\S+)\s*$/i.exec(E);if(!R?.[1])return p("Usage: /run show <name> \u2014 or show --global|-g|--chat|-p <name> (-g shared, -p private)");let P=R[1];if(v==="resolved"){let se=je(t,n,P);return se?pi(se):mi(P)}let M=v==="global"?"global":"chat",L=Mt(M,t,n,P);if(L)return pi(L,M==="global"?"From gateway-shared recipes (--global).":"From this chat only (--chat).");let A=M==="chat"&&Mt("global",t,n,P)?`
|
|
322
|
+
(Gateway-shared recipe exists: /run show --global ${P})`:"",K=M==="global"&&Mt("chat",t,n,P)?`
|
|
323
|
+
(This chat stores an override: /run show --chat ${P})`:"",X=M==="global"?`Unknown recipe "${P}" in gateway-shared storage.`:`Unknown recipe "${P}" in this chat storage.`;return p(`${X}${A}${K}`)}let c=/^add\b([\s\S]*)$/i.exec(i);if(c){let{scope:b,remainder:v}=Oi((c[1]??"").trim()),E=v.match(/^(\S+)\s+([\s\S]+)$/);if(!E)return p(b==="global"?'Usage: /run add --global <name> <command\u2026> [--template "\u2026"] \u2014 cmd must reference "$OMNISH_TASK"':"Usage: /run add [--global|-g|--chat|-p] <name> <command\u2026> \u2014 see /run help");try{let R=Ni(E[2],n.recipesMacroDefaultCommand);An(t,E[1],R,b);let P=gt(E[1]),M=P.ok?P.normalized:E[1].toLowerCase(),L=Mt(b,t,n,M)??je(t,n,M);return L?dr(M,L,b):p("Recipe save failed.")}catch(R){return p(String(R))}}let m=/^set\b([\s\S]*)$/i.exec(i);if(m){let{scope:b,remainder:v,explicit:E}=Li((m[1]??"").trim()),R=iu(v);if(R){if(E)return p("Cannot combine a leading scope flag (-g, --global, --chat, -p) with a trailing scope flag on the same line.");let L=gt(R.name);if(!L.ok)return p(L.error);let A=_i(t,L.normalized,R.target,n);if(!A.ok)return p(A.error);if(A.kind==="noop")return p(A.message);let K=Mt(A.target,t,n,L.normalized)??je(t,n,L.normalized);return K?dr(L.normalized,K,A.target):p("Recipe scope update failed.")}let P=v.match(/^(\S+)\s*$/);if(P?.[1]&&E){let L=gt(P[1]);if(!L.ok)return p(L.error);let A=_i(t,L.normalized,b,n);if(!A.ok)return p(A.error);if(A.kind==="noop")return p(A.message);let K=Mt(A.target,t,n,L.normalized)??je(t,n,L.normalized);return K?dr(L.normalized,K,A.target):p("Recipe scope update failed.")}let M=v.match(/^(\S+)\s+([\s\S]+)$/);if(!M)return p(b==="global"?'Usage: /run set --global <name> <command\u2026> [--template "\u2026"] \u2014 or scope-only: /run set --global <name> | /run set <name> -g':"Usage: /run set [--global|-g|--chat|-p] <name> <command\u2026> \u2014 or move scope without changing the body: /run set -g <name> | /run set <name> -p \u2014 see /run help");try{let L=Ni(M[2],n.recipesMacroDefaultCommand);An(t,M[1],L,b);let A=gt(M[1]),K=A.ok?A.normalized:M[1].toLowerCase(),X=Mt(b,t,n,K)??je(t,n,K);return X?dr(K,X,b):p("Recipe save failed.")}catch(L){return p(String(L))}}let f=/^(?:remove|rm|del)\b([\s\S]*)$/i.exec(i);if(f){let{scope:b,remainder:v}=Oi((f[1]??"").trim()),E=v.match(/^(\S+)\s*$/);if(!E?.[1])return p(b==="global"?"Usage: /run remove --global <name> (aliases: rm, del)":"Usage: /run remove [--global|-g|--chat|-p] <name>");let R=E[1];return lu(t,R,b)?Rc(R,b):b==="chat"&&Mt("global",t,n,R)?p(`No recipe "${R}" in this chat. There is a gateway-shared recipe with that name \u2014 remove it with:
|
|
324
|
+
/run remove --global ${R}`):Cc(R,b)}let h=/^queue\s+load\b([\s\S]*)$/i.exec(i);if(h){let b=(h[1]??"").trim(),v=Jd(n),E=null,R=/^json(?:\s+([\s\S]+))?$/i.exec(b);if(R){let A=(R[1]??"").trim();if(!A)return p('Usage: /run queue load json [{"recipe":"\u2026","task":"\u2026"}, \u2026] \u2014 or { "tasks": [ \u2026 ] }');E=A}else if(b.length>0){let A=b;(A.startsWith('"')&&A.endsWith('"')||A.startsWith("'")&&A.endsWith("'"))&&(A=A.slice(1,-1));let K=oe(t).cwd,X=await Rr(K,A);if(X.length===0)return p(`No files matched: ${A}`);if(X.length>1)return p("Queue load: specify a single JSON file.");let se=await Cr(X);if(!se.ok)return p(se.error);let he=ya(X[0],v);if(!he.ok)return p(he.error);E=he.text}else if(o){let A=ya(o,v);if(!A.ok)return p(A.error);E=A.text}else return p("Usage: /run queue load <file.json> \u2014 or /run queue load json [\u2026] \u2014 or attach a file with caption /run queue load");let P=Kd(E);if(!P.ok)return p(P.error);let M=zd(t,n,P.jobs);if(!M.ok)return p(M.error);let L=[];for(let A of M.items)L.push(r.enqueueQueuedRun(t,A,n));return p(L.join(`
|
|
325
|
+
`))}if(/^queue$/i.test(i))return p(r.runQueueStatus(t));if(/^queue\s+resume\s*$/i.test(i))return p(r.resumeRunQueue(t,n));let g=Gw(i);if(g){let{recipe:b,task:v,queued:E}=g,R=je(t,n,b);if(!R)return mi(b);if(R.steps&&R.steps.length>0){let X=oe(t).cwd,se=R.steps.length,he=R.steps;if(s?.sendToPeer){let D=s.sendToPeer;(async()=>{let ae=[],Y=!1;for(let x=0;x<he.length;x++){let O=he[x];if(Y){ae.push({index:x,label:O.label??`step ${x+1}`,cmd:O.cmd,exitCode:null,timedOut:!1,skipped:!0,output:""});continue}let F=await sn(n.shell,O.cmd,{timeoutMs:n.syncTimeoutMs,maxBytes:n.syncMaxBytes,cwd:X}),Q=[F.stdout,F.stderr].filter(Boolean).join(`
|
|
326
|
+
`).trim(),z=F.code===0&&!F.timedOut;ae.push({index:x,label:O.label??`step ${x+1}`,cmd:O.cmd,exitCode:F.code,timedOut:F.timedOut,skipped:!1,output:Q}),!z&&!O.continueOnFail&&(Y=!0)}let k=uu(b,ae);await D(t,k)})().catch(()=>{})}return p(`Runbook "${b}" started (${se} steps). Results will be sent when complete.`)}let P=R.taskEnv??"OMNISH_TASK";if(!Pn(R.command,P))return p(`Recipe "${b}" command must reference "$${P}".`);let M=vo(v,n.recipesMaxTaskChars);if(!M.ok)return p(M.error);let L=R.promptTemplate?xo(R.promptTemplate,P,M.task):M.task,A={[P]:L};if(E)return p(r.enqueueQueuedRun(t,{command:R.command,extraEnv:A,recipeLabel:b},n));let K=$o(b);return p(r.start(t,K,R.command,n,A))}let y=i.match(/^(\S+)$/);if(y){let b=y[1],v=b.toLowerCase();return v==="add"||v==="set"?p('Usage: /run add <name> cmd [--template "\u2026"] \u2014 cmd must include "$OMNISH_TASK"'):v==="show"?p("Usage: /run show <name> \u2014 optional show --global|-g|--chat|-p <name>"):v==="remove"||v==="rm"||v==="del"?p("Usage: /run remove [--global|-g|--chat|-p] <name>"):je(t,n,v)?p(`Usage: /run ${b} <task text\u2026> \u2014 replaces <<<OMNISH_TASK>>> / $OMNISH_TASK in stored templates`):p(`Unknown recipe "${b}". /run list`)}return p("/run: could not parse. /run help")}async function Kw(e,t,n){let r=e.trim();if(!r||/^help$/i.test(r))return Z(ui());let o=/^online\b([\s\S]*)$/i.exec(r);if(o)return Yn((o[1]??"").trim(),t,n,dp);let s=/^(\S+)\s+publish\b([\s\S]*)$/i.exec(r);if(s){let c=fp(t,s[1],s[2]??"");if(!c.ok)return p(c.error);let m=await Qn(c.body);return p(m.ok?m.message:m.error)}let i=/^list\b([\s\S]*)$/i.exec(r);if(i){let c=(i[1]??"").trim(),{filter:m,bad:f}=Wd(c);return f?p(`Unknown /shortcut list suffix: "${f}". Use: list | list --chat | list -p | list --global | list -g`):bc(Dd(t,m))}let a=/^show\b([\s\S]*)$/i.exec(r);if(a){let c=(a[1]??"").trim(),{mode:m,remainder:f}=Fd(c),h=/^(\S+)\s*$/i.exec(f);if(!h?.[1])return p("Usage: /shortcut show <name> \u2014 or show --global|-g|--chat|-p <name> (-g shared, -p private)");let g=h[1];if(m==="resolved"){let P=ws(t,g);if(!P)return Sc(g);let M=P.scope==="global"?"Shared shortcut (all chats use it unless this chat overrides the name).":"This chat only.";return di(g,P.body,M)}let y=m==="global"?"global":"chat",b=Et(y,t,g);if(b!==void 0)return di(g,b,y==="global"?"From the shared shortcut list (--global / -g).":"From this chat only (--chat / -p).");let v=y==="chat"&&Et("global",t,g)!==void 0?`
|
|
327
|
+
(Shared shortcut exists: /shortcut show --global ${g})`:"",E=y==="global"&&Et("chat",t,g)!==void 0?`
|
|
328
|
+
(This chat overrides the name: /shortcut show --chat ${g})`:"",R=y==="global"?`Unknown shortcut "${g}" in shared shortcuts.`:`Unknown shortcut "${g}" in this chat.`;return p(`${R}${v}${E}`)}let l=/^add\b([\s\S]*)$/i.exec(r);if(l){let{scope:c,remainder:m}=fa((l[1]??"").trim()),f=m.match(/^(\S+)\s+([\s\S]+)$/);if(!f)return p(c==="global"?"Usage: /shortcut add --global <name> <command\u2026> (short: add -g <name> <command\u2026>) \u2014 -g shared, -p private":"Usage: /shortcut add [--global|-g|--chat|-p] <name> <command\u2026> \u2014 -g shared, -p private");try{Tr(t,f[1],f[2],c);let h=wt(f[1]),g=h.ok?h.normalized:f[1].trim().toLowerCase(),y=Et(c,t,g)??"";return ur(g,y,c)}catch(h){return p(String(h))}}let d=/^set\b([\s\S]*)$/i.exec(r);if(d){let{scope:c,remainder:m,explicit:f}=ma((d[1]??"").trim()),h=Ud(m);if(h){if(f)return p("Cannot combine a leading scope flag (-g, --global, --chat, -p) with a trailing flag on the same line.");let b=wt(h.name);if(!b.ok)return p(b.error);let v=ga(t,b.normalized,h.target);if(!v.ok)return p(v.error);if(v.kind==="noop")return p(v.message);let E=Et(v.target,t,b.normalized)??"";return ur(b.normalized,E,v.target)}let g=m.match(/^(\S+)\s*$/);if(g?.[1]&&f){let b=wt(g[1]);if(!b.ok)return p(b.error);let v=ga(t,b.normalized,c);if(!v.ok)return p(v.error);if(v.kind==="noop")return p(v.message);let E=Et(v.target,t,b.normalized)??"";return ur(b.normalized,E,v.target)}let y=m.match(/^(\S+)\s+([\s\S]+)$/);if(!y)return p(c==="global"?"Usage: /shortcut set --global <name> <command\u2026> \u2014 or scope-only: /shortcut set -g <name> | /shortcut set <name> -g (-g shared)":"Usage: /shortcut set [--global|-g|--chat|-p] <name> <command\u2026> \u2014 or move scope: /shortcut set -g <name> | /shortcut set <name> -p (-g shared, -p private)");try{Tr(t,y[1],y[2],c);let b=wt(y[1]),v=b.ok?b.normalized:y[1].trim().toLowerCase(),E=Et(c,t,v)??"";return ur(v,E,c)}catch(b){return p(String(b))}}let u=/^(?:remove|rm|del)\b([\s\S]*)$/i.exec(r);if(u){let{scope:c,remainder:m}=fa((u[1]??"").trim()),f=m.match(/^(\S+)\s*$/);if(!f?.[1])return p(c==="global"?"Usage: /shortcut remove --global <name> (aliases: rm, del)":"Usage: /shortcut remove [--global|-g|--chat|-p] <name>");let h=f[1];return Hd(t,h,c)?kc(h,c):c==="chat"&&Et("global",t,h)!==void 0?p(`No shortcut "${h}" in this chat. There is a shared shortcut with that name \u2014 remove it with:
|
|
329
|
+
/shortcut remove --global ${h}`):vc(h,c)}return Z(ui())}async function zw(e,t,n,r,o){let s=e.slice(5).trim(),i=s.toLowerCase();if(!i||i==="help")return Z(fi());let a=s.match(/^(\S+)\s*(.*)$/s);if(!a)return Z(fi());let l=a[1].toLowerCase(),d=(a[2]??"").trim();if(l==="online")return Yn(d,t,n,cp);let u=d.match(/^publish\b([\s\S]*)$/i);if(u){let c=l,m=r.getSessionCommand(t,c);if(!m)return p(`No running session "${c}" with a command. /apps list`);let f=gp(c,m,u[1]??"");if(!f.ok)return p(f.error);let h=await Qn(f.body);return p(h.ok?h.message:h.error)}switch(l){case"start":{let c=d.match(/^(\S+)\s+([\s\S]+)$/);return c?p(r.start(t,c[1],c[2],n)):p("Usage: /apps start <name> <command\u2026>")}case"attach":{let c=d.split(/\s+/)[0];return c?p(r.attach(t,c)):p("Usage: /apps attach <name>")}case"detach":return p(r.detach(t));case"list":return p(r.list(t));case"info":case"get":{let c=d.split(/\s+/)[0];return p(r.info(t,c||void 0))}case"send":{let c=d.match(/^(\S+)\s+([\s\S]+)$/);return c?p(await r.sendText(t,c[1],c[2])):p("Usage: /apps send <name> <text\u2026>")}case"key":{let c=d.match(/^(\S+)\s+([\s\S]+)$/);return c?p(r.sendKey(t,c[1],c[2].trim())):p("Usage: /apps key <name> <KEY[,KEY\u2026]>")}case"tail":{let c=d.match(/^(\S+)(?:\s+(\d+))?\s*$/);if(!c)return p("Usage: /apps tail <name> [lines]");let m=c[2]?Number.parseInt(c[2],10):n.appsLogTailLines;return p(r.tail(t,c[1],m))}case"since":{let c=d.split(/\s+/)[0];if(!c)return p("Usage: /apps since <name>");let m=Dw(t,c),f=o.get(m)??0,{text:h,nextOffset:g}=r.readSince(t,c,f);return o.set(m,g),p(h.trimEnd()||"(no new log bytes)")}case"mute":{let c=d.split(/\s+/)[0];return c?p(r.mute(t,c)):p("Usage: /apps mute <name>")}case"unmute":{let c=d.split(/\s+/)[0];return c?p(r.unmute(t,c)):p("Usage: /apps unmute <name>")}case"raw":{let c=d.match(/^(\S+)\s+(on|off)\s*$/i);return c?p(r.setRaw(t,c[1],c[2].toLowerCase()==="on")):p("Usage: /apps raw <name> on|off")}case"resize":{let c=d.trim().split(/\s+/).filter(Boolean);if(c.length<3)return p("Usage: /apps resize <name> <cols> <rows>");let m=c[0],f=Number(c[1]),h=Number(c[2]);return!Number.isFinite(f)||!Number.isFinite(h)?p("cols and rows must be numbers."):p(r.resize(t,m,f,h))}case"stop":{let c=d.split(/\s+/)[0];return c?p(r.stop(t,c)):p("Usage: /apps stop <name>")}case"kill":{let c=d.split(/\s+/)[0];return c?p(r.kill(t,c)):p("Usage: /apps kill <name>")}case"rm":{let c=d.split(/\s+/)[0];return c?p(r.rm(t,c)):p("Usage: /apps rm <name>")}default:return p(`Unknown /apps subcommand "${l}". /apps help`)}}function Ep(e,t,n){return!e.clusterEnabled||Ta(t.trim())!==null?!0:n?Yc(n,e):!1}be();qe();function Pp(){return p("Not allowlisted on this omnish device.")}async function Ar(e,t,n,r,o,s,i,a,l,d,u,c){if((c?.surface??(s.peerKey.startsWith("tg:")?"telegram":"whatsapp"))==="telegram"){let f=Gr(e.telegramAllowFrom),h=s.peerKey.startsWith("tg:")?s.peerKey.slice(3):"";if(!h||!f.has(h)){T.warn({denied:s.peerKey,uid:h},"telegram denied"),await u({kind:"text",body:Pp()});return}}else{let f=jr(e.allowFrom),h=l.startsWith("wa:")&&/^wa:\+\d+$/.test(l)?l.slice(3):s.peerKey.replace(/^wa:/,""),g=ee(h)||"";if(!g||!f.has(g)){T.warn({denied:s.peerKey,phone:g,senderKey:l},"denied"),await u({kind:"text",body:Pp()});return}}try{if(!!!(s.mediaSavedPath||s.mediaError)&&!Ep(e,s.text,l))return;if(s.mediaError&&await u({kind:"text",body:p(s.mediaError)}),s.mediaSavedPath&&await u({kind:"text",body:p(`Saved: ${s.mediaSavedPath}`)}),s.text.trim()){let h=await Vn(e,t,n,r,o,s,i,a,l,!1,d);if(h!==null)if(h.kind==="texts")for(let g of h.bodies)await u({kind:"text",body:g});else await u(h)}}catch(f){T.error({err:String(f)},"inbound handler error"),await u({kind:"text",body:p(`Error: ${String(f)}`)}).catch(()=>{})}}function Yw(){if(process.env.OMNISH_BACKGROUND_GATEWAY==="1")try{Ea.readFileSync(le,"utf8").trim()===String(process.pid)&&Ea.unlinkSync(le)}catch{}}async function Ap(e){let t=new Tt,n=new Map,r=new Map,o=new Map,s=null,i=new Map,a=async(D,ae)=>{let Y=i.get(D);s?.sendReply(D,ae,Y)},l=async(D,ae)=>{let Y=D.startsWith("tg:")?"telegram":"whatsapp",k=i.get(D),x=Yi(D,{kind:"file",spec:ae},k,Y);s?.sendRoutedReply(D,x)},d=async(D,ae)=>{let Y=i.get(D),k=D.startsWith("tg:")?"telegram":"whatsapp";s?.sendReply(D,ke(p(ae),k).text,Y)},u={onPlainTextLlmFallback(D,ae){Ln($(),D,ae,Y=>a(D,Y))},sendToPeer:a},c=new Jt(()=>$(),a),m={async reload(){return{ok:!0,summary:"Attached mode: config reloaded from disk. Messengers run on the omnish platform; this device executes commands locally."}}};if(s=new ss({env:e,onReplyError:async(D,ae,Y)=>{let k=D.startsWith("tg:")?"telegram":"whatsapp";s?.sendReply(D,ke(p(`Error sending: ${ae}`),k).text,Y)},onMessage:async D=>{let ae=ea(),Y=D.peerKey;i.set(Y,D.messageId);let k=D.surface==="telegram"?"telegram":"whatsapp",x=D.senderE164&&k==="whatsapp"?`wa:${D.senderE164}`:Y;yn()&&T.info({peerKey:Y,senderKey:x,surface:k},"platform inbound message");let O=D.mediaSavedPath,F=D.mediaError;if(D.inboundMedia){let z=dd(ae,Y,D.inboundMedia);O=z.mediaSavedPath??O,F=z.mediaError??F}let Q={peerKey:Y,text:D.text,...O?{mediaSavedPath:O}:{},...F?{mediaError:F}:{}};await Ar(ae,t,n,r,o,Q,c,m,x,u,async z=>{try{if(z.kind==="texts"){for(let Ue of z.bodies){let Ie=ke(Ue,k);s?.sendReply(Y,Ie.text,D.messageId)}return}if(z.kind==="text"){let Ue=ke(z.body,k);s?.sendReply(Y,Ue.text,D.messageId);return}let we=Yi(Y,z,D.messageId,k);s?.sendRoutedReply(Y,we)}catch(we){s?.sendReply(Y,`Error sending file: ${String(we)}`,D.messageId)}},{surface:k})}}),process.env.OMNISH_BACKGROUND_GATEWAY==="1")try{Ea.writeFileSync(le,`${process.pid}
|
|
330
|
+
`,{mode:384})}catch(D){T.warn({err:String(D)},"could not write gateway pidfile")}Xo({getCfg:()=>$(),getWaOutbound:()=>null,getTgSendMedia:()=>null,getTgSendText:()=>null,sendPlatformMedia:l,sendPlatformText:d});let f=null,h=$();if(h.webhookEnabled){let D=h.webhookToken||qw.randomBytes(32).toString("hex");h.webhookToken||W({webhookToken:D}),f=Zo({port:h.webhookPort,host:h.webhookHost,token:D},{sendToPeer:a,getDefaultPeerKey:()=>null}).stop}let g=fs({getRunningVersion:Ze,getConfig:$,log:T}),y=Uo({getConfig:$,sendToPeer:a,sendMediaToPeer:l}),b=no({getConfig:$,sendToPeer:a}),{deviceId:v,account:E}=await s.connect(),R=await ns(e,E??null),P=5*60*1e3,M=setInterval(()=>{ns(e,null).catch(()=>{})},P);M.unref?.();let L=e.deviceId?.trim();if(L){L!==v&&T.warn({configuredDeviceId:L,registeredDeviceId:v},"platform_device_id does not match registered device id");try{await ta(e,L)}catch(D){T.warn({err:String(D)},"could not set platform default device")}}let A=R?.gatewayMode??E?.gatewayMode,K=R?.connectors.whatsapp??E?.connectors?.whatsapp,X=R?.connectors.telegram??E?.connectors?.telegram,se=[K?`whatsapp:${K.linked?"linked":K.status}`:null,X?`telegram:${X.linked?"linked":X.status}`:null].filter(Boolean).join(", ");console.error(`omnish attached to platform (device ${v})`+(A?` \u2014 gatewayMode=${A}`:"")+(se?` \u2014 ${se}`:""));let he=()=>{clearInterval(M),y(),b(),g?.(),f?.(),Yw(),Hn(),s?.stop(),c.dispose(),t.killAllRunning(),dn().stopAll().catch(()=>{}),console.error(`
|
|
331
|
+
${C(process.stderr,"shutting down\u2026")}`),process.exit(0)};process.on("SIGINT",he),process.on("SIGTERM",he),await new Promise(()=>{})}function Pa(e,t){return async n=>{if(t.surface==="whatsapp"&&t.waJid){if(n.kind==="file")await e.sendWaMedia?.(t.waJid,n.spec);else if(n.kind==="files")for(let r of n.specs)await e.sendWaMedia?.(t.waJid,r);else if(n.kind==="texts")for(let r of n.bodies)await e.sendWaText?.(t.waJid,ke(r,"whatsapp").text);else await e.sendWaText?.(t.waJid,ke(n.body,"whatsapp").text);return}if(t.surface==="telegram"&&e.sendTg)if(n.kind==="texts")for(let r of n.bodies)await e.sendTg({kind:"text",body:r});else await e.sendTg(n)}}pe();G();function Ms(){if(fe()){let i=$(),a=Rt(i);return lr(a)?{ok:!1,message:`Fix security errors before starting the gateway.
|
|
332
|
+
|
|
333
|
+
${a.filter(u=>u.severity==="error").map(u=>{let c=[u.message];return u.detail&&c.push(u.detail),u.fixHint&&c.push(`Fix: ${u.fixHint}`),c.join(`
|
|
334
|
+
`)}).join(`
|
|
335
|
+
|
|
336
|
+
`)}`}:{ok:!0}}let e=$(),t=e.gatewayMode,n=t==="whatsapp"||t==="both",r=t==="telegram"||t==="both",o=ve(e);if(r&&!o)return{ok:!1,message:`Telegram enabled (gatewayMode) but no bot token. Set telegramBotToken in config or TELEGRAM_BOT_TOKEN.
|
|
337
|
+
config: ${_}`};if(n&&!nt())return{ok:!1,message:"WhatsApp enabled but no session. Run `omnish link` first."};let s=Rt(e);return lr(s)?{ok:!1,message:`Fix security errors before starting the gateway (or change gatewayMode / token).
|
|
248
338
|
|
|
249
|
-
${s.filter(l=>l.severity==="error").map(l=>{let
|
|
339
|
+
${s.filter(l=>l.severity==="error").map(l=>{let d=[l.message];return l.detail&&d.push(l.detail),l.fixHint&&d.push(`Fix: ${l.fixHint}`),d.join(`
|
|
250
340
|
`)}).join(`
|
|
251
341
|
|
|
252
|
-
`)}`}:{ok:!0}}import
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
342
|
+
`)}`}:{ok:!0}}G();import As from"node:fs";import Is from"node:path";pe();import{spawn as Qw}from"node:child_process";import Ip from"node:fs";import{stdout as Vw}from"node:process";G();var Aa=["tunnelRelayUrl","platformToken","platformDeviceId"],Lp=[{label:"Platform (attached gateway + tunnel)",keys:["tunnelRelayUrl","platformToken","platformDeviceId"]},{label:"Tunnel",keys:["tunnelEnabled","tunnelMaxActive"]},{label:"Gateway",keys:["gatewayMode","commandPrefix","shell"]},{label:"Cluster",keys:["clusterEnabled","clusterRole","clusterLabel","clusterSenderBindings"]},{label:"Telegram",keys:["telegramBotToken"]},{label:"Apps",keys:["appsCols","appsRows","appsFlushMs","appsMinIntervalMs","appsMaxFlushBytes","appsMaxSessions","appsMaxSessionsTotal","appsMaxWaChars","appsLogTailLines","appsSubmitDelayMs","appsClearInput","appsClearInputDelayMs","appsClearInputSequence","appsSkipClearOnPasswordPrompt","appsPasswordPromptHint"]},{label:"Files",keys:["fileSendMaxBytes","fileReceiveMaxBytes","fileInboxSubdir","fileReceiveRootMode","fileReceiveRootPath"]},{label:"Recipes",keys:["recipesAllowDangerousBuiltins","recipesMaxTaskChars","recipesMacroDefaultCommand"]},{label:"Sync / jobs",keys:["syncTimeoutMs","syncMaxBytes","jobLogTailLines"]},{label:"Service / updates",keys:["serviceInstallFromChat","updateCheckEnabled","updateCheckIntervalMs","updateCheckPackageName","updateInfoUrl"]},{label:"Chat LLM fallback",keys:["chatLlmFallbackEnabled","chatLlmShellCommand","chatLlmTimeoutMs","chatLlmMaxInputChars","chatLlmMaxOutputChars","chatLlmNeedsTty","chatLlmWorkDir"]}];function Es(e){let t=[J(e,"omnish config"),w(e,"Manage ~/.omnish/config.json (env vars still override for platform credentials)."),"",J(e,"Usage:"),` ${S(e,"omnish config add <key> <value> [key value ...]")}`,` ${S(e,"omnish config show <key>[,<key>|*]")}`,` ${S(e,"omnish config edit")}`,` ${S(e,"omnish config edit <key> <value>")}`,` ${S(e,"omnish config delete <key>[,<key>]")}`,"",w(e,"Platform aliases: platform_url \u2192 tunnelRelayUrl, platform_token \u2192 platformToken,"),w(e," platform_device_id \u2192 platformDeviceId"),` ${S(e,"omnish config show platform")} ${w(e,"\u2014 attached-mode diagnostics (URL, token, devices)")}`,w(e," Full setup: omnish help platform"),w(e,"add sets/overwrites scalar keys (not array append). show * lists all keys."),""];console.log(t.join(`
|
|
343
|
+
`))}function Op(e){return e.split(/[,\s]+/).map(t=>t.trim()).filter(Boolean)}function Ia(e){if(e.length===0)return[];let t=e.join(" ");if(t.includes(",")){let n=[];for(let r of t.split(",")){let o=r.trim();if(!o)continue;let s=o.split(/\s+/);if(s.length<2)throw new Error(`Invalid pair "${o}" \u2014 expected "key value"`);n.push({key:s[0],value:s.slice(1).join(" ")})}return n}if(e.length===2)return[{key:e[0],value:e[1]}];if(e.length%2===0){let n=[];for(let r=0;r<e.length;r+=2)n.push({key:e[r],value:e[r+1]});return n}return[{key:e[0],value:e.slice(1).join(" ")}]}function Ts(e,t){if(e==="clusterSenderBindings")return JSON.stringify(t.clusterSenderBindings);let n=String(t[e]??"");return e==="platformToken"||e==="telegramBotToken"?tn(e,n):n}function Np(e){return e==="tunnelRelayUrl"?{value:$r(),source:zn()}:e==="platformToken"?{value:tn(e,ca()),source:Kn()}:e==="platformDeviceId"?{value:(ua()??"")||"(empty)",source:da()}:null}function Xw(e){if(e!=="platformToken"&&e!=="tunnelRelayUrl")return;let n=$().platformToken.trim()||xt()?.token?.trim()||"";n&&ft({token:n,relayUrl:$r()})}async function Zw(){let e=process.env.VISUAL?.trim()||process.env.EDITOR?.trim()||(process.platform==="win32"?"notepad":"nano"),t=Ip.readFileSync(_,"utf8");await new Promise((n,r)=>{let o=Qw(e,[_],{stdio:"inherit"});o.on("error",r),o.on("exit",s=>{s===0?n():r(new Error(`Editor exited with code ${s??1}`))})});try{$()}catch(n){throw Ip.writeFileSync(_,t,{mode:384}),new Error(`Invalid config after edit: ${n instanceof Error?n.message:String(n)}`)}}async function _p(e){let t=Vw,n=process.stderr,r=(e[0]??"").trim().toLowerCase(),o=e.slice(1);if(!r||r==="help"||r==="--help"||r==="-h"){Es(t);return}if(r==="add"||r==="edit"){try{if(r==="edit"&&o.length===0){await Zw(),console.log(B(t,`Updated ${_}`));return}let s=Ia(o);if(s.length===0){console.error(C(n,"Expected at least one key/value pair.")),process.exitCode=1;return}for(let{key:i,value:a}of s){let l=po(i);if(!l){console.error(C(n,`Unknown key "${i}". Try: ${_c().slice(0,8).join(", ")}\u2026 (omnish config show *)`)),process.exitCode=1;return}Rn(l,a),Xw(l);let d=$(),u=l==="tunnelRelayUrl"?d.tunnelRelayUrl:Aa.includes(l)?tn(l,String(d[l]??"")):Ts(l,d);console.log(B(t,`${i} \u2192 ${u}`))}console.log(B(t,_))}catch(s){console.error(C(n,s instanceof Error?s.message:String(s))),process.exitCode=1}return}if(r==="show"){let s=o.join(" ").trim()||"*";if(s==="*"){let l=$(),d=[J(t,"Config"),w(t,_),""];for(let m of Lp){d.push(J(t,m.label));for(let f of m.keys){let h=Ts(f,l),g=Np(f);g&&Aa.includes(f)?d.push(` ${S(t,f)}: ${h} ${w(t,`(effective: ${g.value}, source: ${g.source})`)}`):d.push(` ${S(t,f)}: ${h}`)}d.push("")}let u=new Set(Lp.flatMap(m=>m.keys)),c=hi.filter(m=>!u.has(m));if(c.length>0){d.push(J(t,"Other"));for(let m of c)d.push(` ${S(t,m)}: ${Ts(m,l)}`)}console.log(d.join(`
|
|
344
|
+
`));return}let i=Op(s),a=$();for(let l of i){let d=po(l);if(!d){console.error(C(n,`Unknown key "${l}".`)),process.exitCode=1;return}let u=Ts(d,a),c=Np(d);c&&Aa.includes(d)?console.log(`${l}: ${u} (effective: ${c.value}, source: ${c.source})`):console.log(`${l}: ${u}`)}return}if(r==="delete"){let s=o.join(" ").trim();if(!s){console.error(C(n,"Expected at least one key to delete.")),process.exitCode=1;return}if(s==="*"){console.error(C(n,'Refusing "delete *". List keys explicitly.')),process.exitCode=1;return}try{for(let i of Op(s)){let a=po(i);if(!a){console.error(C(n,`Unknown key "${i}".`)),process.exitCode=1;return}Fc(a),console.log(B(t,`cleared ${i}`))}console.log(B(t,_))}catch(i){console.error(C(n,i instanceof Error?i.message:String(i))),process.exitCode=1}return}console.error(C(n,`Unknown subcommand "${r}".`)),Es(t),process.exitCode=1}async function Fp(e){let t=fe(),n=[];if(n.push(J(e,"platform")),!t){n.push(` ${w(e,"attached:")} ${me(e,"no")} \u2014 set platform_url + platform_token (omnish config add) or env`);let a=zn(),l=Kn();return(a==="env"||l==="env")&&n.push(` ${w(e,"note:")} ${V(e,"partial env override (need URL + token)")}`),n}let r=zn(),o=Kn(),s=da(),i=r==="env"||o==="env"||s==="env"?` ${w(e,"(env overrides config)")}`:"";n.push(` ${w(e,"attached:")} ${S(e,"yes")}${i}`),n.push(` ${w(e,"url:")} ${S(e,t.platformUrl)} ${V(e,`[${r}]`)}`),n.push(` ${w(e,"token:")} ${S(e,tn("platformToken",t.token))} ${V(e,`[${o}]`)}`),t.deviceId&&n.push(` ${w(e,"device:")} ${S(e,t.deviceId)} ${V(e,`[${s}]`)}`);try{let{fetchPlatformAccount:a}=await Promise.resolve().then(()=>(rs(),hd)),l=await a(t);n.push(` ${w(e,"gatewayMode:")} ${S(e,l.gatewayMode)} ${V(e,"(platform)")}`);let d=c=>{let m=l.connectors[c];return m?m.linked?S(e,"linked"):V(e,m.status):me(e,"idle")};n.push(` ${w(e,"whatsapp:")} ${d("whatsapp")}`),n.push(` ${w(e,"telegram:")} ${d("telegram")}`);let u=l.routing.onlineCount;n.push(` ${w(e,"routing:")} default=${l.routing.defaultDeviceId??V(e,"(none)")} online=${u}`),t.deviceId&&l.routing.defaultDeviceId&&t.deviceId!==l.routing.defaultDeviceId&&n.push(` ${w(e,"warn:")} ${me(e,"platform_device_id \u2260 platform defaultDeviceId \u2014 run omnish run or set default on dashboard")}`),u===0&&n.push(` ${w(e,"warn:")} ${me(e,"no device online on platform")}`)}catch{n.push(` ${w(e,"account:")} ${V(e,"(could not fetch /v1/me \u2014 check token and URL)")}`)}return n}rs();function Up(){let e=fe();return e||(console.error(C(process.stderr,"Set platform_url + platform_token (omnish config add) or OMNISH_PLATFORM_URL + OMNISH_TOKEN.")),process.exitCode=1,null)}function eb(e){let t=e.trim();return t?t.startsWith("wa:")?{kind:"wa",value:t.slice(3).trim()}:t.startsWith("tg:")?{kind:"tg",value:t.slice(3).trim()}:/^\+?\d{8,}$/.test(t.replace(/\s/g,""))?{kind:"wa",value:t}:/^\d+$/.test(t)?{kind:"tg",value:t}:null:null}function Wp(e){return e.replace(/\D/g,"")}async function Dp(){let e=Up();if(!e)return;let t=await Bn(e),n=t.connectors.whatsapp,r=t.connectors.telegram;console.log(B(process.stdout,"Platform account")),console.log(` gatewayMode: ${t.gatewayMode}`),console.log(` whatsapp: ${n?.linked?"linked":n?.status??"idle"} (allowlist: ${t.allowFrom.length})`);let o=r?.tokenConfigured?"token saved":"no token";console.log(` telegram: ${r?.linked?"linked":r?.status??"idle"} (${o}, allowlist: ${t.telegramAllowFrom.length})`),console.log(` defaultDeviceId: ${t.defaultDeviceId??"\u2014"}`),console.log(` online devices: ${t.routing.onlineCount}`),t.allowFrom.length&&console.log(` allowFrom: ${t.allowFrom.map(s=>`+${s}`).join(", ")}`),t.telegramAllowFrom.length&&console.log(` telegramAllowFrom: ${t.telegramAllowFrom.join(", ")}`)}async function Hp(e){let t=Up();if(!t)return;let n=(e[0]??"").toLowerCase();if(!n||n==="-h"||n==="--help"){console.log(["Usage:"," omnish platform allow list"," omnish platform allow add wa:+15551234567 tg:123456789"," omnish platform allow set --wa +1555,... --tg 123,...",""].join(`
|
|
345
|
+
`));return}if(n==="list"){let r=await Bn(t);console.log(B(process.stdout,"Platform allowlists")),console.log(` WhatsApp (${r.allowFrom.length}): ${r.allowFrom.length?r.allowFrom.map(o=>`+${o}`).join(", "):"(empty \u2014 any sender)"}`),console.log(` Telegram (${r.telegramAllowFrom.length}): ${r.telegramAllowFrom.length?r.telegramAllowFrom.join(", "):"(empty \u2014 any sender)"}`);return}if(n==="add"){let r=await Bn(t),o=[...r.allowFrom],s=[...r.telegramAllowFrom];for(let a of e.slice(1)){let l=eb(a);if(!l){console.error(C(process.stderr,`Unrecognized entry: ${a}`)),process.exitCode=1;return}if(l.kind==="wa"){let d=Wp(l.value);d&&!o.includes(d)&&o.push(d)}else{let d=l.value.replace(/\D/g,"");d&&!s.includes(d)&&s.push(d)}}let i=await ts(t,{allowFrom:o,telegramAllowFrom:s});console.log(B(process.stdout,`Allowlists updated (${i.allowFrom.length} WhatsApp, ${i.telegramAllowFrom.length} Telegram).`));return}if(n==="set"){let r,o;for(let a=1;a<e.length;a++){let l=e[a];l==="--wa"&&e[a+1]?r=e[++a].split(",").map(d=>Wp(d.trim())).filter(Boolean):l==="--tg"&&e[a+1]&&(o=e[++a].split(",").map(d=>d.trim().replace(/^tg:/i,"").replace(/\D/g,"")).filter(Boolean))}if(r===void 0&&o===void 0){console.error(C(process.stderr,"Use --wa and/or --tg with comma-separated values.")),process.exitCode=1;return}let s={};r!==void 0&&(s.allowFrom=r),o!==void 0&&(s.telegramAllowFrom=o);let i=await ts(t,s);console.log(B(process.stdout,`Allowlists set (${i.allowFrom.length} WhatsApp, ${i.telegramAllowFrom.length} Telegram).`));return}console.error(C(process.stderr,`Unknown allow subcommand: ${e[0]}`)),process.exitCode=1}async function Bp(e){let t="",n="",r="";for(let o=0;o<e.length;o++){let s=e[o];(s==="--url"||s==="-u")&&e[o+1]?t=e[++o].trim():(s==="--token"||s==="-t")&&e[o+1]?n=e[++o].trim():s==="--device-id"&&e[o+1]&&(r=e[++o].trim())}if(!t||!n){let o=Ia(e);for(let s of o)s.key==="platform_url"&&(t=s.value),s.key==="platform_token"&&(n=s.value),s.key==="platform_device_id"&&(r=s.value)}if(!t||!n){console.error(C(process.stderr,"Usage: omnish platform login --url <url> --token <token> [--device-id <id>]")),process.exitCode=1;return}Rn("tunnelRelayUrl",t.replace(/\/$/,"")),Rn("platformToken",n),r&&Rn("platformDeviceId",r),console.log(B(process.stdout,"Platform credentials saved to config.json.")),console.log(" Run: omnish platform status && omnish run")}import tb from"qrcode-terminal";var nb=1e3,rb=120;function jp(){let e=fe();return e||(console.error(C(process.stderr,"Set platform_url + platform_token (omnish config add) or OMNISH_PLATFORM_URL + OMNISH_TOKEN.")),process.exitCode=1,null)}function Oa(e){let t=e.replace(/\/$/,"");return/^https?:\/\//i.test(t)?t:`http://${t}`}async function Gp(e){let t=`${Oa(e.platformUrl)}/v1/connectors/whatsapp/status`,n=await fetch(t,{headers:{Authorization:`Bearer ${e.token}`}}),r=await n.json().catch(()=>({}));if(!n.ok)throw new Error(r.error||`HTTP ${n.status}`);return r}async function ob(e){let t=await fetch(`${Oa(e.platformUrl)}/v1/connectors/whatsapp/start`,{method:"POST",headers:{"content-type":"application/json",Authorization:`Bearer ${e.token}`},body:JSON.stringify({})}),n=await t.json().catch(()=>({}));if(!t.ok)throw new Error(n.error||`HTTP ${t.status}`);return n}function Jp(e){let t=process.stdout,n=w(t,"\xB7".repeat(42));console.log(V(t,"Scan with WhatsApp \u2192 Linked devices")),console.log(n),tb.generate(e,{small:!0}),console.log(n)}var Ps="",La=!1;function sb(e){let t=e.status??"";return t==="qr"||t==="connecting"||t==="reconnecting"||t==="pairing_restart"}async function ib(e){for(let t=0;t<rb;t++){let n=await Gp(e);if(n.status==="linked"||n.linked)return n;if(n.status==="pairing_restart"&&!La&&(La=!0,console.log(w(process.stdout,"Finishing WhatsApp link after scan (server restart) \u2014 wait a few seconds\u2026"))),n.qr&&n.qr!==Ps&&(Ps=n.qr,Jp(n.qr)),!sb(n))throw new Error(n.error||n.statusMessage||`Unexpected status: ${n.status}`);await new Promise(r=>setTimeout(r,nb))}throw new Error("Timed out waiting for WhatsApp to link (scan the QR within ~2 minutes).")}async function Kp(){let e=jp();if(!e)return;let t=await Gp(e);if(t.status==="linked"||t.linked){console.log(B(process.stdout,"WhatsApp already linked on the platform.")),t.linkedPhoneE164&&(console.log(` Phone: +${t.linkedPhoneE164}`),console.log(` Hint: omnish platform allow add wa:+${t.linkedPhoneE164}`));return}console.log(B(process.stdout,"Starting WhatsApp pairing on the platform\u2026")),La=!1,Ps="",t=await ob(e),t.qr&&(Jp(t.qr),Ps=t.qr),t=await ib(e),console.log(B(process.stdout,"WhatsApp linked on the platform.")),t.linkedPhoneE164&&(console.log(` Phone: +${t.linkedPhoneE164}`),console.log(` Next: omnish platform allow add wa:+${t.linkedPhoneE164}`),console.log(" Then: omnish run"))}async function zp(){let e=jp();if(!e)return;let t=await fetch(`${Oa(e.platformUrl)}/v1/connectors/whatsapp/unlink`,{method:"POST",headers:{"content-type":"application/json",Authorization:`Bearer ${e.token}`},body:JSON.stringify({})}),n=await t.json().catch(()=>({}));if(!t.ok){console.error(C(process.stderr,n.error||`HTTP ${t.status}`)),process.exitCode=1;return}console.log(B(process.stdout,`WhatsApp unlinked (status: ${n.status??"idle"}).`))}import ab from"ws";function qp(e,t,n){return new Promise(r=>{let o=new ab(e,{headers:{Authorization:`Bearer ${t}`}}),s=setTimeout(()=>{o.terminate(),r({ok:!1,message:"timeout"})},n);o.once("open",()=>{clearTimeout(s),o.close(),r({ok:!0})}),o.once("unexpected-response",(i,a)=>{clearTimeout(s),r({ok:!1,status:a.statusCode,message:`HTTP ${a.statusCode} ${a.statusMessage}`})}),o.once("error",i=>{clearTimeout(s),r({ok:!1,message:String(i?.message??i)})})})}async function Yp(e,t,n=12e3){let r=e.replace(/\/$/,""),o=jn(r),s=await qp(o,t,n),i,a,l="";for(let d of os(r)){let u=await qp(d,t,n);if(u.ok)return i=new URL(d).pathname,{ok:!0,controlWsOk:s.ok,deviceWsOk:!0,deviceWsPath:i};a=u.status,l=u.message}return a===400?{ok:!1,controlWsOk:s.ok,deviceWsOk:!1,deviceWsStatus:400,error:"Device WebSocket returned HTTP 400 on /platform/device and could not use /control/device. Redeploy the latest relay image (adds /control/device), or route /platform/* to port 8788 in Caddy/EasyPanel."}:{ok:!1,controlWsOk:s.ok,deviceWsOk:!1,deviceWsStatus:a,error:`Device WebSocket failed: ${l}`}}var lb=400,cb=8*1024*1024,ub=2*1024*1024;function db(e){let t=ne,n=!1,r=!1;for(let o=0;o<e.length;o++){let s=e[o];s==="-h"||s==="--help"?r=!0:s==="--force"?n=!0:(s==="--auth-dir"||s==="--authDir")&&e[o+1]&&(t=Is.resolve(e[++o]))}return{authDir:t,force:n,help:r}}function pb(e){let t={},n=0,r=0;function o(s,i){let a;try{a=As.readdirSync(i,{withFileTypes:!0})}catch{return}for(let l of a){let d=l.name;if(d==="."||d==="..")continue;let u=Is.join(i,d),m=(s?`${s}/${d}`:d).split(Is.sep).join("/");if(!l.isSymbolicLink()){if(l.isDirectory())o(m,u);else if(l.isFile()){let f=As.readFileSync(u);if(f.length>ub)throw new Error(`file too large: ${m}`);if(n+=f.length,n>cb)throw new Error("total auth size exceeds limit (8 MiB)");if(r+=1,r>lb)throw new Error("too many files (max 400)");t[m]=f.toString("base64")}}}}return o("",e),t}function Ls(e){console.log([ce(e,"omnish platform"),w(e,"Attached mode: messengers on the hosted platform; shell on this machine (omnish run)."),"",J(e,"When to use"),w(e," Link WhatsApp/Telegram once on the platform dashboard. Run omnish run on laptops, servers, or containers without local Baileys."),"",J(e,"Prerequisites"),w(e," 1. Platform account token (signup/login on relay dashboard)."),w(e," 2. Messengers linked on the platform (dashboard QR or import-whatsapp)."),w(e," 3. allowFrom / telegramAllowFrom on dashboard Allowlists (Telegram: DM bot /id for your user id)."),"",J(e,"Configure"),` ${S(e,"omnish config add platform_url <url> platform_token <token>")}`,w(e," Optional: platform_device_id <id>"),w(e," Env overrides: OMNISH_PLATFORM_URL, OMNISH_TOKEN, OMNISH_DEVICE_ID"),w(e," (aliases: OMNISH_COMM_LAYER_URL, OMNISH_DEVICE_TOKEN, OMNISH_TUNNEL_*)"),"",J(e,"Workflow"),` ${S(e,"omnish platform login --url <url> --token <t>")} ${w(e,"\u2014 save credentials (or config add)")}`,` ${S(e,"omnish platform status")} ${w(e,"\u2014 account, connectors, allowlists")}`,` ${S(e,"omnish platform allow add tg:<id>")} ${w(e,"\u2014 or dashboard + bot /id")}`,` ${S(e,"omnish platform probe")} ${w(e,"\u2014 test WebSocket routing")}`,` ${S(e,"omnish run")} ${w(e,"\u2014 attach device; expect 'omnish attached to platform'")}`,"",J(e,"Subcommands"),S(e,"omnish platform login --url <url> --token <token> [--device-id <id>]"),w(e," Save platform URL and token to config.json."),"",S(e,"omnish platform status"),w(e," Show gatewayMode, connector status, allowlists, online devices."),"",S(e,"omnish platform allow list|add|set"),w(e," Manage platform allowlists (PUT /v1/me/allowlists)."),"",S(e,"omnish platform probe"),w(e," Test control-plane WebSocket routing (diagnose attached omnish run 400 errors)."),"",S(e,"omnish platform link-whatsapp"),w(e," Pair WhatsApp on the platform (terminal QR + poll until linked)."),"",S(e,"omnish platform unlink-whatsapp"),w(e," Remove platform WhatsApp session and auth files."),"",S(e,"omnish platform import-whatsapp [--auth-dir <path>] [--force]"),w(e," Upload local Baileys auth (after omnish link on this host). Use link-whatsapp or dashboard QR when possible."),w(e," Stop local omnish run before import to avoid WhatsApp session conflicts."),"",w(e,"Standalone omnish link on this host is not used for platform WhatsApp when attached."),w(e,"Docs: docs/guides/platform-reference.md (full API/CLI)"),w(e," docs/guides/platform-attached-mode.md (walkthrough)"),""].join(`
|
|
346
|
+
`))}async function mb(e){let{authDir:t,force:n,help:r}=db(e);if(r){console.log(["Usage: omnish platform import-whatsapp [--auth-dir <path>] [--force]","","Requires OMNISH_PLATFORM_URL and OMNISH_TOKEN.","Default auth directory: ~/.omnish/auth (Baileys useMultiFileAuthState).","Stop `omnish run` on this machine before importing to avoid WhatsApp session conflicts.",""].join(`
|
|
347
|
+
`));return}let o=fe();if(!o){console.error(C(process.stderr,"Set OMNISH_PLATFORM_URL (control plane URL) and OMNISH_TOKEN (from dashboard signup/login).")),process.exitCode=1;return}if(!As.existsSync(t)){console.error(C(process.stderr,`Auth directory not found: ${t}`)),process.exitCode=1;return}te();let s=Is.join(t,"creds.json");if(!As.existsSync(s)){console.error(C(process.stderr,`No creds.json in ${t} \u2014 link WhatsApp locally first (omnish link).`)),process.exitCode=1;return}let i;try{i=pb(t)}catch(f){console.error(C(process.stderr,String(f))),process.exitCode=1;return}if(Object.keys(i).length===0){console.error(C(process.stderr,"No files collected (empty auth directory?).")),process.exitCode=1;return}let a=o.platformUrl.replace(/\/$/,""),d=`${/^https?:\/\//i.test(a)?a:`http://${a}`}/v1/connectors/whatsapp/import`,u=await fetch(d,{method:"POST",headers:{"content-type":"application/json",Authorization:`Bearer ${o.token}`},body:JSON.stringify({files:i,force:n})}),c=await u.json().catch(()=>({}));if(!u.ok){console.error(C(process.stderr,c.error||`HTTP ${u.status}`)),process.exitCode=1;return}let m=c.qr?" QR emitted \u2014 open the platform dashboard if you need to scan.":"";console.log(B(process.stdout,`Uploaded Baileys auth. Platform connector status: ${c.status??"unknown"}.${m}`))}async function fb(){let e=process.stdout,t=process.stderr,n=fe();if(!n){console.error(C(t,"Set platform_url + platform_token (omnish config add) or OMNISH_PLATFORM_URL + OMNISH_TOKEN.")),process.exitCode=1;return}console.log(`${ce(e,"Platform probe")} ${w(e,n.platformUrl)}`);let r=await Yp(n.platformUrl,n.token);console.log(` ${w(e,"wss /control:")} ${r.controlWsOk?S(e,"ok"):me(e,"fail")}`);for(let o of["/platform/device","/control/device"]){if(r.ok&&r.deviceWsPath&&r.deviceWsPath!==o){console.log(` ${w(e,`wss ${o}:`)} ${V(e,"skipped (fallback used)")}`);continue}if(r.ok&&r.deviceWsPath===o){console.log(` ${w(e,`wss ${o}:`)} ${S(e,"ok")}`);continue}!r.ok&&o==="/platform/device"&&console.log(` ${w(e,`wss ${o}:`)} ${me(e,"fail")}${r.deviceWsStatus?` (HTTP ${r.deviceWsStatus})`:""}`),!r.ok&&o==="/control/device"&&console.log(` ${w(e,`wss ${o}:`)} ${me(e,"fail")}`)}if(r.ok){r.deviceWsPath==="/control/device"&&console.log(w(e," Using /control/device fallback (/platform/* not on control port \u2014 redeploy Caddy when convenient).")),console.log(B(e,"Attached omnish run should be able to connect."));return}console.error(C(t,r.error??"Platform probe failed.")),r.controlWsOk&&!r.deviceWsOk&&console.error(C(t,"Fix: redeploy the latest tunnel-relay image (enables wss /control/device), update omnish CLI, then retry. Also route /platform/* to 8788 when you can (Caddyfile.standalone).")),process.exitCode=1}async function Qp(e){let t=(e[0]??"").toLowerCase();if(!t||t==="-h"||t==="--help"){Ls(process.stdout);return}if(t==="login"){await Bp(e.slice(1));return}if(t==="status"){await Dp();return}if(t==="allow"){await Hp(e.slice(1));return}if(t==="probe"){await fb();return}if(t==="link-whatsapp"){await Kp();return}if(t==="unlink-whatsapp"){await zp();return}if(t==="import-whatsapp"){await mb(e.slice(1));return}console.error(C(process.stderr,`Unknown platform subcommand: ${e[0]}`)),Ls(process.stderr),process.exitCode=1}G();import Zp from"node:crypto";import fn from"node:fs";import em from"node:path";function hb(){return Zp.randomBytes(24).toString("hex")}function Vp(){return Zp.randomBytes(32).toString("hex")}function Xp(e){try{let t=fn.readFileSync(e,"utf8"),n=JSON.parse(t);if(typeof n.token=="string"&&n.token.length>=16&&typeof n.secret=="string"&&n.secret.length>=16)return{token:n.token,secret:n.secret}}catch{}return null}function gb(){let e=Xp(ct);if(e)return e;if(e=Xp(er),e){j(em.dirname(ct)),fn.writeFileSync(ct,JSON.stringify(e,null,2)+`
|
|
348
|
+
`,{mode:384});try{fn.unlinkSync(er)}catch{}return e}return null}function tm(e){j(em.dirname(ct));let t=gb(),n=(e??"").trim();if(n){let o={token:n,secret:t?.secret??Vp()};fn.writeFileSync(ct,JSON.stringify(o,null,2)+`
|
|
349
|
+
`,{mode:384});try{fn.existsSync(er)&&fn.unlinkSync(er)}catch{}return o}if(t)return t;let r={token:hb(),secret:Vp()};return fn.writeFileSync(ct,JSON.stringify(r,null,2)+`
|
|
350
|
+
`,{mode:384}),r}pe();import Vt from"node:fs";import vb from"node:http";import Ae from"node:path";import it from"node:process";import{fileURLToPath as xb}from"node:url";import $b from"node:os";qe();G();import Na from"node:crypto";var _a="omnish_cfg_sess",nm=7*24*60*60*1e3;function Fa(e){let t=Date.now()+nm,n=Buffer.from(JSON.stringify({exp:t}),"utf8").toString("base64url"),r=Na.createHmac("sha256",e).update(n).digest("hex");return`${n}.${r}`}function rm(e,t){let n=yb(t??"")[_a];if(!n||!n.includes("."))return!1;let r=n.lastIndexOf("."),o=n.slice(0,r),s=n.slice(r+1),i=Na.createHmac("sha256",e).update(o).digest("hex");try{let a=Buffer.from(s,"hex"),l=Buffer.from(i,"hex");if(a.length!==l.length||!Na.timingSafeEqual(a,l))return!1}catch{return!1}try{let a=JSON.parse(Buffer.from(o,"base64url").toString("utf8"));return!(typeof a.exp!="number"||a.exp<Date.now())}catch{return!1}}function Wa(e){let t=Math.floor(nm/1e3);return`${_a}=${e}; HttpOnly; Path=/; SameSite=Lax; Max-Age=${t}`}function om(){return`${_a}=; HttpOnly; Path=/; SameSite=Lax; Max-Age=0`}function yb(e){let t={};for(let n of e.split(";")){let r=n.indexOf("=");if(r===-1)continue;let o=n.slice(0,r).trim(),s=n.slice(r+1).trim();o&&(t[o]=decodeURIComponent(s))}return t}G();import Ua from"node:fs";import Os from"node:process";function sm(e){try{return Os.kill(e,0),!0}catch{return!1}}function im(e){let t=Date.now()+e;for(;Date.now()<t;);}function wb(){try{let e=Ua.readFileSync(Fr,"utf8"),t=JSON.parse(e);if(!t||typeof t!="object")return null;let n=t,r=typeof n.pid=="number"?n.pid:Number(n.pid),o=typeof n.port=="number"?n.port:Number(n.port),s=typeof n.host=="string"?n.host:"",i=typeof n.startedAt=="string"?n.startedAt:"";return!Number.isFinite(r)||r<=0||!Number.isFinite(o)||o<=0||o>65535?null:{pid:r,port:o,host:s,startedAt:i}}catch{return null}}function am(e){Ua.writeFileSync(Fr,`${JSON.stringify(e)}
|
|
351
|
+
`,{mode:384})}function Ir(){try{Ua.unlinkSync(Fr)}catch{}}function lm(e){let t=wb();if(t&&t.port===e&&t.pid!==Os.pid){if(!sm(t.pid)){Ir();return}try{Os.kill(t.pid,"SIGTERM")}catch{}if(im(350),sm(t.pid)){try{Os.kill(t.pid,"SIGKILL")}catch{}im(100)}Ir()}}pe();G();import Da from"node:fs";import bb from"node:process";function kb(e){try{return bb.kill(e,0),!0}catch{return!1}}function Sb(){try{let e=Da.readFileSync(le,"utf8").trim(),t=Number.parseInt(e,10);return Number.isFinite(t)&&t>0?t:null}catch{return null}}function Ns(){let e=Sb();return e===null?!1:kb(e)}var Ha=class{busy=!1;subscribers=new Set;abort=null;currentSock=null;subscribe(t){return this.subscribers.add(t),()=>{this.subscribers.delete(t)}}emit(t){for(let n of this.subscribers)try{n(t)}catch{}}requestCancel(){this.abort?.abort(),this.currentSock&&(Wn(this.currentSock),this.currentSock=null)}beginPairing(t){if(this.busy)throw new Error("Pairing already in progress.");if(Ns())throw new Error("Gateway appears to be running (gateway.pid). Stop `omnish run` or your service before pairing from the browser.");if(!t.force&&nt())throw new Error("WhatsApp session already linked. Use \u201CReplace session\u201D or run `omnish link --force` from the CLI.");te(),t.force&&(Da.rmSync(ne,{recursive:!0,force:!0}),Da.mkdirSync(ne,{recursive:!0,mode:448})),this.busy=!0,this.abort=new AbortController;let n=this.abort.signal;(async()=>{try{await rd({authDir:ne,verbose:!1,printQr:!1,signal:n,onQr:r=>this.emit({type:"qr",payload:r}),onRestart515:()=>this.emit({type:"restart"}),onSocketReady:r=>{this.currentSock=r},onSocketClosed:()=>{this.currentSock=null}}),this.emit({type:"open"}),this.emit({type:"done",ok:!0})}catch(r){let o=r instanceof Error?r.message:String(r);o==="Pairing cancelled."?this.emit({type:"error",message:"Cancelled."}):this.emit({type:"error",message:o}),this.emit({type:"done",ok:!1})}finally{this.busy=!1,this.abort=null,this.currentSock=null}})()}},Lr=new Ha;var ie="application/json; charset=utf-8",Or=null;function Rb(){Ir();let e=Or;Or=null,e?e.close(()=>{it.exit(0)}):it.exit(0),setTimeout(()=>it.exit(0),4e3).unref()}function Cb(){let e=it.env.OMNISH_CONFIG_UI_STATIC?.trim(),t=it.env.OMNISH_UI_STATIC?.trim()||e;if(t&&Vt.existsSync(Ae.join(t,"index.html")))return t;let n=Ae.dirname(xb(import.meta.url)),r=Ae.join(n,"ui");if(Vt.existsSync(Ae.join(r,"index.html")))return r;let o=Ae.join(n,"..","..","dist","ui");if(Vt.existsSync(Ae.join(o,"index.html")))return o;let s=Ae.join(it.cwd(),"dist","ui");if(Vt.existsSync(Ae.join(s,"index.html")))return s;throw new Error("omnish ui static files not found (expected dist/ui/index.html). Run `pnpm build`.")}function Mb(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 Tb(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 Ba(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 Eb(e){return Bt.includes(e)}function ja(e){let t=(e.telegramBotToken??"").trim(),n=t.length===0?"":t.length<=8?"(set)":`${t.slice(0,4)}\u2026${t.slice(-4)}`;return{...e,telegramBotToken:n,telegramBotTokenConfigured:!!ve(e),telegramBotTokenEnvOverride:typeof it.env.TELEGRAM_BOT_TOKEN=="string"&&it.env.TELEGRAM_BOT_TOKEN.trim().length>0}}function Pb(){let e=$();return{version:Ze(),dataDir:H,configPath:_,waAuthDir:ne,whatsappLinked:nt(),gatewayPidHint:Vt.existsSync(Ae.join(H,"gateway.pid")),gatewayRunning:Ns(),gatewayLogFile:Ne,gatewayMode:e.gatewayMode,telegramBotTokenMasked:ve(e).length===0?"":ja(e).telegramBotToken,telegramBotTokenEnvOverride:typeof it.env.TELEGRAM_BOT_TOKEN=="string"&&it.env.TELEGRAM_BOT_TOKEN.trim().length>0}}function Ab(e,t){if(!Array.isArray(e)||!Array.isArray(t))throw new Error("allowFrom and telegramAllowFrom must be arrays of strings.");let n=e.map(a=>String(a).trim()).filter(Boolean),r=t.map(a=>String(a).trim()).filter(Boolean),o=Br(n).filter(a=>a!=="*"),s=[];for(let a of r){let l=Ee(a);if(!l)throw new Error(`Invalid Telegram allow entry: ${a}`);s.push(l)}let i=$();i.allowFrom=o.sort(),i.telegramAllowFrom=[...new Set(s)].sort(),_e(i)}function Ib(e){return typeof e=="string"?e:typeof e=="boolean"||typeof e=="number"?String(e):JSON.stringify(e)}async function cm(e){let t=Cb(),{meta:n}=e;lm(e.port);let r=vb.createServer(async(o,s)=>{try{let i=new URL(o.url??"/",`http://${o.headers.host??"localhost"}`),a=i.pathname;if(o.method==="GET"&&a==="/"){let m=i.searchParams.get("token")?.trim();if(m&&m===n.token){let f=Fa(n.secret);s.statusCode=302,s.setHeader("Location","/"),s.setHeader("Set-Cookie",Wa(f)),s.end();return}}if(a.startsWith("/api/")){let m=o.headers.cookie,f=rm(n.secret,m);if(o.method==="POST"&&a==="/api/session"){let h=await Ba(o),g=typeof h?.token=="string"?h.token.trim():"";if(!g||g!==n.token){s.statusCode=401,s.setHeader("Content-Type",ie),s.end(JSON.stringify({ok:!1,error:"Invalid token."}));return}let y=Fa(n.secret);s.statusCode=200,s.setHeader("Content-Type",ie),s.setHeader("Set-Cookie",Wa(y)),s.end(JSON.stringify({ok:!0}));return}if(o.method==="GET"&&a==="/api/me"){s.statusCode=f?200:401,s.setHeader("Content-Type",ie),s.end(JSON.stringify({ok:f}));return}if(!f){s.statusCode=401,s.setHeader("Content-Type",ie),s.end(JSON.stringify({ok:!1,error:"Unauthorized."}));return}if(o.method==="GET"&&a==="/api/status"){s.statusCode=200,s.setHeader("Content-Type",ie),s.end(JSON.stringify({ok:!0,...Pb()}));return}if(o.method==="GET"&&a==="/api/config"){s.statusCode=200,s.setHeader("Content-Type",ie),s.end(JSON.stringify({ok:!0,config:ja($())}));return}if(o.method==="PUT"&&a==="/api/config"){let h=await Ba(o);if(!h||typeof h!="object"){s.statusCode=400,s.setHeader("Content-Type",ie),s.end(JSON.stringify({ok:!1,error:"Expected JSON object."}));return}let g=h;for(let[y,b]of Object.entries(g))if(b!==void 0&&!(y==="allowFrom"||y==="telegramAllowFrom")){if(y==="telegramBotToken"){let v=typeof b=="string"?b.trim():"";if(!v||v==="(set)"||v.includes("\u2026"))continue;if(!dt(v))throw new Error("Invalid Telegram bot token format.");uo("telegramBotToken",v);continue}if(!Eb(y))throw new Error(`Unknown or unsupported config key: ${y}`);uo(y,Ib(b))}if("allowFrom"in g||"telegramAllowFrom"in g){let y=$();Ab("allowFrom"in g?g.allowFrom:y.allowFrom,"telegramAllowFrom"in g?g.telegramAllowFrom:y.telegramAllowFrom)}s.statusCode=200,s.setHeader("Content-Type",ie),s.end(JSON.stringify({ok:!0,config:ja($())}));return}if(o.method==="POST"&&a==="/api/logout"){s.statusCode=200,s.setHeader("Content-Type",ie),s.setHeader("Set-Cookie",om()),s.end(JSON.stringify({ok:!0}));return}if(o.method==="POST"&&a==="/api/shutdown"){s.statusCode=200,s.setHeader("Content-Type",ie),s.end(JSON.stringify({ok:!0})),setImmediate(()=>{Lr.requestCancel(),Rb()});return}if(o.method==="POST"&&a==="/api/gateway/start"){if(Ns()){s.statusCode=409,s.setHeader("Content-Type",ie),s.end(JSON.stringify({ok:!1,error:"Gateway already appears to be running (pidfile + live process). Stop it first if you need to restart."}));return}let h=Ms();if(!h.ok){s.statusCode=400,s.setHeader("Content-Type",ie),s.end(JSON.stringify({ok:!1,error:h.message}));return}let g=Ne,y=qo(g);if(!y.ok){s.statusCode=500,s.setHeader("Content-Type",ie),s.end(JSON.stringify({ok:!1,error:y.message}));return}s.statusCode=200,s.setHeader("Content-Type",ie),s.end(JSON.stringify({ok:!0,pid:y.pid,logFile:g}));return}if(o.method==="POST"&&a==="/api/gateway/stop"){let h=Yo();switch(h.outcome){case"no_pidfile":s.statusCode=400,s.setHeader("Content-Type",ie),s.end(JSON.stringify({ok:!1,error:"No gateway pidfile \u2014 background gateway does not appear to be running."}));return;case"invalid_pidfile":s.statusCode=400,s.setHeader("Content-Type",ie),s.end(JSON.stringify({ok:!1,error:"Invalid pidfile (removed)."}));return;case"stale_cleaned":s.statusCode=200,s.setHeader("Content-Type",ie),s.end(JSON.stringify({ok:!0,stale:!0,pid:h.pid,message:"Process was not running; stale pidfile removed."}));return;case"sent_signal":s.statusCode=200,s.setHeader("Content-Type",ie),s.end(JSON.stringify({ok:!0,pid:h.pid}));return;case"taskkill_ok":s.statusCode=200,s.setHeader("Content-Type",ie),s.end(JSON.stringify({ok:!0,pid:h.pid,taskkill:!0}));return;case"failed":s.statusCode=500,s.setHeader("Content-Type",ie),s.end(JSON.stringify({ok:!1,error:h.message}));return}}if(o.method==="GET"&&a==="/api/wa/link/events"){s.writeHead(200,{"Content-Type":"text/event-stream; charset=utf-8","Cache-Control":"no-store",Connection:"keep-alive"}),s.write(`: connected
|
|
257
352
|
|
|
258
|
-
`);let
|
|
353
|
+
`);let h=Lr.subscribe(g=>{s.write(`data: ${JSON.stringify(g)}
|
|
259
354
|
|
|
260
|
-
`)});o.on("close",()=>{
|
|
261
|
-
`;return new Promise(o=>{let s=!1,i="";function a(
|
|
262
|
-
`);if(
|
|
263
|
-
`)}var
|
|
264
|
-
`))}function
|
|
265
|
-
`)}:{error:null,kind:"text",recipientsSent:
|
|
266
|
-
`)};let
|
|
267
|
-
`+
|
|
268
|
-
`)));return}let t=
|
|
269
|
-
`)||
|
|
270
|
-
`)}),
|
|
271
|
-
`)}),
|
|
272
|
-
`))}function
|
|
273
|
-
`))}function
|
|
274
|
-
Try: omnish link --help`}}return n!==null?t?{kind:"error",message:"[omnish] --force applies to WhatsApp only; do not combine with --tg."}:{kind:"tg",token:n}:{kind:"wa",force:t}}function
|
|
275
|
-
`))}function
|
|
276
|
-
`))}function
|
|
277
|
-
Script: ${o.scriptPath}`,
|
|
278
|
-
`),console.warn(
|
|
279
|
-
`,{mode:384})}catch(
|
|
280
|
-
`),
|
|
355
|
+
`)});o.on("close",()=>{h()});return}if(o.method==="POST"&&a==="/api/wa/link/start"){let g=(await Ba(o))?.force===!0;try{Lr.beginPairing({force:g})}catch(y){let b=String(y),v=b.includes("already in progress")||b.includes("Gateway appears")?409:400;s.statusCode=v,s.setHeader("Content-Type",ie),s.end(JSON.stringify({ok:!1,error:b}));return}s.statusCode=202,s.setHeader("Content-Type",ie),s.end(JSON.stringify({ok:!0}));return}if(o.method==="POST"&&a==="/api/wa/link/cancel"){Lr.requestCancel(),s.statusCode=200,s.setHeader("Content-Type",ie),s.end(JSON.stringify({ok:!0}));return}s.statusCode=404,s.setHeader("Content-Type",ie),s.end(JSON.stringify({ok:!1,error:"Not found."}));return}let l=a==="/"?"index.html":a.replace(/^\/+/,""),d=Tb(t,l),u=Ae.join(t,"index.html");d&&Vt.existsSync(d)&&Vt.statSync(d).isFile()&&(u=d);let c=Vt.readFileSync(u);s.statusCode=200,s.setHeader("Content-Type",Mb(u)),s.setHeader("Cache-Control",Ae.basename(u)==="index.html"?"no-store":"public, max-age=3600"),s.end(c)}catch(i){s.statusCode=500,s.setHeader("Content-Type",ie),s.end(JSON.stringify({ok:!1,error:String(i)}))}});await new Promise((o,s)=>{r.once("error",s),r.listen(e.port,e.host,()=>o())}),Or=r,am({pid:it.pid,port:e.port,host:e.host,startedAt:new Date().toISOString()}),r.on("close",()=>{Ir(),Or===r&&(Or=null)})}function um(e){let t=$b.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 Wb from"node:readline";import pm from"node:path";import Te from"node:process";pe();qe();G();import Lb from"node:net";import Ob from"node:fs";function Nb(){try{let e=Ob.readFileSync(hn,"utf8"),t=JSON.parse(e);return typeof t.host!="string"||typeof t.port!="number"||typeof t.token!="string"||!Number.isFinite(t.port)?null:{host:t.host,port:t.port,token:t.token}}catch{return null}}async function Nr(e){let t=Nb();if(!t)return"No gateway control endpoint \u2014 is `omnish run` active? (control metadata missing.)";let n={...e,token:t.token},r=`${JSON.stringify(n)}
|
|
356
|
+
`;return new Promise(o=>{let s=!1,i="";function a(d){s||(s=!0,o(d))}let l=Lb.connect({host:t.host,port:t.port},()=>{l.write(r)});l.setTimeout(6e5),l.on("data",d=>{i+=d.toString("utf8");let u=i.indexOf(`
|
|
357
|
+
`);if(u>=0){let c=i.slice(0,u).trim();try{let m=JSON.parse(c);m.ok?a(null):a(m.error||"Unknown error from gateway control.")}catch{a("Invalid response from gateway control.")}l.destroy()}}),l.on("error",d=>{a(`Control connection failed: ${String(d)}`)}),l.on("timeout",()=>{l.destroy(),a("Gateway control timed out.")}),l.on("close",()=>{!s&&i.trim()===""&&a("Gateway closed the control connection without a response.")})})}qe();function _b(e){let t=e.trimStart();if(/^--text=/i.test(t)){let n=t.slice(t.indexOf("=")+1).trimEnd();return n.length>0?n:null}if(/^--text(\s+|$)/i.test(t)){let n=t.replace(/^--text\s*/i,"").trim();return n.length>0?n:null}if(/^-t=/i.test(t)){let n=t.slice(t.indexOf("=")+1).trimEnd();return n.length>0?n:null}if(/^-t(\s+|$)/i.test(t)){let n=t.replace(/^-t\s*/i,"").trim();return n.length>0?n:null}}function Fb(e){let t=e.toLowerCase();if(t==="*"||t==="all")return{channel:"all"};if(t==="wa"||t==="whatsapp"||t==="wa:all"||t==="whatsapp:all")return{channel:"whatsapp-all"};if(t==="tg"||t==="telegram"||t==="tg:all"||t==="telegram:all")return{channel:"telegram-all"};if(t.startsWith("tg:")||t.startsWith("telegram:")){let n=Ee(e);if(!n)return null;let r=Number(n);return Number.isFinite(r)?{channel:"telegram",chatId:r}:null}if(t.startsWith("wa:")){let n=e.slice(3).trim();if(!n)return null;let r=n.split(",").map(o=>ee(o.trim())).filter(o=>!!o);return r.length===0?null:{channel:"whatsapp",e164s:r}}if(e.includes(",")){let n=e.split(",").map(r=>ee(r.trim())).filter(r=>!!r);return n.length===0?null:{channel:"whatsapp",e164s:n}}if(e.startsWith("+")){let n=ee(e);return n?{channel:"whatsapp",e164s:[n]}:null}return null}function dm(e){let t=e.trim();if(!/^\/sendto(\s|$)/i.test(t))return null;let n=t.replace(/^\/sendto\s+/i,"").trim();if(!n)return null;let r=n.indexOf(" ");if(r===-1)return null;let o=n.slice(0,r).trim(),s=n.slice(r+1).trim();if(!s)return null;let i=Fb(o);if(!i)return null;let a=_b(s);if(a===null)return null;if(a!==void 0)return{...i,mode:"text",body:a};let l=hs(s);if(!l)return null;let{selectorPart:d,caption:u}=l;return{...i,mode:"media",selectorPart:d,caption:u}}function Ga(){return["/sendto wa|tg|* <selectors> [-- caption]","/sendto +E164 or +E164,+E164 <selectors> [-- caption]","/sendto <dest> --text <message> or --text=<msg> or -t <msg>","/sendto tg:<chat_id> <selectors> [-- caption] (compat: single Telegram id)","selectors: file1,file2 or *.mp4 or **/*.mp4 (from current session cwd); a path ./--text sends a file named --text","examples: /sendto wa ./promo.mp4 | /sendto * **/*.mp4 -- Daily clips","examples: /sendto +15551234567 --text Hello | /sendto tg:123 -t=Status OK","examples: /sendto +15550000001,+15550000002 intro.png,deck.pdf -- Launch","Also supports compat aliases: wa:all, whatsapp:all, tg:all, telegram:all.","Requires `omnish run` on this machine."].join(`
|
|
358
|
+
`)}G();var Xn="wa:cli:interactive",Ub={onPlainTextLlmFallback(e,t){Ln($(),e,t,async n=>{n.trim()&&console.log(B(Te.stdout,n))})}};function Db(e){let t=e.trim();if(!t)return null;let n=t.toLowerCase();if(n.startsWith("tg:")||n.startsWith("telegram:")){let s=Ee(t);return s?`tg:${s}`:null}let r=n.startsWith("wa:")?t.slice(3):t,o=ee(r);return o?`wa:${o}`:null}function Hb(e){let t=null,n=null;for(let r=0;r<e.length;){let o=e[r]??"";if(o==="--as"){let s=e[r+1];if(!s||s.startsWith("-"))return{opts:{senderKey:null,oneShot:null},error:"--as requires a sender (wa:+E164 or tg:<id>)."};let i=Db(s);if(!i)return{opts:{senderKey:null,oneShot:null},error:`Could not parse sender "${s}". Use +E164, wa:+E164, or tg:<user_id>.`};t=i,r+=2;continue}if(o==="-c"||o==="--command"){let s=e[r+1];if(typeof s!="string")return{opts:{senderKey:null,oneShot:null},error:`${o} requires a command string.`};n=s,r+=2;continue}return o==="--help"||o==="-h"?{opts:{senderKey:null,oneShot:null},error:"help"}:{opts:{senderKey:null,oneShot:null},error:`Unknown argument: ${o}`}}return{opts:{senderKey:t,oneShot:n},error:null}}function Ja(e){let t=Te.cwd(),n=[`${ce(e,"omnish i")} ${w(e,"[options]")}`,V(e,"Interactive shell \u2014 same commands as WhatsApp/Telegram chat."),"",J(e,"Usage:"),` ${S(e,"omnish i [options]")}`,` ${S(e,"omnish interactive [options]")}`,"",J(e,"Options:"),...St(e," ",[{left:"--as <sender>",right:"Sender key for cluster commands (wa:+E164 or tg:id). Default: synthetic wa:cli:interactive."},{left:"-c, --command <line>",right:"Run one line and exit (non-interactive)."},{left:"-h, --help",right:"Show this help."}],r=>w(e,r)),"",`${w(e,"Trust:")} ${S(e,"Full local access like your shell; not gated by inbox allowlist.")}`,`${w(e,"Jobs:")} ${S(e,"/bg and /jobs apply only to this REPL session (not the gateway process).")}`,`${w(e,"Files:")} ${S(e,"Use /sendto for files or --text for plain messages through the gateway; plain /send needs a chat peer.")}`,`${w(e,"Gateway:")} ${S(e,"/reload and /updates require omnish run; /sendto requires omnish run for WA/TG delivery.")}`,"",Ga(),"",`${w(e,"cwd:")} ${S(e,`session starts at ${t} (change with !cd or ${$().commandPrefix}cd).`)}`];console.log(n.join(`
|
|
359
|
+
`))}function mm(e,t){let n=new Set,r=new Set;if(e.channel==="whatsapp-all"||e.channel==="all"){let o=new Set;for(let s of t.allowFrom){let i=ee(String(s));i&&!o.has(i)&&(o.add(i),n.add(i))}}if(e.channel==="telegram-all"||e.channel==="all")for(let o of t.telegramAllowFrom){let s=Ee(String(o));if(!s)continue;let i=Number(s);Number.isFinite(i)&&r.add(i)}if(e.channel==="whatsapp")for(let o of e.e164s)n.add(o);return e.channel==="telegram"&&r.add(e.chatId),{waTargets:n,tgTargets:r}}async function Bb(e){let t=$();if(e.mode==="text"){let{waTargets:c,tgTargets:m}=mm(e,t);if(c.size===0&&m.size===0)return{error:"No recipients matched the requested /sendto destination."};let f=[];for(let h of c){let g=await Nr({op:"sendText",channel:"whatsapp",e164:h,text:e.body});g&&f.push(`[wa:${h}] ${g}`)}for(let h of m){let g=await Nr({op:"sendText",channel:"telegram",chatId:h,text:e.body});g&&f.push(`[tg:${h}] ${g}`)}return f.length>0?{error:f.join(`
|
|
360
|
+
`)}:{error:null,kind:"text",recipientsSent:c.size+m.size}}let n=oe(Xn).cwd,r=await Rr(n,e.selectorPart);if(r.length===0)return{error:`No files matched: ${e.selectorPart}`};let o=await Cr(r);if(!o.ok)return{error:o.error};let s=r.map(c=>Kt(c,t.fileSendMaxBytes));for(let c of s)if("error"in c)return{error:c.error};let{waTargets:i,tgTargets:a}=mm(e,t);if(i.size===0&&a.size===0)return{error:"No recipients matched the requested /sendto destination."};let l=[];for(let c of i)for(let m of s){if("error"in m)continue;let f=await Nr({op:"sendMedia",channel:"whatsapp",e164:c,absPath:m.absPath,caption:e.caption});f&&l.push(`[wa:${c}] ${m.displayName}: ${f}`)}for(let c of a)for(let m of s){if("error"in m)continue;let f=await Nr({op:"sendMedia",channel:"telegram",chatId:c,absPath:m.absPath,caption:e.caption});f&&l.push(`[tg:${c}] ${m.displayName}: ${f}`)}if(l.length>0)return{error:l.join(`
|
|
361
|
+
`)};let d=i.size+a.size,u=s.length;return{error:null,kind:"media",recipientsSent:d,filesSent:u,messagesSent:d*u}}async function jb(e,t,n,r,o,s,i){let a=e.trim();if(!a)return;let l=dm(a);if(l!==null||/^\/sendto(\s|$)/i.test(a)){if(l===null){console.log(C(Te.stderr,`Invalid /sendto.
|
|
362
|
+
`+Ga()));return}let c=await Bb(l);if(c.error!==null)console.log(C(Te.stderr,c.error));else if(c.kind==="text")console.log(B(Te.stdout,`Sent text to ${c.recipientsSent} recipient(s).`));else{let m=`Sent ${c.filesSent} file(s) to ${c.recipientsSent} recipient(s) (${c.messagesSent} message(s)).`;console.log(B(Te.stdout,m))}return}let d={peerKey:Xn,text:e},u=await Vn($(),t,n,r,o,d,s,void 0,i,!1,Ub);u!==null&&await Gb(u)}async function Gb(e){if(e===null)return;if(e.kind==="file"||e.kind==="files"){console.log(B(Te.stdout,["This CLI session has no chat peer to attach to.","Push through the gateway instead, for example:"," /sendto wa:+15551234567 ./my.pdf"," /sendto tg:123456789 ~/photo.jpg -- optional caption","(Requires `omnish run` on this machine.)"].join(`
|
|
363
|
+
`)));return}if(e.kind==="texts"){for(let n=0;n<e.bodies.length;n++){let r=ke(e.bodies[n],"whatsapp").text;r.trim()&&(n>0&&console.log(""),console.log(r))}return}let t=ke(e.body,"whatsapp").text;t.trim()&&console.log(t)}async function fm(e){let t=Hb(e);if(t.error==="help"){Ja(Te.stdout);return}if(t.error&&t.error!==null){console.error(C(Te.stderr,t.error)),console.error(w(Te.stderr,"Try: omnish i --help")),Te.exitCode=1;return}let{senderKey:n,oneShot:r}=t.opts,o=n??Xn;te(),ro(Xn,Te.cwd());let s=new Tt,i=new Map,a=new Map,l=new Map,d=new Jt(()=>$(),async(f,h)=>{Te.stdout.write(h),h.endsWith(`
|
|
364
|
+
`)||Te.stdout.write(`
|
|
365
|
+
`)}),u=async f=>{try{await jb(f,s,i,a,l,d,o)}catch(h){console.error(C(Te.stderr,String(h)))}};if(r!==null){await u(r),d.dispose(),s.killAllRunning();return}let c=Wb.createInterface({input:Te.stdin,output:Te.stdout}),m=pm.basename(oe(Xn).cwd);c.setPrompt(`${m}> `),c.on("line",f=>{u(f).then(()=>{let h=pm.basename(oe(Xn).cwd);c.setPrompt(`${h}> `),c.prompt()})}),c.on("close",()=>{d.dispose(),s.killAllRunning(),Te.stdout.write(`
|
|
366
|
+
`)}),c.prompt()}Jb.setDefaultResultOrder("ipv4first");function ym(){let e=process.stdout,t=[`${Ce(e,"omnish run")} ${w(e,"[options]")}`,V(e,"Listen for DMs and run shell commands from allowlisted chats."),"",J(e,"Usage:"),` ${S(e,"omnish run [options]")}`,"",J(e,"Options:"),...St(e," ",[{left:"-d, --background",right:"Start the gateway detached; log to --log-file (default: <data>/logs/gateway.log)."},{left:"--log-file <path>",right:`Append stdout/stderr when background (default: ${Ne}).`},{left:"-h, --help",right:"Show this help."}],r=>w(e,r)),"",`${S(e,"Config reload:")} ${w(e,"while the gateway runs, edit config then send /reload or /restart from an allowlisted chat (no restart needed for many keys). /updates checks npm (and optional updateInfoUrl).")}`,""],n=fe();n?t.push(J(e,"Attached mode (platform credentials detected):"),` ${w(e,"url:")} ${S(e,n.platformUrl)} ${V(e,`[${zn()}]`)}`,` ${w(e,"token:")} ${S(e,tn("platformToken",n.token))} ${V(e,`[${Kn()}]`)}`,w(e," Messengers run on the platform; allowlist is set on the dashboard. Run: omnish platform probe"),""):t.push(w(e,"Platform attached mode: omnish config add platform_url <url> platform_token <token>"),w(e," then omnish platform probe && omnish run \u2014 see omnish help platform"),""),console.log(t.join(`
|
|
367
|
+
`))}function wm(){let e=process.stdout,t=[{left:"omnish link [--force]",right:"WhatsApp: scan QR (Linked devices). --force wipes session first."},{left:"omnish link --tg <bot_token>",right:"Telegram: save token to config; gatewayMode telegram or both if WhatsApp is linked."}],n=t.map(i=>w(e,i.left)),r=Math.max(...n.map(so)),o=t.map((i,a)=>Xs(" ",r,n[a],S(e,i.right))),s=[`${Ce(e,"omnish link")} ${w(e,"[options]")}`,V(e,"Connect WhatsApp (QR) or save a Telegram bot token."),"",J(e,"Usage:"),` ${S(e,"omnish link [--force]")}`,` ${S(e,"omnish link --tg <bot_token>")}`,"",J(e,"Modes:"),...o,` ${V(e,"Do not combine --tg with --force.")}`,"",J(e,"Options:"),...St(e," ",[{left:"-f, --force",right:"WhatsApp only: delete saved session before pairing."},{left:"-h, --help",right:"Show this help."}],i=>w(e,i)),"",`${S(e,"Next:")} ${ce(e,"omnish allow tg:<your_user_id>")} ${w(e,"then")} ${ce(e,"omnish run")}`,`${w(e,"Config:")} ${S(e,_)}`,""];fe()&&s.push(J(e,"Platform attached mode:"),w(e," omnish link on this host is for standalone only. Link WhatsApp on the platform dashboard or:"),` ${S(e,"omnish platform import-whatsapp")} ${w(e,"(after a local omnish link, with omnish run stopped)")}`,w(e," Telegram: set bot token on the platform dashboard, not --tg here."),""),console.log(s.join(`
|
|
368
|
+
`))}function zb(e){let t=!1,n=null;for(let r=0;r<e.length;r++){let o=e[r]??"";if(o==="--help"||o==="-h")return{kind:"help"};if(o==="--force"||o==="-f"){t=!0;continue}if(o==="--tg"||o==="--telegram"){let s=e[r+1];if(!s||s.startsWith("-"))return{kind:"error",message:"[omnish] --tg requires a bot token (from @BotFather)."};n=s,r++;continue}if(o.startsWith("--tg=")||o.startsWith("--telegram=")){let s=o.indexOf("="),i=o.slice(s+1).trim();if(!i)return{kind:"error",message:"[omnish] --tg= requires a non-empty bot token."};n=i;continue}return{kind:"error",message:`[omnish] unknown link argument: ${o}
|
|
369
|
+
Try: omnish link --help`}}return n!==null?t?{kind:"error",message:"[omnish] --force applies to WhatsApp only; do not combine with --tg."}:{kind:"tg",token:n}:{kind:"wa",force:t}}function za(){let e=process.stdout,t=`${Ce(e,"omnish")} ${w(e,`v${Ze()}`)}`,n=[{left:"link [--force] [--tg <token>]",right:"WhatsApp (QR) or Telegram bot token \u2014 omnish link --help"},{left:"run [options]",right:"Listen for DMs (WhatsApp and/or Telegram \u2014 see gatewayMode in config)"},{left:"stop",right:`Stop background gateway (pidfile: ${le})`},{left:"service <subcommand>",right:"Boot install hints, logs, systemd/LaunchAgent \u2014 omnish service help"},{left:"logout",right:"Delete saved WhatsApp session"},{left:"allow +<E164> | tg:<id>",right:"Add allowlist entry"},{left:"deny +<E164> | tg:<id>",right:"Remove allowlist entry"},{left:"status [--check-updates]",right:"Channels, identity, allowlists, jobs, security, cluster (if enabled)"},{left:"commands",right:"Chat commands for allowlisted users (same as /help)"},{left:"security [--json]",right:"Configuration security report (JSON for scripts)"},{left:"cluster [status | use <sender> <label-or-id>]",right:"Per-sender machine bindings"},{left:"i | interactive [options]",right:"Local REPL (chat commands; /sendto needs omnish run)"},{left:"ui [options]",right:"Browser setup UI on LAN (token-gated) \u2014 omnish ui --help"},{left:"config <add|show|edit|delete>",right:"Manage config.json (platform URL/token, tunnel, gateway) \u2014 omnish config help"},{left:"platform <subcommand>",right:"Attached mode: configure, probe, import WA \u2014 omnish help platform"},{left:"tunnel <subcommand>",right:"Expose local HTTP/TCP via omnish relay \u2014 omnish tunnel help"}],r=[{left:"-v, --version",right:"Print version and exit."},{left:"-h, --help",right:"Show this help (same as omnish help)."}],o=[t,V(e,"Allowlisted inbox \u2192 your real shell. No AI."),"",J(e,"Usage:"),` ${S(e,"omnish [options] <command> [args...]")}`,"",J(e,"Options:"),...St(e," ",r,s=>w(e,s)),"",J(e,"Commands:"),...St(e," ",n,s=>w(e,s)),"",`${w(e,"Config:")} ${S(e,`${_} \u2014 gatewayMode: "whatsapp" | "telegram" | "both"`)}`,`${w(e,"Platform:")} ${S(e,"platform_url + platform_token \u2192 attached omnish run (omnish help platform)")}`,`${w(e,"Env:")} ${S(e,"OMNISH_VERBOSE=1 (Baileys; legacy WHATSVERBOSE=1 still works), TELEGRAM_BOT_TOKEN (optional)")}`,`${w(e,"Data:")} ${S(e,"~/.omnish by default; ~/.whatslive reused if it already exists. OMNISH_HOME overrides.")}`,`${w(e,"See also:")} ${S(e,"https://omnish.dev")}`,""];console.log(o.join(`
|
|
370
|
+
`))}function qb(e){let t=(e??"").trim().toLowerCase(),n=process.stdout;if(!t){za();return}switch(t){case"link":wm();return;case"run":ym();return;case"service":km();return;case"i":case"interactive":Ja(n);return;case"ui":Sm();return;case"config":Es(n);return;case"platform":Ls(n);return;default:console.error(C(process.stderr,`No detailed help for "${e}". Try: omnish help`)),process.exitCode=1}}function Yb(e){let t=!1,n="",r=!1;for(let s=0;s<e.length;s++){let i=e[s]??"";if(i==="-d"||i==="--background")t=!0;else if(i==="--log-file"||i==="--log"){let a=e[++s];a||(console.error(C(process.stderr,"--log-file requires a path.")),process.exit(1)),n=a}else if(i==="--help"||i==="-h")r=!0;else{let a=process.stderr;console.error(C(a,`unknown run option: ${i}`)),console.error(C(a,"Try: omnish run --help")),process.exit(1)}}let o=n.trim()!==""?Ka.isAbsolute(n)?n:Ka.resolve(process.cwd(),n):Ne;return{background:t,logFile:o,help:r}}function Qb(e){let t=qo(e);t.ok||(console.error(C(process.stderr,t.message)),process.exit(1));let n=process.stdout;console.log(`${B(n,`gateway started in background (pid ${t.pid}).`)} ${w(n,`Log: ${e}`)}`)}function Vb(){let e=Yo();switch(e.outcome){case"no_pidfile":console.error(C(process.stderr,`no pidfile at ${le} \u2014 is a background gateway running?`)),process.exitCode=1;return;case"invalid_pidfile":console.error(C(process.stderr,"invalid pidfile.")),process.exitCode=1;return;case"stale_cleaned":console.log(B(process.stdout,`process ${e.pid} is not running; removing stale pidfile.`));return;case"sent_signal":console.log(B(process.stdout,`sent SIGTERM to gateway (pid ${e.pid}).`));return;case"taskkill_ok":console.log(B(process.stdout,`stopped gateway (pid ${e.pid}) using taskkill.`));return;case"failed":console.error(C(process.stderr,e.message)),process.exitCode=1;return}}function hm(){if(process.env.OMNISH_BACKGROUND_GATEWAY==="1")try{Xt.readFileSync(le,"utf8").trim()===String(process.pid)&&Xt.unlinkSync(le)}catch{}}function Xb(e){return e.length<=8?"(set)":`${e.slice(0,4)}\u2026${e.slice(-4)}`}function Zb(){if(!Xt.existsSync(le))return"gateway process: not running (no pid file)";let e=Xt.readFileSync(le,"utf8").trim(),t=Number(e);if(!Number.isFinite(t)||t<=0)return"gateway process: invalid pid file";try{return process.kill(t,0),`gateway process: running (pid ${t})`}catch{return`gateway process: not running (stale pid ${t} in pid file)`}}var bm=120;function km(){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 ${bm}).`},{left:"install",right:"Write user systemd / LaunchAgent unit (needs serviceInstallFromChat=true)."},{left:"uninstall",right:"Remove that unit (same gate as install)."}],n=[`${Ce(e,"omnish service")} ${w(e,"<subcommand>")}`,V(e,"Boot integration, crash restart policy, and logs (same ideas as /service in chat)."),"",J(e,"Usage:"),` ${S(e,"omnish service <subcommand>")}`,"",J(e,"Subcommands:"),...St(e," ",t,r=>w(e,r)),"",J(e,"Restart and reload:"),` ${S(e,"Process")} ${w(e,"\u2014 Linux user unit: Restart=on-failure, RestartSec=5. macOS: KeepAlive. Full restart loads config from disk.")}`,` ${S(e,"Config live")} ${w(e,"\u2014 allowlisted chat while gateway runs: /reload or /restart")}`,"",`${w(e,"Docs:")} ${S(e,"docs/guides/background-and-boot.md")} ${V(e,"\xB7")} https://omnish.dev`,""];console.log(n.join(`
|
|
371
|
+
`))}function ek(e){let t=process.stdout,n=process.stderr,r=(e[0]??"help").toLowerCase();if(r==="help"||r==="--help"||r==="-h"){km();return}if(r==="instructions"){let o=nn();if(o.error){console.error(C(n,o.error)),process.exitCode=1;return}console.log(fo(o));return}if(r==="status"){let o=nn(),s=(()=>{try{return Xt.existsSync(le)?`gateway.pid: ${Xt.readFileSync(le,"utf8").trim()}`:"gateway.pid: (missing)"}catch(c){return`gateway.pid: (read error: ${String(c)})`}})(),i=process.env.OMNISH_BACKGROUND_GATEWAY==="1"?"This process: background gateway (OMNISH_BACKGROUND_GATEWAY=1).":"This process: CLI (not the gateway \u2014 run omnish service status on the host where the gateway runs for live pid info).",a=typeof process.env.OMNISH_HOME=="string"&&process.env.OMNISH_HOME.trim()?`OMNISH_HOME env: ${process.env.OMNISH_HOME.trim()}`:"OMNISH_HOME env: (not set \u2014 using default data dir)",l=o.error?o.error:`Node: ${o.nodePath}
|
|
372
|
+
Script: ${o.scriptPath}`,u=$().serviceInstallFromChat?"Install from CLI/chat: enabled (omnish service install / /service install).":"Install from CLI/chat: off \u2014 set serviceInstallFromChat true in config (same trust as shell).";console.log(Ce(t,"omnish service status")),console.log(""),console.log(`${w(t,"platform:")} ${S(t,process.platform)}`),console.log(`${w(t,"session:")} ${S(t,i)}`),console.log(`${w(t,"env:")} ${S(t,a)}`),console.log(`${w(t,"data dir:")} ${S(t,H)}`),console.log(`${w(t,"pidfile:")} ${S(t,s)}`),console.log(`${w(t,"default log:")} ${S(t,Ne)}`),console.log(""),console.log(l),console.log(""),console.log(S(t,u));return}if(r==="logs"){let o=e.length>=2?Number.parseInt(e[1],10):80,s=Number.isFinite(o)&&o>0?Math.min(o,bm):80,i=wo(Ne,s);console.log(`${w(t,"file:")} ${S(t,Ne)}`),console.log(`${w(t,"lines:")} ${S(t,String(s))}`),console.log(""),console.log(i);return}if(r==="install"){if(!$().serviceInstallFromChat){console.error(C(n,"Install is disabled. Set serviceInstallFromChat to true in config (same trust as shell), then run again.")),process.exitCode=1;return}let s=ho();s.ok?console.log(B(t,s.detail)):(console.error(C(n,s.detail)),process.exitCode=1);return}if(r==="uninstall"){if(!$().serviceInstallFromChat){console.error(C(n,"Uninstall is disabled. Set serviceInstallFromChat to true in config or remove the unit file on the host manually.")),process.exitCode=1;return}let s=go();s.ok?console.log(B(t,s.detail)):(console.error(C(n,s.detail)),process.exitCode=1);return}console.error(C(n,`Unknown subcommand "${r}". Try: omnish service help`)),process.exitCode=1}function tk(e){let t=e.trim();if(!t)return null;let n=t.toLowerCase();if(n.startsWith("tg:")||n.startsWith("telegram:")){let s=Ee(t);return s?`tg:${s}`:null}let r=n.startsWith("wa:")?t.slice(3):t,o=ee(r);return o?`wa:${o}`:null}async function nk(){let e=null,t=Ms();t.ok||(console.error(C(process.stderr,t.message)),process.exit(1));let n=fe();if(n){await Ap(n);return}let r=$(),o=r.gatewayMode,s=o==="whatsapp"||o==="both",i=o==="telegram"||o==="both",a=ve(r),l=Rt(r),d=process.stderr,u=Ql(l,"warn");if(u.length>0&&(console.warn(`${B(d,`Security (${u.length} finding(s)):`)}
|
|
373
|
+
`),console.warn(ri(u,d))),process.env.OMNISH_BACKGROUND_GATEWAY==="1")try{Xt.writeFileSync(le,`${process.pid}
|
|
374
|
+
`,{mode:384})}catch(k){T.warn({err:String(k)},"could not write gateway pidfile")}let c=(k,x)=>{let O=$();if(!O.clusterEnabled)return k;let F=Xe(),Q=(O.clusterLabel??"").trim()||gm.hostname(),z=null;if(x.startsWith("tg:"))z=x;else if(x){let Ue=ee(x);Ue&&(z=`wa:${Ue}`)}let we=z?Ct(O,z):null;return Wc(k,{nodeId:F,label:Q,role:O.clusterRole,activeNodeId:we?.nodeId??""})},m=new Tt,f=new Map,h=new Map,g=new Map,y=null,b={stop:null,sendText:null,sendMedia:null},v=null,E=!1,R=async(k,x)=>{if(k.startsWith("wa:")){let O=k.slice(3);y&&await y.sendText(O,x)}else if(k.startsWith("tg:")){let O=Number(k.slice(3));b.sendText&&Number.isFinite(O)&&await b.sendText(O,p(x))}},P={onPlainTextLlmFallback(k,x){Ln($(),k,x,O=>R(k,O))},sendToPeer:R},M=async(k,x)=>{if(k.startsWith("wa:")){let O=k.slice(3);y&&await y.sendMedia(O,x)}else if(k.startsWith("tg:")){let O=Number(k.slice(3));b.sendMedia&&Number.isFinite(O)&&await b.sendMedia(O,x)}},L=()=>new Jt(()=>$(),R),A=L();v=A;let K,X={async reload(){try{T.info("gateway reload requested from chat"),await b.stop?.().catch(()=>{}),b.stop=null,b.sendText=null,b.sendMedia=null;let k=$(),x=k.gatewayMode==="telegram"||k.gatewayMode==="both",O=ve(k);if(x&&O){let z=await zi(O,()=>$(),K,{decorate:c});b.sendText=z.sendText,b.sendMedia=z.sendMedia,b.stop=z.stop}let F=["Reload complete.",`gatewayMode: ${k.gatewayMode}`,x&&O?"Telegram bot is running with the current token.":"Telegram bot is stopped (enable telegram/both + token if you want it).","Allowlists, shell, app session limits, and timeouts are read from disk on each command."].join(`
|
|
375
|
+
`),Q=ms(vr());return{ok:!0,summary:Q?`${F}
|
|
281
376
|
|
|
282
|
-
Updates (last check): ${
|
|
283
|
-
${
|
|
284
|
-
`))}async function
|
|
285
|
-
`));return}await
|
|
286
|
-
`)),o){let
|
|
377
|
+
Updates (last check): ${Q}`:F}}catch(k){return{ok:!1,error:String(k)}}}};if(K=async(k,x)=>{let O=$();await Ar(O,m,f,h,g,k,A,X,k.peerKey,P,Pa({sendTg:x},{surface:"telegram"}),{surface:"telegram"})},i){let k=await zi(a,()=>$(),K,{decorate:c});b.sendText=k.sendText,b.sendMedia=k.sendMedia,b.stop=k.stop}Xo({getCfg:()=>$(),getWaOutbound:()=>y,getTgSendMedia:()=>b.sendMedia,getTgSendText:()=>b.sendText});let se=null;{let k=$();if(k.webhookEnabled){let x=k.webhookToken||Kb.randomBytes(32).toString("hex");k.webhookToken||W({webhookToken:x}),se=Zo({port:k.webhookPort,host:k.webhookHost,token:x},{sendToPeer:R,getDefaultPeerKey:()=>{let F=$();return F.allowFrom.length>0?`wa:${It(F.allowFrom[0])}`:F.telegramAllowFrom.length>0?`tg:${F.telegramAllowFrom[0]}`:null}}).stop}}e=fs({getRunningVersion:Ze,getConfig:$,log:T});let he=Uo({getConfig:$,sendToPeer:R,sendMediaToPeer:M}),D=no({getConfig:$,sendToPeer:R}),ae=!i,Y=()=>{E=!0,he(),D(),e?.(),e=null,se?.(),hm(),Hn(),b.stop?.().catch(()=>{}),v?.dispose(),m.killAllRunning(),dn().stopAll().catch(()=>{}),console.error(`
|
|
378
|
+
${C(process.stderr,"shutting down\u2026")}`),process.exit(0)};if(process.on("SIGINT",Y),process.on("SIGTERM",Y),s)for(;!E;){let k=!1,x;try{x=await jo({printQr:!1,verbose:yn()}),await un(Go(x),3e5,"Gateway: timed out waiting for WhatsApp connection (5 min).")}catch(F){console.error(C(process.stderr,`connect failed: ${String(F)}`)),await new Promise(Q=>setTimeout(Q,5e3));continue}y=Vu(x,{decorate:c});let O=Ju(x,async F=>{let Q=$(),z=F.fromE164||ee(F.fromJid)||"",we=Xc(F),Ue=`wa:${z}`;await Ar(Q,m,f,h,g,we,A,X,Ue,P,Pa({sendWaText:(Ie,de)=>y.sendText(Ie,de),sendWaMedia:(Ie,de)=>y.sendMedia(Ie,de)},{surface:"whatsapp",waJid:F.fromJid}),{surface:"whatsapp"})});if(await new Promise(F=>{let Q=z=>{z.connection==="close"&&(Gi(z.lastDisconnect)===cn.loggedOut&&(k=!0),x.ev.off("connection.update",Q),F())};x.ev.on("connection.update",Q)}),O(),ae&&(A.dispose(),A=L(),v=A),Wn(x),y=null,k&&(console.error(C(process.stderr,"session logged out. Run `omnish link` again.")),he(),e?.(),e=null,hm(),Hn(),b.stop?.().catch(()=>{}),process.exit(1)),E)break;await new Promise(F=>setTimeout(F,3e3))}else for(;!E;)await new Promise(k=>setTimeout(k,500));he(),e?.(),Hn(),b.stop?.().catch(()=>{}),A.dispose()}function rk(e){let t=!1,n="0.0.0.0",r=3789,o;for(let s=0;s<e.length;s++){let i=e[s];if(i==="--help"||i==="-h"){t=!0;continue}if(i==="--host"||i==="-H"){let l=e[++s];l||(console.error(C(process.stderr,"--host requires an address (e.g. 127.0.0.1).")),process.exit(1)),n=l;continue}if(i==="--port"||i==="-p"){let l=e[++s],d=Number.parseInt(l??"",10);Number.isFinite(d)||(console.error(C(process.stderr,"--port requires a number.")),process.exit(1)),r=d;continue}if(i==="--token"||i==="-t"){let l=e[++s];(!l||l.startsWith("-"))&&(console.error(C(process.stderr,"--token requires a secret string.")),process.exit(1)),o=l;continue}let a=process.stderr;console.error(C(a,`unknown ui argument: ${i}`)),console.error(C(a,"Try: omnish ui --help")),process.exit(1)}return{help:t,host:n,port:r,token:o}}function Sm(){let e=process.stdout,t=[`${Ce(e,"omnish ui")} ${w(e,"[options]")}`,V(e,"Serve the browser setup panel on your LAN (token-gated). Same trust as editing config on disk."),"",J(e,"Usage:"),` ${S(e,"omnish ui [options]")}`,"",J(e,"Options:"),...St(e," ",[{left:"--host <addr>",right:"Bind address (default 0.0.0.0 \u2014 reachable on LAN). Use 127.0.0.1 for loopback only."},{left:"--port <n>",right:"TCP port (default 3789)."},{left:"--token <secret>",right:`Set or rotate setup token (saved to ${ct}).`},{left:"-h, --help",right:"This help."}],n=>w(e,n)),"",`${w(e,"Warning:")} ${me(e,"Listening on all interfaces exposes a remote control panel on your network \u2014 use a trusted LAN.")}`,""];console.log(t.join(`
|
|
379
|
+
`))}async function ok(){let[,,e,...t]=process.argv;if(e==="--version"||e==="-v"||e==="-V"){let n=process.stdout;console.log(`${Ce(n,"omnish")} ${w(n,Ze())}`);return}if(e==="--help"||e==="-h"){za();return}if(e==="help"){qb(t[0]);return}switch(te(),e){case"link":{let n=zb(t);if(n.kind==="help"){wm();return}if(n.kind==="error"){let r=process.stderr,o=n.message.replace(/^\[omnish\]\s*/,"");console.error(C(r,o)),process.exitCode=1;return}if(n.kind==="tg"){let r=n.token.trim(),o=process.stderr,s=process.stdout;if(!dt(r)){console.error(C(o,"That does not look like a Telegram bot token (expect digits:secret from @BotFather).")),process.exitCode=1;return}Lt(r);let i=nt()?"both":"telegram";qr(i),console.log([`${ce(s,"Telegram")} ${S(s,"bot token saved to")} ${w(s,_)}`,`${w(s,"gatewayMode:")} ${ce(s,i)}`,"",S(s,"Next:"),` ${w(s,"1.")} ${S(s,"Find your numeric user id (e.g. t.me/userinfobot), then:")} ${ce(s,"omnish allow tg:<id>")}`,` ${w(s,"2.")} ${ce(s,"omnish run")}`,""].join(`
|
|
380
|
+
`));return}await od({verbose:yn(),force:n.force});return}case"run":{let n=Yb(t);if(n.help){ym();return}if(n.background){Qb(n.logFile);return}await nk();return}case"stop":Vb();return;case"logout":{try{Xt.rmSync(ne,{recursive:!0,force:!0}),console.log(B(process.stdout,"Session removed. Run `omnish link` to pair again."))}catch(n){console.error(C(process.stderr,`logout failed: ${String(n)}`)),process.exitCode=1}return}case"allow":{let n=t[0],r=process.stdout,o=process.stderr;if(!n){console.error(C(o,"Usage: omnish allow +<E164> or omnish allow tg:<user_id>")),process.exitCode=1;return}try{let s=Kr(n);console.log(`${w(r,"allowFrom:")} ${S(r,s.allowFrom.join(", ")||"(empty)")}`),console.log(`${w(r,"telegramAllowFrom:")} ${S(r,s.telegramAllowFrom.join(", ")||"(empty)")}`)}catch(s){console.error(C(o,String(s).replace(/^\[omnish\]\s*/,""))),process.exitCode=1}return}case"deny":{let n=t[0],r=process.stdout,o=process.stderr;if(!n){console.error(C(o,"Usage: omnish deny +<E164> or omnish deny tg:<user_id>")),process.exitCode=1;return}try{let s=zr(n);console.log(`${w(r,"allowFrom:")} ${S(r,s.allowFrom.join(", ")||"(empty)")}`),console.log(`${w(r,"telegramAllowFrom:")} ${S(r,s.telegramAllowFrom.join(", ")||"(empty)")}`)}catch(s){console.error(C(o,String(s).replace(/^\[omnish\]\s*/,""))),process.exitCode=1}return}case"i":case"interactive":{await fm(t);return}case"status":{let n=process.stdout,r=$(),o=t.includes("--check-updates"),s=new Tt().list(),i=s.filter(h=>h.status==="running").length,a=ve(r),l=Rt(r),d=r.gatewayMode==="whatsapp"||r.gatewayMode==="both",u=r.gatewayMode==="telegram"||r.gatewayMode==="both",c=nt(),m=c?td(ne):null,f=[];if(f.push(`${Ce(n,"omnish")} ${w(n,Ze())}`,`${w(n,"gatewayMode:")} ${S(n,r.gatewayMode)}`,`${w(n,"data dir:")} ${S(n,Ka.dirname(ne))}`,"",`${w(n,"gateway process:")} ${S(n,Zb().replace(/^gateway process: /,""))}`,"",Dt(n),ce(n,"whatsapp"),` ${w(n,"in use:")} ${d?S(n,"yes"):me(n,"no (gatewayMode is telegram-only)")}`),d){let h=c?S(n,`linked (${ne})`):me(n,"missing \u2014 run omnish link");f.push(` ${w(n,"session:")} ${h}`),c&&m&&f.push(` ${w(n,"linked as:")} ${S(n,m)}`),c&&!m&&f.push(` ${w(n,"linked as:")} ${V(n,"(not in creds yet \u2014 try again after omnish link completes)")}`)}if(f.push(` ${J(n,"Allowed")}`),r.allowFrom.length===0)f.push(` ${V(n,"(none)")}`);else for(let h of r.allowFrom)f.push(` ${w(n,"whatsapp:")} ${S(n,h)}`);if(f.push("",Dt(n),ce(n,"telegram")),f.push(` ${w(n,"in use:")} ${u?S(n,"yes"):me(n,"no (gatewayMode is whatsapp-only)")}`),u){let h=a?S(n,Xb(a)):me(n,"(none) \u2014 omnish link --tg <token> or TELEGRAM_BOT_TOKEN");f.push(` ${w(n,"bot token:")} ${h}`)}if(f.push(` ${J(n,"Allowed")}`),r.telegramAllowFrom.length===0)f.push(` ${V(n,"(none)")}`);else for(let h of r.telegramAllowFrom)f.push(` ${w(n,"telegram:")} ${S(n,h)}`);if(f.push("",Dt(n),...await Fp(n)),f.push("",Dt(n),`${ce(n,"jobs")} ${w(n,`(recent): ${s.length} total, ${i} running`)}`,Zl(l,n)),console.log(f.join(`
|
|
381
|
+
`)),o){let h=await xr(Ze(),r),g=cr(h);console.log(""),console.log(Dt(n)),console.log(ke(g,"whatsapp").text)}if(r.clusterEnabled){let h=Xe(),g=ge(),y=Object.keys(g.senderBindings).length,b=Object.keys(r.clusterSenderBindings??{}).length;console.log(""),console.log(Dt(n)),console.log(ce(n,"cluster")),console.log(` ${w(n,"\xB7")} ${S(n,`enabled \xB7 label ${r.clusterLabel||gm.hostname()} \xB7 bindings ${y} chat / ${b} default`)}`),console.log(` ${w(n,"node:")} ${S(n,`${h.slice(0,8)}\u2026`)}`)}return}case"commands":{let n=process.stdout,r=$();console.log(zl(xn(r),n)),console.log(""),console.log(Dt(n)),console.log(S(n,"Keys editable from chat via /config set (same trust as shell):")),console.log(w(n,Bt.join(", "))),console.log(`${w(n,"See also:")} ${S(n,_)}`);return}case"cluster":{let n=process.stdout,r=process.stderr,o=(t[0]??"status").toLowerCase();if(o==="status"){let s=$(),i=Xe();if(console.log(`${w(n,"clusterEnabled:")} ${S(n,String(s.clusterEnabled))}`),console.log(`${w(n,"clusterLabel:")} ${S(n,s.clusterLabel||"(hostname)")}`),console.log(`${w(n,"clusterRole:")} ${S(n,`${s.clusterRole} (informational; no longer used to gate traffic)`)}`),console.log(`${w(n,"node id:")} ${ce(n,i)}`),s.clusterEnabled){let a=ge(),l=Ri(s,null);console.log(""),console.log(S(n,l.wa.replace(/\*([^*]+)\*/g,"$1").replace(/`([^`]+)`/g,"$1"))),console.log(""),console.log($i(a,s,null));let d=Object.keys(a.senderBindings).length,u=Object.entries(s.clusterSenderBindings??{});if(d>0){console.log(""),console.log(ce(n,"Chat bindings (cluster-local.json)"));for(let[c,m]of Object.entries(a.senderBindings))console.log(` ${w(n,c)} ${V(n,"->")} ${S(n,`${m.nodeId} (${m.source}, since ${m.sinceIso})`)}`)}if(u.length>0){console.log(""),console.log(ce(n,"Config defaults (clusterSenderBindings)"));for(let[c,m]of u)console.log(` ${w(n,c)} ${V(n,"->")} ${S(n,m)}`)}}else console.log(""),console.log(me(n,"(cluster disabled \u2014 /config set clusterEnabled true to enable, then /c use <label-or-id> from each sender)"));return}if(o==="use"||o==="bind"){let s=t[1],i=t.slice(2).join(" ").trim();if(!s||!i){console.error(C(r,"Usage: omnish cluster use <senderE164|tg:id> <label-or-id>")),process.exitCode=1;return}let a=tk(s);if(!a){console.error(C(r,`Could not parse sender "${s}". Use +E164 (WhatsApp) or tg:<user_id> (Telegram).`)),process.exitCode=1;return}let{state:l,resolved:d}=Vc(a,i);if(!d.ok){if(d.reason==="ambiguous-label"){let c=(d.matches??[]).map(m=>`${m.nodeId}(${m.label})`).join(", ");console.error(C(r,`Label "${i}" matches multiple machines: ${c}. Use the 8-character id.`))}else console.error(C(r,`No machine matches "${i}". Run /c status from the chat first to populate the roster, or pass an 8-character node id.`));process.exitCode=1;return}console.log(`${ce(n,"cluster:")} ${S(n,`${a} -> ${d.peer.nodeId} (${d.peer.label}).`)}`);let u=$();console.log(""),console.log($i(l,u,a));return}if(o==="here"){console.error(C(r,"omnish cluster here is no longer available. Use: omnish cluster use <senderE164|tg:id> <label-or-id>")),console.error(C(r,"Or send /c here from the controller's chat on the machine you want to bind.")),process.exitCode=1;return}console.error(C(r,"Usage: omnish cluster [status | use <sender> <label-or-id>]")),process.exitCode=1;return}case"security":{let n=$(),r=Rt(n),o=t.includes("--json");console.log(o?Vl(r):ri(r,process.stdout)),lr(r)&&(process.exitCode=1);return}case"service":{ek(t);return}case"config":{await _p(t);return}case"tunnel":{await Md(t);return}case"platform":{await Qp(t);return}case"ui":{let n=rk(t);if(n.help){Sm();return}if(!Number.isFinite(n.port)||n.port<1||n.port>65535){console.error(C(process.stderr,"port must be between 1 and 65535.")),process.exitCode=1;return}let r=tm(n.token);await cm({host:n.host,port:n.port,meta:r});let o=process.stdout,s=um(n.port);console.log(""),console.log(`${Ce(o,"ui")} ${w(o,"listening")}`),console.log(`${w(o,"bind:")} ${S(o,`${n.host}:${n.port}`)}`),console.log(`${w(o,"setup token:")} ${S(o,r.token)}`),console.log(`${w(o,"token file:")} ${V(o,ct)}`),console.log(""),console.log(me(o,"Anyone on your network who can reach this port needs the token \u2014 do not expose to untrusted Wi\u2011Fi.")),console.log(""),console.log(`${w(o,"Open:")}`),console.log(` ${S(o,`http://127.0.0.1:${n.port}/`)}`);for(let i of s)console.log(` ${S(o,i)}`);console.log(""),console.log(`${w(o,"Quick link (same Wi\u2011Fi):")} ${S(o,`http://127.0.0.1:${n.port}/?token=${encodeURIComponent(r.token)}`)}`),console.log("");return}default:za(),e&&(process.exitCode=1)}}ok().catch(e=>{console.error(C(process.stderr,String(e))),process.exit(1)});
|