clawdbot 2026.1.4-1 → 2026.1.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (117) hide show
  1. package/CHANGELOG.md +26 -6
  2. package/README.md +26 -1
  3. package/dist/agents/pi-embedded-runner.js +2 -0
  4. package/dist/agents/pi-embedded-subscribe.js +18 -3
  5. package/dist/agents/pi-tools.js +45 -6
  6. package/dist/agents/tools/browser-tool.js +38 -89
  7. package/dist/agents/tools/cron-tool.js +8 -8
  8. package/dist/agents/workspace.js +8 -1
  9. package/dist/auto-reply/command-detection.js +26 -0
  10. package/dist/auto-reply/reply/agent-runner.js +15 -8
  11. package/dist/auto-reply/reply/commands.js +36 -25
  12. package/dist/auto-reply/reply/directive-handling.js +4 -2
  13. package/dist/auto-reply/reply/directives.js +12 -0
  14. package/dist/auto-reply/reply/session-updates.js +2 -4
  15. package/dist/auto-reply/reply.js +26 -4
  16. package/dist/browser/config.js +22 -4
  17. package/dist/browser/profiles-service.js +3 -1
  18. package/dist/browser/profiles.js +14 -3
  19. package/dist/canvas-host/a2ui/.bundle.hash +2 -0
  20. package/dist/cli/gateway-cli.js +2 -2
  21. package/dist/cli/profile.js +81 -0
  22. package/dist/cli/program.js +10 -1
  23. package/dist/cli/run-main.js +33 -0
  24. package/dist/commands/configure.js +5 -0
  25. package/dist/commands/onboard-providers.js +1 -1
  26. package/dist/commands/setup.js +4 -1
  27. package/dist/config/defaults.js +56 -0
  28. package/dist/config/io.js +47 -6
  29. package/dist/config/paths.js +2 -2
  30. package/dist/config/port-defaults.js +32 -0
  31. package/dist/config/sessions.js +3 -2
  32. package/dist/config/validation.js +2 -2
  33. package/dist/config/zod-schema.js +16 -0
  34. package/dist/discord/monitor.js +75 -266
  35. package/dist/entry.js +16 -0
  36. package/dist/gateway/call.js +8 -1
  37. package/dist/gateway/server-methods/chat.js +1 -1
  38. package/dist/gateway/server.js +14 -3
  39. package/dist/index.js +2 -2
  40. package/dist/infra/control-ui-assets.js +118 -0
  41. package/dist/infra/dotenv.js +15 -0
  42. package/dist/infra/shell-env.js +79 -0
  43. package/dist/infra/system-events.js +50 -23
  44. package/dist/macos/relay.js +8 -2
  45. package/dist/telegram/bot.js +24 -1
  46. package/dist/utils.js +8 -2
  47. package/dist/web/auto-reply.js +18 -21
  48. package/dist/web/inbound.js +5 -1
  49. package/dist/web/qr-image.js +4 -4
  50. package/dist/web/session.js +2 -3
  51. package/docs/agent.md +0 -2
  52. package/docs/assets/markdown.css +4 -1
  53. package/docs/audio.md +0 -2
  54. package/docs/clawd.md +0 -2
  55. package/docs/configuration.md +62 -3
  56. package/docs/docs.json +9 -1
  57. package/docs/faq.md +32 -7
  58. package/docs/gateway.md +28 -0
  59. package/docs/images.md +0 -2
  60. package/docs/index.md +2 -4
  61. package/docs/mac/icon.md +1 -1
  62. package/docs/nix.md +57 -11
  63. package/docs/onboarding.md +0 -2
  64. package/docs/refactor/webagent-session.md +0 -2
  65. package/docs/research/memory.md +1 -1
  66. package/docs/skills.md +0 -2
  67. package/docs/templates/AGENTS.md +2 -2
  68. package/docs/tools.md +15 -0
  69. package/docs/whatsapp.md +2 -0
  70. package/package.json +8 -16
  71. package/dist/control-ui/assets/index-BFID3yAA.css +0 -1
  72. package/dist/control-ui/assets/index-CE_axlTS.js +0 -2235
  73. package/dist/control-ui/assets/index-CE_axlTS.js.map +0 -1
  74. package/dist/control-ui/index.html +0 -15
  75. package/dist/daemon/constants.js +0 -10
  76. package/dist/daemon/launchd.js +0 -276
  77. package/dist/daemon/legacy.js +0 -63
  78. package/dist/daemon/program-args.js +0 -76
  79. package/dist/daemon/schtasks.js +0 -257
  80. package/dist/daemon/service.js +0 -60
  81. package/dist/daemon/systemd.js +0 -266
  82. package/dist/imessage/client.js +0 -165
  83. package/dist/imessage/index.js +0 -3
  84. package/dist/imessage/monitor.js +0 -272
  85. package/dist/imessage/probe.js +0 -26
  86. package/dist/imessage/send.js +0 -83
  87. package/dist/imessage/targets.js +0 -176
  88. package/dist/sessions/send-policy.js +0 -68
  89. package/dist/signal/client.js +0 -134
  90. package/dist/signal/daemon.js +0 -69
  91. package/dist/signal/index.js +0 -3
  92. package/dist/signal/monitor.js +0 -336
  93. package/dist/signal/probe.js +0 -46
  94. package/dist/signal/send.js +0 -91
  95. package/dist/slack/actions.js +0 -97
  96. package/dist/slack/index.js +0 -5
  97. package/dist/slack/monitor.js +0 -1029
  98. package/dist/slack/probe.js +0 -47
  99. package/dist/slack/send.js +0 -131
  100. package/dist/slack/token.js +0 -10
  101. package/dist/tui/commands.js +0 -74
  102. package/dist/tui/components/assistant-message.js +0 -16
  103. package/dist/tui/components/chat-log.js +0 -92
  104. package/dist/tui/components/custom-editor.js +0 -53
  105. package/dist/tui/components/selectors.js +0 -8
  106. package/dist/tui/components/tool-execution.js +0 -111
  107. package/dist/tui/components/user-message.js +0 -17
  108. package/dist/tui/gateway-chat.js +0 -140
  109. package/dist/tui/layout.js +0 -41
  110. package/dist/tui/message-list.js +0 -57
  111. package/dist/tui/theme/theme.js +0 -80
  112. package/dist/tui/theme.js +0 -25
  113. package/dist/tui/tui.js +0 -708
  114. package/dist/wizard/clack-prompter.js +0 -56
  115. package/dist/wizard/onboarding.js +0 -452
  116. package/dist/wizard/prompts.js +0 -6
  117. package/dist/wizard/session.js +0 -203
