omnish 2.1.5 → 2.1.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -231,7 +231,7 @@ ${n.join(`
231
231
  `,{mode:384})}function Pl(){try{Ep.unlinkSync(qi)}catch{}}function eO(){return process.env.OMNISH_TOKEN?.trim()||process.env.OMNISH_TUNNEL_TOKEN?.trim()||process.env.OMNISH_DEVICE_TOKEN?.trim()||""}function rn(){let e=eO();if(e)return e;let t=C().platformToken.trim();return t||(Pn()?.token??"")}function Mp(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=C().tunnelRelayUrl.trim();if(n)return n.replace(/\/$/,"");let o=Pn()?.relayUrl?.trim();return o?o.replace(/\/$/,""):e.replace(/\/$/,"")}function jt(e){return Mp(e)}var qo=w(()=>{"use strict";Q();W()});var $e,ro=w(()=>{"use strict";$e="https://tunnel.omnish.dev"});function so(e){return(e??"").trim()}function tO(){return so(process.env.OMNISH_PLATFORM_URL)||so(process.env.OMNISH_COMM_LAYER_URL)||so(process.env.OMNISH_TUNNEL_RELAY)}function nO(){return so(process.env.OMNISH_TOKEN)||so(process.env.OMNISH_DEVICE_TOKEN)||so(process.env.OMNISH_TUNNEL_TOKEN)}function wi(){return rn()}function Vr(){return nO()?"env":C().platformToken.trim()?"config":Pn()?.token?"file":"default"}function Ko(){let e=C();return Mp(e.tunnelRelayUrl||$e)}function Qr(){return tO()?"env":C().tunnelRelayUrl.trim()?"config":Pn()?.relayUrl?.trim()?"file":"default"}function Ap(){let e=so(process.env.OMNISH_DEVICE_ID);if(e)return e;let t=C().platformDeviceId.trim();if(t)return t}function $p(){return so(process.env.OMNISH_DEVICE_ID)?"env":C().platformDeviceId.trim()?"config":"default"}function le(){let e=Ko(),t=wi();if(!e||!t)return null;let n=Ap();return{platformUrl:e.replace(/\/$/,""),token:t,deviceId:n}}var gt=w(()=>{"use strict";Q();qo();ro()});var $k={};Ji($k,{fetchPlatformAccount:()=>sn,getAttachedConfig:()=>Xr,getAttachedPlatformSnapshot:()=>lO,loadConfigForSendtoBroadcast:()=>Lp,mergeAttachedPlatformPolicy:()=>Op,parsePlatformMeResponse:()=>Mk,renamePlatformDevice:()=>_p,setAttachedPlatformSnapshot:()=>Ip,setPlatformDefaultDevice:()=>Np,snapshotFromRegisteredAccount:()=>Ak,syncAttachedPlatformPolicy:()=>$l,updatePlatformAllowlists:()=>Al});function Ml(e){let t=e.replace(/\/$/,"");return/^https?:\/\//i.test(t)?t:`http://${t}`}function oO(e){return e==="telegram"||e==="both"||e==="whatsapp"?e:"whatsapp"}function rO(e){if(!e||typeof e!="object"||Array.isArray(e))return{};let t={};for(let[n,o]of Object.entries(e))Array.isArray(o)&&(t[n]=o.map(String));return t}function sO(e,t){return Array.isArray(e)&&e.length?[...new Set(e.map(n=>String(n).toLowerCase()))]:Nu.filter(n=>t[n]?.linked===!0)}function iO(e,t){return(Array.isArray(e)&&e.length?[...new Set(e.map(r=>String(r).toLowerCase()))]:Object.entries(t).filter(([r,s])=>r!=="omnishchat"&&s.linked).map(([r])=>r)).filter(r=>r!=="omnishchat")}function aO(e){if(!e||typeof e!="object")return{};let t={};for(let[n,o]of Object.entries(e)){if(!o||typeof o!="object")continue;let r=o,s=typeof r.status=="string"?r.status:"idle";t[n]={status:s,linked:r.linked===!0||s==="linked",...typeof r.tokenConfigured=="boolean"?{tokenConfigured:r.tokenConfigured}:{}}}return t}function Mk(e){let t=e.routing,n=t&&typeof t=="object"?t:null,o=Array.isArray(n?.onlineDeviceIds)?n.onlineDeviceIds.map(String):[],r=n?.defaultDeviceId!=null?String(n.defaultDeviceId):e.defaultDeviceId!=null?String(e.defaultDeviceId):null,s=aO(e.connectors),i=rO(e.allowFromByChannel),a=sO(e.messengerChannels,s),l=iO(e.enabledChannels,s),c=a.length>0?Ls(a):oO(e.gatewayMode),u=(Array.isArray(e.devices)?e.devices:[]).map(p=>{if(!p||typeof p!="object")return null;let f=p,g=f.id!=null?String(f.id):"";return g?{id:g,label:typeof f.label=="string"?f.label:g,online:f.online===!0,isDefault:f.isDefault===!0||r!=null&&g===r,lastSeenAt:f.lastSeenAt!=null&&typeof f.lastSeenAt!="object"?String(f.lastSeenAt):null}:null}).filter(p=>p!==null);return{allowFrom:Array.isArray(e.allowFrom)?e.allowFrom.map(String):[],telegramAllowFrom:Array.isArray(e.telegramAllowFrom)?e.telegramAllowFrom.map(String):[],allowFromByChannel:i,enabledChannels:l,messengerChannels:a,gatewayMode:c,connectors:s,defaultDeviceId:r,devices:u,routing:{defaultDeviceId:r,onlineDeviceIds:o,onlineCount:typeof n?.onlineCount=="number"?n.onlineCount:o.length}}}function Ak(e,t=[],n=[]){let o=e.defaultDeviceId??null;return{allowFrom:t,telegramAllowFrom:n,allowFromByChannel:{},enabledChannels:[],messengerChannels:[],gatewayMode:e.gatewayMode,connectors:e.connectors,defaultDeviceId:o,devices:[],routing:{defaultDeviceId:o,onlineDeviceIds:[],onlineCount:0}}}function Op(e,t){let n={...e.allowFromByChannel??{},...t.allowFromByChannel};t.allowFrom.length&&(n.whatsapp=[...t.allowFrom]),t.telegramAllowFrom.length&&(n.telegram=[...t.telegramAllowFrom]);let o=t.messengerChannels.length>0?[...t.messengerChannels]:t.enabledChannels.filter(s=>Nu.includes(s)),r=o.length>0?Ls(o):t.gatewayMode;return{...e,allowFrom:[...t.allowFrom],telegramAllowFrom:[...t.telegramAllowFrom],allowFromByChannel:n,enabledChannels:t.enabledChannels.length>0?[...t.enabledChannels]:To(r),gatewayMode:r}}function Ip(e){El=e}function lO(){return El}function Xr(){let e=C();return El?Op(e,El):e}async function Lp(){let e=C(),t=le();if(!t)return e;try{return Op(e,await sn(t))}catch(n){return x.warn({err:String(n)},"sendto: could not fetch platform allowlists; using local config.json"),e}}async function sn(e){let t=await fetch(`${Ml(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 Mk(n)}async function Al(e,t){let n=await fetch(`${Ml(e.platformUrl)}/v1/me/allowlists`,{method:"PUT",headers:{"content-type":"application/json",Authorization:`Bearer ${e.token}`},body:JSON.stringify(t)}),o=await n.json().catch(()=>({}));if(!n.ok)throw new Error(o.error||`Platform PUT /v1/me/allowlists failed: HTTP ${n.status}`);return{allowFrom:Array.isArray(o.allowFrom)?o.allowFrom.map(String):[],telegramAllowFrom:Array.isArray(o.telegramAllowFrom)?o.telegramAllowFrom.map(String):[],allowFromByChannel:o.allowFromByChannel&&typeof o.allowFromByChannel=="object"?Object.fromEntries(Object.entries(o.allowFromByChannel).map(([r,s])=>[r,Array.isArray(s)?s.map(String):[]])):{}}}async function Np(e,t){let n=await fetch(`${Ml(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 o=await n.json().catch(()=>({}));throw new Error(o.error||`Platform PUT /v1/me/default-device failed: HTTP ${n.status}`)}}async function _p(e,t,n){let o=await sn(e),r=t.trim(),s="",i=o.devices.filter(u=>u.label.toLowerCase()===r.toLowerCase());if(i.length===1)s=i[0].id;else if(r.length>=8){let u=o.devices.filter(p=>p.id.startsWith(r));if(u.length===1)s=u[0].id;else if(u.length>1)throw new Error(`Ambiguous device id prefix "${r}". Use a longer prefix.`)}if(!s){let u=o.devices.map(p=>p.label).join(", ");throw new Error(u.length>0?`Unknown device "${r}". Devices: ${u}.`:`Unknown device "${r}".`)}let a=await fetch(`${Ml(e.platformUrl)}/v1/devices`,{method:"PUT",headers:{"content-type":"application/json",Authorization:`Bearer ${e.token}`},body:JSON.stringify({deviceId:s,label:n})}),l=await a.json().catch(()=>({}));if(!a.ok)throw new Error(l.error||`Platform PUT /v1/devices failed: HTTP ${a.status}`);let c=typeof l.label=="string"?l.label:n,d=typeof l.deviceId=="string"?l.deviceId:s;return{message:`Renamed device to "${c}". Use >${c} !cmd in chat.`,deviceId:d,label:c}}async function $l(e,t){try{let n=await sn(e);return Ip(n),x.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 o=Ak(t);return Ip(o),x.warn({err:String(n)},"platform GET /v1/me failed; using register ack for gatewayMode/connectors only (allowlists from local config until /v1/me works)"),o}return x.warn({err:String(n)},"platform GET /v1/me failed; attached inbound uses local config.json allowlists"),null}}var El,Zr=w(()=>{"use strict";Q();Y();gt();ee();El=null});function Il(e){let t=C();return{mode:e,version:it(),os:process.platform,arch:process.arch,nodeVersion:process.version,gatewayMode:t.gatewayMode,enabledChannelCount:Pe(t).length,sessionStartedAt:cO}}var cO,Fp=w(()=>{"use strict";Q();Bo();ee();cO=new Date().toISOString()});function bi(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}var Nl=w(()=>{"use strict"});function mO(e,t,n){return!e.clusterEnabled||bi(t.trim())!==null?!0:n?$d(n,e):!1}function Fk(e,t,n,o){return mO(e,t,n)?!0:!n||e.clusterAutoBindOnInbound===!1?!1:o?.attached?Zs(e,n).ok?(x.info({senderKey:n},"cluster auto-bound sender (attached inbound)"),!0):!1:Jw(e)&&Zs(e,n).ok?(x.info({senderKey:n},"cluster auto-bound sender (sole host)"),!0):!1}function fO(e){let t=ye();return Id(t,e)===pt(_e())}function Dk(e,t){return!e.clusterEnabled||!t||$d(t,e)||!fO(e)?null:m("This host is not bound for your sender. Send /c here or /c use <label-or-id> to bind this chat to a machine.")}var Wk=w(()=>{"use strict";Y();G();Nl();Vs();kn()});import jk from"node:fs";import hO from"node:path";function wO(){try{let e=jk.readFileSync(zi,"utf8"),t=JSON.parse(e);if(t&&typeof t=="object")for(let[n,o]of Object.entries(t)){if(!o||typeof o!="object")continue;let r={};for(let[s,i]of Object.entries(o)){let a=String(s).toLowerCase();typeof i=="string"&&i.length>0&&(r[a]=i.trim())}ki.set(n,r)}}catch{}}function _l(){U(hO.dirname(zi));let e={};for(let[t,n]of ki)Object.entries(n).length>0&&(e[t]={...n});jk.writeFileSync(zi,JSON.stringify(e,null,2)+`
232
232
  `,{mode:384})}function Fl(){Uk||(wO(),Uk=!0)}function bO(e){return yO.has(e.trim().toLowerCase())}function En(e){let t=e.trim();if(!t)return{ok:!1,error:"Name is empty."};let n=t.toLowerCase();return gO.test(n)?bO(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 Wp(e){let t=e.replace(/\r\n/g,`
233
233
  `).replace(/\n/g," ").trim();return t?t.length>Bk?{ok:!1,error:`Body too long (max ${Bk} characters).`}:{ok:!0,body:t}:{ok:!1,error:"Body is empty."}}function an(e,t){Fl();let n=ki.get(e);return!n&&t&&(n={},ki.set(e,n)),n??{}}function kO(e,t){Fl();let n=e,o=an(n,!1),r=an(zo,!1);if(t==="chat")return Object.entries(o).map(([a,l])=>({name:a,body:l,scope:"chat"})).sort((a,l)=>a.name.localeCompare(l.name));if(t==="global")return Object.entries(r).map(([a,l])=>({name:a,body:l,scope:"global"})).sort((a,l)=>a.name.localeCompare(l.name));let s=new Set([...Object.keys(o),...Object.keys(r)]),i=[];for(let a of[...s].sort()){if(a in o){let c=o[a];c!==void 0&&i.push({name:a,body:c,scope:"chat"});continue}let l=r[a];l!==void 0&&i.push({name:a,body:l,scope:"global"})}return i}function Hk(e,t="merged"){return kO(e,t)}function SO(e,t){let n=t.trim().toLowerCase(),o=an(e,!1)[n];return o!==void 0?o:an(zo,!1)[n]}function Dl(e,t){let n=En(t);if(!n.ok)return;let o=n.normalized,r=an(e,!1)[o];if(r!==void 0)return{name:o,body:r,scope:"chat"};let s=an(zo,!1)[o];if(s!==void 0)return{name:o,body:s,scope:"global"}}function Yo(e,t,n){let o=n.trim().toLowerCase(),r=e==="global"?zo:t;return an(r,!1)[o]}function Si(e,t,n,o="chat"){let r=En(t);if(!r.ok)throw new Error(r.error);let s=Wp(n);if(!s.ok)throw new Error(s.error);let i=o==="global"?zo:e,a=an(i,!0);a[r.normalized]=s.body,_l()}function Gk(e,t,n="chat"){let o=t.trim().toLowerCase(),r=n==="global"?zo:e;Fl();let s=ki.get(r);return!s||!(o in s)?!1:(delete s[o],_l(),!0)}function Jk(e,t,n){let o=En(t);if(!o.ok)return{ok:!1,error:o.error};let r=o.normalized,s=e;Fl();let i=an(s,!0),a=an(zo,!0),l=i[r],c=a[r];if(n==="global"){if(l!==void 0){let d=Wp(l);return d.ok?(a[r]=d.body,delete i[r],_l(),{ok:!0,kind:"moved",target:"global",name:r}):{ok:!1,error:d.error}}return c!==void 0?{ok:!0,kind:"noop",message:`Shortcut "${r}" is already shared on this gateway.`}:{ok:!1,error:`No shortcut "${r}" in this chat to share. Add with /shortcut add ${r} \u2026 or make the shared copy private with /shortcut set -p ${r}.`}}if(c!==void 0){let d=Wp(c);return d.ok?(i[r]=d.body,delete a[r],_l(),{ok:!0,kind:"moved",target:"chat",name:r}):{ok:!1,error:d.error}}return l!==void 0?{ok:!0,kind:"noop",message:`Shortcut "${r}" is already private to this chat.`}:{ok:!1,error:`No shared shortcut "${r}" to make private. Add with /shortcut add --global ${r} \u2026 or share from this chat with /shortcut set -g ${r}.`}}function xO(e){return e.includes(`$${qk}`)}function CO(e,t){return e.replace(vO,t)}function TO(e){let t=e.trim();if(!t)return null;let n=/^(\S+)(?:\s+([\s\S]+))?$/.exec(t);if(!n?.[1])return null;let o=En(n[1]);if(!o.ok)return null;let r=n[2]?.trim();return r!==void 0&&r.length>0?{name:o.normalized,input:r}:{name:o.normalized}}function Kk(e,t,n=0){let o=TO(t);if(!o)return null;let r=SO(e,o.name);if(r===void 0)return null;if(xO(r)){if(o.input===void 0)return{ok:!1,error:`Shortcut "${o.name}" expects input (text after the name on the same line). Example: !${o.name} <text\u2026> or /${o.name} <text\u2026>`};let s=qr(o.input,n);return s.ok?{ok:!0,text:CO(r,s.task)}:{ok:!1,error:s.error}}return{ok:!0,text:r}}var zo,Bk,gO,yO,ki,Uk,qk,vO,vi=w(()=>{"use strict";W();to();ml();zo="__omnish_shortcuts_global__",Bk=500,gO=/^[a-zA-Z0-9][a-zA-Z0-9_-]{0,31}$/,yO=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"]),ki=new Map,Uk=!1;qk="OMNISH_INPUT",vO=new RegExp(`\\$${qk}\\b`,"g")});import RO from"node:os";import*as Yk from"node-pty";function PO(e,t){return e.length<=t?e:`${e.slice(0,t)}
234
- [...truncated]`}function EO(e){if(e===void 0||e===0)return null;let t=RO.constants.signals;for(let[n,o]of Object.entries(t))if(o===e)return n;return null}function zk(e){try{process.platform==="win32"?e.kill():e.kill("SIGTERM")}catch{e.kill()}}function yt(e,t,n){return new Promise(o=>{let r=Date.now(),s=!1,i="",a=n.cwd,l=null,c=!1,d=null,u=null,p,f=y=>{if(c)return;c=!0,p!==void 0&&clearTimeout(p),d?.dispose(),d=null,o(y);let k=u;u=null,queueMicrotask(()=>k?.dispose())},h={...n.env??process.env,TERM:"xterm-256color",COLORTERM:"truecolor",...a?{PWD:a}:{}};try{Cl(),l=Yk.spawn(e,["-c",t],{name:"xterm-256color",cols:120,rows:40,cwd:a,env:h})}catch(y){f({code:null,stdout:"",stderr:String(y),durationMs:Date.now()-r,timedOut:!1,signal:null});return}p=setTimeout(()=>{s=!0,l&&zk(l)},n.timeoutMs),d=l.onData(y=>{i+=y,i.length>n.maxBytes&&(i=PO(i,n.maxBytes),l&&zk(l))}),u=l.onExit(y=>{f({code:y.exitCode,stdout:i,stderr:"",durationMs:Date.now()-r,timedOut:s,signal:EO(y.signal)})})})}var io=w(()=>{"use strict";xp()});var Qk,Vk=w(()=>{Qk={version:1,generatedAt:"2026-07-11T08:12:05.710Z",repoUrl:"https://github.com/eligapris/omnish/blob/main",entries:[{id:"docs-advanced-implementation",path:"docs/advanced/implementation.md",title:"Implementation Guide - omnish",summary:"For contributors and developers who want to understand and extend omnish.",sections:[{title:"Development Setup",body:"Prerequisites Node.js >= 20 pnpm package manager Native build tools (make, g++, etc.) Getting Started ```bash git clone https://github.com/eligapris/omnish.git cd omnish pnpm install"}],keywords:["implementation","guide","omnish","for","contributors","and","developers","who","want","to","understand","extend","development","setup"],relatedCommands:["/omnish","/github","/eligapris","/wa","/inbound","/tg","/gateway","/signal","/outbound","/index","/config","/stats"]},{id:"docs-advanced-troubleshooting",path:"docs/advanced/troubleshooting.md",title:"Troubleshooting Guide - omnish",summary:"Common issues and solutions for omnish.",sections:[{title:"Quick Reference",body:`Symptom Common Cause Solution ---------------------------------- ----------------------------------------- -------------------------------------------------------------------------- fails Node.js too old (< 22.13) ; use install script or nvm/fnm Native module failed to load Node upgraded/downgraded after install Node 22.13+; or Connection failed Network issues Check connectivity "Not in allowlist" Wrong format Use E.164 format Sessions won't start Resource limits Check session limits Messages cut off Size limits Adjust config Telegram not working Bot token Verify token Attached: probe fails / WS 400 Proxy routes to wrong port Route , to control port 8788; Attached: online but no commands Platform allowlist / routing Dashboard allowFrom; relay logs Message yourself: complete silence Older builds dropped all traffic Update omnish + relay; send after upgrade`},{title:"Attached / platform mode",body:"Symptom: fails or errors with WebSocket Solutions: Reverse proxy must forward , , , , to relay control port (8788), not the HTTP edge (8787). See tunnel relay README. Run before . Confirm URL and token: . Symptom: Device shows online on dashboard but WhatsApp commands do nothing Solutions: Set allowFrom on the platform dashboard (attached mode uses platform policy, not local ). Check relay logs for with your device id. Only one should hold the device WebSocket per token. WhatsApp LID chats: ensure relay and CLI are up to date; use on the device and look for vs . with correct in logs but lists that number: the platform stores allowlist phones as digits only (CLI shows for display). Older CLI builds compared to raw digits and always denied \u2014 update omnish so normalizes platform entries. Symptom: Attached Telegram/WhatsApp \u2014 replies but , , or plain text get no reply (device verbose log shows ) Cause: With , only fleet commands ( , , ) and traffic from senders bound to this machine are processed. is answered on the platform relay and never hits the device. Current builds auto-bind allowlisted senders on the platform default device (and on any device that receives platform-routed inbound) when is true (default). Solutions: Upgrade omnish \u2014 attached default-device traffic should work without . Non-default device: send once; that device auto-binds on first inbound. Standalone multi-host WhatsApp: send or (auto-bind does not run when remote peers are known). Or set / for single-host installs. Disable auto-bind: . Symptom: Relay logs show / every 1\u20132 minutes while stays up Solutions: Run only one per platform token (a second process replaces the WebSocket session). Reverse proxy: route and to relay control port (8788) and set / idle timeout to at least 3600s for WebSocket paths (Caddy handles this by default; nginx needs explicit config). Device and relay exchange WebSocket ping frames every 25s to keep connections alive through strict proxies. Replies sent during a brief disconnect are queued on the device (max 50) and flushed on reconnect \u2014 if churn is heavy, upgrade relay + CLI and stabilize the proxy. Symptom: Message yourself (or self-chat) \u2014 commands get no reply at all (not even \u201CNot allowlisted\u201D) Cause: WhatsApp marks messages you send from your phone as on linked devices. Older omnish builds ignored all upserts, so the documented Message yourself flow never reached the gateway. Solutions: Upgrade both the CLI ( ) and the platform relay ( ) to a version that tracks outbound message ids and routes allowlisted non-echo traffic. Confirm your number is on the platform allowlist (dashboard \u2192 Save allowlists). Run , then ; send or in Message yourself. Relay logs should show after you send a command. If you see nothing, WhatsApp may still be disconnected on the platform \u2014 check dashboard WhatsApp status. Standalone mode: same behavior applies; use and on the linked host. Symptom: warning at attach Solutions: Fix token, URL, or platform database (MongoDB on self-hosted relay). Until works, attached mode falls back to local allowlists. Full setup: Platform attached mode. Complete API/CLI/dashboard: Platform reference. WhatsApp link / unlink on platform Symptom Fix ------------------------------- ------------------------------------------------------------------------------------------------- QR never appears Dashboard Link WhatsApp or ; check relay logs Stuck after logout Reconnect on dashboard or then link again Import fails Stop local ; use Linked but dashboard shows idle with autostart; ensure volume persists Allowlists and persistence Symptom Fix -------------------------------------- ------------------------------------------------------------------------------------- Dashboard does not remember allowlists Relay needs ; click Save allowlists CLI changes not reflected on device Wait up to 5 min or restart (policy refresh) Telegram token missing in UI Expected \u2014 token is not returned; leave field empty and Link to reuse saved token"},{title:"Installation Issues",body:"Node.js version too old Symptom: exits during with a message that Node 22.13+ is required, or postinstall reports native modules ( , , ) failed to load. Cause: omnish requires Node.js 22.13+ ( ). Dependencies such as need Node 22. Native addons are compiled for the Node ABI you install with \u2014 switching Node versions later without reinstalling breaks the load step. Fix (guided): The script tries nvm or fnm, sets Node 22 as default when possible, then runs . Fix (nvm manual): Fix (already on Node 22 but natives broken): ```bash npm rebuild -g omnish"}],keywords:["troubleshooting","guide","omnish","common","issues","and","solutions","for","quick","reference","attached","platform","mode","installation"],relatedCommands:["/help","/wa help","/tg help","/contrib","/install-omnish","/fnm","/downgraded after","/platform","/v1","/control","/dashboard","/auth","/tunnel-relay","/readme","/whatsapp"]},{id:"docs-architecture-communication-layer-model",path:"docs/architecture/communication-layer-model.md",title:"Communication layer and omnish CLI \u2014 unified model",summary:"Single source of truth for how omnish works today and how the optional hosted communication layer fits. Implementation sketches: Communication layer \u2014 API & ops, Gateway config precedence.",sections:[{title:"Summary",body:"CLI installs anywhere ( ) and works without the hosted layer (standalone mode). Optional hosted communication layer exposes WhatsApp, Telegram, Discord, Slack, Matrix, and 17 more surfaces through an API. Users link messengers once on the platform. Many devices (Docker, VM, VPS, bare metal) run the CLI with a device token and talk to that layer over a long-lived channel. One messenger connection \u2192 many devices: the platform routes chat to the right attached CLI; replies return through the same layer. That single connection is what makes the platform useful for fleets and containers. Shell execution stays on each device. The communication layer is not a remote shell; the CLI is the agent (shell, PTY, files, tunnels) on the box."},{title:"Two modes",body:"Mode When Where messengers connect CLI role -------------- ------------------------------------------------------------ ------------------------------------- ----------------------------------------------------------------------------------------- Standalone No platform URL + token (default) On the same host as Full gateway: , , Attached + (or config / aliases) On the hosted communication layer Local executor + WebSocket client: register device, receive routed messages, send replies Both modes: allowlist and shell authority on the device. The platform may store policy in a dashboard, but the CLI enforces who may trigger commands before running shell on that host. Standalone (today) Documented in the README and Quick start. Baileys / Telegram bot clients run inside the gateway process; credentials live under . Attached (implemented) User links WhatsApp/Telegram on the platform dashboard (or ) \u2014 once per account. User sets platform URL + account token on each machine ( or env). On the target: , then . CLI opens WebSocket (fallback ), handles inbound messages like the standalone router, executes locally, posts replies. Setup: Platform attached mode. Full API/CLI/dashboard: Platform reference. No per-container WhatsApp QR is required for the default path."},{title:"Docker example (attached mode)",body:"A team runs an app in Docker and wants chat-driven ops inside that container without running Baileys in the image: Messengers stay on the communication layer; the container only needs outbound HTTPS to the layer API. See Docker gateway golden path."},{title:"Environment contract",body:"Purpose Canonical Also accepted --------------------------------- --------------------- ---------------------------------------------- Platform base URL , Account token (tunnel + attached) , Optional device id in config Config file keys: ( ), ( ), ( ). Precedence: env \u2192 \u2192 tunnel auth file. Implementation: ."},{title:"Security boundaries",body:"Concern Standalone Attached -------------------------------- ----------------------------- --------------------------------------------------------------- Who runs shell Local gateway Local CLI on device Messenger secrets on gateway host Communication layer (connectors) Stolen device token N/A Risk to that attach point; revoke in dashboard Platform sees message content N/A (direct to gateway) Yes, for routing \u2014 document retention and policy Remote shell from platform alone No No \u2014 requires valid device token + allowlisted sender on device Connector secrets at rest N/A AES-256-GCM when set (see ADR-0014) Never echo full tokens in chat replies (same as tunnel tokens today)."},{title:"Channel capability matrix",body:"Peer prefixes and surface ids match between standalone ( ) and platform ( ). Platform = linkable on tunnel-relay dashboard/API. Channel Standalone Platform Inbound media Outbound media Host notes -------------- ---------- --------------------------- ------------- ----------------- --------------------------------- whatsapp Yes Yes (dedicated QR routes) Yes Yes Baileys auth on relay disk telegram Yes Yes (dedicated token route) Yes Yes Bot token in ConnectorSession discord Yes Yes Partial Yes (text + file) Bot token slack Yes Yes No Text Socket Mode app + bot tokens matrix Yes Yes No Text Homeserver + access token irc Yes Yes No Text IRC host/nick on relay signal Yes Yes (dedicated QR routes) No Text Requires on relay googlechat Yes Yes (webhook) No Text Hook URL registered on link mattermost Yes Yes (webhook) No Text baseUrl + token feishu Yes Yes (webhook) No Text appId + appSecret line Yes Yes (webhook) No Text channelToken msteams Yes Yes (webhook) No Text Outbound webhook URL nextcloud-talk Yes Yes (webhook) No Text baseUrl + token synology-chat Yes Yes (webhook) No Text outgoingUrl nostr Yes Yes No Text NIP-04 DMs zalo Yes Yes (webhook) No Text OA token zalouser Yes Yes No Text Session polling (best-effort) qqbot Yes Yes (webhook) No Text appId + bot token tlon Yes Yes No Text Urbit ship URL polling twitch Yes Yes No Text OAuth + channel name imessage Yes No No Text macOS standalone only bluebubbles Yes No No Text macOS BlueBubbles standalone only Enforced in CI: ."},{title:"Multi-device routing (attached mode)",body:"When several machines run on one platform account, inbound chat is routed on the relay before it reaches a CLI: \u2014 one-shot route to a device by dashboard label (e.g. ) \u2014 route by device id prefix (8+ characters) Unprefixed \u2014 default (primary) device only; fails with online hints if primary is offline \u2014 list registered devices and online status (platform replies directly) \u2014 rename device label for routing (dashboard and CLI also supported) Set labels via or dashboard Devices. Rename via dashboard, chat, or . Set primary via dashboard Set default. See ADR-0013."},{title:"Relation to this repository",body:"Component Status --------------------------------------------------------------- -------------------------------------------------------------- Standalone gateway ( , ) Implemented in Attached mode client ( , platform WebSocket) Implemented in , Hosted communication layer (relay + dashboard) Implemented in (deploy separately)"},{title:"See also",body:"Platform attached mode \u2014 connect, link, configure, run Platform reference \u2014 complete API, CLI, dashboard, persistence Vision \u2014 hosted communication layer \u2014 short product summary Platform layer spec \u2014 API sketch, tokens, threat model Gateway config precedence \u2014 config merge in attached mode Relay operator README \u2014 deploy, paths, dashboard docs/ideas/ \u2014 historical voice-note inputs"}],keywords:["communication","layer","and","omnish","cli","unified","model","single","source","of","truth","for","how","works","today","the","optional","hosted","fits","implementation","sketches","api","ops","gateway","config","precedence","summary","two","modes","docker","example","attached","mode","environment","contract","security","boundaries","channel","capability","matrix","multi-device","routing","relation","to","this","repository","see","also"],relatedCommands:["/readme","/guides","/quick-start","/telegram on","/platform","/device","/control","/platform-attached-mode","/cli","/dashboard","/platform-reference","/tunnel"]},{id:"docs-architecture-gateway-config-precedence",path:"docs/architecture/gateway-config-precedence.md",title:"Gateway configuration precedence",summary:"How effective configuration is computed. See Communication layer model for standalone vs attached modes.",sections:[{title:"Sources (ordered low \u2192 high priority)",body:"Standalone ( without platform token) Compiled defaults \u2014 values baked into the binary when a key is unset. Local \u2014 under . Environment variables \u2014 e.g. , . Chat \u2014 allowlisted senders; same trust as shell. Higher layers win per key. Attached ( with platform URL + token) Compiled defaults Local \u2014 host paths, shell, tunnel, jobs, etc. Platform account ( ) \u2014 wins for policy keys: , , (derived from linked connectors on the platform). Not written back to disk by default. Environment variables \u2014 still override local file for host keys; do not replace platform allowlists unless you also change them on the dashboard. Chat \u2014 updates local file only in attached mode; platform allowlists remain authoritative for inbound from messengers on the layer. If fails at connect, the CLI logs a warning and uses local for allowlists (or partial data from the WebSocket ack: + connectors only)."},{title:"Keys that stay host-only (never taken from platform)",body:", job limits, sync paths, webhook ports/tokens on the device Baileys auth paths, local tunnel client settings WhatsApp session blobs and Telegram bot tokens in attached mode (connectors run on the platform) , , \u2014 cluster host policy; attached default device uses platform allowlists only as input when seeding auto-bind"},{title:"Keys owned by the platform in attached mode",body:", \u2014 set on the dashboard; device enforces the platform copy on inbound WebSocket messages. \u2014 derived from which connectors are linked on the platform ( , , or )."},{title:"Merge algorithm (attached inbound)",body:"```text effective = loadConfig() # local host + defaults if platform snapshot loaded: effective.allowFrom = platform.allowFrom effective.telegramAllowFrom = platform.telegramAllowFrom effective.gatewayMode = platform.gatewayMode"}],keywords:["gateway","configuration","precedence","how","effective","is","computed","see","communication","layer","model","for","standalone","vs","attached","modes","sources","ordered","low","high","priority","keys","that","stay","host-only","never","taken","from","platform","owned","by","the","in","mode","merge","algorithm","inbound"],relatedCommands:["/v1","/me","/config","/guides","/platform-reference","/config set","/tokens on","/platform","/account-sync","/default-device"]},{id:"docs-architecture-overview",path:"docs/architecture/overview.md",title:"Architecture Overview - omnish",summary:"",sections:[{title:"Executive Summary",body:"omnish is a secure, deterministic messaging-to-shell gateway that bridges messaging platforms (WhatsApp, Telegram) to system shell access. The architecture follows a thin transport adapter pattern with a unified core, explicit security controls, and comprehensive session management. Local control plane: When the gateway process ( ) is up, it may expose a localhost-only control endpoint (metadata in ) so a separate process can request outbound file sends that reuse the same Baileys/grammY sessions\u2014avoiding a second login. This is optional machinery for CLI ergonomics; inbound chat traffic still flows through the transports above."},{title:"Key Architectural Principles",body:"Thin Transport Adapter Pattern Transport Layer: Platform-specific implementations (WhatsApp, Telegram) Adapter Layer: Normalizes all inputs to format Core Layer: Unified business logic and message routing Persistence Layer: Configuration and state management Deterministic Behavior No AI or agent layer Rule-based message routing Predictable command precedence Explicit state management Security-First Design Explicit allowlists required No anonymous access Per-peer session isolation Minimal attack surface"},{title:"System Architecture",body:"High-Level Diagram Component Responsibilities Layer Component Responsibility --------------- --------------- -------------------------------------------- Transport WhatsApp WebSocket lifecycle, QR auth, media handling Telegram Long polling, bot API, formatting Adapter Inbound Normalize messages to Outbound Platform-specific message sending Core Gateway Transport multiplexing, lifecycle management Router Command dispatch with precedence rules Session Manager Per-peer working directories and state Apps Manager Interactive PTY session lifecycle Job Manager Background process execution Persistence Config Configuration loading and validation Sessions JSON-based state persistence Files Log files, session output, job output"},{title:"Data Flow Architecture",body:"Message Processing Pipeline Command Precedence Flow Messages are processed in strict precedence order: Free Shell Toggle: / Sync Commands: (e.g., ) System Commands: (e.g., , ) App Shorthand: (e.g., ) Free Shell Mode: Plain text execution Attached App: Forward to focused PTY session Help Fallback: Display help message"},{title:"Security Architecture",body:"Allowlist System Peer Key Model WhatsApp: or normalized phone number Telegram: Hashed for storage: SHA-1 prefix for log directories Session isolation: Each peer has separate state Security Boundaries Transport Boundary: Platform-specific authentication Authorization Boundary: Allowlist validation Execution Boundary: Command and process isolation Data Boundary: Per-peer state separation"},{title:"Session Management Architecture",body:"Session Context Persistence Session Lifecycle Load: From JSON storage or create default Update: Persist changes immediately Isolate: Per-peer separation with migration support Cleanup: Remove on session destruction"},{title:"Transport Architecture",body:"Transport Interface All transports implement the same core interface: Transport-Specific Features Transport Authentication Message Limit Features --------- -------------- ------------- -------------------------------- WhatsApp QR code 3500 chars Media download, auto-reconnect Telegram Bot token 4096 chars HTML formatting, webhook support"},{title:"Output Processing Architecture",body:"Output Flow Output Processing Pipeline Buffer: Collect output chunks Debounce: Wait for output completion ( ) Chunk: Split into transport-sized pieces Format: Strip ANSI, add prefixes, etc. Send: Deliver via appropriate transport"},{title:"State Management Architecture",body:"State Types Configuration: Global settings Session Context: Per-peer state Job State: Background job metadata App State: Interactive session state Persistence Strategy State Type Format Location Access Pattern ---------- ------ ------------------------- --------------------------------- Config JSON Load at startup, update on change Sessions JSON Load on demand, append updates Jobs Files Streaming write, read on demand Apps Files Streaming write, read on demand"},{title:"Error Handling Architecture",body:"Error Categories Transport Errors: Connection issues, timeouts Validation Errors: Invalid input, malformed commands Execution Errors: Command failures, process timeouts Resource Errors: Memory limits, session limits Error Handling Flow Capture: Error detected at source Log: Structured logging with context Notify: User-friendly error message Recover: Graceful degradation where possible"},{title:"Extension Points",body:"Transport Extension Command Extension Configuration Extension"},{title:"Performance Considerations",body:"Memory Management Session Limits: Prevent unbounded growth Output Buffering: Size-limited buffers Cleanup: Proper resource disposal Network Optimization Message Chunking: Minimize API calls Connection Reuse: Persistent connections Backoff Strategy: Exponential backoff for failures I/O Optimization Async Operations: Non-blocking file I/O Lazy Loading: Load data on demand Output Streaming: Real-time output delivery This architecture overview provides the foundation for understanding omnish's design principles and implementation patterns."}],keywords:["architecture","overview","omnish","executive","summary","key","architectural","principles","system","data","flow","security","session","management","transport","output","processing","state","error","handling","extension","points","performance","considerations"],relatedCommands:["/grammy sessions","/inbound","/gateway","/outbound","/jobs","/apps","/command","/bg","/job","/pty","/config","/sessions"]},{id:"docs-architecture-platform-layer-spec",path:"docs/architecture/platform-layer-spec.md",title:"Communication layer \u2014 API and operations sketch",summary:"Implementation detail for the unified model in Communication layer model. The attached CLI client is implemented in and . The hosted relay (connectors, dashboard, routing) lives in .",sections:[{title:"Goals",body:"Messenger connectors on the layer: WhatsApp, Telegram, others \u2014 linked once per workspace from a dashboard. Device registry and routing: many CLIs attach with ; inbound chat is routed to the correct device; replies return through the layer. Token issuance and lifecycle from the dashboard (create, rotate, revoke). Minimal env on each device: + (legacy aliases: , , ). Non-goals: Running user shell or PTY in the cloud. Replacing local enforcement of allowlists before command execution on each device. Requiring the hosted layer for open-source standalone use ( on the host without env)."},{title:"Operating modes (messenger termination)",body:"Mode Messenger clients Device (CLI) -------------- ------------------------------------------------------------- -------------------------------------------- Standalone On gateway host ( , Baileys / Telegram bot today) Same process as messengers Attached On communication layer (connectors) CLI only: API/WebSocket client + local shell See Communication layer model \u2014 Two modes."},{title:"Threat model (summary)",body:"Asset Risk Mitigation ----------------------------- ----------------------------------- -------------------------------------------------------------------------------------------------- Device token Theft \u2192 control of one attach point Short TTL, rotation, revoke; scoped to one device slot Workspace / dashboard session Account takeover Strong auth, audit log on connector and device changes Message content on layer Privacy / retention Policy, encryption in transit, minimal logging; no training on content by default (product policy) Signed config blob Forged defaults on device Signing keys, , host-only keys; see Gateway config precedence Chat as secret channel Token pasted in DM Never echo full secrets in replies Trust boundary: The communication layer routes messages and holds connector credentials in attached mode. Each CLI enforces allowlists and runs shell locally. Layer compromise must not run shell on a device without a valid device token and allowlisted sender on that device."},{title:"Token scopes (recommended)",body:"Scope Purpose Typical lifetime ------------------- ------------------------------------------------------ --------------------- Long-lived channel for one CLI attach point Rotatable; per device One-time or short exchange when creating a device slot Minutes Fetch non-secret config subset for a device Hours\u2013days Relay bearer (may be issued by layer) Per relay policy Human dashboard (not for CLI) Session Rules: narrow scopes, stable token ids for revocation, no user-facing \u201Cgod\u201D token."},{title:"Implemented API (this repository)",body:"The relay implements a single-account model (not multi-workspace). Authoritative route list, request bodies, and CLI commands: Platform reference Summary: Area Implemented paths -------- ------------------------------------------------------------------ Auth , Account , , Devices , Telegram WhatsApp , , , Attach , UI"},{title:"Future API sketch (not implemented)",body:"The following were early design notes; do not assume they exist on the relay today: Multi-workspace ( ) Separate device tokens per slot (today: one account bearer token) , scoped bootstrap tokens SSE alternative to WebSocket"},{title:"Compliance and logging",body:"Attached mode: layer may process message content for delivery; document retention and access in privacy policy. Avoid logging message bodies in application logs by default."},{title:"Relation to this repository",body:"Communication layer service: (deploy separately; MongoDB + dashboard). CLI attached mode: , \u2014 WebSocket client + local shell. Standalone: / on the same host (unchanged when platform env is unset). Operator guides: Platform attached mode, Platform reference."}],keywords:["communication","layer","api","and","operations","sketch","implementation","detail","for","the","unified","model","in","attached","cli","client","is","implemented","hosted","relay","connectors","dashboard","routing","lives","goals","operating","modes","messenger","termination","threat","summary","token","scopes","recommended","this","repository","future","not","compliance","logging","relation","to"],relatedCommands:["/platform","/gateway","/attached","/tunnel-relay","/contrib","/guides","/platform-reference","/platform-attached-mode","/websocket client","/auth","/signup","/login"]},{id:"docs-architecture-routing",path:"docs/architecture/routing.md",title:"Message Routing Architecture - omnish",summary:"",sections:[{title:"Introduction",body:"The message routing system is the heart of omnish, responsible for determining how incoming messages are processed and executed. It follows a deterministic precedence model with clear rules for each message type."},{title:"Routing Overview",body:"Message Flow Core Components Allowlist Validator: Ensures user has permission Message Normalizer: Converts transport-specific formats Session Loader: Retrieves per-peer state Router: Applies precedence rules and dispatches Executors: Command-specific handlers"},{title:"Message Precedence Rules",body:"Precedence Hierarchy Messages are evaluated in strict order from highest to lowest precedence: Free Shell Mode Toggle ( / ) Synchronous Commands ( ) System Commands ( ) App Session Shorthand ( ) Attached App Sessions (when focused and running) Lone-URL auto-download ( or LLM off \u2192 background ) Free Shell Mode (when enabled) Chat LLM fallback (optional plain-text handler when configured) Help Fallback ( ) Rule Implementation"},{title:"Command Types and Handling",body:"Free Shell Mode Toggle Purpose: Enable/disable direct shell execution Pattern: or Synchronous Commands Purpose: Execute shell commands immediately Pattern: System Commands Purpose: Control omnish functionality Pattern: App Session Shorthand Purpose: Quick interaction with app sessions Pattern: Free Shell Mode Purpose: Execute any text as shell command Pattern: Plain text (when enabled) Attached App Sessions Purpose: Send input to focused PTY session Pattern: Plain text (when session focused)"},{title:"Special Cases and Edge Handling",body:"Shortcut Expansion After stripping the sync prefix ( by default) or when an unknown slash is not a built-in command, omnish calls : only \u2014 expand the stored body (legacy bare shortcuts). \u2014 if the body contains , substitute trimmed input for every placeholder; if the placeholder is present but input is missing, reply with a usage error. Bodies without ignore extra words after the name. Expansion re-enters once with (no nested shortcut expansion). Command Precedence Override Certain commands always take precedence: Error Handling"},{title:"Session Integration",body:"Session Context The router uses session context for state: Session Management"},{title:"Transport Integration",body:"Peer Key Normalization Transport-Specific Handling"},{title:"Performance Considerations",body:"Optimization Strategies Session Caching: Cache frequently accessed sessions Command Caching: Cache command results for identical inputs Output Debouncing: Buffer output to reduce message spam Lazy Loading: Load sessions only when needed Concurrency Handling"},{title:"Extension Points",body:"Adding New Command Types Custom Command Handlers"},{title:"Fleet and config slash commands",body:"The authoritative dispatch order for messages that start with is implemented in (not the pseudocode snippets earlier in this doc). Highlights: runs before fleet aliases so paths like never collide with . Fleet commands accept , , or ( ): only a standalone token or + rest matches \u2014 paths such as do not match the shortcut. (also , ) sets ; reapplies config while is active. See Cluster and chat configuration for behavior and Configuration guide for fields."},{title:"Testing and Validation",body:"Unit Tests Integration Tests This message routing architecture ensures predictable, efficient processing of all incoming messages while maintaining security and session isolation."}],keywords:["message","routing","architecture","omnish","introduction","overview","precedence","rules","command","types","and","handling","special","cases","edge","session","integration","transport","performance","considerations","extension","points","fleet","config","slash","commands","testing","validation"],relatedCommands:["/command","/dl","/dl job","/disable direct","/name","/new","/path","/router","/config","/config set","/computers","/pcs"]},{id:"docs-architecture-security",path:"docs/architecture/security.md",title:"Security model",summary:"omnish bridges WhatsApp and/or Telegram direct messages to shell commands on your machine. Security is explicit allowlists plus local filesystem and process boundaries. There is no cloud relay for commands: whoever can send messages as an allowed identity can execute code as the OS user running .",sections:[{title:"Trust boundaries",body:"Allowlisted identities are credentials. If an attacker can spoof or compromise an allowed WhatsApp number or Telegram user id, they get the same power as you gave on that host. Wildcards are rejected. must never contain . Secrets on disk: WhatsApp session material lives under the data directory ( / / legacy ). Telegram bot tokens live in or . Restrict permissions so other Unix users cannot read them. Chat config: lets allowlisted users edit from chat (same trust as running shell commands). Do not treat DMs as \u201Clow privilege.\u201D Multiple Telegram bots: If and several hosts run Telegram, use a different bot token per host; one token cannot be long-polled twice (see finding in ). Interactive terminal ( ): Same trust as running a shell on that machine. It is not gated by WhatsApp/Telegram inbound allowlists\u2014those lists still apply to messages arriving through the gateways. Gateway control ( ): Written only while is active; carries localhost connection info and a token so can request outbound media through the existing gateway. Anyone who can talk to 127.0.0.1 as your user could misuse it\u2014treat host-local access like filesystem access. : Sends files to arbitrary WhatsApp numbers or Telegram chats through your linked session, comparable to sending those files manually from the linked WhatsApp device or bot\u2014powerful and intentional. Tunneling ( , when ): Publishes public URLs to local HTTP/TCP ports. Anyone with the URL can reach the forwarded service; chat tunneling is gated by the same allowlists as shell commands."},{title:"Automated posture checks",body:"The same rules run in three places: Surface How ------- ------------------------------------------------------------------------------------------------------------------------- CLI (plain text) or Chat , , Startup refuses to start if any error-severity finding is present; warnings are printed but do not block Automated checks cover configuration and local Unix permissions. They do not replace firewall rules, SSH hardening, malware scanning, or review of who physically accesses the machine. Severity levels error \u2014 Blocks until fixed (or until you change / token so the check no longer applies). warn \u2014 Shown on startup and in reports; you should understand and usually fix. info \u2014 Informational (for example, env var overrides)."},{title:"Finding codes and remediation",body:"Code Severity Meaning What to do ---------------------------- -------- --------------------------------------------------------------------------------- --------------------------------------------------------------- error contains Remove from in . error Telegram transport enabled but no token Set in config or . error Configured binary does not exist Install the shell or point at a valid absolute path. error is but path empty Set or change mode. error Fixed receive path not absolute Use an absolute . warn WhatsApp enabled, empty warn Telegram enabled, empty warn is true Set to unless you trust every recipe. warn is not an absolute path Use e.g. in config. warn Could not stat Fix permissions/path. warn group/world readable or writable on config file. warn Process UID is root Run as a normal user when possible. warn Data directory group/world accessible on data dir ( or default ). warn Jobs directory group/world accessible (only checked if data dir is already tight) on the jobs directory under the data dir. warn WhatsApp auth dir readable by others on under the data dir. info set; overrides config Unset env if you want config file to win. info Cluster + Telegram: same token must not be used on two running gateways Use one bot per host or run Telegram on fewer machines. Unix permission checks are skipped on Windows."},{title:"JSON output for automation",body:"prints: Exit code if any error-severity finding exists (same as plain text)."},{title:"Related commands",body:"\u2014 Includes a one-line security summary. In chat: for a short hardening checklist. See also the user guide Security section in User Guide."}],keywords:["security","model","omnish","bridges","whatsapp","and/or","telegram","direct","messages","to","shell","commands","on","your","machine","is","explicit","allowlists","plus","local","filesystem","and","process","boundaries","there","no","cloud","relay","for","whoever","can","send","as","an","allowed","identity","execute","code","the","os","user","running","trust","automated","posture","checks","finding","codes","remediation","json","output","automation","related"],relatedCommands:["/security help","/security summary","/or telegram","/config set","/telegram","/sendto","/tunnel","/tcp ports","/security","/security tips","/run","/bin","/bash"]},{id:"docs-features-agents-from-chat",path:"docs/features/agents-from-chat.md",title:"Agent integrations from chat",summary:"Install Cursor and Claude Code notify hooks and skills from allowlisted chat or the host CLI \u2014 without downloading from omnish.dev.",sections:[{title:"Chat commands",body:"Command Gate Purpose ------------------------------------- ---------------------- -------------------------------------------- \u2014 Command list \u2014 Health check on gateway host \u2014 Paste bundled offline \u2014 / / guide Run bundled install on gateway host Remove omnish IDE entries Enable mutating installs: Same trust model as and . reports when enabled."},{title:"Host CLI (always available)",body:"Bundles ship inside the npm package under and (copied from at build time)."},{title:"What gets installed",body:"Cursor See contrib/omnish-cursor/README.md \u2014 hooks in , skill in . Claude Code See contrib/omnish-claude/README.md \u2014 Stop hook in , skill in . Terminal agents (/run, /apps) No IDE install required. Set and use env injected into long-running jobs: \u2014 replying peer \u2014 template for mobile briefs on the gateway host"},{title:"Prerequisites",body:"(or ) on the gateway host Allowlist or platform token configured For hooks: python3 and omnish on PATH when install runs"},{title:"Related",body:"Cursor integration (contrib) Service from chat Media commands"}],keywords:["agent","integrations","from","chat","install","cursor","and","claude","code","notify","hooks","skills","allowlisted","or","the","host","cli","without","downloading","omnish","dev","commands","always","available","what","gets","installed","prerequisites","related"],relatedCommands:["/run","/apps","/agents notify","/agents help","/agents doctor","/agents script","/agents install","/agents uninstall","/config set","/service install","/dl install","/omnish-cursor"]},{id:"docs-features-background-jobs",path:"docs/features/background-jobs.md",title:"Background jobs \u2014 omnish",summary:"Background jobs run a single shell command asynchronously in the current chat\u2019s working directory while you keep using the chat. Output is appended to a log file under your data directory; you can pull the last N lines or only new bytes since your last for that job in this chat.",sections:[{title:"Commands (implemented)",body:"Command Meaning ----------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------- Start a background job; reply includes an 8-char job id and hints for and . \xB7 \xB7 Same as , but stores a name you can use instead of the hex id with , , and . \xB7 Start a job that sends a completion notification to your chat when it exits (includes exit code, duration, and command summary). Combine flags: named job with completion notification. List recent jobs (up to 20), newest first: id (and name if set), status ( \\ \\ ), exit code, duration, command preview. Last lines of the log (default from ; max 500 if you pass a number: or ). Incremental: new log bytes since your last of this job (resolved id) in this chat (per-chat cursor). Send SIGTERM to the running process (or best-effort to the recorded pid if the child already detached). Job ids are 8-character hex (e.g. ). Names are optional labels (letters, digits, , , , up to 64 characters). If several jobs share the same name, / / resolve to the newest (most recently started) matching job. Resolving -shaped tokens: if a job with that id exists on disk, the token is treated as an id; otherwise it is treated as a name (so a name that looks like 8 hex digits still works when no job file uses that id). There is no , , or in chat \u2014 use and / instead. ( exists for gateway shutdown; it is not exposed as a slash command.)"},{title:"Behavior details",body:"Working directory: Same as session cwd ( applies before ). Environment: Inherited from the omnish gateway process (the Node process running ), not from a prior in chat \u2014 each and runs a new shell. Set env vars in the shell that starts the gateway, or put them in the job command line. Timeouts: applies to synchronous commands, not to children. Long-running jobs are not auto-killed by that setting. Logs: and (see )."},{title:"Configuration",body:""},{title:"Job lifecycle (actual statuses)",body:"From : running \u2014 child spawned; log growing. done \u2014 process exited (or spawn error); / recorded in meta. killed \u2014 user ran (SIGTERM); meta updated. Optional field is stored in when you start a job with / ."},{title:"Completion notifications",body:"When you start a job with (or ), the gateway sends a message to your chat as soon as the process exits: The notification includes: Exit code (0 for success, non-zero for failure) or signal name (e.g. SIGTERM) Duration of the run Command that was executed This is useful for long builds, deployments, or test suites where you want to walk away and get notified when it finishes. Combine with for readability: Notifications are sent via the same transport as the chat (WhatsApp or Telegram). If the gateway shuts down before the job finishes, the notification is lost."},{title:"Examples",body:"Same using the printed id:"},{title:"Compared to Cowork",body:"---------------- --------------------------- -------------------------------------- Scheduling One-shot , , , etc. Notifications Opt-in per job ( ) Per task ( + ) Queue / catch-up No Yes (SQLite + pending queue) Typical use Ad hoc long command Recurring or on-demand saved tasks See cowork.md."},{title:"Troubleshooting",body:"No output in chat: Streaming behavior depends on the gateway; use or for the log file. Lost jobs on gateway restart: In-memory handles are cleared; log and meta files on disk may still be present under . Verbose gateway logs: ---"},{title:"Change log \u2014 named background jobs (2026-05-08)",body:"Area Change -------------------------------------------------------- ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ; , , , ; . Parse named ; , , accept id or name via ; lists name when set. help and main help bullet updated for names. Unit tests for parsing, validation, and name/id resolution. User-facing docs This file, user-guide.md, quick-start.md, README.md, comprehensive-documentation.md, practical-guide-for-agents.md, CHANGELOG.md."}],keywords:["background","jobs","omnish","run","single","shell","command","asynchronously","in","the","current","chat","working","directory","while","you","keep","using","output","is","appended","to","log","file","under","your","data","can","pull","last","lines","or","only","new","bytes","since","for","that","job","this","commands","implemented","behavior","details","configuration","lifecycle","actual","statuses","completion","notifications","examples","compared","cowork","troubleshooting","change","named","2026-05-08"],relatedCommands:["/bg","/jobs","/tail","/log","/kill","/log abcdef12","/log mybuild","/stats","/history","/kill all","/shell","/src"]},{id:"docs-features-chat-agent-daemon",path:"docs/features/chat-agent-daemon.md",title:"Chat agent daemon (optional)",summary:"When is true and is set, plain inbound chat (that would otherwise hit \u201CNo command matched\u201D or LLM fallback) is queued to a long-lived process on stdin\u2014one daemon per peer by default. The gateway does not stream the child\u2019s stdout/stderr to mobile; the process should send curated updates with (same model as detached ).",sections:[{title:"Enable",body:"Edit on the gateway host: Copy and customize , or point at your own wrapper. ---"},{title:"Notify environment (parity with `/run`)",body:"On spawn, omnish merges the same notify env as recipe/app starts: Variable Purpose ---------------------------- ----------------------------------------------------------------------------------------------- Initiator peer ( / ) Template: Board-style guidance when is true (daemon sessions are treated as muted) Use or on milestones (ack, progress, question, done, blocked)\u2014not every log line. See also Curated mobile notifications. ---"},{title:"Chat commands",body:"Command Action --------------- -------------------------------------------- List running daemons and queue depth Stop daemon for current peer Stop and clear daemon state for current peer Short help ---"},{title:"Related keys",body:"Key Default Purpose ---------------------- ------- ---------------------------------------------------------- Master switch Shell command for (long-lived; reads stdin) One process per peer; = shared daemon Max queued lines per daemon before reject Include on spawn ---"},{title:"See also",body:"Chat LLM fallback \u2014 one-shot subprocess per message System agents and"}],keywords:["chat","agent","daemon","optional","when","is","true","and","set","plain","inbound","that","would","otherwise","hit","no","command","matched","or","llm","fallback","queued","to","long-lived","process","on","stdin","one","per","peer","by","default","the","gateway","does","not","stream","child","stdout/stderr","mobile","should","send","curated","updates","with","same","model","as","detached","enable","notify","environment","parity","/run","commands","related","keys","see","also"],relatedCommands:["/stderr to","/sendto peer","/run","/config","/absolute","/path","/to","/your-chat-agent-wrapper","/chat-agent-wrapper","/scripts","/app starts","/agent-notify"]},{id:"docs-features-chat-llm-fallback",path:"docs/features/chat-llm-fallback.md",title:"Chat LLM fallback (optional)",summary:"When this feature is on, a plain inbound message that would normally get \u201CNo command matched\u201D is handled silently: omnish runs your in the background (with limits and a restricted environment), then sends the subprocess output back to the same WhatsApp or Telegram chat. In , the reply is printed to the terminal instead.",sections:[{title:"Where to change settings",body:"What Where -------------------------- --------------------------------------------------------------------------------------------------------------------------------------------------------------------- Config file . If is unset, omnish uses (or the legacy tree if does not exist). Template / all keys Repo root \u2014 copy the entries into your real . Confirm data directory Run on the gateway host, or . Edit the file with any text editor on the gateway host (SSH, local console, or your usual config workflow). Valid JSON is required (trailing commas are not allowed). Reload behavior: The gateway calls when handling each inbound message, so changes to fields in normally apply on the next message without restarting . API keys and environment variables: The subprocess receives a filtered copy of the gateway process environment (see below). Keys such as are only present if they were set when the gateway was started. If you add or change API keys in the shell profile or systemd unit, restart so the parent process picks them up. ---"},{title:"How to enable (step by step)",body:"Open (or ) on the machine that runs . Add or merge these fields (defaults shown; enable only when ready): Set to a single shell command passed as the argument to (same pattern as sync commands). Use an absolute path to a wrapper script if the default in the sandbox is too minimal. Smoke test: With the gateway running, send a plain line from an allowlisted chat (not starting with or your , not text). You should get no immediate \u201CNo command matched\u201D reply; after the subprocess finishes, the combined stdout/stderr (trimmed and capped) is sent back. Sandbox / real isolation: omnish runs the command in a dedicated working directory (temp dir under the data directory unless is set) and a reduced env. For Docker, , or other tools, put that logic inside your wrapper and point at it. ---"},{title:"What the subprocess receives",body:"Shell: Config key (e.g. ). Working directory: if non-empty (created if needed); otherwise a new temp directory under the omnish data dir for that run (removed afterward). Stdin (default): The inbound chat text (truncated to ). If is true, stdin is not piped the same way; use (and a PTY) instead. Environment (high level): \u2014 e.g. or \u2014 same payload as stdin (piped mode) Common vars: , , , , , , , , All vars from the parent process Parent vars whose names end with or Place API keys in the gateway environment (systemd , shell before , etc.) so they are inherited and forwarded by the allowlist above. ---"},{title:"When the fallback runs (and when it does not)",body:"Runs only if all of the following hold: is true and is non-empty after trim. The message is plain text that reached the router\u2019s final \u201Cno match\u201D branch: not a command, not shell, not / , not app line, not consumed by a focused session, and free shell mode is off for that peer. Does not run (examples): unknown , , , media-only messages, messages dropped by cluster binding on non-primary hosts, or any path that already returns a normal reply. ---"},{title:"Related keys (reference)",body:"Key Purpose ------------------------ ------------------------------------------------------------------------------------------------ Master switch ( by default). Full command string for . Wall-clock limit (ms). Max size of text passed in (stdin / ). Max captured output (stdout+stderr in piped mode). Use PTY execution (for CLIs that require a TTY); prefer in the script. Fixed cwd; empty = ephemeral dir per run. Implementation checklist and code pointers: chat-llm-fallback-implementation-plan.md. ---"},{title:"Security note",body:"Allowlisted chats already have shell-level trust on the gateway host. Turning this on means accidental plain messages can invoke your command and any API usage inside it. Keep allowlists small, use timeouts and caps, and wrap external agents in a policy you control."}],keywords:["chat","llm","fallback","optional","when","this","feature","is","on","plain","inbound","message","that","would","normally","get","no","command","matched","handled","silently","omnish","runs","your","in","the","background","with","limits","and","restricted","environment","then","sends","subprocess","output","back","to","same","whatsapp","or","telegram","reply","printed","terminal","instead","where","change","settings","how","enable","step","by","what","receives","it","does","not","related","keys","reference","security","note"],relatedCommands:["/config help","/config","/absolute","/path","/to","/your-wrapper","/stderr","/bin","/bash","/apps","/foo","/help","/chat-llm-fallback-implementation-plan"]},{id:"docs-features-cluster-and-chat-config",path:"docs/features/cluster-and-chat-config.md",title:"Multi-host cluster and chat configuration",summary:"This document describes the chat-driven cluster ( , , ) and for viewing or editing from WhatsApp or Telegram.",sections:[{title:"Cluster (per-sender bindings)",body:`Use one WhatsApp phone number with multiple linked devices \u2014 each machine runs once. Every machine that runs will receive the same DMs through WhatsApp multi-device. There is no shared file. There is no Syncthing/NFS to set up. Coordination flows entirely through the WhatsApp chat itself: every reply omnish sends carries an invisible footer that other linked omnish hosts read to learn who is online and which machine each sender is currently bound to. Telegram cannot carry this coordination (different bot tokens cannot see each other), so the per-sender binding still applies on Telegram, but you only get convergence between linked hosts on WhatsApp. How it routes Each allowlisted sender (one phone number / one Telegram id) picks one machine to talk to. Only that machine processes the sender's normal traffic; other linked hosts stay silent for that sender. Two different controllers can independently bind to two different machines without affecting each other. A's only runs on . B's only runs on . Neither sees the other's output. Fleet commands ( , , ) are processed by every linked host so that bindings, status, and help stay reachable even before a sender has bound. Stable identity Each data directory has a file ( ) with a UUID. Display names in the roster come from , or the OS hostname when empty. When you , "alpha" matches against the (or the 8-character node id). Local state Each host keeps a private file at (schema v3): \u2014 short id, label, role, and last-seen timestamp for every host this machine has observed in chat traffic. \u2014 map of sender key ( or ) to the bound machine's short node id, when it was set, and whether it came from a chat command ( ), config defaults ( ), or automatic attached/sole-host bind ( ). This file is per host and never shared. Disagreements between linked hosts are resolved by the next footer-stamped message anyone sends to the chat. Attached mode (platform) auto-bind Platform routing ( or unprefixed \u2192 default device) picks one device WebSocket before cluster gating runs on that host. When is true (default): Default device \u2014 on attach, allowlisted senders from platform policy are pre-bound to the local node id (no for unprefixed commands). Any attached device \u2014 first inbound routed to that device auto-binds the sender to that host's node id (covers without manual setup). Bindings use the 8-char node id, not or platform , so renames do not break routing. remains for standalone multi-host WhatsApp (all linked hosts see the same DM). is platform device routing; is cluster host focus \u2014 use both when you run a fleet. Commands (WhatsApp / Telegram) may be shortened to or (the bare token matches; does not). Command Action ---------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------ Overview of the per-sender model Bind YOUR messages to that machine. Resolves either an 8-character node id or a . Other senders are not affected. Bind YOUR messages to the local machine (shorthand for ). Show YOUR current binding (machine, source: chat or config). Clear YOUR chat binding. The config default in (if any) takes over again. Every online host replies with its own one-paragraph status (this is the discovery moment \u2014 each host parses the others' footers and updates its peer list). Locally known roster \u2014 only one host responds (the bound one, or a deterministic fallback). Convergence When you send , every linked host runs locally. Only the resolved target host (the one labelled ) sends the confirmation reply; siblings stay silent. The reply carries the cluster footer with , and every sibling observes it via WhatsApp upserts and converges on the same binding for your sender key. Defaults via config You can pre-seed bindings so a controller never has to type on first contact: Keys are sender keys ( or ). Values are the target machine's or its 8-character node id. Chat-set bindings ( ) always win over confi`},{title:"Chat configuration (`/config`)",body:"Allowlisted chat users can change many settings without SSH. This is as sensitive as shell access: anyone who can DM as an allowed identity can modify config. Commands Command Action --------------------------- -------------------------------------------------------------- or Help (overview + hint) Snapshot of effective config; telegram bot token masked One whitelisted key Update one key; values may be quoted for paths with spaces List all keys allowed for After changes Save goes to immediately (same normalization as the rest of the app). For or , the gateway runs -style Telegram restart when is up; otherwise send after editing. Changing returns an explicit warning in the reply. Whitelist ( ) The canonical allowlist is in . In chat, prints the same comma-separated list your build supports. Groups include: gateway ( , \u2026); cluster; shell / sync / jobs; apps; files; recipes (including , ); Telegram ( ); service ( ); update checks ( , , , ); tunneling ( , , ); chat LLM fallback ( , , , , , , ). accepts a JSON object value (or / to wipe), e.g. . Not exposed via : , \u2014 use and in chat (or / on the host). Tunnel token is not in : run on the gateway host (or set ). Chat can turn tunneling on with after the token exists. Examples ---"},{title:"Related documentation",body:"Configuration guide \u2014 full file layout and fields Security model \u2014 trust boundaries and Message routing \u2014 slash-command ordering (high level)"}],keywords:["multi-host","cluster","and","chat","configuration","this","document","describes","the","chat-driven","for","viewing","or","editing","from","whatsapp","telegram","per-sender","bindings","/config","related","documentation"],relatedCommands:["/c help","/config help","/computers","/pcs","/config","/nfs","/node-id","/c use","/cluster-local","/sole-host bind","/c here","/cluster","/c using"]},{id:"docs-features-cowork",path:"docs/features/cowork.md",title:"Cowork \u2014 scheduled and on-demand shell tasks",summary:"Cowork runs saved shell commands on a timer while is active. It uses the same execution model as sync commands ( via your configured ), with and from . There is no AI layer.",sections:[{title:"Prerequisites",body:"A running gateway: (foreground or background). You must be allowlisted (same trust as ). With cluster enabled, only the host bound to your sender processes chat commands; tasks you create are stored on that machine\u2019s data directory."},{title:"Commands",body:"Use or . Send for the short form. Action Example -------------------------------- ---------------------------------------------------------------------------- Add task List Show Run now (on-demand or scheduled) Enable / disable / Remove (also , ) Check in (heartbeat only) Update \u2014 notify condition (see below) \u2014 also send the run log file with each notify \u2014 optional artifact paths (see below) Add syntax Scheduled / on-demand tasks (run a command): Heartbeat tasks (dead-man's-switch, no command): Name: letters, digits, , ; max 32 characters (stored lowercased). Schedule: (or ), , , , (weekday names or \u2013 , Sunday = ), or . For scheduled/on-demand tasks, the command must come after a literal separator. Heartbeat tasks have no command. Duration format (heartbeat): , , , . Minimum interval: 1m. Minimum grace: 30s. Grace defaults to 50% of interval if omitted. Schedules and time zones Fire times use the gateway host\u2019s local timezone (Node\u2019s on that machine). Output logs Each run writes one UTF-8 log file under the task\u2019s (default ). Filenames include a timestamp, task id, and whether the run was scheduled, catch-up, or on-demand. Catch-up Successful scheduled runs are recorded in (per task id and slot time). The scheduler uses that database as the source of truth for what is already done; may still contain a legacy field, which is seeded into SQLite on first open if the DB has no rows for that task. If several slots were missed (gateway down), the next tick runs the command once for the newest due slot and records coalesced rows for older missed slots so the backlog clears in a single execution instead of one run every 30 seconds per missed slot. A slot is only recorded after exit code 0, no timeout, and no terminating signal; failed runs do not advance the watermark (same retry behavior as before). Kinds stored for successful scheduled work: (within 2 minutes of the slot), (late), (satisfied without a separate run during backlog coalescing), and (imported from legacy on upgrade). On-demand queue appends a request to . The scheduler dequeues up to eight entries per tick, one read/write of the queue file per batch, so a crash mid-run does not drop the rest of the queue. Notifications After each run, a short summary can be sent (same routing as other replies: WhatsApp vs Telegram formatting). For scheduled and catch-up runs, the message is one line: The bracketed status appears only when the run did not finish cleanly (timeout, non-zero exit, or signal). For on-demand runs ( ), the message is two lines: Optional lines may follow when attachments fail or when too many artifact files match (see below). Mode Recipients ---------------- ----------------------------------------- (default) Task owner only All entries in (WhatsApp) All entries in Owner plus both allowlists No messages Trust: , , and can message every allowlisted identity. Anyone who can edit tasks (allowlisted senders) can point shell and notifications at powerful actions\u2014same overall model as remote shell. WhatsApp notification targets use normalized JIDs so delivery matches Baileys . Conditional notifications ( ) Control when a notification is sent with : Mode Behavior ------------------ -------------------------------------------------------------------------------------------------------------------------------------------------- (default) Notify after every run Notify only when the command exits with a non-zero code, times out, or is killed by a signal Notify only when the result flips (e.g. ok to fail, or fail to ok). The last-known state is tracked in SQLite so it survives gateway restarts. is useful for tasks that run frequently (e.g. hourly health checks) where you only want to know when something breaks or recovers, not on every successful run. Heartbeat (dead-man's-switch) A heartbeat task does not run a command. Instead, it expects periodic check-ins and alerts when they stop arriving. This creates a task that expects a check-in at least every 1 hour, with a 10-mi"},{title:"Data files",body:"Under (default unless overridden): \u2014 task definitions (atomic replace on save). \u2014 successful scheduled slot completions (watermark for catch-up); WAL mode. \u2014 queued on-demand runs. Directory mode is restrictive ( for where created by the app; task file )."},{title:"Limitations and caveats",body:"Gateway must be up at fire time for scheduled runs; catch-up handles downtime afterward. WhatsApp-only: the cowork scheduler starts before the first successful WhatsApp connection; very early completion notifications to WhatsApp may no-op until outbound is ready. Telegram outbound is typically ready earlier when the bot is enabled. Shared : if several gateways share the same data directory (unusual), each running process could execute the same schedules\u2014use one data dir per machine for normal setups. Cluster: tasks live on disk for the gateway that received commands; they do not sync across hosts."},{title:"See also",body:"Cowork implementation plan (design and maintainer notes) User guide \u2014 Cowork section Security model"}],keywords:["cowork","scheduled","and","on-demand","shell","tasks","runs","saved","commands","on","timer","while","is","active","it","uses","the","same","execution","model","as","sync","via","your","configured","with","from","there","no","ai","layer","prerequisites","data","files","limitations","caveats","see","also"],relatedCommands:["/cowork help","/cw help","/cowork","/cw","/cowork add","/data","/backup","/cowork list","/cowork show","/cowork run","/cowork enable","/cowork disable","/cowork remove"]},{id:"docs-features-device-update-delivery",path:"docs/features/device-update-delivery.md",title:"Update information on installed Omnish devices",summary:"This document is the reference plan for how operators and maintainers can reach already-installed Omnish gateways with version and notice information. It matches what the CLI and gateway implement today.",sections:[{title:"Reality check: there is no silent \u201Cpush\u201D to every machine",body:"Omnish is self-hosted: each install is a process on a user\u2019s machine, talking to WhatsApp/Telegram. There is no central fleet server and no always-on back channel from the project to those hosts unless the host initiates outbound traffic (or an allowlisted user sends a chat command). So 100% delivery in the literal sense (every device, every time, with no user action) is not possible: machines can be offline, firewalled, on air-gapped networks, or running an old binary forever. What is achievable is a reliable, predictable path that works whenever the network and registry are reachable\u2014same practical bar as other CLIs ( , , etc.)."},{title:"Options that were considered",body:"Approach Pros Cons ------------------------------------------------------ ------------------------------------------------------------------------------ ----------------------------------------------------------------------------------------------- npm registry Canonical published version; no custom infra; HTTPS; already used for installs Needs outbound HTTPS; scoped/unpublished forks differ GitHub Releases / API Rich metadata Rate limits; not identical to \u201Cwhat gets\u201D Static JSON on a URL you control ( ) Arbitrary maintainer text (security, migration) You must host and secure expectations (HTTPS only in omnish) In-chat broadcast from \u201Cthe project\u201D Uses existing DM surface There is no project-owned chat to all installs; only allowlisted users can command their host Auto-upgrade in place Hands-off for users High risk (native deps, , Baileys); out of scope for this design doc Chosen combination: npm registry for semver discovery + optional HTTPS JSON for human notices, exposed in the product as , (cached one-liner), , optional background checks when is true, and for the same keys."},{title:"Implemented behavior (source of truth)",body:"(allowlisted chat, gateway running): performs a live GET to (default package name ). If is set to an https URL, fetches JSON (link must also be ). : shows the last in-memory snapshot from the last live or scheduled check (no network). : gateway runs a timer (checks at most every 1 minute internally, but only performs a registry+info fetch when has elapsed, clamped 1h\u20137d). Results are logged with and stored for / . : CLI one-shot check (same fetches as ). : completion text may append \u201CUpdates (last check): \u2026\u201D if a snapshot exists. Configuration keys (also in and ): (boolean, default false) \u2014 privacy-first default. (default 86400000) \u2014 clamped between 1 hour and 7 days. (default ) \u2014 for forks or scoped packages. (default empty) \u2014 optional maintainer notice JSON over HTTPS."},{title:"Maintainer playbook",body:"Publish a new version to npm as today ( bump, publish). Devices that run or have background checks enabled will see the new latest when the registry updates. Optional notice (security advisory, breaking change): host a static JSON file at an HTTPS URL you control; set in docs or tell users to set it via . Do not rely on chat alone to \u201Creach\u201D every install; treat registry + optional URL as the scalable channel."},{title:"Code map",body:"Piece Role ------------------------------------------------------ -------------------------------------------------------------------------------- npm + optional info URL fetch, snapshot, scheduler Numeric compare for \u201Cnewer on npm\u201D Reads running from package root (dev + bundled ) , Schedules checks; ; reload footer status lines, formatted reply Schema defaults and merge"},{title:"Future extensions (not implemented)",body:"Signed notices (e.g. minisign) over . Deprecation warnings via (visible on , not parsed here)."}],keywords:["update","information","on","installed","omnish","devices","this","document","is","the","reference","plan","for","how","operators","and","maintainers","can","reach","already-installed","gateways","with","version","notice","it","matches","what","cli","gateway","implement","today","reality","check","there","no","silent","push","to","every","machine","options","that","were","considered","implemented","behavior","source","of","truth","maintainer","playbook","code","map","future","extensions","not"],relatedCommands:["/updates","/updates cached","/telegram","/latest","/unpublished forks","/gateway","/config set","/registry","/reload","/config keys","/omnish-notice","/check"]},{id:"docs-features-docs-search-from-chat",path:"docs/features/docs-search-from-chat.md",title:"Documentation search from chat",summary:"Find omnish guides by topic from WhatsApp, Telegram, or \u2014without hunting the repo or omnish.dev first. Search is offline (bundled index at build time; no AI layer).",sections:[{title:"Commands",body:"Command Purpose --------------------------- --------------------------------------------------------- or Subcommand list Ranked results (keywords + headings) Repeat the last search list in this chat or Excerpt, GitHub link, and Try: related slash commands Run the primary related help (e.g. ) Legacy aliases: (same as ), \u2192 , . Host terminal (same index): ( still works.) ---"},{title:"Example flow",body:"You are not sure which command exposes HTTP: Pick a result: You get a short excerpt, the doc path, a GitHub link, and lines like . Jump to live help: Same as sending in that chat. ---"},{title:"When nothing matches a slash command",body:"Plain text that looks like a question (contains a space or ) gets a hint on No command matched: Use with your own keywords if the auto-filled phrase is too long. ---"},{title:"Index scope",body:"The build includes guides, features, architecture, and advanced troubleshooting under (not , , or maintainer runbooks). Rebuild the index with: ( and run this automatically.) Overrides for related commands: . ---"},{title:"Related",body:"User guide Message routing Online catalog \u2014 community recipes/apps (separate from docs search)"}],keywords:["documentation","search","from","chat","find","omnish","guides","by","topic","whatsapp","telegram","or","without","hunting","the","repo","dev","first","is","offline","bundled","index","at","build","time","no","ai","layer","commands","example","flow","when","nothing","matches","slash","command","scope","related"],relatedCommands:["/s help","/search help","/s q","/s list","/s show","/s follow","/tunnel help","/search","/docs search","/help search","/features","/tunneling"]},{id:"docs-features-implementation-101",path:"docs/features/implementation-101.md",title:"Tunneling implementation 101",summary:"This document explains how omnish tunneling is built: relay edge, client, CLI, chat integration, configuration, security, and how to exercise it locally.",sections:[{title:"Big picture",body:"Tunneling is a relay + client design. The relay is the public edge; the omnish client on the user machine keeps an outbound WebSocket and forwards traffic to a local port. HTTP uses JSON control messages on the WebSocket. TCP uses JSON for stream open/close and length-prefixed binary frames for payload. Default production relay: . The relay service itself lives under and is deployed separately from the npm CLI package."},{title:"Repository map",body:"Path Role ------------------------------------------------------------------------------ ------------------------------------------------------- Deployable relay: HTTP edge, WSS control, TCP listeners Relay package ( dependency) Shared control message types and binary frame codec Tunnel kinds, records, default relay URL One tunnel: WSS session, local forwarding Active tunnels, limits, stop/stopAll Token and relay URL resolution CLI and chat argument parsing subcommands handlers for the gateway CLI dispatch; gateway shutdown stops tunnels Chat routing for and , , under the data dir Posture findings for tunneling lines when tunneling is enabled Tests: , , , ."},{title:"Relay (`contrib/tunnel-relay/`)",body:"is the tunnel process. Production ships it in one Docker image with Caddy (TLS, wildcard DNS-01) via and . proxies public / to and . Listeners Listener Default Role ----------- --------------- -------------------------------- HTTP edge Public HTTP for tunneled apps Control WSS Authenticated client connections Environment variables: \u2014 base URL shown to users (default ) \u2014 HTTP edge bind port \u2014 control WebSocket bind port / \u2014 TCP tunnel port range \u2014 comma-separated bearer tokens allowed to connect \u2014 per-token tunnel quota Authentication Clients connect with on the WebSocket upgrade. Invalid or missing tokens close the socket. Registration After , the client sends with ( ), , , and optional . The relay assigns a slug (from or random) and replies with including . HTTP routing Local dev: Production-style: when matches that host pattern Fallback: Each slug is bound to the WebSocket session that registered it (not \u201Cany session with the same token\u201D), so multiple tunnels sharing one token each get correct routing. Incoming requests are turned into control messages to the owning client; the client returns . TCP For , the relay binds a port in the configured range and returns . New public TCP connections emit on the control socket; bytes flow over binary frames. State In-memory maps ( , , ). Tunnels disappear when the client disconnects. No cross-replica sticky routing in v1."},{title:"Shared protocol (`src/tunnel/protocol.ts`)",body:"Control messages (JSON on the WebSocket): Session: , , Lifecycle: , , , HTTP: , (optional ) TCP: , Keepalive: , Binary frames (TCP payload): 1 byte frame type ( , , , ) 4 byte stream id (big-endian) 4 byte payload length payload bytes and implement the codec on client and relay."},{title:"Client (`src/tunnel/client.ts`)",body:"represents one active tunnel. Derives from the relay URL ( \u2192 , default path ). Connects with the bearer token. Sends with a random 8-hex . Waits for and stores and . HTTP path: On , issues to , then sends with status, headers, and optional base64 body. TCP path: On , connects locally and pumps bytes via binary frames; or relay close tears down the stream. Lifecycle: Periodic ; on stop, sends and closes the socket. Default target host is unless overrides it."},{title:"Manager (`src/tunnel/manager.ts`)",body:"holds live instances keyed by tunnel id and slug. Resolves relay URL via and token via Enforces from config / for CLI, chat, and gateway shutdown The gateway and CLI share one manager instance from ( )."},{title:"Configuration and secrets",body:"In (non-secret): \u2014 gate chat commands (default ) \u2014 default relay origin (default ) \u2014 max concurrent tunnels on this host (default ) Secrets (not in ): or ( ) via Optional relay override: or in the auth file See and ."},{title:"CLI (`omnish tunnel`)",body:"Implemented in ; wired from . Subcommand Behavior ----------------- ------------------------------------------------------ Save token (and optional relay) to Remove saved token Register HTTP tunnel; foreground unless Register TCP tunnel List active tunnels on this machine Stop one tunnel Relay reachability and auth presence Flags: , , , (see )."},{title:"Chat integration",body:"When is true, handles: / (optional , ) delegates to the shared . Tunnels run inside ; standalone still works without the gateway. Trust model matches : allowlisted chat users can open tunnels as the gateway OS user. Public URL possession is the visitor credential."},{title:"Security",body:"findings: \u2014 chat tunneling on \u2014 chat tunneling on without a token \u2014 non-default documents tunnel URLs as capabilities and the gateway shutdown path that stops active tunnels."},{title:"Local exercise",body:"Install relay deps: Start relay, for example: - - Run a local HTTP server on a port (for example ) Open the printed (path-based on loopback: ) Automated coverage: spins the relay and asserts HTTP end-to-end."},{title:"Out of scope (v1)",body:"Mandatory omnish.dev account or billing Tunnel persistence across client disconnect or HA relay fleet UDP, mesh VPN, or bundled ngrok/cloudflared as the primary path Setup UI for tunnel login (CLI is the v1 configuration surface)"},{title:"Success criteria (from product plan)",body:"+ yields a public URL that serves the local app (with a running relay) exposes a public TCP endpoint to the local port With and the gateway running, allowlisted users get the same URLs from chat and docs describe capability risk clearly"}],keywords:["tunneling","implementation","101","this","document","explains","how","omnish","is","built","relay","edge","client","cli","chat","integration","configuration","security","and","to","exercise","it","locally","big","picture","repository","map","contrib/tunnel-relay/","shared","protocol","src/tunnel/protocol","ts","src/tunnel/client","manager","src/tunnel/manager","secrets","tunnel","local","out","of","scope","v1","success","criteria","from","product","plan"],relatedCommands:["/tunneling","/architecture","/security","/close and","/tunnel","/tunnel-relay","/server","/contrib","/package","/protocol","/src","/types"]},{id:"docs-features-job-board",path:"docs/features/job-board.md",title:"Job board \u2014 digital employee coordination",summary:"The job board is omnish\u2019s work queue for autonomous digital employees: dependencies, coordinator dispatch, supervisor accountability, and host-wide resource limits.",sections:[{title:"Prerequisites",body:"(gateway active). Allowlisted sender for commands. with specialist commands (see example pack)."},{title:"Coordinator (on by default)",body:"While the gateway runs, the coordinator ticks automatically ( defaults to ). Set it to for manual only. Configure specialists: (or edit )."},{title:"Commands",body:"Command Action -------------------------------------------- ------------------------------------------------------------------------------ Human intake; optional , , Status counts plus per-job lines for review/pending/rework Job detail One coordinator + supervisor cycle (also sends status DMs when hooked to chat) Primary agent CLI for registry roles Resource governor snapshot Cancel job Registry names Coordinator onboarding and profiles Human satisfaction after Role-split suggestions Mark work-plan step done (for agent CLIs) is an alias for . Notification config Key Default Meaning ---------------------- ------------ ---------------------------------------- Phase completion messages Start / review / done / failed / blocked"},{title:"Data paths",body:"Path Purpose ----------------------------------- ---------------------------------------- Jobs, audit, slots Specialist commands Profile, skills, flow overrides Concurrency and CPU/RAM/GPU limits , , artifacts"},{title:"Coordinator (each tick)",body:"Unblock jobs whose dependencies are . Run supervisor on jobs in . If capacity allows, dispatch highest-priority runnable job to its specialist command."},{title:"Core competencies (non-optional)",body:"All employees run through the harness ( ). Registry entries use only (legacy alias). omnish seeds , enforces five phases, and notifies on progress. See Digital employees \u2014 core competencies."},{title:"Work plan (specialist discipline)",body:"The harness seeds before the inner agent runs. Specialists mark todos via scripts or . Handoff requires full completion, self-reviews on major steps, and in ."},{title:"Supervisor",body:"After a specialist finishes, the job moves to . The supervisor checks , , and job-plan lines. Outcomes: pass \u2192 ; dependents may unblock. rework \u2192 then reassigned (until ). fail \u2192 ; owner notified."},{title:"Separation of duties",body:"The coordinator does not approve quality; the supervisor does not choose the next assignee or create prerequisite jobs. That split keeps accountability auditable (see in SQLite)."}],keywords:["job","board","digital","employee","coordination","the","is","omnish","work","queue","for","autonomous","employees","dependencies","coordinator","dispatch","supervisor","accountability","and","host-wide","resource","limits","prerequisites","on","by","default","commands","data","paths","each","tick","core","competencies","non-optional","plan","specialist","discipline","separation","of","duties"],relatedCommands:["/guides","/employee-task-flow","/digital-employees","/coordinator-workforce","/board","/employees","/board tick","/board agent","/board add","/board list","/pending","/rework"]},{id:"docs-features-media-commands",path:"docs/features/media-commands.md",title:"Media commands (`/dl`, `/dlf`, `/dlv`, `/tr`, `/edit`)",summary:"Download, transcribe, and edit media from chat \u2014 using yt-dlp, ffmpeg, and optionally openai-whisper on the gateway host.",sections:[{title:"Defaults",body:"Files are sent to chat by default ( ). Background jobs deliver results over the gateway control channel when is active. To only list paths, set:"},{title:"Install tools (host)",body:"Binaries are stored under . Whisper uses . From chat (same trust as shell \u2014 off by default):"},{title:"Chat commands",body:"Command Action --------------------------------------------- ------------------------------------------------------------ Auto: file (HTTP), video (yt-dlp), or HTML\u2192markdown Force HTTP file download (never yt-dlp) Force yt-dlp video download Tool status OS-specific manual install steps Install tools into Whisper transcript + (+ video if URL); background job Trim or convert with ffmpeg; background job flags: / , / , / , / , . How classifies URLs File signals (always HTTP, never yt-dlp): pathname extension ( , , \u2026), path heuristics ( for arxiv, etc.), or from HEAD. Video: yt-dlp probe ( ) \u2014 if yt-dlp has a named site extractor (1000+ sites), download with yt-dlp. HTML page: fetch page and send markdown in chat (large pages may also save a file). Use when auto-detect might guess wrong (e.g. force HTTP on a video URL). Use to force yt-dlp. Save location Config / chat Where files go ----------------------------- --------------------------- set That directory (flat) for this chat Session cwd ( ) Default (flat) No dated subfolders. Share-friendly filenames All download paths normalize saved basenames for easy sharing in chat: ASCII-safe names (unicode stripped or transliterated, emoji removed) Spaces and unsafe characters become Collisions get numeric suffixes ( , ) yt-dlp uses plus a post-download rename pass Whisper sidecars ( , , etc.) inherit the normalized video basename. Background jobs , , , , and run as background jobs ( ). You get a job id immediately; finished files and markdown are sent to chat when the job completes (if is true). Optional flags (stripped before the payload): / \u2014 extra ping when the shell job finishes (exit status) / \u2014 no-op (already background) Step progress When is true (default), multi-step work sends chat messages such as while it runs. Auto-detect URLs When is true (default), a message that is only an or URL runs in the background \u2014 even when free shell ( ) is on. Focused PTY still wins over auto-dl. When chat LLM fallback is off and no LLM shell command is configured, lone URLs also auto-download even if was set to false (paste-a-link intent). Trailing punctuation from chat clients (e.g. ) is stripped before detection. Auto-dl replies with a short \u201CDownloading link\u2026\u201D message instead of a verbose job block. Legacy still works as aliases: / \u2192 , / \u2192 . Other modes show a deprecation hint."},{title:"Config keys",body:"Key Default Purpose -------------------------- ----------- ----------------------------------------------------------------------------------------- Send files to chat after download Allow from chat Lone URL \u2192 auto Output root (empty \u2192 Downloads/Omnish or cwd) yt-dlp cap (0 = none) Max size when sending downloads to chat (0 = no omnish cap; WA/TG limits still apply) Whisper / Transformers model size ( , , , \u2026) (default device, then CPU), , or (whisper CLI only) Per-attempt timeout for whisper/ffmpeg (10 min; max 15 min) , , or (whisper then Transformers.js fallback) When engine fails, retry with Transformers.js if installed On hosts with a busy or small GPU, set to or use a smaller ( , ). For long files that hit timeouts, raise . Transformers.js (no Python): installs under . Set to to use it exclusively, or keep / with (default) to fall back when openai-whisper fails. Step-by-step chat messages during multi-step work / / Binary overrides Older keys in are migrated on load (e.g. \u2192 ). Size limits ( , , ) Two different caps: Key When it applies ------------------ ---------------------------------------------------------------------------------------------------------- Download only (yt-dlp ). = no cap. Sending the saved file to chat after the job finishes. = no omnish cap on standalone . If the file downloads but chat says \u201CN file(s) not sent\u201D with a size, the job succeeded on disk; outbound delivery hit a cap \u2014 not . Standalone (local WhatsApp/Telegram on the host): only (and messenger limits). Attached ( with platform credentials, messengers on the relay): no default platform hop cap. Optional env (bytes; = unlimited) on both the relay and the device gateway caps outbound files over the WebSocket. WhatsApp/Telegram limits still apply upstream. Check values: (or ) for and ."},{title:"Legal and safety",body:"Same allowlist trust as shell commands. Private/local URLs are blocked ( , RFC1918, etc.) when the job runs. Respect copyright and platform terms; omnish only runs tools you install locally."},{title:"See also",body:"Files send/receive \u2014 for manual delivery Background jobs \u2014 , ,"}],keywords:["media","commands","/dl","/dlf","/dlv","/tr","/edit","download","transcribe","and","edit","from","chat","using","yt-dlp","ffmpeg","optionally","openai-whisper","on","the","gateway","host","defaults","install","tools","config","keys","legal","safety","see","also"],relatedCommands:["/dl","/dlf","/dlv","/tr","/edit","/bin","/venvs","/whisper","/config set","/dl install","/dl doctor","/dl setup"]},{id:"docs-features-media-pull",path:"docs/features/media-pull.md",title:"Media pull (`/pull`) \u2014 deprecated",summary:"> Superseded by media commands ( , , ). This page is kept for older releases.",sections:[{title:"Enable",body:"In (or from an allowlisted chat):"},{title:"Install tools (host)",body:"Binaries are stored under . Whisper uses . From chat (same trust as shell \u2014 off by default):"},{title:"Chat commands",body:"Command Action ------------------------ -------------------------------------------------- Usage Tool status OS-specific manual install steps Best video (needs ffmpeg) Audio extract (m4a) Subtitles (en + auto) Whisper speech-to-text video + audio + subs + transcript (background job) Flags: / \u2014 run as job / \u2014 notify in chat when the background job finishes and always run in the background. / / run synchronously unless is set. Auto-detect URLs When is true, a message that is only an or URL runs with (default ). Send results back to chat When is true, files under (and any cap in attached mode) are sent with automatically. Otherwise the reply lists paths \u2014 use manually."},{title:"Config keys",body:"Key Default Purpose ------------------------------------------------------ --------- ------------------------------------------------------ Master switch Allow Lone URL \u2192 pull Default mode Output root (empty \u2192 ) yt-dlp cap (0 = none) Push files to chat Whisper model / / Binary overrides"},{title:"Legal and safety",body:"Same allowlist trust as shell commands. Private/local URLs are blocked ( , RFC1918, etc.). Respect copyright and platform terms; omnish only runs tools you install locally."},{title:"See also",body:"Files send/receive \u2014 after a pull Background jobs \u2014 , ,"}],keywords:["media","pull","/pull","deprecated","superseded","by","commands","this","page","is","kept","for","older","releases","enable","install","tools","host","chat","config","keys","legal","and","safety","see","also"],relatedCommands:["/pull","/dl","/tr","/edit","/config","/config set","/bin","/venvs","/whisper","/pull install","/pull help","/pull doctor"]},{id:"docs-features-monetization",path:"docs/features/monetization.md",title:"Monetization \u2014 device-first omnish",summary:"omnish keeps WhatsApp and Telegram as the free control plane: allowlisted messages run on the user\u2019s machine. Paid tiers charge for reachability, identity, team boundaries, and safety nets\u2014not for sending a command.",sections:[{title:"Who pays at ~$10/month",body:"Someone who already runs on a home server, laptop, or Mac mini and wants the inbox to stay useful when they are away from the desk: show a dev server, start a long job, drive a TUI agent, tail logs, or hand a link to someone else. Free omnish sells my phone is a remote control for my computer. Paid omnish sells that control still works when I need a stable public URL, more than one tunnel, or light team structure\u2014without being my own ngrok admin."},{title:"Strongest first paid wedge",body:"Hosted tunneling on , integrated with the same chat and CLI, is the most natural first paid feature. Execution stays on their box; omnish runs the edge they would otherwise assemble (TLS, wildcard subdomains, relay uptime, abuse limits). For agent builds on the user\u2019s machine, steered from chat: the agent runs locally; Plus provides a shareable HTTPS preview via or , while iteration stays in WhatsApp or Telegram. That is not a cloud IDE\u2014it is a link that works while code and processes stay on their hardware. Plus ($10) \u2014 draft shape Several concurrent HTTP tunnels (free: self-hosted relay only, or one hosted tunnel with ephemeral names). Reserved slugs so does not change every session. Caps on bandwidth, tunnel lifetime, and concurrent tunnels so hosted relay cost stays bounded. TCP tunnels optional on Plus or stricter on free (TCP is riskier to operate). Comparable spend to ngrok-class tools; the hook is already inside omnish, not a separate dashboard."},{title:"Second layer: team and accountability",body:"Solo users may stay on free plus self-hosted relay. Small teams pay when my phone controls our box needs structure: More than one allowlisted identity with clearer roles (run vs read-only vs tunnel-only). Audit trail: who ran what, which tunnels opened, when\u2014exportable for a client or cofounder. Named machines in cluster mode without ambiguous shared binding. Still device-first: gateway on their hardware; paid layer is policy and visibility."},{title:"Third layer: reliability",body:"Weaker as the only $10 hook unless they depend on the gateway daily; pairs well with hosted tunnels: Offline alerts when stops heartbeating. Guided boot / service setup with a simple health view (gateway up, last command, disk, tunnel count). Optional backup / restore of omnish data (shortcuts, cowork defs, session cwd maps)\u2014not the whole disk."},{title:"What not to lead with at $10",body:"Hosted agent sandbox \u2014 fights device-first positioning; competes with Cursor, Replit, etc. Generic AI \u2014 conflicts with no-AI, your-shell positioning. Per-message chat fees \u2014 trains workarounds. Security theater \u2014 erodes trust on remote shell access."},{title:"Packaging sketch",body:"Tier Free Plus $10 Pro (later) --------------------------- ----------------- ----------------- ------------------- Chat \u2192 your shell Yes Yes Yes Self-hosted relay Yes Yes Yes Omnish-hosted HTTPS tunnels No / very limited Yes, with caps Higher caps Stable / custom names No Reserved slug Custom domain Team / audit Basic allowlist Small team + logs More seats, exports Gateway monitoring DIY Optional alerts SLA-style support One-liner: Keep controlling your machine from WhatsApp and Telegram for free; pay for public preview links, stable names, and team guardrails when that is how you work."},{title:"When someone will pay",body:"They pay when hosted tunnels remove a recurring pain: client demos, \u201Copen this while I change it from chat,\u201D or not maintaining Caddy, Cloudflare, and a relay on a VPS. Remote shell alone may stay free forever\u2014that supports adoption, not revenue."},{title:"Sequencing",body:"Plus = hosted tunnel + limits + account/token; chat stays unlimited. Team audit when paying users need shared access. Custom domains when reserved slugs feel tight. The preview-from-chat loop on their machine is credible paid value only if the URL is stable, HTTPS, and boring\u2014that is worth money while the shell stays free and local."}],keywords:["monetization","device-first","omnish","keeps","whatsapp","and","telegram","as","the","free","control","plane","allowlisted","messages","run","on","user","machine","paid","tiers","charge","for","reachability","identity","team","boundaries","safety","nets","not","sending","command","who","pays","at","10/month","strongest","first","wedge","second","layer","accountability","third","reliability","what","to","lead","with","10","packaging","sketch","when","someone","will","pay","sequencing"],relatedCommands:["/month someone","/tunnel","/theirname","/token"]},{id:"docs-features-omnish-chat",path:"docs/features/omnish-chat.md",title:"Omnish Chat",summary:"First-party, installable command inbox for omnish \u2014 platform account, no WhatsApp/Telegram required.",sections:[{title:"What it is",body:"PWA in \u2014 Vite + React, omnish-web tokens Surface with peer prefix \u2014 same routing as WA/TG Auth \u2014 existing platform signup/login; one token for dashboard, devices, and chat"},{title:"What it is not",body:"A full messenger (no groups, media, or social features in v0.1) A cloud shell \u2014 execution stays on your device"},{title:"Docs",body:"Resource Link ----------------- ------------------------------------------------------------------------------ Sidecar README contrib/chat/README.md Architecture contrib/chat/ARCHITECTURE.md Protocol contrib/chat/docs/protocol.md UX principles contrib/chat/docs/ux-principles.md Research & phases omnish-chat-v0.1.md ADR ADR-0018"},{title:"Quick start (developers)",body:""},{title:"User path (target v0.1)",body:"Create account at your platform URL (or in the PWA signup screen). On a machine: then . Allow your chat install: on the device or add the install id under Security \u2192 Allowlists \u2192 Omnish Chat on the platform dashboard. Install PWA, sign in, send or ."},{title:"Troubleshooting",body:"Symptom Cause Fix ------- ----- --- works, other commands return Not allowlisted on this omnish device is answered on the platform relay; shell commands are checked on the device allowlist Add your PWA install id ( shown after connect) via or dashboard Omnish Chat allowlist Logged into Omnish Chat but still denied Platform account login \u2260 device authorization (by design) Same as above \u2014 explicit pairing per chat install Added allowlist on dashboard, still denied for 1 min Attached syncs platform allowlists on a short interval Wait up to a minute and retry, or run locally for immediate standalone effect Device identity (not MAC / IP) Chat install \u2014 stable UUID in browser ( ); peer key . CLI device \u2014 stable UUID in when registers with the platform. Platform device record \u2014 MongoDB id + human label; not tied to IP (IPs change) or MAC (unavailable in browsers, randomized on many networks). MAC-based allowlisting is intentionally not used: it is not available to the PWA, changes with network interfaces, and is weaker than explicit install pairing for a shell gateway."},{title:"Implementation status",body:"Piece Status ------------------------------- ------- PWA scaffold + auth UI Done WS client + protocol types Done Relay Done CLI transport Done Web Push on reply Planned"}],keywords:["omnish","chat","first-party","installable","command","inbox","for","platform","account","no","whatsapp/telegram","required","what","it","is","not","docs","quick","start","developers","user","path","target","v0","troubleshooting","implementation","status"],relatedCommands:["/telegram required","/chat","/tg","/login","/readme","/contrib","/architecture","/docs","/protocol","/ux-principles","/research","/omnish-chat-v0"]},{id:"docs-features-online-catalog",path:"docs/features/online-catalog.md",title:"Online catalog",summary:"Share and install recipes, app templates, cowork tasks, and shortcuts across the omnish community. Browse and download from chat; publish when logged into the platform.",sections:[{title:"Discover and install (chat)",body:"Browse from the command family you care about \u2014 each prefix filters the catalog to the matching kind in MongoDB: Prefix Kind filter Example -------------------- ------------------------------------------------ -------------------------------------- all kinds (optional on trending/search) only only only Shared subcommands (replace with , , , or ): Command Purpose --------------------------------------- ---------------------------------------------------- Catalog subcommands for that family Most downloaded (kind-scoped when not ) Text search Full payload (review before install) Install to gateway-shared storage Install item #n from the last list in that family Repeat the last trending/search list for that family Examples: Use or on download to install for this chat only instead of gateway-shared: Numbered lists are per family \u2014 uses the last list, not a prior list. Platform URL: set in config (default hosted relay). Browse does not require a token; the CLI uses your configured relay origin. ---"},{title:"Publish (requires platform account)",body:"Sign up on the relay dashboard or run , then publish from chat: Command What is published Catalog -------------------------------------------------------------- --------------------------------------------------- -------------- User or gateway-shared recipe (not built-ins) Running PTY session command (session must be alive) Cowork task for this chat Shortcut (chat or shared) On success you get a (e.g. ). Others install with the matching prefix, e.g. or . Flags: , , , ---"},{title:"What gets installed",body:"Kind Local storage ---------- ---------------------------------------------------------------------- gateway-shared bucket Same as recipe (category ; use with stored command) gateway-shared bucket (owned by the importing chat) ---"},{title:"Security",body:"Recipes and app commands can run shell on your machine. Always before download. The platform rejects dangerous recipe flags and invalid command shapes at publish time. Download does not auto-run \u2014 it only adds the template locally. ---"},{title:"API",body:"Hosted relay routes are documented in Platform reference \u2014 Catalog. Related: Recipes / \xB7 Cowork \xB7 Shortcuts"}],keywords:["online","catalog","share","and","install","recipes","app","templates","cowork","tasks","shortcuts","across","the","omnish","community","browse","download","from","chat","publish","when","logged","into","platform","discover","requires","account","what","gets","installed","security","api"],relatedCommands:["/run online help","/apps online help","/guides","/platform-reference","/run online","/search","/apps online","/cowork online","/shortcut online","/run","/apps","/cowork","/shortcut","/search list"]},{id:"docs-features-run-queue",path:"docs/features/run-queue.md",title:"`/run` queue (`-q` / `--queue`)",summary:"The run queue runs recipe launches one at a time per chat, in FIFO order. It is meant for back-to-back agent jobs (e.g. several tasks) without starting multiple PTYs at once.",sections:[{title:"Syntax",body:"Recipes can also store a queue default at creation time: Then enqueues FIFO automatically (same as passing on every launch). Explicit / on launch still works and is equivalent. and are equivalent (case-insensitive). Short form works the same way. Combine with attach flags after the recipe name (same as non-queued ): attaches the queue head on start; without / , the head starts detached (default; see in Configuration). Not the same as the slash subcommand (status) or ."},{title:"Loading many jobs from JSON (`/run queue load`)",body:"You can enqueue multiple recipe tasks in one step from a JSON payload. Each row is validated like a separate (same recipe resolution, task length limits, and behavior). The queue still runs them one at a time in order. Ways to provide the JSON Method What to send -------------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ Host file path \u2014 path is resolved from this chat\u2019s session cwd (same idea as ): relative segments, globs, and quoted paths work like file selection elsewhere. Exactly one file must match. Inline JSON \u2014 everything after the word (with separating whitespace) is parsed as a single JSON value. Keep the payload on one message line if your client splits on newlines. Inbound file + caption Attach a document (e.g. ) and set the message text to (no path). Omnish uses the saved upload path from that same inbound turn (see Files \u2014 send & receive). You still get the usual \u201CSaved: \u2026\u201D line before the queue result. JSON format The file or inline text must be valid JSON in one of these shapes: Array of jobs (most common): Object with a single property (optional wrapper): Rules: Each job object must contain only two keys: and , both strings. Extra keys are rejected (catches typos like vs ). Top-level object form must be exactly \u2014 no other top-level keys. There must be at least one job. Empty arrays are rejected. Maximum 64 jobs per load. Not supported: raw , , or arbitrary shell in JSON. That would bypass recipe validation; only + are accepted, and omnish builds each run the same way as . File size limit for When reading a JSON file from disk (path argument or saved attachment path), the read is capped: If in is greater than zero, that value is used. If it is zero (no inbound cap), queue-load still uses a 1 MiB ceiling so a huge file cannot be pulled into memory unintentionally. Batch behavior and replies Jobs are enqueued in array order (first element becomes the next head when the queue is idle, or waits behind the current head). Omnish returns one reply that concatenates the status line from each enqueue step (started head, \u201Cwait slot \u2026\u201D, paused, etc.), same semantics as sending several messages in sequence. , , pauses, and clean-exit rules are unchanged \u2014 see Status and control and Pauses and failures. See also (files) Files \u2014 send & receive \u2014 where uploads land, , per-chat ."},{title:"What actually happens",body:"In memory only \u2014 Each waiting item is a small record (command + env + recipe label). Nothing runs until it becomes the head of the queue. Head starts immediately \u2014 When you enqueue and nothing else is running as the queue head, omnish shifts the first item off the FIFO and starts one app session (PTY), same as a normal . Others wait \u2014 Additional / calls append to the waiting list. No extra processes are spawned for those rows; the next job starts only when the previous head session exits with exit code 0 and signal 0 (clean exit). Per chat \u2014 Queue state is keyed by the chat ( ), not global across all users."},{title:"Why `/run queue` can show `Pending: 0` right after you queued something",body:"counts only jobs that have not started yet. The first item you add with / is removed from the pending list as soon as it starts; it then appears under Active with the session name and recipe label. Example: you send three queued runs in a row while the gateway is up: After message 1: Active = new session, Pending = 0 (nothing left waiting). After message 2: Active still the first session, Pending = 1 (second job waiting). After message 3: Pending = 2 (second and third waiting). So after a single enqueue is normal \u2014 it means the one job is already running, not that the queue \u201Clost\u201D your task. If you used before omnish accepted , the old parser treated as part of the task text and ran non-queued instead; the queue stayed empty. Use or after the recipe name (current omnish supports both)."},{title:"Status and control",body:"\u2014 Shows Active (session + recipe), Pending (with a short numbered list of waiting recipe labels), Paused, and a reminder that the next item auto-starts only after a clean exit. \u2014 Clears the paused flag and tries to start the next waiting item (e.g. after you fixed the host or session limits). If a head session is still running, resume tells you to wait until it finishes cleanly. \u2014 Enqueue many jobs from JSON (file path, inline , or attachment + caption); see Loading many jobs from JSON."},{title:"Pauses and failures",body:"Non-clean exit (non-zero exit code, or non-zero signal, e.g. SIGKILL) on the queue head \u2192 the queue pauses; waiting items stay in the FIFO until you (or fix limits and resume). Failed spawn (e.g. per-chat app limit reached) when trying to start the head \u2192 queue pauses and the item is put back; fix the error, then ."},{title:"Resource model",body:"Waiting rows: RAM only (no polling timers for the queue itself). Running head: one PTY + child process, same cost as a normal . Not persisted \u2014 If the gateway process ( ) restarts, the in-memory queue is cleared. Long-term scheduling belongs in Cowork or your own job runner."},{title:"See also",body:"System agents and User guide \u2014 shortcuts vs Interactive sessions ( )"}],keywords:["/run","queue","-q","--queue","the","run","runs","recipe","launches","one","at","time","per","chat","in","fifo","order","it","is","meant","for","back-to-back","agent","jobs","several","tasks","without","starting","multiple","ptys","once","syntax","loading","many","from","json","load","what","actually","happens","why","can","show","pending","right","after","you","queued","something","status","and","control","pauses","failures","resource","model","see","also"],relatedCommands:["/run help","/run queue","/run","/run remosh","/run add","/run trace","/guides","/configuration","/to","/file","/send","/files-send-receive","/receive here"]},{id:"docs-features-service-from-chat",path:"docs/features/service-from-chat.md",title:"Service commands from chat (`/service`)",summary:"After WhatsApp or Telegram is connected and your identity is allowlisted, you can manage background gateway setup from the same DM thread \u2014 without SSH.",sections:[{title:"Commands",body:"Command Purpose ----------------------- -------------------------------------------------------------------------------------------------------------------------- Overview OS, data directory, , resolved Node + entry script paths Copy-paste steps for this host (paths filled by the running gateway) Last n lines of the default gateway log (default 80, max 120) Writes a user-level unit (Linux systemd or macOS LaunchAgent). Requires . Removes that unit (same gate). Windows: instructions only."},{title:"Trust model",body:", , and are safe to use like any other slash command (same allowlist as ). and modify login/boot integration files under your home directory. That is equivalent to shell access: anyone who can DM as an allowed identity can trigger them once is true in . Default: is . Enable only when you trust every entry on / as much as SSH. When enabled, reports a warning for ."},{title:"Relation to `CHANGE_ME` templates",body:"The contrib plist/service/XML files use placeholders for manual edits. bypasses that by injecting live paths from the running process. writes generated units with those paths automatically."},{title:"See also",body:"Background gateway and start on boot Cluster and chat configuration ( )"}],keywords:["service","commands","from","chat","/service","after","whatsapp","or","telegram","is","connected","and","your","identity","allowlisted","you","can","manage","background","gateway","setup","the","same","dm","thread","without","ssh","trust","model","relation","to","change","me","templates","see","also"],relatedCommands:["/service help","/service","/service status","/service instructions","/service logs","/service install","/config set","/service uninstall","/help","/boot integration","/contrib","/xml files"]},{id:"docs-features-sessions",path:"docs/features/sessions.md",title:"Interactive Sessions - omnish",summary:"Interactive sessions provide full terminal access within your messaging chats, enabling you to run TUI applications, REPLs, and interactive tools directly from WhatsApp or Telegram.",sections:[{title:"Overview",body:"Interactive sessions use to create pseudo-terminal sessions that mimic real terminal behavior. Each chat can maintain multiple named sessions with independent state. Key Features PTY-based: Full terminal emulation Named sessions: Multiple sessions per chat Focus management: One session attached at a time Output streaming: Real-time output with debouncing ANSI support: Color output and formatting Session persistence: State maintained across chats"},{title:"Session Management",body:"Starting Sessions Session Limits Per chat: Default 5 sessions (configurable) Global: Default 20 sessions (configurable) Named: Each session has a unique name per chat Plain messages and free shell For a single incoming line with no , , or , the router sends text to the attached session first when it is running; free shell mode ( ) applies only if nothing is attached (or the focused session is not running). Use for a one-off sync shell line while attached. Session States Created: Session initialized but not started Running: Session active and accepting input Attached: Session is focused for input Detached: Session running but not focused Stopped: Graceful shutdown requested Killed: Force termination Exited: Process terminated normally"},{title:"Session Commands",body:"Basic Operations Input Control Output Management Session Information"},{title:"Configuration",body:"Session Limits Terminal Settings Behavior Settings When , , or similar shows a password prompt, omnish detects it in recent terminal output and does not send the readline clear keys ( ) for your next reply \u2014 those keys break no-echo password readers and often appear as literal in chat. A one-time hint is sent unless is false. Passwords are still written to the session log on disk."},{title:"Use Cases",body:"Development Workflows ```text"}],keywords:["interactive","sessions","omnish","provide","full","terminal","access","within","your","messaging","chats","enabling","you","to","run","tui","applications","repls","and","tools","directly","from","whatsapp","or","telegram","overview","session","management","commands","configuration","use","cases"],relatedCommands:["/apps help","/apps start","/apps list","/apps attach","/apps detach","/apps stop","/apps kill","/apps send","/apps key","/apps tail","/apps since","/apps mute","/apps raw"]},{id:"docs-features-tunneling",path:"docs/features/tunneling.md",title:"Tunneling \u2014 omnish",summary:"omnish tunneling publishes a public URL that forwards to a local HTTP or TCP port on the machine running the tunnel client. The default relay is .",sections:[{title:"CLI (primary)",body:"Secrets are stored in (mode ) or . Override the relay with , in , or on expose commands."},{title:"Chat (optional)",body:"Login, logout, and status from chat work whenever the gateway runs (even if is false): , , . The bot reply does not echo your token; the inbound chat message still contains it (WhatsApp/Telegram history) \u2014 prefer on the host for highly sensitive tokens. When is in , allowlisted users can also run: Chat tunnels run inside the gateway process ( ) and share the same relay token as the CLI."},{title:"Security",body:"A tunnel URL is a capability: anyone who can open the URL can reach the forwarded service. Dev servers are often unauthenticated; treat tunneling like exposing a port on the public internet. Chat tunneling uses the same trust model as : allowlisted identities can open tunnels as the gateway OS user. in chat stores the bearer token on the gateway host but leaves a copy in the messaging transcript; use host CLI login if that risk matters for your threat model. See Security model."},{title:"Self-hosted relay",body:"For development or private deployments, run the relay in and point omnish at it with or . Operators: testing and operations (health checks, smoke, production VPS layout)."}],keywords:["tunneling","omnish","publishes","public","url","that","forwards","to","local","http","or","tcp","port","on","the","machine","running","tunnel","client","default","relay","is","cli","primary","chat","optional","security","self-hosted"],relatedCommands:["/tunnel help","/tunnels","/config help","/tunnel","/guides","/tunnel-setup-from-zero","/tunnel-auth","/tunnel login","/tunnel logout","/tunnel status","/telegram history","/tunnel http","/tunnel tcp","/tunnel stop"]},{id:"docs-features-watch",path:"docs/features/watch.md",title:"Watch \u2014 OS event eye",summary:"Lightweight OS event subscriptions that notify you on WhatsApp or Telegram when something changes on the machine running .",sections:[{title:"Quick start",body:"Enable watching: Or: or Run . Add rules: You should receive debounced messages like:"},{title:"Device-wide rules",body:"Watch rules are shared on the host, not private to one chat: One namespace per machine in (max 20 rules per device). Any allowlisted peer can , edit, pause, or remove any rule. Rule names must be unique on the device (two peers cannot each have a rule named ). (the default on new rules) alerts the peer who created the rule. Use , , or to reach more recipients. shows the creator peer key. If you had duplicate names from an older per-chat layout, omnish renames extras to on load."},{title:"Runtime model",body:"Watch is not a separate daemon or timed session. It runs inside the gateway process ( , foreground or ). Topic Behavior ----------------- ------------------------------------------------------------------------------------------------------------------------------------------------------- How long Indefinite while the gateway runs, is true, and the rule is enabled and not paused. There is no session timeout. Debounce (default 2s, range 500ms\u201360s) coalesces bursts before chat notify. Rate cap per rule (default 30). Service polls Each rule runs / / about every 30 seconds. Sys journal Each rule tails (Linux) with auth.log fallback; classifies login, fail, sudo, reboot, accounts, firewall. Background Same Node process as the gateway; keeps Watch alive like foreground. Not a separate OS service unless you installed the gateway as one."},{title:"Cowork, `/bg`, and Watch",body:"Feature What it does Relation to Watch ---------- ------------------------------------------------------------------ ------------------------------------------------------------------------------------------------- Watch OS events (FS, package logs, services, journal/auth) \u2192 chat alerts \u2014 Cowork Scheduled or on-demand shell commands while the gateway runs Parallel \u2014 same gateway, shared notify routing only. Does not trigger or consume watch rules. Background shell jobs from chat Unrelated \u2014 no integration with watch adapters. See Cowork for task schedules and heartbeats."},{title:"Persistence and restart",body:'Watch rules are not memory-only. They are saved on disk and survive gateway and service restarts. Data Path ------------------------------------------ -------------------------------------------------------------------------------- Rules (paths, excludes, paused, notify, \u2026) Recent events and state-change keys Global on/off and tuning ( , , debounce, rate cap) On gateway boot: If and (default), omnish reloads and starts adapters for rules that are enabled and not paused. If , rules remain on disk but adapters do not run until . If , rules persist but you must (or change a rule) to start adapters without restarting the whole gateway. Use to see file paths, saved rule counts, and adapter health. Troubleshooting persistence Symptom Check --------------------------------- --------------------------------------------------------------------------------- "Rules gone" after restart \u2014 they should still be there No alerts after restart in config; paused/disabled rules; Rules on disk but nothing running or ; if'},{title:"Rule lifecycle",body:"State What it means Command -------------- ------------------------------------- ------------------------------------------------ Active Adapter running, alerts on or Paused Rule kept, adapter stopped, no alerts or Disabled Rule kept in list, Removed Deleted from Global off All adapters stopped After pause, stop, disable, or rm, pending debounced messages are cancelled so you should not get a late alert. pause / stop \u2014 same effect: stop watching, keep the rule for later. resume \u2014 start again if the rule is enabled. enable / disable \u2014 per-rule on/off (distinct from global and ). Check state: , , ."},{title:"Filesystem watches",body:"Add Root path \u2014 first path after the name ( , ). Events \u2014 optional comma list (default: ). Excludes (optional, both supported): Syntax Example ------------------ -------------------------------------- or You can combine them: between exclude clauses is also accepted as a separator. Manage excludes later Excludes are applied twice for efficiency: native watcher ignore list + post-filter before notify."},{title:"Package and service watches",body:"Kind Command -------- ----------------------------------------------------------------------- Packages \u2014 install/remove from OS logs Services \u2014 state changes on named units Use on noisy service checks."},{title:"System journal watches (`sys`)",body:"Monitor login/logout, failed auth, sudo, reboot/shutdown, account changes, and firewall events on Linux (journal + auth.log). Omit topics to subscribe to all six. Preset adds (sys) + (pkg) with . Host permissions (Linux): gateway user should be in and groups to read system logs without root: ```bash sudo usermod -aG adm,systemd-journal $USER"}],keywords:["watch","os","event","eye","lightweight","subscriptions","that","notify","you","on","whatsapp","or","telegram","when","something","changes","the","machine","running","quick","start","device-wide","rules","runtime","model","cowork","/bg","and","persistence","restart","rule","lifecycle","filesystem","watches","package","service","system","journal","sys"],relatedCommands:["/watch help","/config","/watch on","/config set","/watch add","/deploy create","/projects","/tmp","/watch apply","/watch list","/home","/you"]},{id:"docs-features-webhook-receiver",path:"docs/features/webhook-receiver.md",title:"Webhook receiver \u2014 CI/CD notifications via chat",summary:"The webhook receiver is a lightweight HTTP server built into the omnish gateway. It accepts requests with JSON payloads, formats them into concise messages, and delivers them to your WhatsApp or Telegram chat via the existing pipeline.",sections:[{title:"Prerequisites",body:"A running gateway: . set to in ."},{title:"Configuration",body:"Key Type Default Description ---------------- ------- ------------- --------------------------------------------------------------------------------------------------------------------------------------- boolean Enable the webhook HTTP server number (random) Port to listen on. picks a random available port. string Bind address. Use to accept external connections (see security note). string Bearer token for authentication. If empty when the receiver starts, a random 32-byte hex token is generated and saved to . Example :"},{title:"Endpoint",body:"The token can also be passed as a query parameter: . Request body Any valid JSON object. The receiver formats the payload into a chat message using built-in formatters for known CI systems, or a generic format for everything else. Optional fields in the JSON body: Field Type Description --------- ------ ------------------------------------------------------------------------------------------------------------------------------------------ string Target chat identity (e.g. or ). If omitted, the message goes to the first allowlisted peer. string Label for the source system (shown in the formatted message). Can also be set via query parameter or header. string Simple text message (used as-is when present). string Fallback message text. string Title line for generic payloads. string Status line for generic payloads. Response Status Body Meaning ------ -------------------------------------------------- ------------------------------ Message delivered Invalid JSON or no target peer Missing or invalid token Not a POST request Body exceeds 256 KB failed"},{title:"Built-in CI formatters",body:"GitHub Actions When the payload contains and a object, the receiver formats: GitLab CI When the payload contains and , the receiver formats: Generic payloads For any other JSON, the receiver uses , , , and fields if present, or falls back to a truncated JSON preview."},{title:"Examples",body:"GitHub Actions workflow Add a step at the end of your workflow to notify on completion: GitLab CI Simple notification from a script"},{title:"Security",body:"The webhook server binds to 127.0.0.1 by default \u2014 only local processes can reach it. If you set to , the server is accessible from the network. Use a firewall or reverse proxy with TLS in production. The bearer token is compared using to prevent timing attacks. Maximum payload size is 256 KB."},{title:"Default peer resolution",body:"When the incoming payload does not include a , the receiver picks the first available peer from the gateway's allowlist: WhatsApp peers are checked first, then Telegram. If no peer is available, the request returns a error."},{title:"See also",body:"Configuration guide \u2014 webhook config keys Cowork heartbeat \u2014 combine with heartbeat tasks for dead-man's-switch monitoring Background jobs \u2014 per-job completion notifications"}],keywords:["webhook","receiver","ci/cd","notifications","via","chat","the","is","lightweight","http","server","built","into","omnish","gateway","it","accepts","requests","with","json","payloads","formats","them","concise","messages","and","delivers","to","your","whatsapp","or","telegram","existing","pipeline","prerequisites","configuration","endpoint","built-in","ci","formatters","examples","security","default","peer","resolution","see","also"],relatedCommands:["/help","/webhook authorization","/json","/repo","/github","/owner","/actions","/runs","/project","/checkout","/your-server","/webhook","/localhost"]},{id:"docs-guides-background-and-boot",path:"docs/guides/background-and-boot.md",title:"Background gateway and start on boot",summary:"Portable CLI (all platforms): starts the gateway detached by default; logs default to ; pass (alias: ) to append elsewhere; reads and stops the process. Equivalent: (or ). Foreground: or . Use these for ad-hoc background runs without installing a system integration.",sections:[{title:"Linux (systemd --user)",body:"Copy contrib/omnish.service to . Set to your Node path and absolute path to , and if the data directory is not . Run: For a user service to run without an active login session, enable lingering:"},{title:"macOS (launchd)",body:"The LaunchAgent label is (reverse-DNS for omnish.dev). Older templates used ; if you already installed that, boot it out before installing the new plist (see below). Edit contrib/dev.omnish.gateway.plist: set Node path, path, and (and log paths if you use them). Copy to . Load (replace with output): To unload: Migrating from : remove the old job first, then install the new file: Alternative: add a small script that runs to System Settings \u2192 General \u2192 Login Items (simpler, no auto-restart on crash)."},{title:"Windows (Task Scheduler)",body:"Open Task Scheduler \u2192 Create Task\u2026 (not a basic task). General: run only when user is logged on (typical for WhatsApp session). Triggers: At log on for your user. Actions: Start a program Program: path to (e.g. from in ). Add arguments: (adjust the path; quotes if it contains spaces). Start in (optional): install folder. Set OMNISHHOME under user environment variables if you do not use the default data directory, or use a wrapper that then runs . Optional: import contrib/omnish-windows-task.xml after editing paths (import may need tweaks per account; the GUI is more reliable if XML import fails). Advanced: NSSM or WinSW can install Node as a Windows Service for machine-wide or headless scenarios \u2014 not maintained by this repo."},{title:"Stopping the gateway",body:"(any OS): uses the pidfile from or . On Windows, if signaling the process fails, omnish may fall back to (best-effort shutdown). systemd / launchd / Task Scheduler: use the manager\u2019s stop / disable as usual; do not rely on from a different start method."},{title:"Environment",body:": data directory (same as elsewhere in omnish). For services, set it in the unit / plist / task environment, not only in the shell profile."}],keywords:["background","gateway","and","start","on","boot","portable","cli","all","platforms","starts","the","detached","by","default","logs","to","pass","alias","append","elsewhere","reads","stops","process","equivalent","or","foreground","use","these","for","ad-hoc","runs","without","installing","system","integration","linux","systemd","--user","macos","launchd","windows","task","scheduler","stopping","environment"],relatedCommands:["/service help","/service instructions","/logs","/gateway","/service","/service logs","/service install","/features","/service-from-chat","/dist","/index","/omnish","/contrib"]},{id:"docs-guides-channels-bluebubbles",path:"docs/guides/channels/bluebubbles.md",title:"bluebubbles",summary:"Enable in , configure , allowlist with .",sections:[],keywords:["bluebubbles","enable","in","configure","allowlist","with"],relatedCommands:["/readme"]},{id:"docs-guides-channels-discord",path:"docs/guides/channels/discord.md",title:"Discord",summary:"",sections:[{title:"Standalone",body:"Create a bot at Discord Developer Portal. Enable Message Content Intent and Direct Messages. Set token via env or config: DM-only by default (no guild shell execution)."},{title:"Platform (hosted relay)",body:"Discord bot install uses an invite URL, not per-user OAuth tokens. The relay operator sets: Dashboard flow: Click Add Discord (opens invite URL). Add the bot to your server. Click I've added the bot in the dashboard. DM the bot and allowlist your user id (same pattern as Telegram). One Omnish Discord application serves all platform users; bot token stays on the relay."}],keywords:["discord","standalone","platform","hosted","relay"],relatedCommands:["/discord","/developers","/applications","/id"]},{id:"docs-guides-channels-feishu",path:"docs/guides/channels/feishu.md",title:"feishu",summary:"Enable in , configure , allowlist with .",sections:[],keywords:["feishu","enable","in","configure","allowlist","with"],relatedCommands:["/readme"]},{id:"docs-guides-channels-googlechat",path:"docs/guides/channels/googlechat.md",title:"googlechat",summary:"Enable in , configure , allowlist with .",sections:[],keywords:["googlechat","enable","in","configure","allowlist","with"],relatedCommands:["/readme"]},{id:"docs-guides-channels-imessage",path:"docs/guides/channels/imessage.md",title:"imessage",summary:"Enable in , configure , allowlist with .",sections:[],keywords:["imessage","enable","in","configure","allowlist","with"],relatedCommands:["/readme"]},{id:"docs-guides-channels-irc",path:"docs/guides/channels/irc.md",title:"irc",summary:"Enable in , configure , allowlist with .",sections:[],keywords:["irc","enable","in","configure","allowlist","with"],relatedCommands:["/readme"]},{id:"docs-guides-channels-line",path:"docs/guides/channels/line.md",title:"line",summary:"Enable in , configure , allowlist with .",sections:[],keywords:["line","enable","in","configure","allowlist","with"],relatedCommands:["/readme"]},{id:"docs-guides-channels-matrix",path:"docs/guides/channels/matrix.md",title:"matrix",summary:"Enable in , configure , allowlist with .",sections:[],keywords:["matrix","enable","in","configure","allowlist","with"],relatedCommands:["/readme"]},{id:"docs-guides-channels-mattermost",path:"docs/guides/channels/mattermost.md",title:"mattermost",summary:"Enable in , configure , allowlist with .",sections:[],keywords:["mattermost","enable","in","configure","allowlist","with"],relatedCommands:["/readme"]},{id:"docs-guides-channels-msteams",path:"docs/guides/channels/msteams.md",title:"msteams",summary:"Enable in , configure , allowlist with .",sections:[],keywords:["msteams","enable","in","configure","allowlist","with"],relatedCommands:["/readme"]},{id:"docs-guides-channels-nextcloud-talk",path:"docs/guides/channels/nextcloud-talk.md",title:"nextcloud-talk",summary:"Enable in , configure , allowlist with .",sections:[],keywords:["nextcloud-talk","enable","in","configure","allowlist","with"],relatedCommands:["/readme"]},{id:"docs-guides-channels-nostr",path:"docs/guides/channels/nostr.md",title:"nostr",summary:"Enable in , configure , allowlist with .",sections:[],keywords:["nostr","enable","in","configure","allowlist","with"],relatedCommands:["/readme"]},{id:"docs-guides-channels-qqbot",path:"docs/guides/channels/qqbot.md",title:"qqbot",summary:"Enable in , configure , allowlist with .",sections:[],keywords:["qqbot","enable","in","configure","allowlist","with"],relatedCommands:["/readme"]},{id:"docs-guides-channels-README",path:"docs/guides/channels/README.md",title:"Chat channels",summary:"omnish supports 22 messaging channels (OpenClaw parity, excluding internal QA harness).",sections:[{title:"Enable channels",body:"Legacy ( ) still works and maps to ."},{title:"CLI",body:"See ADR-0012 for architecture."}],keywords:["chat","channels","omnish","supports","22","messaging","openclaw","parity","excluding","internal","qa","harness","enable","cli"],relatedCommands:["/whatsapp","/telegram","/discord","/slack","/matrix","/irc","/signal","/googlechat","/mattermost","/lark","/feishu","/line"]},{id:"docs-guides-channels-signal",path:"docs/guides/channels/signal.md",title:"Signal",summary:"Text DMs via signal-cli as a linked device to your primary Signal phone.",sections:[{title:"Prerequisites",body:"installed on the gateway host (standalone) or relay host (platform attached mode). Version 0.13+ recommended. A Signal account on your phone (primary device)."},{title:"Standalone: QR link",body:"Scan the terminal QR from Signal \u2192 Linked devices \u2192 Link a device. Replace an existing session: Credentials are stored under ( and in ). Enable the channel and allowlist senders: Env overrides (optional): , (if you configured manually)."},{title:"Platform attached mode",body:"Ensure is on the relay host. Dashboard Connectors \u2192 Signal \u2192 Link Signal, or: Scan the QR from your phone (Linked devices). Add your number to allowlists ( on dashboard Security, or ). Run on attached devices. Unlink:"},{title:"Limits and notes",body:"Signal allows up to five linked devices per primary account. The pairing URI expires quickly; retry link if the QR times out (3 minutes). Stop local before re-linking to avoid session conflicts. SMS register/verify for a new dedicated number is not built into omnish \u2014 use manually if you need that path. Text only (no inbound/outbound media in omnish today). See channels README."}],keywords:["signal","text","dms","via","signal-cli","as","linked","device","to","your","primary","phone","prerequisites","standalone","qr","link","platform","attached","mode","limits","and","notes"],relatedCommands:["/github","/asamk","/signal-cli","/signal","/verify","/outbound media","/readme"]},{id:"docs-guides-channels-slack",path:"docs/guides/channels/slack.md",title:"Slack",summary:"",sections:[{title:"Create the Slack app",body:"Use the ready-made manifest and icons in : api.slack.com/apps \u2192 Create New App \u2192 From an app manifest \u2192 paste . Upload as the app icon. Enable Socket Mode and create an App-Level Token ( ) \u2192 ( ). Install to Workspace \u2192 copy Bot Token \u2192 ( )."},{title:"Standalone",body:"DM the bot to discover your Slack user id, then allowlist: Or configure / in config JSON. Bot scopes Scope Purpose ----------------------- ------------------------- Reply in DMs , Receive DMs (Socket Mode) User lookup for Inbound attachments Outbound files DM-only \u2014 non-IM channels are ignored."},{title:"Platform (hosted relay)",body:"When the relay operator configures a hosted Omnish Slack app: Users open the dashboard Connectors tab and click Connect with Slack. OAuth v2 install stores a per-workspace in encrypted ; the shared stays on the relay. Manual tokens remain available under Advanced: manual tokens in the dashboard. See contrib/slack-app/README.md for manifest details and asset sizes."}],keywords:["slack","create","the","app","standalone","platform","hosted","relay"],relatedCommands:["/slack-app","/contrib","/readme","/apps","/api","/manifest","/assets","/icon-512","/id","/send"]},{id:"docs-guides-channels-synology-chat",path:"docs/guides/channels/synology-chat.md",title:"synology-chat",summary:"Enable in , configure , allowlist with .",sections:[],keywords:["synology-chat","enable","in","configure","allowlist","with"],relatedCommands:["/readme"]},{id:"docs-guides-channels-telegram",path:"docs/guides/channels/telegram.md",title:"telegram",summary:"Enable in , configure , allowlist with .",sections:[],keywords:["telegram","enable","in","configure","allowlist","with"],relatedCommands:["/readme"]},{id:"docs-guides-channels-tlon",path:"docs/guides/channels/tlon.md",title:"tlon",summary:"Enable in , configure , allowlist with .",sections:[],keywords:["tlon","enable","in","configure","allowlist","with"],relatedCommands:["/readme"]},{id:"docs-guides-channels-twitch",path:"docs/guides/channels/twitch.md",title:"Twitch",summary:"",sections:[{title:"Standalone",body:"Enable in , configure ( , , ), allowlist with . See channels README."},{title:"Platform (hosted relay)",body:"When the relay operator configures a hosted Omnish Twitch app: Users click Connect with Twitch in the dashboard. OAuth2 authorization code flow stores IRC credentials ( , , ) in encrypted connector secrets. Manual token fields remain under Advanced in the dashboard."}],keywords:["twitch","standalone","platform","hosted","relay"],relatedCommands:["/readme"]},{id:"docs-guides-channels-whatsapp",path:"docs/guides/channels/whatsapp.md",title:"whatsapp",summary:"Enable in , configure , allowlist with .",sections:[],keywords:["whatsapp","enable","in","configure","allowlist","with"],relatedCommands:["/readme"]},{id:"docs-guides-channels-zalo",path:"docs/guides/channels/zalo.md",title:"zalo",summary:"Enable in , configure , allowlist with .",sections:[],keywords:["zalo","enable","in","configure","allowlist","with"],relatedCommands:["/readme"]},{id:"docs-guides-channels-zalouser",path:"docs/guides/channels/zalouser.md",title:"zalouser",summary:"Enable in , configure , allowlist with .",sections:[],keywords:["zalouser","enable","in","configure","allowlist","with"],relatedCommands:["/readme"]},{id:"docs-guides-configuration",path:"docs/guides/configuration.md",title:"Configuration Guide - omnish",summary:"Complete configuration reference and customization guide.",sections:[{title:"Configuration Overview",body:"omnish uses a JSON configuration file with sensible defaults. You can customize every aspect of the system's behavior. Configuration File Location Default: Legacy: (if upgrading) Override: Set Check your configuration location: ```bash omnish status"}],keywords:["configuration","guide","omnish","complete","reference","and","customization","overview"],relatedCommands:["/config help","/config keys","/config","/path","/to","/dir","/bin","/bash","/features","/chat-llm-fallback","/tunnel","/login","/v1","/me"]},{id:"docs-guides-coordinator-workforce",path:"docs/guides/coordinator-workforce.md",title:"Coordinator workforce manager",summary:"The job board Coordinator is a deterministic workforce layer: onboard employees, assess skills, customize work flow, dispatch through the harness, record performance, collect human feedback, and suggest recruitment when roles are overloaded.",sections:[{title:"Communication (all employees)",body:"Every specialist run receives , , , and (coordinator-only) . Do not document communication as alone \u2014 always use the full terminal commands below (requires on the host). Role When Full command ------------------ ------------------------------------------------------------- ------------------------------------------------- Any specialist While job is (at least once per phase; on blockers) Coordinator After supervisor pass, when the plan requires file delivery Helper script (employee pack): runs the specialist command. Specialists must not send final CHECK deliverables via sendto before review. The coordinator has final say on files. At handoff, must still include: \u2014 what was communicated to the coordinator/human \u2014 plain-language outcome for the job owner Work plans include a step under the plan phase. The supervisor rejects handoffs missing these fields. When is true (default), the coordinator runs the file-send command automatically after if the plan mentions / distribution and a media CHECK file exists in the work dir."},{title:"Onboarding",body:"Creates with , , and registry entry. Coordinator seeds core competencies and activates the employee when the skill pack exists."},{title:"Skills and flow",body:"Before dispatch (when is true), the Coordinator assesses gaps vs the job plan. With , it runs your agent to fill ; otherwise a deterministic template fills gaps and writes . Per-employee may add extra steps in allowed phases; required phases and cannot be removed. Harness env: , ."},{title:"Performance and feedback",body:"After each supervisor verdict, a row is stored in . When a job passes:"},{title:"Recruitment",body:"Heuristics (overload, multi-domain plans, rework) create suggestions. Humans accept before a new employee exists:"},{title:"Optional finetuning",body:"When and the coordinator agent are enabled, poor feedback can produce files under . Promote only after review:"},{title:"Config",body:"Key Default Purpose --------------------------------- ------- ----------------------------------------------------------------------------------- External agent for research/finetune Shell command Allow proposed tuning Gap assessment before dispatch Coordinator runs after done when plan requires delivery One-shot satisfaction reminder See also Employee task flow, digital-employees.md, and job-board.md."}],keywords:["coordinator","workforce","manager","the","job","board","is","deterministic","layer","onboard","employees","assess","skills","customize","work","flow","dispatch","through","harness","record","performance","collect","human","feedback","and","suggest","recruitment","when","roles","are","overloaded","communication","all","onboarding","optional","finetuning","config"],relatedCommands:["/sendto","/employee-pack-example","/scripts","/board-status","/human","/board employee","/board","/employees","/board feedback","/board recruit","/proposed","/finetune"]},{id:"docs-guides-digital-employees",path:"docs/guides/digital-employees.md",title:"Digital employees \u2014 job board, coordinator, supervisor",summary:"omnish stays local execution infrastructure. Digital employees are external agent CLIs (or shell wrappers) coordinated by three meta-roles plus specialists.",sections:[{title:"Roles",body:"Role Responsibility Human? --------------- ------------------------------------------------------------------------------------------------- ----------- Human Posts parent jobs to the job board Intake only Coordinator Assigns specialists, creates prerequisite jobs, unblocks dependents, enforces resource limits No Supervisor Verifies work against the job plan; pass, rework, or fail No Specialist Runs assigned work; writes No The coordinator and supervisor run inside the gateway while it is up ( defaults to true). Use for a one-shot cycle or set to for fully manual mode."},{title:"Job board",body:"Storage: Artifacts: (report, verdict, audit via DB) Status flow Full pipeline (human intake through supervisor and feedback): Employee task flow. Chat commands and are aliases. Acceptance criteria in the plan Supervisor runs deterministic checks from the plan: Optional LLM review can wrap the same contract later; the built-in supervisor is deterministic first."},{title:"Core competencies (always on)",body:"No matter how vague an is (e.g. ), omnish never runs it directly. The employee harness always: Seeds with canonical steps: plan \u2192 implement \u2192 execute \u2192 test \u2192 review Runs the inner command via Tracks phase progress and notifies the job owner (configurable) Enforces handoff via the supervisor (work plan + report contract) You define what runs ( ). omnish defines how employees work. Competency Enforcement ----------------------- ----------------------------------------------------------- Five phases Pre-seeded work plan Plan before work Harness seeds plan before inner CLI Structure + self-review Supervisor + step Outcome ownership , , Progress visibility Chat notifications on phases and status"},{title:"Specialist contract",body:"Each employee is configured in : Legacy is accepted as an alias for . Environment injected at run time: , , , \u2014 harness-seeded \u2014 \u2014 e.g. Plan before work (harness + supervisor) The harness creates the work plan before your agent runs. Specialists mark steps complete (in script or via ). Before handoff: All steps and set; with and matching The supervisor rejects handoffs that miss any core competency (with human-readable labels in verdict notes). Schema: work-plan.schema.json. Example scripts: contrib/employee-pack-example/specialists/. report.json (required) See contrib/employee-pack-example/schemas/report.schema.json. Must reference and with matching completed ids in the work plan. Prerequisite requests (exit code 2) If work cannot start, exit 2 and set in the report. The coordinator creates child jobs, blocks the parent, and unblocks when children reach . Specialists must not spawn parallel agents themselves."},{title:"Resource governor",body:"(defaults created on first coordinator run): Field Default Meaning --------------------- ------- ---------------------------------- 2 Host-wide running specialists 1 Extra cap for jobs 75 Pause dispatch when load is high 2048 Pause when free RAM is low 90 Optional gate Check live status: ."},{title:"Progress notifications",body:"In : Setting Values Effect ---------------------- ------------------------------- -------------------------------------------------- \\ \\ Phase step completion (and for minor status) \\ Start, review, done, failed, blocked, rework Examples: , ."},{title:"Coordinator workforce",body:"Onboarding, skills, performance history, human feedback, and recruitment suggestions are documented in Coordinator workforce manager."},{title:"Configuration",body:"In : Primary agent for all registry roles: Escalations (job after max rework) always notify the job owner on the allowlisted chat surface."},{title:"Example pack",body:"contrib/employee-pack-example \u2014 stub , , scripts and sample . Point your registry at real agent CLIs when ready: Then reference that wrapper from instead of the stub scripts."},{title:"Related",body:"Employee task flow \u2014 human \u2192 coordinator \u2192 specialist \u2192 accomplishment Job board feature reference System agents and Cowork \u2014 scheduled shell shifts (complementary)"}],keywords:["digital","employees","job","board","coordinator","supervisor","omnish","stays","local","execution","infrastructure","are","external","agent","clis","or","shell","wrappers","coordinated","by","three","meta-roles","plus","specialists","roles","core","competencies","always","on","specialist","contract","resource","governor","progress","notifications","workforce","configuration","example","pack","related"],relatedCommands:["/board tick","/board","/cowork","/jobs","/employee-task-flow","/board add","/ok","/board list","/board show","/board capacity","/board cancel","/board employees"]},{id:"docs-guides-docker-gateway-golden-path",path:"docs/guides/docker-gateway-golden-path.md",title:"Docker gateway \u2014 golden path (reference)",summary:"Run omnish inside a container so a deployed app gains chat-driven shell access on that box. Reference files: .",sections:[{title:"Two ways to use omnish in Docker",body:"Path When Setup in container -------------------------- ------------------------------------------------ ----------------------------------------------------------------------------------------------- Attached (recommended) Platform account; messengers linked on dashboard , , , \u2014 no QR in container Standalone No hosted layer; expert / air-gapped Persistent volume + + inside container (or on host) The compose file in implements standalone by default. Use attached env vars for the platform path (see Platform attached mode, Platform reference)."},{title:"Attached mode (implemented)",body:"Add chat-driven ops to an existing app container without Baileys inside the image: Link WhatsApp/Telegram on the platform dashboard (once per account). Set allowlist on the dashboard. Deploy with the env above; outbound HTTPS to the platform only. Legacy env aliases: , . Messengers do not run inside the container; the platform routes chat to this CLI."},{title:"Standalone reference (implemented today)",body:"What you get Pinned npm version ( ). Non-root user (uid 1000), as PID 1. HEALTHCHECK ( ) \u2014 process up only; not messenger connectivity. Build and run First-time pairing (empty volume): Then as above."},{title:"Networking",body:"Concern Guidance -------------- -------------------------------------------------------------------------------------------------- Attached Outbound HTTPS to communication layer; no Baileys in container Standalone Outbound to WhatsApp/Telegram APIs from container Tunnels Tunnel client runs in gateway namespace; see Tunnel setup from zero"},{title:"Environment",body:"Standalone today: \u2014 volume mount (compose: ) , \u2014 optional overrides Attached: , (aliases: , ) Optional /"},{title:"Security",body:"Allowlisted inbox \u2192 real shell as the container user. Protect volumes and tokens. Do not expose without TLS and auth \u2014 Webhook receiver."},{title:"See also",body:"Communication layer model Background gateway and start on boot"}],keywords:["docker","gateway","golden","path","reference","run","omnish","inside","container","so","deployed","app","gains","chat-driven","shell","access","on","that","box","files","two","ways","to","use","in","attached","mode","implemented","standalone","today","networking","environment","security","see","also"],relatedCommands:["/gateway-docker","/contrib","/architecture","/communication-layer-model","/tunnel","/telegram on","/docker-compose","/telegram apis","/tunnel-setup-from-zero","/home","/node","/features"]},{id:"docs-guides-employee-task-flow",path:"docs/guides/employee-task-flow.md",title:"Human \u2192 Coordinator \u2192 Employee \u2192 Task accomplishment",summary:"End-to-end flow for the omnish job board: how work moves from a human through the coordinator and a harnessed digital employee to supervised completion.",sections:[{title:"Four roles (not one \u201Cemployee\u201D)",body:"Role Who Responsibility --------------------------------- -------------------------------------------------- -------------------------------------------------------------------------- Human Allowlisted chat peer (job owner) Creates jobs, cancels, optional feedback after completion Coordinator Gateway loop ( ) Unblocks deps, picks assignee, acquires resource slots, dispatches harness Specialist (digital employee) External CLI from Runs inside harness; writes + Supervisor Deterministic QA: pass \u2192 , rework, or fail The coordinator does not judge quality. The supervisor does not choose the next assignee or create prerequisite jobs. That split is intentional \u2014 see Job board \u2014 separation of duties."},{title:"High-level sequence",body:""},{title:"Phase 1 \u2014 Human intake",body:"Entry: Chat \u2192 ( ) \u2192 ( ). Example: What happens: Job in with Work directory: = the human who posted the job Optional ; otherwise the coordinator picks on dispatch via plan heuristics Human can also: , , , (one coordinator cycle), , , and post-completion ."},{title:"Phase 2 \u2014 Coordinator tick (automation)",body:"Trigger: when is true, or . Each tick runs once, in this order: \u2014 \u2192 when all jobs are Notify pending jobs whose dependencies just became satisfied \u2014 diff and notify owner of phase changes (if configured) \u2014 every job \u2192 (may finish work from a previous dispatch) Dispatch at most one new job (if governor slot available): \u2014 or , deps satisfied Auto-assign via if no assignee (review \u2192 reviewer; implement/build/code \u2192 implementer; else researcher) + \u2014 skills research, communication brief, role context \u2014 harness execution \u2192 again \u2014 supervises jobs that entered during this tick (so one can dispatch and finish) Implementation sets directly from / . The status exists in the type system but is not used on the main dispatch path. Default: is while the gateway runs; status and handoff messages go to the job owner on chat."},{title:"Phase 3 \u2014 Digital employee execution (harness)",body:"The coordinator never runs directly. Load specialist from ( only; legacy alias) \u2014 phases: plan \u2192 implement \u2192 execute \u2192 test \u2192 review Env: , , , , briefing extras \u2192 Specialist marks steps (env hints or ) Specialist writes ( , , , , \u2026) Outcomes after harness exits: Exit / report Job status Next step ------------------------------------- ---------------------- ----------------------------------------------- Normal (not exit 2) Supervisor on this or next tick Exit 2 + Child jobs created; parent waits on Missing registry / bad config (stub report) Supervisor likely rework / fail When children reach , the parent unblocks and is dispatched again."},{title:"Phase 4 \u2014 Supervisor (quality gate)",body:"calls for each job. Checks include: exists and validates (steps complete, , competencies) contract (summary, , , communication, handoff) matches completed step ids in the work plan Plan lines \u2014 files must exist under the work dir Verdict \u2192 status: Verdict New status Notes ---------- ---------- --------------------------------------------------------- pass prompts feedback; may suggest recruitment rework Re-enters queue until (default 3) fail Owner notified; slot released Writes , audit rows, and performance history per assignee. Technical accomplishment = supervisor pass. Human satisfaction is separate ( )."},{title:"Phase 5 \u2014 Human closure (after `done`)",body:"Optional but closes the workforce loop: if a role-split was suggested Optional coordinator agent ( ) can propose / under \u2014 promote only after review via ."},{title:"Job status state machine",body:"Statuses: , , (reserved), , , , , , \u2014 see ."},{title:"Artifacts per job",body:"Path Writer ----------------------------------- -------------------- Harness + specialist Specialist (or stub) Supervisor Status and audit"},{title:"Not this flow",body:"is scheduled shell shifts \u2014 complementary, not the human \u2192 coordinator \u2192 specialist \u2192 supervisor pipeline."},{title:"Summary",body:"The human posts criteria to the board; the coordinator ticks to unblock, review prior handoffs, and dispatch one runnable job through the harnessed specialist CLI; the supervisor accepts or rejects into / / ; the human optionally rates satisfaction after ."}],keywords:["human","coordinator","employee","task","accomplishment","end-to-end","flow","for","the","omnish","job","board","how","work","moves","from","through","and","harnessed","digital","to","supervised","completion","four","roles","not","one","high-level","sequence","phase","intake","tick","automation","execution","harness","supervisor","quality","gate","closure","after","done","status","state","machine","artifacts","per","this","summary"],relatedCommands:["/digital-employees","/features","/job-board","/coordinator-workforce","/board","/coordinator","/employees","/supervisor","/board add","/board tick","/board feedback","/chat"]},{id:"docs-guides-interactive-cli",path:"docs/guides/interactive-cli.md",title:"Interactive terminal (`omnish i`)",summary:"Run the same commands you use in chat, but from your local terminal.",sections:[{title:"Commands",body:"Run from any directory. Your CLI session\u2019s working directory starts at (change it with or using your configured command prefix). Flags Flag Meaning ------------------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------- Sender key for chat-driven cluster commands ( or ). If omitted, a synthetic sender tied to the CLI session is used ( ). / Run a single line and exit (non-interactive). Useful for scripts."},{title:"Trust model",body:"is not gated by the inbox allowlist. Anyone who can run commands on your machine can use it\u2014it has the same trust as your login shell. Chat interfaces remain allowlisted as before."},{title:"`/sendto`: push files or plain text to WhatsApp or Telegram",body:"When is active on this machine, you can use from to choose exactly who gets files, or to send a plain chat message (not an attachment). Syntax Files: selectors are checked from the current folder. Add an optional caption after . Plain text: same destinations; message is everything after the flag (or the value). To send a file whose name looks like a flag, use an explicit path (e.g. ). Destination forms: \u2014 all WhatsApp recipients from \u2014 all Telegram recipients from \u2014 both channels (all allowlisted WA + TG recipients) \u2014 one explicit WhatsApp recipient \u2014 explicit WhatsApp recipient list \u2014 one explicit Telegram recipient (compatibility form) Selector forms: \u2014 explicit list \u2014 cwd glob \u2014 recursive cwd glob Compatibility aliases also accepted: , , , , . Examples: Common patterns ```text"}],keywords:["interactive","terminal","omnish","run","the","same","commands","you","use","in","chat","but","from","your","local","trust","model","/sendto","push","files","or","plain","text","to","whatsapp","telegram"],relatedCommands:["/help","/sendto","/sendto wa","/photo","/sendto tg","/report","/promo","/downloads","/telegram outbound","/send","/files-send-receive","/bg","/reload"]},{id:"docs-guides-platform-attached-mode",path:"docs/guides/platform-attached-mode.md",title:"Platform attached mode",summary:"Run on a laptop, server, or container while messengers stay on the hosted omnish platform (tunnel relay). You link channels once on the dashboard; each machine attaches with an account token and executes shell commands locally.",sections:[{title:"Standalone vs attached",body:"Standalone (default) Attached (platform) ---------------------------- ---------------------------------------- ------------------------------------------------------- When No platform URL + token + (or env) set Where messengers connect Same host as Hosted relay (dashboard) Link WhatsApp on the device Dashboard QR or Link Telegram on the device Dashboard bot token Allowlist \u2192 local Dashboard allowlists ( on ) Shell execution Local Local (unchanged) If platform credentials are set, does not start local Baileys/Telegram clients \u2014 it opens a WebSocket to the platform and runs commands on this host only."},{title:"Prerequisites",body:"A platform account (signup/login on your relay \u2014 see tunnel relay README). Messengers linked on the platform (not via on the device, unless you use import \u2014 see below). Your phone or Telegram id on the platform allowlist (dashboard). Outbound HTTPS from the device to the platform URL (and correct reverse-proxy routing if you self-host)."},{title:"Step 1 \u2014 Account and token",body:"On the hosted relay (e.g. ): Sign up or log in via / , or use . Copy the account token from the response or dashboard. The same token works for: Attached gateway: + Persisted config:"},{title:"Step 2 \u2014 Link messengers on the platform",body:"Do this on the platform, not on the machine that will run shell commands (unless noted). WhatsApp Option A \u2014 Dashboard (recommended, 1 minute) Open the platform dashboard ( on your relay origin). Click Link WhatsApp \u2014 a QR appears automatically. Scan with WhatsApp \u2192 Linked devices. The phone may show \u201CLogging in\u201D for a few seconds while the platform reconnects after scan (515 restart \u2014 dashboard shows Finishing link\u2026; no second QR). When connected, use Add my number to allowlist (one click) if shown. Run on your machine. Option A2 \u2014 CLI QR in terminal Scan the ASCII QR, then: To disconnect: dashboard Unlink WhatsApp or . Option B \u2014 Import from a machine that already linked locally On a machine where you ran successfully, stop . Set platform URL + token, then: ```bash omnish platform import-whatsapp"}],keywords:["platform","attached","mode","run","on","laptop","server","or","container","while","messengers","stay","the","hosted","omnish","tunnel","relay","you","link","channels","once","dashboard","each","machine","attaches","with","an","account","token","and","executes","shell","commands","locally","standalone","vs","prerequisites","step"],relatedCommands:["/help","/architecture","/communication-layer-model","/gateway-config-precedence","/contrib","/tunnel-relay","/readme","/v1","/me","/telegram clients","/login on","/tunnel","/auth"]},{id:"docs-guides-platform-reference",path:"docs/guides/platform-reference.md",title:"Platform reference (complete)",summary:"Consolidated documentation for the omnish hosted platform (tunnel relay + dashboard + attached ). For a guided walkthrough, start with Platform attached mode. For relay deployment, see contrib/tunnel-relay/README.md.",sections:[{title:"One-minute checklist",body:"Step Action ---- --------------------------------------------------------------------------------------------------------------------------------------- 1 Sign up / log in at \u2014 copy account token 2 Link WhatsApp: dashboard Link WhatsApp \u2192 scan QR \u2192 Add my number to allowlist (if offered) 3 Link Telegram: paste bot token \u2192 Link / restart Telegram \u2192 DM bot \u2192 add id under Allowlists \u2192 Save allowlists 4 On your machine: (or ) 5 then 6 From allowlisted chat: or ---"},{title:"How it works",body:"Messengers terminate on the relay (WhatsApp QR, Telegram bot, Discord/Slack bots, webhook channels, etc.). Shell runs only on machines where is attached. Policy ( , , legacy ) is stored on the platform and merged into the attached CLI via (refreshed every 5 minutes and on connect). ---"},{title:"What is persisted",body:"Requires on the relay (account data). Without MongoDB, only static relay tokens work \u2014 no dashboard accounts or persistence. Data Storage Survives relay restart --------------------------------- --------------------------------------------- -------------------------------------------- Account token, email MongoDB Yes , MongoDB Yes MongoDB Yes Derived from linked rows Yes (on ) MongoDB Yes Telegram bot token MongoDB ( ) Yes (encrypted when set) Connector secrets (generic) MongoDB Yes (AES-256-GCM at rest) WhatsApp Baileys auth files Disk Yes Connector status, linked WA phone MongoDB Yes Peer \u2192 device bindings MongoDB Yes Device slots MongoDB Yes Community catalog entries MongoDB Yes On MongoDB connect, linked connectors are restored automatically ( ). Volumes (self-hosted): \u2014 WhatsApp sessions MongoDB \u2014 accounts, allowlists, connector sessions ---"},{title:"Dashboard (`/dashboard/`)",body:"After login, the dashboard loads and hydrates all forms. Account status Shows (compat shim), , connector state per channel, allowlist counts, default device, online device count. is the primary signal for which surfaces are active; ( / / ) is derived for legacy clients. Devices Create device slots, set default device, see online/offline status. The first attached may auto-create a device. Routing Chat routing (platform attached mode) uses device labels and the account default device: Syntax Routes to -------------------------------- ----------------------------------------------------- Device with label (must be online) Device by Mongo id prefix (8+ chars) / Default device only (primary) Platform reply listing all devices (no CLI required) Rename device label ( works immediately) Set default device for unprefixed commands Rename (updates routing target): Chat: Dashboard: Devices \u2192 Rename CLI: and Labels: 1\u201364 chars, alphanumeric plus , unique per account (case-insensitive). Pin hosts with so reconnect does not create a duplicate slot under an old env label. Routing order when a message arrives: \u2014 explicit target by label or id prefix; prefix stripped before forward Unprefixed \u2014 default device only (fails with hint if primary offline) Failure replies in chat for unknown/offline targets Dashboard peer bindings ( \u2192 ) remain in the API for automation but do not affect inbound dispatch (see ADR-0013). Example fleet: Set default device in dashboard Devices tab or . Allowlists Field Format Notes --------------------- --------------------------- ------------------------------------------- WhatsApp E.164, comma-separated e.g. Telegram Numeric user ids Use bot in DM to discover your id Other linked channels Sender ids, comma-separated Stored in Save allowlists \u2192 with , , and/or (single object preserves untouched channels). Changes apply immediately (connectors reload allowlists per message). Catalog: lists all 22 channel specs including ( , , , , , ). Webhook hook URLs: (token returned when linking webhook-kind connectors). Link auth (hosted relay): Slack and Twitch use OAuth ( \u2192 provider \u2192 callback). Discord uses a bot invite URL plus . Configure , , , and on the relay (see ADR-0016). Warning: empty allowlist = any sender can run commands. Telegram connector Paste bot token from @BotFather. Link / restart Telegram \u2014 token required on first link; if already linked, empty token field reuses saved token. Users DM the bot before allowlisting to learn their numeric id. WhatsApp connector State UI ------------------ -------------------------------------------------------------------------------------------------------------- Not linked Link WhatsApp \u2014 shows QR, auto-polls until connected; Refresh / Restart pairing while QR is active After scan, WhatsApp closes with 515 \u2014 QR hidden, \u201CFinishing link\u2026\u201D (10s); normal Runtime reconnect after a drop \u2014 QR hidden Linked Green \u201Cconnected\u201D, optional Add my number to allowlist, Unlink WhatsApp Logged out / error Reconnect (unlink + new QR) Advanced: import from local via (see CLI below). Signal connector Link Signal \u2014 , poll , scan QR (device-link URI). Requires on the relay host. CLI: . Slack / Discord / Twitch (link auth) Channel Dashboard Relay env ------- ------------------------------------------------ ----------------------------------------------------------------------- Slack Connect with Slack (OAuth v2) , , Discord Add Discord \u2192 confirm I've added the bot , Twitch Connect with Twitch (OAuth2) , Manual token fields remain under Advanced in the dashboard. Success redirects to . Attached CLI snippet Shows , , optional , and . ---"},{title:"CLI reference (`omnish platform \u2026`)",body:"All commands require + (config or env). Run for the latest help text. Setup and diagnostics Command Purpose ------------------------------------------------------------------ --------------------------------------------------------- Save credentials to Same (config CLI aliases) Account, connectors, allowlist counts, online devices URL, token source, effective platform block Test WebSocket paths before Attach device (attached mode when platform creds resolve) Allowlists (platform policy) Command Purpose --------------------------------------------------- ------------------------------------- Show WhatsApp and Telegram allowlists Merge entries into allowlists Replace one or both lists Examples: Wildcard is rejected on the platform (same security model as standalone). Devices (multi-host routing) Command Purpose ------------------------------------------------ ------------------------------------------ List device id, label, online, default Rename label used by chat routing WhatsApp on platform Command Purpose --------------------------------------------------------------- ---------------------------------------------------------------------------------------- Start pairing, print ASCII QR, poll until linked (includes after scan) Remove session and auth files on platform Upload local Baileys auth from on this host Stop local before import to avoid WhatsApp session conflicts. Environment variables Canonical Also accepted Purpose --------------------- ---------------------------------------------- ------------------------- , Relay base URL , Account bearer token in config Pin device slot on attach Precedence: environment \u2192 \u2192 (if used). Config keys: ( ), ( ), ( ). ---"},{title:"HTTP API reference",body:"Base URL: relay origin (e.g. ). Auth: for most routes. Catalog browse/download routes are public (no bearer required); publish requires a bearer token. Chat commands for the catalog: Online catalog. Auth (no bearer on signup/login body) Method Path Body Response ------ --------------------------- --------------------------------------- ---------------------------------------------- POST or POST (403 if email unverified) POST POST GET \u2014 Redirect to POST POST Signup channels: requires email only (verification before token). / allow email or phone; phone signup returns a token immediately; email-only signup requires verification. SMTP env: , , , , (optional ). Account Method Path Body Response ------ ----------------------- ------------------------------------ --------------------------------------------------------------------------------------------------------------------- GET \u2014 , , , , , , , , , \u2026 PUT PUT GET \u2014 shape (example): means a bot token is stored; the token is never returned. Devices and routing Method Path Body Response ------ ------------------ ----------------------- ---------------------------------------------------- GET \u2014 POST (201) PUT \u2014 rename device PUT DELETE Telegram connector Method Path Body Response ------ ------------------------- ----------------------------------- -------------------------- PUT required on first link; omit on later calls to reuse stored token. optional; can also use . WhatsApp connector Method Path Body Response ------ -------------------------------- ---------------------------------------------------- --------------------------------------------------------------------------- POST (optional legacy ) GET \u2014 POST GET \u2014 Legacy; prefer POST Status values: , , , , , , , . After a successful QR scan, Baileys typically closes with (515) and reconnects using saved creds (no second QR). The connector reports (no ) until the new socket opens as . Signal connector Method Path Body Response ------ ------------------------------ ---- ------------------------------------------------------------------ POST GET \u2014 POST Link authentication (Slack, Discord, Twitch) Method Path Auth Response ------ ---------------------------------------- ------ --------------------------------------------------------------------------------- GET Bearer \u2014 , , GET public Redirect to dashboard ( or ) POST Bearer Catalog (community templates) Public read (no bearer). Publish requires account bearer token. Method Path Auth Query / body Response ------ -------------------------------- ------------ ----------------------------------------------------------------- ------------------------------------------------------------------------------------------------- GET optional GET optional GET optional \u2014 Full entry including POST required POST optional \u2014 Full entry; increments values: , , , . Upsert on publish is per . Max payload 32 KiB. Recipe payloads must include quoted (or custom ) in the command. WebSocket (attached CLI) Path Purpose --------------------- ------------------------------------------------------- Primary attach path Fallback when not proxied to control port Register frame: Inbound: Outbound: Reverse proxy (required paths \u2192 control port 8788) , , , , If these hit the HTTP edge (8787) instead, attach fails with WebSocket 400. Run . ---"},{title:"Telegram `/id` command",body:"Works on platform-linked and standalone bots, before the user is on the allowlist. Mode Reply hints ---------- ------------------------------------------------------------ Platform Numeric id + + dashboard / Standalone Numeric id + See Telegram integration notes. ---"},{title:"Policy in attached mode",body:"When attaches successfully: loads , , . These override local for inbound messenger policy. Host-only settings (shell, jobs, tunnels, webhooks) still come from local config. Policy refreshes every 5 minutes while attached. If fails at startup, the CLI may fall back to local allowlists until the next successful sync. You do not need on the device for platform-routed chat when platform policy loads successfully. ---"},{title:"Troubleshooting index",body:"Symptom See -------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- WebSocket 400 on attach Platform attached mode \u2014 Troubleshooting, relay README Device online, no command output Platform allowlist; relay logs; Troubleshooting \u2014 Attached Dashboard fields empty after login Requires MongoDB; hard-refresh; re-save allowlists Telegram token \u201Cgone\u201D after reload By design \u2014 token not shown; use empty field + Link to reuse Phone stuck on \u201CLogging in\u201D after scan Wait 15s \u2014 platform should show then connected; see Troubleshooting \u2014 Platform WhatsApp WhatsApp stuck / logged out Dashboard Reconnect or then link-whatsapp with correct allowlist WhatsApp LID resolution \u2014 update relay + CLI Empty allowlist surprises Empty = allow all senders ---"},{title:"See also",body:"Platform attached mode \u2014 guided setup Quick start \u2014 Path C Configuration \u2014 Platform / attached mode Docker gateway golden path Tunnel setup from zero \u2014 same account token for tunnels Communication layer \u2014 API sketch (future notes; implemented API is in this doc)"}],keywords:["platform","reference","complete","consolidated","documentation","for","the","omnish","hosted","tunnel","relay","dashboard","attached","guided","walkthrough","start","with","mode","deployment","see","contrib/tunnel-relay/readme","md","one-minute","checklist","how","it","works","what","is","persisted","/dashboard/","cli","http","api","telegram","/id","command","policy","in","troubleshooting","index","also"],relatedCommands:["/tunnel-relay","/readme","/contrib","/architecture","/communication-layer-model","/gateway-config-precedence","/telegram-integration-notes","/dashboard","/id","/slack bots","/v1","/me"]},{id:"docs-guides-quick-start",path:"docs/guides/quick-start.md",title:"Quick Start Guide - omnish",summary:"",sections:[{title:"What you\u2019ll get",body:"By the end of this page you will have: A working gateway \u2014 listening for your DMs. A first win \u2014 a successful sync command (e.g. or ) with output back in chat. A path to deep control \u2014 optional: for a PTY smoke test (see the main README)."},{title:"What is omnish?",body:"A secure CLI tool that bridges messaging platforms (WhatsApp, Telegram) to your system shell. It allows allowlisted users to: Execute shell commands directly from chat Run background jobs with streaming output Start interactive terminal sessions Transfer files between your system and chats Key differentiator: No AI layer - direct, deterministic shell access with explicit security controls."},{title:"One phone with WhatsApp",body:"You do not need a second phone. Host \u2014 omnish runs on a laptop, desktop, home server, or VPS (Linux, macOS, or Windows). It is a CLI gateway, not a phone app. Phone \u2014 you use the same WhatsApp account: scan the QR once under Linked devices, then send command DMs from that app (for example Message yourself). Linking works like WhatsApp Web: your account is the phone plus linked devices. Session files live under (see Comprehensive documentation). The phone stays the primary device; omnish on the host is an extra linked device. Keep the phone online as WhatsApp expects for multi-device; Meta\u2019s caps on linked devices still apply. Follow Path A: WhatsApp below for install, , , and . Private chats only \u2014 groups are ignored. No computer handy? You still need a machine that stays on to run ; a small VPS is typical. If you prefer not to use WhatsApp linked devices, you can use Telegram instead ( ); see Telegram integration notes. Security Treat allowlisted numbers (and Telegram ids) like remote shell passwords \u2014 only add identities you fully trust. See the main README and Security model."},{title:"Installation",body:"From npm (recommended) Requires Node.js 22.13+ ( ). Older Node fails at install or when native modules load. If install fails with a Node version error, use the guided installer (installs nvm/fnm when needed, sets default Node 22, then runs ): Or upgrade manually (nvm example): npm 11+: Advisory warnings during install are expected and non-blocking. omnish declares trusted native deps in its package manifest. macOS: If shell commands fail with , update omnish (recent versions fix permissions automatically). Manual fix: . See Troubleshooting \u2014 macOS spawn-helper. From source ```bash git clone https://github.com/eligapris/omnish.git cd omnish pnpm install"}],keywords:["quick","start","guide","omnish","what","you","ll","get","is","one","phone","with","whatsapp","installation"],relatedCommands:["/help","/wa help","/tg help","/omnish","/media","/logo-horizontal","/apps start","/readme","/auth","/comprehensive-documentation","/telegram-integration-notes","/architecture","/security","/fnm when","/raw"]},{id:"docs-guides-system-agents-and-run",path:"docs/guides/system-agents-and-run.md",title:"System agents, multi-agent, and `/run`",summary:"omnish is built for agents on your machine \u2014 not another hosted model or subscription. You run on hardware you control; from chat you drive system agents: CLIs and TUIs such as , , your own scripts, or multi-step orchestrators. Omnish forwards deterministic shell and PTY to those tools; it does not replace them.",sections:[{title:"Curated mobile notifications",body:"When running coding agents from mobile, keep sessions detached/muted and send only curated updates. Use the status format: Recommended update types: Ack (task accepted) Progress (phase milestone) Question (human input needed) Done (result summary) Blocked (failure or blocker) The repository includes a helper script: It targets , then , and falls back to . For a persistent chat agent (plain DMs queued to one long-lived process), enable and set to a wrapper that calls on milestones. The daemon receives the same notify env as ( , , ). See Chat agent daemon and ."},{title:"Default macro command (`recipesMacroDefaultCommand`)",body:"In , is the shell command used when a macro-style recipe stores a long body as a . The default targets the Claude CLI agent: Point it at any agent or orchestrator that reads the task from the environment, for example a wrapper that fans out to multiple local agents: Restart the gateway after editing (or use when supported)."},{title:"Per-chat recipes with `/run add`",body:"The stored command must reference (or your custom ). Example: Cursor / other CLI agents Example: Claude (default-class agent CLI) Patterns are the same for any binary on the gateway host: omnish only spawns the process you configure."},{title:"Optional: local model CLIs",body:"If your agent stack sometimes calls a local inference CLI (e.g. Ollama), you can still register it in a recipe \u2014 same pattern. That is optional plumbing, not the core story: Prefer scripts on disk for awkward HTTP/JSON so you are not pasting payloads into chat."},{title:"Limits and safety",body:"\u2014 see configuration. \u2014 gated built-ins; leave off unless you understand the risk. Secrets: use env vars on the gateway host or your secret store; do not paste keys into chat."},{title:"Discoverability",body:", Cowork: \u2014 scheduled and on-demand tasks, notifications, logs (name unchanged)."},{title:"See also",body:"User guide \u2014 shortcuts vs Configuration Interactive sessions Background jobs vs Cowork"}],keywords:["system","agents","multi-agent","and","/run","omnish","is","built","for","on","your","machine","not","another","hosted","model","or","subscription","you","run","hardware","control","from","chat","drive","clis","tuis","such","as","own","scripts","multi-step","orchestrators","forwards","deterministic","shell","pty","to","those","tools","it","does","replace","them","curated","mobile","notifications","default","macro","command","recipesmacrodefaultcommand","per-chat","recipes","with","add","optional","local","limits","safety","discoverability","see","also"],relatedCommands:["/run help","/apps help","/run","/apps start","/apps attach","/jobs","/cowork","/cw","/features","/sessions","/apps","/run queue","/run-queue"]},{id:"docs-guides-tunnel-setup-from-zero",path:"docs/guides/tunnel-setup-from-zero.md",title:"Tunnel setup from zero (gateway host)",summary:"This guide is for someone who wants public URLs for apps running on the machine where executes, using WhatsApp or Telegram (optional commands) or the CLI.",sections:[{title:"What runs where",body:"Piece Where Role ------------------ ------------------------------------------------------ --------------------------------------------------------------- Relay VPS (e.g. ) or your own Docker host Accepts HTTPS/WSS, forwards to connected clients omnish gateway Your PC / server \u2014 runs shell, chat commands, and tunnel client Your app Same host as the gateway (usually) , etc. The default public relay is . You can self-host from and point clients at your origin."},{title:"Checklist",body:"Install omnish on the gateway host Native modules may require a normal install (not ). See the project README. Link WhatsApp and/or Telegram, allow yourself ```bash omnish link omnish allow +YOURE164"}],keywords:["tunnel","setup","from","zero","gateway","host","this","guide","is","for","someone","who","wants","public","urls","apps","running","on","the","machine","where","executes","using","whatsapp","or","telegram","optional","commands","cli","what","runs","checklist"],relatedCommands:["/tunnel help","/tunnel login","/tunnel","/features","/tunneling","/contrib","/tunnel-relay","/docs","/testing-and-operations","/docker-gateway-golden-path","/wss","/or telegram","/auth","/signup"]},{id:"docs-guides-ui",path:"docs/guides/ui.md",title:"Browser setup UI (`omnish ui`)",summary:"Local-first configuration panel that edits the same as the CLI and chat commands. Use it when you want to finish basics from a phone on your LAN before touching WhatsApp/Telegram.",sections:[{title:"Quick start",body:"Default bind is (reachable on your LAN). The CLI prints: A setup token (saved under your data dir as ; legacy installs may have had , which is migrated automatically) URLs on localhost and discovered IPv4 LAN addresses A quick link that includes so your phone can authenticate in one tap Then open the UI in a browser, unlock with the token, edit core settings, and Save."},{title:"Run the gateway from the UI",body:"After configuration (and WhatsApp pairing if you use it), you can start the chat gateway without going back to the terminal: Under Host snapshot, use Start gateway. This is equivalent to (or ): a detached process runs with the same data directory, and stdout/stderr append to the default log file ( \u2014 the panel shows the resolved path). Stop gateway sends SIGTERM to the background gateway tracked by , same idea as (including stale pidfile cleanup when the process is already gone). Starting the gateway does not stop the setup UI. You can Stop setup server and leave the gateway running in the background. Authenticated session cookies are required (same as the rest of the panel): and . Port reclaim and stopping the UI The host writes in your data dir with the last process PID and bind port. When you start again on the same (default 3789), the new process terminates the previous one if it is still running, so the port is usually free without manual . A different does not kill another UI instance on a different port (the state file is overwritten when the new server starts). In the browser, Stop setup server (Core settings) ends the HTTP process after your session authenticates \u2014 the page will disconnect; run on the machine again to continue. Same trust as the rest of the panel: only someone with the setup token can unlock a session that can call ."},{title:"WhatsApp pairing (QR in the browser)",body:"You can link this host to WhatsApp without a terminal QR: Stop the gateway on this machine if it is running: use Stop gateway in the UI, , or stop your service. The UI checks for a live process and refuses browser pairing while it is present \u2014 two Baileys clients must not use the same directory at once. Unlock the UI with your setup token. Under Host snapshot, open WhatsApp pairing (QR) and choose Start QR pairing. On your phone: WhatsApp \u2192 Settings \u2192 Linked devices \u2192 Link a device, then scan the QR shown in the browser. If this host is already linked and you need a fresh login, enable Replace session (clears saved WhatsApp auth on disk, same idea as ) and confirm. You can still use from a shell on the host if you prefer the terminal QR. Pairing events are delivered over an authenticated same-origin event stream ( ) after you call . Traffic remains plain HTTP on the LAN \u2014 same trust model as the rest of the UI."},{title:"Security model (read this)",body:"Same trust as editing config on disk. Anyone who can change settings here could affect gateway behavior after reload/restart. LAN exposure: binding to all interfaces means anyone on your Wi\u2011Fi/Ethernet who can reach the port must not guess the token. Treat the token like a password. Not HTTPS: traffic is plain HTTP on your network segment v1. Run behind a reverse proxy with TLS only if you extend exposure beyond the LAN. Reduce exposure \u2014 loopback only (no phone from LAN). Firewall \u2014 block TCP 3789 (or your ) from the WAN side of your router. Guest Wi\u2011Fi \u2014 avoid running on networks shared with untrusted devices."},{title:"Flags",body:"Flag Meaning ---------------------------------- ----------------------------------------------------------- / Bind address (default ). / TCP port (default ). / Set or rotate the setup token (stored in )."},{title:"Environment",body:"Variable Meaning ------------------------- ------------------------------------------------------------------------------------------- Absolute path to a directory containing (advanced override for custom builds). Legacy alias for ."},{title:"Build note for contributors",body:"The UI is built into during / . If static files are missing, run the repo build from the project root so Vite runs before the esbuild CLI bundle."}],keywords:["browser","setup","ui","omnish","local-first","configuration","panel","that","edits","the","same","as","cli","and","chat","commands","use","it","when","you","want","to","finish","basics","from","phone","on","your","lan","before","touching","whatsapp/telegram","quick","start","run","gateway","whatsapp","pairing","qr","in","security","model","read","this","flags","environment","build","note","for","contributors"],relatedCommands:["/help","/configuration","/config set","/telegram","/stderr append","/logs","/gateway","/api","/start","/stop","/shutdown","/wa","/link"]},{id:"docs-guides-user-guide",path:"docs/guides/user-guide.md",title:"User Guide - omnish",summary:"Complete manual for using omnish's features.",sections:[{title:"Table of Contents",body:"Introduction Basic Shell Commands Background Jobs Interactive Sessions Terminal interactive ( ) File Transfer System Commands User Shortcuts User shortcuts vs recipes Cowork (scheduled tasks) Working with Multiple Platforms Configuration Management Chat LLM fallback (optional) Best Practices"},{title:"Introduction",body:"omnish bridges your messaging chats to your system shell with these main execution surfaces: Sync Shell: Immediate command execution (prefix: ) Background Jobs: Asynchronous commands with streaming output ( ) Cowork: Saved commands on a schedule or on demand ( ) while the gateway runs \u2014 see Cowork (scheduled tasks) Interactive Sessions: Terminal sessions via PTY ( ) Terminal REPL ( ): The same slash/ interface in your local shell\u2014see Interactive terminal (not gated by the inbox allowlist; same trust as your login session). Each chat maintains its own working directory and state, making it perfect for team workflows and personal use. The CLI REPL uses a dedicated session key and starts in your current directory when you launch ."},{title:"Basic Shell Commands",body:"Synchronous Execution Commands prefixed with execute immediately in the shell: Working Directories Each chat maintains its own working directory: Use to change directories Changes persist across commands View current directory with Command Prefix The prefix can be customized in : Without prefix, use free shell mode (see below)."},{title:"Background Jobs",body:"Start long-running commands without blocking your chat. Full detail: Background jobs. Starting Jobs Job Management Job IDs are 8-character hex strings; you can also assign a name with (or ). / / accept either form. Use (or ) to get a chat message when the job exits. Job Output and limits Logs persist under with matching . Jobs use the chat session cwd; they inherit the gateway process environment (not a prior from another shell). applies to sync commands only, not to children."},{title:"Interactive Sessions",body:'Start full terminal sessions in your chat: Starting Sessions Session Management Session Features Maximum 5 sessions per chat (configurable) One session "attached" at a time Plain text goes to attached session Output debounced and chunked ANSI codes can be preserved Session Commands Session Lifecycle'},{title:"Terminal interactive (`omnish i`)",body:"From a terminal on the same machine, run (or ) to type the same commands you would send in WhatsApp or Telegram: , , , , , etc. Trust: The inbox allowlist does not apply\u2014only local OS users who can run processes as you can start . Outbound files: Use or while is active; see Interactive terminal and Files send/receive. Media: , , \u2014 download, transcribe, and edit media (yt-dlp + ffmpeg + optional Whisper) \u2014 see media commands. Jobs: in this REPL belongs only to this process, not to the gateway\u2019s job list. Full reference: Interactive terminal."},{title:"File Transfer",body:'Sending Files Receiving Files When someone sends media files: Files are automatically downloaded Saved to organized directory structure Reply with "Saved: /path/to/file" File Location Control'},{title:"System Commands",body:"Gateway Control Notes: Aliases: and work like . is accepted as an alias for . prints command help. Service management from chat and are gated by (same trust as shell). Allowlist Management Cluster (optional, chat-driven) When is true on multiple machines linked to the same WhatsApp number, only the active host replies. Coordination flows through the chat itself \u2014 no shared file, no Syncthing. Shorthand: \u2026 and the long form \u2026 (only the exact token matches; does not). See Cluster and chat configuration. Config from chat View or change many keys without SSH (same trust as shell): Allowlists stay / , not . Full list: Cluster and chat configuration. Other Commands"},{title:"User Shortcuts",body:"Create chat-specific command aliases: Creating Shortcuts Using Shortcuts Parameterized shortcuts ( ) Put in the saved command where runtime text should go. Everything after the shortcut name on the same line is substituted in (trimmed; not shell-parsed). Shortcuts without still work as bare or only (extra words on the line are ignored). Shortcuts with require input; bare or returns a usage hint. Input length follows the same cap as tasks when is set. Managing Shortcuts Scope model: / : private to this chat. / : shared on this gateway across chats. Resolution order for or : chat shortcut first, then shared."},{title:"User shortcuts vs `/run` recipes",body:"Both features are per-chat (stored on the gateway host), but they solve different problems: Shortcuts ( , , ) recipes ( , ) --------------- ---------------------------------------------------------------------------------------------------------------------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- Purpose One-line alias: expand to a saved message once (e.g. ). Optional substitutes chat text inline. Parameterized runner: inject your task text into a CLI via (and optional ), then start a detached app session (PTY) by default \u2014 use or when you want plain DMs to go to the agent. still attaches on start. Typical use Jump to a directory, repeat a favorite sync command, short macros, custom wrappers with varying text. Drive system agents (e.g. , , your orchestrators) from chat with a task each time. Invocation or ; with in the body: or . \u2014 task is required for named recipes. Shortcuts substitute in the expanded line only (no shell env). Recipes require the stored command to reference (or a custom ) and run in a PTY session. For system agents, multi-agent PTY, and , see System agents and . Queued recipe runs (same chat, one PTY at a time): or , then for status. You can also batch-enqueue from JSON with (file path, inline JSON, or an uploaded file with that caption); see Run queue for the exact JSON shape and limits. Multi-step runbooks: create a recipe with sequential steps using : Steps are separated by or newlines. Each step runs in order; if any step fails (non-zero exit), remaining steps are skipped. Prefix a step with to continue on failure: When you invoke a runbook ( ), it runs in the background and sends a formatted per-step summary when complete. Use to see the step listing. Useful management forms:"},{title:"Cowork (scheduled tasks)",body:"Cowork saves shell commands and runs them on a schedule or on demand while is active. Commands use the same shell, timeout, and byte limits as sync commands. Alias: . Default log directory: (override with ). Missed scheduled times catch up when the gateway returns (one oldest slot per tick). Disabled tasks reject until re-enabled. Conditional notifications: only sends a notification when the task fails. tracks ok/fail transitions and only alerts on flips (useful for frequent health checks). Full reference: Cowork feature doc (notifications, data files, cluster and WhatsApp caveats)."},{title:"Working with Multiple Platforms",body:"Multi-Platform Setup Configure for both platforms: Platform Differences Feature WhatsApp Telegram -------------- --------------- ----------------------------- Message Limit 3500 chars 4096 chars Format Plain text HTML support File Types All media types Photo, document, video, audio Authentication QR code Bot token Best Practices Use different users for different platforms Keep allowlists minimal Monitor usage across platforms Use platform-specific features appropriately"},{title:"Chat LLM fallback (optional)",body:"If is true in on the gateway host, plain messages that would otherwise show \u201CNo command matched\u201D are answered asynchronously by a subprocess you configure ( ). Replies go back to the same WhatsApp/Telegram chat (or to the terminal in ). Where to configure: the same file as the rest of omnish \u2014 default , or if set. You edit JSON on the machine where runs; API keys must be available in that process\u2019s environment if your wrapper needs them (restart the gateway after changing systemd/shell env). Full step-by-step instructions, stdin vs PTY, and environment forwarding: Chat LLM fallback."},{title:"Configuration Management",body:"Configuration File Location Default: Override with environment variable Legacy: Uses if doesn't exist Editing from chat Most tunables support (whitelist). See Cluster and chat configuration and Configuration guide. Key Configuration Options Environment Variables : Override data directory : Enable debug logging (legacy: ) : Override bot token"},{title:"Best Practices",body:`Security Minimal Allowlists: Only add trusted users No Wildcards: Explicit phone numbers only Monitor Usage: Regularly check allowlists Session Isolation: Each chat is isolated Performance Output Chunking: Large outputs automatically split Session Limits: Don't exceed max sessions Timeout Settings: Adjust for your use case Log Rotation: Monitor log file sizes Usage Patterns Use Sessions for Interactive Apps: Vim, Python REPL, etc. Use Jobs for Long Operations: Builds, data processing Use Shortcuts for Common Tasks: Reduce typing Organize with Working Directories: Keep related work together Troubleshooting "Not in allowlist": Check E.164 format (+15551234567) "Maximum sessions reached": Stop unused sessions Messages cut off: Check Connection issues: Use for debug logs`},{title:"Next Steps",body:"Configuration: Customize for your workflow Security: Learn about the security model Features: Explore advanced features Integration: Set up for your team"}],keywords:["user","guide","omnish","complete","manual","for","using","features","table","of","contents","introduction","basic","shell","commands","background","jobs","interactive","sessions","terminal","file","transfer","system","shortcuts","vs","/run","recipes","cowork","scheduled","tasks","working","with","multiple","platforms","chat","llm","fallback","optional","configuration","management","best","practices","next","steps"],relatedCommands:["/help","/docs help","/run","/bg","/cowork","/apps","/tmp","/path","/var","/log","/system","/features","/background-jobs","/bg npm"]}]}});function Wl(e,t){return`${e.replace(/\/$/,"")}/${t}`}var ao,Bl=w(()=>{"use strict";Vk();ao=Qk});function AO(e){return e.toLowerCase().replace(/[^a-z0-9\s/-]/g," ").split(/\s+/).filter(t=>t.length>1)}function $O(e,t,n){if(t.length===0)return null;let o=e.title.toLowerCase(),r=e.summary.toLowerCase(),s=new Set(e.keywords),i=0,a,l=n.toLowerCase().trim();l.length>=3&&o.includes(l)&&(i+=40);for(let c of t){o.includes(c)&&(i+=12),r.includes(c)&&(i+=4),s.has(c)&&(i+=6),e.path.toLowerCase().includes(c)&&(i+=3);for(let d of e.sections){let u=d.title.toLowerCase(),p=d.body.toLowerCase();u.includes(c)&&(i+=5,a??=d.title),p.includes(c)&&(i+=2,a??=d.title)}}return i<=0?null:{entry:e,score:i,matchedSection:a}}function Ul(e,t=12){let n=e.trim();if(!n)return[];let o=AO(n),r=[];for(let s of ao.entries){let i=$O(s,o,n);i&&r.push(i)}return r.sort((s,i)=>i.score-s.score||s.entry.title.localeCompare(i.entry.title)),r.slice(0,t)}function jl(e){let{entry:t,matchedSection:n}=e,o=n?`${t.summary.slice(0,120)} (${n})`:t.summary.slice(0,160);return{id:t.id,path:t.path,title:t.title,summary:o,relatedCommands:t.relatedCommands}}function xi(e){return ao.entries.find(t=>t.id===e)}function Hl(e){let t=e.replace(/\\/g,"/").replace(/^\.\//,"");return ao.entries.find(n=>n.path===t||n.id===t)}function Gl(e,t=1800){let n=[];e.summary&&n.push(e.summary);for(let r of e.sections){if(n.join(`
234
+ [...truncated]`}function EO(e){if(e===void 0||e===0)return null;let t=RO.constants.signals;for(let[n,o]of Object.entries(t))if(o===e)return n;return null}function zk(e){try{process.platform==="win32"?e.kill():e.kill("SIGTERM")}catch{e.kill()}}function yt(e,t,n){return new Promise(o=>{let r=Date.now(),s=!1,i="",a=n.cwd,l=null,c=!1,d=null,u=null,p,f=y=>{if(c)return;c=!0,p!==void 0&&clearTimeout(p),d?.dispose(),d=null,o(y);let k=u;u=null,queueMicrotask(()=>k?.dispose())},h={...n.env??process.env,TERM:"xterm-256color",COLORTERM:"truecolor",...a?{PWD:a}:{}};try{Cl(),l=Yk.spawn(e,["-c",t],{name:"xterm-256color",cols:120,rows:40,cwd:a,env:h})}catch(y){f({code:null,stdout:"",stderr:String(y),durationMs:Date.now()-r,timedOut:!1,signal:null});return}p=setTimeout(()=>{s=!0,l&&zk(l)},n.timeoutMs),d=l.onData(y=>{i+=y,i.length>n.maxBytes&&(i=PO(i,n.maxBytes),l&&zk(l))}),u=l.onExit(y=>{f({code:y.exitCode,stdout:i,stderr:"",durationMs:Date.now()-r,timedOut:s,signal:EO(y.signal)})})})}var io=w(()=>{"use strict";xp()});var Qk,Vk=w(()=>{Qk={version:1,generatedAt:"2026-07-11T08:20:24.991Z",repoUrl:"https://github.com/eligapris/omnish/blob/main",entries:[{id:"docs-advanced-implementation",path:"docs/advanced/implementation.md",title:"Implementation Guide - omnish",summary:"For contributors and developers who want to understand and extend omnish.",sections:[{title:"Development Setup",body:"Prerequisites Node.js >= 20 pnpm package manager Native build tools (make, g++, etc.) Getting Started ```bash git clone https://github.com/eligapris/omnish.git cd omnish pnpm install"}],keywords:["implementation","guide","omnish","for","contributors","and","developers","who","want","to","understand","extend","development","setup"],relatedCommands:["/omnish","/github","/eligapris","/wa","/inbound","/tg","/gateway","/signal","/outbound","/index","/config","/stats"]},{id:"docs-advanced-troubleshooting",path:"docs/advanced/troubleshooting.md",title:"Troubleshooting Guide - omnish",summary:"Common issues and solutions for omnish.",sections:[{title:"Quick Reference",body:`Symptom Common Cause Solution ---------------------------------- ----------------------------------------- -------------------------------------------------------------------------- fails Node.js too old (< 22.13) ; use install script or nvm/fnm Native module failed to load Node upgraded/downgraded after install Node 22.13+; or Connection failed Network issues Check connectivity "Not in allowlist" Wrong format Use E.164 format Sessions won't start Resource limits Check session limits Messages cut off Size limits Adjust config Telegram not working Bot token Verify token Attached: probe fails / WS 400 Proxy routes to wrong port Route , to control port 8788; Attached: online but no commands Platform allowlist / routing Dashboard allowFrom; relay logs Message yourself: complete silence Older builds dropped all traffic Update omnish + relay; send after upgrade`},{title:"Attached / platform mode",body:"Symptom: fails or errors with WebSocket Solutions: Reverse proxy must forward , , , , to relay control port (8788), not the HTTP edge (8787). See tunnel relay README. Run before . Confirm URL and token: . Symptom: Device shows online on dashboard but WhatsApp commands do nothing Solutions: Set allowFrom on the platform dashboard (attached mode uses platform policy, not local ). Check relay logs for with your device id. Only one should hold the device WebSocket per token. WhatsApp LID chats: ensure relay and CLI are up to date; use on the device and look for vs . with correct in logs but lists that number: the platform stores allowlist phones as digits only (CLI shows for display). Older CLI builds compared to raw digits and always denied \u2014 update omnish so normalizes platform entries. Symptom: Attached Telegram/WhatsApp \u2014 replies but , , or plain text get no reply (device verbose log shows ) Cause: With , only fleet commands ( , , ) and traffic from senders bound to this machine are processed. is answered on the platform relay and never hits the device. Current builds auto-bind allowlisted senders on the platform default device (and on any device that receives platform-routed inbound) when is true (default). Solutions: Upgrade omnish \u2014 attached default-device traffic should work without . Non-default device: send once; that device auto-binds on first inbound. Standalone multi-host WhatsApp: send or (auto-bind does not run when remote peers are known). Or set / for single-host installs. Disable auto-bind: . Symptom: Relay logs show / every 1\u20132 minutes while stays up Solutions: Run only one per platform token (a second process replaces the WebSocket session). Reverse proxy: route and to relay control port (8788) and set / idle timeout to at least 3600s for WebSocket paths (Caddy handles this by default; nginx needs explicit config). Device and relay exchange WebSocket ping frames every 25s to keep connections alive through strict proxies. Replies sent during a brief disconnect are queued on the device (max 50) and flushed on reconnect \u2014 if churn is heavy, upgrade relay + CLI and stabilize the proxy. Symptom: Message yourself (or self-chat) \u2014 commands get no reply at all (not even \u201CNot allowlisted\u201D) Cause: WhatsApp marks messages you send from your phone as on linked devices. Older omnish builds ignored all upserts, so the documented Message yourself flow never reached the gateway. Solutions: Upgrade both the CLI ( ) and the platform relay ( ) to a version that tracks outbound message ids and routes allowlisted non-echo traffic. Confirm your number is on the platform allowlist (dashboard \u2192 Save allowlists). Run , then ; send or in Message yourself. Relay logs should show after you send a command. If you see nothing, WhatsApp may still be disconnected on the platform \u2014 check dashboard WhatsApp status. Standalone mode: same behavior applies; use and on the linked host. Symptom: warning at attach Solutions: Fix token, URL, or platform database (MongoDB on self-hosted relay). Until works, attached mode falls back to local allowlists. Full setup: Platform attached mode. Complete API/CLI/dashboard: Platform reference. WhatsApp link / unlink on platform Symptom Fix ------------------------------- ------------------------------------------------------------------------------------------------- QR never appears Dashboard Link WhatsApp or ; check relay logs Stuck after logout Reconnect on dashboard or then link again Import fails Stop local ; use Linked but dashboard shows idle with autostart; ensure volume persists Allowlists and persistence Symptom Fix -------------------------------------- ------------------------------------------------------------------------------------- Dashboard does not remember allowlists Relay needs ; click Save allowlists CLI changes not reflected on device Wait up to 5 min or restart (policy refresh) Telegram token missing in UI Expected \u2014 token is not returned; leave field empty and Link to reuse saved token"},{title:"Installation Issues",body:"Node.js version too old Symptom: exits during with a message that Node 22.13+ is required, or postinstall reports native modules ( , , ) failed to load. Cause: omnish requires Node.js 22.13+ ( ). Dependencies such as need Node 22. Native addons are compiled for the Node ABI you install with \u2014 switching Node versions later without reinstalling breaks the load step. Fix (guided): The script tries nvm or fnm, sets Node 22 as default when possible, then runs . Fix (nvm manual): Fix (already on Node 22 but natives broken): ```bash npm rebuild -g omnish"}],keywords:["troubleshooting","guide","omnish","common","issues","and","solutions","for","quick","reference","attached","platform","mode","installation"],relatedCommands:["/help","/wa help","/tg help","/contrib","/install-omnish","/fnm","/downgraded after","/platform","/v1","/control","/dashboard","/auth","/tunnel-relay","/readme","/whatsapp"]},{id:"docs-architecture-communication-layer-model",path:"docs/architecture/communication-layer-model.md",title:"Communication layer and omnish CLI \u2014 unified model",summary:"Single source of truth for how omnish works today and how the optional hosted communication layer fits. Implementation sketches: Communication layer \u2014 API & ops, Gateway config precedence.",sections:[{title:"Summary",body:"CLI installs anywhere ( ) and works without the hosted layer (standalone mode). Optional hosted communication layer exposes WhatsApp, Telegram, Discord, Slack, Matrix, and 17 more surfaces through an API. Users link messengers once on the platform. Many devices (Docker, VM, VPS, bare metal) run the CLI with a device token and talk to that layer over a long-lived channel. One messenger connection \u2192 many devices: the platform routes chat to the right attached CLI; replies return through the same layer. That single connection is what makes the platform useful for fleets and containers. Shell execution stays on each device. The communication layer is not a remote shell; the CLI is the agent (shell, PTY, files, tunnels) on the box."},{title:"Two modes",body:"Mode When Where messengers connect CLI role -------------- ------------------------------------------------------------ ------------------------------------- ----------------------------------------------------------------------------------------- Standalone No platform URL + token (default) On the same host as Full gateway: , , Attached + (or config / aliases) On the hosted communication layer Local executor + WebSocket client: register device, receive routed messages, send replies Both modes: allowlist and shell authority on the device. The platform may store policy in a dashboard, but the CLI enforces who may trigger commands before running shell on that host. Standalone (today) Documented in the README and Quick start. Baileys / Telegram bot clients run inside the gateway process; credentials live under . Attached (implemented) User links WhatsApp/Telegram on the platform dashboard (or ) \u2014 once per account. User sets platform URL + account token on each machine ( or env). On the target: , then . CLI opens WebSocket (fallback ), handles inbound messages like the standalone router, executes locally, posts replies. Setup: Platform attached mode. Full API/CLI/dashboard: Platform reference. No per-container WhatsApp QR is required for the default path."},{title:"Docker example (attached mode)",body:"A team runs an app in Docker and wants chat-driven ops inside that container without running Baileys in the image: Messengers stay on the communication layer; the container only needs outbound HTTPS to the layer API. See Docker gateway golden path."},{title:"Environment contract",body:"Purpose Canonical Also accepted --------------------------------- --------------------- ---------------------------------------------- Platform base URL , Account token (tunnel + attached) , Optional device id in config Config file keys: ( ), ( ), ( ). Precedence: env \u2192 \u2192 tunnel auth file. Implementation: ."},{title:"Security boundaries",body:"Concern Standalone Attached -------------------------------- ----------------------------- --------------------------------------------------------------- Who runs shell Local gateway Local CLI on device Messenger secrets on gateway host Communication layer (connectors) Stolen device token N/A Risk to that attach point; revoke in dashboard Platform sees message content N/A (direct to gateway) Yes, for routing \u2014 document retention and policy Remote shell from platform alone No No \u2014 requires valid device token + allowlisted sender on device Connector secrets at rest N/A AES-256-GCM when set (see ADR-0014) Never echo full tokens in chat replies (same as tunnel tokens today)."},{title:"Channel capability matrix",body:"Peer prefixes and surface ids match between standalone ( ) and platform ( ). Platform = linkable on tunnel-relay dashboard/API. Channel Standalone Platform Inbound media Outbound media Host notes -------------- ---------- --------------------------- ------------- ----------------- --------------------------------- whatsapp Yes Yes (dedicated QR routes) Yes Yes Baileys auth on relay disk telegram Yes Yes (dedicated token route) Yes Yes Bot token in ConnectorSession discord Yes Yes Partial Yes (text + file) Bot token slack Yes Yes No Text Socket Mode app + bot tokens matrix Yes Yes No Text Homeserver + access token irc Yes Yes No Text IRC host/nick on relay signal Yes Yes (dedicated QR routes) No Text Requires on relay googlechat Yes Yes (webhook) No Text Hook URL registered on link mattermost Yes Yes (webhook) No Text baseUrl + token feishu Yes Yes (webhook) No Text appId + appSecret line Yes Yes (webhook) No Text channelToken msteams Yes Yes (webhook) No Text Outbound webhook URL nextcloud-talk Yes Yes (webhook) No Text baseUrl + token synology-chat Yes Yes (webhook) No Text outgoingUrl nostr Yes Yes No Text NIP-04 DMs zalo Yes Yes (webhook) No Text OA token zalouser Yes Yes No Text Session polling (best-effort) qqbot Yes Yes (webhook) No Text appId + bot token tlon Yes Yes No Text Urbit ship URL polling twitch Yes Yes No Text OAuth + channel name imessage Yes No No Text macOS standalone only bluebubbles Yes No No Text macOS BlueBubbles standalone only Enforced in CI: ."},{title:"Multi-device routing (attached mode)",body:"When several machines run on one platform account, inbound chat is routed on the relay before it reaches a CLI: \u2014 one-shot route to a device by dashboard label (e.g. ) \u2014 route by device id prefix (8+ characters) Unprefixed \u2014 default (primary) device only; fails with online hints if primary is offline \u2014 list registered devices and online status (platform replies directly) \u2014 rename device label for routing (dashboard and CLI also supported) Set labels via or dashboard Devices. Rename via dashboard, chat, or . Set primary via dashboard Set default. See ADR-0013."},{title:"Relation to this repository",body:"Component Status --------------------------------------------------------------- -------------------------------------------------------------- Standalone gateway ( , ) Implemented in Attached mode client ( , platform WebSocket) Implemented in , Hosted communication layer (relay + dashboard) Implemented in (deploy separately)"},{title:"See also",body:"Platform attached mode \u2014 connect, link, configure, run Platform reference \u2014 complete API, CLI, dashboard, persistence Vision \u2014 hosted communication layer \u2014 short product summary Platform layer spec \u2014 API sketch, tokens, threat model Gateway config precedence \u2014 config merge in attached mode Relay operator README \u2014 deploy, paths, dashboard docs/ideas/ \u2014 historical voice-note inputs"}],keywords:["communication","layer","and","omnish","cli","unified","model","single","source","of","truth","for","how","works","today","the","optional","hosted","fits","implementation","sketches","api","ops","gateway","config","precedence","summary","two","modes","docker","example","attached","mode","environment","contract","security","boundaries","channel","capability","matrix","multi-device","routing","relation","to","this","repository","see","also"],relatedCommands:["/readme","/guides","/quick-start","/telegram on","/platform","/device","/control","/platform-attached-mode","/cli","/dashboard","/platform-reference","/tunnel"]},{id:"docs-architecture-gateway-config-precedence",path:"docs/architecture/gateway-config-precedence.md",title:"Gateway configuration precedence",summary:"How effective configuration is computed. See Communication layer model for standalone vs attached modes.",sections:[{title:"Sources (ordered low \u2192 high priority)",body:"Standalone ( without platform token) Compiled defaults \u2014 values baked into the binary when a key is unset. Local \u2014 under . Environment variables \u2014 e.g. , . Chat \u2014 allowlisted senders; same trust as shell. Higher layers win per key. Attached ( with platform URL + token) Compiled defaults Local \u2014 host paths, shell, tunnel, jobs, etc. Platform account ( ) \u2014 wins for policy keys: , , (derived from linked connectors on the platform). Not written back to disk by default. Environment variables \u2014 still override local file for host keys; do not replace platform allowlists unless you also change them on the dashboard. Chat \u2014 updates local file only in attached mode; platform allowlists remain authoritative for inbound from messengers on the layer. If fails at connect, the CLI logs a warning and uses local for allowlists (or partial data from the WebSocket ack: + connectors only)."},{title:"Keys that stay host-only (never taken from platform)",body:", job limits, sync paths, webhook ports/tokens on the device Baileys auth paths, local tunnel client settings WhatsApp session blobs and Telegram bot tokens in attached mode (connectors run on the platform) , , \u2014 cluster host policy; attached default device uses platform allowlists only as input when seeding auto-bind"},{title:"Keys owned by the platform in attached mode",body:", \u2014 set on the dashboard; device enforces the platform copy on inbound WebSocket messages. \u2014 derived from which connectors are linked on the platform ( , , or )."},{title:"Merge algorithm (attached inbound)",body:"```text effective = loadConfig() # local host + defaults if platform snapshot loaded: effective.allowFrom = platform.allowFrom effective.telegramAllowFrom = platform.telegramAllowFrom effective.gatewayMode = platform.gatewayMode"}],keywords:["gateway","configuration","precedence","how","effective","is","computed","see","communication","layer","model","for","standalone","vs","attached","modes","sources","ordered","low","high","priority","keys","that","stay","host-only","never","taken","from","platform","owned","by","the","in","mode","merge","algorithm","inbound"],relatedCommands:["/v1","/me","/config","/guides","/platform-reference","/config set","/tokens on","/platform","/account-sync","/default-device"]},{id:"docs-architecture-overview",path:"docs/architecture/overview.md",title:"Architecture Overview - omnish",summary:"",sections:[{title:"Executive Summary",body:"omnish is a secure, deterministic messaging-to-shell gateway that bridges messaging platforms (WhatsApp, Telegram) to system shell access. The architecture follows a thin transport adapter pattern with a unified core, explicit security controls, and comprehensive session management. Local control plane: When the gateway process ( ) is up, it may expose a localhost-only control endpoint (metadata in ) so a separate process can request outbound file sends that reuse the same Baileys/grammY sessions\u2014avoiding a second login. This is optional machinery for CLI ergonomics; inbound chat traffic still flows through the transports above."},{title:"Key Architectural Principles",body:"Thin Transport Adapter Pattern Transport Layer: Platform-specific implementations (WhatsApp, Telegram) Adapter Layer: Normalizes all inputs to format Core Layer: Unified business logic and message routing Persistence Layer: Configuration and state management Deterministic Behavior No AI or agent layer Rule-based message routing Predictable command precedence Explicit state management Security-First Design Explicit allowlists required No anonymous access Per-peer session isolation Minimal attack surface"},{title:"System Architecture",body:"High-Level Diagram Component Responsibilities Layer Component Responsibility --------------- --------------- -------------------------------------------- Transport WhatsApp WebSocket lifecycle, QR auth, media handling Telegram Long polling, bot API, formatting Adapter Inbound Normalize messages to Outbound Platform-specific message sending Core Gateway Transport multiplexing, lifecycle management Router Command dispatch with precedence rules Session Manager Per-peer working directories and state Apps Manager Interactive PTY session lifecycle Job Manager Background process execution Persistence Config Configuration loading and validation Sessions JSON-based state persistence Files Log files, session output, job output"},{title:"Data Flow Architecture",body:"Message Processing Pipeline Command Precedence Flow Messages are processed in strict precedence order: Free Shell Toggle: / Sync Commands: (e.g., ) System Commands: (e.g., , ) App Shorthand: (e.g., ) Free Shell Mode: Plain text execution Attached App: Forward to focused PTY session Help Fallback: Display help message"},{title:"Security Architecture",body:"Allowlist System Peer Key Model WhatsApp: or normalized phone number Telegram: Hashed for storage: SHA-1 prefix for log directories Session isolation: Each peer has separate state Security Boundaries Transport Boundary: Platform-specific authentication Authorization Boundary: Allowlist validation Execution Boundary: Command and process isolation Data Boundary: Per-peer state separation"},{title:"Session Management Architecture",body:"Session Context Persistence Session Lifecycle Load: From JSON storage or create default Update: Persist changes immediately Isolate: Per-peer separation with migration support Cleanup: Remove on session destruction"},{title:"Transport Architecture",body:"Transport Interface All transports implement the same core interface: Transport-Specific Features Transport Authentication Message Limit Features --------- -------------- ------------- -------------------------------- WhatsApp QR code 3500 chars Media download, auto-reconnect Telegram Bot token 4096 chars HTML formatting, webhook support"},{title:"Output Processing Architecture",body:"Output Flow Output Processing Pipeline Buffer: Collect output chunks Debounce: Wait for output completion ( ) Chunk: Split into transport-sized pieces Format: Strip ANSI, add prefixes, etc. Send: Deliver via appropriate transport"},{title:"State Management Architecture",body:"State Types Configuration: Global settings Session Context: Per-peer state Job State: Background job metadata App State: Interactive session state Persistence Strategy State Type Format Location Access Pattern ---------- ------ ------------------------- --------------------------------- Config JSON Load at startup, update on change Sessions JSON Load on demand, append updates Jobs Files Streaming write, read on demand Apps Files Streaming write, read on demand"},{title:"Error Handling Architecture",body:"Error Categories Transport Errors: Connection issues, timeouts Validation Errors: Invalid input, malformed commands Execution Errors: Command failures, process timeouts Resource Errors: Memory limits, session limits Error Handling Flow Capture: Error detected at source Log: Structured logging with context Notify: User-friendly error message Recover: Graceful degradation where possible"},{title:"Extension Points",body:"Transport Extension Command Extension Configuration Extension"},{title:"Performance Considerations",body:"Memory Management Session Limits: Prevent unbounded growth Output Buffering: Size-limited buffers Cleanup: Proper resource disposal Network Optimization Message Chunking: Minimize API calls Connection Reuse: Persistent connections Backoff Strategy: Exponential backoff for failures I/O Optimization Async Operations: Non-blocking file I/O Lazy Loading: Load data on demand Output Streaming: Real-time output delivery This architecture overview provides the foundation for understanding omnish's design principles and implementation patterns."}],keywords:["architecture","overview","omnish","executive","summary","key","architectural","principles","system","data","flow","security","session","management","transport","output","processing","state","error","handling","extension","points","performance","considerations"],relatedCommands:["/grammy sessions","/inbound","/gateway","/outbound","/jobs","/apps","/command","/bg","/job","/pty","/config","/sessions"]},{id:"docs-architecture-platform-layer-spec",path:"docs/architecture/platform-layer-spec.md",title:"Communication layer \u2014 API and operations sketch",summary:"Implementation detail for the unified model in Communication layer model. The attached CLI client is implemented in and . The hosted relay (connectors, dashboard, routing) lives in .",sections:[{title:"Goals",body:"Messenger connectors on the layer: WhatsApp, Telegram, others \u2014 linked once per workspace from a dashboard. Device registry and routing: many CLIs attach with ; inbound chat is routed to the correct device; replies return through the layer. Token issuance and lifecycle from the dashboard (create, rotate, revoke). Minimal env on each device: + (legacy aliases: , , ). Non-goals: Running user shell or PTY in the cloud. Replacing local enforcement of allowlists before command execution on each device. Requiring the hosted layer for open-source standalone use ( on the host without env)."},{title:"Operating modes (messenger termination)",body:"Mode Messenger clients Device (CLI) -------------- ------------------------------------------------------------- -------------------------------------------- Standalone On gateway host ( , Baileys / Telegram bot today) Same process as messengers Attached On communication layer (connectors) CLI only: API/WebSocket client + local shell See Communication layer model \u2014 Two modes."},{title:"Threat model (summary)",body:"Asset Risk Mitigation ----------------------------- ----------------------------------- -------------------------------------------------------------------------------------------------- Device token Theft \u2192 control of one attach point Short TTL, rotation, revoke; scoped to one device slot Workspace / dashboard session Account takeover Strong auth, audit log on connector and device changes Message content on layer Privacy / retention Policy, encryption in transit, minimal logging; no training on content by default (product policy) Signed config blob Forged defaults on device Signing keys, , host-only keys; see Gateway config precedence Chat as secret channel Token pasted in DM Never echo full secrets in replies Trust boundary: The communication layer routes messages and holds connector credentials in attached mode. Each CLI enforces allowlists and runs shell locally. Layer compromise must not run shell on a device without a valid device token and allowlisted sender on that device."},{title:"Token scopes (recommended)",body:"Scope Purpose Typical lifetime ------------------- ------------------------------------------------------ --------------------- Long-lived channel for one CLI attach point Rotatable; per device One-time or short exchange when creating a device slot Minutes Fetch non-secret config subset for a device Hours\u2013days Relay bearer (may be issued by layer) Per relay policy Human dashboard (not for CLI) Session Rules: narrow scopes, stable token ids for revocation, no user-facing \u201Cgod\u201D token."},{title:"Implemented API (this repository)",body:"The relay implements a single-account model (not multi-workspace). Authoritative route list, request bodies, and CLI commands: Platform reference Summary: Area Implemented paths -------- ------------------------------------------------------------------ Auth , Account , , Devices , Telegram WhatsApp , , , Attach , UI"},{title:"Future API sketch (not implemented)",body:"The following were early design notes; do not assume they exist on the relay today: Multi-workspace ( ) Separate device tokens per slot (today: one account bearer token) , scoped bootstrap tokens SSE alternative to WebSocket"},{title:"Compliance and logging",body:"Attached mode: layer may process message content for delivery; document retention and access in privacy policy. Avoid logging message bodies in application logs by default."},{title:"Relation to this repository",body:"Communication layer service: (deploy separately; MongoDB + dashboard). CLI attached mode: , \u2014 WebSocket client + local shell. Standalone: / on the same host (unchanged when platform env is unset). Operator guides: Platform attached mode, Platform reference."}],keywords:["communication","layer","api","and","operations","sketch","implementation","detail","for","the","unified","model","in","attached","cli","client","is","implemented","hosted","relay","connectors","dashboard","routing","lives","goals","operating","modes","messenger","termination","threat","summary","token","scopes","recommended","this","repository","future","not","compliance","logging","relation","to"],relatedCommands:["/platform","/gateway","/attached","/tunnel-relay","/contrib","/guides","/platform-reference","/platform-attached-mode","/websocket client","/auth","/signup","/login"]},{id:"docs-architecture-routing",path:"docs/architecture/routing.md",title:"Message Routing Architecture - omnish",summary:"",sections:[{title:"Introduction",body:"The message routing system is the heart of omnish, responsible for determining how incoming messages are processed and executed. It follows a deterministic precedence model with clear rules for each message type."},{title:"Routing Overview",body:"Message Flow Core Components Allowlist Validator: Ensures user has permission Message Normalizer: Converts transport-specific formats Session Loader: Retrieves per-peer state Router: Applies precedence rules and dispatches Executors: Command-specific handlers"},{title:"Message Precedence Rules",body:"Precedence Hierarchy Messages are evaluated in strict order from highest to lowest precedence: Free Shell Mode Toggle ( / ) Synchronous Commands ( ) System Commands ( ) App Session Shorthand ( ) Attached App Sessions (when focused and running) Lone-URL auto-download ( or LLM off \u2192 background ) Free Shell Mode (when enabled) Chat LLM fallback (optional plain-text handler when configured) Help Fallback ( ) Rule Implementation"},{title:"Command Types and Handling",body:"Free Shell Mode Toggle Purpose: Enable/disable direct shell execution Pattern: or Synchronous Commands Purpose: Execute shell commands immediately Pattern: System Commands Purpose: Control omnish functionality Pattern: App Session Shorthand Purpose: Quick interaction with app sessions Pattern: Free Shell Mode Purpose: Execute any text as shell command Pattern: Plain text (when enabled) Attached App Sessions Purpose: Send input to focused PTY session Pattern: Plain text (when session focused)"},{title:"Special Cases and Edge Handling",body:"Shortcut Expansion After stripping the sync prefix ( by default) or when an unknown slash is not a built-in command, omnish calls : only \u2014 expand the stored body (legacy bare shortcuts). \u2014 if the body contains , substitute trimmed input for every placeholder; if the placeholder is present but input is missing, reply with a usage error. Bodies without ignore extra words after the name. Expansion re-enters once with (no nested shortcut expansion). Command Precedence Override Certain commands always take precedence: Error Handling"},{title:"Session Integration",body:"Session Context The router uses session context for state: Session Management"},{title:"Transport Integration",body:"Peer Key Normalization Transport-Specific Handling"},{title:"Performance Considerations",body:"Optimization Strategies Session Caching: Cache frequently accessed sessions Command Caching: Cache command results for identical inputs Output Debouncing: Buffer output to reduce message spam Lazy Loading: Load sessions only when needed Concurrency Handling"},{title:"Extension Points",body:"Adding New Command Types Custom Command Handlers"},{title:"Fleet and config slash commands",body:"The authoritative dispatch order for messages that start with is implemented in (not the pseudocode snippets earlier in this doc). Highlights: runs before fleet aliases so paths like never collide with . Fleet commands accept , , or ( ): only a standalone token or + rest matches \u2014 paths such as do not match the shortcut. (also , ) sets ; reapplies config while is active. See Cluster and chat configuration for behavior and Configuration guide for fields."},{title:"Testing and Validation",body:"Unit Tests Integration Tests This message routing architecture ensures predictable, efficient processing of all incoming messages while maintaining security and session isolation."}],keywords:["message","routing","architecture","omnish","introduction","overview","precedence","rules","command","types","and","handling","special","cases","edge","session","integration","transport","performance","considerations","extension","points","fleet","config","slash","commands","testing","validation"],relatedCommands:["/command","/dl","/dl job","/disable direct","/name","/new","/path","/router","/config","/config set","/computers","/pcs"]},{id:"docs-architecture-security",path:"docs/architecture/security.md",title:"Security model",summary:"omnish bridges WhatsApp and/or Telegram direct messages to shell commands on your machine. Security is explicit allowlists plus local filesystem and process boundaries. There is no cloud relay for commands: whoever can send messages as an allowed identity can execute code as the OS user running .",sections:[{title:"Trust boundaries",body:"Allowlisted identities are credentials. If an attacker can spoof or compromise an allowed WhatsApp number or Telegram user id, they get the same power as you gave on that host. Wildcards are rejected. must never contain . Secrets on disk: WhatsApp session material lives under the data directory ( / / legacy ). Telegram bot tokens live in or . Restrict permissions so other Unix users cannot read them. Chat config: lets allowlisted users edit from chat (same trust as running shell commands). Do not treat DMs as \u201Clow privilege.\u201D Multiple Telegram bots: If and several hosts run Telegram, use a different bot token per host; one token cannot be long-polled twice (see finding in ). Interactive terminal ( ): Same trust as running a shell on that machine. It is not gated by WhatsApp/Telegram inbound allowlists\u2014those lists still apply to messages arriving through the gateways. Gateway control ( ): Written only while is active; carries localhost connection info and a token so can request outbound media through the existing gateway. Anyone who can talk to 127.0.0.1 as your user could misuse it\u2014treat host-local access like filesystem access. : Sends files to arbitrary WhatsApp numbers or Telegram chats through your linked session, comparable to sending those files manually from the linked WhatsApp device or bot\u2014powerful and intentional. Tunneling ( , when ): Publishes public URLs to local HTTP/TCP ports. Anyone with the URL can reach the forwarded service; chat tunneling is gated by the same allowlists as shell commands."},{title:"Automated posture checks",body:"The same rules run in three places: Surface How ------- ------------------------------------------------------------------------------------------------------------------------- CLI (plain text) or Chat , , Startup refuses to start if any error-severity finding is present; warnings are printed but do not block Automated checks cover configuration and local Unix permissions. They do not replace firewall rules, SSH hardening, malware scanning, or review of who physically accesses the machine. Severity levels error \u2014 Blocks until fixed (or until you change / token so the check no longer applies). warn \u2014 Shown on startup and in reports; you should understand and usually fix. info \u2014 Informational (for example, env var overrides)."},{title:"Finding codes and remediation",body:"Code Severity Meaning What to do ---------------------------- -------- --------------------------------------------------------------------------------- --------------------------------------------------------------- error contains Remove from in . error Telegram transport enabled but no token Set in config or . error Configured binary does not exist Install the shell or point at a valid absolute path. error is but path empty Set or change mode. error Fixed receive path not absolute Use an absolute . warn WhatsApp enabled, empty warn Telegram enabled, empty warn is true Set to unless you trust every recipe. warn is not an absolute path Use e.g. in config. warn Could not stat Fix permissions/path. warn group/world readable or writable on config file. warn Process UID is root Run as a normal user when possible. warn Data directory group/world accessible on data dir ( or default ). warn Jobs directory group/world accessible (only checked if data dir is already tight) on the jobs directory under the data dir. warn WhatsApp auth dir readable by others on under the data dir. info set; overrides config Unset env if you want config file to win. info Cluster + Telegram: same token must not be used on two running gateways Use one bot per host or run Telegram on fewer machines. Unix permission checks are skipped on Windows."},{title:"JSON output for automation",body:"prints: Exit code if any error-severity finding exists (same as plain text)."},{title:"Related commands",body:"\u2014 Includes a one-line security summary. In chat: for a short hardening checklist. See also the user guide Security section in User Guide."}],keywords:["security","model","omnish","bridges","whatsapp","and/or","telegram","direct","messages","to","shell","commands","on","your","machine","is","explicit","allowlists","plus","local","filesystem","and","process","boundaries","there","no","cloud","relay","for","whoever","can","send","as","an","allowed","identity","execute","code","the","os","user","running","trust","automated","posture","checks","finding","codes","remediation","json","output","automation","related"],relatedCommands:["/security help","/security summary","/or telegram","/config set","/telegram","/sendto","/tunnel","/tcp ports","/security","/security tips","/run","/bin","/bash"]},{id:"docs-features-agents-from-chat",path:"docs/features/agents-from-chat.md",title:"Agent integrations from chat",summary:"Install Cursor and Claude Code notify hooks and skills from allowlisted chat or the host CLI \u2014 without downloading from omnish.dev.",sections:[{title:"Chat commands",body:"Command Gate Purpose ------------------------------------- ---------------------- -------------------------------------------- \u2014 Command list \u2014 Health check on gateway host \u2014 Paste bundled offline \u2014 / / guide Run bundled install on gateway host Remove omnish IDE entries Enable mutating installs: Same trust model as and . reports when enabled."},{title:"Host CLI (always available)",body:"Bundles ship inside the npm package under and (copied from at build time)."},{title:"What gets installed",body:"Cursor See contrib/omnish-cursor/README.md \u2014 hooks in , skill in . Claude Code See contrib/omnish-claude/README.md \u2014 Stop hook in , skill in . Terminal agents (/run, /apps) No IDE install required. Set and use env injected into long-running jobs: \u2014 replying peer \u2014 template for mobile briefs on the gateway host"},{title:"Prerequisites",body:"(or ) on the gateway host Allowlist or platform token configured For hooks: python3 and omnish on PATH when install runs"},{title:"Related",body:"Cursor integration (contrib) Service from chat Media commands"}],keywords:["agent","integrations","from","chat","install","cursor","and","claude","code","notify","hooks","skills","allowlisted","or","the","host","cli","without","downloading","omnish","dev","commands","always","available","what","gets","installed","prerequisites","related"],relatedCommands:["/run","/apps","/agents notify","/agents help","/agents doctor","/agents script","/agents install","/agents uninstall","/config set","/service install","/dl install","/omnish-cursor"]},{id:"docs-features-background-jobs",path:"docs/features/background-jobs.md",title:"Background jobs \u2014 omnish",summary:"Background jobs run a single shell command asynchronously in the current chat\u2019s working directory while you keep using the chat. Output is appended to a log file under your data directory; you can pull the last N lines or only new bytes since your last for that job in this chat.",sections:[{title:"Commands (implemented)",body:"Command Meaning ----------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------- Start a background job; reply includes an 8-char job id and hints for and . \xB7 \xB7 Same as , but stores a name you can use instead of the hex id with , , and . \xB7 Start a job that sends a completion notification to your chat when it exits (includes exit code, duration, and command summary). Combine flags: named job with completion notification. List recent jobs (up to 20), newest first: id (and name if set), status ( \\ \\ ), exit code, duration, command preview. Last lines of the log (default from ; max 500 if you pass a number: or ). Incremental: new log bytes since your last of this job (resolved id) in this chat (per-chat cursor). Send SIGTERM to the running process (or best-effort to the recorded pid if the child already detached). Job ids are 8-character hex (e.g. ). Names are optional labels (letters, digits, , , , up to 64 characters). If several jobs share the same name, / / resolve to the newest (most recently started) matching job. Resolving -shaped tokens: if a job with that id exists on disk, the token is treated as an id; otherwise it is treated as a name (so a name that looks like 8 hex digits still works when no job file uses that id). There is no , , or in chat \u2014 use and / instead. ( exists for gateway shutdown; it is not exposed as a slash command.)"},{title:"Behavior details",body:"Working directory: Same as session cwd ( applies before ). Environment: Inherited from the omnish gateway process (the Node process running ), not from a prior in chat \u2014 each and runs a new shell. Set env vars in the shell that starts the gateway, or put them in the job command line. Timeouts: applies to synchronous commands, not to children. Long-running jobs are not auto-killed by that setting. Logs: and (see )."},{title:"Configuration",body:""},{title:"Job lifecycle (actual statuses)",body:"From : running \u2014 child spawned; log growing. done \u2014 process exited (or spawn error); / recorded in meta. killed \u2014 user ran (SIGTERM); meta updated. Optional field is stored in when you start a job with / ."},{title:"Completion notifications",body:"When you start a job with (or ), the gateway sends a message to your chat as soon as the process exits: The notification includes: Exit code (0 for success, non-zero for failure) or signal name (e.g. SIGTERM) Duration of the run Command that was executed This is useful for long builds, deployments, or test suites where you want to walk away and get notified when it finishes. Combine with for readability: Notifications are sent via the same transport as the chat (WhatsApp or Telegram). If the gateway shuts down before the job finishes, the notification is lost."},{title:"Examples",body:"Same using the printed id:"},{title:"Compared to Cowork",body:"---------------- --------------------------- -------------------------------------- Scheduling One-shot , , , etc. Notifications Opt-in per job ( ) Per task ( + ) Queue / catch-up No Yes (SQLite + pending queue) Typical use Ad hoc long command Recurring or on-demand saved tasks See cowork.md."},{title:"Troubleshooting",body:"No output in chat: Streaming behavior depends on the gateway; use or for the log file. Lost jobs on gateway restart: In-memory handles are cleared; log and meta files on disk may still be present under . Verbose gateway logs: ---"},{title:"Change log \u2014 named background jobs (2026-05-08)",body:"Area Change -------------------------------------------------------- ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ; , , , ; . Parse named ; , , accept id or name via ; lists name when set. help and main help bullet updated for names. Unit tests for parsing, validation, and name/id resolution. User-facing docs This file, user-guide.md, quick-start.md, README.md, comprehensive-documentation.md, practical-guide-for-agents.md, CHANGELOG.md."}],keywords:["background","jobs","omnish","run","single","shell","command","asynchronously","in","the","current","chat","working","directory","while","you","keep","using","output","is","appended","to","log","file","under","your","data","can","pull","last","lines","or","only","new","bytes","since","for","that","job","this","commands","implemented","behavior","details","configuration","lifecycle","actual","statuses","completion","notifications","examples","compared","cowork","troubleshooting","change","named","2026-05-08"],relatedCommands:["/bg","/jobs","/tail","/log","/kill","/log abcdef12","/log mybuild","/stats","/history","/kill all","/shell","/src"]},{id:"docs-features-chat-agent-daemon",path:"docs/features/chat-agent-daemon.md",title:"Chat agent daemon (optional)",summary:"When is true and is set, plain inbound chat (that would otherwise hit \u201CNo command matched\u201D or LLM fallback) is queued to a long-lived process on stdin\u2014one daemon per peer by default. The gateway does not stream the child\u2019s stdout/stderr to mobile; the process should send curated updates with (same model as detached ).",sections:[{title:"Enable",body:"Edit on the gateway host: Copy and customize , or point at your own wrapper. ---"},{title:"Notify environment (parity with `/run`)",body:"On spawn, omnish merges the same notify env as recipe/app starts: Variable Purpose ---------------------------- ----------------------------------------------------------------------------------------------- Initiator peer ( / ) Template: Board-style guidance when is true (daemon sessions are treated as muted) Use or on milestones (ack, progress, question, done, blocked)\u2014not every log line. See also Curated mobile notifications. ---"},{title:"Chat commands",body:"Command Action --------------- -------------------------------------------- List running daemons and queue depth Stop daemon for current peer Stop and clear daemon state for current peer Short help ---"},{title:"Related keys",body:"Key Default Purpose ---------------------- ------- ---------------------------------------------------------- Master switch Shell command for (long-lived; reads stdin) One process per peer; = shared daemon Max queued lines per daemon before reject Include on spawn ---"},{title:"See also",body:"Chat LLM fallback \u2014 one-shot subprocess per message System agents and"}],keywords:["chat","agent","daemon","optional","when","is","true","and","set","plain","inbound","that","would","otherwise","hit","no","command","matched","or","llm","fallback","queued","to","long-lived","process","on","stdin","one","per","peer","by","default","the","gateway","does","not","stream","child","stdout/stderr","mobile","should","send","curated","updates","with","same","model","as","detached","enable","notify","environment","parity","/run","commands","related","keys","see","also"],relatedCommands:["/stderr to","/sendto peer","/run","/config","/absolute","/path","/to","/your-chat-agent-wrapper","/chat-agent-wrapper","/scripts","/app starts","/agent-notify"]},{id:"docs-features-chat-llm-fallback",path:"docs/features/chat-llm-fallback.md",title:"Chat LLM fallback (optional)",summary:"When this feature is on, a plain inbound message that would normally get \u201CNo command matched\u201D is handled silently: omnish runs your in the background (with limits and a restricted environment), then sends the subprocess output back to the same WhatsApp or Telegram chat. In , the reply is printed to the terminal instead.",sections:[{title:"Where to change settings",body:"What Where -------------------------- --------------------------------------------------------------------------------------------------------------------------------------------------------------------- Config file . If is unset, omnish uses (or the legacy tree if does not exist). Template / all keys Repo root \u2014 copy the entries into your real . Confirm data directory Run on the gateway host, or . Edit the file with any text editor on the gateway host (SSH, local console, or your usual config workflow). Valid JSON is required (trailing commas are not allowed). Reload behavior: The gateway calls when handling each inbound message, so changes to fields in normally apply on the next message without restarting . API keys and environment variables: The subprocess receives a filtered copy of the gateway process environment (see below). Keys such as are only present if they were set when the gateway was started. If you add or change API keys in the shell profile or systemd unit, restart so the parent process picks them up. ---"},{title:"How to enable (step by step)",body:"Open (or ) on the machine that runs . Add or merge these fields (defaults shown; enable only when ready): Set to a single shell command passed as the argument to (same pattern as sync commands). Use an absolute path to a wrapper script if the default in the sandbox is too minimal. Smoke test: With the gateway running, send a plain line from an allowlisted chat (not starting with or your , not text). You should get no immediate \u201CNo command matched\u201D reply; after the subprocess finishes, the combined stdout/stderr (trimmed and capped) is sent back. Sandbox / real isolation: omnish runs the command in a dedicated working directory (temp dir under the data directory unless is set) and a reduced env. For Docker, , or other tools, put that logic inside your wrapper and point at it. ---"},{title:"What the subprocess receives",body:"Shell: Config key (e.g. ). Working directory: if non-empty (created if needed); otherwise a new temp directory under the omnish data dir for that run (removed afterward). Stdin (default): The inbound chat text (truncated to ). If is true, stdin is not piped the same way; use (and a PTY) instead. Environment (high level): \u2014 e.g. or \u2014 same payload as stdin (piped mode) Common vars: , , , , , , , , All vars from the parent process Parent vars whose names end with or Place API keys in the gateway environment (systemd , shell before , etc.) so they are inherited and forwarded by the allowlist above. ---"},{title:"When the fallback runs (and when it does not)",body:"Runs only if all of the following hold: is true and is non-empty after trim. The message is plain text that reached the router\u2019s final \u201Cno match\u201D branch: not a command, not shell, not / , not app line, not consumed by a focused session, and free shell mode is off for that peer. Does not run (examples): unknown , , , media-only messages, messages dropped by cluster binding on non-primary hosts, or any path that already returns a normal reply. ---"},{title:"Related keys (reference)",body:"Key Purpose ------------------------ ------------------------------------------------------------------------------------------------ Master switch ( by default). Full command string for . Wall-clock limit (ms). Max size of text passed in (stdin / ). Max captured output (stdout+stderr in piped mode). Use PTY execution (for CLIs that require a TTY); prefer in the script. Fixed cwd; empty = ephemeral dir per run. Implementation checklist and code pointers: chat-llm-fallback-implementation-plan.md. ---"},{title:"Security note",body:"Allowlisted chats already have shell-level trust on the gateway host. Turning this on means accidental plain messages can invoke your command and any API usage inside it. Keep allowlists small, use timeouts and caps, and wrap external agents in a policy you control."}],keywords:["chat","llm","fallback","optional","when","this","feature","is","on","plain","inbound","message","that","would","normally","get","no","command","matched","handled","silently","omnish","runs","your","in","the","background","with","limits","and","restricted","environment","then","sends","subprocess","output","back","to","same","whatsapp","or","telegram","reply","printed","terminal","instead","where","change","settings","how","enable","step","by","what","receives","it","does","not","related","keys","reference","security","note"],relatedCommands:["/config help","/config","/absolute","/path","/to","/your-wrapper","/stderr","/bin","/bash","/apps","/foo","/help","/chat-llm-fallback-implementation-plan"]},{id:"docs-features-cluster-and-chat-config",path:"docs/features/cluster-and-chat-config.md",title:"Multi-host cluster and chat configuration",summary:"This document describes the chat-driven cluster ( , , ) and for viewing or editing from WhatsApp or Telegram.",sections:[{title:"Cluster (per-sender bindings)",body:`Use one WhatsApp phone number with multiple linked devices \u2014 each machine runs once. Every machine that runs will receive the same DMs through WhatsApp multi-device. There is no shared file. There is no Syncthing/NFS to set up. Coordination flows entirely through the WhatsApp chat itself: every reply omnish sends carries an invisible footer that other linked omnish hosts read to learn who is online and which machine each sender is currently bound to. Telegram cannot carry this coordination (different bot tokens cannot see each other), so the per-sender binding still applies on Telegram, but you only get convergence between linked hosts on WhatsApp. How it routes Each allowlisted sender (one phone number / one Telegram id) picks one machine to talk to. Only that machine processes the sender's normal traffic; other linked hosts stay silent for that sender. Two different controllers can independently bind to two different machines without affecting each other. A's only runs on . B's only runs on . Neither sees the other's output. Fleet commands ( , , ) are processed by every linked host so that bindings, status, and help stay reachable even before a sender has bound. Stable identity Each data directory has a file ( ) with a UUID. Display names in the roster come from , or the OS hostname when empty. When you , "alpha" matches against the (or the 8-character node id). Local state Each host keeps a private file at (schema v3): \u2014 short id, label, role, and last-seen timestamp for every host this machine has observed in chat traffic. \u2014 map of sender key ( or ) to the bound machine's short node id, when it was set, and whether it came from a chat command ( ), config defaults ( ), or automatic attached/sole-host bind ( ). This file is per host and never shared. Disagreements between linked hosts are resolved by the next footer-stamped message anyone sends to the chat. Attached mode (platform) auto-bind Platform routing ( or unprefixed \u2192 default device) picks one device WebSocket before cluster gating runs on that host. When is true (default): Default device \u2014 on attach, allowlisted senders from platform policy are pre-bound to the local node id (no for unprefixed commands). Any attached device \u2014 first inbound routed to that device auto-binds the sender to that host's node id (covers without manual setup). Bindings use the 8-char node id, not or platform , so renames do not break routing. remains for standalone multi-host WhatsApp (all linked hosts see the same DM). is platform device routing; is cluster host focus \u2014 use both when you run a fleet. Commands (WhatsApp / Telegram) may be shortened to or (the bare token matches; does not). Command Action ---------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------ Overview of the per-sender model Bind YOUR messages to that machine. Resolves either an 8-character node id or a . Other senders are not affected. Bind YOUR messages to the local machine (shorthand for ). Show YOUR current binding (machine, source: chat or config). Clear YOUR chat binding. The config default in (if any) takes over again. Every online host replies with its own one-paragraph status (this is the discovery moment \u2014 each host parses the others' footers and updates its peer list). Locally known roster \u2014 only one host responds (the bound one, or a deterministic fallback). Convergence When you send , every linked host runs locally. Only the resolved target host (the one labelled ) sends the confirmation reply; siblings stay silent. The reply carries the cluster footer with , and every sibling observes it via WhatsApp upserts and converges on the same binding for your sender key. Defaults via config You can pre-seed bindings so a controller never has to type on first contact: Keys are sender keys ( or ). Values are the target machine's or its 8-character node id. Chat-set bindings ( ) always win over confi`},{title:"Chat configuration (`/config`)",body:"Allowlisted chat users can change many settings without SSH. This is as sensitive as shell access: anyone who can DM as an allowed identity can modify config. Commands Command Action --------------------------- -------------------------------------------------------------- or Help (overview + hint) Snapshot of effective config; telegram bot token masked One whitelisted key Update one key; values may be quoted for paths with spaces List all keys allowed for After changes Save goes to immediately (same normalization as the rest of the app). For or , the gateway runs -style Telegram restart when is up; otherwise send after editing. Changing returns an explicit warning in the reply. Whitelist ( ) The canonical allowlist is in . In chat, prints the same comma-separated list your build supports. Groups include: gateway ( , \u2026); cluster; shell / sync / jobs; apps; files; recipes (including , ); Telegram ( ); service ( ); update checks ( , , , ); tunneling ( , , ); chat LLM fallback ( , , , , , , ). accepts a JSON object value (or / to wipe), e.g. . Not exposed via : , \u2014 use and in chat (or / on the host). Tunnel token is not in : run on the gateway host (or set ). Chat can turn tunneling on with after the token exists. Examples ---"},{title:"Related documentation",body:"Configuration guide \u2014 full file layout and fields Security model \u2014 trust boundaries and Message routing \u2014 slash-command ordering (high level)"}],keywords:["multi-host","cluster","and","chat","configuration","this","document","describes","the","chat-driven","for","viewing","or","editing","from","whatsapp","telegram","per-sender","bindings","/config","related","documentation"],relatedCommands:["/c help","/config help","/computers","/pcs","/config","/nfs","/node-id","/c use","/cluster-local","/sole-host bind","/c here","/cluster","/c using"]},{id:"docs-features-cowork",path:"docs/features/cowork.md",title:"Cowork \u2014 scheduled and on-demand shell tasks",summary:"Cowork runs saved shell commands on a timer while is active. It uses the same execution model as sync commands ( via your configured ), with and from . There is no AI layer.",sections:[{title:"Prerequisites",body:"A running gateway: (foreground or background). You must be allowlisted (same trust as ). With cluster enabled, only the host bound to your sender processes chat commands; tasks you create are stored on that machine\u2019s data directory."},{title:"Commands",body:"Use or . Send for the short form. Action Example -------------------------------- ---------------------------------------------------------------------------- Add task List Show Run now (on-demand or scheduled) Enable / disable / Remove (also , ) Check in (heartbeat only) Update \u2014 notify condition (see below) \u2014 also send the run log file with each notify \u2014 optional artifact paths (see below) Add syntax Scheduled / on-demand tasks (run a command): Heartbeat tasks (dead-man's-switch, no command): Name: letters, digits, , ; max 32 characters (stored lowercased). Schedule: (or ), , , , (weekday names or \u2013 , Sunday = ), or . For scheduled/on-demand tasks, the command must come after a literal separator. Heartbeat tasks have no command. Duration format (heartbeat): , , , . Minimum interval: 1m. Minimum grace: 30s. Grace defaults to 50% of interval if omitted. Schedules and time zones Fire times use the gateway host\u2019s local timezone (Node\u2019s on that machine). Output logs Each run writes one UTF-8 log file under the task\u2019s (default ). Filenames include a timestamp, task id, and whether the run was scheduled, catch-up, or on-demand. Catch-up Successful scheduled runs are recorded in (per task id and slot time). The scheduler uses that database as the source of truth for what is already done; may still contain a legacy field, which is seeded into SQLite on first open if the DB has no rows for that task. If several slots were missed (gateway down), the next tick runs the command once for the newest due slot and records coalesced rows for older missed slots so the backlog clears in a single execution instead of one run every 30 seconds per missed slot. A slot is only recorded after exit code 0, no timeout, and no terminating signal; failed runs do not advance the watermark (same retry behavior as before). Kinds stored for successful scheduled work: (within 2 minutes of the slot), (late), (satisfied without a separate run during backlog coalescing), and (imported from legacy on upgrade). On-demand queue appends a request to . The scheduler dequeues up to eight entries per tick, one read/write of the queue file per batch, so a crash mid-run does not drop the rest of the queue. Notifications After each run, a short summary can be sent (same routing as other replies: WhatsApp vs Telegram formatting). For scheduled and catch-up runs, the message is one line: The bracketed status appears only when the run did not finish cleanly (timeout, non-zero exit, or signal). For on-demand runs ( ), the message is two lines: Optional lines may follow when attachments fail or when too many artifact files match (see below). Mode Recipients ---------------- ----------------------------------------- (default) Task owner only All entries in (WhatsApp) All entries in Owner plus both allowlists No messages Trust: , , and can message every allowlisted identity. Anyone who can edit tasks (allowlisted senders) can point shell and notifications at powerful actions\u2014same overall model as remote shell. WhatsApp notification targets use normalized JIDs so delivery matches Baileys . Conditional notifications ( ) Control when a notification is sent with : Mode Behavior ------------------ -------------------------------------------------------------------------------------------------------------------------------------------------- (default) Notify after every run Notify only when the command exits with a non-zero code, times out, or is killed by a signal Notify only when the result flips (e.g. ok to fail, or fail to ok). The last-known state is tracked in SQLite so it survives gateway restarts. is useful for tasks that run frequently (e.g. hourly health checks) where you only want to know when something breaks or recovers, not on every successful run. Heartbeat (dead-man's-switch) A heartbeat task does not run a command. Instead, it expects periodic check-ins and alerts when they stop arriving. This creates a task that expects a check-in at least every 1 hour, with a 10-mi"},{title:"Data files",body:"Under (default unless overridden): \u2014 task definitions (atomic replace on save). \u2014 successful scheduled slot completions (watermark for catch-up); WAL mode. \u2014 queued on-demand runs. Directory mode is restrictive ( for where created by the app; task file )."},{title:"Limitations and caveats",body:"Gateway must be up at fire time for scheduled runs; catch-up handles downtime afterward. WhatsApp-only: the cowork scheduler starts before the first successful WhatsApp connection; very early completion notifications to WhatsApp may no-op until outbound is ready. Telegram outbound is typically ready earlier when the bot is enabled. Shared : if several gateways share the same data directory (unusual), each running process could execute the same schedules\u2014use one data dir per machine for normal setups. Cluster: tasks live on disk for the gateway that received commands; they do not sync across hosts."},{title:"See also",body:"Cowork implementation plan (design and maintainer notes) User guide \u2014 Cowork section Security model"}],keywords:["cowork","scheduled","and","on-demand","shell","tasks","runs","saved","commands","on","timer","while","is","active","it","uses","the","same","execution","model","as","sync","via","your","configured","with","from","there","no","ai","layer","prerequisites","data","files","limitations","caveats","see","also"],relatedCommands:["/cowork help","/cw help","/cowork","/cw","/cowork add","/data","/backup","/cowork list","/cowork show","/cowork run","/cowork enable","/cowork disable","/cowork remove"]},{id:"docs-features-device-update-delivery",path:"docs/features/device-update-delivery.md",title:"Update information on installed Omnish devices",summary:"This document is the reference plan for how operators and maintainers can reach already-installed Omnish gateways with version and notice information. It matches what the CLI and gateway implement today.",sections:[{title:"Reality check: there is no silent \u201Cpush\u201D to every machine",body:"Omnish is self-hosted: each install is a process on a user\u2019s machine, talking to WhatsApp/Telegram. There is no central fleet server and no always-on back channel from the project to those hosts unless the host initiates outbound traffic (or an allowlisted user sends a chat command). So 100% delivery in the literal sense (every device, every time, with no user action) is not possible: machines can be offline, firewalled, on air-gapped networks, or running an old binary forever. What is achievable is a reliable, predictable path that works whenever the network and registry are reachable\u2014same practical bar as other CLIs ( , , etc.)."},{title:"Options that were considered",body:"Approach Pros Cons ------------------------------------------------------ ------------------------------------------------------------------------------ ----------------------------------------------------------------------------------------------- npm registry Canonical published version; no custom infra; HTTPS; already used for installs Needs outbound HTTPS; scoped/unpublished forks differ GitHub Releases / API Rich metadata Rate limits; not identical to \u201Cwhat gets\u201D Static JSON on a URL you control ( ) Arbitrary maintainer text (security, migration) You must host and secure expectations (HTTPS only in omnish) In-chat broadcast from \u201Cthe project\u201D Uses existing DM surface There is no project-owned chat to all installs; only allowlisted users can command their host Auto-upgrade in place Hands-off for users High risk (native deps, , Baileys); out of scope for this design doc Chosen combination: npm registry for semver discovery + optional HTTPS JSON for human notices, exposed in the product as , (cached one-liner), , optional background checks when is true, and for the same keys."},{title:"Implemented behavior (source of truth)",body:"(allowlisted chat, gateway running): performs a live GET to (default package name ). If is set to an https URL, fetches JSON (link must also be ). : shows the last in-memory snapshot from the last live or scheduled check (no network). : gateway runs a timer (checks at most every 1 minute internally, but only performs a registry+info fetch when has elapsed, clamped 1h\u20137d). Results are logged with and stored for / . : CLI one-shot check (same fetches as ). : completion text may append \u201CUpdates (last check): \u2026\u201D if a snapshot exists. Configuration keys (also in and ): (boolean, default false) \u2014 privacy-first default. (default 86400000) \u2014 clamped between 1 hour and 7 days. (default ) \u2014 for forks or scoped packages. (default empty) \u2014 optional maintainer notice JSON over HTTPS."},{title:"Maintainer playbook",body:"Publish a new version to npm as today ( bump, publish). Devices that run or have background checks enabled will see the new latest when the registry updates. Optional notice (security advisory, breaking change): host a static JSON file at an HTTPS URL you control; set in docs or tell users to set it via . Do not rely on chat alone to \u201Creach\u201D every install; treat registry + optional URL as the scalable channel."},{title:"Code map",body:"Piece Role ------------------------------------------------------ -------------------------------------------------------------------------------- npm + optional info URL fetch, snapshot, scheduler Numeric compare for \u201Cnewer on npm\u201D Reads running from package root (dev + bundled ) , Schedules checks; ; reload footer status lines, formatted reply Schema defaults and merge"},{title:"Future extensions (not implemented)",body:"Signed notices (e.g. minisign) over . Deprecation warnings via (visible on , not parsed here)."}],keywords:["update","information","on","installed","omnish","devices","this","document","is","the","reference","plan","for","how","operators","and","maintainers","can","reach","already-installed","gateways","with","version","notice","it","matches","what","cli","gateway","implement","today","reality","check","there","no","silent","push","to","every","machine","options","that","were","considered","implemented","behavior","source","of","truth","maintainer","playbook","code","map","future","extensions","not"],relatedCommands:["/updates","/updates cached","/telegram","/latest","/unpublished forks","/gateway","/config set","/registry","/reload","/config keys","/omnish-notice","/check"]},{id:"docs-features-docs-search-from-chat",path:"docs/features/docs-search-from-chat.md",title:"Documentation search from chat",summary:"Find omnish guides by topic from WhatsApp, Telegram, or \u2014without hunting the repo or omnish.dev first. Search is offline (bundled index at build time; no AI layer).",sections:[{title:"Commands",body:"Command Purpose --------------------------- --------------------------------------------------------- or Subcommand list Ranked results (keywords + headings) Repeat the last search list in this chat or Excerpt, GitHub link, and Try: related slash commands Run the primary related help (e.g. ) Legacy aliases: (same as ), \u2192 , . Host terminal (same index): ( still works.) ---"},{title:"Example flow",body:"You are not sure which command exposes HTTP: Pick a result: You get a short excerpt, the doc path, a GitHub link, and lines like . Jump to live help: Same as sending in that chat. ---"},{title:"When nothing matches a slash command",body:"Plain text that looks like a question (contains a space or ) gets a hint on No command matched: Use with your own keywords if the auto-filled phrase is too long. ---"},{title:"Index scope",body:"The build includes guides, features, architecture, and advanced troubleshooting under (not , , or maintainer runbooks). Rebuild the index with: ( and run this automatically.) Overrides for related commands: . ---"},{title:"Related",body:"User guide Message routing Online catalog \u2014 community recipes/apps (separate from docs search)"}],keywords:["documentation","search","from","chat","find","omnish","guides","by","topic","whatsapp","telegram","or","without","hunting","the","repo","dev","first","is","offline","bundled","index","at","build","time","no","ai","layer","commands","example","flow","when","nothing","matches","slash","command","scope","related"],relatedCommands:["/s help","/search help","/s q","/s list","/s show","/s follow","/tunnel help","/search","/docs search","/help search","/features","/tunneling"]},{id:"docs-features-implementation-101",path:"docs/features/implementation-101.md",title:"Tunneling implementation 101",summary:"This document explains how omnish tunneling is built: relay edge, client, CLI, chat integration, configuration, security, and how to exercise it locally.",sections:[{title:"Big picture",body:"Tunneling is a relay + client design. The relay is the public edge; the omnish client on the user machine keeps an outbound WebSocket and forwards traffic to a local port. HTTP uses JSON control messages on the WebSocket. TCP uses JSON for stream open/close and length-prefixed binary frames for payload. Default production relay: . The relay service itself lives under and is deployed separately from the npm CLI package."},{title:"Repository map",body:"Path Role ------------------------------------------------------------------------------ ------------------------------------------------------- Deployable relay: HTTP edge, WSS control, TCP listeners Relay package ( dependency) Shared control message types and binary frame codec Tunnel kinds, records, default relay URL One tunnel: WSS session, local forwarding Active tunnels, limits, stop/stopAll Token and relay URL resolution CLI and chat argument parsing subcommands handlers for the gateway CLI dispatch; gateway shutdown stops tunnels Chat routing for and , , under the data dir Posture findings for tunneling lines when tunneling is enabled Tests: , , , ."},{title:"Relay (`contrib/tunnel-relay/`)",body:"is the tunnel process. Production ships it in one Docker image with Caddy (TLS, wildcard DNS-01) via and . proxies public / to and . Listeners Listener Default Role ----------- --------------- -------------------------------- HTTP edge Public HTTP for tunneled apps Control WSS Authenticated client connections Environment variables: \u2014 base URL shown to users (default ) \u2014 HTTP edge bind port \u2014 control WebSocket bind port / \u2014 TCP tunnel port range \u2014 comma-separated bearer tokens allowed to connect \u2014 per-token tunnel quota Authentication Clients connect with on the WebSocket upgrade. Invalid or missing tokens close the socket. Registration After , the client sends with ( ), , , and optional . The relay assigns a slug (from or random) and replies with including . HTTP routing Local dev: Production-style: when matches that host pattern Fallback: Each slug is bound to the WebSocket session that registered it (not \u201Cany session with the same token\u201D), so multiple tunnels sharing one token each get correct routing. Incoming requests are turned into control messages to the owning client; the client returns . TCP For , the relay binds a port in the configured range and returns . New public TCP connections emit on the control socket; bytes flow over binary frames. State In-memory maps ( , , ). Tunnels disappear when the client disconnects. No cross-replica sticky routing in v1."},{title:"Shared protocol (`src/tunnel/protocol.ts`)",body:"Control messages (JSON on the WebSocket): Session: , , Lifecycle: , , , HTTP: , (optional ) TCP: , Keepalive: , Binary frames (TCP payload): 1 byte frame type ( , , , ) 4 byte stream id (big-endian) 4 byte payload length payload bytes and implement the codec on client and relay."},{title:"Client (`src/tunnel/client.ts`)",body:"represents one active tunnel. Derives from the relay URL ( \u2192 , default path ). Connects with the bearer token. Sends with a random 8-hex . Waits for and stores and . HTTP path: On , issues to , then sends with status, headers, and optional base64 body. TCP path: On , connects locally and pumps bytes via binary frames; or relay close tears down the stream. Lifecycle: Periodic ; on stop, sends and closes the socket. Default target host is unless overrides it."},{title:"Manager (`src/tunnel/manager.ts`)",body:"holds live instances keyed by tunnel id and slug. Resolves relay URL via and token via Enforces from config / for CLI, chat, and gateway shutdown The gateway and CLI share one manager instance from ( )."},{title:"Configuration and secrets",body:"In (non-secret): \u2014 gate chat commands (default ) \u2014 default relay origin (default ) \u2014 max concurrent tunnels on this host (default ) Secrets (not in ): or ( ) via Optional relay override: or in the auth file See and ."},{title:"CLI (`omnish tunnel`)",body:"Implemented in ; wired from . Subcommand Behavior ----------------- ------------------------------------------------------ Save token (and optional relay) to Remove saved token Register HTTP tunnel; foreground unless Register TCP tunnel List active tunnels on this machine Stop one tunnel Relay reachability and auth presence Flags: , , , (see )."},{title:"Chat integration",body:"When is true, handles: / (optional , ) delegates to the shared . Tunnels run inside ; standalone still works without the gateway. Trust model matches : allowlisted chat users can open tunnels as the gateway OS user. Public URL possession is the visitor credential."},{title:"Security",body:"findings: \u2014 chat tunneling on \u2014 chat tunneling on without a token \u2014 non-default documents tunnel URLs as capabilities and the gateway shutdown path that stops active tunnels."},{title:"Local exercise",body:"Install relay deps: Start relay, for example: - - Run a local HTTP server on a port (for example ) Open the printed (path-based on loopback: ) Automated coverage: spins the relay and asserts HTTP end-to-end."},{title:"Out of scope (v1)",body:"Mandatory omnish.dev account or billing Tunnel persistence across client disconnect or HA relay fleet UDP, mesh VPN, or bundled ngrok/cloudflared as the primary path Setup UI for tunnel login (CLI is the v1 configuration surface)"},{title:"Success criteria (from product plan)",body:"+ yields a public URL that serves the local app (with a running relay) exposes a public TCP endpoint to the local port With and the gateway running, allowlisted users get the same URLs from chat and docs describe capability risk clearly"}],keywords:["tunneling","implementation","101","this","document","explains","how","omnish","is","built","relay","edge","client","cli","chat","integration","configuration","security","and","to","exercise","it","locally","big","picture","repository","map","contrib/tunnel-relay/","shared","protocol","src/tunnel/protocol","ts","src/tunnel/client","manager","src/tunnel/manager","secrets","tunnel","local","out","of","scope","v1","success","criteria","from","product","plan"],relatedCommands:["/tunneling","/architecture","/security","/close and","/tunnel","/tunnel-relay","/server","/contrib","/package","/protocol","/src","/types"]},{id:"docs-features-job-board",path:"docs/features/job-board.md",title:"Job board \u2014 digital employee coordination",summary:"The job board is omnish\u2019s work queue for autonomous digital employees: dependencies, coordinator dispatch, supervisor accountability, and host-wide resource limits.",sections:[{title:"Prerequisites",body:"(gateway active). Allowlisted sender for commands. with specialist commands (see example pack)."},{title:"Coordinator (on by default)",body:"While the gateway runs, the coordinator ticks automatically ( defaults to ). Set it to for manual only. Configure specialists: (or edit )."},{title:"Commands",body:"Command Action -------------------------------------------- ------------------------------------------------------------------------------ Human intake; optional , , Status counts plus per-job lines for review/pending/rework Job detail One coordinator + supervisor cycle (also sends status DMs when hooked to chat) Primary agent CLI for registry roles Resource governor snapshot Cancel job Registry names Coordinator onboarding and profiles Human satisfaction after Role-split suggestions Mark work-plan step done (for agent CLIs) is an alias for . Notification config Key Default Meaning ---------------------- ------------ ---------------------------------------- Phase completion messages Start / review / done / failed / blocked"},{title:"Data paths",body:"Path Purpose ----------------------------------- ---------------------------------------- Jobs, audit, slots Specialist commands Profile, skills, flow overrides Concurrency and CPU/RAM/GPU limits , , artifacts"},{title:"Coordinator (each tick)",body:"Unblock jobs whose dependencies are . Run supervisor on jobs in . If capacity allows, dispatch highest-priority runnable job to its specialist command."},{title:"Core competencies (non-optional)",body:"All employees run through the harness ( ). Registry entries use only (legacy alias). omnish seeds , enforces five phases, and notifies on progress. See Digital employees \u2014 core competencies."},{title:"Work plan (specialist discipline)",body:"The harness seeds before the inner agent runs. Specialists mark todos via scripts or . Handoff requires full completion, self-reviews on major steps, and in ."},{title:"Supervisor",body:"After a specialist finishes, the job moves to . The supervisor checks , , and job-plan lines. Outcomes: pass \u2192 ; dependents may unblock. rework \u2192 then reassigned (until ). fail \u2192 ; owner notified."},{title:"Separation of duties",body:"The coordinator does not approve quality; the supervisor does not choose the next assignee or create prerequisite jobs. That split keeps accountability auditable (see in SQLite)."}],keywords:["job","board","digital","employee","coordination","the","is","omnish","work","queue","for","autonomous","employees","dependencies","coordinator","dispatch","supervisor","accountability","and","host-wide","resource","limits","prerequisites","on","by","default","commands","data","paths","each","tick","core","competencies","non-optional","plan","specialist","discipline","separation","of","duties"],relatedCommands:["/guides","/employee-task-flow","/digital-employees","/coordinator-workforce","/board","/employees","/board tick","/board agent","/board add","/board list","/pending","/rework"]},{id:"docs-features-media-commands",path:"docs/features/media-commands.md",title:"Media commands (`/dl`, `/dlf`, `/dlv`, `/tr`, `/edit`)",summary:"Download, transcribe, and edit media from chat \u2014 using yt-dlp, ffmpeg, and optionally openai-whisper on the gateway host.",sections:[{title:"Defaults",body:"Files are sent to chat by default ( ). Background jobs deliver results over the gateway control channel when is active. To only list paths, set:"},{title:"Install tools (host)",body:"Binaries are stored under . Whisper uses . From chat (same trust as shell \u2014 off by default):"},{title:"Chat commands",body:"Command Action --------------------------------------------- ------------------------------------------------------------ Auto: file (HTTP), video (yt-dlp), or HTML\u2192markdown Force HTTP file download (never yt-dlp) Force yt-dlp video download Tool status OS-specific manual install steps Install tools into Whisper transcript + (+ video if URL); background job Trim or convert with ffmpeg; background job flags: / , / , / , / , . How classifies URLs File signals (always HTTP, never yt-dlp): pathname extension ( , , \u2026), path heuristics ( for arxiv, etc.), or from HEAD. Video: yt-dlp probe ( ) \u2014 if yt-dlp has a named site extractor (1000+ sites), download with yt-dlp. HTML page: fetch page and send markdown in chat (large pages may also save a file). Use when auto-detect might guess wrong (e.g. force HTTP on a video URL). Use to force yt-dlp. Save location Config / chat Where files go ----------------------------- --------------------------- set That directory (flat) for this chat Session cwd ( ) Default (flat) No dated subfolders. Share-friendly filenames All download paths normalize saved basenames for easy sharing in chat: ASCII-safe names (unicode stripped or transliterated, emoji removed) Spaces and unsafe characters become Collisions get numeric suffixes ( , ) yt-dlp uses plus a post-download rename pass Whisper sidecars ( , , etc.) inherit the normalized video basename. Background jobs , , , , and run as background jobs ( ). You get a job id immediately; finished files and markdown are sent to chat when the job completes (if is true). Optional flags (stripped before the payload): / \u2014 extra ping when the shell job finishes (exit status) / \u2014 no-op (already background) Step progress When is true (default), multi-step work sends chat messages such as while it runs. Auto-detect URLs When is true (default), a message that is only an or URL runs in the background \u2014 even when free shell ( ) is on. Focused PTY still wins over auto-dl. When chat LLM fallback is off and no LLM shell command is configured, lone URLs also auto-download even if was set to false (paste-a-link intent). Trailing punctuation from chat clients (e.g. ) is stripped before detection. Auto-dl replies with a short \u201CDownloading link\u2026\u201D message instead of a verbose job block. Legacy still works as aliases: / \u2192 , / \u2192 . Other modes show a deprecation hint."},{title:"Config keys",body:"Key Default Purpose -------------------------- ----------- ----------------------------------------------------------------------------------------- Send files to chat after download Allow from chat Lone URL \u2192 auto Output root (empty \u2192 Downloads/Omnish or cwd) yt-dlp cap (0 = none) Max size when sending downloads to chat (0 = no omnish cap; WA/TG limits still apply) Whisper / Transformers model size ( , , , \u2026) (default device, then CPU), , or (whisper CLI only) Per-attempt timeout for whisper/ffmpeg (10 min; max 15 min) , , or (whisper then Transformers.js fallback) When engine fails, retry with Transformers.js if installed On hosts with a busy or small GPU, set to or use a smaller ( , ). For long files that hit timeouts, raise . Transformers.js (no Python): installs under . Set to to use it exclusively, or keep / with (default) to fall back when openai-whisper fails. Step-by-step chat messages during multi-step work / / Binary overrides Older keys in are migrated on load (e.g. \u2192 ). Size limits ( , , ) Two different caps: Key When it applies ------------------ ---------------------------------------------------------------------------------------------------------- Download only (yt-dlp ). = no cap. Sending the saved file to chat after the job finishes. = no omnish cap on standalone . If the file downloads but chat says \u201CN file(s) not sent\u201D with a size, the job succeeded on disk; outbound delivery hit a cap \u2014 not . Standalone (local WhatsApp/Telegram on the host): only (and messenger limits). Attached ( with platform credentials, messengers on the relay): no default platform hop cap. Optional env (bytes; = unlimited) on both the relay and the device gateway caps outbound files over the WebSocket. WhatsApp/Telegram limits still apply upstream. Check values: (or ) for and ."},{title:"Legal and safety",body:"Same allowlist trust as shell commands. Private/local URLs are blocked ( , RFC1918, etc.) when the job runs. Respect copyright and platform terms; omnish only runs tools you install locally."},{title:"See also",body:"Files send/receive \u2014 for manual delivery Background jobs \u2014 , ,"}],keywords:["media","commands","/dl","/dlf","/dlv","/tr","/edit","download","transcribe","and","edit","from","chat","using","yt-dlp","ffmpeg","optionally","openai-whisper","on","the","gateway","host","defaults","install","tools","config","keys","legal","safety","see","also"],relatedCommands:["/dl","/dlf","/dlv","/tr","/edit","/bin","/venvs","/whisper","/config set","/dl install","/dl doctor","/dl setup"]},{id:"docs-features-media-pull",path:"docs/features/media-pull.md",title:"Media pull (`/pull`) \u2014 deprecated",summary:"> Superseded by media commands ( , , ). This page is kept for older releases.",sections:[{title:"Enable",body:"In (or from an allowlisted chat):"},{title:"Install tools (host)",body:"Binaries are stored under . Whisper uses . From chat (same trust as shell \u2014 off by default):"},{title:"Chat commands",body:"Command Action ------------------------ -------------------------------------------------- Usage Tool status OS-specific manual install steps Best video (needs ffmpeg) Audio extract (m4a) Subtitles (en + auto) Whisper speech-to-text video + audio + subs + transcript (background job) Flags: / \u2014 run as job / \u2014 notify in chat when the background job finishes and always run in the background. / / run synchronously unless is set. Auto-detect URLs When is true, a message that is only an or URL runs with (default ). Send results back to chat When is true, files under (and any cap in attached mode) are sent with automatically. Otherwise the reply lists paths \u2014 use manually."},{title:"Config keys",body:"Key Default Purpose ------------------------------------------------------ --------- ------------------------------------------------------ Master switch Allow Lone URL \u2192 pull Default mode Output root (empty \u2192 ) yt-dlp cap (0 = none) Push files to chat Whisper model / / Binary overrides"},{title:"Legal and safety",body:"Same allowlist trust as shell commands. Private/local URLs are blocked ( , RFC1918, etc.). Respect copyright and platform terms; omnish only runs tools you install locally."},{title:"See also",body:"Files send/receive \u2014 after a pull Background jobs \u2014 , ,"}],keywords:["media","pull","/pull","deprecated","superseded","by","commands","this","page","is","kept","for","older","releases","enable","install","tools","host","chat","config","keys","legal","and","safety","see","also"],relatedCommands:["/pull","/dl","/tr","/edit","/config","/config set","/bin","/venvs","/whisper","/pull install","/pull help","/pull doctor"]},{id:"docs-features-monetization",path:"docs/features/monetization.md",title:"Monetization \u2014 device-first omnish",summary:"omnish keeps WhatsApp and Telegram as the free control plane: allowlisted messages run on the user\u2019s machine. Paid tiers charge for reachability, identity, team boundaries, and safety nets\u2014not for sending a command.",sections:[{title:"Who pays at ~$10/month",body:"Someone who already runs on a home server, laptop, or Mac mini and wants the inbox to stay useful when they are away from the desk: show a dev server, start a long job, drive a TUI agent, tail logs, or hand a link to someone else. Free omnish sells my phone is a remote control for my computer. Paid omnish sells that control still works when I need a stable public URL, more than one tunnel, or light team structure\u2014without being my own ngrok admin."},{title:"Strongest first paid wedge",body:"Hosted tunneling on , integrated with the same chat and CLI, is the most natural first paid feature. Execution stays on their box; omnish runs the edge they would otherwise assemble (TLS, wildcard subdomains, relay uptime, abuse limits). For agent builds on the user\u2019s machine, steered from chat: the agent runs locally; Plus provides a shareable HTTPS preview via or , while iteration stays in WhatsApp or Telegram. That is not a cloud IDE\u2014it is a link that works while code and processes stay on their hardware. Plus ($10) \u2014 draft shape Several concurrent HTTP tunnels (free: self-hosted relay only, or one hosted tunnel with ephemeral names). Reserved slugs so does not change every session. Caps on bandwidth, tunnel lifetime, and concurrent tunnels so hosted relay cost stays bounded. TCP tunnels optional on Plus or stricter on free (TCP is riskier to operate). Comparable spend to ngrok-class tools; the hook is already inside omnish, not a separate dashboard."},{title:"Second layer: team and accountability",body:"Solo users may stay on free plus self-hosted relay. Small teams pay when my phone controls our box needs structure: More than one allowlisted identity with clearer roles (run vs read-only vs tunnel-only). Audit trail: who ran what, which tunnels opened, when\u2014exportable for a client or cofounder. Named machines in cluster mode without ambiguous shared binding. Still device-first: gateway on their hardware; paid layer is policy and visibility."},{title:"Third layer: reliability",body:"Weaker as the only $10 hook unless they depend on the gateway daily; pairs well with hosted tunnels: Offline alerts when stops heartbeating. Guided boot / service setup with a simple health view (gateway up, last command, disk, tunnel count). Optional backup / restore of omnish data (shortcuts, cowork defs, session cwd maps)\u2014not the whole disk."},{title:"What not to lead with at $10",body:"Hosted agent sandbox \u2014 fights device-first positioning; competes with Cursor, Replit, etc. Generic AI \u2014 conflicts with no-AI, your-shell positioning. Per-message chat fees \u2014 trains workarounds. Security theater \u2014 erodes trust on remote shell access."},{title:"Packaging sketch",body:"Tier Free Plus $10 Pro (later) --------------------------- ----------------- ----------------- ------------------- Chat \u2192 your shell Yes Yes Yes Self-hosted relay Yes Yes Yes Omnish-hosted HTTPS tunnels No / very limited Yes, with caps Higher caps Stable / custom names No Reserved slug Custom domain Team / audit Basic allowlist Small team + logs More seats, exports Gateway monitoring DIY Optional alerts SLA-style support One-liner: Keep controlling your machine from WhatsApp and Telegram for free; pay for public preview links, stable names, and team guardrails when that is how you work."},{title:"When someone will pay",body:"They pay when hosted tunnels remove a recurring pain: client demos, \u201Copen this while I change it from chat,\u201D or not maintaining Caddy, Cloudflare, and a relay on a VPS. Remote shell alone may stay free forever\u2014that supports adoption, not revenue."},{title:"Sequencing",body:"Plus = hosted tunnel + limits + account/token; chat stays unlimited. Team audit when paying users need shared access. Custom domains when reserved slugs feel tight. The preview-from-chat loop on their machine is credible paid value only if the URL is stable, HTTPS, and boring\u2014that is worth money while the shell stays free and local."}],keywords:["monetization","device-first","omnish","keeps","whatsapp","and","telegram","as","the","free","control","plane","allowlisted","messages","run","on","user","machine","paid","tiers","charge","for","reachability","identity","team","boundaries","safety","nets","not","sending","command","who","pays","at","10/month","strongest","first","wedge","second","layer","accountability","third","reliability","what","to","lead","with","10","packaging","sketch","when","someone","will","pay","sequencing"],relatedCommands:["/month someone","/tunnel","/theirname","/token"]},{id:"docs-features-omnish-chat",path:"docs/features/omnish-chat.md",title:"Omnish Chat",summary:"First-party, installable command inbox for omnish \u2014 platform account, no WhatsApp/Telegram required.",sections:[{title:"What it is",body:"PWA in \u2014 Vite + React, omnish-web tokens Surface with peer prefix \u2014 same routing as WA/TG Auth \u2014 existing platform signup/login; one token for dashboard, devices, and chat"},{title:"What it is not",body:"A full messenger (no groups, media, or social features in v0.1) A cloud shell \u2014 execution stays on your device"},{title:"Docs",body:"Resource Link ----------------- ------------------------------------------------------------------------------ Sidecar README contrib/chat/README.md Architecture contrib/chat/ARCHITECTURE.md Protocol contrib/chat/docs/protocol.md UX principles contrib/chat/docs/ux-principles.md Research & phases omnish-chat-v0.1.md ADR ADR-0018"},{title:"Quick start (developers)",body:""},{title:"User path (target v0.1)",body:"Create account at your platform URL (or in the PWA signup screen). On a machine: then . Allow your chat install: on the device or add the install id under Security \u2192 Allowlists \u2192 Omnish Chat on the platform dashboard. Install PWA, sign in, send or ."},{title:"Troubleshooting",body:"Symptom Cause Fix ------- ----- --- works, other commands return Not allowlisted on this omnish device is answered on the platform relay; shell commands are checked on the device allowlist Add your PWA install id ( shown after connect) via or dashboard Omnish Chat allowlist Logged into Omnish Chat but still denied Platform account login \u2260 device authorization (by design) Same as above \u2014 explicit pairing per chat install Added allowlist on dashboard, still denied for 1 min Attached syncs platform allowlists on a short interval Wait up to a minute and retry, or run locally for immediate standalone effect Device identity (not MAC / IP) Chat install \u2014 stable UUID in browser ( ); peer key . CLI device \u2014 stable UUID in when registers with the platform. Platform device record \u2014 MongoDB id + human label; not tied to IP (IPs change) or MAC (unavailable in browsers, randomized on many networks). MAC-based allowlisting is intentionally not used: it is not available to the PWA, changes with network interfaces, and is weaker than explicit install pairing for a shell gateway."},{title:"Implementation status",body:"Piece Status ------------------------------- ------- PWA scaffold + auth UI Done WS client + protocol types Done Relay Done CLI transport Done Web Push on reply Planned"}],keywords:["omnish","chat","first-party","installable","command","inbox","for","platform","account","no","whatsapp/telegram","required","what","it","is","not","docs","quick","start","developers","user","path","target","v0","troubleshooting","implementation","status"],relatedCommands:["/telegram required","/chat","/tg","/login","/readme","/contrib","/architecture","/docs","/protocol","/ux-principles","/research","/omnish-chat-v0"]},{id:"docs-features-online-catalog",path:"docs/features/online-catalog.md",title:"Online catalog",summary:"Share and install recipes, app templates, cowork tasks, and shortcuts across the omnish community. Browse and download from chat; publish when logged into the platform.",sections:[{title:"Discover and install (chat)",body:"Browse from the command family you care about \u2014 each prefix filters the catalog to the matching kind in MongoDB: Prefix Kind filter Example -------------------- ------------------------------------------------ -------------------------------------- all kinds (optional on trending/search) only only only Shared subcommands (replace with , , , or ): Command Purpose --------------------------------------- ---------------------------------------------------- Catalog subcommands for that family Most downloaded (kind-scoped when not ) Text search Full payload (review before install) Install to gateway-shared storage Install item #n from the last list in that family Repeat the last trending/search list for that family Examples: Use or on download to install for this chat only instead of gateway-shared: Numbered lists are per family \u2014 uses the last list, not a prior list. Platform URL: set in config (default hosted relay). Browse does not require a token; the CLI uses your configured relay origin. ---"},{title:"Publish (requires platform account)",body:"Sign up on the relay dashboard or run , then publish from chat: Command What is published Catalog -------------------------------------------------------------- --------------------------------------------------- -------------- User or gateway-shared recipe (not built-ins) Running PTY session command (session must be alive) Cowork task for this chat Shortcut (chat or shared) On success you get a (e.g. ). Others install with the matching prefix, e.g. or . Flags: , , , ---"},{title:"What gets installed",body:"Kind Local storage ---------- ---------------------------------------------------------------------- gateway-shared bucket Same as recipe (category ; use with stored command) gateway-shared bucket (owned by the importing chat) ---"},{title:"Security",body:"Recipes and app commands can run shell on your machine. Always before download. The platform rejects dangerous recipe flags and invalid command shapes at publish time. Download does not auto-run \u2014 it only adds the template locally. ---"},{title:"API",body:"Hosted relay routes are documented in Platform reference \u2014 Catalog. Related: Recipes / \xB7 Cowork \xB7 Shortcuts"}],keywords:["online","catalog","share","and","install","recipes","app","templates","cowork","tasks","shortcuts","across","the","omnish","community","browse","download","from","chat","publish","when","logged","into","platform","discover","requires","account","what","gets","installed","security","api"],relatedCommands:["/run online help","/apps online help","/guides","/platform-reference","/run online","/search","/apps online","/cowork online","/shortcut online","/run","/apps","/cowork","/shortcut","/search list"]},{id:"docs-features-run-queue",path:"docs/features/run-queue.md",title:"`/run` queue (`-q` / `--queue`)",summary:"The run queue runs recipe launches one at a time per chat, in FIFO order. It is meant for back-to-back agent jobs (e.g. several tasks) without starting multiple PTYs at once.",sections:[{title:"Syntax",body:"Recipes can also store a queue default at creation time: Then enqueues FIFO automatically (same as passing on every launch). Explicit / on launch still works and is equivalent. and are equivalent (case-insensitive). Short form works the same way. Combine with attach flags after the recipe name (same as non-queued ): attaches the queue head on start; without / , the head starts detached (default; see in Configuration). Not the same as the slash subcommand (status) or ."},{title:"Loading many jobs from JSON (`/run queue load`)",body:"You can enqueue multiple recipe tasks in one step from a JSON payload. Each row is validated like a separate (same recipe resolution, task length limits, and behavior). The queue still runs them one at a time in order. Ways to provide the JSON Method What to send -------------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ Host file path \u2014 path is resolved from this chat\u2019s session cwd (same idea as ): relative segments, globs, and quoted paths work like file selection elsewhere. Exactly one file must match. Inline JSON \u2014 everything after the word (with separating whitespace) is parsed as a single JSON value. Keep the payload on one message line if your client splits on newlines. Inbound file + caption Attach a document (e.g. ) and set the message text to (no path). Omnish uses the saved upload path from that same inbound turn (see Files \u2014 send & receive). You still get the usual \u201CSaved: \u2026\u201D line before the queue result. JSON format The file or inline text must be valid JSON in one of these shapes: Array of jobs (most common): Object with a single property (optional wrapper): Rules: Each job object must contain only two keys: and , both strings. Extra keys are rejected (catches typos like vs ). Top-level object form must be exactly \u2014 no other top-level keys. There must be at least one job. Empty arrays are rejected. Maximum 64 jobs per load. Not supported: raw , , or arbitrary shell in JSON. That would bypass recipe validation; only + are accepted, and omnish builds each run the same way as . File size limit for When reading a JSON file from disk (path argument or saved attachment path), the read is capped: If in is greater than zero, that value is used. If it is zero (no inbound cap), queue-load still uses a 1 MiB ceiling so a huge file cannot be pulled into memory unintentionally. Batch behavior and replies Jobs are enqueued in array order (first element becomes the next head when the queue is idle, or waits behind the current head). Omnish returns one reply that concatenates the status line from each enqueue step (started head, \u201Cwait slot \u2026\u201D, paused, etc.), same semantics as sending several messages in sequence. , , pauses, and clean-exit rules are unchanged \u2014 see Status and control and Pauses and failures. See also (files) Files \u2014 send & receive \u2014 where uploads land, , per-chat ."},{title:"What actually happens",body:"In memory only \u2014 Each waiting item is a small record (command + env + recipe label). Nothing runs until it becomes the head of the queue. Head starts immediately \u2014 When you enqueue and nothing else is running as the queue head, omnish shifts the first item off the FIFO and starts one app session (PTY), same as a normal . Others wait \u2014 Additional / calls append to the waiting list. No extra processes are spawned for those rows; the next job starts only when the previous head session exits with exit code 0 and signal 0 (clean exit). Per chat \u2014 Queue state is keyed by the chat ( ), not global across all users."},{title:"Why `/run queue` can show `Pending: 0` right after you queued something",body:"counts only jobs that have not started yet. The first item you add with / is removed from the pending list as soon as it starts; it then appears under Active with the session name and recipe label. Example: you send three queued runs in a row while the gateway is up: After message 1: Active = new session, Pending = 0 (nothing left waiting). After message 2: Active still the first session, Pending = 1 (second job waiting). After message 3: Pending = 2 (second and third waiting). So after a single enqueue is normal \u2014 it means the one job is already running, not that the queue \u201Clost\u201D your task. If you used before omnish accepted , the old parser treated as part of the task text and ran non-queued instead; the queue stayed empty. Use or after the recipe name (current omnish supports both)."},{title:"Status and control",body:"\u2014 Shows Active (session + recipe), Pending (with a short numbered list of waiting recipe labels), Paused, and a reminder that the next item auto-starts only after a clean exit. \u2014 Clears the paused flag and tries to start the next waiting item (e.g. after you fixed the host or session limits). If a head session is still running, resume tells you to wait until it finishes cleanly. \u2014 Enqueue many jobs from JSON (file path, inline , or attachment + caption); see Loading many jobs from JSON."},{title:"Pauses and failures",body:"Non-clean exit (non-zero exit code, or non-zero signal, e.g. SIGKILL) on the queue head \u2192 the queue pauses; waiting items stay in the FIFO until you (or fix limits and resume). Failed spawn (e.g. per-chat app limit reached) when trying to start the head \u2192 queue pauses and the item is put back; fix the error, then ."},{title:"Resource model",body:"Waiting rows: RAM only (no polling timers for the queue itself). Running head: one PTY + child process, same cost as a normal . Not persisted \u2014 If the gateway process ( ) restarts, the in-memory queue is cleared. Long-term scheduling belongs in Cowork or your own job runner."},{title:"See also",body:"System agents and User guide \u2014 shortcuts vs Interactive sessions ( )"}],keywords:["/run","queue","-q","--queue","the","run","runs","recipe","launches","one","at","time","per","chat","in","fifo","order","it","is","meant","for","back-to-back","agent","jobs","several","tasks","without","starting","multiple","ptys","once","syntax","loading","many","from","json","load","what","actually","happens","why","can","show","pending","right","after","you","queued","something","status","and","control","pauses","failures","resource","model","see","also"],relatedCommands:["/run help","/run queue","/run","/run remosh","/run add","/run trace","/guides","/configuration","/to","/file","/send","/files-send-receive","/receive here"]},{id:"docs-features-service-from-chat",path:"docs/features/service-from-chat.md",title:"Service commands from chat (`/service`)",summary:"After WhatsApp or Telegram is connected and your identity is allowlisted, you can manage background gateway setup from the same DM thread \u2014 without SSH.",sections:[{title:"Commands",body:"Command Purpose ----------------------- -------------------------------------------------------------------------------------------------------------------------- Overview OS, data directory, , resolved Node + entry script paths Copy-paste steps for this host (paths filled by the running gateway) Last n lines of the default gateway log (default 80, max 120) Writes a user-level unit (Linux systemd or macOS LaunchAgent). Requires . Removes that unit (same gate). Windows: instructions only."},{title:"Trust model",body:", , and are safe to use like any other slash command (same allowlist as ). and modify login/boot integration files under your home directory. That is equivalent to shell access: anyone who can DM as an allowed identity can trigger them once is true in . Default: is . Enable only when you trust every entry on / as much as SSH. When enabled, reports a warning for ."},{title:"Relation to `CHANGE_ME` templates",body:"The contrib plist/service/XML files use placeholders for manual edits. bypasses that by injecting live paths from the running process. writes generated units with those paths automatically."},{title:"See also",body:"Background gateway and start on boot Cluster and chat configuration ( )"}],keywords:["service","commands","from","chat","/service","after","whatsapp","or","telegram","is","connected","and","your","identity","allowlisted","you","can","manage","background","gateway","setup","the","same","dm","thread","without","ssh","trust","model","relation","to","change","me","templates","see","also"],relatedCommands:["/service help","/service","/service status","/service instructions","/service logs","/service install","/config set","/service uninstall","/help","/boot integration","/contrib","/xml files"]},{id:"docs-features-sessions",path:"docs/features/sessions.md",title:"Interactive Sessions - omnish",summary:"Interactive sessions provide full terminal access within your messaging chats, enabling you to run TUI applications, REPLs, and interactive tools directly from WhatsApp or Telegram.",sections:[{title:"Overview",body:"Interactive sessions use to create pseudo-terminal sessions that mimic real terminal behavior. Each chat can maintain multiple named sessions with independent state. Key Features PTY-based: Full terminal emulation Named sessions: Multiple sessions per chat Focus management: One session attached at a time Output streaming: Real-time output with debouncing ANSI support: Color output and formatting Session persistence: State maintained across chats"},{title:"Session Management",body:"Starting Sessions Session Limits Per chat: Default 5 sessions (configurable) Global: Default 20 sessions (configurable) Named: Each session has a unique name per chat Plain messages and free shell For a single incoming line with no , , or , the router sends text to the attached session first when it is running; free shell mode ( ) applies only if nothing is attached (or the focused session is not running). Use for a one-off sync shell line while attached. Session States Created: Session initialized but not started Running: Session active and accepting input Attached: Session is focused for input Detached: Session running but not focused Stopped: Graceful shutdown requested Killed: Force termination Exited: Process terminated normally"},{title:"Session Commands",body:"Basic Operations Input Control Output Management Session Information"},{title:"Configuration",body:"Session Limits Terminal Settings Behavior Settings When , , or similar shows a password prompt, omnish detects it in recent terminal output and does not send the readline clear keys ( ) for your next reply \u2014 those keys break no-echo password readers and often appear as literal in chat. A one-time hint is sent unless is false. Passwords are still written to the session log on disk."},{title:"Use Cases",body:"Development Workflows ```text"}],keywords:["interactive","sessions","omnish","provide","full","terminal","access","within","your","messaging","chats","enabling","you","to","run","tui","applications","repls","and","tools","directly","from","whatsapp","or","telegram","overview","session","management","commands","configuration","use","cases"],relatedCommands:["/apps help","/apps start","/apps list","/apps attach","/apps detach","/apps stop","/apps kill","/apps send","/apps key","/apps tail","/apps since","/apps mute","/apps raw"]},{id:"docs-features-tunneling",path:"docs/features/tunneling.md",title:"Tunneling \u2014 omnish",summary:"omnish tunneling publishes a public URL that forwards to a local HTTP or TCP port on the machine running the tunnel client. The default relay is .",sections:[{title:"CLI (primary)",body:"Secrets are stored in (mode ) or . Override the relay with , in , or on expose commands."},{title:"Chat (optional)",body:"Login, logout, and status from chat work whenever the gateway runs (even if is false): , , . The bot reply does not echo your token; the inbound chat message still contains it (WhatsApp/Telegram history) \u2014 prefer on the host for highly sensitive tokens. When is in , allowlisted users can also run: Chat tunnels run inside the gateway process ( ) and share the same relay token as the CLI."},{title:"Security",body:"A tunnel URL is a capability: anyone who can open the URL can reach the forwarded service. Dev servers are often unauthenticated; treat tunneling like exposing a port on the public internet. Chat tunneling uses the same trust model as : allowlisted identities can open tunnels as the gateway OS user. in chat stores the bearer token on the gateway host but leaves a copy in the messaging transcript; use host CLI login if that risk matters for your threat model. See Security model."},{title:"Self-hosted relay",body:"For development or private deployments, run the relay in and point omnish at it with or . Operators: testing and operations (health checks, smoke, production VPS layout)."}],keywords:["tunneling","omnish","publishes","public","url","that","forwards","to","local","http","or","tcp","port","on","the","machine","running","tunnel","client","default","relay","is","cli","primary","chat","optional","security","self-hosted"],relatedCommands:["/tunnel help","/tunnels","/config help","/tunnel","/guides","/tunnel-setup-from-zero","/tunnel-auth","/tunnel login","/tunnel logout","/tunnel status","/telegram history","/tunnel http","/tunnel tcp","/tunnel stop"]},{id:"docs-features-watch",path:"docs/features/watch.md",title:"Watch \u2014 OS event eye",summary:"Lightweight OS event subscriptions that notify you on WhatsApp or Telegram when something changes on the machine running .",sections:[{title:"Quick start",body:"Enable watching: Or: or Run . Add rules: You should receive debounced messages like:"},{title:"Device-wide rules",body:"Watch rules are shared on the host, not private to one chat: One namespace per machine in (max 20 rules per device). Any allowlisted peer can , edit, pause, or remove any rule. Rule names must be unique on the device (two peers cannot each have a rule named ). (the default on new rules) alerts the peer who created the rule. Use , , or to reach more recipients. shows the creator peer key. If you had duplicate names from an older per-chat layout, omnish renames extras to on load."},{title:"Runtime model",body:"Watch is not a separate daemon or timed session. It runs inside the gateway process ( , foreground or ). Topic Behavior ----------------- ------------------------------------------------------------------------------------------------------------------------------------------------------- How long Indefinite while the gateway runs, is true, and the rule is enabled and not paused. There is no session timeout. Debounce (default 2s, range 500ms\u201360s) coalesces bursts before chat notify. Rate cap per rule (default 30). Service polls Each rule runs / / about every 30 seconds. Sys journal Each rule tails (Linux) with auth.log fallback; classifies login, fail, sudo, reboot, accounts, firewall. Background Same Node process as the gateway; keeps Watch alive like foreground. Not a separate OS service unless you installed the gateway as one."},{title:"Cowork, `/bg`, and Watch",body:"Feature What it does Relation to Watch ---------- ------------------------------------------------------------------ ------------------------------------------------------------------------------------------------- Watch OS events (FS, package logs, services, journal/auth) \u2192 chat alerts \u2014 Cowork Scheduled or on-demand shell commands while the gateway runs Parallel \u2014 same gateway, shared notify routing only. Does not trigger or consume watch rules. Background shell jobs from chat Unrelated \u2014 no integration with watch adapters. See Cowork for task schedules and heartbeats."},{title:"Persistence and restart",body:'Watch rules are not memory-only. They are saved on disk and survive gateway and service restarts. Data Path ------------------------------------------ -------------------------------------------------------------------------------- Rules (paths, excludes, paused, notify, \u2026) Recent events and state-change keys Global on/off and tuning ( , , debounce, rate cap) On gateway boot: If and (default), omnish reloads and starts adapters for rules that are enabled and not paused. If , rules remain on disk but adapters do not run until . If , rules persist but you must (or change a rule) to start adapters without restarting the whole gateway. Use to see file paths, saved rule counts, and adapter health. Troubleshooting persistence Symptom Check --------------------------------- --------------------------------------------------------------------------------- "Rules gone" after restart \u2014 they should still be there No alerts after restart in config; paused/disabled rules; Rules on disk but nothing running or ; if'},{title:"Rule lifecycle",body:"State What it means Command -------------- ------------------------------------- ------------------------------------------------ Active Adapter running, alerts on or Paused Rule kept, adapter stopped, no alerts or Disabled Rule kept in list, Removed Deleted from Global off All adapters stopped After pause, stop, disable, or rm, pending debounced messages are cancelled so you should not get a late alert. pause / stop \u2014 same effect: stop watching, keep the rule for later. resume \u2014 start again if the rule is enabled. enable / disable \u2014 per-rule on/off (distinct from global and ). Check state: , , ."},{title:"Filesystem watches",body:"Add Root path \u2014 first path after the name ( , ). Events \u2014 optional comma list (default: ). Excludes (optional, both supported): Syntax Example ------------------ -------------------------------------- or You can combine them: between exclude clauses is also accepted as a separator. Manage excludes later Excludes are applied twice for efficiency: native watcher ignore list + post-filter before notify."},{title:"Package and service watches",body:"Kind Command -------- ----------------------------------------------------------------------- Packages \u2014 install/remove from OS logs Services \u2014 state changes on named units Use on noisy service checks."},{title:"System journal watches (`sys`)",body:"Monitor login/logout, failed auth, sudo, reboot/shutdown, account changes, and firewall events on Linux (journal + auth.log). Omit topics to subscribe to all six. Preset adds (sys) + (pkg) with . Host permissions (Linux): gateway user should be in and groups to read system logs without root: ```bash sudo usermod -aG adm,systemd-journal $USER"}],keywords:["watch","os","event","eye","lightweight","subscriptions","that","notify","you","on","whatsapp","or","telegram","when","something","changes","the","machine","running","quick","start","device-wide","rules","runtime","model","cowork","/bg","and","persistence","restart","rule","lifecycle","filesystem","watches","package","service","system","journal","sys"],relatedCommands:["/watch help","/config","/watch on","/config set","/watch add","/deploy create","/projects","/tmp","/watch apply","/watch list","/home","/you"]},{id:"docs-features-webhook-receiver",path:"docs/features/webhook-receiver.md",title:"Webhook receiver \u2014 CI/CD notifications via chat",summary:"The webhook receiver is a lightweight HTTP server built into the omnish gateway. It accepts requests with JSON payloads, formats them into concise messages, and delivers them to your WhatsApp or Telegram chat via the existing pipeline.",sections:[{title:"Prerequisites",body:"A running gateway: . set to in ."},{title:"Configuration",body:"Key Type Default Description ---------------- ------- ------------- --------------------------------------------------------------------------------------------------------------------------------------- boolean Enable the webhook HTTP server number (random) Port to listen on. picks a random available port. string Bind address. Use to accept external connections (see security note). string Bearer token for authentication. If empty when the receiver starts, a random 32-byte hex token is generated and saved to . Example :"},{title:"Endpoint",body:"The token can also be passed as a query parameter: . Request body Any valid JSON object. The receiver formats the payload into a chat message using built-in formatters for known CI systems, or a generic format for everything else. Optional fields in the JSON body: Field Type Description --------- ------ ------------------------------------------------------------------------------------------------------------------------------------------ string Target chat identity (e.g. or ). If omitted, the message goes to the first allowlisted peer. string Label for the source system (shown in the formatted message). Can also be set via query parameter or header. string Simple text message (used as-is when present). string Fallback message text. string Title line for generic payloads. string Status line for generic payloads. Response Status Body Meaning ------ -------------------------------------------------- ------------------------------ Message delivered Invalid JSON or no target peer Missing or invalid token Not a POST request Body exceeds 256 KB failed"},{title:"Built-in CI formatters",body:"GitHub Actions When the payload contains and a object, the receiver formats: GitLab CI When the payload contains and , the receiver formats: Generic payloads For any other JSON, the receiver uses , , , and fields if present, or falls back to a truncated JSON preview."},{title:"Examples",body:"GitHub Actions workflow Add a step at the end of your workflow to notify on completion: GitLab CI Simple notification from a script"},{title:"Security",body:"The webhook server binds to 127.0.0.1 by default \u2014 only local processes can reach it. If you set to , the server is accessible from the network. Use a firewall or reverse proxy with TLS in production. The bearer token is compared using to prevent timing attacks. Maximum payload size is 256 KB."},{title:"Default peer resolution",body:"When the incoming payload does not include a , the receiver picks the first available peer from the gateway's allowlist: WhatsApp peers are checked first, then Telegram. If no peer is available, the request returns a error."},{title:"See also",body:"Configuration guide \u2014 webhook config keys Cowork heartbeat \u2014 combine with heartbeat tasks for dead-man's-switch monitoring Background jobs \u2014 per-job completion notifications"}],keywords:["webhook","receiver","ci/cd","notifications","via","chat","the","is","lightweight","http","server","built","into","omnish","gateway","it","accepts","requests","with","json","payloads","formats","them","concise","messages","and","delivers","to","your","whatsapp","or","telegram","existing","pipeline","prerequisites","configuration","endpoint","built-in","ci","formatters","examples","security","default","peer","resolution","see","also"],relatedCommands:["/help","/webhook authorization","/json","/repo","/github","/owner","/actions","/runs","/project","/checkout","/your-server","/webhook","/localhost"]},{id:"docs-guides-background-and-boot",path:"docs/guides/background-and-boot.md",title:"Background gateway and start on boot",summary:"Portable CLI (all platforms): starts the gateway detached by default; logs default to ; pass (alias: ) to append elsewhere; reads and stops the process. Equivalent: (or ). Foreground: or . Use these for ad-hoc background runs without installing a system integration.",sections:[{title:"Linux (systemd --user)",body:"Copy contrib/omnish.service to . Set to your Node path and absolute path to , and if the data directory is not . Run: For a user service to run without an active login session, enable lingering:"},{title:"macOS (launchd)",body:"The LaunchAgent label is (reverse-DNS for omnish.dev). Older templates used ; if you already installed that, boot it out before installing the new plist (see below). Edit contrib/dev.omnish.gateway.plist: set Node path, path, and (and log paths if you use them). Copy to . Load (replace with output): To unload: Migrating from : remove the old job first, then install the new file: Alternative: add a small script that runs to System Settings \u2192 General \u2192 Login Items (simpler, no auto-restart on crash)."},{title:"Windows (Task Scheduler)",body:"Open Task Scheduler \u2192 Create Task\u2026 (not a basic task). General: run only when user is logged on (typical for WhatsApp session). Triggers: At log on for your user. Actions: Start a program Program: path to (e.g. from in ). Add arguments: (adjust the path; quotes if it contains spaces). Start in (optional): install folder. Set OMNISHHOME under user environment variables if you do not use the default data directory, or use a wrapper that then runs . Optional: import contrib/omnish-windows-task.xml after editing paths (import may need tweaks per account; the GUI is more reliable if XML import fails). Advanced: NSSM or WinSW can install Node as a Windows Service for machine-wide or headless scenarios \u2014 not maintained by this repo."},{title:"Stopping the gateway",body:"(any OS): uses the pidfile from or . On Windows, if signaling the process fails, omnish may fall back to (best-effort shutdown). systemd / launchd / Task Scheduler: use the manager\u2019s stop / disable as usual; do not rely on from a different start method."},{title:"Environment",body:": data directory (same as elsewhere in omnish). For services, set it in the unit / plist / task environment, not only in the shell profile."}],keywords:["background","gateway","and","start","on","boot","portable","cli","all","platforms","starts","the","detached","by","default","logs","to","pass","alias","append","elsewhere","reads","stops","process","equivalent","or","foreground","use","these","for","ad-hoc","runs","without","installing","system","integration","linux","systemd","--user","macos","launchd","windows","task","scheduler","stopping","environment"],relatedCommands:["/service help","/service instructions","/logs","/gateway","/service","/service logs","/service install","/features","/service-from-chat","/dist","/index","/omnish","/contrib"]},{id:"docs-guides-channels-bluebubbles",path:"docs/guides/channels/bluebubbles.md",title:"bluebubbles",summary:"Enable in , configure , allowlist with .",sections:[],keywords:["bluebubbles","enable","in","configure","allowlist","with"],relatedCommands:["/readme"]},{id:"docs-guides-channels-discord",path:"docs/guides/channels/discord.md",title:"Discord",summary:"",sections:[{title:"Standalone",body:"Create a bot at Discord Developer Portal. Enable Message Content Intent and Direct Messages. Set token via env or config: DM-only by default (no guild shell execution)."},{title:"Platform (hosted relay)",body:"Discord bot install uses an invite URL, not per-user OAuth tokens. The relay operator sets: Dashboard flow: Click Add Discord (opens invite URL). Add the bot to your server. Click I've added the bot in the dashboard. DM the bot and allowlist your user id (same pattern as Telegram). One Omnish Discord application serves all platform users; bot token stays on the relay."}],keywords:["discord","standalone","platform","hosted","relay"],relatedCommands:["/discord","/developers","/applications","/id"]},{id:"docs-guides-channels-feishu",path:"docs/guides/channels/feishu.md",title:"feishu",summary:"Enable in , configure , allowlist with .",sections:[],keywords:["feishu","enable","in","configure","allowlist","with"],relatedCommands:["/readme"]},{id:"docs-guides-channels-googlechat",path:"docs/guides/channels/googlechat.md",title:"googlechat",summary:"Enable in , configure , allowlist with .",sections:[],keywords:["googlechat","enable","in","configure","allowlist","with"],relatedCommands:["/readme"]},{id:"docs-guides-channels-imessage",path:"docs/guides/channels/imessage.md",title:"imessage",summary:"Enable in , configure , allowlist with .",sections:[],keywords:["imessage","enable","in","configure","allowlist","with"],relatedCommands:["/readme"]},{id:"docs-guides-channels-irc",path:"docs/guides/channels/irc.md",title:"irc",summary:"Enable in , configure , allowlist with .",sections:[],keywords:["irc","enable","in","configure","allowlist","with"],relatedCommands:["/readme"]},{id:"docs-guides-channels-line",path:"docs/guides/channels/line.md",title:"line",summary:"Enable in , configure , allowlist with .",sections:[],keywords:["line","enable","in","configure","allowlist","with"],relatedCommands:["/readme"]},{id:"docs-guides-channels-matrix",path:"docs/guides/channels/matrix.md",title:"matrix",summary:"Enable in , configure , allowlist with .",sections:[],keywords:["matrix","enable","in","configure","allowlist","with"],relatedCommands:["/readme"]},{id:"docs-guides-channels-mattermost",path:"docs/guides/channels/mattermost.md",title:"mattermost",summary:"Enable in , configure , allowlist with .",sections:[],keywords:["mattermost","enable","in","configure","allowlist","with"],relatedCommands:["/readme"]},{id:"docs-guides-channels-msteams",path:"docs/guides/channels/msteams.md",title:"msteams",summary:"Enable in , configure , allowlist with .",sections:[],keywords:["msteams","enable","in","configure","allowlist","with"],relatedCommands:["/readme"]},{id:"docs-guides-channels-nextcloud-talk",path:"docs/guides/channels/nextcloud-talk.md",title:"nextcloud-talk",summary:"Enable in , configure , allowlist with .",sections:[],keywords:["nextcloud-talk","enable","in","configure","allowlist","with"],relatedCommands:["/readme"]},{id:"docs-guides-channels-nostr",path:"docs/guides/channels/nostr.md",title:"nostr",summary:"Enable in , configure , allowlist with .",sections:[],keywords:["nostr","enable","in","configure","allowlist","with"],relatedCommands:["/readme"]},{id:"docs-guides-channels-qqbot",path:"docs/guides/channels/qqbot.md",title:"qqbot",summary:"Enable in , configure , allowlist with .",sections:[],keywords:["qqbot","enable","in","configure","allowlist","with"],relatedCommands:["/readme"]},{id:"docs-guides-channels-README",path:"docs/guides/channels/README.md",title:"Chat channels",summary:"omnish supports 22 messaging channels (OpenClaw parity, excluding internal QA harness).",sections:[{title:"Enable channels",body:"Legacy ( ) still works and maps to ."},{title:"CLI",body:"See ADR-0012 for architecture."}],keywords:["chat","channels","omnish","supports","22","messaging","openclaw","parity","excluding","internal","qa","harness","enable","cli"],relatedCommands:["/whatsapp","/telegram","/discord","/slack","/matrix","/irc","/signal","/googlechat","/mattermost","/lark","/feishu","/line"]},{id:"docs-guides-channels-signal",path:"docs/guides/channels/signal.md",title:"Signal",summary:"Text DMs via signal-cli as a linked device to your primary Signal phone.",sections:[{title:"Prerequisites",body:"installed on the gateway host (standalone) or relay host (platform attached mode). Version 0.13+ recommended. A Signal account on your phone (primary device)."},{title:"Standalone: QR link",body:"Scan the terminal QR from Signal \u2192 Linked devices \u2192 Link a device. Replace an existing session: Credentials are stored under ( and in ). Enable the channel and allowlist senders: Env overrides (optional): , (if you configured manually)."},{title:"Platform attached mode",body:"Ensure is on the relay host. Dashboard Connectors \u2192 Signal \u2192 Link Signal, or: Scan the QR from your phone (Linked devices). Add your number to allowlists ( on dashboard Security, or ). Run on attached devices. Unlink:"},{title:"Limits and notes",body:"Signal allows up to five linked devices per primary account. The pairing URI expires quickly; retry link if the QR times out (3 minutes). Stop local before re-linking to avoid session conflicts. SMS register/verify for a new dedicated number is not built into omnish \u2014 use manually if you need that path. Text only (no inbound/outbound media in omnish today). See channels README."}],keywords:["signal","text","dms","via","signal-cli","as","linked","device","to","your","primary","phone","prerequisites","standalone","qr","link","platform","attached","mode","limits","and","notes"],relatedCommands:["/github","/asamk","/signal-cli","/signal","/verify","/outbound media","/readme"]},{id:"docs-guides-channels-slack",path:"docs/guides/channels/slack.md",title:"Slack",summary:"",sections:[{title:"Create the Slack app",body:"Use the ready-made manifest and icons in : api.slack.com/apps \u2192 Create New App \u2192 From an app manifest \u2192 paste . Upload as the app icon. Enable Socket Mode and create an App-Level Token ( ) \u2192 ( ). Install to Workspace \u2192 copy Bot Token \u2192 ( )."},{title:"Standalone",body:"DM the bot to discover your Slack user id, then allowlist: Or configure / in config JSON. Bot scopes Scope Purpose ----------------------- ------------------------- Reply in DMs , Receive DMs (Socket Mode) User lookup for Inbound attachments Outbound files DM-only \u2014 non-IM channels are ignored."},{title:"Platform (hosted relay)",body:"When the relay operator configures a hosted Omnish Slack app: Users open the dashboard Connectors tab and click Connect with Slack. OAuth v2 install stores a per-workspace in encrypted ; the shared stays on the relay. Manual tokens remain available under Advanced: manual tokens in the dashboard. See contrib/slack-app/README.md for manifest details and asset sizes."}],keywords:["slack","create","the","app","standalone","platform","hosted","relay"],relatedCommands:["/slack-app","/contrib","/readme","/apps","/api","/manifest","/assets","/icon-512","/id","/send"]},{id:"docs-guides-channels-synology-chat",path:"docs/guides/channels/synology-chat.md",title:"synology-chat",summary:"Enable in , configure , allowlist with .",sections:[],keywords:["synology-chat","enable","in","configure","allowlist","with"],relatedCommands:["/readme"]},{id:"docs-guides-channels-telegram",path:"docs/guides/channels/telegram.md",title:"telegram",summary:"Enable in , configure , allowlist with .",sections:[],keywords:["telegram","enable","in","configure","allowlist","with"],relatedCommands:["/readme"]},{id:"docs-guides-channels-tlon",path:"docs/guides/channels/tlon.md",title:"tlon",summary:"Enable in , configure , allowlist with .",sections:[],keywords:["tlon","enable","in","configure","allowlist","with"],relatedCommands:["/readme"]},{id:"docs-guides-channels-twitch",path:"docs/guides/channels/twitch.md",title:"Twitch",summary:"",sections:[{title:"Standalone",body:"Enable in , configure ( , , ), allowlist with . See channels README."},{title:"Platform (hosted relay)",body:"When the relay operator configures a hosted Omnish Twitch app: Users click Connect with Twitch in the dashboard. OAuth2 authorization code flow stores IRC credentials ( , , ) in encrypted connector secrets. Manual token fields remain under Advanced in the dashboard."}],keywords:["twitch","standalone","platform","hosted","relay"],relatedCommands:["/readme"]},{id:"docs-guides-channels-whatsapp",path:"docs/guides/channels/whatsapp.md",title:"whatsapp",summary:"Enable in , configure , allowlist with .",sections:[],keywords:["whatsapp","enable","in","configure","allowlist","with"],relatedCommands:["/readme"]},{id:"docs-guides-channels-zalo",path:"docs/guides/channels/zalo.md",title:"zalo",summary:"Enable in , configure , allowlist with .",sections:[],keywords:["zalo","enable","in","configure","allowlist","with"],relatedCommands:["/readme"]},{id:"docs-guides-channels-zalouser",path:"docs/guides/channels/zalouser.md",title:"zalouser",summary:"Enable in , configure , allowlist with .",sections:[],keywords:["zalouser","enable","in","configure","allowlist","with"],relatedCommands:["/readme"]},{id:"docs-guides-configuration",path:"docs/guides/configuration.md",title:"Configuration Guide - omnish",summary:"Complete configuration reference and customization guide.",sections:[{title:"Configuration Overview",body:"omnish uses a JSON configuration file with sensible defaults. You can customize every aspect of the system's behavior. Configuration File Location Default: Legacy: (if upgrading) Override: Set Check your configuration location: ```bash omnish status"}],keywords:["configuration","guide","omnish","complete","reference","and","customization","overview"],relatedCommands:["/config help","/config keys","/config","/path","/to","/dir","/bin","/bash","/features","/chat-llm-fallback","/tunnel","/login","/v1","/me"]},{id:"docs-guides-coordinator-workforce",path:"docs/guides/coordinator-workforce.md",title:"Coordinator workforce manager",summary:"The job board Coordinator is a deterministic workforce layer: onboard employees, assess skills, customize work flow, dispatch through the harness, record performance, collect human feedback, and suggest recruitment when roles are overloaded.",sections:[{title:"Communication (all employees)",body:"Every specialist run receives , , , and (coordinator-only) . Do not document communication as alone \u2014 always use the full terminal commands below (requires on the host). Role When Full command ------------------ ------------------------------------------------------------- ------------------------------------------------- Any specialist While job is (at least once per phase; on blockers) Coordinator After supervisor pass, when the plan requires file delivery Helper script (employee pack): runs the specialist command. Specialists must not send final CHECK deliverables via sendto before review. The coordinator has final say on files. At handoff, must still include: \u2014 what was communicated to the coordinator/human \u2014 plain-language outcome for the job owner Work plans include a step under the plan phase. The supervisor rejects handoffs missing these fields. When is true (default), the coordinator runs the file-send command automatically after if the plan mentions / distribution and a media CHECK file exists in the work dir."},{title:"Onboarding",body:"Creates with , , and registry entry. Coordinator seeds core competencies and activates the employee when the skill pack exists."},{title:"Skills and flow",body:"Before dispatch (when is true), the Coordinator assesses gaps vs the job plan. With , it runs your agent to fill ; otherwise a deterministic template fills gaps and writes . Per-employee may add extra steps in allowed phases; required phases and cannot be removed. Harness env: , ."},{title:"Performance and feedback",body:"After each supervisor verdict, a row is stored in . When a job passes:"},{title:"Recruitment",body:"Heuristics (overload, multi-domain plans, rework) create suggestions. Humans accept before a new employee exists:"},{title:"Optional finetuning",body:"When and the coordinator agent are enabled, poor feedback can produce files under . Promote only after review:"},{title:"Config",body:"Key Default Purpose --------------------------------- ------- ----------------------------------------------------------------------------------- External agent for research/finetune Shell command Allow proposed tuning Gap assessment before dispatch Coordinator runs after done when plan requires delivery One-shot satisfaction reminder See also Employee task flow, digital-employees.md, and job-board.md."}],keywords:["coordinator","workforce","manager","the","job","board","is","deterministic","layer","onboard","employees","assess","skills","customize","work","flow","dispatch","through","harness","record","performance","collect","human","feedback","and","suggest","recruitment","when","roles","are","overloaded","communication","all","onboarding","optional","finetuning","config"],relatedCommands:["/sendto","/employee-pack-example","/scripts","/board-status","/human","/board employee","/board","/employees","/board feedback","/board recruit","/proposed","/finetune"]},{id:"docs-guides-digital-employees",path:"docs/guides/digital-employees.md",title:"Digital employees \u2014 job board, coordinator, supervisor",summary:"omnish stays local execution infrastructure. Digital employees are external agent CLIs (or shell wrappers) coordinated by three meta-roles plus specialists.",sections:[{title:"Roles",body:"Role Responsibility Human? --------------- ------------------------------------------------------------------------------------------------- ----------- Human Posts parent jobs to the job board Intake only Coordinator Assigns specialists, creates prerequisite jobs, unblocks dependents, enforces resource limits No Supervisor Verifies work against the job plan; pass, rework, or fail No Specialist Runs assigned work; writes No The coordinator and supervisor run inside the gateway while it is up ( defaults to true). Use for a one-shot cycle or set to for fully manual mode."},{title:"Job board",body:"Storage: Artifacts: (report, verdict, audit via DB) Status flow Full pipeline (human intake through supervisor and feedback): Employee task flow. Chat commands and are aliases. Acceptance criteria in the plan Supervisor runs deterministic checks from the plan: Optional LLM review can wrap the same contract later; the built-in supervisor is deterministic first."},{title:"Core competencies (always on)",body:"No matter how vague an is (e.g. ), omnish never runs it directly. The employee harness always: Seeds with canonical steps: plan \u2192 implement \u2192 execute \u2192 test \u2192 review Runs the inner command via Tracks phase progress and notifies the job owner (configurable) Enforces handoff via the supervisor (work plan + report contract) You define what runs ( ). omnish defines how employees work. Competency Enforcement ----------------------- ----------------------------------------------------------- Five phases Pre-seeded work plan Plan before work Harness seeds plan before inner CLI Structure + self-review Supervisor + step Outcome ownership , , Progress visibility Chat notifications on phases and status"},{title:"Specialist contract",body:"Each employee is configured in : Legacy is accepted as an alias for . Environment injected at run time: , , , \u2014 harness-seeded \u2014 \u2014 e.g. Plan before work (harness + supervisor) The harness creates the work plan before your agent runs. Specialists mark steps complete (in script or via ). Before handoff: All steps and set; with and matching The supervisor rejects handoffs that miss any core competency (with human-readable labels in verdict notes). Schema: work-plan.schema.json. Example scripts: contrib/employee-pack-example/specialists/. report.json (required) See contrib/employee-pack-example/schemas/report.schema.json. Must reference and with matching completed ids in the work plan. Prerequisite requests (exit code 2) If work cannot start, exit 2 and set in the report. The coordinator creates child jobs, blocks the parent, and unblocks when children reach . Specialists must not spawn parallel agents themselves."},{title:"Resource governor",body:"(defaults created on first coordinator run): Field Default Meaning --------------------- ------- ---------------------------------- 2 Host-wide running specialists 1 Extra cap for jobs 75 Pause dispatch when load is high 2048 Pause when free RAM is low 90 Optional gate Check live status: ."},{title:"Progress notifications",body:"In : Setting Values Effect ---------------------- ------------------------------- -------------------------------------------------- \\ \\ Phase step completion (and for minor status) \\ Start, review, done, failed, blocked, rework Examples: , ."},{title:"Coordinator workforce",body:"Onboarding, skills, performance history, human feedback, and recruitment suggestions are documented in Coordinator workforce manager."},{title:"Configuration",body:"In : Primary agent for all registry roles: Escalations (job after max rework) always notify the job owner on the allowlisted chat surface."},{title:"Example pack",body:"contrib/employee-pack-example \u2014 stub , , scripts and sample . Point your registry at real agent CLIs when ready: Then reference that wrapper from instead of the stub scripts."},{title:"Related",body:"Employee task flow \u2014 human \u2192 coordinator \u2192 specialist \u2192 accomplishment Job board feature reference System agents and Cowork \u2014 scheduled shell shifts (complementary)"}],keywords:["digital","employees","job","board","coordinator","supervisor","omnish","stays","local","execution","infrastructure","are","external","agent","clis","or","shell","wrappers","coordinated","by","three","meta-roles","plus","specialists","roles","core","competencies","always","on","specialist","contract","resource","governor","progress","notifications","workforce","configuration","example","pack","related"],relatedCommands:["/board tick","/board","/cowork","/jobs","/employee-task-flow","/board add","/ok","/board list","/board show","/board capacity","/board cancel","/board employees"]},{id:"docs-guides-docker-gateway-golden-path",path:"docs/guides/docker-gateway-golden-path.md",title:"Docker gateway \u2014 golden path (reference)",summary:"Run omnish inside a container so a deployed app gains chat-driven shell access on that box. Reference files: .",sections:[{title:"Two ways to use omnish in Docker",body:"Path When Setup in container -------------------------- ------------------------------------------------ ----------------------------------------------------------------------------------------------- Attached (recommended) Platform account; messengers linked on dashboard , , , \u2014 no QR in container Standalone No hosted layer; expert / air-gapped Persistent volume + + inside container (or on host) The compose file in implements standalone by default. Use attached env vars for the platform path (see Platform attached mode, Platform reference)."},{title:"Attached mode (implemented)",body:"Add chat-driven ops to an existing app container without Baileys inside the image: Link WhatsApp/Telegram on the platform dashboard (once per account). Set allowlist on the dashboard. Deploy with the env above; outbound HTTPS to the platform only. Legacy env aliases: , . Messengers do not run inside the container; the platform routes chat to this CLI."},{title:"Standalone reference (implemented today)",body:"What you get Pinned npm version ( ). Non-root user (uid 1000), as PID 1. HEALTHCHECK ( ) \u2014 process up only; not messenger connectivity. Build and run First-time pairing (empty volume): Then as above."},{title:"Networking",body:"Concern Guidance -------------- -------------------------------------------------------------------------------------------------- Attached Outbound HTTPS to communication layer; no Baileys in container Standalone Outbound to WhatsApp/Telegram APIs from container Tunnels Tunnel client runs in gateway namespace; see Tunnel setup from zero"},{title:"Environment",body:"Standalone today: \u2014 volume mount (compose: ) , \u2014 optional overrides Attached: , (aliases: , ) Optional /"},{title:"Security",body:"Allowlisted inbox \u2192 real shell as the container user. Protect volumes and tokens. Do not expose without TLS and auth \u2014 Webhook receiver."},{title:"See also",body:"Communication layer model Background gateway and start on boot"}],keywords:["docker","gateway","golden","path","reference","run","omnish","inside","container","so","deployed","app","gains","chat-driven","shell","access","on","that","box","files","two","ways","to","use","in","attached","mode","implemented","standalone","today","networking","environment","security","see","also"],relatedCommands:["/gateway-docker","/contrib","/architecture","/communication-layer-model","/tunnel","/telegram on","/docker-compose","/telegram apis","/tunnel-setup-from-zero","/home","/node","/features"]},{id:"docs-guides-employee-task-flow",path:"docs/guides/employee-task-flow.md",title:"Human \u2192 Coordinator \u2192 Employee \u2192 Task accomplishment",summary:"End-to-end flow for the omnish job board: how work moves from a human through the coordinator and a harnessed digital employee to supervised completion.",sections:[{title:"Four roles (not one \u201Cemployee\u201D)",body:"Role Who Responsibility --------------------------------- -------------------------------------------------- -------------------------------------------------------------------------- Human Allowlisted chat peer (job owner) Creates jobs, cancels, optional feedback after completion Coordinator Gateway loop ( ) Unblocks deps, picks assignee, acquires resource slots, dispatches harness Specialist (digital employee) External CLI from Runs inside harness; writes + Supervisor Deterministic QA: pass \u2192 , rework, or fail The coordinator does not judge quality. The supervisor does not choose the next assignee or create prerequisite jobs. That split is intentional \u2014 see Job board \u2014 separation of duties."},{title:"High-level sequence",body:""},{title:"Phase 1 \u2014 Human intake",body:"Entry: Chat \u2192 ( ) \u2192 ( ). Example: What happens: Job in with Work directory: = the human who posted the job Optional ; otherwise the coordinator picks on dispatch via plan heuristics Human can also: , , , (one coordinator cycle), , , and post-completion ."},{title:"Phase 2 \u2014 Coordinator tick (automation)",body:"Trigger: when is true, or . Each tick runs once, in this order: \u2014 \u2192 when all jobs are Notify pending jobs whose dependencies just became satisfied \u2014 diff and notify owner of phase changes (if configured) \u2014 every job \u2192 (may finish work from a previous dispatch) Dispatch at most one new job (if governor slot available): \u2014 or , deps satisfied Auto-assign via if no assignee (review \u2192 reviewer; implement/build/code \u2192 implementer; else researcher) + \u2014 skills research, communication brief, role context \u2014 harness execution \u2192 again \u2014 supervises jobs that entered during this tick (so one can dispatch and finish) Implementation sets directly from / . The status exists in the type system but is not used on the main dispatch path. Default: is while the gateway runs; status and handoff messages go to the job owner on chat."},{title:"Phase 3 \u2014 Digital employee execution (harness)",body:"The coordinator never runs directly. Load specialist from ( only; legacy alias) \u2014 phases: plan \u2192 implement \u2192 execute \u2192 test \u2192 review Env: , , , , briefing extras \u2192 Specialist marks steps (env hints or ) Specialist writes ( , , , , \u2026) Outcomes after harness exits: Exit / report Job status Next step ------------------------------------- ---------------------- ----------------------------------------------- Normal (not exit 2) Supervisor on this or next tick Exit 2 + Child jobs created; parent waits on Missing registry / bad config (stub report) Supervisor likely rework / fail When children reach , the parent unblocks and is dispatched again."},{title:"Phase 4 \u2014 Supervisor (quality gate)",body:"calls for each job. Checks include: exists and validates (steps complete, , competencies) contract (summary, , , communication, handoff) matches completed step ids in the work plan Plan lines \u2014 files must exist under the work dir Verdict \u2192 status: Verdict New status Notes ---------- ---------- --------------------------------------------------------- pass prompts feedback; may suggest recruitment rework Re-enters queue until (default 3) fail Owner notified; slot released Writes , audit rows, and performance history per assignee. Technical accomplishment = supervisor pass. Human satisfaction is separate ( )."},{title:"Phase 5 \u2014 Human closure (after `done`)",body:"Optional but closes the workforce loop: if a role-split was suggested Optional coordinator agent ( ) can propose / under \u2014 promote only after review via ."},{title:"Job status state machine",body:"Statuses: , , (reserved), , , , , , \u2014 see ."},{title:"Artifacts per job",body:"Path Writer ----------------------------------- -------------------- Harness + specialist Specialist (or stub) Supervisor Status and audit"},{title:"Not this flow",body:"is scheduled shell shifts \u2014 complementary, not the human \u2192 coordinator \u2192 specialist \u2192 supervisor pipeline."},{title:"Summary",body:"The human posts criteria to the board; the coordinator ticks to unblock, review prior handoffs, and dispatch one runnable job through the harnessed specialist CLI; the supervisor accepts or rejects into / / ; the human optionally rates satisfaction after ."}],keywords:["human","coordinator","employee","task","accomplishment","end-to-end","flow","for","the","omnish","job","board","how","work","moves","from","through","and","harnessed","digital","to","supervised","completion","four","roles","not","one","high-level","sequence","phase","intake","tick","automation","execution","harness","supervisor","quality","gate","closure","after","done","status","state","machine","artifacts","per","this","summary"],relatedCommands:["/digital-employees","/features","/job-board","/coordinator-workforce","/board","/coordinator","/employees","/supervisor","/board add","/board tick","/board feedback","/chat"]},{id:"docs-guides-interactive-cli",path:"docs/guides/interactive-cli.md",title:"Interactive terminal (`omnish i`)",summary:"Run the same commands you use in chat, but from your local terminal.",sections:[{title:"Commands",body:"Run from any directory. Your CLI session\u2019s working directory starts at (change it with or using your configured command prefix). Flags Flag Meaning ------------------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------- Sender key for chat-driven cluster commands ( or ). If omitted, a synthetic sender tied to the CLI session is used ( ). / Run a single line and exit (non-interactive). Useful for scripts."},{title:"Trust model",body:"is not gated by the inbox allowlist. Anyone who can run commands on your machine can use it\u2014it has the same trust as your login shell. Chat interfaces remain allowlisted as before."},{title:"`/sendto`: push files or plain text to WhatsApp or Telegram",body:"When is active on this machine, you can use from to choose exactly who gets files, or to send a plain chat message (not an attachment). Syntax Files: selectors are checked from the current folder. Add an optional caption after . Plain text: same destinations; message is everything after the flag (or the value). To send a file whose name looks like a flag, use an explicit path (e.g. ). Destination forms: \u2014 all WhatsApp recipients from \u2014 all Telegram recipients from \u2014 both channels (all allowlisted WA + TG recipients) \u2014 one explicit WhatsApp recipient \u2014 explicit WhatsApp recipient list \u2014 one explicit Telegram recipient (compatibility form) Selector forms: \u2014 explicit list \u2014 cwd glob \u2014 recursive cwd glob Compatibility aliases also accepted: , , , , . Examples: Common patterns ```text"}],keywords:["interactive","terminal","omnish","run","the","same","commands","you","use","in","chat","but","from","your","local","trust","model","/sendto","push","files","or","plain","text","to","whatsapp","telegram"],relatedCommands:["/help","/sendto","/sendto wa","/photo","/sendto tg","/report","/promo","/downloads","/telegram outbound","/send","/files-send-receive","/bg","/reload"]},{id:"docs-guides-platform-attached-mode",path:"docs/guides/platform-attached-mode.md",title:"Platform attached mode",summary:"Run on a laptop, server, or container while messengers stay on the hosted omnish platform (tunnel relay). You link channels once on the dashboard; each machine attaches with an account token and executes shell commands locally.",sections:[{title:"Standalone vs attached",body:"Standalone (default) Attached (platform) ---------------------------- ---------------------------------------- ------------------------------------------------------- When No platform URL + token + (or env) set Where messengers connect Same host as Hosted relay (dashboard) Link WhatsApp on the device Dashboard QR or Link Telegram on the device Dashboard bot token Allowlist \u2192 local Dashboard allowlists ( on ) Shell execution Local Local (unchanged) If platform credentials are set, does not start local Baileys/Telegram clients \u2014 it opens a WebSocket to the platform and runs commands on this host only."},{title:"Prerequisites",body:"A platform account (signup/login on your relay \u2014 see tunnel relay README). Messengers linked on the platform (not via on the device, unless you use import \u2014 see below). Your phone or Telegram id on the platform allowlist (dashboard). Outbound HTTPS from the device to the platform URL (and correct reverse-proxy routing if you self-host)."},{title:"Step 1 \u2014 Account and token",body:"On the hosted relay (e.g. ): Sign up or log in via / , or use . Copy the account token from the response or dashboard. The same token works for: Attached gateway: + Persisted config:"},{title:"Step 2 \u2014 Link messengers on the platform",body:"Do this on the platform, not on the machine that will run shell commands (unless noted). WhatsApp Option A \u2014 Dashboard (recommended, 1 minute) Open the platform dashboard ( on your relay origin). Click Link WhatsApp \u2014 a QR appears automatically. Scan with WhatsApp \u2192 Linked devices. The phone may show \u201CLogging in\u201D for a few seconds while the platform reconnects after scan (515 restart \u2014 dashboard shows Finishing link\u2026; no second QR). When connected, use Add my number to allowlist (one click) if shown. Run on your machine. Option A2 \u2014 CLI QR in terminal Scan the ASCII QR, then: To disconnect: dashboard Unlink WhatsApp or . Option B \u2014 Import from a machine that already linked locally On a machine where you ran successfully, stop . Set platform URL + token, then: ```bash omnish platform import-whatsapp"}],keywords:["platform","attached","mode","run","on","laptop","server","or","container","while","messengers","stay","the","hosted","omnish","tunnel","relay","you","link","channels","once","dashboard","each","machine","attaches","with","an","account","token","and","executes","shell","commands","locally","standalone","vs","prerequisites","step"],relatedCommands:["/help","/architecture","/communication-layer-model","/gateway-config-precedence","/contrib","/tunnel-relay","/readme","/v1","/me","/telegram clients","/login on","/tunnel","/auth"]},{id:"docs-guides-platform-reference",path:"docs/guides/platform-reference.md",title:"Platform reference (complete)",summary:"Consolidated documentation for the omnish hosted platform (tunnel relay + dashboard + attached ). For a guided walkthrough, start with Platform attached mode. For relay deployment, see contrib/tunnel-relay/README.md.",sections:[{title:"One-minute checklist",body:"Step Action ---- --------------------------------------------------------------------------------------------------------------------------------------- 1 Sign up / log in at \u2014 copy account token 2 Link WhatsApp: dashboard Link WhatsApp \u2192 scan QR \u2192 Add my number to allowlist (if offered) 3 Link Telegram: paste bot token \u2192 Link / restart Telegram \u2192 DM bot \u2192 add id under Allowlists \u2192 Save allowlists 4 On your machine: (or ) 5 then 6 From allowlisted chat: or ---"},{title:"How it works",body:"Messengers terminate on the relay (WhatsApp QR, Telegram bot, Discord/Slack bots, webhook channels, etc.). Shell runs only on machines where is attached. Policy ( , , legacy ) is stored on the platform and merged into the attached CLI via (refreshed every 5 minutes and on connect). ---"},{title:"What is persisted",body:"Requires on the relay (account data). Without MongoDB, only static relay tokens work \u2014 no dashboard accounts or persistence. Data Storage Survives relay restart --------------------------------- --------------------------------------------- -------------------------------------------- Account token, email MongoDB Yes , MongoDB Yes MongoDB Yes Derived from linked rows Yes (on ) MongoDB Yes Telegram bot token MongoDB ( ) Yes (encrypted when set) Connector secrets (generic) MongoDB Yes (AES-256-GCM at rest) WhatsApp Baileys auth files Disk Yes Connector status, linked WA phone MongoDB Yes Peer \u2192 device bindings MongoDB Yes Device slots MongoDB Yes Community catalog entries MongoDB Yes On MongoDB connect, linked connectors are restored automatically ( ). Volumes (self-hosted): \u2014 WhatsApp sessions MongoDB \u2014 accounts, allowlists, connector sessions ---"},{title:"Dashboard (`/dashboard/`)",body:"After login, the dashboard loads and hydrates all forms. Account status Shows (compat shim), , connector state per channel, allowlist counts, default device, online device count. is the primary signal for which surfaces are active; ( / / ) is derived for legacy clients. Devices Create device slots, set default device, see online/offline status. The first attached may auto-create a device. Routing Chat routing (platform attached mode) uses device labels and the account default device: Syntax Routes to -------------------------------- ----------------------------------------------------- Device with label (must be online) Device by Mongo id prefix (8+ chars) / Default device only (primary) Platform reply listing all devices (no CLI required) Rename device label ( works immediately) Set default device for unprefixed commands Rename (updates routing target): Chat: Dashboard: Devices \u2192 Rename CLI: and Labels: 1\u201364 chars, alphanumeric plus , unique per account (case-insensitive). Pin hosts with so reconnect does not create a duplicate slot under an old env label. Routing order when a message arrives: \u2014 explicit target by label or id prefix; prefix stripped before forward Unprefixed \u2014 default device only (fails with hint if primary offline) Failure replies in chat for unknown/offline targets Dashboard peer bindings ( \u2192 ) remain in the API for automation but do not affect inbound dispatch (see ADR-0013). Example fleet: Set default device in dashboard Devices tab or . Allowlists Field Format Notes --------------------- --------------------------- ------------------------------------------- WhatsApp E.164, comma-separated e.g. Telegram Numeric user ids Use bot in DM to discover your id Other linked channels Sender ids, comma-separated Stored in Save allowlists \u2192 with , , and/or (single object preserves untouched channels). Changes apply immediately (connectors reload allowlists per message). Catalog: lists all 22 channel specs including ( , , , , , ). Webhook hook URLs: (token returned when linking webhook-kind connectors). Link auth (hosted relay): Slack and Twitch use OAuth ( \u2192 provider \u2192 callback). Discord uses a bot invite URL plus . Configure , , , and on the relay (see ADR-0016). Warning: empty allowlist = any sender can run commands. Telegram connector Paste bot token from @BotFather. Link / restart Telegram \u2014 token required on first link; if already linked, empty token field reuses saved token. Users DM the bot before allowlisting to learn their numeric id. WhatsApp connector State UI ------------------ -------------------------------------------------------------------------------------------------------------- Not linked Link WhatsApp \u2014 shows QR, auto-polls until connected; Refresh / Restart pairing while QR is active After scan, WhatsApp closes with 515 \u2014 QR hidden, \u201CFinishing link\u2026\u201D (10s); normal Runtime reconnect after a drop \u2014 QR hidden Linked Green \u201Cconnected\u201D, optional Add my number to allowlist, Unlink WhatsApp Logged out / error Reconnect (unlink + new QR) Advanced: import from local via (see CLI below). Signal connector Link Signal \u2014 , poll , scan QR (device-link URI). Requires on the relay host. CLI: . Slack / Discord / Twitch (link auth) Channel Dashboard Relay env ------- ------------------------------------------------ ----------------------------------------------------------------------- Slack Connect with Slack (OAuth v2) , , Discord Add Discord \u2192 confirm I've added the bot , Twitch Connect with Twitch (OAuth2) , Manual token fields remain under Advanced in the dashboard. Success redirects to . Attached CLI snippet Shows , , optional , and . ---"},{title:"CLI reference (`omnish platform \u2026`)",body:"All commands require + (config or env). Run for the latest help text. Setup and diagnostics Command Purpose ------------------------------------------------------------------ --------------------------------------------------------- Save credentials to Same (config CLI aliases) Account, connectors, allowlist counts, online devices URL, token source, effective platform block Test WebSocket paths before Attach device (attached mode when platform creds resolve) Allowlists (platform policy) Command Purpose --------------------------------------------------- ------------------------------------- Show WhatsApp and Telegram allowlists Merge entries into allowlists Replace one or both lists Examples: Wildcard is rejected on the platform (same security model as standalone). Devices (multi-host routing) Command Purpose ------------------------------------------------ ------------------------------------------ List device id, label, online, default Rename label used by chat routing WhatsApp on platform Command Purpose --------------------------------------------------------------- ---------------------------------------------------------------------------------------- Start pairing, print ASCII QR, poll until linked (includes after scan) Remove session and auth files on platform Upload local Baileys auth from on this host Stop local before import to avoid WhatsApp session conflicts. Environment variables Canonical Also accepted Purpose --------------------- ---------------------------------------------- ------------------------- , Relay base URL , Account bearer token in config Pin device slot on attach Precedence: environment \u2192 \u2192 (if used). Config keys: ( ), ( ), ( ). ---"},{title:"HTTP API reference",body:"Base URL: relay origin (e.g. ). Auth: for most routes. Catalog browse/download routes are public (no bearer required); publish requires a bearer token. Chat commands for the catalog: Online catalog. Auth (no bearer on signup/login body) Method Path Body Response ------ --------------------------- --------------------------------------- ---------------------------------------------- POST or POST (403 if email unverified) POST POST GET \u2014 Redirect to POST POST Signup channels: requires email only (verification before token). / allow email or phone; phone signup returns a token immediately; email-only signup requires verification. SMTP env: , , , , (optional ). Account Method Path Body Response ------ ----------------------- ------------------------------------ --------------------------------------------------------------------------------------------------------------------- GET \u2014 , , , , , , , , , \u2026 PUT PUT GET \u2014 shape (example): means a bot token is stored; the token is never returned. Devices and routing Method Path Body Response ------ ------------------ ----------------------- ---------------------------------------------------- GET \u2014 POST (201) PUT \u2014 rename device PUT DELETE Telegram connector Method Path Body Response ------ ------------------------- ----------------------------------- -------------------------- PUT required on first link; omit on later calls to reuse stored token. optional; can also use . WhatsApp connector Method Path Body Response ------ -------------------------------- ---------------------------------------------------- --------------------------------------------------------------------------- POST (optional legacy ) GET \u2014 POST GET \u2014 Legacy; prefer POST Status values: , , , , , , , . After a successful QR scan, Baileys typically closes with (515) and reconnects using saved creds (no second QR). The connector reports (no ) until the new socket opens as . Signal connector Method Path Body Response ------ ------------------------------ ---- ------------------------------------------------------------------ POST GET \u2014 POST Link authentication (Slack, Discord, Twitch) Method Path Auth Response ------ ---------------------------------------- ------ --------------------------------------------------------------------------------- GET Bearer \u2014 , , GET public Redirect to dashboard ( or ) POST Bearer Catalog (community templates) Public read (no bearer). Publish requires account bearer token. Method Path Auth Query / body Response ------ -------------------------------- ------------ ----------------------------------------------------------------- ------------------------------------------------------------------------------------------------- GET optional GET optional GET optional \u2014 Full entry including POST required POST optional \u2014 Full entry; increments values: , , , . Upsert on publish is per . Max payload 32 KiB. Recipe payloads must include quoted (or custom ) in the command. WebSocket (attached CLI) Path Purpose --------------------- ------------------------------------------------------- Primary attach path Fallback when not proxied to control port Register frame: Inbound: Outbound: Reverse proxy (required paths \u2192 control port 8788) , , , , If these hit the HTTP edge (8787) instead, attach fails with WebSocket 400. Run . ---"},{title:"Telegram `/id` command",body:"Works on platform-linked and standalone bots, before the user is on the allowlist. Mode Reply hints ---------- ------------------------------------------------------------ Platform Numeric id + + dashboard / Standalone Numeric id + See Telegram integration notes. ---"},{title:"Policy in attached mode",body:"When attaches successfully: loads , , . These override local for inbound messenger policy. Host-only settings (shell, jobs, tunnels, webhooks) still come from local config. Policy refreshes every 5 minutes while attached. If fails at startup, the CLI may fall back to local allowlists until the next successful sync. You do not need on the device for platform-routed chat when platform policy loads successfully. ---"},{title:"Troubleshooting index",body:"Symptom See -------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- WebSocket 400 on attach Platform attached mode \u2014 Troubleshooting, relay README Device online, no command output Platform allowlist; relay logs; Troubleshooting \u2014 Attached Dashboard fields empty after login Requires MongoDB; hard-refresh; re-save allowlists Telegram token \u201Cgone\u201D after reload By design \u2014 token not shown; use empty field + Link to reuse Phone stuck on \u201CLogging in\u201D after scan Wait 15s \u2014 platform should show then connected; see Troubleshooting \u2014 Platform WhatsApp WhatsApp stuck / logged out Dashboard Reconnect or then link-whatsapp with correct allowlist WhatsApp LID resolution \u2014 update relay + CLI Empty allowlist surprises Empty = allow all senders ---"},{title:"See also",body:"Platform attached mode \u2014 guided setup Quick start \u2014 Path C Configuration \u2014 Platform / attached mode Docker gateway golden path Tunnel setup from zero \u2014 same account token for tunnels Communication layer \u2014 API sketch (future notes; implemented API is in this doc)"}],keywords:["platform","reference","complete","consolidated","documentation","for","the","omnish","hosted","tunnel","relay","dashboard","attached","guided","walkthrough","start","with","mode","deployment","see","contrib/tunnel-relay/readme","md","one-minute","checklist","how","it","works","what","is","persisted","/dashboard/","cli","http","api","telegram","/id","command","policy","in","troubleshooting","index","also"],relatedCommands:["/tunnel-relay","/readme","/contrib","/architecture","/communication-layer-model","/gateway-config-precedence","/telegram-integration-notes","/dashboard","/id","/slack bots","/v1","/me"]},{id:"docs-guides-quick-start",path:"docs/guides/quick-start.md",title:"Quick Start Guide - omnish",summary:"",sections:[{title:"What you\u2019ll get",body:"By the end of this page you will have: A working gateway \u2014 listening for your DMs. A first win \u2014 a successful sync command (e.g. or ) with output back in chat. A path to deep control \u2014 optional: for a PTY smoke test (see the main README)."},{title:"What is omnish?",body:"A secure CLI tool that bridges messaging platforms (WhatsApp, Telegram) to your system shell. It allows allowlisted users to: Execute shell commands directly from chat Run background jobs with streaming output Start interactive terminal sessions Transfer files between your system and chats Key differentiator: No AI layer - direct, deterministic shell access with explicit security controls."},{title:"One phone with WhatsApp",body:"You do not need a second phone. Host \u2014 omnish runs on a laptop, desktop, home server, or VPS (Linux, macOS, or Windows). It is a CLI gateway, not a phone app. Phone \u2014 you use the same WhatsApp account: scan the QR once under Linked devices, then send command DMs from that app (for example Message yourself). Linking works like WhatsApp Web: your account is the phone plus linked devices. Session files live under (see Comprehensive documentation). The phone stays the primary device; omnish on the host is an extra linked device. Keep the phone online as WhatsApp expects for multi-device; Meta\u2019s caps on linked devices still apply. Follow Path A: WhatsApp below for install, , , and . Private chats only \u2014 groups are ignored. No computer handy? You still need a machine that stays on to run ; a small VPS is typical. If you prefer not to use WhatsApp linked devices, you can use Telegram instead ( ); see Telegram integration notes. Security Treat allowlisted numbers (and Telegram ids) like remote shell passwords \u2014 only add identities you fully trust. See the main README and Security model."},{title:"Installation",body:"From npm (recommended) Requires Node.js 22.13+ ( ). Older Node fails at install or when native modules load. If install fails with a Node version error, use the guided installer (installs nvm/fnm when needed, sets default Node 22, then runs ): Or upgrade manually (nvm example): npm 11+: Advisory warnings during install are expected and non-blocking. omnish declares trusted native deps in its package manifest. macOS: If shell commands fail with , update omnish (recent versions fix permissions automatically). Manual fix: . See Troubleshooting \u2014 macOS spawn-helper. From source ```bash git clone https://github.com/eligapris/omnish.git cd omnish pnpm install"}],keywords:["quick","start","guide","omnish","what","you","ll","get","is","one","phone","with","whatsapp","installation"],relatedCommands:["/help","/wa help","/tg help","/omnish","/media","/logo-horizontal","/apps start","/readme","/auth","/comprehensive-documentation","/telegram-integration-notes","/architecture","/security","/fnm when","/raw"]},{id:"docs-guides-system-agents-and-run",path:"docs/guides/system-agents-and-run.md",title:"System agents, multi-agent, and `/run`",summary:"omnish is built for agents on your machine \u2014 not another hosted model or subscription. You run on hardware you control; from chat you drive system agents: CLIs and TUIs such as , , your own scripts, or multi-step orchestrators. Omnish forwards deterministic shell and PTY to those tools; it does not replace them.",sections:[{title:"Curated mobile notifications",body:"When running coding agents from mobile, keep sessions detached/muted and send only curated updates. Use the status format: Recommended update types: Ack (task accepted) Progress (phase milestone) Question (human input needed) Done (result summary) Blocked (failure or blocker) The repository includes a helper script: It targets , then , and falls back to . For a persistent chat agent (plain DMs queued to one long-lived process), enable and set to a wrapper that calls on milestones. The daemon receives the same notify env as ( , , ). See Chat agent daemon and ."},{title:"Default macro command (`recipesMacroDefaultCommand`)",body:"In , is the shell command used when a macro-style recipe stores a long body as a . The default targets the Claude CLI agent: Point it at any agent or orchestrator that reads the task from the environment, for example a wrapper that fans out to multiple local agents: Restart the gateway after editing (or use when supported)."},{title:"Per-chat recipes with `/run add`",body:"The stored command must reference (or your custom ). Example: Cursor / other CLI agents Example: Claude (default-class agent CLI) Patterns are the same for any binary on the gateway host: omnish only spawns the process you configure."},{title:"Optional: local model CLIs",body:"If your agent stack sometimes calls a local inference CLI (e.g. Ollama), you can still register it in a recipe \u2014 same pattern. That is optional plumbing, not the core story: Prefer scripts on disk for awkward HTTP/JSON so you are not pasting payloads into chat."},{title:"Limits and safety",body:"\u2014 see configuration. \u2014 gated built-ins; leave off unless you understand the risk. Secrets: use env vars on the gateway host or your secret store; do not paste keys into chat."},{title:"Discoverability",body:", Cowork: \u2014 scheduled and on-demand tasks, notifications, logs (name unchanged)."},{title:"See also",body:"User guide \u2014 shortcuts vs Configuration Interactive sessions Background jobs vs Cowork"}],keywords:["system","agents","multi-agent","and","/run","omnish","is","built","for","on","your","machine","not","another","hosted","model","or","subscription","you","run","hardware","control","from","chat","drive","clis","tuis","such","as","own","scripts","multi-step","orchestrators","forwards","deterministic","shell","pty","to","those","tools","it","does","replace","them","curated","mobile","notifications","default","macro","command","recipesmacrodefaultcommand","per-chat","recipes","with","add","optional","local","limits","safety","discoverability","see","also"],relatedCommands:["/run help","/apps help","/run","/apps start","/apps attach","/jobs","/cowork","/cw","/features","/sessions","/apps","/run queue","/run-queue"]},{id:"docs-guides-tunnel-setup-from-zero",path:"docs/guides/tunnel-setup-from-zero.md",title:"Tunnel setup from zero (gateway host)",summary:"This guide is for someone who wants public URLs for apps running on the machine where executes, using WhatsApp or Telegram (optional commands) or the CLI.",sections:[{title:"What runs where",body:"Piece Where Role ------------------ ------------------------------------------------------ --------------------------------------------------------------- Relay VPS (e.g. ) or your own Docker host Accepts HTTPS/WSS, forwards to connected clients omnish gateway Your PC / server \u2014 runs shell, chat commands, and tunnel client Your app Same host as the gateway (usually) , etc. The default public relay is . You can self-host from and point clients at your origin."},{title:"Checklist",body:"Install omnish on the gateway host Native modules may require a normal install (not ). See the project README. Link WhatsApp and/or Telegram, allow yourself ```bash omnish link omnish allow +YOURE164"}],keywords:["tunnel","setup","from","zero","gateway","host","this","guide","is","for","someone","who","wants","public","urls","apps","running","on","the","machine","where","executes","using","whatsapp","or","telegram","optional","commands","cli","what","runs","checklist"],relatedCommands:["/tunnel help","/tunnel login","/tunnel","/features","/tunneling","/contrib","/tunnel-relay","/docs","/testing-and-operations","/docker-gateway-golden-path","/wss","/or telegram","/auth","/signup"]},{id:"docs-guides-ui",path:"docs/guides/ui.md",title:"Browser setup UI (`omnish ui`)",summary:"Local-first configuration panel that edits the same as the CLI and chat commands. Use it when you want to finish basics from a phone on your LAN before touching WhatsApp/Telegram.",sections:[{title:"Quick start",body:"Default bind is (reachable on your LAN). The CLI prints: A setup token (saved under your data dir as ; legacy installs may have had , which is migrated automatically) URLs on localhost and discovered IPv4 LAN addresses A quick link that includes so your phone can authenticate in one tap Then open the UI in a browser, unlock with the token, edit core settings, and Save."},{title:"Run the gateway from the UI",body:"After configuration (and WhatsApp pairing if you use it), you can start the chat gateway without going back to the terminal: Under Host snapshot, use Start gateway. This is equivalent to (or ): a detached process runs with the same data directory, and stdout/stderr append to the default log file ( \u2014 the panel shows the resolved path). Stop gateway sends SIGTERM to the background gateway tracked by , same idea as (including stale pidfile cleanup when the process is already gone). Starting the gateway does not stop the setup UI. You can Stop setup server and leave the gateway running in the background. Authenticated session cookies are required (same as the rest of the panel): and . Port reclaim and stopping the UI The host writes in your data dir with the last process PID and bind port. When you start again on the same (default 3789), the new process terminates the previous one if it is still running, so the port is usually free without manual . A different does not kill another UI instance on a different port (the state file is overwritten when the new server starts). In the browser, Stop setup server (Core settings) ends the HTTP process after your session authenticates \u2014 the page will disconnect; run on the machine again to continue. Same trust as the rest of the panel: only someone with the setup token can unlock a session that can call ."},{title:"WhatsApp pairing (QR in the browser)",body:"You can link this host to WhatsApp without a terminal QR: Stop the gateway on this machine if it is running: use Stop gateway in the UI, , or stop your service. The UI checks for a live process and refuses browser pairing while it is present \u2014 two Baileys clients must not use the same directory at once. Unlock the UI with your setup token. Under Host snapshot, open WhatsApp pairing (QR) and choose Start QR pairing. On your phone: WhatsApp \u2192 Settings \u2192 Linked devices \u2192 Link a device, then scan the QR shown in the browser. If this host is already linked and you need a fresh login, enable Replace session (clears saved WhatsApp auth on disk, same idea as ) and confirm. You can still use from a shell on the host if you prefer the terminal QR. Pairing events are delivered over an authenticated same-origin event stream ( ) after you call . Traffic remains plain HTTP on the LAN \u2014 same trust model as the rest of the UI."},{title:"Security model (read this)",body:"Same trust as editing config on disk. Anyone who can change settings here could affect gateway behavior after reload/restart. LAN exposure: binding to all interfaces means anyone on your Wi\u2011Fi/Ethernet who can reach the port must not guess the token. Treat the token like a password. Not HTTPS: traffic is plain HTTP on your network segment v1. Run behind a reverse proxy with TLS only if you extend exposure beyond the LAN. Reduce exposure \u2014 loopback only (no phone from LAN). Firewall \u2014 block TCP 3789 (or your ) from the WAN side of your router. Guest Wi\u2011Fi \u2014 avoid running on networks shared with untrusted devices."},{title:"Flags",body:"Flag Meaning ---------------------------------- ----------------------------------------------------------- / Bind address (default ). / TCP port (default ). / Set or rotate the setup token (stored in )."},{title:"Environment",body:"Variable Meaning ------------------------- ------------------------------------------------------------------------------------------- Absolute path to a directory containing (advanced override for custom builds). Legacy alias for ."},{title:"Build note for contributors",body:"The UI is built into during / . If static files are missing, run the repo build from the project root so Vite runs before the esbuild CLI bundle."}],keywords:["browser","setup","ui","omnish","local-first","configuration","panel","that","edits","the","same","as","cli","and","chat","commands","use","it","when","you","want","to","finish","basics","from","phone","on","your","lan","before","touching","whatsapp/telegram","quick","start","run","gateway","whatsapp","pairing","qr","in","security","model","read","this","flags","environment","build","note","for","contributors"],relatedCommands:["/help","/configuration","/config set","/telegram","/stderr append","/logs","/gateway","/api","/start","/stop","/shutdown","/wa","/link"]},{id:"docs-guides-user-guide",path:"docs/guides/user-guide.md",title:"User Guide - omnish",summary:"Complete manual for using omnish's features.",sections:[{title:"Table of Contents",body:"Introduction Basic Shell Commands Background Jobs Interactive Sessions Terminal interactive ( ) File Transfer System Commands User Shortcuts User shortcuts vs recipes Cowork (scheduled tasks) Working with Multiple Platforms Configuration Management Chat LLM fallback (optional) Best Practices"},{title:"Introduction",body:"omnish bridges your messaging chats to your system shell with these main execution surfaces: Sync Shell: Immediate command execution (prefix: ) Background Jobs: Asynchronous commands with streaming output ( ) Cowork: Saved commands on a schedule or on demand ( ) while the gateway runs \u2014 see Cowork (scheduled tasks) Interactive Sessions: Terminal sessions via PTY ( ) Terminal REPL ( ): The same slash/ interface in your local shell\u2014see Interactive terminal (not gated by the inbox allowlist; same trust as your login session). Each chat maintains its own working directory and state, making it perfect for team workflows and personal use. The CLI REPL uses a dedicated session key and starts in your current directory when you launch ."},{title:"Basic Shell Commands",body:"Synchronous Execution Commands prefixed with execute immediately in the shell: Working Directories Each chat maintains its own working directory: Use to change directories Changes persist across commands View current directory with Command Prefix The prefix can be customized in : Without prefix, use free shell mode (see below)."},{title:"Background Jobs",body:"Start long-running commands without blocking your chat. Full detail: Background jobs. Starting Jobs Job Management Job IDs are 8-character hex strings; you can also assign a name with (or ). / / accept either form. Use (or ) to get a chat message when the job exits. Job Output and limits Logs persist under with matching . Jobs use the chat session cwd; they inherit the gateway process environment (not a prior from another shell). applies to sync commands only, not to children."},{title:"Interactive Sessions",body:'Start full terminal sessions in your chat: Starting Sessions Session Management Session Features Maximum 5 sessions per chat (configurable) One session "attached" at a time Plain text goes to attached session Output debounced and chunked ANSI codes can be preserved Session Commands Session Lifecycle'},{title:"Terminal interactive (`omnish i`)",body:"From a terminal on the same machine, run (or ) to type the same commands you would send in WhatsApp or Telegram: , , , , , etc. Trust: The inbox allowlist does not apply\u2014only local OS users who can run processes as you can start . Outbound files: Use or while is active; see Interactive terminal and Files send/receive. Media: , , \u2014 download, transcribe, and edit media (yt-dlp + ffmpeg + optional Whisper) \u2014 see media commands. Jobs: in this REPL belongs only to this process, not to the gateway\u2019s job list. Full reference: Interactive terminal."},{title:"File Transfer",body:'Sending Files Receiving Files When someone sends media files: Files are automatically downloaded Saved to organized directory structure Reply with "Saved: /path/to/file" File Location Control'},{title:"System Commands",body:"Gateway Control Notes: Aliases: and work like . is accepted as an alias for . prints command help. Service management from chat and are gated by (same trust as shell). Allowlist Management Cluster (optional, chat-driven) When is true on multiple machines linked to the same WhatsApp number, only the active host replies. Coordination flows through the chat itself \u2014 no shared file, no Syncthing. Shorthand: \u2026 and the long form \u2026 (only the exact token matches; does not). See Cluster and chat configuration. Config from chat View or change many keys without SSH (same trust as shell): Allowlists stay / , not . Full list: Cluster and chat configuration. Other Commands"},{title:"User Shortcuts",body:"Create chat-specific command aliases: Creating Shortcuts Using Shortcuts Parameterized shortcuts ( ) Put in the saved command where runtime text should go. Everything after the shortcut name on the same line is substituted in (trimmed; not shell-parsed). Shortcuts without still work as bare or only (extra words on the line are ignored). Shortcuts with require input; bare or returns a usage hint. Input length follows the same cap as tasks when is set. Managing Shortcuts Scope model: / : private to this chat. / : shared on this gateway across chats. Resolution order for or : chat shortcut first, then shared."},{title:"User shortcuts vs `/run` recipes",body:"Both features are per-chat (stored on the gateway host), but they solve different problems: Shortcuts ( , , ) recipes ( , ) --------------- ---------------------------------------------------------------------------------------------------------------------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- Purpose One-line alias: expand to a saved message once (e.g. ). Optional substitutes chat text inline. Parameterized runner: inject your task text into a CLI via (and optional ), then start a detached app session (PTY) by default \u2014 use or when you want plain DMs to go to the agent. still attaches on start. Typical use Jump to a directory, repeat a favorite sync command, short macros, custom wrappers with varying text. Drive system agents (e.g. , , your orchestrators) from chat with a task each time. Invocation or ; with in the body: or . \u2014 task is required for named recipes. Shortcuts substitute in the expanded line only (no shell env). Recipes require the stored command to reference (or a custom ) and run in a PTY session. For system agents, multi-agent PTY, and , see System agents and . Queued recipe runs (same chat, one PTY at a time): or , then for status. You can also batch-enqueue from JSON with (file path, inline JSON, or an uploaded file with that caption); see Run queue for the exact JSON shape and limits. Multi-step runbooks: create a recipe with sequential steps using : Steps are separated by or newlines. Each step runs in order; if any step fails (non-zero exit), remaining steps are skipped. Prefix a step with to continue on failure: When you invoke a runbook ( ), it runs in the background and sends a formatted per-step summary when complete. Use to see the step listing. Useful management forms:"},{title:"Cowork (scheduled tasks)",body:"Cowork saves shell commands and runs them on a schedule or on demand while is active. Commands use the same shell, timeout, and byte limits as sync commands. Alias: . Default log directory: (override with ). Missed scheduled times catch up when the gateway returns (one oldest slot per tick). Disabled tasks reject until re-enabled. Conditional notifications: only sends a notification when the task fails. tracks ok/fail transitions and only alerts on flips (useful for frequent health checks). Full reference: Cowork feature doc (notifications, data files, cluster and WhatsApp caveats)."},{title:"Working with Multiple Platforms",body:"Multi-Platform Setup Configure for both platforms: Platform Differences Feature WhatsApp Telegram -------------- --------------- ----------------------------- Message Limit 3500 chars 4096 chars Format Plain text HTML support File Types All media types Photo, document, video, audio Authentication QR code Bot token Best Practices Use different users for different platforms Keep allowlists minimal Monitor usage across platforms Use platform-specific features appropriately"},{title:"Chat LLM fallback (optional)",body:"If is true in on the gateway host, plain messages that would otherwise show \u201CNo command matched\u201D are answered asynchronously by a subprocess you configure ( ). Replies go back to the same WhatsApp/Telegram chat (or to the terminal in ). Where to configure: the same file as the rest of omnish \u2014 default , or if set. You edit JSON on the machine where runs; API keys must be available in that process\u2019s environment if your wrapper needs them (restart the gateway after changing systemd/shell env). Full step-by-step instructions, stdin vs PTY, and environment forwarding: Chat LLM fallback."},{title:"Configuration Management",body:"Configuration File Location Default: Override with environment variable Legacy: Uses if doesn't exist Editing from chat Most tunables support (whitelist). See Cluster and chat configuration and Configuration guide. Key Configuration Options Environment Variables : Override data directory : Enable debug logging (legacy: ) : Override bot token"},{title:"Best Practices",body:`Security Minimal Allowlists: Only add trusted users No Wildcards: Explicit phone numbers only Monitor Usage: Regularly check allowlists Session Isolation: Each chat is isolated Performance Output Chunking: Large outputs automatically split Session Limits: Don't exceed max sessions Timeout Settings: Adjust for your use case Log Rotation: Monitor log file sizes Usage Patterns Use Sessions for Interactive Apps: Vim, Python REPL, etc. Use Jobs for Long Operations: Builds, data processing Use Shortcuts for Common Tasks: Reduce typing Organize with Working Directories: Keep related work together Troubleshooting "Not in allowlist": Check E.164 format (+15551234567) "Maximum sessions reached": Stop unused sessions Messages cut off: Check Connection issues: Use for debug logs`},{title:"Next Steps",body:"Configuration: Customize for your workflow Security: Learn about the security model Features: Explore advanced features Integration: Set up for your team"}],keywords:["user","guide","omnish","complete","manual","for","using","features","table","of","contents","introduction","basic","shell","commands","background","jobs","interactive","sessions","terminal","file","transfer","system","shortcuts","vs","/run","recipes","cowork","scheduled","tasks","working","with","multiple","platforms","chat","llm","fallback","optional","configuration","management","best","practices","next","steps"],relatedCommands:["/help","/docs help","/run","/bg","/cowork","/apps","/tmp","/path","/var","/log","/system","/features","/background-jobs","/bg npm"]}]}});function Wl(e,t){return`${e.replace(/\/$/,"")}/${t}`}var ao,Bl=w(()=>{"use strict";Vk();ao=Qk});function AO(e){return e.toLowerCase().replace(/[^a-z0-9\s/-]/g," ").split(/\s+/).filter(t=>t.length>1)}function $O(e,t,n){if(t.length===0)return null;let o=e.title.toLowerCase(),r=e.summary.toLowerCase(),s=new Set(e.keywords),i=0,a,l=n.toLowerCase().trim();l.length>=3&&o.includes(l)&&(i+=40);for(let c of t){o.includes(c)&&(i+=12),r.includes(c)&&(i+=4),s.has(c)&&(i+=6),e.path.toLowerCase().includes(c)&&(i+=3);for(let d of e.sections){let u=d.title.toLowerCase(),p=d.body.toLowerCase();u.includes(c)&&(i+=5,a??=d.title),p.includes(c)&&(i+=2,a??=d.title)}}return i<=0?null:{entry:e,score:i,matchedSection:a}}function Ul(e,t=12){let n=e.trim();if(!n)return[];let o=AO(n),r=[];for(let s of ao.entries){let i=$O(s,o,n);i&&r.push(i)}return r.sort((s,i)=>i.score-s.score||s.entry.title.localeCompare(i.entry.title)),r.slice(0,t)}function jl(e){let{entry:t,matchedSection:n}=e,o=n?`${t.summary.slice(0,120)} (${n})`:t.summary.slice(0,160);return{id:t.id,path:t.path,title:t.title,summary:o,relatedCommands:t.relatedCommands}}function xi(e){return ao.entries.find(t=>t.id===e)}function Hl(e){let t=e.replace(/\\/g,"/").replace(/^\.\//,"");return ao.entries.find(n=>n.path===t||n.id===t)}function Gl(e,t=1800){let n=[];e.summary&&n.push(e.summary);for(let r of e.sections){if(n.join(`
235
235
 
236
236
  `).length>=t)break;n.push(`${r.title}
237
237
  ${r.body.slice(0,600)}`)}let o=n.join(`