beeos-claw 0.5.69 → 0.5.70

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{createHash,createPrivateKey,randomUUID}from"node:crypto";import{join}from"node:path";import{CONVERSATION_RUNTIME_CANONICAL_COMMAND_NAMES,RUNTIME_METHOD_PARITY_MANIFEST,RUNTIME_RESOURCE_BINDING_CONTRACT_REVISION,RUNTIME_RPC_CONTRACT_REVISION}from"@beeos-ai/beeos-types/runtime";import{agentBoundFetch}from"../agent-auth.js";import{FileRuntimeCommandJournal}from"./runtime-command-journal.js";import{FileRuntimeAgentIdentityRegistry}from"./runtime-agent-identity-registry.js";import{RuntimeRegistrationIdentityStore}from"./runtime-registration-identity-store.js";import{InstanceAuthorityRuntimeRegistrationTransport,RuntimeRegistrationCoordinator}from"./runtime-registration.js";import{verifiedRuntimeParityManifestDigest}from"./runtime-parity-manifest-digest.js";import{intersectRuntimeServiceMethodsWithBackendCapabilities}from"./runtime-service-contract-compatibility.js";export class RuntimeProductionAuthority{options;journal;identities;identityStore;handlerIdentity;listeners=new Set;coordinator;unsubscribeCoordinator;starting;rolling=new Map;refreshing;stoppedCoordinators=new WeakSet;coordinatorGeneration=0;lineage;identity;preparing;runtimeConsumerLive=!1;runtimeServiceMethods=Object.freeze([]);realtimeCapabilityReady=new Map;resourceBindingContractRevision;manifestDigest=verifiedRuntimeParityManifestDigest();started=!1;lifecycleVersion=0;boundAgentFetch;constructor(t){this.options=t,this.handlerIdentity=t.handlerIdentity??randomUUID(),this.journal=new FileRuntimeCommandJournal(join(t.stateDirectory,"runtime-command-journal.json")),this.identities=new FileRuntimeAgentIdentityRegistry(join(t.stateDirectory,"runtime-agent-identities.json")),this.boundAgentFetch=t.boundAgentFetch??((i,e)=>agentBoundFetch(i,t.keys,e)),this.identityStore=new RuntimeRegistrationIdentityStore(join(t.stateDirectory,"runtime-registration-identity.json"))}get agentKeys(){return this.options.keys}get stateDirectory(){return this.options.stateDirectory}get authorityLease(){return this.coordinator?.authorityLease??null}subscribeAuthority(t){return this.listeners.add(t),()=>{this.listeners.delete(t)}}async prepareConsumerBinding(){return this.preparing||(this.preparing=(async()=>{const t=await this.identityStore.acquire(this.handlerIdentity,this.journal),i={journalStoreId:t.journalStoreId,journalGeneration:t.journalGeneration};return this.lineage=i,this.identity=t,this.consumerBinding(t,i)})().finally(()=>{this.preparing=void 0})),await this.preparing}setRuntimeConsumerLive(t){if(this.started)throw new Error("runtime consumer availability is immutable while registered");this.runtimeConsumerLive=t}setRuntimeServiceMethods(t){if(this.started)throw new Error("runtime Service methods are immutable while registered");this.runtimeServiceMethods=intersectRuntimeServiceMethodsWithBackendCapabilities(t)}setRuntimeRealtimeCapabilityReady(t,i){const e=this.realtimeCapabilityReady.get(t)!==i;if(this.realtimeCapabilityReady.set(t,i),!e||!this.started)return;const r=this.lifecycleVersion;this.refreshRealtimeCapabilities(r).catch(t=>this.options.logger?.warn?.(`[runtime-authority] realtime capability refresh fenced: ${t instanceof Error?t.message:t}`))}setRuntimeResourceBindingContractRevision(t){if(this.started)throw new Error("runtime resource binding revision is immutable while registered");if(void 0!==t&&t!==RUNTIME_RESOURCE_BINDING_CONTRACT_REVISION)throw new Error("unsupported runtime resource binding revision");this.resourceBindingContractRevision=t}async start(t){if(this.starting)return await this.starting;if(this.started)return;if(t?.aborted)return;const i=this.lifecycleVersion,e=this.startOnce(i,t).finally(()=>{this.starting===e&&(this.starting=void 0)});this.starting=e,await e}stop(){this.lifecycleVersion++,this.starting=void 0,this.started=!1;const t=this.coordinator;t&&this.detachCoordinator(t),this.publish(null)}async startOnce(t,i){if(await this.prepareConsumerBinding(),t!==this.lifecycleVersion||i?.aborted)return;this.lineage;const e=this.identity;this.started=!0,await this.activate(e,t)}async activate(t,i){if(!this.isLifecycleCurrent(i))return;if(this.resourceBindingContractRevision&&!this.runtimeConsumerLive)throw new Error("resource binding revision requires a live Message Service consumer");if(!this.runtimeConsumerLive||0===this.runtimeServiceMethods.length)throw new Error(`runtime registration refused: consumerLive=${this.runtimeConsumerLive} methods=${this.runtimeServiceMethods.length}`);const e=[...this.runtimeServiceMethods],r=runtimeCapabilities(this.runtimeServiceMethods,this.realtimeCapabilityReady);this.options.logger?.info?.(`[runtime-authority] advertising ${e.length} methods (renew-safe)`);const n=++this.coordinatorGeneration,o=new RuntimeRegistrationCoordinator(new InstanceAuthorityRuntimeRegistrationTransport({agentGatewayUrl:this.options.agentGatewayUrl,registrationId:t.registrationId,agentFetch:this.options.agentFetch}),{registrationId:t.registrationId,handlerIdentity:t.handlerIdentity,contractRevision:RUNTIME_RPC_CONTRACT_REVISION,runtimeMethods:e,capabilities:r,manifestDigest:this.manifestDigest,journalStoreId:t.journalStoreId,journalGeneration:t.journalGeneration,instanceIdentityKeyId:createHash("sha256").update(this.options.keys.publicKey).digest("hex"),privateKey:createPrivateKey({key:privateKeyDer(this.options.keys.privateKey),format:"der",type:"pkcs8"})},(t,e)=>{if(!this.isCoordinatorCurrent(i,n,o))return;const r=e instanceof Error?e.message:void 0!==e?String(e):"";this.options.logger?.warn?.(r?`[runtime-authority] lease lost: ${t}: ${r}`:`[runtime-authority] lease lost: ${t}`),"expired"===t&&this.rollExpired(i,n,o).catch(t=>this.options.logger?.warn?.(`[runtime-authority] expired registration roll fenced: ${t instanceof Error?t.message:t}`))});this.coordinator=o,this.unsubscribeCoordinator=o.subscribeAuthority(t=>{this.isCoordinatorCurrent(i,n,o)&&this.publish(t)});const s=await o.start();this.isCoordinatorCurrent(i,n,o)?this.options.logger?.info?.("[runtime-authority] registration "+(s?"active":"pending")):this.stopCoordinatorOnce(o)}async rollExpired(t,i,e){if(!this.lineage||!this.isCoordinatorCurrent(t,i,e))return;const r=this.rolling.get(i);if(r)return await r;const n=(async()=>{if(!this.isCoordinatorCurrent(t,i,e))return;this.detachCoordinator(e);const r=this.identity;if(!r)return;const n=await this.identityStore.roll(r,this.journal);this.isLifecycleCurrent(t)&&void 0===this.coordinator&&(this.identity=n,this.lineage={journalStoreId:n.journalStoreId,journalGeneration:n.journalGeneration},await this.activate(n,t))})().finally(()=>{this.rolling.get(i)===n&&this.rolling.delete(i)});this.rolling.set(i,n),await n}async refreshRealtimeCapabilities(t){if(!this.isLifecycleCurrent(t))return;const i=this.refreshing;if(i)return await i;const e=(async()=>{if(!this.isLifecycleCurrent(t))return;const i=this.identity,e=this.coordinator;i&&e&&(this.detachCoordinator(e),this.isLifecycleCurrent(t)&&void 0===this.coordinator&&await this.activate(i,t))})().finally(()=>{this.refreshing===e&&(this.refreshing=void 0)});this.refreshing=e,await e}isLifecycleCurrent(t){return this.started&&t===this.lifecycleVersion}isCoordinatorCurrent(t,i,e){return this.isLifecycleCurrent(t)&&this.coordinatorGeneration===i&&this.coordinator===e}detachCoordinator(t){this.coordinator===t&&(this.unsubscribeCoordinator?.(),this.unsubscribeCoordinator=void 0,this.coordinator=void 0),this.stopCoordinatorOnce(t)}stopCoordinatorOnce(t){this.stoppedCoordinators.has(t)||(this.stoppedCoordinators.add(t),t.stop())}publish(t){for(const i of this.listeners)try{i(t)}catch{}}consumerBinding(t,i){return Object.freeze({handlerIdentity:t.handlerIdentity,journalStoreId:i.journalStoreId,journalGeneration:i.journalGeneration})}}const ED25519_PKCS8_PREFIX=Buffer.from("302e020100300506032b657004220420","hex"),privateKeyDer=t=>32===t.length?Buffer.concat([ED25519_PKCS8_PREFIX,Buffer.from(t)]):Buffer.from(t),REALTIME_CAPABILITY_NAMES=["terminal","canvas"];function runtimeCapabilities(t,i){const e=new Set(t),r=RUNTIME_METHOD_PARITY_MANIFEST.filter(t=>t.serviceAllowed&&void 0!==runtimeInvocationMethod(t)&&e.has(runtimeInvocationMethod(t))).map(t=>t.capability),n=REALTIME_CAPABILITY_NAMES.filter(t=>!0===i.get(t));return[...new Set([...r,...n])]}function runtimeInvocationMethod(t){return"methods"===t.serviceRoute&&t.serviceMethod?t.serviceMethod:Object.entries(CONVERSATION_RUNTIME_CANONICAL_COMMAND_NAMES).find(([,i])=>i===t.canonicalCommand)?.[0]}
1
+ import{createHash,createPrivateKey,randomUUID}from"node:crypto";import{join}from"node:path";import{CONVERSATION_RUNTIME_CANONICAL_COMMAND_NAMES,RUNTIME_METHOD_PARITY_MANIFEST,RUNTIME_RESOURCE_BINDING_CONTRACT_REVISION,RUNTIME_RPC_CONTRACT_REVISION}from"@beeos-ai/beeos-types/runtime";import{agentBoundFetch}from"../agent-auth.js";import{FileRuntimeCommandJournal}from"./runtime-command-journal.js";import{FileRuntimeAgentIdentityRegistry}from"./runtime-agent-identity-registry.js";import{RuntimeRegistrationIdentityStore}from"./runtime-registration-identity-store.js";import{InstanceAuthorityRuntimeRegistrationTransport,RuntimeRegistrationCoordinator}from"./runtime-registration.js";import{verifiedRuntimeParityManifestDigest}from"./runtime-parity-manifest-digest.js";import{intersectRuntimeServiceMethodsWithBackendCapabilities}from"./runtime-service-contract-compatibility.js";export class RuntimeProductionAuthority{options;journal;identities;identityStore;handlerIdentity;listeners=new Set;coordinator;unsubscribeCoordinator;starting;rolling=new Map;refreshing;stoppedCoordinators=new WeakSet;coordinatorGeneration=0;lineage;identity;preparing;runtimeConsumerLive=!1;runtimeServiceMethods=Object.freeze([]);realtimeCapabilityReady=new Map;resourceBindingContractRevision;manifestDigest=verifiedRuntimeParityManifestDigest();started=!1;lifecycleVersion=0;boundAgentFetch;constructor(t){this.options=t,this.handlerIdentity=t.handlerIdentity??(process.env.HOSTNAME?.trim()||process.env.POD_NAME?.trim()||randomUUID()),this.journal=new FileRuntimeCommandJournal(join(t.stateDirectory,"runtime-command-journal.json")),this.identities=new FileRuntimeAgentIdentityRegistry(join(t.stateDirectory,"runtime-agent-identities.json")),this.boundAgentFetch=t.boundAgentFetch??((i,e)=>agentBoundFetch(i,t.keys,e)),this.identityStore=new RuntimeRegistrationIdentityStore(join(t.stateDirectory,"runtime-registration-identity.json"))}get agentKeys(){return this.options.keys}get stateDirectory(){return this.options.stateDirectory}get authorityLease(){return this.coordinator?.authorityLease??null}subscribeAuthority(t){return this.listeners.add(t),()=>{this.listeners.delete(t)}}async prepareConsumerBinding(){return this.preparing||(this.preparing=(async()=>{const t=await this.identityStore.acquire(this.handlerIdentity,this.journal),i={journalStoreId:t.journalStoreId,journalGeneration:t.journalGeneration};return this.lineage=i,this.identity=t,this.consumerBinding(t,i)})().finally(()=>{this.preparing=void 0})),await this.preparing}setRuntimeConsumerLive(t){if(this.started)throw new Error("runtime consumer availability is immutable while registered");this.runtimeConsumerLive=t}setRuntimeServiceMethods(t){if(this.started)throw new Error("runtime Service methods are immutable while registered");this.runtimeServiceMethods=intersectRuntimeServiceMethodsWithBackendCapabilities(t)}setRuntimeRealtimeCapabilityReady(t,i){const e=this.realtimeCapabilityReady.get(t)!==i;if(this.realtimeCapabilityReady.set(t,i),!e||!this.started)return;const r=this.lifecycleVersion;this.refreshRealtimeCapabilities(r).catch(t=>this.options.logger?.warn?.(`[runtime-authority] realtime capability refresh fenced: ${t instanceof Error?t.message:t}`))}setRuntimeResourceBindingContractRevision(t){if(this.started)throw new Error("runtime resource binding revision is immutable while registered");if(void 0!==t&&t!==RUNTIME_RESOURCE_BINDING_CONTRACT_REVISION)throw new Error("unsupported runtime resource binding revision");this.resourceBindingContractRevision=t}async start(t){if(this.starting)return await this.starting;if(this.started)return;if(t?.aborted)return;const i=this.lifecycleVersion,e=this.startOnce(i,t).finally(()=>{this.starting===e&&(this.starting=void 0)});this.starting=e,await e}stop(){this.lifecycleVersion++,this.starting=void 0,this.started=!1;const t=this.coordinator;t&&this.detachCoordinator(t),this.publish(null)}async startOnce(t,i){if(await this.prepareConsumerBinding(),t!==this.lifecycleVersion||i?.aborted)return;this.lineage;const e=this.identity;this.started=!0,await this.activate(e,t)}async activate(t,i){if(!this.isLifecycleCurrent(i))return;if(this.resourceBindingContractRevision&&!this.runtimeConsumerLive)throw new Error("resource binding revision requires a live Message Service consumer");if(!this.runtimeConsumerLive||0===this.runtimeServiceMethods.length)throw new Error(`runtime registration refused: consumerLive=${this.runtimeConsumerLive} methods=${this.runtimeServiceMethods.length}`);const e=[...this.runtimeServiceMethods],r=runtimeCapabilities(this.runtimeServiceMethods,this.realtimeCapabilityReady);this.options.logger?.info?.(`[runtime-authority] advertising ${e.length} methods (renew-safe)`);const n=++this.coordinatorGeneration,o=new RuntimeRegistrationCoordinator(new InstanceAuthorityRuntimeRegistrationTransport({agentGatewayUrl:this.options.agentGatewayUrl,registrationId:t.registrationId,agentFetch:this.options.agentFetch}),{registrationId:t.registrationId,handlerIdentity:t.handlerIdentity,contractRevision:RUNTIME_RPC_CONTRACT_REVISION,runtimeMethods:e,capabilities:r,manifestDigest:this.manifestDigest,journalStoreId:t.journalStoreId,journalGeneration:t.journalGeneration,instanceIdentityKeyId:createHash("sha256").update(this.options.keys.publicKey).digest("hex"),privateKey:createPrivateKey({key:privateKeyDer(this.options.keys.privateKey),format:"der",type:"pkcs8"})},(t,e)=>{if(!this.isCoordinatorCurrent(i,n,o))return;const r=e instanceof Error?e.message:void 0!==e?String(e):"";this.options.logger?.warn?.(r?`[runtime-authority] lease lost: ${t}: ${r}`:`[runtime-authority] lease lost: ${t}`),"expired"===t&&this.rollExpired(i,n,o).catch(t=>this.options.logger?.warn?.(`[runtime-authority] expired registration roll fenced: ${t instanceof Error?t.message:t}`))});this.coordinator=o,this.unsubscribeCoordinator=o.subscribeAuthority(t=>{this.isCoordinatorCurrent(i,n,o)&&this.publish(t)});const s=await o.start();this.isCoordinatorCurrent(i,n,o)?this.options.logger?.info?.("[runtime-authority] registration "+(s?"active":"pending")):this.stopCoordinatorOnce(o)}async rollExpired(t,i,e){if(!this.lineage||!this.isCoordinatorCurrent(t,i,e))return;const r=this.rolling.get(i);if(r)return await r;const n=(async()=>{if(!this.isCoordinatorCurrent(t,i,e))return;this.detachCoordinator(e);const r=this.identity;if(!r)return;const n=await this.identityStore.roll(r,this.journal);this.isLifecycleCurrent(t)&&void 0===this.coordinator&&(this.identity=n,this.lineage={journalStoreId:n.journalStoreId,journalGeneration:n.journalGeneration},await this.activate(n,t))})().finally(()=>{this.rolling.get(i)===n&&this.rolling.delete(i)});this.rolling.set(i,n),await n}async refreshRealtimeCapabilities(t){if(!this.isLifecycleCurrent(t))return;const i=this.refreshing;if(i)return await i;const e=(async()=>{if(!this.isLifecycleCurrent(t))return;const i=this.identity,e=this.coordinator;i&&e&&(e.authorityLease?(this.detachCoordinator(e),this.isLifecycleCurrent(t)&&void 0===this.coordinator&&await this.activate(i,t)):this.options.logger?.info?.("[runtime-authority] deferring realtime capability refresh until registration is active"))})().finally(()=>{this.refreshing===e&&(this.refreshing=void 0)});this.refreshing=e,await e}isLifecycleCurrent(t){return this.started&&t===this.lifecycleVersion}isCoordinatorCurrent(t,i,e){return this.isLifecycleCurrent(t)&&this.coordinatorGeneration===i&&this.coordinator===e}detachCoordinator(t){this.coordinator===t&&(this.unsubscribeCoordinator?.(),this.unsubscribeCoordinator=void 0,this.coordinator=void 0),this.stopCoordinatorOnce(t)}stopCoordinatorOnce(t){this.stoppedCoordinators.has(t)||(this.stoppedCoordinators.add(t),t.stop())}publish(t){for(const i of this.listeners)try{i(t)}catch{}}consumerBinding(t,i){return Object.freeze({handlerIdentity:t.handlerIdentity,journalStoreId:i.journalStoreId,journalGeneration:i.journalGeneration})}}const ED25519_PKCS8_PREFIX=Buffer.from("302e020100300506032b657004220420","hex"),privateKeyDer=t=>32===t.length?Buffer.concat([ED25519_PKCS8_PREFIX,Buffer.from(t)]):Buffer.from(t),REALTIME_CAPABILITY_NAMES=["terminal","canvas"];function runtimeCapabilities(t,i){const e=new Set(t),r=RUNTIME_METHOD_PARITY_MANIFEST.filter(t=>t.serviceAllowed&&void 0!==runtimeInvocationMethod(t)&&e.has(runtimeInvocationMethod(t))).map(t=>t.capability),n=REALTIME_CAPABILITY_NAMES.filter(t=>!0===i.get(t));return[...new Set([...r,...n])]}function runtimeInvocationMethod(t){return"methods"===t.serviceRoute&&t.serviceMethod?t.serviceMethod:Object.entries(CONVERSATION_RUNTIME_CANONICAL_COMMAND_NAMES).find(([,i])=>i===t.canonicalCommand)?.[0]}
@@ -1 +1 @@
1
- import{createHash,randomBytes,sign}from"node:crypto";import{RUNTIME_INTERNAL_API_V1_ROUTE_CONTRACTS,RUNTIME_HASH_DOMAINS,RUNTIME_RPC_PROTOCOL_VERSION,runtimeHashPreimage,runtimeIdentitySignatureMessage,validateRuntimeInternalRegistrationResult}from"@beeos-ai/beeos-types/runtime";export class RuntimeRegistrationLeaseExpiredError extends Error{constructor(){super("Instance authority returned an expired active lease"),this.name="RuntimeRegistrationLeaseExpiredError"}}export class RuntimeRegistrationCoordinator{transport;config;onLeaseLost;now;active=null;timer;expiryTimer;stopped=!1;generation=0;authorityListeners=new Set;constructor(t,e,i,s=Date.now){this.transport=t,this.config=e,this.onLeaseLost=i,this.now=s}get lease(){return!this.active||Date.parse(this.active.leaseExpiresAt)<=this.now()?null:this.active}get canClaimMutations(){return null!==this.lease}get authorityLease(){const t=this.lease;return t?Object.freeze({...t,handlerIdentity:this.config.handlerIdentity}):null}subscribeAuthority(t){return this.authorityListeners.add(t),()=>{this.authorityListeners.delete(t)}}async start(){const t=++this.generation;return this.stopped=!1,await this.registerOnce(t)}async registerOnce(t){if(!this.isCurrent(t))return null;this.timer=void 0,this.active&&(this.clearLeaseExpiry(),this.active=null,this.notifyAuthority());const e={registrationId:this.config.registrationId,handlerIdentity:this.config.handlerIdentity,contractRevision:this.config.contractRevision,runtimeRpcProtocolVersion:RUNTIME_RPC_PROTOCOL_VERSION,runtimeMethods:this.config.runtimeMethods,capabilities:this.config.capabilities,manifestDigest:this.config.manifestDigest,journalStoreId:this.config.journalStoreId,journalGeneration:this.config.journalGeneration};let i;try{i=await this.transport.register(this.signProof("runtime.register",e))}catch(e){return this.isCurrent(t)?(this.onLeaseLost(e instanceof RuntimeRegistrationLeaseExpiredError?"expired":"heartbeat_failed",e),e instanceof RuntimeRegistrationLeaseExpiredError||this.scheduleRegistration(5e3,t),null):null}return this.isCurrent(t)?"active"!==i.status?("fenced"===i.status?this.onLeaseLost("fenced"):this.scheduleRegistration(i.retryAfterMs,t),null):(this.active={instanceId:i.instanceId,leaseId:i.leaseId,runtimeEpoch:i.runtimeEpoch,runtimeLeaseCredential:i.runtimeLeaseCredential,leaseExpiresAt:i.leaseExpiresAt},this.notifyAuthority(),this.scheduleLeaseExpiry(this.active,t),this.schedule(i.heartbeatIntervalMs,t),this.active):null}stop(){this.generation++,this.stopped=!0,this.timer&&clearTimeout(this.timer),this.expiryTimer&&clearTimeout(this.expiryTimer),this.timer=void 0,this.expiryTimer=void 0,this.active=null,this.notifyAuthority()}schedule(t,e){if(!this.isCurrent(e))return;const i=this.active;i&&(this.timer=setTimeout(()=>{this.sendHeartbeat(t,e,i)},t),this.timer.unref?.())}scheduleRegistration(t,e){this.isCurrent(e)&&(this.timer=setTimeout(()=>{this.registerOnce(e)},t),this.timer.unref?.())}async sendHeartbeat(t,e,i){if(!this.isCurrentLease(e,i))return;const s={leaseId:i.leaseId,handlerIdentity:this.config.handlerIdentity,runtimeEpoch:i.runtimeEpoch};try{const r=await this.transport.heartbeat(this.signProof("runtime.heartbeat",s));if(!this.isCurrentLease(e,i))return;if("renewed"!==r.status)return this.active=null,this.clearLeaseExpiry(),this.notifyAuthority(),void this.onLeaseLost(r.status);this.active={...i,runtimeLeaseCredential:r.runtimeLeaseCredential,leaseExpiresAt:r.leaseExpiresAt},this.notifyAuthority(),this.scheduleLeaseExpiry(this.active,e),this.schedule(t,e)}catch(t){if(!this.isCurrentLease(e,i))return;this.active=null,this.clearLeaseExpiry(),this.notifyAuthority(),this.onLeaseLost("heartbeat_failed",t),this.scheduleRegistration(5e3,e)}}scheduleLeaseExpiry(t,e){this.clearLeaseExpiry();const i=`${t.leaseId}\0${t.runtimeEpoch}\0${t.leaseExpiresAt}`,s=()=>{const r=Date.parse(t.leaseExpiresAt)-this.now();if(r>0)return this.expiryTimer=setTimeout(s,Math.min(2147483647,r)),void this.expiryTimer.unref?.();const n=this.active;this.isCurrentLease(e,t)&&n&&`${n.leaseId}\0${n.runtimeEpoch}\0${n.leaseExpiresAt}`===i&&(this.active=null,this.expiryTimer=void 0,this.notifyAuthority(),this.onLeaseLost("expired"))};s()}clearLeaseExpiry(){this.expiryTimer&&clearTimeout(this.expiryTimer),this.expiryTimer=void 0}isCurrent(t){return!this.stopped&&t===this.generation}isCurrentLease(t,e){return this.isCurrent(t)&&this.active===e}notifyAuthority(){const t=this.authorityLease;for(const e of this.authorityListeners)try{e(t)}catch{}}signProof(t,e){const i=new Date(this.now()).toISOString(),s=randomBytes(18).toString("base64url"),r="runtime.register"===t?RUNTIME_HASH_DOMAINS.registration:RUNTIME_HASH_DOMAINS.heartbeat,n=createHash("sha256").update(runtimeHashPreimage(r,e)).digest("hex"),a=runtimeIdentitySignatureMessage({purpose:t,instanceIdentityKeyId:this.config.instanceIdentityKeyId,signedAt:i,nonce:s,payloadHash:n});return{...e,signaturePurpose:t,instanceIdentityKeyId:this.config.instanceIdentityKeyId,signedAt:i,nonce:s,payloadHash:n,signature:sign(null,a,this.config.privateKey).toString("base64url")}}}export class InstanceAuthorityRuntimeRegistrationTransport{options;constructor(t){this.options=t}async register(t){if(t.registrationId!==this.options.registrationId)throw new Error("runtime registration id mismatch");const e=await this.post(RUNTIME_INTERNAL_API_V1_ROUTE_CONTRACTS.runtimeRegister.path,t);if("active"===e.status&&Number.isFinite(Date.parse(e.leaseExpiresAt))&&Date.parse(e.leaseExpiresAt)<=Date.now())throw new RuntimeRegistrationLeaseExpiredError;if(validateRuntimeInternalRegistrationResult(e)||"fenced"!==e.status&&(e.journalStoreId!==t.journalStoreId||e.journalGeneration!==t.journalGeneration)||"active"===e.status&&(!e.instanceId||!e.leaseId||!e.runtimeLeaseCredential||!Number.isInteger(e.heartbeatIntervalMs)||!Number.isFinite(Date.parse(e.leaseExpiresAt))||Date.parse(e.leaseExpiresAt)<=Date.now()))throw new Error("Instance authority returned an invalid registration binding");return e}async heartbeat(t){const e=await this.post(RUNTIME_INTERNAL_API_V1_ROUTE_CONTRACTS.runtimeHeartbeat.path.replace("{registrationId}",encodeURIComponent(this.options.registrationId)),t);if(!(e&&["renewed","fenced","expired"].includes(e.status)&&("renewed"!==e.status||e.runtimeLeaseCredential&&Number.isFinite(Date.parse(e.leaseExpiresAt)))))throw new Error("Instance authority returned an invalid heartbeat result");return e}async post(t,e){const i=new URL(t,this.options.agentGatewayUrl),s=await this.options.agentFetch(i,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(e)});if(!s.ok)throw new Error(`runtime Instance authority request failed (${s.status} ${s.statusText}) ${i.pathname}`);return await s.json()}}
1
+ import{createHash,randomBytes,sign}from"node:crypto";import{RUNTIME_INTERNAL_API_V1_ROUTE_CONTRACTS,RUNTIME_HASH_DOMAINS,RUNTIME_RPC_PROTOCOL_VERSION,runtimeHashPreimage,runtimeIdentitySignatureMessage,validateRuntimeInternalRegistrationResult}from"@beeos-ai/beeos-types/runtime";export class RuntimeRegistrationLeaseExpiredError extends Error{constructor(){super("Instance authority returned an expired active lease"),this.name="RuntimeRegistrationLeaseExpiredError"}}export class RuntimeRegistrationHandoffRequiredError extends Error{retryAfterMs;constructor(t=65e3){super("Instance authority requires waiting for the active runtime lease to expire"),this.name="RuntimeRegistrationHandoffRequiredError",this.retryAfterMs=t}}export class RuntimeRegistrationCoordinator{transport;config;onLeaseLost;now;active=null;timer;expiryTimer;stopped=!1;generation=0;authorityListeners=new Set;constructor(t,e,i,r=Date.now){this.transport=t,this.config=e,this.onLeaseLost=i,this.now=r}get lease(){return!this.active||Date.parse(this.active.leaseExpiresAt)<=this.now()?null:this.active}get canClaimMutations(){return null!==this.lease}get authorityLease(){const t=this.lease;return t?Object.freeze({...t,handlerIdentity:this.config.handlerIdentity}):null}subscribeAuthority(t){return this.authorityListeners.add(t),()=>{this.authorityListeners.delete(t)}}async start(){const t=++this.generation;return this.stopped=!1,await this.registerOnce(t)}async registerOnce(t){if(!this.isCurrent(t))return null;this.timer=void 0,this.active&&(this.clearLeaseExpiry(),this.active=null,this.notifyAuthority());const e={registrationId:this.config.registrationId,handlerIdentity:this.config.handlerIdentity,contractRevision:this.config.contractRevision,runtimeRpcProtocolVersion:RUNTIME_RPC_PROTOCOL_VERSION,runtimeMethods:this.config.runtimeMethods,capabilities:this.config.capabilities,manifestDigest:this.config.manifestDigest,journalStoreId:this.config.journalStoreId,journalGeneration:this.config.journalGeneration};let i;try{i=await this.transport.register(this.signProof("runtime.register",e))}catch(e){if(!this.isCurrent(t))return null;if(this.onLeaseLost(e instanceof RuntimeRegistrationLeaseExpiredError?"expired":"heartbeat_failed",e),e instanceof RuntimeRegistrationLeaseExpiredError)return null;const i=e instanceof RuntimeRegistrationHandoffRequiredError?e.retryAfterMs:5e3;return this.scheduleRegistration(i,t),null}return this.isCurrent(t)?"active"!==i.status?("fenced"===i.status?this.onLeaseLost("fenced"):this.scheduleRegistration(i.retryAfterMs,t),null):(this.active={instanceId:i.instanceId,leaseId:i.leaseId,runtimeEpoch:i.runtimeEpoch,runtimeLeaseCredential:i.runtimeLeaseCredential,leaseExpiresAt:i.leaseExpiresAt},this.notifyAuthority(),this.scheduleLeaseExpiry(this.active,t),this.schedule(i.heartbeatIntervalMs,t),this.active):null}stop(){this.generation++,this.stopped=!0,this.timer&&clearTimeout(this.timer),this.expiryTimer&&clearTimeout(this.expiryTimer),this.timer=void 0,this.expiryTimer=void 0,this.active=null,this.notifyAuthority()}schedule(t,e){if(!this.isCurrent(e))return;const i=this.active;i&&(this.timer=setTimeout(()=>{this.sendHeartbeat(t,e,i)},t),this.timer.unref?.())}scheduleRegistration(t,e){this.isCurrent(e)&&(this.timer=setTimeout(()=>{this.registerOnce(e)},t),this.timer.unref?.())}async sendHeartbeat(t,e,i){if(!this.isCurrentLease(e,i))return;const r={leaseId:i.leaseId,handlerIdentity:this.config.handlerIdentity,runtimeEpoch:i.runtimeEpoch};try{const s=await this.transport.heartbeat(this.signProof("runtime.heartbeat",r));if(!this.isCurrentLease(e,i))return;if("renewed"!==s.status)return this.active=null,this.clearLeaseExpiry(),this.notifyAuthority(),void this.onLeaseLost(s.status);this.active={...i,runtimeLeaseCredential:s.runtimeLeaseCredential,leaseExpiresAt:s.leaseExpiresAt},this.notifyAuthority(),this.scheduleLeaseExpiry(this.active,e),this.schedule(t,e)}catch(t){if(!this.isCurrentLease(e,i))return;this.active=null,this.clearLeaseExpiry(),this.notifyAuthority(),this.onLeaseLost("heartbeat_failed",t),this.scheduleRegistration(5e3,e)}}scheduleLeaseExpiry(t,e){this.clearLeaseExpiry();const i=`${t.leaseId}\0${t.runtimeEpoch}\0${t.leaseExpiresAt}`,r=()=>{const s=Date.parse(t.leaseExpiresAt)-this.now();if(s>0)return this.expiryTimer=setTimeout(r,Math.min(2147483647,s)),void this.expiryTimer.unref?.();const n=this.active;this.isCurrentLease(e,t)&&n&&`${n.leaseId}\0${n.runtimeEpoch}\0${n.leaseExpiresAt}`===i&&(this.active=null,this.expiryTimer=void 0,this.notifyAuthority(),this.onLeaseLost("expired"))};r()}clearLeaseExpiry(){this.expiryTimer&&clearTimeout(this.expiryTimer),this.expiryTimer=void 0}isCurrent(t){return!this.stopped&&t===this.generation}isCurrentLease(t,e){return this.isCurrent(t)&&this.active===e}notifyAuthority(){const t=this.authorityLease;for(const e of this.authorityListeners)try{e(t)}catch{}}signProof(t,e){const i=new Date(this.now()).toISOString(),r=randomBytes(18).toString("base64url"),s="runtime.register"===t?RUNTIME_HASH_DOMAINS.registration:RUNTIME_HASH_DOMAINS.heartbeat,n=createHash("sha256").update(runtimeHashPreimage(s,e)).digest("hex"),a=runtimeIdentitySignatureMessage({purpose:t,instanceIdentityKeyId:this.config.instanceIdentityKeyId,signedAt:i,nonce:r,payloadHash:n});return{...e,signaturePurpose:t,instanceIdentityKeyId:this.config.instanceIdentityKeyId,signedAt:i,nonce:r,payloadHash:n,signature:sign(null,a,this.config.privateKey).toString("base64url")}}}export class InstanceAuthorityRuntimeRegistrationTransport{options;constructor(t){this.options=t}async register(t){if(t.registrationId!==this.options.registrationId)throw new Error("runtime registration id mismatch");const e=await this.post(RUNTIME_INTERNAL_API_V1_ROUTE_CONTRACTS.runtimeRegister.path,t);if("active"===e.status&&Number.isFinite(Date.parse(e.leaseExpiresAt))&&Date.parse(e.leaseExpiresAt)<=Date.now())throw new RuntimeRegistrationLeaseExpiredError;if(validateRuntimeInternalRegistrationResult(e)||"fenced"!==e.status&&(e.journalStoreId!==t.journalStoreId||e.journalGeneration!==t.journalGeneration)||"active"===e.status&&(!e.instanceId||!e.leaseId||!e.runtimeLeaseCredential||!Number.isInteger(e.heartbeatIntervalMs)||!Number.isFinite(Date.parse(e.leaseExpiresAt))||Date.parse(e.leaseExpiresAt)<=Date.now()))throw new Error("Instance authority returned an invalid registration binding");return e}async heartbeat(t){const e=await this.post(RUNTIME_INTERNAL_API_V1_ROUTE_CONTRACTS.runtimeHeartbeat.path.replace("{registrationId}",encodeURIComponent(this.options.registrationId)),t);if(!(e&&["renewed","fenced","expired"].includes(e.status)&&("renewed"!==e.status||e.runtimeLeaseCredential&&Number.isFinite(Date.parse(e.leaseExpiresAt)))))throw new Error("Instance authority returned an invalid heartbeat result");return e}async post(t,e){const i=new URL(t,this.options.agentGatewayUrl),r=await this.options.agentFetch(i,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(e)});if(!r.ok){if(409===r.status&&i.pathname.endsWith("/runtime/registrations"))throw new RuntimeRegistrationHandoffRequiredError;const t=await r.text().catch(()=>"");throw new Error(`runtime Instance authority request failed (${r.status} ${r.statusText}) ${i.pathname}`+(t?`: ${t.slice(0,240)}`:""))}return await r.json()}}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "beeos-claw",
3
- "version": "0.5.69",
3
+ "version": "0.5.70",
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>",