@@ -1,2235 +0,0 @@
1
- (function(){const t=document.createElement("link").relList;if(t&&t.supports&&t.supports("modulepreload"))return;for(const i of document.querySelectorAll('link[rel="modulepreload"]'))n(i);new MutationObserver(i=>{for(const a of i)if(a.type==="childList")for(const l of a.addedNodes)l.tagName==="LINK"&&l.rel==="modulepreload"&&n(l)}).observe(document,{childList:!0,subtree:!0});function s(i){const a={};return i.integrity&&(a.integrity=i.integrity),i.referrerPolicy&&(a.referrerPolicy=i.referrerPolicy),i.crossOrigin==="use-credentials"?a.credentials="include":i.crossOrigin==="anonymous"?a.credentials="omit":a.credentials="same-origin",a}function n(i){if(i.ep)return;i.ep=!0;const a=s(i);fetch(i.href,a)}})();const le=globalThis,we=le.ShadowRoot&&(le.ShadyCSS===void 0||le.ShadyCSS.nativeShadow)&&"adoptedStyleSheets"in Document.prototype&&"replace"in CSSStyleSheet.prototype,Je=Symbol(),Le=new WeakMap;let ht=class{constructor(t,s,n){if(this._$cssResult$=!0,n!==Je)throw Error("CSSResult is not constructable. Use `unsafeCSS` or `css` instead.");this.cssText=t,this.t=s}get styleSheet(){let t=this.o;const s=this.t;if(we&&t===void 0){const n=s!==void 0&&s.length===1;n&&(t=Le.get(s)),t===void 0&&((this.o=t=new CSSStyleSheet).replaceSync(this.cssText),n&&Le.set(s,t))}return t}toString(){return this.cssText}};const gt=e=>new ht(typeof e=="string"?e:e+"",void 0,Je),mt=(e,t)=>{if(we)e.adoptedStyleSheets=t.map(s=>s instanceof CSSStyleSheet?s:s.styleSheet);else for(const s of t){const n=document.createElement("style"),i=le.litNonce;i!==void 0&&n.setAttribute("nonce",i),n.textContent=s.cssText,e.appendChild(n)}},Fe=we?e=>e:e=>e instanceof CSSStyleSheet?(t=>{let s="";for(const n of t.cssRules)s+=n.cssText;return gt(s)})(e):e;const{is:pt,defineProperty:ft,getOwnPropertyDescriptor:vt,getOwnPropertyNames:bt,getOwnPropertySymbols:yt,getPrototypeOf:$t}=Object,ue=globalThis,Te=ue.trustedTypes,wt=Te?Te.emptyScript:"",St=ue.reactiveElementPolyfillSupport,H=(e,t)=>e,re={toAttribute(e,t){switch(t){case Boolean:e=e?wt:null;break;case Object:case Array:e=e==null?e:JSON.stringify(e)}return e},fromAttribute(e,t){let s=e;switch(t){case Boolean:s=e!==null;break;case Number:s=e===null?null:Number(e);break;case Object:case Array:try{s=JSON.parse(e)}catch{s=null}}return s}},Se=(e,t)=>!pt(e,t),Pe={attribute:!0,type:String,converter:re,reflect:!1,useDefault:!1,hasChanged:Se};Symbol.metadata??=Symbol("metadata"),ue.litPropertyMetadata??=new WeakMap;let U=class extends HTMLElement{static addInitializer(t){this._$Ei(),(this.l??=[]).push(t)}static get observedAttributes(){return this.finalize(),this._$Eh&&[...this._$Eh.keys()]}static createProperty(t,s=Pe){if(s.state&&(s.attribute=!1),this._$Ei(),this.prototype.hasOwnProperty(t)&&((s=Object.create(s)).wrapped=!0),this.elementProperties.set(t,s),!s.noAccessor){const n=Symbol(),i=this.getPropertyDescriptor(t,n,s);i!==void 0&&ft(this.prototype,t,i)}}static getPropertyDescriptor(t,s,n){const{get:i,set:a}=vt(this.prototype,t)??{get(){return this[s]},set(l){this[s]=l}};return{get:i,set(l){const o=i?.call(this);a?.call(this,l),this.requestUpdate(t,o,n)},configurable:!0,enumerable:!0}}static getPropertyOptions(t){return this.elementProperties.get(t)??Pe}static _$Ei(){if(this.hasOwnProperty(H("elementProperties")))return;const t=$t(this);t.finalize(),t.l!==void 0&&(this.l=[...t.l]),this.elementProperties=new Map(t.elementProperties)}static finalize(){if(this.hasOwnProperty(H("finalized")))return;if(this.finalized=!0,this._$Ei(),this.hasOwnProperty(H("properties"))){const s=this.properties,n=[...bt(s),...yt(s)];for(const i of n)this.createProperty(i,s[i])}const t=this[Symbol.metadata];if(t!==null){const s=litPropertyMetadata.get(t);if(s!==void 0)for(const[n,i]of s)this.elementProperties.set(n,i)}this._$Eh=new Map;for(const[s,n]of this.elementProperties){const i=this._$Eu(s,n);i!==void 0&&this._$Eh.set(i,s)}this.elementStyles=this.finalizeStyles(this.styles)}static finalizeStyles(t){const s=[];if(Array.isArray(t)){const n=new Set(t.flat(1/0).reverse());for(const i of n)s.unshift(Fe(i))}else t!==void 0&&s.push(Fe(t));return s}static _$Eu(t,s){const n=s.attribute;return n===!1?void 0:typeof n=="string"?n:typeof t=="string"?t.toLowerCase():void 0}constructor(){super(),this._$Ep=void 0,this.isUpdatePending=!1,this.hasUpdated=!1,this._$Em=null,this._$Ev()}_$Ev(){this._$ES=new Promise(t=>this.enableUpdating=t),this._$AL=new Map,this._$E_(),this.requestUpdate(),this.constructor.l?.forEach(t=>t(this))}addController(t){(this._$EO??=new Set).add(t),this.renderRoot!==void 0&&this.isConnected&&t.hostConnected?.()}removeController(t){this._$EO?.delete(t)}_$E_(){const t=new Map,s=this.constructor.elementProperties;for(const n of s.keys())this.hasOwnProperty(n)&&(t.set(n,this[n]),delete this[n]);t.size>0&&(this._$Ep=t)}createRenderRoot(){const t=this.shadowRoot??this.attachShadow(this.constructor.shadowRootOptions);return mt(t,this.constructor.elementStyles),t}connectedCallback(){this.renderRoot??=this.createRenderRoot(),this.enableUpdating(!0),this._$EO?.forEach(t=>t.hostConnected?.())}enableUpdating(t){}disconnectedCallback(){this._$EO?.forEach(t=>t.hostDisconnected?.())}attributeChangedCallback(t,s,n){this._$AK(t,n)}_$ET(t,s){const n=this.constructor.elementProperties.get(t),i=this.constructor._$Eu(t,n);if(i!==void 0&&n.reflect===!0){const a=(n.converter?.toAttribute!==void 0?n.converter:re).toAttribute(s,n.type);this._$Em=t,a==null?this.removeAttribute(i):this.setAttribute(i,a),this._$Em=null}}_$AK(t,s){const n=this.constructor,i=n._$Eh.get(t);if(i!==void 0&&this._$Em!==i){const a=n.getPropertyOptions(i),l=typeof a.converter=="function"?{fromAttribute:a.converter}:a.converter?.fromAttribute!==void 0?a.converter:re;this._$Em=i;const o=l.fromAttribute(s,a.type);this[i]=o??this._$Ej?.get(i)??o,this._$Em=null}}requestUpdate(t,s,n,i=!1,a){if(t!==void 0){const l=this.constructor;if(i===!1&&(a=this[t]),n??=l.getPropertyOptions(t),!((n.hasChanged??Se)(a,s)||n.useDefault&&n.reflect&&a===this._$Ej?.get(t)&&!this.hasAttribute(l._$Eu(t,n))))return;this.C(t,s,n)}this.isUpdatePending===!1&&(this._$ES=this._$EP())}C(t,s,{useDefault:n,reflect:i,wrapped:a},l){n&&!(this._$Ej??=new Map).has(t)&&(this._$Ej.set(t,l??s??this[t]),a!==!0||l!==void 0)||(this._$AL.has(t)||(this.hasUpdated||n||(s=void 0),this._$AL.set(t,s)),i===!0&&this._$Em!==t&&(this._$Eq??=new Set).add(t))}async _$EP(){this.isUpdatePending=!0;try{await this._$ES}catch(s){Promise.reject(s)}const t=this.scheduleUpdate();return t!=null&&await t,!this.isUpdatePending}scheduleUpdate(){return this.performUpdate()}performUpdate(){if(!this.isUpdatePending)return;if(!this.hasUpdated){if(this.renderRoot??=this.createRenderRoot(),this._$Ep){for(const[i,a]of this._$Ep)this[i]=a;this._$Ep=void 0}const n=this.constructor.elementProperties;if(n.size>0)for(const[i,a]of n){const{wrapped:l}=a,o=this[i];l!==!0||this._$AL.has(i)||o===void 0||this.C(i,void 0,a,o)}}let t=!1;const s=this._$AL;try{t=this.shouldUpdate(s),t?(this.willUpdate(s),this._$EO?.forEach(n=>n.hostUpdate?.()),this.update(s)):this._$EM()}catch(n){throw t=!1,this._$EM(),n}t&&this._$AE(s)}willUpdate(t){}_$AE(t){this._$EO?.forEach(s=>s.hostUpdated?.()),this.hasUpdated||(this.hasUpdated=!0,this.firstUpdated(t)),this.updated(t)}_$EM(){this._$AL=new Map,this.isUpdatePending=!1}get updateComplete(){return this.getUpdateComplete()}getUpdateComplete(){return this._$ES}shouldUpdate(t){return!0}update(t){this._$Eq&&=this._$Eq.forEach(s=>this._$ET(s,this[s])),this._$EM()}updated(t){}firstUpdated(t){}};U.elementStyles=[],U.shadowRootOptions={mode:"open"},U[H("elementProperties")]=new Map,U[H("finalized")]=new Map,St?.({ReactiveElement:U}),(ue.reactiveElementVersions??=[]).push("2.1.2");const ke=globalThis,Re=e=>e,ce=ke.trustedTypes,_e=ce?ce.createPolicy("lit-html",{createHTML:e=>e}):void 0,Ge="$lit$",_=`lit$${Math.random().toFixed(9).slice(2)}$`,Ye="?"+_,kt=`<${Ye}>`,K=document,Y=()=>K.createComment(""),z=e=>e===null||typeof e!="object"&&typeof e!="function",Ce=Array.isArray,Ct=e=>Ce(e)||typeof e?.[Symbol.iterator]=="function",pe=`[
2
- \f\r]`,B=/<(?:(!--|\/[^a-zA-Z])|(\/?[a-zA-Z][^>\s]*)|(\/?$))/g,Ie=/-->/g,Ne=/>/g,I=RegExp(`>|${pe}(?:([^\\s"'>=/]+)(${pe}*=${pe}*(?:[^
3
- \f\r"'\`<>=]|("|')|))|$)`,"g"),Ke=/'/g,Ue=/"/g,ze=/^(?:script|style|textarea|title)$/i,Mt=e=>(t,...s)=>({_$litType$:e,strings:t,values:s}),c=Mt(1),O=Symbol.for("lit-noChange"),f=Symbol.for("lit-nothing"),Oe=new WeakMap,N=K.createTreeWalker(K,129);function Ve(e,t){if(!Ce(e)||!e.hasOwnProperty("raw"))throw Error("invalid template strings array");return _e!==void 0?_e.createHTML(t):t}const At=(e,t)=>{const s=e.length-1,n=[];let i,a=t===2?"<svg>":t===3?"<math>":"",l=B;for(let o=0;o<s;o++){const r=e[o];let d,p,u=-1,v=0;for(;v<r.length&&(l.lastIndex=v,p=l.exec(r),p!==null);)v=l.lastIndex,l===B?p[1]==="!--"?l=Ie:p[1]!==void 0?l=Ne:p[2]!==void 0?(ze.test(p[2])&&(i=RegExp("</"+p[2],"g")),l=I):p[3]!==void 0&&(l=I):l===I?p[0]===">"?(l=i??B,u=-1):p[1]===void 0?u=-2:(u=l.lastIndex-p[2].length,d=p[1],l=p[3]===void 0?I:p[3]==='"'?Ue:Ke):l===Ue||l===Ke?l=I:l===Ie||l===Ne?l=B:(l=I,i=void 0);const $=l===I&&e[o+1].startsWith("/>")?" ":"";a+=l===B?r+kt:u>=0?(n.push(d),r.slice(0,u)+Ge+r.slice(u)+_+$):r+_+(u===-2?o:$)}return[Ve(e,a+(e[s]||"<?>")+(t===2?"</svg>":t===3?"</math>":"")),n]};class V{constructor({strings:t,_$litType$:s},n){let i;this.parts=[];let a=0,l=0;const o=t.length-1,r=this.parts,[d,p]=At(t,s);if(this.el=V.createElement(d,n),N.currentNode=this.el.content,s===2||s===3){const u=this.el.content.firstChild;u.replaceWith(...u.childNodes)}for(;(i=N.nextNode())!==null&&r.length<o;){if(i.nodeType===1){if(i.hasAttributes())for(const u of i.getAttributeNames())if(u.endsWith(Ge)){const v=p[l++],$=i.getAttribute(u).split(_),y=/([.?@])?(.*)/.exec(v);r.push({type:1,index:a,name:y[2],strings:$,ctor:y[1]==="."?xt:y[1]==="?"?Lt:y[1]==="@"?Ft:he}),i.removeAttribute(u)}else u.startsWith(_)&&(r.push({type:6,index:a}),i.removeAttribute(u));if(ze.test(i.tagName)){const u=i.textContent.split(_),v=u.length-1;if(v>0){i.textContent=ce?ce.emptyScript:"";for(let $=0;$<v;$++)i.append(u[$],Y()),N.nextNode(),r.push({type:2,index:++a});i.append(u[v],Y())}}}else if(i.nodeType===8)if(i.data===Ye)r.push({type:2,index:a});else{let u=-1;for(;(u=i.data.indexOf(_,u+1))!==-1;)r.push({type:7,index:a}),u+=_.length-1}a++}}static createElement(t,s){const n=K.createElement("template");return n.innerHTML=t,n}}function D(e,t,s=e,n){if(t===O)return t;let i=n!==void 0?s._$Co?.[n]:s._$Cl;const a=z(t)?void 0:t._$litDirective$;return i?.constructor!==a&&(i?._$AO?.(!1),a===void 0?i=void 0:(i=new a(e),i._$AT(e,s,n)),n!==void 0?(s._$Co??=[])[n]=i:s._$Cl=i),i!==void 0&&(t=D(e,i._$AS(e,t.values),i,n)),t}class Et{constructor(t,s){this._$AV=[],this._$AN=void 0,this._$AD=t,this._$AM=s}get parentNode(){return this._$AM.parentNode}get _$AU(){return this._$AM._$AU}u(t){const{el:{content:s},parts:n}=this._$AD,i=(t?.creationScope??K).importNode(s,!0);N.currentNode=i;let a=N.nextNode(),l=0,o=0,r=n[0];for(;r!==void 0;){if(l===r.index){let d;r.type===2?d=new ee(a,a.nextSibling,this,t):r.type===1?d=new r.ctor(a,r.name,r.strings,this,t):r.type===6&&(d=new Tt(a,this,t)),this._$AV.push(d),r=n[++o]}l!==r?.index&&(a=N.nextNode(),l++)}return N.currentNode=K,i}p(t){let s=0;for(const n of this._$AV)n!==void 0&&(n.strings!==void 0?(n._$AI(t,n,s),s+=n.strings.length-2):n._$AI(t[s])),s++}}class ee{get _$AU(){return this._$AM?._$AU??this._$Cv}constructor(t,s,n,i){this.type=2,this._$AH=f,this._$AN=void 0,this._$AA=t,this._$AB=s,this._$AM=n,this.options=i,this._$Cv=i?.isConnected??!0}get parentNode(){let t=this._$AA.parentNode;const s=this._$AM;return s!==void 0&&t?.nodeType===11&&(t=s.parentNode),t}get startNode(){return this._$AA}get endNode(){return this._$AB}_$AI(t,s=this){t=D(this,t,s),z(t)?t===f||t==null||t===""?(this._$AH!==f&&this._$AR(),this._$AH=f):t!==this._$AH&&t!==O&&this._(t):t._$litType$!==void 0?this.$(t):t.nodeType!==void 0?this.T(t):Ct(t)?this.k(t):this._(t)}O(t){return this._$AA.parentNode.insertBefore(t,this._$AB)}T(t){this._$AH!==t&&(this._$AR(),this._$AH=this.O(t))}_(t){this._$AH!==f&&z(this._$AH)?this._$AA.nextSibling.data=t:this.T(K.createTextNode(t)),this._$AH=t}$(t){const{values:s,_$litType$:n}=t,i=typeof n=="number"?this._$AC(t):(n.el===void 0&&(n.el=V.createElement(Ve(n.h,n.h[0]),this.options)),n);if(this._$AH?._$AD===i)this._$AH.p(s);else{const a=new Et(i,this),l=a.u(this.options);a.p(s),this.T(l),this._$AH=a}}_$AC(t){let s=Oe.get(t.strings);return s===void 0&&Oe.set(t.strings,s=new V(t)),s}k(t){Ce(this._$AH)||(this._$AH=[],this._$AR());const s=this._$AH;let n,i=0;for(const a of t)i===s.length?s.push(n=new ee(this.O(Y()),this.O(Y()),this,this.options)):n=s[i],n._$AI(a),i++;i<s.length&&(this._$AR(n&&n._$AB.nextSibling,i),s.length=i)}_$AR(t=this._$AA.nextSibling,s){for(this._$AP?.(!1,!0,s);t!==this._$AB;){const n=Re(t).nextSibling;Re(t).remove(),t=n}}setConnected(t){this._$AM===void 0&&(this._$Cv=t,this._$AP?.(t))}}class he{get tagName(){return this.element.tagName}get _$AU(){return this._$AM._$AU}constructor(t,s,n,i,a){this.type=1,this._$AH=f,this._$AN=void 0,this.element=t,this.name=s,this._$AM=i,this.options=a,n.length>2||n[0]!==""||n[1]!==""?(this._$AH=Array(n.length-1).fill(new String),this.strings=n):this._$AH=f}_$AI(t,s=this,n,i){const a=this.strings;let l=!1;if(a===void 0)t=D(this,t,s,0),l=!z(t)||t!==this._$AH&&t!==O,l&&(this._$AH=t);else{const o=t;let r,d;for(t=a[0],r=0;r<a.length-1;r++)d=D(this,o[n+r],s,r),d===O&&(d=this._$AH[r]),l||=!z(d)||d!==this._$AH[r],d===f?t=f:t!==f&&(t+=(d??"")+a[r+1]),this._$AH[r]=d}l&&!i&&this.j(t)}j(t){t===f?this.element.removeAttribute(this.name):this.element.setAttribute(this.name,t??"")}}class xt extends he{constructor(){super(...arguments),this.type=3}j(t){this.element[this.name]=t===f?void 0:t}}class Lt extends he{constructor(){super(...arguments),this.type=4}j(t){this.element.toggleAttribute(this.name,!!t&&t!==f)}}class Ft extends he{constructor(t,s,n,i,a){super(t,s,n,i,a),this.type=5}_$AI(t,s=this){if((t=D(this,t,s,0)??f)===O)return;const n=this._$AH,i=t===f&&n!==f||t.capture!==n.capture||t.once!==n.once||t.passive!==n.passive,a=t!==f&&(n===f||i);i&&this.element.removeEventListener(this.name,this,n),a&&this.element.addEventListener(this.name,this,t),this._$AH=t}handleEvent(t){typeof this._$AH=="function"?this._$AH.call(this.options?.host??this.element,t):this._$AH.handleEvent(t)}}class Tt{constructor(t,s,n){this.element=t,this.type=6,this._$AN=void 0,this._$AM=s,this.options=n}get _$AU(){return this._$AM._$AU}_$AI(t){D(this,t)}}const Pt=ke.litHtmlPolyfillSupport;Pt?.(V,ee),(ke.litHtmlVersions??=[]).push("3.3.2");const Rt=(e,t,s)=>{const n=s?.renderBefore??t;let i=n._$litPart$;if(i===void 0){const a=s?.renderBefore??null;n._$litPart$=i=new ee(t.insertBefore(Y(),a),a,void 0,s??{})}return i._$AI(e),i};const Me=globalThis;class W extends U{constructor(){super(...arguments),this.renderOptions={host:this},this._$Do=void 0}createRenderRoot(){const t=super.createRenderRoot();return this.renderOptions.renderBefore??=t.firstChild,t}update(t){const s=this.render();this.hasUpdated||(this.renderOptions.isConnected=this.isConnected),super.update(t),this._$Do=Rt(s,this.renderRoot,this.renderOptions)}connectedCallback(){super.connectedCallback(),this._$Do?.setConnected(!0)}disconnectedCallback(){super.disconnectedCallback(),this._$Do?.setConnected(!1)}render(){return O}}W._$litElement$=!0,W.finalized=!0,Me.litElementHydrateSupport?.({LitElement:W});const _t=Me.litElementPolyfillSupport;_t?.({LitElement:W});(Me.litElementVersions??=[]).push("4.2.2");const It=e=>(t,s)=>{s!==void 0?s.addInitializer(()=>{customElements.define(e,t)}):customElements.define(e,t)};const Nt={attribute:!0,type:String,converter:re,reflect:!1,hasChanged:Se},Kt=(e=Nt,t,s)=>{const{kind:n,metadata:i}=s;let a=globalThis.litPropertyMetadata.get(i);if(a===void 0&&globalThis.litPropertyMetadata.set(i,a=new Map),n==="setter"&&((e=Object.create(e)).wrapped=!0),a.set(s.name,e),n==="accessor"){const{name:l}=s;return{set(o){const r=t.get.call(this);t.set.call(this,o),this.requestUpdate(l,r,e,!0,o)},init(o){return o!==void 0&&this.C(l,void 0,e,o),o}}}if(n==="setter"){const{name:l}=s;return function(o){const r=this[l];t.call(this,o),this.requestUpdate(l,r,e,!0,o)}}throw Error("Unsupported decorator location: "+n)};function Ut(e){return(t,s)=>typeof s=="object"?Kt(e,t,s):((n,i,a)=>{const l=i.hasOwnProperty(a);return i.constructor.createProperty(a,n),l?Object.getOwnPropertyDescriptor(i,a):void 0})(e,t,s)}function m(e){return Ut({...e,state:!0,attribute:!1})}function De(e){e[6]=e[6]&15|64,e[8]=e[8]&63|128;let t="";for(let s=0;s<e.length;s++)t+=e[s].toString(16).padStart(2,"0");return`${t.slice(0,8)}-${t.slice(8,12)}-${t.slice(12,16)}-${t.slice(16,20)}-${t.slice(20)}`}function Ot(){const e=new Uint8Array(16),t=Date.now();for(let s=0;s<e.length;s++)e[s]=Math.floor(Math.random()*256);return e[0]^=t&255,e[1]^=t>>>8&255,e[2]^=t>>>16&255,e[3]^=t>>>24&255,e}function Qe(e=globalThis.crypto){if(e&&typeof e.randomUUID=="function")return e.randomUUID();if(e&&typeof e.getRandomValues=="function"){const t=new Uint8Array(16);return e.getRandomValues(t),De(t)}return De(Ot())}class Dt{constructor(t){this.opts=t,this.ws=null,this.pending=new Map,this.closed=!1,this.lastSeq=null,this.backoffMs=800}start(){this.closed=!1,this.connect()}stop(){this.closed=!0,this.ws?.close(),this.ws=null,this.flushPending(new Error("gateway client stopped"))}get connected(){return this.ws?.readyState===WebSocket.OPEN}connect(){this.closed||(this.ws=new WebSocket(this.opts.url),this.ws.onopen=()=>this.sendConnect(),this.ws.onmessage=t=>this.handleMessage(String(t.data??"")),this.ws.onclose=t=>{const s=String(t.reason??"");this.ws=null,this.flushPending(new Error(`gateway closed (${t.code}): ${s}`)),this.opts.onClose?.({code:t.code,reason:s}),this.scheduleReconnect()},this.ws.onerror=()=>{})}scheduleReconnect(){if(this.closed)return;const t=this.backoffMs;this.backoffMs=Math.min(this.backoffMs*1.7,15e3),window.setTimeout(()=>this.connect(),t)}flushPending(t){for(const[,s]of this.pending)s.reject(t);this.pending.clear()}sendConnect(){const t=this.opts.token||this.opts.password?{token:this.opts.token,password:this.opts.password}:void 0,s={minProtocol:2,maxProtocol:2,client:{name:this.opts.clientName??"clawdbot-control-ui",version:this.opts.clientVersion??"dev",platform:this.opts.platform??navigator.platform??"web",mode:this.opts.mode??"webchat",instanceId:this.opts.instanceId},caps:[],auth:t,userAgent:navigator.userAgent,locale:navigator.language};this.request("connect",s).then(n=>{this.backoffMs=800,this.opts.onHello?.(n)}).catch(()=>{this.ws?.close(1008,"connect failed")})}handleMessage(t){let s;try{s=JSON.parse(t)}catch{return}const n=s;if(n.type==="event"){const i=s,a=typeof i.seq=="number"?i.seq:null;a!==null&&(this.lastSeq!==null&&a>this.lastSeq+1&&this.opts.onGap?.({expected:this.lastSeq+1,received:a}),this.lastSeq=a),this.opts.onEvent?.(i);return}if(n.type==="res"){const i=s,a=this.pending.get(i.id);if(!a)return;this.pending.delete(i.id),i.ok?a.resolve(i.payload):a.reject(new Error(i.error?.message??"request failed"));return}}request(t,s){if(!this.ws||this.ws.readyState!==WebSocket.OPEN)return Promise.reject(new Error("gateway not connected"));const n=Qe(),i={type:"req",id:n,method:t,params:s},a=new Promise((l,o)=>{this.pending.set(n,{resolve:r=>l(r),reject:o})});return this.ws.send(JSON.stringify(i)),a}}const Xe="clawdbot.control.settings.v1";function jt(){const t={gatewayUrl:`${location.protocol==="https:"?"wss":"ws"}://${location.host}`,token:"",sessionKey:"main",theme:"system"};try{const s=localStorage.getItem(Xe);if(!s)return t;const n=JSON.parse(s);return{gatewayUrl:typeof n.gatewayUrl=="string"&&n.gatewayUrl.trim()?n.gatewayUrl.trim():t.gatewayUrl,token:typeof n.token=="string"?n.token:t.token,sessionKey:typeof n.sessionKey=="string"&&n.sessionKey.trim()?n.sessionKey.trim():t.sessionKey,theme:n.theme==="light"||n.theme==="dark"||n.theme==="system"?n.theme:t.theme}}catch{return t}}function qt(e){localStorage.setItem(Xe,JSON.stringify(e))}const Bt=[{label:"Chat",tabs:["chat"]},{label:"Control",tabs:["overview","connections","instances","sessions","cron"]},{label:"Agent",tabs:["skills","nodes"]},{label:"Settings",tabs:["config","debug"]}],Ze={overview:"/overview",connections:"/connections",instances:"/instances",sessions:"/sessions",cron:"/cron",skills:"/skills",nodes:"/nodes",chat:"/chat",config:"/config",debug:"/debug"},et=new Map(Object.entries(Ze).map(([e,t])=>[t,e]));function Ae(e){if(!e)return"";let t=e.trim();return t.startsWith("/")||(t=`/${t}`),t==="/"?"":(t.endsWith("/")&&(t=t.slice(0,-1)),t)}function Q(e){if(!e)return"/";let t=e.trim();return t.startsWith("/")||(t=`/${t}`),t.length>1&&t.endsWith("/")&&(t=t.slice(0,-1)),t}function tt(e,t=""){const s=Ae(t),n=Ze[e];return s?`${s}${n}`:n}function je(e,t=""){const s=Ae(t);let n=e||"/";s&&(n===s?n="/":n.startsWith(`${s}/`)&&(n=n.slice(s.length)));let i=Q(n).toLowerCase();return i.endsWith("/index.html")&&(i="/"),i==="/"?"chat":et.get(i)??null}function Ht(e){let t=Q(e);if(t.endsWith("/index.html")&&(t=Q(t.slice(0,-11))),t==="/")return"";const s=t.split("/").filter(Boolean);if(s.length===0)return"";for(let n=0;n<s.length;n++){const i=`/${s.slice(n).join("/")}`.toLowerCase();if(et.has(i)){const a=s.slice(0,n);return a.length?`/${a.join("/")}`:""}}return`/${s.join("/")}`}function nt(e){switch(e){case"overview":return"Overview";case"connections":return"Connections";case"instances":return"Instances";case"sessions":return"Sessions";case"cron":return"Cron Jobs";case"skills":return"Skills";case"nodes":return"Nodes";case"chat":return"Chat";case"config":return"Config";case"debug":return"Debug";default:return"Control"}}function Wt(e){switch(e){case"overview":return"Gateway status, entry points, and a fast health read.";case"connections":return"Link providers and keep transport settings in sync.";case"instances":return"Presence beacons from connected clients and nodes.";case"sessions":return"Inspect active sessions and adjust per-session defaults.";case"cron":return"Schedule wakeups and recurring agent runs.";case"skills":return"Manage skill availability and API key injection.";case"nodes":return"Paired devices, capabilities, and command exposure.";case"chat":return"Direct gateway chat session for quick interventions.";case"config":return"Edit ~/.clawdbot/clawdbot.json safely.";case"debug":return"Gateway snapshots, events, and manual RPC calls.";default:return""}}const Jt={emoji:"🧩",detailKeys:["command","path","url","targetUrl","targetId","ref","element","node","nodeId","jobId","requestId","to","channelId","guildId","userId","name","query","pattern","messageId"]},Gt={bash:{emoji:"🛠️",title:"Bash",detailKeys:["command"]},process:{emoji:"🧰",title:"Process",detailKeys:["sessionId"]},read:{emoji:"📖",title:"Read",detailKeys:["path"]},write:{emoji:"✍️",title:"Write",detailKeys:["path"]},edit:{emoji:"📝",title:"Edit",detailKeys:["path"]},attach:{emoji:"📎",title:"Attach",detailKeys:["path","url","fileName"]},browser:{emoji:"🌐",title:"Browser",actions:{status:{label:"status"},start:{label:"start"},stop:{label:"stop"},tabs:{label:"tabs"},open:{label:"open",detailKeys:["targetUrl"]},focus:{label:"focus",detailKeys:["targetId"]},close:{label:"close",detailKeys:["targetId"]},snapshot:{label:"snapshot",detailKeys:["targetUrl","targetId","ref","element","format"]},screenshot:{label:"screenshot",detailKeys:["targetUrl","targetId","ref","element"]},navigate:{label:"navigate",detailKeys:["targetUrl","targetId"]},console:{label:"console",detailKeys:["level","targetId"]},pdf:{label:"pdf",detailKeys:["targetId"]},upload:{label:"upload",detailKeys:["paths","ref","inputRef","element","targetId"]},dialog:{label:"dialog",detailKeys:["accept","promptText","targetId"]},act:{label:"act",detailKeys:["request.kind","request.ref","request.selector","request.text","request.value"]}}},canvas:{emoji:"🖼️",title:"Canvas",actions:{present:{label:"present",detailKeys:["target","node","nodeId"]},hide:{label:"hide",detailKeys:["node","nodeId"]},navigate:{label:"navigate",detailKeys:["url","node","nodeId"]},eval:{label:"eval",detailKeys:["javaScript","node","nodeId"]},snapshot:{label:"snapshot",detailKeys:["format","node","nodeId"]},a2ui_push:{label:"A2UI push",detailKeys:["jsonlPath","node","nodeId"]},a2ui_reset:{label:"A2UI reset",detailKeys:["node","nodeId"]}}},nodes:{emoji:"📱",title:"Nodes",actions:{status:{label:"status"},describe:{label:"describe",detailKeys:["node","nodeId"]},pending:{label:"pending"},approve:{label:"approve",detailKeys:["requestId"]},reject:{label:"reject",detailKeys:["requestId"]},notify:{label:"notify",detailKeys:["node","nodeId","title","body"]},camera_snap:{label:"camera snap",detailKeys:["node","nodeId","facing","deviceId"]},camera_list:{label:"camera list",detailKeys:["node","nodeId"]},camera_clip:{label:"camera clip",detailKeys:["node","nodeId","facing","duration","durationMs"]},screen_record:{label:"screen record",detailKeys:["node","nodeId","duration","durationMs","fps","screenIndex"]}}},cron:{emoji:"⏰",title:"Cron",actions:{status:{label:"status"},list:{label:"list"},add:{label:"add",detailKeys:["job.name","job.id","job.schedule","job.cron"]},update:{label:"update",detailKeys:["jobId"]},remove:{label:"remove",detailKeys:["jobId"]},run:{label:"run",detailKeys:["jobId"]},runs:{label:"runs",detailKeys:["jobId"]},wake:{label:"wake",detailKeys:["text","mode"]}}},gateway:{emoji:"🔌",title:"Gateway",actions:{restart:{label:"restart",detailKeys:["reason","delayMs"]}}},whatsapp_login:{emoji:"🟢",title:"WhatsApp Login",actions:{start:{label:"start"},wait:{label:"wait"}}},discord:{emoji:"💬",title:"Discord",actions:{react:{label:"react",detailKeys:["channelId","messageId","emoji"]},reactions:{label:"reactions",detailKeys:["channelId","messageId"]},sticker:{label:"sticker",detailKeys:["to","stickerIds"]},poll:{label:"poll",detailKeys:["question","to"]},permissions:{label:"permissions",detailKeys:["channelId"]},readMessages:{label:"read messages",detailKeys:["channelId","limit"]},sendMessage:{label:"send",detailKeys:["to","content"]},editMessage:{label:"edit",detailKeys:["channelId","messageId"]},deleteMessage:{label:"delete",detailKeys:["channelId","messageId"]},threadCreate:{label:"thread create",detailKeys:["channelId","name"]},threadList:{label:"thread list",detailKeys:["guildId","channelId"]},threadReply:{label:"thread reply",detailKeys:["channelId","content"]},pinMessage:{label:"pin",detailKeys:["channelId","messageId"]},unpinMessage:{label:"unpin",detailKeys:["channelId","messageId"]},listPins:{label:"list pins",detailKeys:["channelId"]},searchMessages:{label:"search",detailKeys:["guildId","content"]},memberInfo:{label:"member",detailKeys:["guildId","userId"]},roleInfo:{label:"roles",detailKeys:["guildId"]},emojiList:{label:"emoji list",detailKeys:["guildId"]},roleAdd:{label:"role add",detailKeys:["guildId","userId","roleId"]},roleRemove:{label:"role remove",detailKeys:["guildId","userId","roleId"]},channelInfo:{label:"channel",detailKeys:["channelId"]},channelList:{label:"channels",detailKeys:["guildId"]},voiceStatus:{label:"voice",detailKeys:["guildId","userId"]},eventList:{label:"events",detailKeys:["guildId"]},eventCreate:{label:"event create",detailKeys:["guildId","name"]},timeout:{label:"timeout",detailKeys:["guildId","userId"]},kick:{label:"kick",detailKeys:["guildId","userId"]},ban:{label:"ban",detailKeys:["guildId","userId"]}}},slack:{emoji:"💬",title:"Slack",actions:{react:{label:"react",detailKeys:["channelId","messageId","emoji"]},reactions:{label:"reactions",detailKeys:["channelId","messageId"]},sendMessage:{label:"send",detailKeys:["to","content"]},editMessage:{label:"edit",detailKeys:["channelId","messageId"]},deleteMessage:{label:"delete",detailKeys:["channelId","messageId"]},readMessages:{label:"read messages",detailKeys:["channelId","limit"]},pinMessage:{label:"pin",detailKeys:["channelId","messageId"]},unpinMessage:{label:"unpin",detailKeys:["channelId","messageId"]},listPins:{label:"list pins",detailKeys:["channelId"]},memberInfo:{label:"member",detailKeys:["userId"]},emojiList:{label:"emoji list"}}}},Yt={fallback:Jt,tools:Gt},st=Yt,qe=st.fallback??{emoji:"🧩"},zt=st.tools??{};function Vt(e){return(e??"tool").trim()}function Qt(e){const t=e.replace(/_/g," ").trim();return t?t.split(/\s+/).map(s=>s.length<=2&&s.toUpperCase()===s?s:`${s.at(0)?.toUpperCase()??""}${s.slice(1)}`).join(" "):"Tool"}function Xt(e){const t=e?.trim();if(t)return t.replace(/_/g," ")}function it(e){if(e!=null){if(typeof e=="string"){const t=e.trim();if(!t)return;const s=t.split(/\r?\n/)[0]?.trim()??"";return s?s.length>160?`${s.slice(0,157)}…`:s:void 0}if(typeof e=="number"||typeof e=="boolean")return String(e);if(Array.isArray(e)){const t=e.map(n=>it(n)).filter(n=>!!n);if(t.length===0)return;const s=t.slice(0,3).join(", ");return t.length>3?`${s}…`:s}}}function Zt(e,t){if(!e||typeof e!="object")return;let s=e;for(const n of t.split(".")){if(!n||!s||typeof s!="object")return;s=s[n]}return s}function en(e,t){for(const s of t){const n=Zt(e,s),i=it(n);if(i)return i}}function tn(e){if(!e||typeof e!="object")return;const t=e,s=typeof t.path=="string"?t.path:void 0;if(!s)return;const n=typeof t.offset=="number"?t.offset:void 0,i=typeof t.limit=="number"?t.limit:void 0;return n!==void 0&&i!==void 0?`${s}:${n}-${n+i}`:s}function nn(e){if(!e||typeof e!="object")return;const t=e;return typeof t.path=="string"?t.path:void 0}function sn(e,t){if(!(!e||!t))return e.actions?.[t]??void 0}function an(e){const t=Vt(e.name),s=t.toLowerCase(),n=zt[s],i=n?.emoji??qe.emoji??"🧩",a=n?.title??Qt(t),l=n?.label??t,o=e.args&&typeof e.args=="object"?e.args.action:void 0,r=typeof o=="string"?o.trim():void 0,d=sn(n,r),p=Xt(d?.label??r);let u;s==="read"&&(u=tn(e.args)),!u&&(s==="write"||s==="edit"||s==="attach")&&(u=nn(e.args));const v=d?.detailKeys??n?.detailKeys??qe.detailKeys??[];return!u&&v.length>0&&(u=en(e.args,v)),!u&&e.meta&&(u=e.meta),u&&(u=on(u)),{name:t,emoji:i,title:a,label:l,verb:p,detail:u}}function ln(e){const t=[];if(e.verb&&t.push(e.verb),e.detail&&t.push(e.detail),t.length!==0)return t.join(" · ")}function on(e){return e&&e.replace(/\/Users\/[^/]+/g,"~").replace(/\/home\/[^/]+/g,"~")}function rn(e){const t=e.connected,s=e.canSend&&!e.sending,n=cn(e.sessionKey,e.sessions),i=e.connected?e.canSend?"Message (⌘↩ to send)":"Connect an iOS/Android node to enable Web Chat + Talk…":"Connect to the gateway to start chatting…";return c`
4
- <section class="card chat">
5
- <div class="chat-header">
6
- <div class="chat-header__left">
7
- <label class="field chat-session">
8
- <span>Session Key</span>
9
- <select
10
- .value=${e.sessionKey}
11
- ?disabled=${!t}
12
- @change=${a=>e.onSessionKeyChange(a.target.value)}
13
- >
14
- ${n.map(a=>c`<option value=${a.key}>
15
- ${a.displayName??a.key}
16
- </option>`)}
17
- </select>
18
- </label>
19
- <button
20
- class="btn"
21
- ?disabled=${e.loading||!t}
22
- @click=${e.onRefresh}
23
- >
24
- ${e.loading?"Loading…":"Refresh"}
25
- </button>
26
- </div>
27
- <div class="chat-header__right">
28
- <div class="muted">Thinking: ${e.thinkingLevel??"inherit"}</div>
29
- </div>
30
- </div>
31
-
32
- ${e.disabledReason?c`<div class="callout" style="margin-top: 12px;">
33
- ${e.disabledReason}
34
- </div>`:f}
35
-
36
- <div class="chat-thread" role="log" aria-live="polite">
37
- ${e.loading?c`<div class="muted">Loading chat…</div>`:f}
38
- ${e.messages.map(a=>Be(a))}
39
- ${e.stream?Be({role:"assistant",content:[{type:"text",text:e.stream}],timestamp:Date.now()},{streaming:!0}):f}
40
- </div>
41
-
42
- <div class="chat-compose">
43
- <label class="field chat-compose__field">
44
- <span>Message</span>
45
- <textarea
46
- .value=${e.draft}
47
- ?disabled=${!e.canSend}
48
- @keydown=${a=>{a.key==="Enter"&&(!a.metaKey&&!a.ctrlKey||(a.preventDefault(),s&&e.onSend()))}}
49
- @input=${a=>e.onDraftChange(a.target.value)}
50
- placeholder=${i}
51
- ></textarea>
52
- </label>
53
- <div class="row chat-compose__actions">
54
- <button
55
- class="btn primary"
56
- ?disabled=${!e.canSend||e.sending}
57
- @click=${e.onSend}
58
- >
59
- ${e.sending?"Sending…":"Send"}
60
- </button>
61
- </div>
62
- </div>
63
- </section>
64
- `}function cn(e,t){const n=Date.now()-1440*60*1e3,a=[...Array.isArray(t?.sessions)?t?.sessions??[]:[]].sort((v,$)=>($.updatedAt??0)-(v.updatedAt??0)),l=[],o=new Set;for(const v of a)o.has(v.key)||(o.add(v.key),!((v.updatedAt??0)<n)&&l.push(v));const r=[],d=new Set,p="main",u=a.find(v=>v.key===p);u?(r.push(u),d.add(p)):e===p&&(r.push({key:p,updatedAt:null}),d.add(p));for(const v of l)d.has(v.key)||(r.push(v),d.add(v.key));return d.has(e)||r.push({key:e,updatedAt:null}),r}function Be(e,t){const s=e,n=typeof s.role=="string"?s.role:"unknown",i=dn(e),a=i.length>0,l=lt(e),o=at(e),r=typeof s.content=="string"?s.content:null,d=a?null:JSON.stringify(e,null,2),p=l?null:o?.trim()?o:r?.trim()?r:d,u=typeof s.timestamp=="number"?new Date(s.timestamp).toLocaleTimeString():"",v=n==="assistant"?"assistant":n==="user"?"user":"other",$=n==="assistant"?"Assistant":n==="user"?"You":n;return c`
65
- <div class="chat-line ${v}">
66
- <div class="chat-msg">
67
- <div class="chat-bubble ${t?.streaming?"streaming":""}">
68
- ${p?c`<div class="chat-text">${p}</div>`:f}
69
- ${i.map(y=>un(y))}
70
- </div>
71
- <div class="chat-stamp mono">
72
- ${$}${u?c` · ${u}`:f}
73
- </div>
74
- </div>
75
- </div>
76
- `}function at(e){const t=e,s=t.content;if(typeof s=="string")return s;if(Array.isArray(s)){const n=s.map(i=>{const a=i;return a.type==="text"&&typeof a.text=="string"?a.text:null}).filter(i=>typeof i=="string");if(n.length>0)return n.join(`
77
- `)}return typeof t.text=="string"?t.text:null}function dn(e){const t=e,s=hn(t.content),n=[];for(const i of s){const a=String(i.type??"").toLowerCase();(["toolcall","tool_call","tooluse","tool_use"].includes(a)||typeof i.name=="string"&&i.arguments!=null)&&n.push({kind:"call",name:i.name??"tool",args:gn(i.arguments??i.args)})}for(const i of s){const a=String(i.type??"").toLowerCase();if(a!=="toolresult"&&a!=="tool_result")continue;const l=mn(i),o=typeof i.name=="string"?i.name:"tool";n.push({kind:"result",name:o,text:l})}if(lt(e)&&!n.some(i=>i.kind==="result")){const i=typeof t.toolName=="string"&&t.toolName||typeof t.tool_name=="string"&&t.tool_name||"tool",a=at(e)??void 0;n.push({kind:"result",name:i,text:a})}return n}function un(e){const t=an({name:e.name,args:e.args}),s=ln(t);return c`
78
- <div class="chat-tool-card">
79
- <div class="chat-tool-card__title">${t.emoji} ${t.label}</div>
80
- ${s?c`<div class="chat-tool-card__detail">${s}</div>`:f}
81
- ${e.text?c`<div class="chat-tool-card__output">${e.text}</div>`:f}
82
- </div>
83
- `}function hn(e){return Array.isArray(e)?e.filter(Boolean):[]}function gn(e){if(typeof e!="string")return e;const t=e.trim();if(!t||!t.startsWith("{")&&!t.startsWith("["))return e;try{return JSON.parse(t)}catch{return e}}function mn(e){if(typeof e.text=="string")return e.text;if(typeof e.content=="string")return e.content}function lt(e){const t=e,s=typeof t.role=="string"?t.role.toLowerCase():"";return s==="toolresult"||s==="tool_result"}function pn(e){if(!e.schema)return c`<div class="muted">Schema unavailable.</div>`;const t=e.schema,s=e.value??{};if(Ee(t)!=="object"||!t.properties)return c`<div class="callout danger">Unsupported schema. Use Raw.</div>`;const i=Object.entries(t.properties).sort((a,l)=>{const o=be([a[0]],e.uiHints)?.order??0,r=be([l[0]],e.uiHints)?.order??0;return o!==r?o-r:a[0].localeCompare(l[0])});return c`
84
- <div class="config-form">
85
- ${i.map(([a,l])=>ve({schema:l,value:s[a],path:[a],hints:e.uiHints,onPatch:e.onPatch}))}
86
- </div>
87
- `}function ve(e){const{schema:t,value:s,path:n,hints:i,onPatch:a}=e,l=Ee(t),o=be(n,i),r=o?.label??t.title??vn(String(n.at(-1))),d=o?.help??t.description;if(t.anyOf||t.oneOf||t.allOf)return c`<div class="callout danger">
88
- ${r}: unsupported schema node. Use Raw.
89
- </div>`;if(l==="object"){const p=t.properties??{},u=Object.entries(p);return u.length===0?f:c`
90
- <fieldset class="field-group">
91
- <legend>${r}</legend>
92
- ${d?c`<div class="muted">${d}</div>`:f}
93
- ${u.map(([v,$])=>ve({schema:$,value:s&&typeof s=="object"?s[v]:void 0,path:[...n,v],hints:i,onPatch:a}))}
94
- </fieldset>
95
- `}if(l==="array"){const p=Array.isArray(t.items)?t.items[0]:t.items,u=Array.isArray(s)?s:[];return c`
96
- <div class="field">
97
- <div class="row" style="justify-content: space-between;">
98
- <span>${r}</span>
99
- <button
100
- class="btn"
101
- @click=${()=>{const v=[...u,fn(p)];a(n,v)}}
102
- >
103
- Add
104
- </button>
105
- </div>
106
- ${d?c`<div class="muted">${d}</div>`:f}
107
- ${u.map((v,$)=>c`<div class="array-item">
108
- ${p?ve({schema:p,value:v,path:[...n,$],hints:i,onPatch:a}):f}
109
- <button
110
- class="btn danger"
111
- @click=${()=>{const y=u.slice();y.splice($,1),a(n,y)}}
112
- >
113
- Remove
114
- </button>
115
- </div>`)}
116
- </div>
117
- `}if(t.enum)return c`
118
- <label class="field">
119
- <span>${r}</span>
120
- ${d?c`<div class="muted">${d}</div>`:f}
121
- <select
122
- .value=${s==null?"":String(s)}
123
- @change=${p=>a(n,p.target.value)}
124
- >
125
- ${t.enum.map(p=>c`<option value=${String(p)}>${String(p)}</option>`)}
126
- </select>
127
- </label>
128
- `;if(l==="boolean")return c`
129
- <label class="field">
130
- <span>${r}</span>
131
- ${d?c`<div class="muted">${d}</div>`:f}
132
- <input
133
- type="checkbox"
134
- .checked=${!!s}
135
- @change=${p=>a(n,p.target.checked)}
136
- />
137
- </label>
138
- `;if(l==="number"||l==="integer")return c`
139
- <label class="field">
140
- <span>${r}</span>
141
- ${d?c`<div class="muted">${d}</div>`:f}
142
- <input
143
- type="number"
144
- .value=${s==null?"":String(s)}
145
- @input=${p=>{const u=p.target.value,v=u===""?void 0:Number(u);a(n,v)}}
146
- />
147
- </label>
148
- `;if(l==="string"){const p=o?.sensitive??bn(n),u=o?.placeholder??(p?"••••":"");return c`
149
- <label class="field">
150
- <span>${r}</span>
151
- ${d?c`<div class="muted">${d}</div>`:f}
152
- <input
153
- type=${p?"password":"text"}
154
- placeholder=${u}
155
- .value=${s==null?"":String(s)}
156
- @input=${v=>a(n,v.target.value)}
157
- />
158
- </label>
159
- `}return c`<div class="field">
160
- <span>${r}</span>
161
- <div class="muted">Unsupported type. Use Raw.</div>
162
- </div>`}function Ee(e){if(e)return Array.isArray(e.type)?e.type.filter(s=>s!=="null")[0]??e.type[0]:e.type}function fn(e){if(!e)return"";if(e.default!==void 0)return e.default;switch(Ee(e)){case"object":return{};case"array":return[];case"boolean":return!1;case"number":case"integer":return 0;case"string":return"";default:return""}}function be(e,t){const s=ot(e);return t[s]}function ot(e){return e.filter(t=>typeof t=="string").join(".")}function vn(e){return e.replace(/_/g," ").replace(/([a-z0-9])([A-Z])/g,"$1 $2").replace(/\s+/g," ").replace(/^./,t=>t.toUpperCase())}function bn(e){const t=ot(e).toLowerCase();return t.includes("token")||t.includes("password")||t.includes("secret")||t.includes("apikey")||t.endsWith("key")}function yn(e){const t=e.valid==null?"unknown":e.valid?"valid":"invalid";return c`
163
- <section class="card">
164
- <div class="row" style="justify-content: space-between;">
165
- <div class="row">
166
- <div class="card-title">Config</div>
167
- <span class="pill">${t}</span>
168
- </div>
169
- <div class="row">
170
- <div class="toggle-group">
171
- <button
172
- class="btn ${e.formMode==="form"?"primary":""}"
173
- ?disabled=${e.schemaLoading||!e.schema}
174
- @click=${()=>e.onFormModeChange("form")}
175
- >
176
- Form
177
- </button>
178
- <button
179
- class="btn ${e.formMode==="raw"?"primary":""}"
180
- @click=${()=>e.onFormModeChange("raw")}
181
- >
182
- Raw
183
- </button>
184
- </div>
185
- <button class="btn" ?disabled=${e.loading} @click=${e.onReload}>
186
- ${e.loading?"Loading…":"Reload"}
187
- </button>
188
- <button
189
- class="btn primary"
190
- ?disabled=${e.saving||!e.connected}
191
- @click=${e.onSave}
192
- >
193
- ${e.saving?"Saving…":"Save"}
194
- </button>
195
- </div>
196
- </div>
197
-
198
- <div class="muted" style="margin-top: 10px;">
199
- Writes to <span class="mono">~/.clawdbot/clawdbot.json</span>. Some changes
200
- require a gateway restart.
201
- </div>
202
-
203
- ${e.formMode==="form"?c`<div style="margin-top: 12px;">
204
- ${e.schemaLoading?c`<div class="muted">Loading schema…</div>`:pn({schema:e.schema,uiHints:e.uiHints,value:e.formValue,onPatch:e.onFormPatch})}
205
- </div>`:c`<label class="field" style="margin-top: 12px;">
206
- <span>Raw JSON5</span>
207
- <textarea
208
- .value=${e.raw}
209
- @input=${s=>e.onRawChange(s.target.value)}
210
- ></textarea>
211
- </label>`}
212
-
213
- ${e.issues.length>0?c`<div class="callout danger" style="margin-top: 12px;">
214
- <pre class="code-block">${JSON.stringify(e.issues,null,2)}</pre>
215
- </div>`:f}
216
- </section>
217
- `}function X(e){return!e&&e!==0?"n/a":new Date(e).toLocaleString()}function M(e){if(!e&&e!==0)return"n/a";const t=Date.now()-e;if(t<0)return"just now";const s=Math.round(t/1e3);if(s<60)return`${s}s ago`;const n=Math.round(s/60);if(n<60)return`${n}m ago`;const i=Math.round(n/60);return i<48?`${i}h ago`:`${Math.round(i/24)}d ago`}function rt(e){if(!e&&e!==0)return"n/a";if(e<1e3)return`${e}ms`;const t=Math.round(e/1e3);if(t<60)return`${t}s`;const s=Math.round(t/60);if(s<60)return`${s}m`;const n=Math.round(s/60);return n<48?`${n}h`:`${Math.round(n/24)}d`}function $n(e,t=120){return e.length<=t?e:`${e.slice(0,Math.max(0,t-1))}…`}function de(e,t){const s=Number(e);return Number.isFinite(s)?s:t}function T(e){return e.split(/[,\n]/).map(t=>t.trim()).filter(t=>t.length>0)}const wn=[{key:"reactions",label:"Reactions"},{key:"stickers",label:"Stickers"},{key:"polls",label:"Polls"},{key:"permissions",label:"Permissions"},{key:"messages",label:"Messages"},{key:"threads",label:"Threads"},{key:"pins",label:"Pins"},{key:"search",label:"Search"},{key:"memberInfo",label:"Member info"},{key:"roleInfo",label:"Role info"},{key:"channelInfo",label:"Channel info"},{key:"voiceStatus",label:"Voice status"},{key:"events",label:"Events"},{key:"roles",label:"Role changes"},{key:"moderation",label:"Moderation"}],Sn=[{key:"reactions",label:"Reactions"},{key:"messages",label:"Messages"},{key:"pins",label:"Pins"},{key:"memberInfo",label:"Member info"},{key:"emojiList",label:"Emoji list"}];function kn(e){const t=e.snapshot?.whatsapp,s=e.snapshot?.telegram,n=e.snapshot?.discord??null,i=e.snapshot?.slack??null,a=e.snapshot?.signal??null,l=e.snapshot?.imessage??null,r=["whatsapp","telegram","discord","slack","signal","imessage"].map((d,p)=>({key:d,enabled:Mn(d,e),order:p})).sort((d,p)=>d.enabled!==p.enabled?d.enabled?-1:1:d.order-p.order);return c`
218
- <section class="grid grid-cols-2">
219
- ${r.map(d=>An(d.key,e,{whatsapp:t,telegram:s,discord:n,slack:i,signal:a,imessage:l}))}
220
- </section>
221
-
222
- <section class="card" style="margin-top: 18px;">
223
- <div class="row" style="justify-content: space-between;">
224
- <div>
225
- <div class="card-title">Connection health</div>
226
- <div class="card-sub">Provider status snapshots from the gateway.</div>
227
- </div>
228
- <div class="muted">${e.lastSuccessAt?M(e.lastSuccessAt):"n/a"}</div>
229
- </div>
230
- ${e.lastError?c`<div class="callout danger" style="margin-top: 12px;">
231
- ${e.lastError}
232
- </div>`:f}
233
- <pre class="code-block" style="margin-top: 12px;">
234
- ${e.snapshot?JSON.stringify(e.snapshot,null,2):"No snapshot yet."}
235
- </pre>
236
- </section>
237
- `}function Cn(e){if(!e&&e!==0)return"n/a";const t=Math.round(e/1e3);if(t<60)return`${t}s`;const s=Math.round(t/60);return s<60?`${s}m`:`${Math.round(s/60)}h`}function Mn(e,t){const s=t.snapshot;if(!s)return!1;switch(e){case"whatsapp":return s.whatsapp.configured||s.whatsapp.linked||s.whatsapp.running;case"telegram":return s.telegram.configured||s.telegram.running;case"discord":return!!(s.discord?.configured||s.discord?.running);case"slack":return!!(s.slack?.configured||s.slack?.running);case"signal":return!!(s.signal?.configured||s.signal?.running);case"imessage":return!!(s.imessage?.configured||s.imessage?.running);default:return!1}}function An(e,t,s){switch(e){case"whatsapp":{const n=s.whatsapp;return c`
238
- <div class="card">
239
- <div class="card-title">WhatsApp</div>
240
- <div class="card-sub">Link WhatsApp Web and monitor connection health.</div>
241
-
242
- <div class="status-list" style="margin-top: 16px;">
243
- <div>
244
- <span class="label">Configured</span>
245
- <span>${n?.configured?"Yes":"No"}</span>
246
- </div>
247
- <div>
248
- <span class="label">Linked</span>
249
- <span>${n?.linked?"Yes":"No"}</span>
250
- </div>
251
- <div>
252
- <span class="label">Running</span>
253
- <span>${n?.running?"Yes":"No"}</span>
254
- </div>
255
- <div>
256
- <span class="label">Connected</span>
257
- <span>${n?.connected?"Yes":"No"}</span>
258
- </div>
259
- <div>
260
- <span class="label">Last connect</span>
261
- <span>
262
- ${n?.lastConnectedAt?M(n.lastConnectedAt):"n/a"}
263
- </span>
264
- </div>
265
- <div>
266
- <span class="label">Last message</span>
267
- <span>
268
- ${n?.lastMessageAt?M(n.lastMessageAt):"n/a"}
269
- </span>
270
- </div>
271
- <div>
272
- <span class="label">Auth age</span>
273
- <span>
274
- ${n?.authAgeMs!=null?Cn(n.authAgeMs):"n/a"}
275
- </span>
276
- </div>
277
- </div>
278
-
279
- ${n?.lastError?c`<div class="callout danger" style="margin-top: 12px;">
280
- ${n.lastError}
281
- </div>`:f}
282
-
283
- ${t.whatsappMessage?c`<div class="callout" style="margin-top: 12px;">
284
- ${t.whatsappMessage}
285
- </div>`:f}
286
-
287
- ${t.whatsappQrDataUrl?c`<div class="qr-wrap">
288
- <img src=${t.whatsappQrDataUrl} alt="WhatsApp QR" />
289
- </div>`:f}
290
-
291
- <div class="row" style="margin-top: 14px; flex-wrap: wrap;">
292
- <button
293
- class="btn primary"
294
- ?disabled=${t.whatsappBusy}
295
- @click=${()=>t.onWhatsAppStart(!1)}
296
- >
297
- ${t.whatsappBusy?"Working…":"Show QR"}
298
- </button>
299
- <button
300
- class="btn"
301
- ?disabled=${t.whatsappBusy}
302
- @click=${()=>t.onWhatsAppStart(!0)}
303
- >
304
- Relink
305
- </button>
306
- <button
307
- class="btn"
308
- ?disabled=${t.whatsappBusy}
309
- @click=${()=>t.onWhatsAppWait()}
310
- >
311
- Wait for scan
312
- </button>
313
- <button
314
- class="btn danger"
315
- ?disabled=${t.whatsappBusy}
316
- @click=${()=>t.onWhatsAppLogout()}
317
- >
318
- Logout
319
- </button>
320
- <button class="btn" @click=${()=>t.onRefresh(!0)}>
321
- Refresh
322
- </button>
323
- </div>
324
- </div>
325
- `}case"telegram":{const n=s.telegram;return c`
326
- <div class="card">
327
- <div class="card-title">Telegram</div>
328
- <div class="card-sub">Bot token and delivery options.</div>
329
-
330
- <div class="status-list" style="margin-top: 16px;">
331
- <div>
332
- <span class="label">Configured</span>
333
- <span>${n?.configured?"Yes":"No"}</span>
334
- </div>
335
- <div>
336
- <span class="label">Running</span>
337
- <span>${n?.running?"Yes":"No"}</span>
338
- </div>
339
- <div>
340
- <span class="label">Mode</span>
341
- <span>${n?.mode??"n/a"}</span>
342
- </div>
343
- <div>
344
- <span class="label">Last start</span>
345
- <span>${n?.lastStartAt?M(n.lastStartAt):"n/a"}</span>
346
- </div>
347
- <div>
348
- <span class="label">Last probe</span>
349
- <span>${n?.lastProbeAt?M(n.lastProbeAt):"n/a"}</span>
350
- </div>
351
- </div>
352
-
353
- ${n?.lastError?c`<div class="callout danger" style="margin-top: 12px;">
354
- ${n.lastError}
355
- </div>`:f}
356
-
357
- ${n?.probe?c`<div class="callout" style="margin-top: 12px;">
358
- Probe ${n.probe.ok?"ok":"failed"} ·
359
- ${n.probe.status??""}
360
- ${n.probe.error??""}
361
- </div>`:f}
362
-
363
- <div class="form-grid" style="margin-top: 16px;">
364
- <label class="field">
365
- <span>Bot token</span>
366
- <input
367
- type="password"
368
- .value=${t.telegramForm.token}
369
- ?disabled=${t.telegramTokenLocked}
370
- @input=${i=>t.onTelegramChange({token:i.target.value})}
371
- />
372
- </label>
373
- <label class="field">
374
- <span>Require mention in groups</span>
375
- <select
376
- .value=${t.telegramForm.requireMention?"yes":"no"}
377
- @change=${i=>t.onTelegramChange({requireMention:i.target.value==="yes"})}
378
- >
379
- <option value="yes">Yes</option>
380
- <option value="no">No</option>
381
- </select>
382
- </label>
383
- <label class="field">
384
- <span>Allow from</span>
385
- <input
386
- .value=${t.telegramForm.allowFrom}
387
- @input=${i=>t.onTelegramChange({allowFrom:i.target.value})}
388
- placeholder="123456789, @team"
389
- />
390
- </label>
391
- <label class="field">
392
- <span>Proxy</span>
393
- <input
394
- .value=${t.telegramForm.proxy}
395
- @input=${i=>t.onTelegramChange({proxy:i.target.value})}
396
- placeholder="socks5://localhost:9050"
397
- />
398
- </label>
399
- <label class="field">
400
- <span>Webhook URL</span>
401
- <input
402
- .value=${t.telegramForm.webhookUrl}
403
- @input=${i=>t.onTelegramChange({webhookUrl:i.target.value})}
404
- placeholder="https://example.com/telegram-webhook"
405
- />
406
- </label>
407
- <label class="field">
408
- <span>Webhook secret</span>
409
- <input
410
- .value=${t.telegramForm.webhookSecret}
411
- @input=${i=>t.onTelegramChange({webhookSecret:i.target.value})}
412
- placeholder="secret"
413
- />
414
- </label>
415
- <label class="field">
416
- <span>Webhook path</span>
417
- <input
418
- .value=${t.telegramForm.webhookPath}
419
- @input=${i=>t.onTelegramChange({webhookPath:i.target.value})}
420
- placeholder="/telegram-webhook"
421
- />
422
- </label>
423
- </div>
424
-
425
- ${t.telegramTokenLocked?c`<div class="callout" style="margin-top: 12px;">
426
- TELEGRAM_BOT_TOKEN is set in the environment. Config edits will not override it.
427
- </div>`:f}
428
-
429
- ${t.telegramStatus?c`<div class="callout" style="margin-top: 12px;">
430
- ${t.telegramStatus}
431
- </div>`:f}
432
-
433
- <div class="row" style="margin-top: 14px;">
434
- <button
435
- class="btn primary"
436
- ?disabled=${t.telegramSaving}
437
- @click=${()=>t.onTelegramSave()}
438
- >
439
- ${t.telegramSaving?"Saving…":"Save"}
440
- </button>
441
- <button class="btn" @click=${()=>t.onRefresh(!0)}>
442
- Probe
443
- </button>
444
- </div>
445
- </div>
446
- `}case"discord":{const n=s.discord,i=n?.probe?.bot?.username;return c`
447
- <div class="card">
448
- <div class="card-title">Discord</div>
449
- <div class="card-sub">Bot connection and probe status.</div>
450
-
451
- <div class="status-list" style="margin-top: 16px;">
452
- <div>
453
- <span class="label">Configured</span>
454
- <span>${n?.configured?"Yes":"No"}</span>
455
- </div>
456
- <div>
457
- <span class="label">Running</span>
458
- <span>${n?.running?"Yes":"No"}</span>
459
- </div>
460
- <div>
461
- <span class="label">Bot</span>
462
- <span>${i?`@${i}`:"n/a"}</span>
463
- </div>
464
- <div>
465
- <span class="label">Last start</span>
466
- <span>${n?.lastStartAt?M(n.lastStartAt):"n/a"}</span>
467
- </div>
468
- <div>
469
- <span class="label">Last probe</span>
470
- <span>${n?.lastProbeAt?M(n.lastProbeAt):"n/a"}</span>
471
- </div>
472
- </div>
473
-
474
- ${n?.lastError?c`<div class="callout danger" style="margin-top: 12px;">
475
- ${n.lastError}
476
- </div>`:f}
477
-
478
- ${n?.probe?c`<div class="callout" style="margin-top: 12px;">
479
- Probe ${n.probe.ok?"ok":"failed"} ·
480
- ${n.probe.status??""}
481
- ${n.probe.error??""}
482
- </div>`:f}
483
-
484
- <div class="form-grid" style="margin-top: 16px;">
485
- <label class="field">
486
- <span>Enabled</span>
487
- <select
488
- .value=${t.discordForm.enabled?"yes":"no"}
489
- @change=${a=>t.onDiscordChange({enabled:a.target.value==="yes"})}
490
- >
491
- <option value="yes">Yes</option>
492
- <option value="no">No</option>
493
- </select>
494
- </label>
495
- <label class="field">
496
- <span>Bot token</span>
497
- <input
498
- type="password"
499
- .value=${t.discordForm.token}
500
- ?disabled=${t.discordTokenLocked}
501
- @input=${a=>t.onDiscordChange({token:a.target.value})}
502
- />
503
- </label>
504
- <label class="field">
505
- <span>Allow DMs from</span>
506
- <input
507
- .value=${t.discordForm.allowFrom}
508
- @input=${a=>t.onDiscordChange({allowFrom:a.target.value})}
509
- placeholder="123456789, username#1234"
510
- />
511
- </label>
512
- <label class="field">
513
- <span>DMs enabled</span>
514
- <select
515
- .value=${t.discordForm.dmEnabled?"yes":"no"}
516
- @change=${a=>t.onDiscordChange({dmEnabled:a.target.value==="yes"})}
517
- >
518
- <option value="yes">Enabled</option>
519
- <option value="no">Disabled</option>
520
- </select>
521
- </label>
522
- <label class="field">
523
- <span>Group DMs</span>
524
- <select
525
- .value=${t.discordForm.groupEnabled?"yes":"no"}
526
- @change=${a=>t.onDiscordChange({groupEnabled:a.target.value==="yes"})}
527
- >
528
- <option value="yes">Enabled</option>
529
- <option value="no">Disabled</option>
530
- </select>
531
- </label>
532
- <label class="field">
533
- <span>Group channels</span>
534
- <input
535
- .value=${t.discordForm.groupChannels}
536
- @input=${a=>t.onDiscordChange({groupChannels:a.target.value})}
537
- placeholder="channelId1, channelId2"
538
- />
539
- </label>
540
- <label class="field">
541
- <span>Media max MB</span>
542
- <input
543
- .value=${t.discordForm.mediaMaxMb}
544
- @input=${a=>t.onDiscordChange({mediaMaxMb:a.target.value})}
545
- placeholder="8"
546
- />
547
- </label>
548
- <label class="field">
549
- <span>History limit</span>
550
- <input
551
- .value=${t.discordForm.historyLimit}
552
- @input=${a=>t.onDiscordChange({historyLimit:a.target.value})}
553
- placeholder="20"
554
- />
555
- </label>
556
- <label class="field">
557
- <span>Text chunk limit</span>
558
- <input
559
- .value=${t.discordForm.textChunkLimit}
560
- @input=${a=>t.onDiscordChange({textChunkLimit:a.target.value})}
561
- placeholder="2000"
562
- />
563
- </label>
564
- <label class="field">
565
- <span>Reply to mode</span>
566
- <select
567
- .value=${t.discordForm.replyToMode}
568
- @change=${a=>t.onDiscordChange({replyToMode:a.target.value})}
569
- >
570
- <option value="off">Off</option>
571
- <option value="first">First</option>
572
- <option value="all">All</option>
573
- </select>
574
- </label>
575
- <div class="field full">
576
- <span>Guilds</span>
577
- <div class="card-sub">
578
- Add each guild (id or slug) and optional channel rules. Empty channel
579
- entries still allow that channel.
580
- </div>
581
- <div class="list">
582
- ${t.discordForm.guilds.map((a,l)=>c`
583
- <div class="list-item">
584
- <div class="list-main">
585
- <div class="form-grid">
586
- <label class="field">
587
- <span>Guild id / slug</span>
588
- <input
589
- .value=${a.key}
590
- @input=${o=>{const r=[...t.discordForm.guilds];r[l]={...r[l],key:o.target.value},t.onDiscordChange({guilds:r})}}
591
- />
592
- </label>
593
- <label class="field">
594
- <span>Slug</span>
595
- <input
596
- .value=${a.slug}
597
- @input=${o=>{const r=[...t.discordForm.guilds];r[l]={...r[l],slug:o.target.value},t.onDiscordChange({guilds:r})}}
598
- />
599
- </label>
600
- <label class="field">
601
- <span>Require mention</span>
602
- <select
603
- .value=${a.requireMention?"yes":"no"}
604
- @change=${o=>{const r=[...t.discordForm.guilds];r[l]={...r[l],requireMention:o.target.value==="yes"},t.onDiscordChange({guilds:r})}}
605
- >
606
- <option value="yes">Yes</option>
607
- <option value="no">No</option>
608
- </select>
609
- </label>
610
- <label class="field">
611
- <span>Reaction notifications</span>
612
- <select
613
- .value=${a.reactionNotifications}
614
- @change=${o=>{const r=[...t.discordForm.guilds];r[l]={...r[l],reactionNotifications:o.target.value},t.onDiscordChange({guilds:r})}}
615
- >
616
- <option value="off">Off</option>
617
- <option value="own">Own</option>
618
- <option value="all">All</option>
619
- <option value="allowlist">Allowlist</option>
620
- </select>
621
- </label>
622
- <label class="field">
623
- <span>Users allowlist</span>
624
- <input
625
- .value=${a.users}
626
- @input=${o=>{const r=[...t.discordForm.guilds];r[l]={...r[l],users:o.target.value},t.onDiscordChange({guilds:r})}}
627
- placeholder="123456789, username#1234"
628
- />
629
- </label>
630
- </div>
631
- ${a.channels.length?c`
632
- <div class="form-grid" style="margin-top: 8px;">
633
- ${a.channels.map((o,r)=>c`
634
- <label class="field">
635
- <span>Channel id / slug</span>
636
- <input
637
- .value=${o.key}
638
- @input=${d=>{const p=[...t.discordForm.guilds],u=[...p[l].channels??[]];u[r]={...u[r],key:d.target.value},p[l]={...p[l],channels:u},t.onDiscordChange({guilds:p})}}
639
- />
640
- </label>
641
- <label class="field">
642
- <span>Allow</span>
643
- <select
644
- .value=${o.allow?"yes":"no"}
645
- @change=${d=>{const p=[...t.discordForm.guilds],u=[...p[l].channels??[]];u[r]={...u[r],allow:d.target.value==="yes"},p[l]={...p[l],channels:u},t.onDiscordChange({guilds:p})}}
646
- >
647
- <option value="yes">Yes</option>
648
- <option value="no">No</option>
649
- </select>
650
- </label>
651
- <label class="field">
652
- <span>Require mention</span>
653
- <select
654
- .value=${o.requireMention?"yes":"no"}
655
- @change=${d=>{const p=[...t.discordForm.guilds],u=[...p[l].channels??[]];u[r]={...u[r],requireMention:d.target.value==="yes"},p[l]={...p[l],channels:u},t.onDiscordChange({guilds:p})}}
656
- >
657
- <option value="yes">Yes</option>
658
- <option value="no">No</option>
659
- </select>
660
- </label>
661
- <label class="field">
662
- <span>&nbsp;</span>
663
- <button
664
- class="btn"
665
- @click=${()=>{const d=[...t.discordForm.guilds],p=[...d[l].channels??[]];p.splice(r,1),d[l]={...d[l],channels:p},t.onDiscordChange({guilds:d})}}
666
- >
667
- Remove
668
- </button>
669
- </label>
670
- `)}
671
- </div>
672
- `:f}
673
- </div>
674
- <div class="list-meta">
675
- <span>Channels</span>
676
- <button
677
- class="btn"
678
- @click=${()=>{const o=[...t.discordForm.guilds],r=[...o[l].channels??[],{key:"",allow:!0,requireMention:!1}];o[l]={...o[l],channels:r},t.onDiscordChange({guilds:o})}}
679
- >
680
- Add channel
681
- </button>
682
- <button
683
- class="btn danger"
684
- @click=${()=>{const o=[...t.discordForm.guilds];o.splice(l,1),t.onDiscordChange({guilds:o})}}
685
- >
686
- Remove guild
687
- </button>
688
- </div>
689
- </div>
690
- `)}
691
- </div>
692
- <button
693
- class="btn"
694
- style="margin-top: 8px;"
695
- @click=${()=>t.onDiscordChange({guilds:[...t.discordForm.guilds,{key:"",slug:"",requireMention:!1,reactionNotifications:"own",users:"",channels:[]}]})}
696
- >
697
- Add guild
698
- </button>
699
- </div>
700
- <label class="field">
701
- <span>Slash command</span>
702
- <select
703
- .value=${t.discordForm.slashEnabled?"yes":"no"}
704
- @change=${a=>t.onDiscordChange({slashEnabled:a.target.value==="yes"})}
705
- >
706
- <option value="yes">Enabled</option>
707
- <option value="no">Disabled</option>
708
- </select>
709
- </label>
710
- <label class="field">
711
- <span>Slash name</span>
712
- <input
713
- .value=${t.discordForm.slashName}
714
- @input=${a=>t.onDiscordChange({slashName:a.target.value})}
715
- placeholder="clawd"
716
- />
717
- </label>
718
- <label class="field">
719
- <span>Slash session prefix</span>
720
- <input
721
- .value=${t.discordForm.slashSessionPrefix}
722
- @input=${a=>t.onDiscordChange({slashSessionPrefix:a.target.value})}
723
- placeholder="discord:slash"
724
- />
725
- </label>
726
- <label class="field">
727
- <span>Slash ephemeral</span>
728
- <select
729
- .value=${t.discordForm.slashEphemeral?"yes":"no"}
730
- @change=${a=>t.onDiscordChange({slashEphemeral:a.target.value==="yes"})}
731
- >
732
- <option value="yes">Yes</option>
733
- <option value="no">No</option>
734
- </select>
735
- </label>
736
- </div>
737
-
738
- <div class="card-sub" style="margin-top: 16px;">Tool actions</div>
739
- <div class="form-grid" style="margin-top: 8px;">
740
- ${wn.map(a=>c`<label class="field">
741
- <span>${a.label}</span>
742
- <select
743
- .value=${t.discordForm.actions[a.key]?"yes":"no"}
744
- @change=${l=>t.onDiscordChange({actions:{...t.discordForm.actions,[a.key]:l.target.value==="yes"}})}
745
- >
746
- <option value="yes">Enabled</option>
747
- <option value="no">Disabled</option>
748
- </select>
749
- </label>`)}
750
- </div>
751
-
752
- ${t.discordTokenLocked?c`<div class="callout" style="margin-top: 12px;">
753
- DISCORD_BOT_TOKEN is set in the environment. Config edits will not override it.
754
- </div>`:f}
755
-
756
- ${t.discordStatus?c`<div class="callout" style="margin-top: 12px;">
757
- ${t.discordStatus}
758
- </div>`:f}
759
-
760
- <div class="row" style="margin-top: 14px;">
761
- <button
762
- class="btn primary"
763
- ?disabled=${t.discordSaving}
764
- @click=${()=>t.onDiscordSave()}
765
- >
766
- ${t.discordSaving?"Saving…":"Save"}
767
- </button>
768
- <button class="btn" @click=${()=>t.onRefresh(!0)}>
769
- Probe
770
- </button>
771
- </div>
772
- </div>
773
- `}case"slack":{const n=s.slack,i=n?.probe?.bot?.name,a=n?.probe?.team?.name;return c`
774
- <div class="card">
775
- <div class="card-title">Slack</div>
776
- <div class="card-sub">Socket mode status and bot details.</div>
777
-
778
- <div class="status-list" style="margin-top: 16px;">
779
- <div>
780
- <span class="label">Configured</span>
781
- <span>${n?.configured?"Yes":"No"}</span>
782
- </div>
783
- <div>
784
- <span class="label">Running</span>
785
- <span>${n?.running?"Yes":"No"}</span>
786
- </div>
787
- <div>
788
- <span class="label">Bot</span>
789
- <span>${i||"n/a"}</span>
790
- </div>
791
- <div>
792
- <span class="label">Team</span>
793
- <span>${a||"n/a"}</span>
794
- </div>
795
- <div>
796
- <span class="label">Last start</span>
797
- <span>${n?.lastStartAt?M(n.lastStartAt):"n/a"}</span>
798
- </div>
799
- <div>
800
- <span class="label">Last probe</span>
801
- <span>${n?.lastProbeAt?M(n.lastProbeAt):"n/a"}</span>
802
- </div>
803
- </div>
804
-
805
- ${n?.lastError?c`<div class="callout danger" style="margin-top: 12px;">
806
- ${n.lastError}
807
- </div>`:f}
808
-
809
- ${n?.probe?c`<div class="callout" style="margin-top: 12px;">
810
- Probe ${n.probe.ok?"ok":"failed"} ·
811
- ${n.probe.status??""}
812
- ${n.probe.error??""}
813
- </div>`:f}
814
-
815
- <div class="form-grid" style="margin-top: 16px;">
816
- <label class="field">
817
- <span>Enabled</span>
818
- <select
819
- .value=${t.slackForm.enabled?"yes":"no"}
820
- @change=${l=>t.onSlackChange({enabled:l.target.value==="yes"})}
821
- >
822
- <option value="yes">Yes</option>
823
- <option value="no">No</option>
824
- </select>
825
- </label>
826
- <label class="field">
827
- <span>Bot token</span>
828
- <input
829
- type="password"
830
- .value=${t.slackForm.botToken}
831
- ?disabled=${t.slackTokenLocked}
832
- @input=${l=>t.onSlackChange({botToken:l.target.value})}
833
- />
834
- </label>
835
- <label class="field">
836
- <span>App token</span>
837
- <input
838
- type="password"
839
- .value=${t.slackForm.appToken}
840
- ?disabled=${t.slackAppTokenLocked}
841
- @input=${l=>t.onSlackChange({appToken:l.target.value})}
842
- />
843
- </label>
844
- <label class="field">
845
- <span>DMs enabled</span>
846
- <select
847
- .value=${t.slackForm.dmEnabled?"yes":"no"}
848
- @change=${l=>t.onSlackChange({dmEnabled:l.target.value==="yes"})}
849
- >
850
- <option value="yes">Enabled</option>
851
- <option value="no">Disabled</option>
852
- </select>
853
- </label>
854
- <label class="field">
855
- <span>Allow DMs from</span>
856
- <input
857
- .value=${t.slackForm.allowFrom}
858
- @input=${l=>t.onSlackChange({allowFrom:l.target.value})}
859
- placeholder="U123, U456, *"
860
- />
861
- </label>
862
- <label class="field">
863
- <span>Group DMs enabled</span>
864
- <select
865
- .value=${t.slackForm.groupEnabled?"yes":"no"}
866
- @change=${l=>t.onSlackChange({groupEnabled:l.target.value==="yes"})}
867
- >
868
- <option value="yes">Enabled</option>
869
- <option value="no">Disabled</option>
870
- </select>
871
- </label>
872
- <label class="field">
873
- <span>Group DM channels</span>
874
- <input
875
- .value=${t.slackForm.groupChannels}
876
- @input=${l=>t.onSlackChange({groupChannels:l.target.value})}
877
- placeholder="G123, #team"
878
- />
879
- </label>
880
- <label class="field">
881
- <span>Reaction notifications</span>
882
- <select
883
- .value=${t.slackForm.reactionNotifications}
884
- @change=${l=>t.onSlackChange({reactionNotifications:l.target.value})}
885
- >
886
- <option value="off">Off</option>
887
- <option value="own">Own</option>
888
- <option value="all">All</option>
889
- <option value="allowlist">Allowlist</option>
890
- </select>
891
- </label>
892
- <label class="field">
893
- <span>Reaction allowlist</span>
894
- <input
895
- .value=${t.slackForm.reactionAllowlist}
896
- @input=${l=>t.onSlackChange({reactionAllowlist:l.target.value})}
897
- placeholder="U123, U456"
898
- />
899
- </label>
900
- <label class="field">
901
- <span>Text chunk limit</span>
902
- <input
903
- .value=${t.slackForm.textChunkLimit}
904
- @input=${l=>t.onSlackChange({textChunkLimit:l.target.value})}
905
- placeholder="4000"
906
- />
907
- </label>
908
- <label class="field">
909
- <span>Media max (MB)</span>
910
- <input
911
- .value=${t.slackForm.mediaMaxMb}
912
- @input=${l=>t.onSlackChange({mediaMaxMb:l.target.value})}
913
- placeholder="20"
914
- />
915
- </label>
916
- </div>
917
-
918
- <div class="card-sub" style="margin-top: 16px;">Slash command</div>
919
- <div class="form-grid" style="margin-top: 8px;">
920
- <label class="field">
921
- <span>Slash enabled</span>
922
- <select
923
- .value=${t.slackForm.slashEnabled?"yes":"no"}
924
- @change=${l=>t.onSlackChange({slashEnabled:l.target.value==="yes"})}
925
- >
926
- <option value="yes">Enabled</option>
927
- <option value="no">Disabled</option>
928
- </select>
929
- </label>
930
- <label class="field">
931
- <span>Slash name</span>
932
- <input
933
- .value=${t.slackForm.slashName}
934
- @input=${l=>t.onSlackChange({slashName:l.target.value})}
935
- placeholder="clawd"
936
- />
937
- </label>
938
- <label class="field">
939
- <span>Slash session prefix</span>
940
- <input
941
- .value=${t.slackForm.slashSessionPrefix}
942
- @input=${l=>t.onSlackChange({slashSessionPrefix:l.target.value})}
943
- placeholder="slack:slash"
944
- />
945
- </label>
946
- <label class="field">
947
- <span>Slash ephemeral</span>
948
- <select
949
- .value=${t.slackForm.slashEphemeral?"yes":"no"}
950
- @change=${l=>t.onSlackChange({slashEphemeral:l.target.value==="yes"})}
951
- >
952
- <option value="yes">Yes</option>
953
- <option value="no">No</option>
954
- </select>
955
- </label>
956
- </div>
957
-
958
- <div class="card-sub" style="margin-top: 16px;">Channels</div>
959
- <div class="card-sub">
960
- Add channel ids or #names and optionally require mentions.
961
- </div>
962
- <div class="list">
963
- ${t.slackForm.channels.map((l,o)=>c`
964
- <div class="list-item">
965
- <div class="list-main">
966
- <div class="form-grid">
967
- <label class="field">
968
- <span>Channel id / name</span>
969
- <input
970
- .value=${l.key}
971
- @input=${r=>{const d=[...t.slackForm.channels];d[o]={...d[o],key:r.target.value},t.onSlackChange({channels:d})}}
972
- />
973
- </label>
974
- <label class="field">
975
- <span>Allow</span>
976
- <select
977
- .value=${l.allow?"yes":"no"}
978
- @change=${r=>{const d=[...t.slackForm.channels];d[o]={...d[o],allow:r.target.value==="yes"},t.onSlackChange({channels:d})}}
979
- >
980
- <option value="yes">Yes</option>
981
- <option value="no">No</option>
982
- </select>
983
- </label>
984
- <label class="field">
985
- <span>Require mention</span>
986
- <select
987
- .value=${l.requireMention?"yes":"no"}
988
- @change=${r=>{const d=[...t.slackForm.channels];d[o]={...d[o],requireMention:r.target.value==="yes"},t.onSlackChange({channels:d})}}
989
- >
990
- <option value="yes">Yes</option>
991
- <option value="no">No</option>
992
- </select>
993
- </label>
994
- <label class="field">
995
- <span>&nbsp;</span>
996
- <button
997
- class="btn"
998
- @click=${()=>{const r=[...t.slackForm.channels];r.splice(o,1),t.onSlackChange({channels:r})}}
999
- >
1000
- Remove
1001
- </button>
1002
- </label>
1003
- </div>
1004
- </div>
1005
- </div>
1006
- `)}
1007
- </div>
1008
- <button
1009
- class="btn"
1010
- style="margin-top: 8px;"
1011
- @click=${()=>t.onSlackChange({channels:[...t.slackForm.channels,{key:"",allow:!0,requireMention:!1}]})}
1012
- >
1013
- Add channel
1014
- </button>
1015
-
1016
- <div class="card-sub" style="margin-top: 16px;">Tool actions</div>
1017
- <div class="form-grid" style="margin-top: 8px;">
1018
- ${Sn.map(l=>c`<label class="field">
1019
- <span>${l.label}</span>
1020
- <select
1021
- .value=${t.slackForm.actions[l.key]?"yes":"no"}
1022
- @change=${o=>t.onSlackChange({actions:{...t.slackForm.actions,[l.key]:o.target.value==="yes"}})}
1023
- >
1024
- <option value="yes">Enabled</option>
1025
- <option value="no">Disabled</option>
1026
- </select>
1027
- </label>`)}
1028
- </div>
1029
-
1030
- ${t.slackTokenLocked||t.slackAppTokenLocked?c`<div class="callout" style="margin-top: 12px;">
1031
- ${t.slackTokenLocked?"SLACK_BOT_TOKEN ":""}
1032
- ${t.slackAppTokenLocked?"SLACK_APP_TOKEN ":""}
1033
- is set in the environment. Config edits will not override it.
1034
- </div>`:f}
1035
-
1036
- ${t.slackStatus?c`<div class="callout" style="margin-top: 12px;">
1037
- ${t.slackStatus}
1038
- </div>`:f}
1039
-
1040
- <div class="row" style="margin-top: 14px;">
1041
- <button
1042
- class="btn primary"
1043
- ?disabled=${t.slackSaving}
1044
- @click=${()=>t.onSlackSave()}
1045
- >
1046
- ${t.slackSaving?"Saving…":"Save"}
1047
- </button>
1048
- <button class="btn" @click=${()=>t.onRefresh(!0)}>
1049
- Probe
1050
- </button>
1051
- </div>
1052
- </div>
1053
- `}case"signal":{const n=s.signal;return c`
1054
- <div class="card">
1055
- <div class="card-title">Signal</div>
1056
- <div class="card-sub">REST daemon status and probe details.</div>
1057
-
1058
- <div class="status-list" style="margin-top: 16px;">
1059
- <div>
1060
- <span class="label">Configured</span>
1061
- <span>${n?.configured?"Yes":"No"}</span>
1062
- </div>
1063
- <div>
1064
- <span class="label">Running</span>
1065
- <span>${n?.running?"Yes":"No"}</span>
1066
- </div>
1067
- <div>
1068
- <span class="label">Base URL</span>
1069
- <span>${n?.baseUrl??"n/a"}</span>
1070
- </div>
1071
- <div>
1072
- <span class="label">Last start</span>
1073
- <span>${n?.lastStartAt?M(n.lastStartAt):"n/a"}</span>
1074
- </div>
1075
- <div>
1076
- <span class="label">Last probe</span>
1077
- <span>${n?.lastProbeAt?M(n.lastProbeAt):"n/a"}</span>
1078
- </div>
1079
- </div>
1080
-
1081
- ${n?.lastError?c`<div class="callout danger" style="margin-top: 12px;">
1082
- ${n.lastError}
1083
- </div>`:f}
1084
-
1085
- ${n?.probe?c`<div class="callout" style="margin-top: 12px;">
1086
- Probe ${n.probe.ok?"ok":"failed"} ·
1087
- ${n.probe.status??""}
1088
- ${n.probe.error??""}
1089
- </div>`:f}
1090
-
1091
- <div class="form-grid" style="margin-top: 16px;">
1092
- <label class="field">
1093
- <span>Enabled</span>
1094
- <select
1095
- .value=${t.signalForm.enabled?"yes":"no"}
1096
- @change=${i=>t.onSignalChange({enabled:i.target.value==="yes"})}
1097
- >
1098
- <option value="yes">Yes</option>
1099
- <option value="no">No</option>
1100
- </select>
1101
- </label>
1102
- <label class="field">
1103
- <span>Account</span>
1104
- <input
1105
- .value=${t.signalForm.account}
1106
- @input=${i=>t.onSignalChange({account:i.target.value})}
1107
- placeholder="+15551234567"
1108
- />
1109
- </label>
1110
- <label class="field">
1111
- <span>HTTP URL</span>
1112
- <input
1113
- .value=${t.signalForm.httpUrl}
1114
- @input=${i=>t.onSignalChange({httpUrl:i.target.value})}
1115
- placeholder="http://127.0.0.1:8080"
1116
- />
1117
- </label>
1118
- <label class="field">
1119
- <span>HTTP host</span>
1120
- <input
1121
- .value=${t.signalForm.httpHost}
1122
- @input=${i=>t.onSignalChange({httpHost:i.target.value})}
1123
- placeholder="127.0.0.1"
1124
- />
1125
- </label>
1126
- <label class="field">
1127
- <span>HTTP port</span>
1128
- <input
1129
- .value=${t.signalForm.httpPort}
1130
- @input=${i=>t.onSignalChange({httpPort:i.target.value})}
1131
- placeholder="8080"
1132
- />
1133
- </label>
1134
- <label class="field">
1135
- <span>CLI path</span>
1136
- <input
1137
- .value=${t.signalForm.cliPath}
1138
- @input=${i=>t.onSignalChange({cliPath:i.target.value})}
1139
- placeholder="signal-cli"
1140
- />
1141
- </label>
1142
- <label class="field">
1143
- <span>Auto start</span>
1144
- <select
1145
- .value=${t.signalForm.autoStart?"yes":"no"}
1146
- @change=${i=>t.onSignalChange({autoStart:i.target.value==="yes"})}
1147
- >
1148
- <option value="yes">Yes</option>
1149
- <option value="no">No</option>
1150
- </select>
1151
- </label>
1152
- <label class="field">
1153
- <span>Receive mode</span>
1154
- <select
1155
- .value=${t.signalForm.receiveMode}
1156
- @change=${i=>t.onSignalChange({receiveMode:i.target.value})}
1157
- >
1158
- <option value="">Default</option>
1159
- <option value="on-start">on-start</option>
1160
- <option value="manual">manual</option>
1161
- </select>
1162
- </label>
1163
- <label class="field">
1164
- <span>Ignore attachments</span>
1165
- <select
1166
- .value=${t.signalForm.ignoreAttachments?"yes":"no"}
1167
- @change=${i=>t.onSignalChange({ignoreAttachments:i.target.value==="yes"})}
1168
- >
1169
- <option value="yes">Yes</option>
1170
- <option value="no">No</option>
1171
- </select>
1172
- </label>
1173
- <label class="field">
1174
- <span>Ignore stories</span>
1175
- <select
1176
- .value=${t.signalForm.ignoreStories?"yes":"no"}
1177
- @change=${i=>t.onSignalChange({ignoreStories:i.target.value==="yes"})}
1178
- >
1179
- <option value="yes">Yes</option>
1180
- <option value="no">No</option>
1181
- </select>
1182
- </label>
1183
- <label class="field">
1184
- <span>Send read receipts</span>
1185
- <select
1186
- .value=${t.signalForm.sendReadReceipts?"yes":"no"}
1187
- @change=${i=>t.onSignalChange({sendReadReceipts:i.target.value==="yes"})}
1188
- >
1189
- <option value="yes">Yes</option>
1190
- <option value="no">No</option>
1191
- </select>
1192
- </label>
1193
- <label class="field">
1194
- <span>Allow from</span>
1195
- <input
1196
- .value=${t.signalForm.allowFrom}
1197
- @input=${i=>t.onSignalChange({allowFrom:i.target.value})}
1198
- placeholder="12345, +1555"
1199
- />
1200
- </label>
1201
- <label class="field">
1202
- <span>Media max MB</span>
1203
- <input
1204
- .value=${t.signalForm.mediaMaxMb}
1205
- @input=${i=>t.onSignalChange({mediaMaxMb:i.target.value})}
1206
- placeholder="8"
1207
- />
1208
- </label>
1209
- </div>
1210
-
1211
- ${t.signalStatus?c`<div class="callout" style="margin-top: 12px;">
1212
- ${t.signalStatus}
1213
- </div>`:f}
1214
-
1215
- <div class="row" style="margin-top: 14px;">
1216
- <button
1217
- class="btn primary"
1218
- ?disabled=${t.signalSaving}
1219
- @click=${()=>t.onSignalSave()}
1220
- >
1221
- ${t.signalSaving?"Saving…":"Save"}
1222
- </button>
1223
- <button class="btn" @click=${()=>t.onRefresh(!0)}>
1224
- Probe
1225
- </button>
1226
- </div>
1227
- </div>
1228
- `}case"imessage":{const n=s.imessage;return c`
1229
- <div class="card">
1230
- <div class="card-title">iMessage</div>
1231
- <div class="card-sub">imsg CLI and database availability.</div>
1232
-
1233
- <div class="status-list" style="margin-top: 16px;">
1234
- <div>
1235
- <span class="label">Configured</span>
1236
- <span>${n?.configured?"Yes":"No"}</span>
1237
- </div>
1238
- <div>
1239
- <span class="label">Running</span>
1240
- <span>${n?.running?"Yes":"No"}</span>
1241
- </div>
1242
- <div>
1243
- <span class="label">CLI</span>
1244
- <span>${n?.cliPath??"n/a"}</span>
1245
- </div>
1246
- <div>
1247
- <span class="label">DB</span>
1248
- <span>${n?.dbPath??"n/a"}</span>
1249
- </div>
1250
- <div>
1251
- <span class="label">Last start</span>
1252
- <span>
1253
- ${n?.lastStartAt?M(n.lastStartAt):"n/a"}
1254
- </span>
1255
- </div>
1256
- <div>
1257
- <span class="label">Last probe</span>
1258
- <span>
1259
- ${n?.lastProbeAt?M(n.lastProbeAt):"n/a"}
1260
- </span>
1261
- </div>
1262
- </div>
1263
-
1264
- ${n?.lastError?c`<div class="callout danger" style="margin-top: 12px;">
1265
- ${n.lastError}
1266
- </div>`:f}
1267
-
1268
- ${n?.probe&&!n.probe.ok?c`<div class="callout" style="margin-top: 12px;">
1269
- Probe failed · ${n.probe.error??"unknown error"}
1270
- </div>`:f}
1271
-
1272
- <div class="form-grid" style="margin-top: 16px;">
1273
- <label class="field">
1274
- <span>Enabled</span>
1275
- <select
1276
- .value=${t.imessageForm.enabled?"yes":"no"}
1277
- @change=${i=>t.onIMessageChange({enabled:i.target.value==="yes"})}
1278
- >
1279
- <option value="yes">Yes</option>
1280
- <option value="no">No</option>
1281
- </select>
1282
- </label>
1283
- <label class="field">
1284
- <span>CLI path</span>
1285
- <input
1286
- .value=${t.imessageForm.cliPath}
1287
- @input=${i=>t.onIMessageChange({cliPath:i.target.value})}
1288
- placeholder="imsg"
1289
- />
1290
- </label>
1291
- <label class="field">
1292
- <span>DB path</span>
1293
- <input
1294
- .value=${t.imessageForm.dbPath}
1295
- @input=${i=>t.onIMessageChange({dbPath:i.target.value})}
1296
- placeholder="~/Library/Messages/chat.db"
1297
- />
1298
- </label>
1299
- <label class="field">
1300
- <span>Service</span>
1301
- <select
1302
- .value=${t.imessageForm.service}
1303
- @change=${i=>t.onIMessageChange({service:i.target.value})}
1304
- >
1305
- <option value="auto">Auto</option>
1306
- <option value="imessage">iMessage</option>
1307
- <option value="sms">SMS</option>
1308
- </select>
1309
- </label>
1310
- <label class="field">
1311
- <span>Region</span>
1312
- <input
1313
- .value=${t.imessageForm.region}
1314
- @input=${i=>t.onIMessageChange({region:i.target.value})}
1315
- placeholder="US"
1316
- />
1317
- </label>
1318
- <label class="field">
1319
- <span>Allow from</span>
1320
- <input
1321
- .value=${t.imessageForm.allowFrom}
1322
- @input=${i=>t.onIMessageChange({allowFrom:i.target.value})}
1323
- placeholder="chat_id:101, +1555"
1324
- />
1325
- </label>
1326
- <label class="field">
1327
- <span>Include attachments</span>
1328
- <select
1329
- .value=${t.imessageForm.includeAttachments?"yes":"no"}
1330
- @change=${i=>t.onIMessageChange({includeAttachments:i.target.value==="yes"})}
1331
- >
1332
- <option value="yes">Yes</option>
1333
- <option value="no">No</option>
1334
- </select>
1335
- </label>
1336
- <label class="field">
1337
- <span>Media max MB</span>
1338
- <input
1339
- .value=${t.imessageForm.mediaMaxMb}
1340
- @input=${i=>t.onIMessageChange({mediaMaxMb:i.target.value})}
1341
- placeholder="16"
1342
- />
1343
- </label>
1344
- </div>
1345
-
1346
- ${t.imessageStatus?c`<div class="callout" style="margin-top: 12px;">
1347
- ${t.imessageStatus}
1348
- </div>`:f}
1349
-
1350
- <div class="row" style="margin-top: 14px;">
1351
- <button
1352
- class="btn primary"
1353
- ?disabled=${t.imessageSaving}
1354
- @click=${()=>t.onIMessageSave()}
1355
- >
1356
- ${t.imessageSaving?"Saving…":"Save"}
1357
- </button>
1358
- <button class="btn" @click=${()=>t.onRefresh(!0)}>
1359
- Probe
1360
- </button>
1361
- </div>
1362
- </div>
1363
- `}default:return f}}function En(e){const t=e.host??"unknown",s=e.ip?`(${e.ip})`:"",n=e.mode??"",i=e.version??"";return`${t} ${s} ${n} ${i}`.trim()}function xn(e){const t=e.ts??null;return t?M(t):"n/a"}function ct(e){return e?`${X(e)} (${M(e)})`:"n/a"}function Ln(e){if(e.totalTokens==null)return"n/a";const t=e.totalTokens??0,s=e.contextTokens??0;return s?`${t} / ${s}`:String(t)}function Fn(e){if(e==null)return"";try{return JSON.stringify(e,null,2)}catch{return String(e)}}function Tn(e){const t=e.state??{},s=t.nextRunAtMs?X(t.nextRunAtMs):"n/a",n=t.lastRunAtMs?X(t.lastRunAtMs):"n/a";return`${t.lastStatus??"n/a"} · next ${s} · last ${n}`}function Pn(e){const t=e.schedule;return t.kind==="at"?`At ${X(t.atMs)}`:t.kind==="every"?`Every ${rt(t.everyMs)}`:`Cron ${t.expr}${t.tz?` (${t.tz})`:""}`}function Rn(e){const t=e.payload;return t.kind==="systemEvent"?`System: ${t.text}`:`Agent: ${t.message}`}function _n(e){return c`
1364
- <section class="grid grid-cols-2">
1365
- <div class="card">
1366
- <div class="card-title">Scheduler</div>
1367
- <div class="card-sub">Gateway-owned cron scheduler status.</div>
1368
- <div class="stat-grid" style="margin-top: 16px;">
1369
- <div class="stat">
1370
- <div class="stat-label">Enabled</div>
1371
- <div class="stat-value">
1372
- ${e.status?e.status.enabled?"Yes":"No":"n/a"}
1373
- </div>
1374
- </div>
1375
- <div class="stat">
1376
- <div class="stat-label">Jobs</div>
1377
- <div class="stat-value">${e.status?.jobCount??"n/a"}</div>
1378
- </div>
1379
- <div class="stat">
1380
- <div class="stat-label">Next wake</div>
1381
- <div class="stat-value">${ct(e.status?.nextWakeAtMs??null)}</div>
1382
- </div>
1383
- </div>
1384
- <div class="row" style="margin-top: 12px;">
1385
- <button class="btn" ?disabled=${e.loading} @click=${e.onRefresh}>
1386
- ${e.loading?"Refreshing…":"Refresh"}
1387
- </button>
1388
- ${e.error?c`<span class="muted">${e.error}</span>`:f}
1389
- </div>
1390
- </div>
1391
-
1392
- <div class="card">
1393
- <div class="card-title">New Job</div>
1394
- <div class="card-sub">Create a scheduled wakeup or agent run.</div>
1395
- <div class="form-grid" style="margin-top: 16px;">
1396
- <label class="field">
1397
- <span>Name</span>
1398
- <input
1399
- .value=${e.form.name}
1400
- @input=${t=>e.onFormChange({name:t.target.value})}
1401
- />
1402
- </label>
1403
- <label class="field">
1404
- <span>Description</span>
1405
- <input
1406
- .value=${e.form.description}
1407
- @input=${t=>e.onFormChange({description:t.target.value})}
1408
- />
1409
- </label>
1410
- <label class="field checkbox">
1411
- <span>Enabled</span>
1412
- <input
1413
- type="checkbox"
1414
- .checked=${e.form.enabled}
1415
- @change=${t=>e.onFormChange({enabled:t.target.checked})}
1416
- />
1417
- </label>
1418
- <label class="field">
1419
- <span>Schedule</span>
1420
- <select
1421
- .value=${e.form.scheduleKind}
1422
- @change=${t=>e.onFormChange({scheduleKind:t.target.value})}
1423
- >
1424
- <option value="every">Every</option>
1425
- <option value="at">At</option>
1426
- <option value="cron">Cron</option>
1427
- </select>
1428
- </label>
1429
- </div>
1430
- ${In(e)}
1431
- <div class="form-grid" style="margin-top: 12px;">
1432
- <label class="field">
1433
- <span>Session</span>
1434
- <select
1435
- .value=${e.form.sessionTarget}
1436
- @change=${t=>e.onFormChange({sessionTarget:t.target.value})}
1437
- >
1438
- <option value="main">Main</option>
1439
- <option value="isolated">Isolated</option>
1440
- </select>
1441
- </label>
1442
- <label class="field">
1443
- <span>Wake mode</span>
1444
- <select
1445
- .value=${e.form.wakeMode}
1446
- @change=${t=>e.onFormChange({wakeMode:t.target.value})}
1447
- >
1448
- <option value="next-heartbeat">Next heartbeat</option>
1449
- <option value="now">Now</option>
1450
- </select>
1451
- </label>
1452
- <label class="field">
1453
- <span>Payload</span>
1454
- <select
1455
- .value=${e.form.payloadKind}
1456
- @change=${t=>e.onFormChange({payloadKind:t.target.value})}
1457
- >
1458
- <option value="systemEvent">System event</option>
1459
- <option value="agentTurn">Agent turn</option>
1460
- </select>
1461
- </label>
1462
- </div>
1463
- <label class="field" style="margin-top: 12px;">
1464
- <span>${e.form.payloadKind==="systemEvent"?"System text":"Agent message"}</span>
1465
- <textarea
1466
- .value=${e.form.payloadText}
1467
- @input=${t=>e.onFormChange({payloadText:t.target.value})}
1468
- rows="4"
1469
- ></textarea>
1470
- </label>
1471
- ${e.form.payloadKind==="agentTurn"?c`
1472
- <div class="form-grid" style="margin-top: 12px;">
1473
- <label class="field checkbox">
1474
- <span>Deliver</span>
1475
- <input
1476
- type="checkbox"
1477
- .checked=${e.form.deliver}
1478
- @change=${t=>e.onFormChange({deliver:t.target.checked})}
1479
- />
1480
- </label>
1481
- <label class="field">
1482
- <span>Channel</span>
1483
- <select
1484
- .value=${e.form.channel}
1485
- @change=${t=>e.onFormChange({channel:t.target.value})}
1486
- >
1487
- <option value="last">Last</option>
1488
- <option value="whatsapp">WhatsApp</option>
1489
- <option value="telegram">Telegram</option>
1490
- </select>
1491
- </label>
1492
- <label class="field">
1493
- <span>To</span>
1494
- <input
1495
- .value=${e.form.to}
1496
- @input=${t=>e.onFormChange({to:t.target.value})}
1497
- placeholder="+1555… or chat id"
1498
- />
1499
- </label>
1500
- <label class="field">
1501
- <span>Timeout (seconds)</span>
1502
- <input
1503
- .value=${e.form.timeoutSeconds}
1504
- @input=${t=>e.onFormChange({timeoutSeconds:t.target.value})}
1505
- />
1506
- </label>
1507
- ${e.form.sessionTarget==="isolated"?c`
1508
- <label class="field">
1509
- <span>Post to main prefix</span>
1510
- <input
1511
- .value=${e.form.postToMainPrefix}
1512
- @input=${t=>e.onFormChange({postToMainPrefix:t.target.value})}
1513
- />
1514
- </label>
1515
- `:f}
1516
- </div>
1517
- `:f}
1518
- <div class="row" style="margin-top: 14px;">
1519
- <button class="btn primary" ?disabled=${e.busy} @click=${e.onAdd}>
1520
- ${e.busy?"Saving…":"Add job"}
1521
- </button>
1522
- </div>
1523
- </div>
1524
- </section>
1525
-
1526
- <section class="card" style="margin-top: 18px;">
1527
- <div class="card-title">Jobs</div>
1528
- <div class="card-sub">All scheduled jobs stored in the gateway.</div>
1529
- ${e.jobs.length===0?c`<div class="muted" style="margin-top: 12px;">No jobs yet.</div>`:c`
1530
- <div class="list" style="margin-top: 12px;">
1531
- ${e.jobs.map(t=>Nn(t,e))}
1532
- </div>
1533
- `}
1534
- </section>
1535
-
1536
- <section class="card" style="margin-top: 18px;">
1537
- <div class="card-title">Run history</div>
1538
- <div class="card-sub">Latest runs for ${e.runsJobId??"(select a job)"}.</div>
1539
- ${e.runs.length===0?c`<div class="muted" style="margin-top: 12px;">No runs yet.</div>`:c`
1540
- <div class="list" style="margin-top: 12px;">
1541
- ${e.runs.map(t=>Kn(t))}
1542
- </div>
1543
- `}
1544
- </section>
1545
- `}function In(e){const t=e.form;return t.scheduleKind==="at"?c`
1546
- <label class="field" style="margin-top: 12px;">
1547
- <span>Run at</span>
1548
- <input
1549
- type="datetime-local"
1550
- .value=${t.scheduleAt}
1551
- @input=${s=>e.onFormChange({scheduleAt:s.target.value})}
1552
- />
1553
- </label>
1554
- `:t.scheduleKind==="every"?c`
1555
- <div class="form-grid" style="margin-top: 12px;">
1556
- <label class="field">
1557
- <span>Every</span>
1558
- <input
1559
- .value=${t.everyAmount}
1560
- @input=${s=>e.onFormChange({everyAmount:s.target.value})}
1561
- />
1562
- </label>
1563
- <label class="field">
1564
- <span>Unit</span>
1565
- <select
1566
- .value=${t.everyUnit}
1567
- @change=${s=>e.onFormChange({everyUnit:s.target.value})}
1568
- >
1569
- <option value="minutes">Minutes</option>
1570
- <option value="hours">Hours</option>
1571
- <option value="days">Days</option>
1572
- </select>
1573
- </label>
1574
- </div>
1575
- `:c`
1576
- <div class="form-grid" style="margin-top: 12px;">
1577
- <label class="field">
1578
- <span>Expression</span>
1579
- <input
1580
- .value=${t.cronExpr}
1581
- @input=${s=>e.onFormChange({cronExpr:s.target.value})}
1582
- />
1583
- </label>
1584
- <label class="field">
1585
- <span>Timezone (optional)</span>
1586
- <input
1587
- .value=${t.cronTz}
1588
- @input=${s=>e.onFormChange({cronTz:s.target.value})}
1589
- />
1590
- </label>
1591
- </div>
1592
- `}function Nn(e,t){return c`
1593
- <div class="list-item">
1594
- <div class="list-main">
1595
- <div class="list-title">${e.name}</div>
1596
- <div class="list-sub">${Pn(e)}</div>
1597
- <div class="muted">${Rn(e)}</div>
1598
- <div class="chip-row" style="margin-top: 6px;">
1599
- <span class="chip">${e.enabled?"enabled":"disabled"}</span>
1600
- <span class="chip">${e.sessionTarget}</span>
1601
- <span class="chip">${e.wakeMode}</span>
1602
- </div>
1603
- </div>
1604
- <div class="list-meta">
1605
- <div>${Tn(e)}</div>
1606
- <div class="row" style="justify-content: flex-end; margin-top: 8px;">
1607
- <button
1608
- class="btn"
1609
- ?disabled=${t.busy}
1610
- @click=${()=>t.onToggle(e,!e.enabled)}
1611
- >
1612
- ${e.enabled?"Disable":"Enable"}
1613
- </button>
1614
- <button class="btn" ?disabled=${t.busy} @click=${()=>t.onRun(e)}>
1615
- Run
1616
- </button>
1617
- <button
1618
- class="btn"
1619
- ?disabled=${t.busy}
1620
- @click=${()=>t.onLoadRuns(e.id)}
1621
- >
1622
- Runs
1623
- </button>
1624
- <button
1625
- class="btn danger"
1626
- ?disabled=${t.busy}
1627
- @click=${()=>t.onRemove(e)}
1628
- >
1629
- Remove
1630
- </button>
1631
- </div>
1632
- </div>
1633
- </div>
1634
- `}function Kn(e){return c`
1635
- <div class="list-item">
1636
- <div class="list-main">
1637
- <div class="list-title">${e.status}</div>
1638
- <div class="list-sub">${e.summary??""}</div>
1639
- </div>
1640
- <div class="list-meta">
1641
- <div>${X(e.ts)}</div>
1642
- <div class="muted">${e.durationMs??0}ms</div>
1643
- ${e.error?c`<div class="muted">${e.error}</div>`:f}
1644
- </div>
1645
- </div>
1646
- `}function Un(e){return c`
1647
- <section class="grid grid-cols-2">
1648
- <div class="card">
1649
- <div class="row" style="justify-content: space-between;">
1650
- <div>
1651
- <div class="card-title">Snapshots</div>
1652
- <div class="card-sub">Status, health, and heartbeat data.</div>
1653
- </div>
1654
- <button class="btn" ?disabled=${e.loading} @click=${e.onRefresh}>
1655
- ${e.loading?"Refreshing…":"Refresh"}
1656
- </button>
1657
- </div>
1658
- <div class="stack" style="margin-top: 12px;">
1659
- <div>
1660
- <div class="muted">Status</div>
1661
- <pre class="code-block">${JSON.stringify(e.status??{},null,2)}</pre>
1662
- </div>
1663
- <div>
1664
- <div class="muted">Health</div>
1665
- <pre class="code-block">${JSON.stringify(e.health??{},null,2)}</pre>
1666
- </div>
1667
- <div>
1668
- <div class="muted">Last heartbeat</div>
1669
- <pre class="code-block">${JSON.stringify(e.heartbeat??{},null,2)}</pre>
1670
- </div>
1671
- </div>
1672
- </div>
1673
-
1674
- <div class="card">
1675
- <div class="card-title">Manual RPC</div>
1676
- <div class="card-sub">Send a raw gateway method with JSON params.</div>
1677
- <div class="form-grid" style="margin-top: 16px;">
1678
- <label class="field">
1679
- <span>Method</span>
1680
- <input
1681
- .value=${e.callMethod}
1682
- @input=${t=>e.onCallMethodChange(t.target.value)}
1683
- placeholder="system-presence"
1684
- />
1685
- </label>
1686
- <label class="field">
1687
- <span>Params (JSON)</span>
1688
- <textarea
1689
- .value=${e.callParams}
1690
- @input=${t=>e.onCallParamsChange(t.target.value)}
1691
- rows="6"
1692
- ></textarea>
1693
- </label>
1694
- </div>
1695
- <div class="row" style="margin-top: 12px;">
1696
- <button class="btn primary" @click=${e.onCall}>Call</button>
1697
- </div>
1698
- ${e.callError?c`<div class="callout danger" style="margin-top: 12px;">
1699
- ${e.callError}
1700
- </div>`:f}
1701
- ${e.callResult?c`<pre class="code-block" style="margin-top: 12px;">${e.callResult}</pre>`:f}
1702
- </div>
1703
- </section>
1704
-
1705
- <section class="card" style="margin-top: 18px;">
1706
- <div class="card-title">Models</div>
1707
- <div class="card-sub">Catalog from models.list.</div>
1708
- <pre class="code-block" style="margin-top: 12px;">${JSON.stringify(e.models??[],null,2)}</pre>
1709
- </section>
1710
-
1711
- <section class="card" style="margin-top: 18px;">
1712
- <div class="card-title">Event Log</div>
1713
- <div class="card-sub">Latest gateway events.</div>
1714
- ${e.eventLog.length===0?c`<div class="muted" style="margin-top: 12px;">No events yet.</div>`:c`
1715
- <div class="list" style="margin-top: 12px;">
1716
- ${e.eventLog.map(t=>c`
1717
- <div class="list-item">
1718
- <div class="list-main">
1719
- <div class="list-title">${t.event}</div>
1720
- <div class="list-sub">${new Date(t.ts).toLocaleTimeString()}</div>
1721
- </div>
1722
- <div class="list-meta">
1723
- <pre class="code-block">${Fn(t.payload)}</pre>
1724
- </div>
1725
- </div>
1726
- `)}
1727
- </div>
1728
- `}
1729
- </section>
1730
- `}function On(e){return c`
1731
- <section class="card">
1732
- <div class="row" style="justify-content: space-between;">
1733
- <div>
1734
- <div class="card-title">Connected Instances</div>
1735
- <div class="card-sub">Presence beacons from the gateway and clients.</div>
1736
- </div>
1737
- <button class="btn" ?disabled=${e.loading} @click=${e.onRefresh}>
1738
- ${e.loading?"Loading…":"Refresh"}
1739
- </button>
1740
- </div>
1741
- ${e.lastError?c`<div class="callout danger" style="margin-top: 12px;">
1742
- ${e.lastError}
1743
- </div>`:f}
1744
- ${e.statusMessage?c`<div class="callout" style="margin-top: 12px;">
1745
- ${e.statusMessage}
1746
- </div>`:f}
1747
- <div class="list" style="margin-top: 16px;">
1748
- ${e.entries.length===0?c`<div class="muted">No instances reported yet.</div>`:e.entries.map(t=>Dn(t))}
1749
- </div>
1750
- </section>
1751
- `}function Dn(e){const t=e.lastInputSeconds!=null?`${e.lastInputSeconds}s ago`:"n/a",s=e.mode??"unknown";return c`
1752
- <div class="list-item">
1753
- <div class="list-main">
1754
- <div class="list-title">${e.host??"unknown host"}</div>
1755
- <div class="list-sub">${En(e)}</div>
1756
- <div class="chip-row">
1757
- <span class="chip">${s}</span>
1758
- ${e.platform?c`<span class="chip">${e.platform}</span>`:f}
1759
- ${e.deviceFamily?c`<span class="chip">${e.deviceFamily}</span>`:f}
1760
- ${e.modelIdentifier?c`<span class="chip">${e.modelIdentifier}</span>`:f}
1761
- ${e.version?c`<span class="chip">${e.version}</span>`:f}
1762
- </div>
1763
- </div>
1764
- <div class="list-meta">
1765
- <div>${xn(e)}</div>
1766
- <div class="muted">Last input ${t}</div>
1767
- <div class="muted">Reason ${e.reason??""}</div>
1768
- </div>
1769
- </div>
1770
- `}function jn(e){return c`
1771
- <section class="card">
1772
- <div class="row" style="justify-content: space-between;">
1773
- <div>
1774
- <div class="card-title">Nodes</div>
1775
- <div class="card-sub">Paired devices and live connections.</div>
1776
- </div>
1777
- <button class="btn" ?disabled=${e.loading} @click=${e.onRefresh}>
1778
- ${e.loading?"Loading…":"Refresh"}
1779
- </button>
1780
- </div>
1781
- <div class="list" style="margin-top: 16px;">
1782
- ${e.nodes.length===0?c`<div class="muted">No nodes found.</div>`:e.nodes.map(t=>qn(t))}
1783
- </div>
1784
- </section>
1785
- `}function qn(e){const t=!!e.connected,s=!!e.paired,n=typeof e.displayName=="string"&&e.displayName.trim()||(typeof e.nodeId=="string"?e.nodeId:"unknown"),i=Array.isArray(e.caps)?e.caps:[],a=Array.isArray(e.commands)?e.commands:[];return c`
1786
- <div class="list-item">
1787
- <div class="list-main">
1788
- <div class="list-title">${n}</div>
1789
- <div class="list-sub">
1790
- ${typeof e.nodeId=="string"?e.nodeId:""}
1791
- ${typeof e.remoteIp=="string"?` · ${e.remoteIp}`:""}
1792
- ${typeof e.version=="string"?` · ${e.version}`:""}
1793
- </div>
1794
- <div class="chip-row" style="margin-top: 6px;">
1795
- <span class="chip">${s?"paired":"unpaired"}</span>
1796
- <span class="chip ${t?"chip-ok":"chip-warn"}">
1797
- ${t?"connected":"offline"}
1798
- </span>
1799
- ${i.slice(0,12).map(l=>c`<span class="chip">${String(l)}</span>`)}
1800
- ${a.slice(0,8).map(l=>c`<span class="chip">${String(l)}</span>`)}
1801
- </div>
1802
- </div>
1803
- </div>
1804
- `}function Bn(e){const t=e.hello?.snapshot,s=t?.uptimeMs?rt(t.uptimeMs):"n/a",n=t?.policy?.tickIntervalMs?`${t.policy.tickIntervalMs}ms`:"n/a";return c`
1805
- <section class="grid grid-cols-2">
1806
- <div class="card">
1807
- <div class="card-title">Gateway Access</div>
1808
- <div class="card-sub">Where the dashboard connects and how it authenticates.</div>
1809
- <div class="form-grid" style="margin-top: 16px;">
1810
- <label class="field">
1811
- <span>WebSocket URL</span>
1812
- <input
1813
- .value=${e.settings.gatewayUrl}
1814
- @input=${i=>{const a=i.target.value;e.onSettingsChange({...e.settings,gatewayUrl:a})}}
1815
- placeholder="ws://100.x.y.z:18789"
1816
- />
1817
- </label>
1818
- <label class="field">
1819
- <span>Gateway Token</span>
1820
- <input
1821
- .value=${e.settings.token}
1822
- @input=${i=>{const a=i.target.value;e.onSettingsChange({...e.settings,token:a})}}
1823
- placeholder="CLAWDBOT_GATEWAY_TOKEN"
1824
- />
1825
- </label>
1826
- <label class="field">
1827
- <span>Password (not stored)</span>
1828
- <input
1829
- type="password"
1830
- .value=${e.password}
1831
- @input=${i=>{const a=i.target.value;e.onPasswordChange(a)}}
1832
- placeholder="system or shared password"
1833
- />
1834
- </label>
1835
- <label class="field">
1836
- <span>Default Session Key</span>
1837
- <input
1838
- .value=${e.settings.sessionKey}
1839
- @input=${i=>{const a=i.target.value;e.onSessionKeyChange(a)}}
1840
- />
1841
- </label>
1842
- </div>
1843
- <div class="row" style="margin-top: 14px;">
1844
- <button class="btn" @click=${()=>e.onRefresh()}>Refresh</button>
1845
- <span class="muted">Reconnect to apply changes.</span>
1846
- </div>
1847
- </div>
1848
-
1849
- <div class="card">
1850
- <div class="card-title">Snapshot</div>
1851
- <div class="card-sub">Latest gateway handshake information.</div>
1852
- <div class="stat-grid" style="margin-top: 16px;">
1853
- <div class="stat">
1854
- <div class="stat-label">Status</div>
1855
- <div class="stat-value ${e.connected?"ok":"warn"}">
1856
- ${e.connected?"Connected":"Disconnected"}
1857
- </div>
1858
- </div>
1859
- <div class="stat">
1860
- <div class="stat-label">Uptime</div>
1861
- <div class="stat-value">${s}</div>
1862
- </div>
1863
- <div class="stat">
1864
- <div class="stat-label">Tick Interval</div>
1865
- <div class="stat-value">${n}</div>
1866
- </div>
1867
- <div class="stat">
1868
- <div class="stat-label">Last Providers Refresh</div>
1869
- <div class="stat-value">
1870
- ${e.lastProvidersRefresh?M(e.lastProvidersRefresh):"n/a"}
1871
- </div>
1872
- </div>
1873
- </div>
1874
- ${e.lastError?c`<div class="callout danger" style="margin-top: 14px;">
1875
- ${e.lastError}
1876
- </div>`:c`<div class="callout" style="margin-top: 14px;">
1877
- Use Connections to link WhatsApp, Telegram, Discord, Signal, or iMessage.
1878
- </div>`}
1879
- </div>
1880
- </section>
1881
-
1882
- <section class="grid grid-cols-3" style="margin-top: 18px;">
1883
- <div class="card stat-card">
1884
- <div class="stat-label">Instances</div>
1885
- <div class="stat-value">${e.presenceCount}</div>
1886
- <div class="muted">Presence beacons in the last 5 minutes.</div>
1887
- </div>
1888
- <div class="card stat-card">
1889
- <div class="stat-label">Sessions</div>
1890
- <div class="stat-value">${e.sessionsCount??"n/a"}</div>
1891
- <div class="muted">Recent session keys tracked by the gateway.</div>
1892
- </div>
1893
- <div class="card stat-card">
1894
- <div class="stat-label">Cron</div>
1895
- <div class="stat-value">
1896
- ${e.cronEnabled==null?"n/a":e.cronEnabled?"Enabled":"Disabled"}
1897
- </div>
1898
- <div class="muted">Next wake ${ct(e.cronNext)}</div>
1899
- </div>
1900
- </section>
1901
-
1902
- <section class="card" style="margin-top: 18px;">
1903
- <div class="card-title">Notes</div>
1904
- <div class="card-sub">Quick reminders for remote control setups.</div>
1905
- <div class="note-grid" style="margin-top: 14px;">
1906
- <div>
1907
- <div class="note-title">Tailscale serve</div>
1908
- <div class="muted">
1909
- Prefer serve mode to keep the gateway on loopback with tailnet auth.
1910
- </div>
1911
- </div>
1912
- <div>
1913
- <div class="note-title">Session hygiene</div>
1914
- <div class="muted">Use /new or sessions.patch to reset context.</div>
1915
- </div>
1916
- <div>
1917
- <div class="note-title">Cron reminders</div>
1918
- <div class="muted">Use isolated sessions for recurring runs.</div>
1919
- </div>
1920
- </div>
1921
- </section>
1922
- `}const Hn=["","off","minimal","low","medium","high"],Wn=["","off","on"];function Jn(e){const t=e.result?.sessions??[];return c`
1923
- <section class="card">
1924
- <div class="row" style="justify-content: space-between;">
1925
- <div>
1926
- <div class="card-title">Sessions</div>
1927
- <div class="card-sub">Active session keys and per-session overrides.</div>
1928
- </div>
1929
- <button class="btn" ?disabled=${e.loading} @click=${e.onRefresh}>
1930
- ${e.loading?"Loading…":"Refresh"}
1931
- </button>
1932
- </div>
1933
-
1934
- <div class="filters" style="margin-top: 14px;">
1935
- <label class="field">
1936
- <span>Active within (minutes)</span>
1937
- <input
1938
- .value=${e.activeMinutes}
1939
- @input=${s=>e.onFiltersChange({activeMinutes:s.target.value,limit:e.limit,includeGlobal:e.includeGlobal,includeUnknown:e.includeUnknown})}
1940
- />
1941
- </label>
1942
- <label class="field">
1943
- <span>Limit</span>
1944
- <input
1945
- .value=${e.limit}
1946
- @input=${s=>e.onFiltersChange({activeMinutes:e.activeMinutes,limit:s.target.value,includeGlobal:e.includeGlobal,includeUnknown:e.includeUnknown})}
1947
- />
1948
- </label>
1949
- <label class="field checkbox">
1950
- <span>Include global</span>
1951
- <input
1952
- type="checkbox"
1953
- .checked=${e.includeGlobal}
1954
- @change=${s=>e.onFiltersChange({activeMinutes:e.activeMinutes,limit:e.limit,includeGlobal:s.target.checked,includeUnknown:e.includeUnknown})}
1955
- />
1956
- </label>
1957
- <label class="field checkbox">
1958
- <span>Include unknown</span>
1959
- <input
1960
- type="checkbox"
1961
- .checked=${e.includeUnknown}
1962
- @change=${s=>e.onFiltersChange({activeMinutes:e.activeMinutes,limit:e.limit,includeGlobal:e.includeGlobal,includeUnknown:s.target.checked})}
1963
- />
1964
- </label>
1965
- </div>
1966
-
1967
- ${e.error?c`<div class="callout danger" style="margin-top: 12px;">${e.error}</div>`:f}
1968
-
1969
- <div class="muted" style="margin-top: 12px;">
1970
- ${e.result?`Store: ${e.result.path}`:""}
1971
- </div>
1972
-
1973
- <div class="table" style="margin-top: 16px;">
1974
- <div class="table-head">
1975
- <div>Key</div>
1976
- <div>Kind</div>
1977
- <div>Updated</div>
1978
- <div>Tokens</div>
1979
- <div>Thinking</div>
1980
- <div>Verbose</div>
1981
- </div>
1982
- ${t.length===0?c`<div class="muted">No sessions found.</div>`:t.map(s=>Gn(s,e.onPatch))}
1983
- </div>
1984
- </section>
1985
- `}function Gn(e,t){const s=e.updatedAt?M(e.updatedAt):"n/a",n=e.thinkingLevel??"",i=e.verboseLevel??"";return c`
1986
- <div class="table-row">
1987
- <div class="mono">${e.displayName??e.key}</div>
1988
- <div>${e.kind}</div>
1989
- <div>${s}</div>
1990
- <div>${Ln(e)}</div>
1991
- <div>
1992
- <select
1993
- .value=${n}
1994
- @change=${a=>{const l=a.target.value;t(e.key,{thinkingLevel:l||null})}}
1995
- >
1996
- ${Hn.map(a=>c`<option value=${a}>${a||"inherit"}</option>`)}
1997
- </select>
1998
- </div>
1999
- <div>
2000
- <select
2001
- .value=${i}
2002
- @change=${a=>{const l=a.target.value;t(e.key,{verboseLevel:l||null})}}
2003
- >
2004
- ${Wn.map(a=>c`<option value=${a}>${a||"inherit"}</option>`)}
2005
- </select>
2006
- </div>
2007
- </div>
2008
- `}function Yn(e){const t=e.report?.skills??[],s=e.filter.trim().toLowerCase(),n=s?t.filter(i=>[i.name,i.description,i.source].join(" ").toLowerCase().includes(s)):t;return c`
2009
- <section class="card">
2010
- <div class="row" style="justify-content: space-between;">
2011
- <div>
2012
- <div class="card-title">Skills</div>
2013
- <div class="card-sub">Bundled, managed, and workspace skills.</div>
2014
- </div>
2015
- <button class="btn" ?disabled=${e.loading} @click=${e.onRefresh}>
2016
- ${e.loading?"Loading…":"Refresh"}
2017
- </button>
2018
- </div>
2019
-
2020
- <div class="filters" style="margin-top: 14px;">
2021
- <label class="field" style="flex: 1;">
2022
- <span>Filter</span>
2023
- <input
2024
- .value=${e.filter}
2025
- @input=${i=>e.onFilterChange(i.target.value)}
2026
- placeholder="Search skills"
2027
- />
2028
- </label>
2029
- <div class="muted">${n.length} shown</div>
2030
- </div>
2031
-
2032
- ${e.error?c`<div class="callout danger" style="margin-top: 12px;">${e.error}</div>`:f}
2033
-
2034
- ${n.length===0?c`<div class="muted" style="margin-top: 16px;">No skills found.</div>`:c`
2035
- <div class="list" style="margin-top: 16px;">
2036
- ${n.map(i=>zn(i,e))}
2037
- </div>
2038
- `}
2039
- </section>
2040
- `}function zn(e,t){const s=t.busyKey===e.skillKey||t.busyKey===e.name,n=t.edits[e.skillKey]??"",i=[...e.missing.bins.map(l=>`bin:${l}`),...e.missing.env.map(l=>`env:${l}`),...e.missing.config.map(l=>`config:${l}`),...e.missing.os.map(l=>`os:${l}`)],a=[];return e.disabled&&a.push("disabled"),e.blockedByAllowlist&&a.push("blocked by allowlist"),c`
2041
- <div class="list-item">
2042
- <div class="list-main">
2043
- <div class="list-title">
2044
- ${e.emoji?`${e.emoji} `:""}${e.name}
2045
- </div>
2046
- <div class="list-sub">${$n(e.description,140)}</div>
2047
- <div class="chip-row" style="margin-top: 6px;">
2048
- <span class="chip">${e.source}</span>
2049
- <span class="chip ${e.eligible?"chip-ok":"chip-warn"}">
2050
- ${e.eligible?"eligible":"blocked"}
2051
- </span>
2052
- ${e.disabled?c`<span class="chip chip-warn">disabled</span>`:f}
2053
- </div>
2054
- ${i.length>0?c`
2055
- <div class="muted" style="margin-top: 6px;">
2056
- Missing: ${i.join(", ")}
2057
- </div>
2058
- `:f}
2059
- ${a.length>0?c`
2060
- <div class="muted" style="margin-top: 6px;">
2061
- Reason: ${a.join(", ")}
2062
- </div>
2063
- `:f}
2064
- </div>
2065
- <div class="list-meta">
2066
- <div class="row" style="justify-content: flex-end; flex-wrap: wrap;">
2067
- <button
2068
- class="btn"
2069
- ?disabled=${s}
2070
- @click=${()=>t.onToggle(e.skillKey,e.disabled)}
2071
- >
2072
- ${e.disabled?"Enable":"Disable"}
2073
- </button>
2074
- ${e.install.length>0?c`<button
2075
- class="btn"
2076
- ?disabled=${s}
2077
- @click=${()=>t.onInstall(e.name,e.install[0].id)}
2078
- >
2079
- ${e.install[0].label}
2080
- </button>`:f}
2081
- </div>
2082
- ${e.primaryEnv?c`
2083
- <div class="field" style="margin-top: 10px;">
2084
- <span>API key</span>
2085
- <input
2086
- type="password"
2087
- .value=${n}
2088
- @input=${l=>t.onEdit(e.skillKey,l.target.value)}
2089
- />
2090
- </div>
2091
- <button
2092
- class="btn primary"
2093
- style="margin-top: 8px;"
2094
- ?disabled=${s}
2095
- @click=${()=>t.onSaveKey(e.skillKey)}
2096
- >
2097
- Save key
2098
- </button>
2099
- `:f}
2100
- </div>
2101
- </div>
2102
- `}const xe={reactions:!0,stickers:!0,polls:!0,permissions:!0,messages:!0,threads:!0,pins:!0,search:!0,memberInfo:!0,roleInfo:!0,channelInfo:!0,voiceStatus:!0,events:!0,roles:!1,moderation:!1},R={reactions:!0,messages:!0,pins:!0,memberInfo:!0,emojiList:!0};async function L(e,t){if(!(!e.client||!e.connected)&&!e.providersLoading){e.providersLoading=!0,e.providersError=null;try{const s=await e.client.request("providers.status",{probe:t,timeoutMs:8e3});e.providersSnapshot=s,e.providersLastSuccess=Date.now(),e.telegramTokenLocked=s.telegram.tokenSource==="env",e.discordTokenLocked=s.discord?.tokenSource==="env",e.slackTokenLocked=s.slack?.botTokenSource==="env",e.slackAppTokenLocked=s.slack?.appTokenSource==="env"}catch(s){e.providersError=String(s)}finally{e.providersLoading=!1}}}async function Vn(e,t){if(!(!e.client||!e.connected||e.whatsappBusy)){e.whatsappBusy=!0;try{const s=await e.client.request("web.login.start",{force:t,timeoutMs:3e4});e.whatsappLoginMessage=s.message??null,e.whatsappLoginQrDataUrl=s.qrDataUrl??null,e.whatsappLoginConnected=null}catch(s){e.whatsappLoginMessage=String(s),e.whatsappLoginQrDataUrl=null,e.whatsappLoginConnected=null}finally{e.whatsappBusy=!1}}}async function Qn(e){if(!(!e.client||!e.connected||e.whatsappBusy)){e.whatsappBusy=!0;try{const t=await e.client.request("web.login.wait",{timeoutMs:12e4});e.whatsappLoginMessage=t.message??null,e.whatsappLoginConnected=t.connected??null,t.connected&&(e.whatsappLoginQrDataUrl=null)}catch(t){e.whatsappLoginMessage=String(t),e.whatsappLoginConnected=null}finally{e.whatsappBusy=!1}}}async function Xn(e){if(!(!e.client||!e.connected||e.whatsappBusy)){e.whatsappBusy=!0;try{await e.client.request("web.logout",{}),e.whatsappLoginMessage="Logged out.",e.whatsappLoginQrDataUrl=null,e.whatsappLoginConnected=null}catch(t){e.whatsappLoginMessage=String(t)}finally{e.whatsappBusy=!1}}}function Zn(e,t){e.telegramForm={...e.telegramForm,...t}}function es(e,t){if(t.actions){e.discordForm={...e.discordForm,...t,actions:{...e.discordForm.actions,...t.actions}};return}e.discordForm={...e.discordForm,...t}}function ts(e,t){if(t.actions){e.slackForm={...e.slackForm,...t,actions:{...e.slackForm.actions,...t.actions}};return}e.slackForm={...e.slackForm,...t}}function ns(e,t){e.signalForm={...e.signalForm,...t}}function ss(e,t){e.imessageForm={...e.imessageForm,...t}}async function is(e){if(!(!e.client||!e.connected)&&!e.telegramSaving){e.telegramSaving=!0,e.telegramConfigStatus=null;try{const s={...e.configSnapshot?.config??{}},n={...s.telegram??{}};if(!e.telegramTokenLocked){const v=e.telegramForm.token.trim();v?n.botToken=v:delete n.botToken}const i=n.groups&&typeof n.groups=="object"?{...n.groups}:{},a=i["*"]&&typeof i["*"]=="object"?{...i["*"]}:{};a.requireMention=e.telegramForm.requireMention,i["*"]=a,n.groups=i,delete n.requireMention;const l=T(e.telegramForm.allowFrom);l.length>0?n.allowFrom=l:delete n.allowFrom;const o=e.telegramForm.proxy.trim();o?n.proxy=o:delete n.proxy;const r=e.telegramForm.webhookUrl.trim();r?n.webhookUrl=r:delete n.webhookUrl;const d=e.telegramForm.webhookSecret.trim();d?n.webhookSecret=d:delete n.webhookSecret;const p=e.telegramForm.webhookPath.trim();p?n.webhookPath=p:delete n.webhookPath,s.telegram=n;const u=`${JSON.stringify(s,null,2).trimEnd()}
2103
- `;await e.client.request("config.set",{raw:u}),e.telegramConfigStatus="Saved. Restart gateway if needed."}catch(t){e.telegramConfigStatus=String(t)}finally{e.telegramSaving=!1}}}async function as(e){if(!(!e.client||!e.connected)&&!e.discordSaving){e.discordSaving=!0,e.discordConfigStatus=null;try{const s={...e.configSnapshot?.config??{}},n={...s.discord??{}},i=e.discordForm;if(i.enabled?delete n.enabled:n.enabled=!1,!e.discordTokenLocked){const b=i.token.trim();b?n.token=b:delete n.token}const a=T(i.allowFrom),l=T(i.groupChannels),o={...n.dm??{}};i.dmEnabled?delete o.enabled:o.enabled=!1,a.length>0?o.allowFrom=a:delete o.allowFrom,i.groupEnabled?o.groupEnabled=!0:delete o.groupEnabled,l.length>0?o.groupChannels=l:delete o.groupChannels,Object.keys(o).length>0?n.dm=o:delete n.dm;const r=Number(i.mediaMaxMb);Number.isFinite(r)&&r>0?n.mediaMaxMb=r:delete n.mediaMaxMb;const d=i.historyLimit.trim();if(d.length===0)delete n.historyLimit;else{const b=Number(d);Number.isFinite(b)&&b>=0?n.historyLimit=b:delete n.historyLimit}const p=i.textChunkLimit.trim();if(p.length===0)delete n.textChunkLimit;else{const b=Number(p);Number.isFinite(b)&&b>0?n.textChunkLimit=b:delete n.textChunkLimit}i.replyToMode==="off"?delete n.replyToMode:n.replyToMode=i.replyToMode;const u=Array.isArray(i.guilds)?i.guilds:[],v={};u.forEach(b=>{const A=String(b.key??"").trim();if(!A)return;const C={},P=String(b.slug??"").trim();P&&(C.slug=P),b.requireMention&&(C.requireMention=!0),(b.reactionNotifications==="off"||b.reactionNotifications==="all"||b.reactionNotifications==="own"||b.reactionNotifications==="allowlist")&&(C.reactionNotifications=b.reactionNotifications);const x=T(b.users);x.length>0&&(C.users=x);const E={};(Array.isArray(b.channels)?b.channels:[]).forEach(j=>{const ne=String(j.key??"").trim();if(!ne)return;const q={};j.allow===!1&&(q.allow=!1),j.requireMention&&(q.requireMention=!0),E[ne]=q}),Object.keys(E).length>0&&(C.channels=E),v[A]=C}),Object.keys(v).length>0?n.guilds=v:delete n.guilds;const $={},y=b=>{const A=i.actions[b];A!==xe[b]&&($[b]=A)};y("reactions"),y("stickers"),y("polls"),y("permissions"),y("messages"),y("threads"),y("pins"),y("search"),y("memberInfo"),y("roleInfo"),y("channelInfo"),y("voiceStatus"),y("events"),y("roles"),y("moderation"),Object.keys($).length>0?n.actions=$:delete n.actions;const w={...n.slashCommand??{}};i.slashEnabled?w.enabled=!0:delete w.enabled,i.slashName.trim()?w.name=i.slashName.trim():delete w.name,i.slashSessionPrefix.trim()?w.sessionPrefix=i.slashSessionPrefix.trim():delete w.sessionPrefix,i.slashEphemeral?delete w.ephemeral:w.ephemeral=!1,Object.keys(w).length>0?n.slashCommand=w:delete n.slashCommand,Object.keys(n).length>0?s.discord=n:delete s.discord;const S=`${JSON.stringify(s,null,2).trimEnd()}
2104
- `;await e.client.request("config.set",{raw:S}),e.discordConfigStatus="Saved. Restart gateway if needed."}catch(t){e.discordConfigStatus=String(t)}finally{e.discordSaving=!1}}}async function ls(e){if(!(!e.client||!e.connected)&&!e.slackSaving){e.slackSaving=!0,e.slackConfigStatus=null;try{const s={...e.configSnapshot?.config??{}},n={...s.slack??{}},i=e.slackForm;if(i.enabled?delete n.enabled:n.enabled=!1,!e.slackTokenLocked){const S=i.botToken.trim();S?n.botToken=S:delete n.botToken}if(!e.slackAppTokenLocked){const S=i.appToken.trim();S?n.appToken=S:delete n.appToken}const a={...n.dm??{}};a.enabled=i.dmEnabled;const l=T(i.allowFrom);l.length>0?a.allowFrom=l:delete a.allowFrom,i.groupEnabled?a.groupEnabled=!0:delete a.groupEnabled;const o=T(i.groupChannels);o.length>0?a.groupChannels=o:delete a.groupChannels,Object.keys(a).length>0?n.dm=a:delete n.dm;const r=Number.parseFloat(i.mediaMaxMb);Number.isFinite(r)&&r>0?n.mediaMaxMb=r:delete n.mediaMaxMb;const d=Number.parseInt(i.textChunkLimit,10);Number.isFinite(d)&&d>0?n.textChunkLimit=d:delete n.textChunkLimit,i.reactionNotifications==="own"?delete n.reactionNotifications:n.reactionNotifications=i.reactionNotifications;const p=T(i.reactionAllowlist);p.length>0?n.reactionAllowlist=p:delete n.reactionAllowlist;const u={...n.slashCommand??{}};i.slashEnabled?u.enabled=!0:delete u.enabled,i.slashName.trim()?u.name=i.slashName.trim():delete u.name,i.slashSessionPrefix.trim()?u.sessionPrefix=i.slashSessionPrefix.trim():delete u.sessionPrefix,i.slashEphemeral?delete u.ephemeral:u.ephemeral=!1,Object.keys(u).length>0?n.slashCommand=u:delete n.slashCommand;const v={},$=S=>{const b=i.actions[S];b!==R[S]&&(v[S]=b)};$("reactions"),$("messages"),$("pins"),$("memberInfo"),$("emojiList"),Object.keys(v).length>0?n.actions=v:delete n.actions;const y=i.channels.map(S=>{const b=S.key.trim();if(!b)return null;const A={allow:S.allow,requireMention:S.requireMention};return[b,A]}).filter(S=>!!S);y.length>0?n.channels=Object.fromEntries(y):delete n.channels,Object.keys(n).length>0?s.slack=n:delete s.slack;const w=`${JSON.stringify(s,null,2).trimEnd()}
2105
- `;await e.client.request("config.set",{raw:w}),e.slackConfigStatus="Saved. Restart gateway if needed."}catch(t){e.slackConfigStatus=String(t)}finally{e.slackSaving=!1}}}async function os(e){if(!(!e.client||!e.connected)&&!e.signalSaving){e.signalSaving=!0,e.signalConfigStatus=null;try{const s={...e.configSnapshot?.config??{}},n={...s.signal??{}},i=e.signalForm;i.enabled?delete n.enabled:n.enabled=!1;const a=i.account.trim();a?n.account=a:delete n.account;const l=i.httpUrl.trim();l?n.httpUrl=l:delete n.httpUrl;const o=i.httpHost.trim();o?n.httpHost=o:delete n.httpHost;const r=Number(i.httpPort);Number.isFinite(r)&&r>0?n.httpPort=r:delete n.httpPort;const d=i.cliPath.trim();d?n.cliPath=d:delete n.cliPath,i.autoStart?delete n.autoStart:n.autoStart=!1,i.receiveMode==="on-start"||i.receiveMode==="manual"?n.receiveMode=i.receiveMode:delete n.receiveMode,i.ignoreAttachments?n.ignoreAttachments=!0:delete n.ignoreAttachments,i.ignoreStories?n.ignoreStories=!0:delete n.ignoreStories,i.sendReadReceipts?n.sendReadReceipts=!0:delete n.sendReadReceipts;const p=T(i.allowFrom);p.length>0?n.allowFrom=p:delete n.allowFrom;const u=Number(i.mediaMaxMb);Number.isFinite(u)&&u>0?n.mediaMaxMb=u:delete n.mediaMaxMb,Object.keys(n).length>0?s.signal=n:delete s.signal;const v=`${JSON.stringify(s,null,2).trimEnd()}
2106
- `;await e.client.request("config.set",{raw:v}),e.signalConfigStatus="Saved. Restart gateway if needed."}catch(t){e.signalConfigStatus=String(t)}finally{e.signalSaving=!1}}}async function rs(e){if(!(!e.client||!e.connected)&&!e.imessageSaving){e.imessageSaving=!0,e.imessageConfigStatus=null;try{const s={...e.configSnapshot?.config??{}},n={...s.imessage??{}},i=e.imessageForm;i.enabled?delete n.enabled:n.enabled=!1;const a=i.cliPath.trim();a?n.cliPath=a:delete n.cliPath;const l=i.dbPath.trim();l?n.dbPath=l:delete n.dbPath,i.service==="auto"?delete n.service:n.service=i.service;const o=i.region.trim();o?n.region=o:delete n.region;const r=T(i.allowFrom);r.length>0?n.allowFrom=r:delete n.allowFrom,i.includeAttachments?n.includeAttachments=!0:delete n.includeAttachments;const d=Number(i.mediaMaxMb);Number.isFinite(d)&&d>0?n.mediaMaxMb=d:delete n.mediaMaxMb,Object.keys(n).length>0?s.imessage=n:delete s.imessage;const p=`${JSON.stringify(s,null,2).trimEnd()}
2107
- `;await e.client.request("config.set",{raw:p}),e.imessageConfigStatus="Saved. Restart gateway if needed."}catch(t){e.imessageConfigStatus=String(t)}finally{e.imessageSaving=!1}}}async function ye(e){if(!(!e.client||!e.connected)&&!e.presenceLoading){e.presenceLoading=!0,e.presenceError=null,e.presenceStatus=null;try{const t=await e.client.request("system-presence",{});Array.isArray(t)?(e.presenceEntries=t,e.presenceStatus=t.length===0?"No instances yet.":null):(e.presenceEntries=[],e.presenceStatus="No presence payload.")}catch(t){e.presenceError=String(t)}finally{e.presenceLoading=!1}}}async function J(e){if(!(!e.client||!e.connected)&&!e.sessionsLoading){e.sessionsLoading=!0,e.sessionsError=null;try{const t={includeGlobal:e.sessionsIncludeGlobal,includeUnknown:e.sessionsIncludeUnknown},s=de(e.sessionsFilterActive,0),n=de(e.sessionsFilterLimit,0);s>0&&(t.activeMinutes=s),n>0&&(t.limit=n);const i=await e.client.request("sessions.list",t);i&&(e.sessionsResult=i)}catch(t){e.sessionsError=String(t)}finally{e.sessionsLoading=!1}}}async function cs(e,t,s){if(!e.client||!e.connected)return;const n={key:t};"thinkingLevel"in s&&(n.thinkingLevel=s.thinkingLevel),"verboseLevel"in s&&(n.verboseLevel=s.verboseLevel);try{await e.client.request("sessions.patch",n),await J(e)}catch(i){e.sessionsError=String(i)}}async function te(e){if(!(!e.client||!e.connected)&&!e.skillsLoading){e.skillsLoading=!0,e.skillsError=null;try{const t=await e.client.request("skills.status",{});t&&(e.skillsReport=t)}catch(t){e.skillsError=String(t)}finally{e.skillsLoading=!1}}}function ds(e,t,s){e.skillEdits={...e.skillEdits,[t]:s}}async function us(e,t,s){if(!(!e.client||!e.connected)){e.skillsBusyKey=t,e.skillsError=null;try{await e.client.request("skills.update",{skillKey:t,enabled:s}),await te(e)}catch(n){e.skillsError=String(n)}finally{e.skillsBusyKey=null}}}async function hs(e,t){if(!(!e.client||!e.connected)){e.skillsBusyKey=t,e.skillsError=null;try{const s=e.skillEdits[t]??"";await e.client.request("skills.update",{skillKey:t,apiKey:s}),await te(e)}catch(s){e.skillsError=String(s)}finally{e.skillsBusyKey=null}}}async function gs(e,t,s){if(!(!e.client||!e.connected)){e.skillsBusyKey=t,e.skillsError=null;try{await e.client.request("skills.install",{name:t,installId:s,timeoutMs:12e4}),await te(e)}catch(n){e.skillsError=String(n)}finally{e.skillsBusyKey=null}}}async function oe(e,t){if(!(!e.client||!e.connected)&&!e.nodesLoading){e.nodesLoading=!0,t?.quiet||(e.lastError=null);try{const s=await e.client.request("node.list",{});e.nodes=Array.isArray(s.nodes)?s.nodes:[]}catch(s){t?.quiet||(e.lastError=String(s))}finally{e.nodesLoading=!1}}}async function G(e){if(!(!e.client||!e.connected)){e.chatLoading=!0,e.lastError=null;try{const t=await e.client.request("chat.history",{sessionKey:e.sessionKey,limit:200});e.chatMessages=Array.isArray(t.messages)?t.messages:[],e.chatThinkingLevel=t.thinkingLevel??null}catch(t){e.lastError=String(t)}finally{e.chatLoading=!1}}}async function ms(e){if(!e.client||!e.connected)return;const t=e.chatMessage.trim();if(!t)return;e.chatSending=!0,e.chatMessage="",e.lastError=null;const s=Qe();e.chatRunId=s,e.chatStream="";try{await e.client.request("chat.send",{sessionKey:e.sessionKey,message:t,deliver:!1,idempotencyKey:s})}catch(n){e.chatRunId=null,e.chatStream=null,e.chatMessage=t,e.lastError=String(n)}finally{e.chatSending=!1}}function ps(e,t){return!t||t.sessionKey!==e.sessionKey||t.runId&&e.chatRunId&&t.runId!==e.chatRunId?null:(t.state==="delta"?e.chatStream=fs(t.message)??e.chatStream:t.state==="final"?(e.chatStream=null,e.chatRunId=null):t.state==="error"&&(e.chatStream=null,e.chatRunId=null,e.lastError=t.errorMessage??"chat error"),t.state)}function fs(e){const t=e,s=t.content;if(typeof s=="string")return s;if(Array.isArray(s)){const n=s.map(i=>{const a=i;return a.type==="text"&&typeof a.text=="string"?a.text:null}).filter(i=>typeof i=="string");if(n.length>0)return n.join(`
2108
- `)}return typeof t.text=="string"?t.text:null}async function F(e){if(!(!e.client||!e.connected)){e.configLoading=!0,e.lastError=null;try{const t=await e.client.request("config.get",{});ys(e,t)}catch(t){e.lastError=String(t)}finally{e.configLoading=!1}}}async function vs(e){if(!(!e.client||!e.connected)&&!e.configSchemaLoading){e.configSchemaLoading=!0;try{const t=await e.client.request("config.schema",{});bs(e,t)}catch(t){e.lastError=String(t)}finally{e.configSchemaLoading=!1}}}function bs(e,t){e.configSchema=t.schema??null,e.configUiHints=t.uiHints??{},e.configSchemaVersion=t.version??null}function ys(e,t){e.configSnapshot=t,typeof t.raw=="string"?e.configRaw=t.raw:t.config&&typeof t.config=="object"&&(e.configRaw=`${JSON.stringify(t.config,null,2).trimEnd()}
2109
- `),e.configValid=typeof t.valid=="boolean"?t.valid:null,e.configIssues=Array.isArray(t.issues)?t.issues:[];const s=t.config??{},n=s.telegram??{},i=s.discord??{},a=s.signal??{},l=s.imessage??{},o=x=>Array.isArray(x)?x.map(E=>String(E??"").trim()).filter(E=>E.length>0).join(", "):"",r=n.groups&&typeof n.groups=="object"?n.groups:{},d=r["*"]&&typeof r["*"]=="object"?r["*"]:{},p=Array.isArray(n.allowFrom)?o(n.allowFrom):typeof n.allowFrom=="string"?n.allowFrom:"";e.telegramForm={token:typeof n.botToken=="string"?n.botToken:"",requireMention:typeof d.requireMention=="boolean"?d.requireMention:!0,allowFrom:p,proxy:typeof n.proxy=="string"?n.proxy:"",webhookUrl:typeof n.webhookUrl=="string"?n.webhookUrl:"",webhookSecret:typeof n.webhookSecret=="string"?n.webhookSecret:"",webhookPath:typeof n.webhookPath=="string"?n.webhookPath:""};const u=i.dm??{},v=i.slashCommand??{},$=i.actions??{},y=i.guilds,w=x=>typeof $[x]=="boolean"?$[x]:xe[x];e.discordForm={enabled:typeof i.enabled=="boolean"?i.enabled:!0,token:typeof i.token=="string"?i.token:"",dmEnabled:typeof u.enabled=="boolean"?u.enabled:!0,allowFrom:o(u.allowFrom),groupEnabled:typeof u.groupEnabled=="boolean"?u.groupEnabled:!1,groupChannels:o(u.groupChannels),mediaMaxMb:typeof i.mediaMaxMb=="number"?String(i.mediaMaxMb):"",historyLimit:typeof i.historyLimit=="number"?String(i.historyLimit):"",textChunkLimit:typeof i.textChunkLimit=="number"?String(i.textChunkLimit):"",replyToMode:i.replyToMode==="first"||i.replyToMode==="all"?i.replyToMode:"off",guilds:Array.isArray(y)?[]:typeof y=="object"&&y?Object.entries(y).map(([x,E])=>{const k=E&&typeof E=="object"?E:{},j=k.channels&&typeof k.channels=="object"?k.channels:{},ne=Object.entries(j).map(([q,me])=>{const se=me&&typeof me=="object"?me:{};return{key:q,allow:typeof se.allow=="boolean"?se.allow:!0,requireMention:typeof se.requireMention=="boolean"?se.requireMention:!1}});return{key:x,slug:typeof k.slug=="string"?k.slug:"",requireMention:typeof k.requireMention=="boolean"?k.requireMention:!1,reactionNotifications:k.reactionNotifications==="off"||k.reactionNotifications==="all"||k.reactionNotifications==="own"||k.reactionNotifications==="allowlist"?k.reactionNotifications:"own",users:o(k.users),channels:ne}}):[],actions:{reactions:w("reactions"),stickers:w("stickers"),polls:w("polls"),permissions:w("permissions"),messages:w("messages"),threads:w("threads"),pins:w("pins"),search:w("search"),memberInfo:w("memberInfo"),roleInfo:w("roleInfo"),channelInfo:w("channelInfo"),voiceStatus:w("voiceStatus"),events:w("events"),roles:w("roles"),moderation:w("moderation")},slashEnabled:typeof v.enabled=="boolean"?v.enabled:!1,slashName:typeof v.name=="string"?v.name:"",slashSessionPrefix:typeof v.sessionPrefix=="string"?v.sessionPrefix:"",slashEphemeral:typeof v.ephemeral=="boolean"?v.ephemeral:!0};const S=slack.dm??{},b=slack.channels,A=slack.slashCommand??{},C=slack.actions??{};e.slackForm={enabled:typeof slack.enabled=="boolean"?slack.enabled:!0,botToken:typeof slack.botToken=="string"?slack.botToken:"",appToken:typeof slack.appToken=="string"?slack.appToken:"",dmEnabled:typeof S.enabled=="boolean"?S.enabled:!0,allowFrom:o(S.allowFrom),groupEnabled:typeof S.groupEnabled=="boolean"?S.groupEnabled:!1,groupChannels:o(S.groupChannels),mediaMaxMb:typeof slack.mediaMaxMb=="number"?String(slack.mediaMaxMb):"",textChunkLimit:typeof slack.textChunkLimit=="number"?String(slack.textChunkLimit):"",reactionNotifications:slack.reactionNotifications==="off"||slack.reactionNotifications==="all"||slack.reactionNotifications==="allowlist"?slack.reactionNotifications:"own",reactionAllowlist:o(slack.reactionAllowlist),slashEnabled:typeof A.enabled=="boolean"?A.enabled:!1,slashName:typeof A.name=="string"?A.name:"",slashSessionPrefix:typeof A.sessionPrefix=="string"?A.sessionPrefix:"",slashEphemeral:typeof A.ephemeral=="boolean"?A.ephemeral:!0,actions:{...R,reactions:typeof C.reactions=="boolean"?C.reactions:R.reactions,messages:typeof C.messages=="boolean"?C.messages:R.messages,pins:typeof C.pins=="boolean"?C.pins:R.pins,memberInfo:typeof C.memberInfo=="boolean"?C.memberInfo:R.memberInfo,emojiList:typeof C.emojiList=="boolean"?C.emojiList:R.emojiList},channels:Array.isArray(b)?[]:typeof b=="object"&&b?Object.entries(b).map(([x,E])=>{const k=E&&typeof E=="object"?E:{};return{key:x,allow:typeof k.allow=="boolean"?k.allow:!0,requireMention:typeof k.requireMention=="boolean"?k.requireMention:!1}}):[]},e.signalForm={enabled:typeof a.enabled=="boolean"?a.enabled:!0,account:typeof a.account=="string"?a.account:"",httpUrl:typeof a.httpUrl=="string"?a.httpUrl:"",httpHost:typeof a.httpHost=="string"?a.httpHost:"",httpPort:typeof a.httpPort=="number"?String(a.httpPort):"",cliPath:typeof a.cliPath=="string"?a.cliPath:"",autoStart:typeof a.autoStart=="boolean"?a.autoStart:!0,receiveMode:a.receiveMode==="on-start"||a.receiveMode==="manual"?a.receiveMode:"",ignoreAttachments:typeof a.ignoreAttachments=="boolean"?a.ignoreAttachments:!1,ignoreStories:typeof a.ignoreStories=="boolean"?a.ignoreStories:!1,sendReadReceipts:typeof a.sendReadReceipts=="boolean"?a.sendReadReceipts:!1,allowFrom:o(a.allowFrom),mediaMaxMb:typeof a.mediaMaxMb=="number"?String(a.mediaMaxMb):""},e.imessageForm={enabled:typeof l.enabled=="boolean"?l.enabled:!0,cliPath:typeof l.cliPath=="string"?l.cliPath:"",dbPath:typeof l.dbPath=="string"?l.dbPath:"",service:l.service==="imessage"||l.service==="sms"||l.service==="auto"?l.service:"auto",region:typeof l.region=="string"?l.region:"",allowFrom:o(l.allowFrom),includeAttachments:typeof l.includeAttachments=="boolean"?l.includeAttachments:!1,mediaMaxMb:typeof l.mediaMaxMb=="number"?String(l.mediaMaxMb):""};const P=t.valid===!1?"Config invalid.":null;e.telegramConfigStatus=P,e.discordConfigStatus=P,e.slackConfigStatus=P,e.signalConfigStatus=P,e.imessageConfigStatus=P,e.configFormDirty||(e.configForm=dt(t.config??{}))}async function $s(e){if(!(!e.client||!e.connected)){e.configSaving=!0,e.lastError=null;try{const t=e.configFormMode==="form"&&e.configForm?`${JSON.stringify(e.configForm,null,2).trimEnd()}
2110
- `:e.configRaw;await e.client.request("config.set",{raw:t}),e.configFormDirty=!1,await F(e)}catch(t){e.lastError=String(t)}finally{e.configSaving=!1}}}function ws(e,t,s){const n=dt(e.configForm??{});Ss(n,t,s),e.configForm=n,e.configFormDirty=!0}function dt(e){return typeof structuredClone=="function"?structuredClone(e):JSON.parse(JSON.stringify(e))}function Ss(e,t,s){if(t.length===0)return;let n=e;for(let a=0;a<t.length-1;a+=1){const l=t[a],o=t[a+1];if(typeof l=="number"){if(!Array.isArray(n))return;n[l]==null&&(n[l]=typeof o=="number"?[]:{}),n=n[l]}else{if(typeof n!="object"||n==null)return;const r=n;r[l]==null&&(r[l]=typeof o=="number"?[]:{}),n=r[l]}}const i=t[t.length-1];if(typeof i=="number"){Array.isArray(n)&&(n[i]=s);return}typeof n=="object"&&n!=null&&(n[i]=s)}async function Z(e){if(!(!e.client||!e.connected))try{const t=await e.client.request("cron.status",{});e.cronStatus=t}catch(t){e.cronError=String(t)}}async function ge(e){if(!(!e.client||!e.connected)&&!e.cronLoading){e.cronLoading=!0,e.cronError=null;try{const t=await e.client.request("cron.list",{includeDisabled:!0});e.cronJobs=Array.isArray(t.jobs)?t.jobs:[]}catch(t){e.cronError=String(t)}finally{e.cronLoading=!1}}}function ks(e){if(e.scheduleKind==="at"){const s=Date.parse(e.scheduleAt);if(!Number.isFinite(s))throw new Error("Invalid run time.");return{kind:"at",atMs:s}}if(e.scheduleKind==="every"){const s=de(e.everyAmount,0);if(s<=0)throw new Error("Invalid interval amount.");const n=e.everyUnit;return{kind:"every",everyMs:s*(n==="minutes"?6e4:n==="hours"?36e5:864e5)}}const t=e.cronExpr.trim();if(!t)throw new Error("Cron expression required.");return{kind:"cron",expr:t,tz:e.cronTz.trim()||void 0}}function Cs(e){if(e.payloadKind==="systemEvent"){const i=e.payloadText.trim();if(!i)throw new Error("System event text required.");return{kind:"systemEvent",text:i}}const t=e.payloadText.trim();if(!t)throw new Error("Agent message required.");const s={kind:"agentTurn",message:t};e.deliver&&(s.deliver=!0),e.channel&&(s.channel=e.channel),e.to.trim()&&(s.to=e.to.trim());const n=de(e.timeoutSeconds,0);return n>0&&(s.timeoutSeconds=n),s}async function Ms(e){if(!(!e.client||!e.connected||e.cronBusy)){e.cronBusy=!0,e.cronError=null;try{const t=ks(e.cronForm),s=Cs(e.cronForm),n={name:e.cronForm.name.trim(),description:e.cronForm.description.trim()||void 0,enabled:e.cronForm.enabled,schedule:t,sessionTarget:e.cronForm.sessionTarget,wakeMode:e.cronForm.wakeMode,payload:s,isolation:e.cronForm.postToMainPrefix.trim()&&e.cronForm.sessionTarget==="isolated"?{postToMainPrefix:e.cronForm.postToMainPrefix.trim()}:void 0};if(!n.name)throw new Error("Name required.");await e.client.request("cron.add",n),e.cronForm={...e.cronForm,name:"",description:"",payloadText:""},await ge(e),await Z(e)}catch(t){e.cronError=String(t)}finally{e.cronBusy=!1}}}async function As(e,t,s){if(!(!e.client||!e.connected||e.cronBusy)){e.cronBusy=!0,e.cronError=null;try{await e.client.request("cron.update",{id:t.id,patch:{enabled:s}}),await ge(e),await Z(e)}catch(n){e.cronError=String(n)}finally{e.cronBusy=!1}}}async function Es(e,t){if(!(!e.client||!e.connected||e.cronBusy)){e.cronBusy=!0,e.cronError=null;try{await e.client.request("cron.run",{id:t.id,mode:"force"}),await ut(e,t.id)}catch(s){e.cronError=String(s)}finally{e.cronBusy=!1}}}async function xs(e,t){if(!(!e.client||!e.connected||e.cronBusy)){e.cronBusy=!0,e.cronError=null;try{await e.client.request("cron.remove",{id:t.id}),e.cronRunsJobId===t.id&&(e.cronRunsJobId=null,e.cronRuns=[]),await ge(e),await Z(e)}catch(s){e.cronError=String(s)}finally{e.cronBusy=!1}}}async function ut(e,t){if(!(!e.client||!e.connected))try{const s=await e.client.request("cron.runs",{id:t,limit:50});e.cronRunsJobId=t,e.cronRuns=Array.isArray(s.entries)?s.entries:[]}catch(s){e.cronError=String(s)}}async function $e(e){if(!(!e.client||!e.connected)&&!e.debugLoading){e.debugLoading=!0;try{const[t,s,n,i]=await Promise.all([e.client.request("status",{}),e.client.request("health",{}),e.client.request("models.list",{}),e.client.request("last-heartbeat",{})]);e.debugStatus=t,e.debugHealth=s;const a=n;e.debugModels=Array.isArray(a?.models)?a?.models:[],e.debugHeartbeat=i}catch(t){e.debugCallError=String(t)}finally{e.debugLoading=!1}}}async function Ls(e){if(!(!e.client||!e.connected)){e.debugCallError=null,e.debugCallResult=null;try{const t=e.debugCallParams.trim()?JSON.parse(e.debugCallParams):{},s=await e.client.request(e.debugCallMethod.trim(),t);e.debugCallResult=JSON.stringify(s,null,2)}catch(t){e.debugCallError=String(t)}}}function Fs(e){const t=e.presenceEntries.length,s=e.sessionsResult?.count??null,n=e.cronStatus?.nextWakeAtMs??null,i=e.nodes.some(l=>{if(!l.connected)return!1;const o=typeof l.platform=="string"?l.platform.trim().toLowerCase():"";return o.startsWith("ios")||o.startsWith("ipados")||o.startsWith("android")}),a=e.connected?i?null:"No connected iOS/Android node — Web Chat + Talk are disabled.":"Disconnected from gateway.";return c`
2111
- <div class="shell">
2112
- <header class="topbar">
2113
- <div class="brand">
2114
- <div class="brand-title">Clawdbot Control</div>
2115
- <div class="brand-sub">Gateway dashboard</div>
2116
- </div>
2117
- <div class="topbar-status">
2118
- <div class="pill">
2119
- <span class="statusDot ${e.connected?"ok":""}"></span>
2120
- <span>Health</span>
2121
- <span class="mono">${e.connected?"OK":"Offline"}</span>
2122
- </div>
2123
- ${Rs(e)}
2124
- </div>
2125
- </header>
2126
- <aside class="nav">
2127
- ${Bt.map(l=>c`
2128
- <div class="nav-group">
2129
- <div class="nav-label">${l.label}</div>
2130
- ${l.tabs.map(o=>Ts(e,o))}
2131
- </div>
2132
- `)}
2133
- </aside>
2134
- <main class="content">
2135
- <section class="content-header">
2136
- <div>
2137
- <div class="page-title">${nt(e.tab)}</div>
2138
- <div class="page-sub">${Wt(e.tab)}</div>
2139
- </div>
2140
- <div class="page-meta">
2141
- ${e.lastError?c`<div class="pill danger">${e.lastError}</div>`:f}
2142
- </div>
2143
- </section>
2144
-
2145
- ${e.tab==="overview"?Bn({connected:e.connected,hello:e.hello,settings:e.settings,password:e.password,lastError:e.lastError,presenceCount:t,sessionsCount:s,cronEnabled:e.cronStatus?.enabled??null,cronNext:n,lastProvidersRefresh:e.providersLastSuccess,onSettingsChange:l=>e.applySettings(l),onPasswordChange:l=>e.password=l,onSessionKeyChange:l=>{e.sessionKey=l,e.chatMessage="",e.resetToolStream(),e.applySettings({...e.settings,sessionKey:l})},onRefresh:()=>e.loadOverview()}):f}
2146
-
2147
- ${e.tab==="connections"?kn({connected:e.connected,loading:e.providersLoading,snapshot:e.providersSnapshot,lastError:e.providersError,lastSuccessAt:e.providersLastSuccess,whatsappMessage:e.whatsappLoginMessage,whatsappQrDataUrl:e.whatsappLoginQrDataUrl,whatsappConnected:e.whatsappLoginConnected,whatsappBusy:e.whatsappBusy,telegramForm:e.telegramForm,telegramTokenLocked:e.telegramTokenLocked,telegramSaving:e.telegramSaving,telegramStatus:e.telegramConfigStatus,discordForm:e.discordForm,discordTokenLocked:e.discordTokenLocked,discordSaving:e.discordSaving,discordStatus:e.discordConfigStatus,slackForm:e.slackForm,slackTokenLocked:e.slackTokenLocked,slackAppTokenLocked:e.slackAppTokenLocked,slackSaving:e.slackSaving,slackStatus:e.slackConfigStatus,signalForm:e.signalForm,signalSaving:e.signalSaving,signalStatus:e.signalConfigStatus,imessageForm:e.imessageForm,imessageSaving:e.imessageSaving,imessageStatus:e.imessageConfigStatus,onRefresh:l=>L(e,l),onWhatsAppStart:l=>e.handleWhatsAppStart(l),onWhatsAppWait:()=>e.handleWhatsAppWait(),onWhatsAppLogout:()=>e.handleWhatsAppLogout(),onTelegramChange:l=>Zn(e,l),onTelegramSave:()=>e.handleTelegramSave(),onDiscordChange:l=>es(e,l),onDiscordSave:()=>e.handleDiscordSave(),onSlackChange:l=>ts(e,l),onSlackSave:()=>e.handleSlackSave(),onSignalChange:l=>ns(e,l),onSignalSave:()=>e.handleSignalSave(),onIMessageChange:l=>ss(e,l),onIMessageSave:()=>e.handleIMessageSave()}):f}
2148
-
2149
- ${e.tab==="instances"?On({loading:e.presenceLoading,entries:e.presenceEntries,lastError:e.presenceError,statusMessage:e.presenceStatus,onRefresh:()=>ye(e)}):f}
2150
-
2151
- ${e.tab==="sessions"?Jn({loading:e.sessionsLoading,result:e.sessionsResult,error:e.sessionsError,activeMinutes:e.sessionsFilterActive,limit:e.sessionsFilterLimit,includeGlobal:e.sessionsIncludeGlobal,includeUnknown:e.sessionsIncludeUnknown,onFiltersChange:l=>{e.sessionsFilterActive=l.activeMinutes,e.sessionsFilterLimit=l.limit,e.sessionsIncludeGlobal=l.includeGlobal,e.sessionsIncludeUnknown=l.includeUnknown},onRefresh:()=>J(e),onPatch:(l,o)=>cs(e,l,o)}):f}
2152
-
2153
- ${e.tab==="cron"?_n({loading:e.cronLoading,status:e.cronStatus,jobs:e.cronJobs,error:e.cronError,busy:e.cronBusy,form:e.cronForm,runsJobId:e.cronRunsJobId,runs:e.cronRuns,onFormChange:l=>e.cronForm={...e.cronForm,...l},onRefresh:()=>e.loadCron(),onAdd:()=>Ms(e),onToggle:(l,o)=>As(e,l,o),onRun:l=>Es(e,l),onRemove:l=>xs(e,l),onLoadRuns:l=>ut(e,l)}):f}
2154
-
2155
- ${e.tab==="skills"?Yn({loading:e.skillsLoading,report:e.skillsReport,error:e.skillsError,filter:e.skillsFilter,edits:e.skillEdits,busyKey:e.skillsBusyKey,onFilterChange:l=>e.skillsFilter=l,onRefresh:()=>te(e),onToggle:(l,o)=>us(e,l,o),onEdit:(l,o)=>ds(e,l,o),onSaveKey:l=>hs(e,l),onInstall:(l,o)=>gs(e,l,o)}):f}
2156
-
2157
- ${e.tab==="nodes"?jn({loading:e.nodesLoading,nodes:e.nodes,onRefresh:()=>oe(e)}):f}
2158
-
2159
- ${e.tab==="chat"?rn({sessionKey:e.sessionKey,onSessionKeyChange:l=>{e.sessionKey=l,e.chatMessage="",e.chatStream=null,e.chatRunId=null,e.resetToolStream(),e.applySettings({...e.settings,sessionKey:l}),G(e)},thinkingLevel:e.chatThinkingLevel,loading:e.chatLoading,sending:e.chatSending,messages:[...e.chatMessages,...e.chatToolMessages],stream:e.chatStream,draft:e.chatMessage,connected:e.connected,canSend:e.connected&&i,disabledReason:a,sessions:e.sessionsResult,onRefresh:()=>(e.resetToolStream(),G(e)),onDraftChange:l=>e.chatMessage=l,onSend:()=>e.handleSendChat()}):f}
2160
-
2161
- ${e.tab==="config"?yn({raw:e.configRaw,valid:e.configValid,issues:e.configIssues,loading:e.configLoading,saving:e.configSaving,connected:e.connected,schema:e.configSchema,schemaLoading:e.configSchemaLoading,uiHints:e.configUiHints,formMode:e.configFormMode,formValue:e.configForm,onRawChange:l=>e.configRaw=l,onFormModeChange:l=>e.configFormMode=l,onFormPatch:(l,o)=>ws(e,l,o),onReload:()=>F(e),onSave:()=>$s(e)}):f}
2162
-
2163
- ${e.tab==="debug"?Un({loading:e.debugLoading,status:e.debugStatus,health:e.debugHealth,models:e.debugModels,heartbeat:e.debugHeartbeat,eventLog:e.eventLog,callMethod:e.debugCallMethod,callParams:e.debugCallParams,callResult:e.debugCallResult,callError:e.debugCallError,onCallMethodChange:l=>e.debugCallMethod=l,onCallParamsChange:l=>e.debugCallParams=l,onRefresh:()=>$e(e),onCall:()=>Ls(e)}):f}
2164
- </main>
2165
- </div>
2166
- `}function Ts(e,t){const s=tt(t,e.basePath);return c`
2167
- <a
2168
- href=${s}
2169
- class="nav-item ${e.tab===t?"active":""}"
2170
- @click=${n=>{n.defaultPrevented||n.button!==0||n.metaKey||n.ctrlKey||n.shiftKey||n.altKey||(n.preventDefault(),e.setTab(t))}}
2171
- >
2172
- <span>${nt(t)}</span>
2173
- </a>
2174
- `}const Ps=["system","light","dark"];function Rs(e){const t=Math.max(0,Ps.indexOf(e.theme)),s=n=>i=>{const l={element:i.currentTarget};(i.clientX||i.clientY)&&(l.pointerClientX=i.clientX,l.pointerClientY=i.clientY),e.setTheme(n,l)};return c`
2175
- <div class="theme-toggle" style="--theme-index: ${t};">
2176
- <div class="theme-toggle__track" role="group" aria-label="Theme">
2177
- <span class="theme-toggle__indicator"></span>
2178
- <button
2179
- class="theme-toggle__button ${e.theme==="system"?"active":""}"
2180
- @click=${s("system")}
2181
- aria-pressed=${e.theme==="system"}
2182
- aria-label="System theme"
2183
- title="System"
2184
- >
2185
- ${Ns()}
2186
- </button>
2187
- <button
2188
- class="theme-toggle__button ${e.theme==="light"?"active":""}"
2189
- @click=${s("light")}
2190
- aria-pressed=${e.theme==="light"}
2191
- aria-label="Light theme"
2192
- title="Light"
2193
- >
2194
- ${_s()}
2195
- </button>
2196
- <button
2197
- class="theme-toggle__button ${e.theme==="dark"?"active":""}"
2198
- @click=${s("dark")}
2199
- aria-pressed=${e.theme==="dark"}
2200
- aria-label="Dark theme"
2201
- title="Dark"
2202
- >
2203
- ${Is()}
2204
- </button>
2205
- </div>
2206
- </div>
2207
- `}function _s(){return c`
2208
- <svg class="theme-icon" viewBox="0 0 24 24" aria-hidden="true">
2209
- <circle cx="12" cy="12" r="4"></circle>
2210
- <path d="M12 2v2"></path>
2211
- <path d="M12 20v2"></path>
2212
- <path d="m4.93 4.93 1.41 1.41"></path>
2213
- <path d="m17.66 17.66 1.41 1.41"></path>
2214
- <path d="M2 12h2"></path>
2215
- <path d="M20 12h2"></path>
2216
- <path d="m6.34 17.66-1.41 1.41"></path>
2217
- <path d="m19.07 4.93-1.41 1.41"></path>
2218
- </svg>
2219
- `}function Is(){return c`
2220
- <svg class="theme-icon" viewBox="0 0 24 24" aria-hidden="true">
2221
- <path
2222
- d="M20.985 12.486a9 9 0 1 1-9.473-9.472c.405-.022.617.46.402.803a6 6 0 0 0 8.268 8.268c.344-.215.825-.004.803.401"
2223
- ></path>
2224
- </svg>
2225
- `}function Ns(){return c`
2226
- <svg class="theme-icon" viewBox="0 0 24 24" aria-hidden="true">
2227
- <rect width="20" height="14" x="2" y="3" rx="2"></rect>
2228
- <line x1="8" x2="16" y1="21" y2="21"></line>
2229
- <line x1="12" x2="12" y1="17" y2="21"></line>
2230
- </svg>
2231
- `}function Ks(){return typeof window>"u"||typeof window.matchMedia!="function"||window.matchMedia("(prefers-color-scheme: dark)").matches?"dark":"light"}function fe(e){return e==="system"?Ks():e}const ie=e=>Number.isNaN(e)?.5:e<=0?0:e>=1?1:e,Us=()=>typeof window>"u"||typeof window.matchMedia!="function"?!1:window.matchMedia("(prefers-reduced-motion: reduce)").matches??!1,ae=e=>{e.classList.remove("theme-transition"),e.style.removeProperty("--theme-switch-x"),e.style.removeProperty("--theme-switch-y")},Os=({nextTheme:e,applyTheme:t,context:s,currentTheme:n})=>{if(n===e)return;const i=globalThis.document??null;if(!i){t();return}const a=i.documentElement,l=i,o=Us();if(!!l.startViewTransition&&!o){let d=.5,p=.5;if(s?.pointerClientX!==void 0&&s?.pointerClientY!==void 0&&typeof window<"u")d=ie(s.pointerClientX/window.innerWidth),p=ie(s.pointerClientY/window.innerHeight);else if(s?.element){const u=s.element.getBoundingClientRect();u.width>0&&u.height>0&&typeof window<"u"&&(d=ie((u.left+u.width/2)/window.innerWidth),p=ie((u.top+u.height/2)/window.innerHeight))}a.style.setProperty("--theme-switch-x",`${d*100}%`),a.style.setProperty("--theme-switch-y",`${p*100}%`),a.classList.add("theme-transition");try{const u=l.startViewTransition?.(()=>{t()});u?.finished?u.finished.finally(()=>ae(a)):ae(a)}catch{ae(a),t()}return}t(),ae(a)};var Ds=Object.defineProperty,js=Object.getOwnPropertyDescriptor,g=(e,t,s,n)=>{for(var i=n>1?void 0:n?js(t,s):t,a=e.length-1,l;a>=0;a--)(l=e[a])&&(i=(n?l(t,s,i):l(i))||i);return n&&i&&Ds(t,s,i),i};const He=50;function qs(e){if(!e||typeof e!="object")return null;const t=e;if(typeof t.text=="string")return t.text;const s=t.content;if(!Array.isArray(s))return null;const n=s.map(i=>{if(!i||typeof i!="object")return null;const a=i;return a.type==="text"&&typeof a.text=="string"?a.text:null}).filter(i=>!!i);return n.length===0?null:n.join(`
2232
- `)}function We(e){if(e==null)return null;if(typeof e=="string")return e;if(typeof e=="number"||typeof e=="boolean")return String(e);const t=qs(e);if(t)return t;try{return JSON.stringify(e,null,2)}catch{return String(e)}}const Bs={name:"",description:"",enabled:!0,scheduleKind:"every",scheduleAt:"",everyAmount:"30",everyUnit:"minutes",cronExpr:"0 7 * * *",cronTz:"",sessionTarget:"main",wakeMode:"next-heartbeat",payloadKind:"systemEvent",payloadText:"",deliver:!1,channel:"last",to:"",timeoutSeconds:"",postToMainPrefix:""};let h=class extends W{constructor(){super(...arguments),this.settings=jt(),this.password="",this.tab="chat",this.connected=!1,this.theme=this.settings.theme??"system",this.themeResolved="dark",this.hello=null,this.lastError=null,this.eventLog=[],this.sessionKey=this.settings.sessionKey,this.chatLoading=!1,this.chatSending=!1,this.chatMessage="",this.chatMessages=[],this.chatToolMessages=[],this.chatStream=null,this.chatRunId=null,this.chatThinkingLevel=null,this.nodesLoading=!1,this.nodes=[],this.configLoading=!1,this.configRaw=`{
2233
- }
2234
- `,this.configValid=null,this.configIssues=[],this.configSaving=!1,this.configSnapshot=null,this.configSchema=null,this.configSchemaVersion=null,this.configSchemaLoading=!1,this.configUiHints={},this.configForm=null,this.configFormDirty=!1,this.configFormMode="form",this.providersLoading=!1,this.providersSnapshot=null,this.providersError=null,this.providersLastSuccess=null,this.whatsappLoginMessage=null,this.whatsappLoginQrDataUrl=null,this.whatsappLoginConnected=null,this.whatsappBusy=!1,this.telegramForm={token:"",requireMention:!0,allowFrom:"",proxy:"",webhookUrl:"",webhookSecret:"",webhookPath:""},this.telegramSaving=!1,this.telegramTokenLocked=!1,this.telegramConfigStatus=null,this.discordForm={enabled:!0,token:"",dmEnabled:!0,allowFrom:"",groupEnabled:!1,groupChannels:"",mediaMaxMb:"",historyLimit:"",textChunkLimit:"",guilds:[],actions:{...xe},slashEnabled:!1,slashName:"",slashSessionPrefix:"",slashEphemeral:!0},this.discordSaving=!1,this.discordTokenLocked=!1,this.discordConfigStatus=null,this.slackForm={enabled:!0,botToken:"",appToken:"",dmEnabled:!0,allowFrom:"",groupEnabled:!1,groupChannels:"",mediaMaxMb:"",textChunkLimit:"",reactionNotifications:"own",reactionAllowlist:"",slashEnabled:!1,slashName:"",slashSessionPrefix:"",slashEphemeral:!0,actions:{...R},channels:[]},this.slackSaving=!1,this.slackTokenLocked=!1,this.slackAppTokenLocked=!1,this.slackConfigStatus=null,this.signalForm={enabled:!0,account:"",httpUrl:"",httpHost:"",httpPort:"",cliPath:"",autoStart:!0,receiveMode:"",ignoreAttachments:!1,ignoreStories:!1,sendReadReceipts:!1,allowFrom:"",mediaMaxMb:""},this.signalSaving=!1,this.signalConfigStatus=null,this.imessageForm={enabled:!0,cliPath:"",dbPath:"",service:"auto",region:"",allowFrom:"",includeAttachments:!1,mediaMaxMb:""},this.imessageSaving=!1,this.imessageConfigStatus=null,this.presenceLoading=!1,this.presenceEntries=[],this.presenceError=null,this.presenceStatus=null,this.sessionsLoading=!1,this.sessionsResult=null,this.sessionsError=null,this.sessionsFilterActive="",this.sessionsFilterLimit="120",this.sessionsIncludeGlobal=!0,this.sessionsIncludeUnknown=!1,this.cronLoading=!1,this.cronJobs=[],this.cronStatus=null,this.cronError=null,this.cronForm={...Bs},this.cronRunsJobId=null,this.cronRuns=[],this.cronBusy=!1,this.skillsLoading=!1,this.skillsReport=null,this.skillsError=null,this.skillsFilter="",this.skillEdits={},this.skillsBusyKey=null,this.debugLoading=!1,this.debugStatus=null,this.debugHealth=null,this.debugModels=[],this.debugHeartbeat=null,this.debugCallMethod="",this.debugCallParams="{}",this.debugCallResult=null,this.debugCallError=null,this.client=null,this.chatScrollFrame=null,this.chatScrollTimeout=null,this.nodesPollInterval=null,this.toolStreamById=new Map,this.toolStreamOrder=[],this.basePath="",this.popStateHandler=()=>this.onPopState(),this.themeMedia=null,this.themeMediaHandler=null}createRenderRoot(){return this}connectedCallback(){super.connectedCallback(),this.basePath=this.inferBasePath(),this.syncTabWithLocation(!0),this.syncThemeWithSettings(),this.attachThemeListener(),window.addEventListener("popstate",this.popStateHandler),this.applySettingsFromUrl(),this.connect(),this.startNodesPolling()}disconnectedCallback(){window.removeEventListener("popstate",this.popStateHandler),this.stopNodesPolling(),this.detachThemeListener(),super.disconnectedCallback()}updated(e){this.tab==="chat"&&(e.has("chatMessages")||e.has("chatToolMessages")||e.has("chatStream")||e.has("chatLoading")||e.has("chatMessage")||e.has("tab"))&&this.scheduleChatScroll()}connect(){this.lastError=null,this.hello=null,this.connected=!1,this.client?.stop(),this.client=new Dt({url:this.settings.gatewayUrl,token:this.settings.token.trim()?this.settings.token:void 0,password:this.password.trim()?this.password:void 0,clientName:"clawdbot-control-ui",mode:"webchat",onHello:e=>{this.connected=!0,this.hello=e,this.applySnapshot(e),oe(this,{quiet:!0}),this.refreshActiveTab()},onClose:({code:e,reason:t})=>{this.connected=!1,this.lastError=`disconnected (${e}): ${t||"no reason"}`},onEvent:e=>this.onEvent(e),onGap:({expected:e,received:t})=>{this.lastError=`event gap detected (expected seq ${e}, got ${t}); refresh recommended`}}),this.client.start()}scheduleChatScroll(){this.chatScrollFrame&&cancelAnimationFrame(this.chatScrollFrame),this.chatScrollTimeout!=null&&(clearTimeout(this.chatScrollTimeout),this.chatScrollTimeout=null),this.chatScrollFrame=requestAnimationFrame(()=>{this.chatScrollFrame=null;const e=this.querySelector(".chat-thread");e&&(e.scrollTop=e.scrollHeight,this.chatScrollTimeout=window.setTimeout(()=>{this.chatScrollTimeout=null;const t=this.querySelector(".chat-thread");t&&(t.scrollTop=t.scrollHeight)},120))})}startNodesPolling(){this.nodesPollInterval==null&&(this.nodesPollInterval=window.setInterval(()=>{oe(this,{quiet:!0})},5e3))}stopNodesPolling(){this.nodesPollInterval!=null&&(clearInterval(this.nodesPollInterval),this.nodesPollInterval=null)}hasConnectedMobileNode(){return this.nodes.some(e=>{if(!e.connected)return!1;const t=typeof e.platform=="string"?e.platform.trim().toLowerCase():"";return t.startsWith("ios")||t.startsWith("ipados")||t.startsWith("android")})}resetToolStream(){this.toolStreamById.clear(),this.toolStreamOrder=[],this.chatToolMessages=[]}trimToolStream(){if(this.toolStreamOrder.length<=He)return;const e=this.toolStreamOrder.length-He,t=this.toolStreamOrder.splice(0,e);for(const s of t)this.toolStreamById.delete(s)}syncToolStreamMessages(){this.chatToolMessages=this.toolStreamOrder.map(e=>this.toolStreamById.get(e)?.message).filter(e=>!!e)}buildToolStreamMessage(e){const t=[];return t.push({type:"toolcall",name:e.name,arguments:e.args??{}}),e.output&&t.push({type:"toolresult",name:e.name,text:e.output}),{role:"assistant",toolCallId:e.toolCallId,runId:e.runId,content:t,timestamp:e.startedAt}}handleAgentEvent(e){if(!e||e.stream!=="tool")return;const t=typeof e.sessionKey=="string"?e.sessionKey:void 0;if(t&&t!==this.sessionKey||!t&&this.chatRunId&&e.runId!==this.chatRunId)return;const s=e.data??{},n=typeof s.toolCallId=="string"?s.toolCallId:"";if(!n)return;const i=typeof s.name=="string"?s.name:"tool",a=typeof s.phase=="string"?s.phase:"",l=a==="start"?s.args:void 0,o=a==="update"?We(s.partialResult):a==="result"?We(s.result):void 0,r=Date.now();let d=this.toolStreamById.get(n);d?(d.name=i,l!==void 0&&(d.args=l),o!==void 0&&(d.output=o),d.updatedAt=r):(d={toolCallId:n,runId:e.runId,sessionKey:t,name:i,args:l,output:o,startedAt:typeof e.ts=="number"?e.ts:r,updatedAt:r,message:{}},this.toolStreamById.set(n,d),this.toolStreamOrder.push(n)),d.message=this.buildToolStreamMessage(d),this.trimToolStream(),this.syncToolStreamMessages()}onEvent(e){if(this.eventLog=[{ts:Date.now(),event:e.event,payload:e.payload},...this.eventLog].slice(0,250),e.event==="agent"){this.handleAgentEvent(e.payload);return}if(e.event==="chat"){const t=e.payload;ps(this,t)==="final"&&G(this);return}if(e.event==="presence"){const t=e.payload;t?.presence&&Array.isArray(t.presence)&&(this.presenceEntries=t.presence,this.presenceError=null,this.presenceStatus=null);return}e.event==="cron"&&this.tab==="cron"&&this.loadCron()}applySnapshot(e){const t=e.snapshot;t?.presence&&Array.isArray(t.presence)&&(this.presenceEntries=t.presence),t?.health&&(this.debugHealth=t.health)}applySettings(e){this.settings=e,qt(e),e.theme!==this.theme&&(this.theme=e.theme,this.applyResolvedTheme(fe(e.theme)))}applySettingsFromUrl(){if(!window.location.search)return;const e=new URLSearchParams(window.location.search),t=e.get("token")?.trim();if(!t)return;this.settings.token||this.applySettings({...this.settings,token:t}),e.delete("token");const s=new URL(window.location.href);s.search=e.toString(),window.history.replaceState({},"",s.toString())}setTab(e){this.tab!==e&&(this.tab=e),this.refreshActiveTab(),this.syncUrlWithTab(e,!1)}setTheme(e,t){Os({nextTheme:e,applyTheme:()=>{this.theme=e,this.applySettings({...this.settings,theme:e}),this.applyResolvedTheme(fe(e))},context:t,currentTheme:this.theme})}async refreshActiveTab(){this.tab==="overview"&&await this.loadOverview(),this.tab==="connections"&&await this.loadConnections(),this.tab==="instances"&&await ye(this),this.tab==="sessions"&&await J(this),this.tab==="cron"&&await this.loadCron(),this.tab==="skills"&&await te(this),this.tab==="nodes"&&await oe(this),this.tab==="chat"&&(await Promise.all([G(this),J(this)]),this.scheduleChatScroll()),this.tab==="config"&&(await vs(this),await F(this)),this.tab==="debug"&&await $e(this)}inferBasePath(){if(typeof window>"u")return"";const e=window.__CLAWDBOT_CONTROL_UI_BASE_PATH__;return typeof e=="string"&&e.trim()?Ae(e):Ht(window.location.pathname)}syncThemeWithSettings(){this.theme=this.settings.theme??"system",this.applyResolvedTheme(fe(this.theme))}applyResolvedTheme(e){if(this.themeResolved=e,typeof document>"u")return;const t=document.documentElement;t.dataset.theme=e,t.style.colorScheme=e}attachThemeListener(){if(typeof window>"u"||typeof window.matchMedia!="function")return;if(this.themeMedia=window.matchMedia("(prefers-color-scheme: dark)"),this.themeMediaHandler=t=>{this.theme==="system"&&this.applyResolvedTheme(t.matches?"dark":"light")},typeof this.themeMedia.addEventListener=="function"){this.themeMedia.addEventListener("change",this.themeMediaHandler);return}this.themeMedia.addListener(this.themeMediaHandler)}detachThemeListener(){if(!this.themeMedia||!this.themeMediaHandler)return;if(typeof this.themeMedia.removeEventListener=="function"){this.themeMedia.removeEventListener("change",this.themeMediaHandler);return}this.themeMedia.removeListener(this.themeMediaHandler),this.themeMedia=null,this.themeMediaHandler=null}syncTabWithLocation(e){if(typeof window>"u")return;const t=je(window.location.pathname,this.basePath)??"chat";this.setTabFromRoute(t),this.syncUrlWithTab(t,e)}onPopState(){if(typeof window>"u")return;const e=je(window.location.pathname,this.basePath);e&&this.setTabFromRoute(e)}setTabFromRoute(e){this.tab!==e&&(this.tab=e),this.connected&&this.refreshActiveTab()}syncUrlWithTab(e,t){if(typeof window>"u")return;const s=Q(tt(e,this.basePath));if(Q(window.location.pathname)===s)return;const i=new URL(window.location.href);i.pathname=s,t?window.history.replaceState({},"",i.toString()):window.history.pushState({},"",i.toString())}async loadOverview(){await Promise.all([L(this,!1),ye(this),J(this),Z(this),$e(this)])}async loadConnections(){await Promise.all([L(this,!0),F(this)])}async loadCron(){await Promise.all([Z(this),ge(this)])}async handleSendChat(){!this.connected||!this.hasConnectedMobileNode()||(await ms(this),G(this))}async handleWhatsAppStart(e){await Vn(this,e),await L(this,!0)}async handleWhatsAppWait(){await Qn(this),await L(this,!0)}async handleWhatsAppLogout(){await Xn(this),await L(this,!0)}async handleTelegramSave(){await is(this),await F(this),await L(this,!0)}async handleDiscordSave(){await as(this),await F(this),await L(this,!0)}async handleSlackSave(){await ls(this),await F(this),await L(this,!0)}async handleSignalSave(){await os(this),await F(this),await L(this,!0)}async handleIMessageSave(){await rs(this),await F(this),await L(this,!0)}render(){return Fs(this)}};g([m()],h.prototype,"settings",2);g([m()],h.prototype,"password",2);g([m()],h.prototype,"tab",2);g([m()],h.prototype,"connected",2);g([m()],h.prototype,"theme",2);g([m()],h.prototype,"themeResolved",2);g([m()],h.prototype,"hello",2);g([m()],h.prototype,"lastError",2);g([m()],h.prototype,"eventLog",2);g([m()],h.prototype,"sessionKey",2);g([m()],h.prototype,"chatLoading",2);g([m()],h.prototype,"chatSending",2);g([m()],h.prototype,"chatMessage",2);g([m()],h.prototype,"chatMessages",2);g([m()],h.prototype,"chatToolMessages",2);g([m()],h.prototype,"chatStream",2);g([m()],h.prototype,"chatRunId",2);g([m()],h.prototype,"chatThinkingLevel",2);g([m()],h.prototype,"nodesLoading",2);g([m()],h.prototype,"nodes",2);g([m()],h.prototype,"configLoading",2);g([m()],h.prototype,"configRaw",2);g([m()],h.prototype,"configValid",2);g([m()],h.prototype,"configIssues",2);g([m()],h.prototype,"configSaving",2);g([m()],h.prototype,"configSnapshot",2);g([m()],h.prototype,"configSchema",2);g([m()],h.prototype,"configSchemaVersion",2);g([m()],h.prototype,"configSchemaLoading",2);g([m()],h.prototype,"configUiHints",2);g([m()],h.prototype,"configForm",2);g([m()],h.prototype,"configFormDirty",2);g([m()],h.prototype,"configFormMode",2);g([m()],h.prototype,"providersLoading",2);g([m()],h.prototype,"providersSnapshot",2);g([m()],h.prototype,"providersError",2);g([m()],h.prototype,"providersLastSuccess",2);g([m()],h.prototype,"whatsappLoginMessage",2);g([m()],h.prototype,"whatsappLoginQrDataUrl",2);g([m()],h.prototype,"whatsappLoginConnected",2);g([m()],h.prototype,"whatsappBusy",2);g([m()],h.prototype,"telegramForm",2);g([m()],h.prototype,"telegramSaving",2);g([m()],h.prototype,"telegramTokenLocked",2);g([m()],h.prototype,"telegramConfigStatus",2);g([m()],h.prototype,"discordForm",2);g([m()],h.prototype,"discordSaving",2);g([m()],h.prototype,"discordTokenLocked",2);g([m()],h.prototype,"discordConfigStatus",2);g([m()],h.prototype,"slackForm",2);g([m()],h.prototype,"slackSaving",2);g([m()],h.prototype,"slackTokenLocked",2);g([m()],h.prototype,"slackAppTokenLocked",2);g([m()],h.prototype,"slackConfigStatus",2);g([m()],h.prototype,"signalForm",2);g([m()],h.prototype,"signalSaving",2);g([m()],h.prototype,"signalConfigStatus",2);g([m()],h.prototype,"imessageForm",2);g([m()],h.prototype,"imessageSaving",2);g([m()],h.prototype,"imessageConfigStatus",2);g([m()],h.prototype,"presenceLoading",2);g([m()],h.prototype,"presenceEntries",2);g([m()],h.prototype,"presenceError",2);g([m()],h.prototype,"presenceStatus",2);g([m()],h.prototype,"sessionsLoading",2);g([m()],h.prototype,"sessionsResult",2);g([m()],h.prototype,"sessionsError",2);g([m()],h.prototype,"sessionsFilterActive",2);g([m()],h.prototype,"sessionsFilterLimit",2);g([m()],h.prototype,"sessionsIncludeGlobal",2);g([m()],h.prototype,"sessionsIncludeUnknown",2);g([m()],h.prototype,"cronLoading",2);g([m()],h.prototype,"cronJobs",2);g([m()],h.prototype,"cronStatus",2);g([m()],h.prototype,"cronError",2);g([m()],h.prototype,"cronForm",2);g([m()],h.prototype,"cronRunsJobId",2);g([m()],h.prototype,"cronRuns",2);g([m()],h.prototype,"cronBusy",2);g([m()],h.prototype,"skillsLoading",2);g([m()],h.prototype,"skillsReport",2);g([m()],h.prototype,"skillsError",2);g([m()],h.prototype,"skillsFilter",2);g([m()],h.prototype,"skillEdits",2);g([m()],h.prototype,"skillsBusyKey",2);g([m()],h.prototype,"debugLoading",2);g([m()],h.prototype,"debugStatus",2);g([m()],h.prototype,"debugHealth",2);g([m()],h.prototype,"debugModels",2);g([m()],h.prototype,"debugHeartbeat",2);g([m()],h.prototype,"debugCallMethod",2);g([m()],h.prototype,"debugCallParams",2);g([m()],h.prototype,"debugCallResult",2);g([m()],h.prototype,"debugCallError",2);h=g([It("clawdbot-app")],h);
2235
- //# sourceMappingURL=index-CE_axlTS.js.map