beeos-claw 0.6.37 → 0.6.38

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{RUNTIME_MESSAGE_TYPES}from"../runtime/runtime-message-channel-contract.js";export class McpCredentialRevokedError extends Error{credentialRef;constructor(e){super("MCP credential is revoked"),this.credentialRef=e}}export class McpDesiredStateController{store;bridge;credentials;references;releases;receiptHistory;nativeProjection;tail=Promise.resolve();unsubscribe;started=!1;revoked=new Map;activeCredentialRevisions=new Map;projections=new Map;credentialServers=new Map;liveReleaseKeys=new Map;resolvedNativeMaterial=new Map;desiredRevision="0";authorityEpoch;authorityGeneration=0;authorityCloseBlocked=!1;constructor(e,t,i,r,s,a,n){this.store=e,this.bridge=t,this.credentials=i,this.references=r,this.releases=s,this.receiptHistory=a,this.nativeProjection=n,this.authorityEpoch=i?.currentAuthority()?.runtimeEpoch??null}setRuntimeReceiptHistoryPort(e){this.receiptHistory=e}async start(e){await this.exclusive(async()=>{this.started||(this.started=!0,this.authorityEpoch=this.credentials?.currentAuthority()?.runtimeEpoch??null,this.unsubscribe=this.credentials?.subscribe(e=>this.acceptCredentialEvent(e)));const t=await this.store.load();await this.reconcileAll(e,t),await this.collectLocalGarbage(void 0,!0),this.scheduleNativeConverge()})}async reconnect(e){await this.exclusive(async()=>{await this.reconcileAll(e),this.scheduleNativeConverge()})}async list(){return await this.exclusive(async()=>{const e=await this.store.load();this.rememberDesired(e);const t=new Map(this.bridge.list().map(e=>[e.name,e]));return{revision:e.revision,servers:e.servers.map(i=>{const r=t.get(i.name);return Object.freeze({...i,connected:Boolean(r),tools:r?.tools??Object.freeze([]),projection:this.projections.get(i.name)??pending(e.revision)})})}})}async set(e,t){return await this.exclusive(async()=>{const i=await this.store.transaction(async i=>{const r=await i.set(e);return e.credentialRef&&this.references?.owns(e.credentialRef)&&await this.references.commit(e.credentialRef).catch(()=>{}),this.rememberDesired(r),this.activeCredentialRevisions.delete(e.name),this.liveReleaseKeys.delete(e.name),this.resolvedNativeMaterial.delete(e.name),await this.bridge.unset(e.name),this.projections.set(e.name,pending(r.revision)),await this.recordDesiredStateCommit(r,e.name,t),await this.collectLocalGarbage(i),r});return this.scheduleNativeConverge(),i})}async unset(e,t){return await this.exclusive(async()=>{const i=await this.store.transaction(async i=>{const r=await i.unset(e);return this.rememberDesired(r),this.activeCredentialRevisions.delete(e),this.liveReleaseKeys.delete(e),this.resolvedNativeMaterial.delete(e),await this.bridge.unset(e),this.projections.set(e,pending(r.revision)),await this.recordDesiredStateCommit(r,e,t),await this.collectLocalGarbage(i),r});return this.scheduleNativeConverge(),i})}async isCredentialReferenced(e){return await this.exclusive(async()=>(await this.store.load()).servers.some(t=>t.credentialRef===e))}async removeLocalCredentialIfUnreferenced(e){await this.exclusive(async()=>{this.references?.owns(e)&&await this.store.transaction(async t=>{(await t.load()).servers.some(t=>t.credentialRef===e)||(await t.assertOwned(),await this.references.remove(e))})})}async project(e,t,i,r){return await this.exclusive(async()=>{const s=await this.store.load();this.rememberDesired(s);const a=s.servers.find(t=>t.name===e);if(t?.aborted)return pending(s.revision);if(i&&!matchesProjectionAttempt(e,a,i))return blocked(s.revision);const n=i?await this.findAttemptActive(s,a,i):void 0;if((i||this.releases)&&!n){this.activeCredentialRevisions.delete(e),this.liveReleaseKeys.delete(e),await this.bridge.unset(e);const t=pending(s.revision);return this.projections.set(e,t),t}if(i&&!r&&a?.credentialRef&&this.credentials?.currentAuthority()?.runtimeEpoch!==i.runtimeEpoch)return this.projections.set(e,blocked(s.revision)),blocked(s.revision);if(!a){this.activeCredentialRevisions.delete(e),this.resolvedNativeMaterial.delete(e),await this.bridge.unset(e);const t=synced(s.revision);return this.projections.set(e,t),this.scheduleNativeConverge(),withOperationReceipt(t,s,void 0,i,n)}const o=n?releaseKey(n):void 0;if(o&&this.liveReleaseKeys.get(e)===o&&this.bridge.list().some(t=>t.name===e)){const t=synced(s.revision);return this.projections.set(e,t),withOperationReceipt(t,s,a,i,n)}const c=await this.reconcileServer(a,s,t,n,i,r);return"synced"===c.state&&o&&this.liveReleaseKeys.set(e,o),this.scheduleNativeConverge(),withOperationReceipt(c,s,a,i,n)})}async stop(){await this.exclusive(async()=>{this.started=!1,this.unsubscribe?.(),this.unsubscribe=void 0,this.activeCredentialRevisions.clear(),this.revoked.clear(),this.projections.clear(),this.credentialServers.clear(),this.liveReleaseKeys.clear(),this.resolvedNativeMaterial.clear(),this.authorityEpoch=null,await this.bridge.close()})}nudgeNativeProjection(){this.scheduleNativeConverge()}scheduleNativeConverge(){this.nativeProjection&&this.nativeProjection.scheduleConverge(()=>this.computeNativeTarget())}async computeNativeTarget(){return await this.exclusive(async()=>{const e=await this.store.load(),t=new Map;for(const i of e.servers){if(!i.config)continue;if(!i.credentialRef){t.set(i.name,{config:i.config});continue}const e=this.resolvedNativeMaterial.get(i.name);e&&t.set(i.name,{config:i.config,credential:e})}return t})}acceptCredentialEvent(e){if("authority"===e.type){this.authorityEpoch=null;const t=++this.authorityGeneration;for(const e of this.credentialServers.keys())this.activeCredentialRevisions.delete(e),this.liveReleaseKeys.delete(e),this.projections.set(e,pending(this.desiredRevision));const i=this.bridge.detachMany(this.credentialServers.keys()).then(()=>({ok:!0}),e=>({ok:!1,error:e}));return void this.exclusive(()=>this.onAuthorityEvent(e.runtimeEpoch,t,i))}this.exclusive(()=>this.onCredentialEvent(e))}async onAuthorityEvent(e,t,i){const r=await i;if(!r.ok)throw this.authorityCloseBlocked=!0,r.error;const s=await this.store.load();if(this.rememberDesired(s),t!==this.authorityGeneration||null===e||this.authorityCloseBlocked)return;const a=this.credentials?.currentAuthority();a&&a.runtimeEpoch===e&&(this.authorityEpoch=e,await this.reconcileAll(void 0,s),this.scheduleNativeConverge())}async onCredentialEvent(e){const t=BigInt(e.credentialRevision),i=this.revoked.get(e.credentialRef)??-1n,r=await this.store.load();this.rememberDesired(r);const s=r.servers.filter(t=>t.credentialRef===e.credentialRef);if("revoked"!==e.type){if(!(t<=i)){this.revoked.delete(e.credentialRef);for(const e of s){if(t>(this.activeCredentialRevisions.get(e.name)??-1n)){if(this.activeCredentialRevisions.delete(e.name),this.liveReleaseKeys.delete(e.name),await this.bridge.unset(e.name),!this.releases){await this.reconcileServer(e,r);continue}this.projections.set(e.name,blocked(r.revision))}}this.scheduleNativeConverge()}}else{if(t<i)return;this.revoked.set(e.credentialRef,t);for(const e of s)this.activeCredentialRevisions.delete(e.name),this.liveReleaseKeys.delete(e.name),this.resolvedNativeMaterial.delete(e.name),this.projections.set(e.name,blocked(r.revision)),await this.bridge.unset(e.name);this.scheduleNativeConverge()}}async reconcileAll(e,t){const i=t??await this.store.load();if(this.rememberDesired(i),!this.releases){const t=new Set(i.servers.map(e=>e.name));for(const e of this.bridge.list())t.has(e.name)||await this.bridge.unset(e.name);for(const t of i.servers)await this.reconcileServer(t,i,e);return}const r=await this.releases.listMcpActiveDesiredStates(),s=await this.releases.listMcpLiveReleases(),a=new Set(i.servers.map(e=>e.name));for(const e of this.bridge.list())a.has(e.name)||await this.bridge.unset(e.name);for(const t of i.servers){let a=r.find(e=>releaseMatchesDesired(e,i,t));if(!a){const e=s.find(e=>releaseMatchesDesired(e,i,t)),r=void 0===e?"pending":await this.recoverPendingServiceRelease(e,i,t);if("string"==typeof r){this.activeCredentialRevisions.delete(t.name),this.liveReleaseKeys.delete(t.name),await this.bridge.unset(t.name),this.projections.set(t.name,"pending"===r?pending(i.revision):blocked(i.revision));continue}a=r}if(t.credentialRef&&this.credentials?.currentAuthority()?.runtimeEpoch!==a.runtimeEpoch){this.activeCredentialRevisions.delete(t.name),this.liveReleaseKeys.delete(t.name),await this.bridge.unset(t.name),this.projections.set(t.name,blocked(i.revision));continue}const n=releaseKey(a);if(this.liveReleaseKeys.get(t.name)===n&&this.bridge.list().some(e=>e.name===t.name)){this.projections.set(t.name,synced(i.revision));continue}"synced"===(await this.reconcileServer(t,i,e,a)).state&&this.liveReleaseKeys.set(t.name,releaseKey(a))}}async recoverPendingServiceRelease(e,t,i){if("service"!==e.source)return"blocked";if(!this.releases||!releaseMatchesDesired(e,t,i))return"blocked";let r;try{r=await this.releases.get(e.operationId)}catch{return"pending"}if(!r||!releaseMatchesJournalRecord(e,r))return"blocked";const s=await this.readReceiptHistory(e.operationId);if("unavailable"===s.status)return"pending";if("not_found"===s.status||"expired"===s.status)return"blocked";if(!serviceHistoryMatchesRelease(e,r,s.snapshot))return"blocked";try{const r=(await this.releases.promoteMcpDesiredStateActive({operationId:e.operationId,source:"service",effectRecordHash:e.effectRecordHash,terminalMessageId:s.snapshot.final.message_id})).mcpActiveDesiredState;return r&&releaseMatchesDesired(r,t,i)?r:"blocked"}catch{return"pending"}}async readReceiptHistory(e){const t=this.receiptHistory;if(!t)return{status:"unavailable"};const i=async()=>{try{return await t.readOperationHistory({operationId:e})}catch{return{status:"unavailable"}}},r=await i();return"unavailable"!==r.status?r:(await Promise.resolve(),await i())}async reconcileServer(e,t,i,r,s,a){try{e.credentialRef&&this.bridge.list().some(t=>t.name===e.name)&&await this.bridge.detach(e.name),await this.connectServer(e,t,i,r,s,a);const n=synced(t.revision);return this.projections.set(e.name,n),n}catch(i){if(i instanceof McpCredentialRevokedError){this.revoked.set(e.credentialRef,this.revoked.get(e.credentialRef)??0n),this.activeCredentialRevisions.delete(e.name),await this.bridge.unset(e.name);const i=blocked(t.revision);return this.projections.set(e.name,i),i}const r=pending(t.revision);return this.projections.set(e.name,r),r}}async connectServer(e,t,i,r,s,a){if(i?.aborted)throw new Error("MCP projection attempt is no longer authoritative");if(a){if(!s||a.runtimeEpoch!==s.runtimeEpoch||r&&!releaseAuthorizesServer(r,e,t,a.runtimeEpoch,s))throw new Error("MCP operation-scoped activation is no longer authoritative");const n={generation:this.authorityGeneration,isCurrent:()=>!i?.aborted&&a.runtimeEpoch===s.runtimeEpoch},o=await this.bridge.set(e.name,a.config,a.credential,n);if(!n.isCurrent())throw await this.bridge.detach(e.name),new Error("MCP operation-scoped activation lost authority during bridge connection");return a.credential?this.resolvedNativeMaterial.set(e.name,a.credential):this.resolvedNativeMaterial.delete(e.name),o}if(r&&!releaseAuthorizesServer(r,e,t,this.credentials?.currentAuthority()?.runtimeEpoch,s))throw new Error("MCP live release proof no longer authorizes this server");if(!e.credentialRef){if(this.activeCredentialRevisions.delete(e.name),this.resolvedNativeMaterial.delete(e.name),!e.config)throw new Error("MCP desired state has no persisted public config for this server");const t=await this.bridge.set(e.name,e.config);if(i?.aborted)throw await this.bridge.detach(e.name),new Error("MCP projection attempt lost authority during bridge connection");return t}if(!this.credentials)throw new Error("runtime-scoped MCP credential resolver is unavailable");if(this.revoked.has(e.credentialRef))throw new McpCredentialRevokedError(e.credentialRef);const n=this.credentials.currentAuthority();if(!n||null===this.authorityEpoch||n.runtimeEpoch!==this.authorityEpoch)throw new Error("runtime-scoped MCP credential authority is pending");const o=this.authorityGeneration,c=this.authorityEpoch,d={generation:o,isCurrent:()=>o===this.authorityGeneration&&this.authorityEpoch===c&&this.credentials?.currentAuthority()?.runtimeEpoch===c};if(!d.isCurrent())throw new Error("runtime-scoped MCP credential authority changed");const h=await this.credentials.resolve({credentialRef:e.credentialRef,serverName:e.name,...i?{signal:i}:{}});if(i?.aborted||h.runtimeEpoch!==c||!d.isCurrent()||r&&!releaseAuthorizesServer(r,e,t,this.credentials?.currentAuthority()?.runtimeEpoch,s))throw new Error("runtime-scoped MCP credential authority changed during resolution");const l=BigInt(h.credentialRevision),u=this.revoked.get(e.credentialRef);if(void 0!==u&&l<=u)throw new McpCredentialRevokedError(e.credentialRef);if(!e.config)throw new Error("MCP desired state has no persisted public config for this server");const v=await this.bridge.set(e.name,e.config,h.credential,d);if(i?.aborted||!d.isCurrent()||r&&!releaseAuthorizesServer(r,e,t,this.credentials?.currentAuthority()?.runtimeEpoch,s))throw await this.bridge.detach(e.name),new Error("runtime-scoped MCP credential authority changed during bridge connection");return this.activeCredentialRevisions.set(e.name,l),this.resolvedNativeMaterial.set(e.name,h.credential),v}rememberDesired(e){this.desiredRevision=e.revision,this.credentialServers.clear();for(const t of e.servers)t.credentialRef&&this.credentialServers.set(t.name,t.credentialRef)}async recordDesiredStateCommit(e,t,i){if(!i)return;if(!this.releases)throw new Error("MCP desired state requires the shared command journal release authority");const r=e.servers.find(e=>e.name===t);await this.releases.recordMcpDesiredStateCommit({schemaVersion:1,...i,desiredStateRevision:e.revision,desiredStateHash:e.sha256,server:r?{name:r.name,definitionRef:r.definitionRef,version:r.version,definitionSha256:r.sha256,...r.credentialRef?{credentialRef:r.credentialRef}:{}}:{name:t,removed:!0}})}async findAttemptActive(e,t,i){const r=(await(this.releases?.listMcpActiveDesiredStates())??[]).find(e=>e.operationId===i.operationId&&e.method===i.method&&e.source===i.source);if(r&&releaseMatchesAttempt(r,e,t,i))return r}async collectLocalGarbage(e,t=!1){if(!this.references)return;if(!e)return void await this.store.transaction(async e=>this.collectLocalGarbage(e,t));const i=await this.references.listOwned(),r=t?await(this.references.listPendingOwned?.())??[]:[];for(const t of[...new Set([...i,...r])]){const i=(await e.load()).servers.some(e=>e.credentialRef===t);await e.assertOwned(),i?r.includes(t)&&await this.references.commit(t).catch(()=>{}):await this.references.remove(t).catch(()=>{})}}async exclusive(e){const t=this.tail.catch(()=>{}).then(e);return this.tail=t.catch(()=>{}),await t}}function pending(e){return Object.freeze({revision:e,state:"pending"})}function synced(e){return Object.freeze({revision:e,state:"synced"})}function blocked(e){return Object.freeze({revision:e,state:"blocked"})}function matchesProjectionAttempt(e,t,i){return i.expected.name===e&&(i.expected.absent?void 0===t:void 0!==t&&t.name===i.expected.name&&t.definitionRef===i.expected.definitionRef&&t.version===i.expected.version&&t.sha256===i.expected.sha256&&t.credentialRef===i.expected.credentialRef)}function withOperationReceipt(e,t,i,r,s){if(!r||!s||"synced"!==e.state)return e;const a=Object.freeze({schemaVersion:1,operationId:r.operationId,method:r.method,runtimeEpoch:r.runtimeEpoch,desiredStateRevision:t.revision,desiredStateHash:t.sha256,server:Object.freeze(i?{name:i.name,definitionRef:i.definitionRef,definitionSha256:i.sha256}:{name:r.expected.name,removed:!0})});return Object.freeze({...e,mcpReceipt:a})}function releaseMatchesDesired(e,t,i){return"mcp.set"===e.method&&e.desiredStateRevision===t.revision&&e.desiredStateHash===t.sha256&&e.server.name===i.name&&e.server.definitionRef===i.definitionRef&&e.server.version===i.version&&e.server.definitionSha256===i.sha256&&e.server.credentialRef===i.credentialRef}function releaseMatchesJournalRecord(e,t){const i=t.mcpDesiredStateCommit,r=t.mcpLiveRelease;return t.operationId===e.operationId&&t.command===e.method&&t.paramsHash===e.paramsHash&&t.runtimeEpoch===e.runtimeEpoch&&("effect_committed"===t.phase||"projection_committed"===t.phase)&&void 0!==t.messageBinding&&void 0!==i&&void 0!==r&&"service"===i.source&&sameMcpRelease(r,e)&&r.effectRecordHash===e.effectRecordHash&&sameMcpRelease(i,e)}function serviceHistoryMatchesRelease(e,t,i){const r=t.messageBinding;if(!r||!e.effectRecordHash)return!1;const s=i.request,a=s.payload,n=i.effectCommitted,o=n?.payload;if(i.operationId!==e.operationId||i.requestMessageId!==r.requestMessageId||i.requestHash!==r.requestHash||s.type!==RUNTIME_MESSAGE_TYPES.request||s.channel_id!==e.operationId||s.message_id!==r.requestMessageId||a.operationId!==e.operationId||a.requestMessageId!==r.requestMessageId||a.requestHash!==r.requestHash||a.paramsHash!==e.paramsHash||("mcp.set"===e.method?"mcp/set"!==a.method:"mcp/unset"!==a.method)||a.command.name!==e.method||a.dispatchEpoch!==e.runtimeEpoch||!n||!o||n.type!==RUNTIME_MESSAGE_TYPES.effectCommitted||n.channel_id!==e.operationId||n.in_reply_to!==r.requestMessageId||n.publisher_id!==a.instanceId||o.operationId!==e.operationId||o.requestMessageId!==r.requestMessageId||o.requestHash!==r.requestHash||o.dispatchEpoch!==e.runtimeEpoch||o.method!==a.method||o.effectRecordHash!==e.effectRecordHash||o.effectRecord.operationId!==e.operationId||o.effectRecord.method!==a.method)return!1;if(i.cancelRequested)return!1;if(!i.terminal)return!1;const c=i.final;return"final_message"===i.terminalSource&&"succeeded"===i.status&&void 0!==c&&c.type===RUNTIME_MESSAGE_TYPES.final&&c.channel_id===e.operationId&&c.in_reply_to===r.requestMessageId&&c.publisher_id===a.instanceId&&c.payload.operationId===e.operationId&&c.payload.requestMessageId===r.requestMessageId&&c.payload.requestHash===r.requestHash&&c.payload.dispatchEpoch===e.runtimeEpoch&&c.payload.method===a.method&&"succeeded"===c.payload.outcome&&c.payload.effectRecordHash===e.effectRecordHash}function sameMcpRelease(e,t){return e.source===t.source&&e.operationId===t.operationId&&e.method===t.method&&e.paramsHash===t.paramsHash&&e.runtimeEpoch===t.runtimeEpoch&&e.desiredStateRevision===t.desiredStateRevision&&e.desiredStateHash===t.desiredStateHash&&e.server.name===t.server.name&&e.server.definitionRef===t.server.definitionRef&&e.server.version===t.server.version&&e.server.definitionSha256===t.server.definitionSha256&&e.server.credentialRef===t.server.credentialRef&&e.server.removed===t.server.removed}function releaseMatchesAttempt(e,t,i,r){return!(e.operationId!==r.operationId||e.method!==r.method||e.source!==r.source||e.paramsHash!==r.paramsHash||e.runtimeEpoch!==r.runtimeEpoch||e.desiredStateRevision!==t.revision||e.desiredStateHash!==t.sha256||"service"===r.source&&!e.effectRecordHash||"acp_local"===r.source&&void 0!==e.effectRecordHash)&&(r.expected.absent?void 0===i&&e.server.name===r.expected.name&&!0===e.server.removed:void 0!==i&&releaseMatchesDesired(e,t,i)&&i.definitionRef===r.expected.definitionRef&&i.version===r.expected.version&&i.sha256===r.expected.sha256&&i.credentialRef===r.expected.credentialRef)}function releaseAuthorizesServer(e,t,i,r,s){return"mcp.set"===e.method&&(!t.credentialRef||e.runtimeEpoch===r)&&releaseMatchesDesired(e,i,t)&&(!s||releaseMatchesAttempt(e,i,t,s))}function releaseKey(e){return[e.source,e.operationId,e.paramsHash,e.runtimeEpoch,e.desiredStateRevision,e.desiredStateHash,e.effectRecordHash??"local"].join("\0")}
1
+ import{RUNTIME_MESSAGE_TYPES}from"../runtime/runtime-message-channel-contract.js";export class McpCredentialRevokedError extends Error{credentialRef;constructor(e){super("MCP credential is revoked"),this.credentialRef=e}}export class McpDesiredStateController{store;bridge;credentials;references;releases;receiptHistory;nativeProjection;logger;tail=Promise.resolve();unsubscribe;started=!1;revoked=new Map;activeCredentialRevisions=new Map;projections=new Map;credentialServers=new Map;liveReleaseKeys=new Map;resolvedNativeMaterial=new Map;credentialRenewalTimers=new Map;desiredRevision="0";authorityEpoch;authorityGeneration=0;authorityCloseBlocked=!1;constructor(e,t,i,r,s,a,n,o){this.store=e,this.bridge=t,this.credentials=i,this.references=r,this.releases=s,this.receiptHistory=a,this.nativeProjection=n,this.logger=o,this.authorityEpoch=i?.currentAuthority()?.runtimeEpoch??null}setRuntimeReceiptHistoryPort(e){this.receiptHistory=e}async start(e){await this.exclusive(async()=>{this.started||(this.started=!0,this.authorityEpoch=this.credentials?.currentAuthority()?.runtimeEpoch??null,this.unsubscribe=this.credentials?.subscribe(e=>this.acceptCredentialEvent(e)));const t=await this.store.load();await this.reconcileAll(e,t),await this.collectLocalGarbage(void 0,!0),this.scheduleNativeConverge()})}async reconnect(e){await this.exclusive(async()=>{await this.reconcileAll(e),this.scheduleNativeConverge()})}async list(){return await this.exclusive(async()=>{const e=await this.store.load();this.rememberDesired(e);const t=new Map(this.bridge.list().map(e=>[e.name,e]));return{revision:e.revision,servers:e.servers.map(i=>{const r=t.get(i.name);return Object.freeze({...i,connected:Boolean(r),tools:r?.tools??Object.freeze([]),projection:this.projections.get(i.name)??pending(e.revision)})})}})}async set(e,t){return await this.exclusive(async()=>{const i=await this.store.transaction(async i=>{const r=await i.set(e);return e.credentialRef&&this.references?.owns(e.credentialRef)&&await this.references.commit(e.credentialRef).catch(()=>{}),this.rememberDesired(r),this.activeCredentialRevisions.delete(e.name),this.liveReleaseKeys.delete(e.name),this.resolvedNativeMaterial.delete(e.name),this.clearCredentialRenewal(e.name),await this.bridge.unset(e.name),this.projections.set(e.name,pending(r.revision)),await this.recordDesiredStateCommit(r,e.name,t),await this.collectLocalGarbage(i),r});return this.scheduleNativeConverge(),i})}async unset(e,t){return await this.exclusive(async()=>{const i=await this.store.transaction(async i=>{const r=await i.unset(e);return this.rememberDesired(r),this.activeCredentialRevisions.delete(e),this.liveReleaseKeys.delete(e),this.resolvedNativeMaterial.delete(e),this.clearCredentialRenewal(e),await this.bridge.unset(e),this.projections.set(e,pending(r.revision)),await this.recordDesiredStateCommit(r,e,t),await this.collectLocalGarbage(i),r});return this.scheduleNativeConverge(),i})}async isCredentialReferenced(e){return await this.exclusive(async()=>(await this.store.load()).servers.some(t=>t.credentialRef===e))}async removeLocalCredentialIfUnreferenced(e){await this.exclusive(async()=>{this.references?.owns(e)&&await this.store.transaction(async t=>{(await t.load()).servers.some(t=>t.credentialRef===e)||(await t.assertOwned(),await this.references.remove(e))})})}async project(e,t,i,r){return await this.exclusive(async()=>{const s=await this.store.load();this.rememberDesired(s);const a=s.servers.find(t=>t.name===e);if(t?.aborted)return pending(s.revision);if(i&&!matchesProjectionAttempt(e,a,i))return blocked(s.revision);const n=i?await this.findAttemptActive(s,a,i):void 0;if((i||this.releases)&&!n){this.activeCredentialRevisions.delete(e),this.liveReleaseKeys.delete(e),await this.bridge.unset(e);const t=pending(s.revision);return this.projections.set(e,t),t}if(i&&!r&&a?.credentialRef&&this.credentials?.currentAuthority()?.runtimeEpoch!==i.runtimeEpoch)return this.projections.set(e,blocked(s.revision)),blocked(s.revision);if(!a){this.activeCredentialRevisions.delete(e),this.resolvedNativeMaterial.delete(e),this.clearCredentialRenewal(e),await this.bridge.unset(e);const t=synced(s.revision);return this.projections.set(e,t),this.scheduleNativeConverge(),withOperationReceipt(t,s,void 0,i,n)}const o=n?releaseKey(n):void 0;if(o&&this.liveReleaseKeys.get(e)===o&&this.bridge.list().some(t=>t.name===e)){const t=synced(s.revision);return this.projections.set(e,t),withOperationReceipt(t,s,a,i,n)}const c=await this.reconcileServer(a,s,t,n,i,r);return"synced"===c.state&&o&&this.liveReleaseKeys.set(e,o),this.scheduleNativeConverge(),withOperationReceipt(c,s,a,i,n)})}async stop(){await this.exclusive(async()=>{this.started=!1,this.unsubscribe?.(),this.unsubscribe=void 0,this.activeCredentialRevisions.clear(),this.revoked.clear(),this.projections.clear(),this.credentialServers.clear(),this.liveReleaseKeys.clear(),this.resolvedNativeMaterial.clear();for(const e of[...this.credentialRenewalTimers.keys()])this.clearCredentialRenewal(e);this.authorityEpoch=null,await this.bridge.close()})}nudgeNativeProjection(){this.scheduleNativeConverge()}scheduleNativeConverge(){this.nativeProjection&&this.nativeProjection.scheduleConverge(()=>this.computeNativeTarget())}scheduleCredentialRenewal(e,t){if(this.clearCredentialRenewal(e),!t)return;const i=Date.parse(t);if(!Number.isFinite(i))return void this.logger?.warn?.(`[mcp-desired] ignoring invalid credential expiresAt name=${e}`);const r=Math.max(i-Date.now()-3e5,3e4),s=setTimeout(()=>{this.credentialRenewalTimers.delete(e),this.exclusive(async()=>{this.resolvedNativeMaterial.delete(e),this.scheduleNativeConverge()})},r);s.unref?.(),this.credentialRenewalTimers.set(e,s)}clearCredentialRenewal(e){const t=this.credentialRenewalTimers.get(e);t&&(clearTimeout(t),this.credentialRenewalTimers.delete(e))}async computeNativeTarget(){return await this.exclusive(async()=>{const e=await this.store.load(),t=new Map;for(const i of e.servers){if(!i.config)continue;if(!i.credentialRef){t.set(i.name,{config:i.config});continue}let e=this.resolvedNativeMaterial.get(i.name);if(!e&&this.credentials&&!this.revoked.has(i.credentialRef))try{const t=await this.credentials.resolve({credentialRef:i.credentialRef,serverName:i.name});e=t.credential,this.resolvedNativeMaterial.set(i.name,t.credential),this.scheduleCredentialRenewal(i.name,t.expiresAt)}catch(e){this.logger?.warn?.(`[mcp-desired] native credential resolve failed name=${i.name}: ${e instanceof Error?e.message:e}`)}e&&t.set(i.name,{config:i.config,credential:e})}return t})}acceptCredentialEvent(e){if("authority"===e.type){this.authorityEpoch=null;const t=++this.authorityGeneration;for(const e of this.credentialServers.keys())this.activeCredentialRevisions.delete(e),this.liveReleaseKeys.delete(e),this.projections.set(e,pending(this.desiredRevision));const i=this.bridge.detachMany(this.credentialServers.keys()).then(()=>({ok:!0}),e=>({ok:!1,error:e}));return void this.exclusive(()=>this.onAuthorityEvent(e.runtimeEpoch,t,i))}this.exclusive(()=>this.onCredentialEvent(e))}async onAuthorityEvent(e,t,i){const r=await i;if(!r.ok)throw this.authorityCloseBlocked=!0,r.error;const s=await this.store.load();if(this.rememberDesired(s),t!==this.authorityGeneration||null===e||this.authorityCloseBlocked)return;const a=this.credentials?.currentAuthority();a&&a.runtimeEpoch===e&&(this.authorityEpoch=e,await this.reconcileAll(void 0,s),this.scheduleNativeConverge())}async onCredentialEvent(e){const t=BigInt(e.credentialRevision),i=this.revoked.get(e.credentialRef)??-1n,r=await this.store.load();this.rememberDesired(r);const s=r.servers.filter(t=>t.credentialRef===e.credentialRef);if("revoked"!==e.type){if(!(t<=i)){this.revoked.delete(e.credentialRef);for(const e of s){if(t>(this.activeCredentialRevisions.get(e.name)??-1n)){if(this.activeCredentialRevisions.delete(e.name),this.liveReleaseKeys.delete(e.name),await this.bridge.unset(e.name),!this.releases){await this.reconcileServer(e,r);continue}this.projections.set(e.name,blocked(r.revision))}}this.scheduleNativeConverge()}}else{if(t<i)return;this.revoked.set(e.credentialRef,t);for(const e of s)this.activeCredentialRevisions.delete(e.name),this.liveReleaseKeys.delete(e.name),this.resolvedNativeMaterial.delete(e.name),this.clearCredentialRenewal(e.name),this.projections.set(e.name,blocked(r.revision)),await this.bridge.unset(e.name);this.scheduleNativeConverge()}}async reconcileAll(e,t){const i=t??await this.store.load();if(this.rememberDesired(i),!this.releases){const t=new Set(i.servers.map(e=>e.name));for(const e of this.bridge.list())t.has(e.name)||await this.bridge.unset(e.name);for(const t of i.servers)await this.reconcileServer(t,i,e);return}const r=await this.releases.listMcpActiveDesiredStates(),s=await this.releases.listMcpLiveReleases(),a=new Set(i.servers.map(e=>e.name));for(const e of this.bridge.list())a.has(e.name)||await this.bridge.unset(e.name);for(const t of i.servers){let a=r.find(e=>releaseMatchesDesired(e,i,t));if(!a){const e=s.find(e=>releaseMatchesDesired(e,i,t)),r=void 0===e?"pending":await this.recoverPendingServiceRelease(e,i,t);if("string"==typeof r){this.activeCredentialRevisions.delete(t.name),this.liveReleaseKeys.delete(t.name),await this.bridge.unset(t.name),this.projections.set(t.name,"pending"===r?pending(i.revision):blocked(i.revision));continue}a=r}if(t.credentialRef&&this.credentials?.currentAuthority()?.runtimeEpoch!==a.runtimeEpoch){this.activeCredentialRevisions.delete(t.name),this.liveReleaseKeys.delete(t.name),await this.bridge.unset(t.name),this.projections.set(t.name,blocked(i.revision));continue}const n=releaseKey(a);if(this.liveReleaseKeys.get(t.name)===n&&this.bridge.list().some(e=>e.name===t.name)){this.projections.set(t.name,synced(i.revision));continue}"synced"===(await this.reconcileServer(t,i,e,a)).state&&this.liveReleaseKeys.set(t.name,releaseKey(a))}}async recoverPendingServiceRelease(e,t,i){if("service"!==e.source)return"blocked";if(!this.releases||!releaseMatchesDesired(e,t,i))return"blocked";let r;try{r=await this.releases.get(e.operationId)}catch{return"pending"}if(!r||!releaseMatchesJournalRecord(e,r))return"blocked";const s=await this.readReceiptHistory(e.operationId);if("unavailable"===s.status)return"pending";if("not_found"===s.status||"expired"===s.status)return"blocked";if(!serviceHistoryMatchesRelease(e,r,s.snapshot))return"blocked";try{const r=(await this.releases.promoteMcpDesiredStateActive({operationId:e.operationId,source:"service",effectRecordHash:e.effectRecordHash,terminalMessageId:s.snapshot.final.message_id})).mcpActiveDesiredState;return r&&releaseMatchesDesired(r,t,i)?r:"blocked"}catch{return"pending"}}async readReceiptHistory(e){const t=this.receiptHistory;if(!t)return{status:"unavailable"};const i=async()=>{try{return await t.readOperationHistory({operationId:e})}catch{return{status:"unavailable"}}},r=await i();return"unavailable"!==r.status?r:(await Promise.resolve(),await i())}async reconcileServer(e,t,i,r,s,a){try{e.credentialRef&&this.bridge.list().some(t=>t.name===e.name)&&await this.bridge.detach(e.name),await this.connectServer(e,t,i,r,s,a);const n=synced(t.revision);return this.projections.set(e.name,n),n}catch(i){if(i instanceof McpCredentialRevokedError){this.revoked.set(e.credentialRef,this.revoked.get(e.credentialRef)??0n),this.activeCredentialRevisions.delete(e.name),await this.bridge.unset(e.name);const i=blocked(t.revision);return this.projections.set(e.name,i),i}this.logger?.warn?.(`[mcp-desired] projection failed name=${e.name}: ${i instanceof Error?i.message:i}`);const r=pending(t.revision);return this.projections.set(e.name,r),r}}async connectServer(e,t,i,r,s,a){if(i?.aborted)throw new Error("MCP projection attempt is no longer authoritative");if(a){if(!s||a.runtimeEpoch!==s.runtimeEpoch||r&&!releaseAuthorizesServer(r,e,t,a.runtimeEpoch,s))throw new Error("MCP operation-scoped activation is no longer authoritative");const n={generation:this.authorityGeneration,isCurrent:()=>!i?.aborted&&a.runtimeEpoch===s.runtimeEpoch};a.credential?this.resolvedNativeMaterial.set(e.name,a.credential):this.resolvedNativeMaterial.delete(e.name);const o=await this.bridge.set(e.name,a.config,a.credential,n);if(!n.isCurrent())throw this.resolvedNativeMaterial.delete(e.name),await this.bridge.detach(e.name),new Error("MCP operation-scoped activation lost authority during bridge connection");return o}if(r&&!releaseAuthorizesServer(r,e,t,this.credentials?.currentAuthority()?.runtimeEpoch,s))throw new Error("MCP live release proof no longer authorizes this server");if(!e.credentialRef){if(this.activeCredentialRevisions.delete(e.name),this.resolvedNativeMaterial.delete(e.name),!e.config)throw new Error("MCP desired state has no persisted public config for this server");const t=await this.bridge.set(e.name,e.config);if(i?.aborted)throw await this.bridge.detach(e.name),new Error("MCP projection attempt lost authority during bridge connection");return t}if(!this.credentials)throw new Error("runtime-scoped MCP credential resolver is unavailable");if(this.revoked.has(e.credentialRef))throw new McpCredentialRevokedError(e.credentialRef);const n=this.credentials.currentAuthority();if(!n||null===this.authorityEpoch||n.runtimeEpoch!==this.authorityEpoch)throw new Error("runtime-scoped MCP credential authority is pending");const o=this.authorityGeneration,c=this.authorityEpoch,d={generation:o,isCurrent:()=>o===this.authorityGeneration&&this.authorityEpoch===c&&this.credentials?.currentAuthority()?.runtimeEpoch===c};if(!d.isCurrent())throw new Error("runtime-scoped MCP credential authority changed");const h=await this.credentials.resolve({credentialRef:e.credentialRef,serverName:e.name,...i?{signal:i}:{}});if(i?.aborted||h.runtimeEpoch!==c||!d.isCurrent()||r&&!releaseAuthorizesServer(r,e,t,this.credentials?.currentAuthority()?.runtimeEpoch,s))throw new Error("runtime-scoped MCP credential authority changed during resolution");const l=BigInt(h.credentialRevision),u=this.revoked.get(e.credentialRef);if(void 0!==u&&l<=u)throw new McpCredentialRevokedError(e.credentialRef);if(!e.config)throw new Error("MCP desired state has no persisted public config for this server");const v=await this.bridge.set(e.name,e.config,h.credential,d);if(i?.aborted||!d.isCurrent()||r&&!releaseAuthorizesServer(r,e,t,this.credentials?.currentAuthority()?.runtimeEpoch,s))throw await this.bridge.detach(e.name),new Error("runtime-scoped MCP credential authority changed during bridge connection");return this.activeCredentialRevisions.set(e.name,l),this.resolvedNativeMaterial.set(e.name,h.credential),this.scheduleCredentialRenewal(e.name,h.expiresAt),v}rememberDesired(e){this.desiredRevision=e.revision,this.credentialServers.clear();for(const t of e.servers)t.credentialRef&&this.credentialServers.set(t.name,t.credentialRef)}async recordDesiredStateCommit(e,t,i){if(!i)return;if(!this.releases)throw new Error("MCP desired state requires the shared command journal release authority");const r=e.servers.find(e=>e.name===t);await this.releases.recordMcpDesiredStateCommit({schemaVersion:1,...i,desiredStateRevision:e.revision,desiredStateHash:e.sha256,server:r?{name:r.name,definitionRef:r.definitionRef,version:r.version,definitionSha256:r.sha256,...r.credentialRef?{credentialRef:r.credentialRef}:{}}:{name:t,removed:!0}})}async findAttemptActive(e,t,i){const r=(await(this.releases?.listMcpActiveDesiredStates())??[]).find(e=>e.operationId===i.operationId&&e.method===i.method&&e.source===i.source);if(r&&releaseMatchesAttempt(r,e,t,i))return r}async collectLocalGarbage(e,t=!1){if(!this.references)return;if(!e)return void await this.store.transaction(async e=>this.collectLocalGarbage(e,t));const i=await this.references.listOwned(),r=t?await(this.references.listPendingOwned?.())??[]:[];for(const t of[...new Set([...i,...r])]){const i=(await e.load()).servers.some(e=>e.credentialRef===t);await e.assertOwned(),i?r.includes(t)&&await this.references.commit(t).catch(()=>{}):await this.references.remove(t).catch(()=>{})}}async exclusive(e){const t=this.tail.catch(()=>{}).then(e);return this.tail=t.catch(()=>{}),await t}}function pending(e){return Object.freeze({revision:e,state:"pending"})}function synced(e){return Object.freeze({revision:e,state:"synced"})}function blocked(e){return Object.freeze({revision:e,state:"blocked"})}function matchesProjectionAttempt(e,t,i){return i.expected.name===e&&(i.expected.absent?void 0===t:void 0!==t&&t.name===i.expected.name&&t.definitionRef===i.expected.definitionRef&&t.version===i.expected.version&&t.sha256===i.expected.sha256&&t.credentialRef===i.expected.credentialRef)}function withOperationReceipt(e,t,i,r,s){if(!r||!s||"synced"!==e.state)return e;const a=Object.freeze({schemaVersion:1,operationId:r.operationId,method:r.method,runtimeEpoch:r.runtimeEpoch,desiredStateRevision:t.revision,desiredStateHash:t.sha256,server:Object.freeze(i?{name:i.name,definitionRef:i.definitionRef,definitionSha256:i.sha256}:{name:r.expected.name,removed:!0})});return Object.freeze({...e,mcpReceipt:a})}function releaseMatchesDesired(e,t,i){return"mcp.set"===e.method&&e.desiredStateRevision===t.revision&&e.desiredStateHash===t.sha256&&e.server.name===i.name&&e.server.definitionRef===i.definitionRef&&e.server.version===i.version&&e.server.definitionSha256===i.sha256&&e.server.credentialRef===i.credentialRef}function releaseMatchesJournalRecord(e,t){const i=t.mcpDesiredStateCommit,r=t.mcpLiveRelease;return t.operationId===e.operationId&&t.command===e.method&&t.paramsHash===e.paramsHash&&t.runtimeEpoch===e.runtimeEpoch&&("effect_committed"===t.phase||"projection_committed"===t.phase)&&void 0!==t.messageBinding&&void 0!==i&&void 0!==r&&"service"===i.source&&sameMcpRelease(r,e)&&r.effectRecordHash===e.effectRecordHash&&sameMcpRelease(i,e)}function serviceHistoryMatchesRelease(e,t,i){const r=t.messageBinding;if(!r||!e.effectRecordHash)return!1;const s=i.request,a=s.payload,n=i.effectCommitted,o=n?.payload;if(i.operationId!==e.operationId||i.requestMessageId!==r.requestMessageId||i.requestHash!==r.requestHash||s.type!==RUNTIME_MESSAGE_TYPES.request||s.channel_id!==e.operationId||s.message_id!==r.requestMessageId||a.operationId!==e.operationId||a.requestMessageId!==r.requestMessageId||a.requestHash!==r.requestHash||a.paramsHash!==e.paramsHash||("mcp.set"===e.method?"mcp/set"!==a.method:"mcp/unset"!==a.method)||a.command.name!==e.method||a.dispatchEpoch!==e.runtimeEpoch||!n||!o||n.type!==RUNTIME_MESSAGE_TYPES.effectCommitted||n.channel_id!==e.operationId||n.in_reply_to!==r.requestMessageId||n.publisher_id!==a.instanceId||o.operationId!==e.operationId||o.requestMessageId!==r.requestMessageId||o.requestHash!==r.requestHash||o.dispatchEpoch!==e.runtimeEpoch||o.method!==a.method||o.effectRecordHash!==e.effectRecordHash||o.effectRecord.operationId!==e.operationId||o.effectRecord.method!==a.method)return!1;if(i.cancelRequested)return!1;if(!i.terminal)return!1;const c=i.final;return"final_message"===i.terminalSource&&"succeeded"===i.status&&void 0!==c&&c.type===RUNTIME_MESSAGE_TYPES.final&&c.channel_id===e.operationId&&c.in_reply_to===r.requestMessageId&&c.publisher_id===a.instanceId&&c.payload.operationId===e.operationId&&c.payload.requestMessageId===r.requestMessageId&&c.payload.requestHash===r.requestHash&&c.payload.dispatchEpoch===e.runtimeEpoch&&c.payload.method===a.method&&"succeeded"===c.payload.outcome&&c.payload.effectRecordHash===e.effectRecordHash}function sameMcpRelease(e,t){return e.source===t.source&&e.operationId===t.operationId&&e.method===t.method&&e.paramsHash===t.paramsHash&&e.runtimeEpoch===t.runtimeEpoch&&e.desiredStateRevision===t.desiredStateRevision&&e.desiredStateHash===t.desiredStateHash&&e.server.name===t.server.name&&e.server.definitionRef===t.server.definitionRef&&e.server.version===t.server.version&&e.server.definitionSha256===t.server.definitionSha256&&e.server.credentialRef===t.server.credentialRef&&e.server.removed===t.server.removed}function releaseMatchesAttempt(e,t,i,r){return!(e.operationId!==r.operationId||e.method!==r.method||e.source!==r.source||e.paramsHash!==r.paramsHash||e.runtimeEpoch!==r.runtimeEpoch||e.desiredStateRevision!==t.revision||e.desiredStateHash!==t.sha256||"service"===r.source&&!e.effectRecordHash||"acp_local"===r.source&&void 0!==e.effectRecordHash)&&(r.expected.absent?void 0===i&&e.server.name===r.expected.name&&!0===e.server.removed:void 0!==i&&releaseMatchesDesired(e,t,i)&&i.definitionRef===r.expected.definitionRef&&i.version===r.expected.version&&i.sha256===r.expected.sha256&&i.credentialRef===r.expected.credentialRef)}function releaseAuthorizesServer(e,t,i,r,s){return"mcp.set"===e.method&&(!t.credentialRef||e.runtimeEpoch===r)&&releaseMatchesDesired(e,i,t)&&(!s||releaseMatchesAttempt(e,i,t,s))}function releaseKey(e){return[e.source,e.operationId,e.paramsHash,e.runtimeEpoch,e.desiredStateRevision,e.desiredStateHash,e.effectRecordHash??"local"].join("\0")}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "beeos-claw",
3
- "version": "0.6.37",
3
+ "version": "0.6.38",
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>",