beeos-claw 0.5.46 → 0.5.47

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.
@@ -1 +1 @@
1
- import{describe,expect,it,vi}from"vitest";import{ACPServer}from"./acp-server.js";const gatewayMock=vi.hoisted(()=>({instances:[]}));function makeConfig(){return{agentGatewayUrl:"https://agent-gw.example.test",keyFile:"/tmp/test.key",bridge:{url:"wss://bridge.example.test",service:"acp",shell:{enabled:!1}},gateway:{url:"ws://127.0.0.1:18789",token:"test-token",protocol:3,clientId:"test-client",agentId:"main"},retry:{baseMs:1,maxMs:10,maxAttempts:0},log:{enabled:!1,verbose:!1,dir:"/tmp"},promptTimeoutMs:1e3,terminalWsPort:0,terminalWsAllowedOrigins:[],streamIdleTimeoutMs:1e3,streamStaleTimeoutMs:1e3,cronSignalWindowMs:1e3,cronMaxDurationDefaultMs:3e5,cronMaxDurationCapMs:18e5,canvasRelayUrl:"",canvasEnabled:!1,openclawCanvasCompat:!1,messageBus:{enabled:!1,serviceUrl:"",url:"",principalId:"",a2aEnabled:!1}}}vi.mock("./gateway-client.js",()=>({GatewayClient:class{connected=!1;request=vi.fn();handlers=new Map;constructor(){gatewayMock.instances.push(this)}get isConnected(){return this.connected}on(e,t){const a=this.handlers.get(e)??[];return a.push(t),this.handlers.set(e,a),this}async start(){}stop(){this.connected=!1}emit(e,t){for(const a of this.handlers.get(e)??[])a(t)}}})),describe("ACPServer agent sync recovery",()=>{it("declares push delivery mode when message bus is enabled",async()=>{gatewayMock.instances.length=0;const e=vi.fn(async(e,t)=>new Response(JSON.stringify({created:0,updated:1,deactivated:0,data:[{id:"platform-main",instanceId:"inst_test",name:"main",displayName:"main",description:"Main",version:"0.1.0",avatarUrl:"",status:"active"}]}),{status:200,headers:{"Content-Type":"application/json"}})),t=makeConfig();t.messageBus.enabled=!0,t.messageBus.serviceUrl="https://msg.example.test";const a={info:vi.fn(),warn:vi.fn(),debug:vi.fn(),error:vi.fn()},n=new ACPServer(t,a,void 0,e,"inst_test");await n.connectGateway();const s=gatewayMock.instances[0];s.connected=!0,s.request.mockImplementation(async e=>{if("agents.list"===e)return{defaultId:"main",mainKey:"main",scope:"test",agents:[{id:"main",name:"Main"}]};if("skills.status"===e)return{skills:[]};throw new Error(`unexpected method: ${e}`)}),n.client={readyState:1},await n.tryAgentSync();const i=JSON.parse(String(e.mock.calls[0]?.[1]?.body));expect(i.agents[0].deliveryMode).toBe("push")}),it("uses the local OpenClaw agent key when agents.list id is a platform UUID",async()=>{gatewayMock.instances.length=0;const e=vi.fn(async(e,t)=>new Response(JSON.stringify({created:0,updated:1,deactivated:1,data:[{id:"25aca934-5e41-4464-b142-157c779cba93",instanceId:"inst_test",name:"main",displayName:"OpenClaw",description:"main",version:"0.1.0",avatarUrl:"",status:"active"}]}),{status:200,headers:{"Content-Type":"application/json"}})),t=makeConfig();t.messageBus.enabled=!0,t.messageBus.serviceUrl="https://msg.example.test";const a={info:vi.fn(),warn:vi.fn(),debug:vi.fn(),error:vi.fn()},n=new ACPServer(t,a,void 0,e,"inst_test");await n.connectGateway();const s=gatewayMock.instances[0];s.connected=!0,s.request.mockImplementation(async(e,t)=>{if("agents.list"===e)return{defaultId:"main",mainKey:"main",scope:"test",agents:[{id:"25aca934-5e41-4464-b142-157c779cba93",name:"main",identity:{name:"OpenClaw"}}]};if("skills.status"===e)return expect(t?.agentId).toBe("25aca934-5e41-4464-b142-157c779cba93"),{skills:[]};throw new Error(`unexpected method: ${e}`)}),n.client={readyState:1},await n.tryAgentSync();const i=JSON.parse(String(e.mock.calls[0]?.[1]?.body));expect(i.agents[0]).toEqual(expect.objectContaining({name:"main",displayName:"OpenClaw",deliveryMode:"push"})),expect(n.core.resolvePlatformAgentId("main")).toBe("25aca934-5e41-4464-b142-157c779cba93")}),it("retries agent sync when OpenClaw Gateway reconnects after initial wait-gateway failure",async()=>{gatewayMock.instances.length=0;let e=!1;const t=vi.fn(async()=>new Response(JSON.stringify({created:1,updated:0,deactivated:0,data:[{id:"platform-main",instanceId:"inst_test",name:"main",displayName:"main",description:"Main",version:"0.1.0",avatarUrl:"",status:"active"}]}),{status:200,headers:{"Content-Type":"application/json"}})),a={info:vi.fn(),warn:vi.fn(),debug:vi.fn(),error:vi.fn()},n=new ACPServer(makeConfig(),a,void 0,t,"inst_test");await n.connectGateway();const s=gatewayMock.instances[0];s.request.mockImplementation(async t=>{if(!e)throw new Error("Not connected to Gateway");if("agents.list"===t)return{defaultId:"main",mainKey:"main",scope:"test",agents:[{id:"main",name:"Main"}]};if("skills.status"===t)return{skills:[]};if("cron.list"===t)return{jobs:[]};throw new Error(`unexpected method: ${t}`)}),n.client={readyState:1},await n.tryAgentSync(),expect(t).not.toHaveBeenCalled(),expect(a.info).toHaveBeenCalledWith("[acp-server] agent sync waiting for OpenClaw Gateway connection"),e=!0,s.connected=!0,s.emit("connected"),await vi.waitFor(()=>expect(t).toHaveBeenCalledTimes(1)),expect(t).toHaveBeenCalledWith(new URL("/api/v1/agents/sync","https://agent-gw.example.test"),expect.objectContaining({method:"POST"})),expect(a.info).toHaveBeenCalledWith(expect.stringContaining("[acp-server] agent mapping loaded: main"))})});
1
+ import{describe,expect,it,vi}from"vitest";import{ACPServer}from"./acp-server.js";const gatewayMock=vi.hoisted(()=>({instances:[]}));function makeConfig(){return{agentGatewayUrl:"https://agent-gw.example.test",keyFile:"/tmp/test.key",bridge:{url:"wss://bridge.example.test",service:"acp",shell:{enabled:!1}},gateway:{url:"ws://127.0.0.1:18789",token:"test-token",protocol:3,clientId:"test-client",agentId:"main"},retry:{baseMs:1,maxMs:10,maxAttempts:0},log:{enabled:!1,verbose:!1,dir:"/tmp"},promptTimeoutMs:1e3,terminalWsPort:0,terminalWsAllowedOrigins:[],streamIdleTimeoutMs:1e3,streamStaleTimeoutMs:1e3,cronSignalWindowMs:1e3,cronMaxDurationDefaultMs:3e5,cronMaxDurationCapMs:18e5,canvasRelayUrl:"",canvasEnabled:!1,openclawCanvasCompat:!1,messageBus:{enabled:!1,serviceUrl:"",url:"",principalId:"",a2aEnabled:!1}}}vi.mock("./gateway-client.js",()=>({GatewayClient:class{connected=!1;request=vi.fn();handlers=new Map;constructor(){gatewayMock.instances.push(this)}get isConnected(){return this.connected}on(e,t){const a=this.handlers.get(e)??[];return a.push(t),this.handlers.set(e,a),this}async start(){}stop(){this.connected=!1}emit(e,t){for(const a of this.handlers.get(e)??[])a(t)}}})),describe("ACPServer agent sync recovery",()=>{it("declares push delivery mode when message bus is enabled",async()=>{gatewayMock.instances.length=0;const e=vi.fn(async(e,t)=>new Response(JSON.stringify({created:0,updated:1,deactivated:0,data:[{id:"platform-main",instanceId:"inst_test",name:"main",displayName:"main",description:"Main",version:"0.1.0",avatarUrl:"",status:"active"}]}),{status:200,headers:{"Content-Type":"application/json"}})),t=makeConfig();t.messageBus.enabled=!0,t.messageBus.serviceUrl="https://msg.example.test";const a={info:vi.fn(),warn:vi.fn(),debug:vi.fn(),error:vi.fn()},n=new ACPServer(t,a,void 0,e,"inst_test");await n.connectGateway();const s=gatewayMock.instances[0];s.connected=!0,s.request.mockImplementation(async e=>{if("agents.list"===e)return{defaultId:"main",mainKey:"main",scope:"test",agents:[{id:"main",name:"Main"}]};if("skills.status"===e)return{skills:[]};throw new Error(`unexpected method: ${e}`)}),n.client={readyState:1},await n.tryAgentSync();const i=JSON.parse(String(e.mock.calls[0]?.[1]?.body));expect(i.agents[0].deliveryMode).toBe("push")}),it("uses the local OpenClaw agent key when agents.list id is a platform UUID",async()=>{gatewayMock.instances.length=0;const e=vi.fn(async(e,t)=>new Response(JSON.stringify({created:0,updated:1,deactivated:1,data:[{id:"25aca934-5e41-4464-b142-157c779cba93",instanceId:"inst_test",name:"main",displayName:"OpenClaw",description:"main",version:"0.1.0",avatarUrl:"",status:"active"}]}),{status:200,headers:{"Content-Type":"application/json"}})),t=makeConfig();t.messageBus.enabled=!0,t.messageBus.serviceUrl="https://msg.example.test";const a={info:vi.fn(),warn:vi.fn(),debug:vi.fn(),error:vi.fn()},n=new ACPServer(t,a,void 0,e,"inst_test");await n.connectGateway();const s=gatewayMock.instances[0];s.connected=!0,s.request.mockImplementation(async(e,t)=>{if("agents.list"===e)return{defaultId:"main",mainKey:"main",scope:"test",agents:[{id:"25aca934-5e41-4464-b142-157c779cba93",name:"main",identity:{name:"OpenClaw"}}]};if("skills.status"===e)return expect(t?.agentId).toBe("25aca934-5e41-4464-b142-157c779cba93"),{skills:[]};throw new Error(`unexpected method: ${e}`)}),n.client={readyState:1},await n.tryAgentSync();const i=JSON.parse(String(e.mock.calls[0]?.[1]?.body));expect(i.agents[0]).toEqual(expect.objectContaining({name:"main",displayName:"OpenClaw",deliveryMode:"push"})),expect(n.core.resolvePlatformAgentId("main")).toBe("25aca934-5e41-4464-b142-157c779cba93")}),it("skips platform UUID shadow agents when a local agent key is present",async()=>{gatewayMock.instances.length=0;const e=vi.fn(async(e,t)=>new Response(JSON.stringify({created:0,updated:1,deactivated:1,data:[{id:"25aca934-5e41-4464-b142-157c779cba93",instanceId:"inst_test",name:"main",displayName:"main",description:"main",version:"0.1.0",avatarUrl:"",status:"active"}]}),{status:200,headers:{"Content-Type":"application/json"}})),t=makeConfig();t.messageBus.enabled=!0,t.messageBus.serviceUrl="https://msg.example.test";const a={info:vi.fn(),warn:vi.fn(),debug:vi.fn(),error:vi.fn()},n=new ACPServer(t,a,void 0,e,"inst_test");await n.connectGateway();const s=gatewayMock.instances[0];s.connected=!0,s.request.mockImplementation(async(e,t)=>{if("agents.list"===e)return{defaultId:"main",mainKey:"main",scope:"test",agents:[{id:"main",name:"main"},{id:"25aca934-5e41-4464-b142-157c779cba93",name:"25aca934-5e41-4464-b142-157c779cba93"}]};if("skills.status"===e)return expect(t?.agentId).toBe("main"),{skills:[]};throw new Error(`unexpected method: ${e}`)}),n.client={readyState:1},await n.tryAgentSync();const i=JSON.parse(String(e.mock.calls[0]?.[1]?.body));expect(i.agents).toHaveLength(1),expect(i.agents[0].name).toBe("main"),expect(a.debug).toHaveBeenCalledWith("[agent-sync] skipping platform-id shadow agent 25aca934-5e41-4464-b142-157c779cba93")}),it("retries agent sync when OpenClaw Gateway reconnects after initial wait-gateway failure",async()=>{gatewayMock.instances.length=0;let e=!1;const t=vi.fn(async()=>new Response(JSON.stringify({created:1,updated:0,deactivated:0,data:[{id:"platform-main",instanceId:"inst_test",name:"main",displayName:"main",description:"Main",version:"0.1.0",avatarUrl:"",status:"active"}]}),{status:200,headers:{"Content-Type":"application/json"}})),a={info:vi.fn(),warn:vi.fn(),debug:vi.fn(),error:vi.fn()},n=new ACPServer(makeConfig(),a,void 0,t,"inst_test");await n.connectGateway();const s=gatewayMock.instances[0];s.request.mockImplementation(async t=>{if(!e)throw new Error("Not connected to Gateway");if("agents.list"===t)return{defaultId:"main",mainKey:"main",scope:"test",agents:[{id:"main",name:"Main"}]};if("skills.status"===t)return{skills:[]};if("cron.list"===t)return{jobs:[]};throw new Error(`unexpected method: ${t}`)}),n.client={readyState:1},await n.tryAgentSync(),expect(t).not.toHaveBeenCalled(),expect(a.info).toHaveBeenCalledWith("[acp-server] agent sync waiting for OpenClaw Gateway connection"),e=!0,s.connected=!0,s.emit("connected"),await vi.waitFor(()=>expect(t).toHaveBeenCalledTimes(1)),expect(t).toHaveBeenCalledWith(new URL("/api/v1/agents/sync","https://agent-gw.example.test"),expect.objectContaining({method:"POST"})),expect(a.info).toHaveBeenCalledWith(expect.stringContaining("[acp-server] agent mapping loaded: main"))})});
@@ -1 +1 @@
1
- const UUID_AGENT_ID_RE=/^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;function resolveSyncAgentName(t){const e=t.id.trim(),n=t.name?.trim()??"";return UUID_AGENT_ID_RE.test(e)&&n?n:e||n}export function isAgentSyncWaitForGateway(t){return!(!t||t instanceof Map||"wait_gateway"!==t.status)}export async function syncAgentsToPlatform(t){const{gateway:e,agentGatewayUrl:n,capabilities:a,deliveryMode:s,agentFetch:i,logger:r}=t;if(!i)return r?.debug?.("[agent-sync] no agentFetch — skipping sync (no key pair)"),null;if(!n)return r?.debug?.("[agent-sync] no agentGatewayUrl — skipping sync"),null;try{const t=await e.request("agents.list",{}),o=t?.agents??[];if(0===o.length)return r?.debug?.("[agent-sync] no agents from Gateway — skipping sync"),null;const c=[];for(const t of o){let n=[];try{const a=await e.request("skills.status",{agentId:t.id});n=(a?.skills??[]).filter(t=>t.eligible&&!t.disabled).map(t=>({name:t.name,description:t.description,...t.emoji?{emoji:t.emoji}:{},...t.skillKey?{skillKey:t.skillKey}:{}}))}catch{r?.debug?.(`[agent-sync] skills.status failed for agent ${t.id} — continuing without skills`)}const i={};if(a)for(const[t,e]of Object.entries(a))e&&(i[t]=!0);const o=resolveSyncAgentName(t),l=t.identity?.name||t.name||o;c.push({name:o,displayName:l,description:t.name||t.identity?.name||o,version:"0.1.0",capabilities:i,skills:n,avatarUrl:t.identity?.avatarUrl||"",homepage:"",...s?{deliveryMode:s}:{}})}const l=new URL("/api/v1/agents/sync",n),d=await i(l,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({agents:c})});if(d.ok){const t=await d.json();r?.info?.(`[agent-sync] synced ${c.length} agent(s) — created=${t.created??0} updated=${t.updated??0} deactivated=${t.deactivated??0}`);const e=new Map,n=Array.isArray(t.data)?t.data:[];for(const t of n)t&&"string"==typeof t.name&&"string"==typeof t.id&&e.set(t.name,{id:t.id,instanceId:t.instanceId??"",name:t.name,displayName:t.displayName??t.name,description:t.description??"",version:t.version??"",avatarUrl:t.avatarUrl??"",status:t.status??"active"});return e.size>0?e:null}{const t=await d.text().catch(()=>"");return r?.warn?.(`[agent-sync] sync failed: HTTP ${d.status} — ${t.slice(0,200)}`),null}}catch(t){const e=t instanceof Error?t.message:String(t);return r?.warn?.(`[agent-sync] sync error: ${e}`),"Not connected to Gateway"===e?{status:"wait_gateway",reason:e}:null}}
1
+ const UUID_AGENT_ID_RE=/^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;function resolveSyncAgentName(t){const e=t.id.trim(),n=t.name?.trim()??"";return UUID_AGENT_ID_RE.test(e)&&n?n:e||n}export function isAgentSyncWaitForGateway(t){return!(!t||t instanceof Map||"wait_gateway"!==t.status)}export async function syncAgentsToPlatform(t){const{gateway:e,agentGatewayUrl:n,capabilities:a,deliveryMode:s,agentFetch:i,logger:r}=t;if(!i)return r?.debug?.("[agent-sync] no agentFetch — skipping sync (no key pair)"),null;if(!n)return r?.debug?.("[agent-sync] no agentGatewayUrl — skipping sync"),null;try{const t=await e.request("agents.list",{}),o=t?.agents??[];if(0===o.length)return r?.debug?.("[agent-sync] no agents from Gateway — skipping sync"),null;const c=o.map(t=>({agent:t,syncName:resolveSyncAgentName(t)})).filter(t=>t.syncName),l=c.some(t=>!UUID_AGENT_ID_RE.test(t.syncName)),y=[];for(const{agent:t,syncName:n}of c){if(l&&UUID_AGENT_ID_RE.test(n)){r?.debug?.(`[agent-sync] skipping platform-id shadow agent ${n}`);continue}let i=[];try{const n=await e.request("skills.status",{agentId:t.id});i=(n?.skills??[]).filter(t=>t.eligible&&!t.disabled).map(t=>({name:t.name,description:t.description,...t.emoji?{emoji:t.emoji}:{},...t.skillKey?{skillKey:t.skillKey}:{}}))}catch{r?.debug?.(`[agent-sync] skills.status failed for agent ${t.id} — continuing without skills`)}const o={};if(a)for(const[t,e]of Object.entries(a))e&&(o[t]=!0);const c=t.identity?.name||t.name||n;y.push({name:n,displayName:c,description:t.name||t.identity?.name||n,version:"0.1.0",capabilities:o,skills:i,avatarUrl:t.identity?.avatarUrl||"",homepage:"",...s?{deliveryMode:s}:{}})}const d=new URL("/api/v1/agents/sync",n),g=await i(d,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({agents:y})});if(g.ok){const t=await g.json();r?.info?.(`[agent-sync] synced ${y.length} agent(s) — created=${t.created??0} updated=${t.updated??0} deactivated=${t.deactivated??0}`);const e=new Map,n=Array.isArray(t.data)?t.data:[];for(const t of n)t&&"string"==typeof t.name&&"string"==typeof t.id&&e.set(t.name,{id:t.id,instanceId:t.instanceId??"",name:t.name,displayName:t.displayName??t.name,description:t.description??"",version:t.version??"",avatarUrl:t.avatarUrl??"",status:t.status??"active"});return e.size>0?e:null}{const t=await g.text().catch(()=>"");return r?.warn?.(`[agent-sync] sync failed: HTTP ${g.status} — ${t.slice(0,200)}`),null}}catch(t){const e=t instanceof Error?t.message:String(t);return r?.warn?.(`[agent-sync] sync error: ${e}`),"Not connected to Gateway"===e?{status:"wait_gateway",reason:e}:null}}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "beeos-claw",
3
- "version": "0.5.46",
3
+ "version": "0.5.47",
4
4
  "description": "OpenClaw plugin that bridges the BeeOS platform with the local OpenClaw Gateway, providing file transfer, ACP bridge, and observability.",
5
5
  "license": "MIT",
6
6
  "author": "BeeOS <dev@beeos.ai>",