#!/usr/bin/env bash set -euo pipefail BASE_URL="https://syhzpqqvrplaqdipcymw.supabase.co/functions/v1/entity-messaging" PLUGIN_DIR="$HOME/.openclaw/extensions/ai-entity" CONFIG_FILE="$HOME/.openclaw/openclaw.json" echo "" echo " 🤖 Ai Entity — OpenClaw Plugin Installer" echo " ─────────────────────────────────────────" echo "" if ! command -v openclaw &>/dev/null; then echo " ❌ OpenClaw not found. Install it first: https://docs.openclaw.ai" exit 1 fi if [ ! -f "\$CONFIG_FILE" ]; then echo " ❌ OpenClaw config not found at \$CONFIG_FILE" echo " Run 'openclaw onboard' first." exit 1 fi echo " Enter your Ai Entity API key:" echo " (starts with ait_user_...)" echo "" printf " API Key: " read -r API_KEY if [ -z "\$API_KEY" ]; then echo ""; echo " ❌ No API key provided."; exit 1 fi echo "" echo " ⏳ Installing plugin..." mkdir -p "\$PLUGIN_DIR" cat > "\$PLUGIN_DIR/openclaw.plugin.json" << 'M_EOF' {"id":"ai-entity","name":"Ai Entity","description":"Channel plugin for +Ai Entity Messaging via Supabase Realtime","version":"1.0.0","channels":["ai-entity"],"configSchema":{"type":"object","additionalProperties":false,"properties":{}}} M_EOF cat > "\$PLUGIN_DIR/package.json" << 'P_EOF' {"name":"ai-entity","version":"1.0.0","main":"index.js","dependencies":{"@supabase/supabase-js":"^2.45.0"}} P_EOF cat > "\$PLUGIN_DIR/index.js" << 'I_EOF' const{createClient}=require("@supabase/supabase-js");const SB_URL="https://syhzpqqvrplaqdipcymw.supabase.co",SB_KEY="eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6InN5aHpwcXF2cnBsYXFkaXBjeW13Iiwicm9sZSI6ImFub24iLCJpYXQiOjE3NDQ4ODAwNDgsImV4cCI6MjA2MDQ1NjA0OH0.X6Rp3JrHra-SdDn8YC0aK3uIsbNZlRJrIUhW9Kiwmac",API_URL="https://syhzpqqvrplaqdipcymw.supabase.co/functions/v1/entity-messaging";function h(k){async function r(m,p,b){const u=API_URL+p,o={method:m,headers:{"x-api-key":k,"Content-Type":"application/json"}};if(b)o.body=JSON.stringify(b);const s=await fetch(u,o);if(!s.ok)throw new Error("Entity API "+s.status+": "+await s.text());return s.json()}return{getIdentity:()=>r("GET","/whoami"),sendMessage:c=>r("POST","/send",{content:c}),setThinking:t=>r("POST","/thinking",{thinking:t}),getMessages:(o={})=>{const p=new URLSearchParams;if(o.limit)p.set("limit",o.limit);if(o.role)p.set("role",o.role);if(o.before)p.set("before",o.before);const q=p.toString();return r("GET","/messages"+(q?"?"+q:""))}}}const ch={id:"ai-entity",meta:{id:"ai-entity",label:"Ai Entity",selectionLabel:"Ai Entity (Supabase Realtime)",docsPath:"/channels/ai-entity",blurb:"+Ai Entity Messaging channel via Supabase Realtime.",aliases:["entity"]},capabilities:{chatTypes:["direct"]},security:{dmPolicy:()=>"open",isAllowed:()=>true},config:{listAccountIds:c=>Object.keys(c.channels?.["ai-entity"]?.accounts??{}),resolveAccount:(c,a)=>c.channels?.["ai-entity"]?.accounts?.[a??"default"]??{accountId:a}},outbound:{deliveryMode:"direct",sendText:async({text:t,account:a})=>{try{const k=a?.config?.apiKey??a?.apiKey;if(!k)return{ok:false,error:"no apiKey"};const hp=h(k);await hp.setThinking(false).catch(()=>{});const r=await hp.sendMessage(t);return{ok:true,messageId:r.message?.id}}catch(e){return{ok:false,error:e.message}}}},gateway:{startAccount:async ctx=>{const a=ctx.account,k=a.config?.apiKey??a?.apiKey;if(!k){ctx.log?.warn?.("[ai-entity] No apiKey");return}const hp=h(k),port=ctx.cfg?.gateway?.port??18789,id=await hp.getIdentity(),cn=id.channel||"entity_chat:"+id.workspace_id;ctx.log?.info?.("[ai-entity] whoami workspace="+id.workspace_id+" channel="+cn);const sb=createClient(SB_URL,SB_KEY),rc=sb.channel(cn);rc.on("broadcast",{event:"user_message"},async({payload:p})=>{ctx.log?.info?.("[ai-entity] user_message: "+(p.content??"").slice(0,120));try{await hp.setThinking(true);await fetch("http://127.0.0.1:"+port+"/ai-entity-webhook",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({senderId:p.user_id??id.user_id,senderName:p.user_name??"User",content:p.content,messageId:p.id,workspaceId:id.workspace_id,accountId:a.accountId,timestamp:p.created_at??new Date().toISOString()})})}catch(e){ctx.log?.error?.("[ai-entity] error: "+e.message);await hp.setThinking(false).catch(()=>{})}});rc.subscribe(s=>{ctx.log?.info?.("[ai-entity] realtime: "+s)});ctx.log?.info?.("[ai-entity] connected");return new Promise(r=>{ctx.abortSignal?.addEventListener("abort",()=>{ctx.log?.info?.("[ai-entity] shutdown");rc.unsubscribe();sb.removeAllChannels();r()})})}}};module.exports=function(api){api.registerHttpHandler?.(async(req,res)=>{const url=new URL(req.url,"http://localhost");if(url.pathname!=="/ai-entity-webhook")return false;if(req.method!=="POST"){res.writeHead(405);res.end("Method not allowed");return true}try{const chunks=[];for await(const c of req)chunks.push(c);const body=JSON.parse(Buffer.concat(chunks).toString()),rt=api.runtime,cfg=api.config,rp=rt?.channel?.reply;if(!rp?.dispatchReplyWithBufferedBlockDispatcher){res.writeHead(500);res.end("dispatch unavailable");return true}const ctx={Provider:"ai-entity",Surface:"ai-entity",AccountSid:body.accountId||"default",From:body.senderId,FromName:body.senderName||"User",To:"ai-entity:"+body.workspaceId,Body:body.content,MessageSid:body.messageId||"entity-"+Date.now(),ChatType:"direct",SessionKey:"agent:entity:main"},fin=rp.finalizeInboundContext(ctx),ak=cfg?.channels?.["ai-entity"]?.accounts?.default?.apiKey,hp=ak?h(ak):null;await rp.dispatchReplyWithBufferedBlockDispatcher({ctx:fin,cfg,dispatcherOptions:{deliver:async p=>{const t=typeof p==="string"?p:(p.text??p.body??String(p));if(hp){await hp.setThinking(false).catch(()=>{});await hp.sendMessage(t)}}}});res.writeHead(200,{"Content-Type":"application/json"});res.end('{"ok":true}')}catch(e){api.logger?.error?.("[ai-entity] "+e.message);res.writeHead(500);res.end(JSON.stringify({error:e.message}))}return true});api.registerChannel?.({plugin:ch});api.registerGatewayMethod?.("ai-entity.status",({respond})=>{respond(true,{registered:true})});api.logger?.info?.("[ai-entity] plugin registered")};module.exports.id="ai-entity";module.exports.name="Ai Entity"; I_EOF echo " 📦 Installing dependencies..." cd "\$PLUGIN_DIR" && npm install --silent 2>/dev/null echo " ⚙️ Updating config..." export API_KEY="\$API_KEY" python3 << 'PYEOF' import json, os config_path = os.path.expanduser("~/.openclaw/openclaw.json") api_key = os.environ.get("API_KEY", "") with open(config_path) as f: config = json.load(f) if "channels" not in config: config["channels"] = {} config["channels"]["ai-entity"] = {"enabled":True,"dmPolicy":"open","accounts":{"default":{"apiKey":api_key,"enabled":True}}} if "plugins" not in config: config["plugins"] = {} if "entries" not in config["plugins"]: config["plugins"]["entries"] = {} config["plugins"]["entries"]["ai-entity"] = {"enabled":True} if "agents" not in config: config["agents"] = {"list":[]} if "list" not in config["agents"]: config["agents"]["list"] = [] ids = [a.get("id") for a in config["agents"]["list"]] if "entity" not in ids: ws = config.get("agents",{}).get("defaults",{}).get("workspace","~/.openclaw/workspace") config["agents"]["list"].append({"id":"entity","name":"Ai Entity","workspace":ws}) if "bindings" not in config: config["bindings"] = [] if not any(b.get("match",{}).get("channel")=="ai-entity" for b in config["bindings"]): config["bindings"].append({"match":{"channel":"ai-entity"},"agentId":"entity"}) with open(config_path,"w") as f: json.dump(config, f, indent=2) print(" ✅ Config updated") PYEOF echo " 🔑 Verifying API key..." WHOAMI=\$(curl -s -H "x-api-key: \$API_KEY" "\$BASE_URL/whoami" 2>/dev/null || echo '{}') if echo "\$WHOAMI" | python3 -c "import sys,json; d=json.load(sys.stdin); assert d.get('workspace_id')" 2>/dev/null; then WID=\$(echo "\$WHOAMI" | python3 -c "import sys,json; print(json.load(sys.stdin)['workspace_id'])") echo " ✅ API key valid — workspace: \$WID" else echo " ⚠️ Could not verify API key (will retry on startup)" fi echo "" echo " 🔄 Restarting OpenClaw..." openclaw gateway restart 2>/dev/null || echo " ⚠️ Run manually: openclaw gateway restart" echo "" echo " ─────────────────────────────────────────" echo " ✅ Ai Entity plugin installed!" echo "" echo " Send a message in the Entity chat to test." echo "" echo " Disable: openclaw plugins disable ai-entity" echo " Enable: openclaw plugins enable ai-entity" echo " ─────────────────────────────────────────" echo ""