beeos-claw 0.6.59 → 0.6.60

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.
package/dist/index.js CHANGED
@@ -1 +1 @@
1
- import{resolveConfigWithMeta,mirrorOpenClawConfig}from"./config.js";import{hasRuntimeReplyApi}from"./runtime/reply-router.js";import{PromptRunStore}from"./runtime/prompt-run-store.js";import{createMcpNativeProjectionNudge,fingerprintMcpServers}from"./acp-gateway/mcp-native-projection-nudge.js";import{createImHotPathDeferral}from"./runtime/im-hot-path-deferral.js";import{SessionWriteGate}from"./acp-gateway/session-write-gate.js";import{OpenClawSessionWriteQuiescence}from"./runtime/openclaw-session-write-quiescence.js";import{SessionMutationCoordinator}from"./runtime/session-mutation-coordinator.js";import{scheduleProcessRuntimeRestart}from"./runtime/runtime-restart-scheduler.js";import{createImInboundHandler}from"./im-inbound-handler.js";import{createTokenProvider}from"@beeos-ai/message-sdk/auth";import{BeeosClawMessageClientProcessOwner}from"./message-client-composition.js";import{MessageProjectionInboundConsumer}from"./message-projection-inbound-consumer.js";import{MessageClientTerminalStateRecovery}from"./runtime/message-client-terminal-state-recovery.js";import{RuntimeAuthorityMessageBusLifecycle}from"./runtime/runtime-authority-message-bus-lifecycle.js";import{MSConnectionStatusSnapshot}from"./runtime/ms-connection-status.js";import{FileInboundExecutionJournal}from"./inbound-execution-journal.js";import{completeA2ATask,registerA2ATools,TaskHandleRegistry}from"./a2a/tools.js";import{BEEOS_UPLOAD_TOOL_NAME,BEEOS_UPLOAD_TOOL_SCHEMA,BEEOS_UPLOAD_TOOL_DESCRIPTION,executeUpload}from"./upload/tool.js";import{BEEOS_DESKTOP_SCREENSHOT_TOOL_NAME,BEEOS_DESKTOP_SCREENSHOT_TOOL_SCHEMA,BEEOS_DESKTOP_SCREENSHOT_TOOL_DESCRIPTION,executeDesktopScreenshot}from"./upload/desktop-screenshot-tool.js";import{BEEOS_LIST_FILES_TOOL_NAME,BEEOS_LIST_FILES_TOOL_SCHEMA,BEEOS_LIST_FILES_TOOL_DESCRIPTION,executeListFiles}from"./files/list-tool.js";import{BEEOS_FETCH_FILE_TOOL_NAME,BEEOS_FETCH_FILE_TOOL_SCHEMA,BEEOS_FETCH_FILE_TOOL_DESCRIPTION,executeFetchFile}from"./files/fetch-tool.js";import{writeUploadToolResult,cleanupUploadToolResultCache}from"./upload/result-cache.js";import{handleFileStage}from"./upload/file-stage.js";import{handleStatusRequest}from"./status.js";import{ACPServer}from"./acp-server.js";import{GatewayClient}from"./gateway-client.js";import{createObsEventWriter}from"./observability.js";import{resolvePluginVersion}from"./plugin-version.js";import{createTerminalWebSocketService}from"./terminal/websocket.js";import{TerminalManager}from"./terminal/session-manager.js";import{loadAgentKeyPair,agentBoundFetch,agentFetch}from"./agent-auth.js";import{readDiscoveryCache,writeDiscoveryCache}from"./discovery-cache.js";import{discoverBridgeFromGW}from"./bridge-discovery.js";import{setUploadDeps}from"./upload/upload-file.js";import{registerLocalFileExtractor}from"./upload/local-file-extractors.js";import{browserScreenshotExtractor}from"./browser/screenshot-extractor.js";import{BEEOS_FILE_RESOURCE_ROUTING_RULE}from"./files/beeos-file-uri.js";import{marksFullRuntimeRegistered,planPluginRegistration,replayCachedPluginTools,resolveCapabilityToolReplay}from"./plugin-registration-plan.js";import{buildAppendSystemContext,buildToolRoutingHint}from"./prompt-system-context.js";import{CONNECTOR_MCP_PROXY_PATH,handleConnectorMcpRequest}from"./mcp-connector-proxy.js";import{CanvasStateManager}from"./canvas/state.js";import{CanvasRelayClient}from"./canvas/relay-client.js";import{createCanvasRelayService}from"./canvas/relay-service.js";import{RENDER_UI_TOOL_NAME,RENDER_UI_TOOL_DESCRIPTION,RENDER_UI_TOOL_SCHEMA,CLOSE_UI_TOOL_NAME,CLOSE_UI_TOOL_DESCRIPTION,CLOSE_UI_TOOL_SCHEMA,createRenderUiHandler,createCloseUiHandler,CANVAS_PRESENTATION_GATE,CANVAS_SYSTEM_PROMPT}from"./canvas/tools.js";import{FormSchemaRegistry}from"./surface/form-schema-registry.js";import{buildCronSchemas}from"./surface/cron-schemas.js";import{RENDER_FORM_TOOL_NAME,RENDER_FORM_TOOL_SCHEMA,buildRenderFormDescription,createRenderFormHandler}from"./surface/render-form-tool.js";import{ResourceDescriptorRegistry}from"./surface/resource-descriptor-registry.js";import{CRON_RESOURCE}from"./surface/cron-resource.js";import{generateActionTools}from"./surface/action-tool-generator.js";import{buildThirdPartyChannelSet,isThirdPartyChannel}from"./channel-detection.js";import{registerArouterMediaProviders}from"./media/index.js";import{homedir}from"node:os";import{join}from"node:path";import{RuntimeProductionAuthority}from"./runtime/runtime-production-authority.js";import{FileRuntimeCommandJournal}from"./runtime/runtime-command-journal.js";import{FileRuntimePoisonDeliveryJournal}from"./runtime/runtime-poison-delivery-journal.js";import{OpenClawNativeMcpProjection}from"./acp-gateway/openclaw-native-mcp-projection.js";import{LocalEncryptedCredentialReferenceVault}from"./acp-gateway/mcp-credential-reference-port.js";import{AgentGatewayGrantJwksReadinessAdapter,RuntimeServiceTransportReadinessAdapter,createRuntimeServiceProductionCandidate}from"./runtime/runtime-service-production-candidate.js";import{MessageServiceCronDeliveryHttpAuthority}from"./runtime/message-service-cron-delivery-http-authority.js";import{AgentMappingRegistry}from"./runtime/agent-mapping-registry.js";import{OpenClawRuntimeProcessOwner}from"./runtime/openclaw-runtime-process-owner.js";import{AgentGatewayHttpProjectionTransport,AgentGatewayTemplateOriginReporter,AgentProjectionProduction,OpenClawAgentProjectionSource}from"./runtime/agent-projection-production.js";import{MessageServiceRunOutputChannel}from"./runtime/message-service-run-output-channel.js";import{AssistantMessageCommitRouter}from"./runtime/assistant-message-commit-router.js";import{ToolResultObservationRouter}from"./runtime/tool-result-observation-router.js";import{ModelCallObservationRouter,registerModelCallObservationHooks}from"./runtime/model-call-observability.js";import{registerArouterLatencyRouting}from"./runtime/arouter-latency-routing.js";import{createPeerRunOutputRouterFactory}from"./runtime/run-output-composition.js";const PLUGIN_VERSION=resolvePluginVersion();let agentKeys=null,sharedCanvasState=null,sharedAcpServer=null;const sharedSessionWriteGate=new SessionWriteGate,sharedPromptRunStore=new PromptRunStore;let lastOpenclawConfig;const sharedMcpNativeProjectionNudge=createMcpNativeProjectionNudge({getActiveImRunCount:()=>sharedPromptRunStore.size(),nudge:()=>{sharedAcpServer?.nudgeMcpNativeProjection()}}),sharedImHotPathDeferral=createImHotPathDeferral({getActiveImRunCount:()=>sharedPromptRunStore.size()});sharedPromptRunStore.setOnBecameIdle(()=>{sharedMcpNativeProjectionNudge.flushIfIdle({fingerprint:fingerprintMcpServers(lastOpenclawConfig)}),sharedImHotPathDeferral.notifyIdle()});const sharedAssistantMessageCommitRouter=new AssistantMessageCommitRouter,sharedToolResultObservationRouter=new ToolResultObservationRouter,sharedModelCallObservationRouter=new ModelCallObservationRouter;let sharedSessionMutationCoordinator,sharedRuntimeAuthority;const sharedMSConnectionStatus=new MSConnectionStatusSnapshot,sharedAgentMappings=new AgentMappingRegistry;let sharedGatewayClient,sharedRuntimeOwner,sharedAgentProjection,sharedRuntimeAgentCapabilities,sharedMcpProjection,sharedMcpCredentialVault;const sharedMessageClientOwner=new BeeosClawMessageClientProcessOwner;let sharedActiveMessageConsumer,sharedInboundExecutionJournal;const sharedTerminalManager=new TerminalManager;let _initializing=!1,_pluginRegisteredAt=0,_fullRuntimeRegistered=!1;const cachedCapabilityTools=[];let rawRegisterTool,wrappedRegisterTool;const ensureRegisterToolCache=e=>{rawRegisterTool||(rawRegisterTool=e.registerTool.bind(e),wrappedRegisterTool=e=>(cachedCapabilityTools.push(e),rawRegisterTool(e)),e.registerTool=wrappedRegisterTool)},ED25519_PKCS8_PREFIX=Buffer.from("302e020100300506032b657004220420","hex");function agentKeyPairToIdentity(e){return{publicKeyBase64:()=>Buffer.from(e.publicKey).toString("base64"),sign:async t=>{const{createPrivateKey:r,sign:o}=await import("node:crypto"),n=r({key:32===e.privateKey.length?Buffer.concat([ED25519_PKCS8_PREFIX,Buffer.from(e.privateKey)]):Buffer.from(e.privateKey),format:"der",type:"pkcs8"});return new Uint8Array(o(null,Buffer.from(t),n))}}}let sharedPluginContext=null,unhandledRejectionObserverInstalled=!1;function installUnhandledRejectionObserver(e,t,r){unhandledRejectionObserverInstalled||(unhandledRejectionObserverInstalled=!0,process.on("unhandledRejection",r=>{const o=r instanceof Error?r.message:String(r),n=r instanceof Error?r.stack:void 0;e.logger.error?.(`[beeos-claw] unhandledRejection: ${o}${n?`\n${n}`:""}`),t({component:"plugin",domain:"fatal",name:"unhandled_rejection",severity:"error",summary:o,error:{code:"unhandled_rejection",message:o,...n?{stack:n}:{}}})}),process.on("uncaughtException",r=>{const o=r instanceof Error?r.message:String(r);e.logger.error?.(`[beeos-claw] uncaughtException: ${r instanceof Error?r.stack??r.message:String(r)}`),t({component:"plugin",domain:"fatal",name:"uncaught_exception",severity:"error",summary:o,error:{code:"uncaught_exception",message:o}})}))}function initializePlugin(e){let t,r;try{t=e.runtime?.config?.loadConfig?.()}catch{}try{r=resolveConfigWithMeta({pluginConfig:e.pluginConfig??{},openclawConfig:t})}catch(t){return e.logger.error?.(`[beeos-claw] Failed to resolve config: ${t instanceof Error?t.message:String(t)}`),null}const o=r.config,n=buildThirdPartyChannelSet(t);n.size>0&&e.logger.info?.(`[beeos-claw] Third-party channels detected: ${[...n].join(", ")}`);const a=o.log.enabled?createObsEventWriter({dir:o.log.dir,redaction:{gatewayToken:o.gateway.token},logger:e.logger}):()=>{};a({component:"plugin",domain:"lifecycle",name:"lifecycle.startup",severity:"info",payload:{pluginVersion:PLUGIN_VERSION,pid:process.pid,nodeVersion:process.version,cwd:process.cwd()}}),a({component:"plugin",domain:"config",name:"config.resolved",severity:"info",payload:{config:o,sources:r.sources,openclawConfigPath:r.openclawConfigPath}});for(const t of r.validation)a({component:"plugin",domain:"config",name:"config.validation",severity:t.severity,summary:t.message,error:{code:t.code,message:t.message,nextSteps:t.nextSteps}}),"error"===t.severity?e.logger.error?.(`[beeos-claw] ${t.message}`):e.logger.warn?.(`[beeos-claw] ${t.message}`);if(r.validation.some(e=>"error"===e.severity)&&e.logger.error?.("[beeos-claw] Plugin has configuration errors — some features may not work."),o.keyFile)try{agentKeys=loadAgentKeyPair(o.keyFile);const t=Buffer.from(agentKeys.publicKey).toString("base64");e.logger.info?.(`[beeos-claw] v${PLUGIN_VERSION} registering (publicKey: ${t})`)}catch(t){e.logger.warn?.(`[beeos-claw] Failed to load agent key pair: ${t}`),e.logger.info?.(`[beeos-claw] v${PLUGIN_VERSION} registering (no key pair — signed requests disabled)`)}else e.logger.info?.(`[beeos-claw] v${PLUGIN_VERSION} registering (no keyFile configured)`);return agentKeys&&o.agentGatewayUrl&&(setUploadDeps({gatewayUrl:o.agentGatewayUrl,keys:agentKeys,logger:e.logger}),registerLocalFileExtractor(browserScreenshotExtractor),e.logger.info?.("[beeos-claw] Upload deps + browser screenshot extractor registered")),lastOpenclawConfig=t,{cfg:o,writeObs:a,thirdPartyChannels:n,openclawConfig:t}}function registerUploadTools(e,t,r){const{cfg:o,writeObs:n}=t;cleanupUploadToolResultCache().catch(t=>{e.logger.warn?.(`[beeos_upload_file] cache cleanup failed on startup: ${String(t)}`)}),e.registerTool({name:BEEOS_UPLOAD_TOOL_NAME,description:BEEOS_UPLOAD_TOOL_DESCRIPTION,parameters:BEEOS_UPLOAD_TOOL_SCHEMA,async execute(t,r,a){e.logger.debug?.(`[beeos_upload_file] toolCallId=${t} params=${JSON.stringify(r)}`);const i=await executeUpload(r,o,agentKeys,a);return e.logger.debug?.(`[beeos_upload_file] toolCallId=${t} ok=${i.ok}`),n({component:"upload-tool",domain:"tool",name:i.ok?"upload.success":"upload.failure",severity:i.ok?"info":"warn",payload:{toolCallId:t,ok:i.ok,fileCount:i.files?.length??0,error:i.error}}),(async r=>{try{await writeUploadToolResult(t,BEEOS_UPLOAD_TOOL_NAME,r),await cleanupUploadToolResultCache()}catch(r){e.logger.warn?.(`[beeos_upload_file] persist cache failed toolCallId=${t}: ${String(r)}`)}return r})(i)}}),e.registerTool({name:BEEOS_DESKTOP_SCREENSHOT_TOOL_NAME,description:BEEOS_DESKTOP_SCREENSHOT_TOOL_DESCRIPTION,parameters:BEEOS_DESKTOP_SCREENSHOT_TOOL_SCHEMA,async execute(t,r,a){e.logger.debug?.(`[beeos_desktop_screenshot] toolCallId=${t} params=${JSON.stringify(r)}`);const i=await executeDesktopScreenshot(r,o,agentKeys,a);return e.logger.debug?.(`[beeos_desktop_screenshot] toolCallId=${t} ok=${i.ok}`),n({component:"desktop-screenshot-tool",domain:"tool",name:i.ok?"desktop_screenshot.success":"desktop_screenshot.failure",severity:i.ok?"info":"warn",payload:{toolCallId:t,ok:i.ok,path:i.path,fileCount:i.files?.length??0,error:i.error}}),(async r=>{try{await writeUploadToolResult(t,BEEOS_DESKTOP_SCREENSHOT_TOOL_NAME,r),await cleanupUploadToolResultCache()}catch(r){e.logger.warn?.(`[beeos_desktop_screenshot] persist cache failed toolCallId=${t}: ${String(r)}`)}return r})(i)}}),e.registerHttpRoute({path:"/beeos/files/stage",auth:"gateway",async handler(e,r){await handleFileStage(e,r,t.cfg)}}),e.registerHttpRoute({path:"/beeos/status",auth:"plugin",handler(e,t){handleStatusRequest(e,t,{publicKey:agentKeys?.publicKey??null,pluginVersion:PLUGIN_VERSION})}}),e.registerTool({name:BEEOS_LIST_FILES_TOOL_NAME,description:BEEOS_LIST_FILES_TOOL_DESCRIPTION,parameters:BEEOS_LIST_FILES_TOOL_SCHEMA,async execute(t,r,a){e.logger.debug?.(`[beeos_list_files] toolCallId=${t} params=${JSON.stringify(r)}`);const i=await executeListFiles(r,o,agentKeys,a);return n({component:"file-list-tool",domain:"tool",name:i.ok?"list_files.success":"list_files.failure",severity:i.ok?"info":"warn",payload:{toolCallId:t,ok:i.ok,fileCount:i.files?.length??0,error:i.error}}),i}}),e.registerTool({name:BEEOS_FETCH_FILE_TOOL_NAME,description:BEEOS_FETCH_FILE_TOOL_DESCRIPTION,parameters:BEEOS_FETCH_FILE_TOOL_SCHEMA,async execute(t,r,a){e.logger.debug?.(`[beeos_fetch_file] toolCallId=${t} params=${JSON.stringify(r)}`);const i=await executeFetchFile(r,o,agentKeys,{signal:a,resolvePath:e.resolvePath});return n({component:"file-fetch-tool",domain:"tool",name:i.ok?"fetch_file.success":"fetch_file.failure",severity:i.ok?"info":"warn",payload:{toolCallId:t,ok:i.ok,path:i.path,size:i.size,error:i.error}}),i}})}function registerConnectorMcpProxy(e,t){e.registerHttpRoute({path:CONNECTOR_MCP_PROXY_PATH,auth:"plugin",async handler(r,o){await handleConnectorMcpRequest(r,o,{agentGatewayUrl:t.cfg.agentGatewayUrl,agentKeys:agentKeys,logger:e.logger})}})}function registerServices(e,t,r){const{cfg:o,writeObs:n,thirdPartyChannels:a}=t;if("function"!=typeof e.on){if(o.messageBus.enabled)throw new Error("openclaw_before_message_write_hook_unavailable")}else registerModelCallObservationHooks((t,r)=>e.on(t,r),sharedModelCallObservationRouter),e.on("before_message_write",(t,r)=>{const o=t.sessionKey??r.sessionKey;if(!o)return void e.logger.debug?.("[before_message_write] ignored assistant write without sessionKey");sharedAssistantMessageCommitRouter.route(o,t.message)||e.logger.debug?.(`[before_message_write] no active IM commit target sessionKey=${o} active_targets=${sharedAssistantMessageCommitRouter.size()}`)}),e.on("after_tool_call",(t,r)=>{const o=r.sessionKey,n=t.toolCallId??r.toolCallId,a=t.toolName||r.toolName;if(!o||!n||!a)return void e.logger.debug?.(`[after_tool_call] ignored structured result without correlation sessionKey=${o??"missing"} toolCallId=${n??"missing"}`);sharedToolResultObservationRouter.route(o,{toolCallId:n,toolName:a,rawResult:t.result,isError:Boolean(t.error)})||e.logger.debug?.(`[after_tool_call] no active IM observation target sessionKey=${o} active_targets=${sharedToolResultObservationRouter.size()}`)});const i=agentKeys?(e,t)=>agentFetch(e,agentKeys,t):void 0,s=agentKeys?(e,t)=>agentBoundFetch(e,agentKeys,t):void 0,l=agentKeys?Buffer.from(agentKeys.publicKey).toString("base64"):void 0,c=join(process.env.OPENCLAW_HOME||join(homedir(),".openclaw"),"beeos"),g=sharedInboundExecutionJournal??=new FileInboundExecutionJournal(join(c,"inbound-execution-journal.v1.jsonl"));if(sharedMcpProjection??=new OpenClawNativeMcpProjection,!sharedMcpCredentialVault&&agentKeys&&l&&(sharedMcpCredentialVault=new LocalEncryptedCredentialReferenceVault(join(c,"mcp-credential-vault.json"),l,agentKeys.privateKey)),!sharedRuntimeAuthority&&agentKeys&&o.agentGatewayUrl&&i&&s&&(sharedRuntimeAuthority=new RuntimeProductionAuthority({stateDirectory:c,agentGatewayUrl:o.agentGatewayUrl,agentFetch:i,boundAgentFetch:s,keys:agentKeys,logger:e.logger,getMSConnectionStatus:e=>sharedMSConnectionStatus.get(e)})),sharedGatewayClient??=new GatewayClient({url:o.gateway.url,token:o.gateway.token||"",clientId:o.gateway.clientId,openclawCanvasCompat:!!o.openclawCanvasCompat,retry:{baseMs:o.retry.baseMs,maxMs:o.retry.maxMs,maxAttempts:o.retry.maxAttempts}}),sharedRuntimeAgentCapabilities??={canvas:o.canvasEnabled,terminal:!1,webSsh:!1,fileSystem:!0,image:!0,audio:!1,mcp:!0,cron:!0,streaming:!0,pushNotifications:o.messageBus.enabled},!sharedAgentProjection&&sharedRuntimeAuthority&&i&&o.agentGatewayUrl){const t=sharedRuntimeAuthority;sharedAgentProjection=new AgentProjectionProduction({authority:t,stateFile:join(t.stateDirectory,"runtime-agent-projection.json"),journal:t.journal,identities:t.identities,source:new OpenClawAgentProjectionSource(()=>sharedGatewayClient),transport:new AgentGatewayHttpProjectionTransport({agentGatewayUrl:o.agentGatewayUrl,boundAgentFetch:t.boundAgentFetch,getRuntimeLeaseCredential:e=>{const r=t.authorityLease;if(!r||r.instanceId!==e.instanceId||r.leaseId!==e.leaseId||r.runtimeEpoch!==e.runtimeEpoch||r.handlerIdentity!==e.handlerIdentity)throw new Error("agent projection fenced by current runtime lease");return r.runtimeLeaseCredential},onAccepted:t=>{0!==t.size&&sharedAgentMappings.replace(t).catch(t=>{e.logger.warn?.("[agent-projection] mapping publication failed: "+(t instanceof Error?t.message:String(t)))})}}),capabilities:sharedRuntimeAgentCapabilities,logger:e.logger})}sharedRuntimeOwner??=new OpenClawRuntimeProcessOwner(sharedGatewayClient,sharedAgentProjection,e.logger);const d=sharedRuntimeOwner;sharedSessionMutationCoordinator??=new SessionMutationCoordinator(sharedSessionWriteGate,new OpenClawSessionWriteQuiescence(e.logger,{agentId:o.gateway.agentId||"main"}),e.logger,{onRecoveryRequired:t=>{scheduleProcessRuntimeRestart(t,e.logger)}});const u=sharedSessionMutationCoordinator;if(!sharedAcpServer){const t=sharedRuntimeAuthority&&o.agentGatewayUrl?new AgentGatewayTemplateOriginReporter({agentGatewayUrl:o.agentGatewayUrl,boundAgentFetch:sharedRuntimeAuthority.boundAgentFetch}):void 0;sharedAcpServer=new ACPServer(o,e.logger,u,n,i,l,a,sharedTerminalManager,sharedRuntimeAuthority,void 0,sharedMcpProjection,sharedMcpCredentialVault,s,{gateway:d.client,agentProjection:sharedAgentProjection,markAgentProjectionDirty:()=>sharedAgentProjection?.markDirty(),templateOriginReporter:t,agentMappings:sharedAgentMappings,agentCapabilities:sharedRuntimeAgentCapabilities,createRunOutputRouter:createPeerRunOutputRouterFactory(new MessageServiceRunOutputChannel({getClient:()=>sharedMessageClientOwner.current?.client??null,logger:e.logger,writeObs:n})),runExclusive:(t,r,o)=>sharedImHotPathDeferral.runExclusive(t,r,e.logger,o)})}const m=sharedAcpServer,p=()=>sharedAgentMappings.snapshot()?.values().next().value;r.current=m,sharedMcpNativeProjectionNudge.request({registrationMode:e.registrationMode,fingerprint:fingerprintMcpServers(t.openclawConfig),logger:e.logger});const y=sharedPromptRunStore,{enabled:h,principalId:f,a2aEnabled:w}=o.messageBus;let C;o.agentGatewayUrl&&agentKeys&&(C=createTokenProvider({agentGatewayUrl:o.agentGatewayUrl,identity:agentKeyPairToIdentity(agentKeys)}));const R=process.env.MESSAGE_RUNTIME_DELIVERY_KEY?.trim(),v=f||process.env.BEEOS_PRINCIPAL_ID||"",b=C?sharedMessageClientOwner.configure({tokenProvider:C,identityId:v,...o.messageBus.serviceUrl?{serviceUrl:o.messageBus.serviceUrl}:{},logger:e.logger,...sharedRuntimeAuthority&&R?{runtimeDelivery:{scopedDeliveryKey:R,authority:{currentLease:()=>{const e=sharedRuntimeAuthority?.authorityLease,t=sharedRuntimeAuthority?.currentConsumerBinding();return e&&t?{...e,journalStoreId:t.journalStoreId,journalGeneration:t.journalGeneration}:null}}}}:{}}):void 0,_=sharedRuntimeAuthority,S=o.messageBus.serviceUrl;if(b&&_&&S&&R){const e=b.nodeTransport;m.setCronConversationDelivery({deliver:async t=>{const r=_.authorityLease;if(!r)throw new Error("current runtime authority lease unavailable for cron delivery");const o=new MessageServiceCronDeliveryHttpAuthority({messageServiceUrl:S,fetchAuthorized:async(t,o)=>await e.fetchRuntimeChannel(t,{runtimeLeaseCredential:r.runtimeLeaseCredential},o)});return await o.deliver({...t,runtimeLeaseCredential:r.runtimeLeaseCredential})}})}else m.setCronConversationDelivery(void 0);const E=sharedRuntimeAuthority?.journal??new FileRuntimeCommandJournal(join(c,"runtime-command-journal.json"),void 0,void 0,{onMaintenanceError:t=>e.logger.error?.(`[runtime-journal] event=checkpoint_failed trigger=threshold error=${t instanceof Error?t.message:String(t)}`)}),O=d.createRegistrationToken();e.registerService({id:"beeos-acp",async start(){const t=Date.now(),r=_pluginRegisteredAt?t-_pluginRegisteredAt:-1;e.logger.info?.(`[beeos-claw] beeos-acp service start() begin, sincePluginRegister=${r}ms, bridge.url=${o.bridge.url||"(empty)"}`);const n=await createRuntimeServiceProductionCandidate({...o.agentGatewayUrl&&i?{authorizationAdapter:new AgentGatewayGrantJwksReadinessAdapter({agentGatewayUrl:o.agentGatewayUrl})}:{},...b&&o.agentGatewayUrl&&s&&R?{transportAdapter:new RuntimeServiceTransportReadinessAdapter({agentGatewayUrl:o.agentGatewayUrl,messageService:b.nodeTransport,messageClient:b.client,configuredServiceUrl:o.messageBus.serviceUrl,boundAgentFetch:s,scopedDeliveryKey:R})}:{},commandJournal:E,poisonJournal:new FileRuntimePoisonDeliveryJournal(join(c,"runtime-poison-deliveries.json")),onConsumerDeliveryError:t=>{const r=t instanceof Error?`${t.name}: ${t.message}${"status"in t?` status=${t.status}`:""}${"code"in t?` code=${t.code}`:""}\n${t.stack??""}`:String(t);e.logger.error?.(`[runtime-service] delivery worker failed before acknowledgement; delivery retained for retry: ${r}`)},onConsumerLeaseStarvation:({starvedForMs:t,recovered:r})=>{const o=Math.round(t/1e3);r?e.logger.info?.(`[runtime-service] runtime authority lease restored after ${o}s without delivery reads`):e.logger.warn?.(`[runtime-service] no runtime authority lease for ${o}s; delivery reads suspended, queued runtime operations (model switch, session control) will not be consumed`)},onConsumerRequestFailure:t=>e.logger.error?.(`[runtime-service] request failed operationId=${t.operationId} method=${t.method}: ${t.error instanceof Error?t.error.stack??t.error.message:String(t.error)}`)});if(sharedRuntimeAuthority?.setRuntimeServiceMethods("ready"===n.status?n.serviceMethods:[]),sharedRuntimeAuthority?.setRuntimeResourceBindingContractRevision("ready"===n.status?n.resourceBindingContractRevision:void 0),m.setRuntimeRpcConsumerFactory("ready"===n.status?n.factory:void 0),"unavailable"===n.status?e.logger.warn?.("[runtime-service] capability fenced: "+n.blockers.map(e=>e.id).join(",")):n.capabilityBlockers.length&&e.logger.warn?.("[runtime-service] method capability fenced: "+n.capabilityBlockers.map(e=>e.method).join(",")),!o.bridge.url&&o.agentGatewayUrl&&i){const t=Date.now(),r=await discoverBridgeFromGW(o.agentGatewayUrl,i);if(e.logger.info?.(`[beeos-claw] bridge discovery took ${Date.now()-t}ms result=${r?.url??"null"}`),r)o.bridge.url=r.url,writeDiscoveryCache({bridge:{url:r.url,discoveredAt:Date.now()}}),e.logger.info?.(`[beeos-claw] Bridge URL discovered from Agent Gateway: ${r.url}`);else{const t=readDiscoveryCache();t?.bridge?.url?(o.bridge.url=t.bridge.url,e.logger.info?.(`[beeos-claw] Bridge URL from cache (GW unreachable): ${t.bridge.url}`)):e.logger.debug?.("[beeos-claw] Bridge URL not available (no GW, no cache) — bridge disabled")}}await d.acquire(O);try{await m.start(o.bridge.url||void 0)}catch(e){throw await d.release(O),e}e.logger.info?.(`[beeos-claw] ACP Server started (total service start: ${Date.now()-t}ms)`)},async stop(){try{await d.release(O)}finally{await m.stop()}await E.checkpoint(),e.logger.info?.("[runtime-service] command journal checkpoint completed"),e.logger.info?.("[beeos-claw] ACP Server stopped")}});if(h&&void 0!==C&&C){const t=sharedMessageClientOwner.createRegistrationToken(),r=async(e,t)=>{const r=await C(e);if(!r.identityId||r.identityId!==e)throw new Error("Message SDK token identity did not match the current runtime authority");return{principalId:e,runtimeEpoch:t}},a=(e,t,r)=>({getLocalAgentId:()=>t,resolveLocalAgentId:e=>e===r?t:void 0,getDefaultPlatformAgent:()=>({instanceId:e.principalId}),ensureImInboundSession:(e,t,r,o)=>{m.ensureImInboundSession(e,t,r,o)},chatAbort:(e,t)=>m.chatAbort(e,t)}),i=sharedRuntimeAuthority?new RuntimeAuthorityMessageBusLifecycle({authority:sharedRuntimeAuthority,logger:e.logger,bus:{connect:async i=>{await sharedMessageClientOwner.acquire(t,async t=>{const s=await r(i.instanceId,i.runtimeEpoch),l=t.client,c={leaseId:i.leaseId,runtimeEpoch:i.runtimeEpoch};sharedMSConnectionStatus.bind(c);const d=new Map,p=new MessageProjectionInboundConsumer({client:l,bindings:()=>{const t=sharedRuntimeAuthority?.authorityLease;if(!t||t.instanceId!==s.principalId||t.runtimeEpoch!==s.runtimeEpoch)return[];const r=sharedAgentMappings.snapshot();return r?[...r.entries()].flatMap(([r,i])=>{if(!i.id||i.instanceId!==s.principalId)return[];let c=d.get(i.id);return c&&c.localAgentId===r||(c={localAgentId:r,inbound:createImInboundHandler({runtime:e.runtime,pluginConfig:e.pluginConfig,client:l,localServer:a(s,r,i.id),promptRunStore:y,waitIfExclusive:(t,r)=>sharedImHotPathDeferral.waitIfExclusive(t,e.logger,r).then(()=>{}),assistantMessageCommitRouter:sharedAssistantMessageCommitRouter,toolResultObservationRouter:sharedToolResultObservationRouter,modelCallObservationRouter:sharedModelCallObservationRouter,sessionMutationCoordinator:u,logger:e.logger,writeObs:n,principalIdOverride:s.principalId,promptTimeoutMs:o.promptTimeoutMs,activeRunAuthority:m.getActiveRunAuthority(),...o.agentGatewayUrl&&agentKeys?{completeA2ATask:(t,r,n)=>completeA2ATask({agentGatewayUrl:o.agentGatewayUrl,agentKeys:agentKeys,logger:e.logger},t,r,n)}:{}})},d.set(i.id,c)),[{principalId:s.principalId,targetAgentId:i.id,runtimeEpoch:t.runtimeEpoch,inbound:c.inbound}]}):[]},executionJournal:g,logger:e.logger}),h=sharedAgentMappings.subscribe(()=>{sharedActiveMessageConsumer===p&&p.refresh()}),f=sharedRuntimeAuthority?.subscribeAuthority(()=>{sharedActiveMessageConsumer===p&&p.refresh()})??(()=>{}),w=new MessageClientTerminalStateRecovery({logger:e.logger,isCurrent:()=>{if(sharedActiveMessageConsumer!==p)return!1;const e=sharedRuntimeAuthority?.authorityLease;return null!=e&&e.instanceId===s.principalId&&e.runtimeEpoch===s.runtimeEpoch&&e.leaseId===i.leaseId&&e.handlerIdentity===i.handlerIdentity},reconnect:async()=>{if("failed"===l.getSnapshot().connection){await l.connect();try{await m.rebindRuntimeDelivery()}catch(t){e.logger.warn?.(`[beeos-claw] runtime Service delivery rebind failed after MessageClient recovery: ${t instanceof Error?t.message:String(t)}`)}}}});let C=l.getSnapshot().connection;sharedMSConnectionStatus.setIfCurrent(c,C);const R=l.subscribe(()=>{const t=l.getSnapshot();t.connection!==C&&(C=t.connection,sharedMSConnectionStatus.setIfCurrent(c,t.connection),"connected"===t.connection?(w.notifyConnected(),e.logger.info?.(`[beeos-claw] connection transport=message_service_realtime state=connected localAgentId=${m.getLocalAgentId()} principalId=${s.principalId}`)):"failed"===t.connection?(e.logger.warn?.(`[beeos-claw] connection transport=message_service_realtime state=failed principalId=${s.principalId} recoveryError=${t.recoveryError??"unknown"}`),w.notifyFailed()):"disconnected"!==t.connection&&"reconnecting"!==t.connection||e.logger.warn?.(`[beeos-claw] connection transport=message_service_realtime state=${t.connection} principalId=${s.principalId}; SDK owns reconnect/recovery`))});sharedActiveMessageConsumer=p,p.start();try{await l.connect()}catch(e){throw sharedActiveMessageConsumer===p&&(sharedActiveMessageConsumer=void 0),sharedMSConnectionStatus.resetIfCurrent(c),w.dispose(),await p.stop(),h(),f(),R(),await l.disconnect().catch(()=>{}),e}try{await m.rebindRuntimeDelivery()}catch(t){e.logger.warn?.(`[beeos-claw] runtime Service delivery rebind failed after MessageClient connect: ${t instanceof Error?t.message:String(t)}`)}return async()=>{sharedActiveMessageConsumer===p&&(sharedActiveMessageConsumer=void 0),sharedMSConnectionStatus.resetIfCurrent(c),w.dispose(),await p.stop(),f(),R(),await l.disconnect(),e.logger.info?.("[beeos-claw] MessageClient disconnected")}})},disconnect:async()=>{await sharedMessageClientOwner.release(t)}}}):void 0;e.registerService({id:"beeos-message-bus",async start(){i?i.start():e.logger.warn?.("[beeos-claw] Message bus requires runtime authority; waiting is unavailable without Agent Gateway runtime identity")},async stop(){await(i?.stop())}})}else h?e.logger.warn?.("[beeos-claw] Message bus enabled but Agent Gateway token provider is unavailable — disabled"):e.logger.info?.(`[beeos-claw] Message bus disabled (enabled=${h})`);if(!1!==w&&o.agentGatewayUrl){const t=e=>m.getGatewaySessionKeyForToolCall(e),r=e=>m.getMentionsForToolCall(e),n=(e,t)=>{m.broadcastNotification(e,t)},a=new TaskHandleRegistry;registerA2ATools(e,{agentGatewayUrl:o.agentGatewayUrl,agentKeys:agentKeys,logger:e.logger},a,t,r,n,()=>p()?.instanceId),e.logger.info?.("[beeos-claw] A2A tools registered (beeos_discover_agents, beeos_call_agent) via REST")}if(o.canvasEnabled){sharedCanvasState||(sharedCanvasState=new CanvasStateManager);const t=sharedCanvasState;if(o.agentGatewayUrl&&agentKeys){let r;t.setLogger(e.logger);{const e=o.agentGatewayUrl,t=agentKeys;r=async()=>{const r=await agentFetch(`${e}/api/v1/canvas/token`,t,{method:"POST"});if(!r.ok){const e=await r.text().catch(()=>"");throw new Error(`Canvas token returned ${r.status}: ${e}`)}return r.json()}}const n=n=>{const a=new CanvasRelayClient({canvasRelayUrl:o.canvasRelayUrl,agentKeys:agentKeys,agentId:n.id,displayName:n.displayName,avatarUrl:n.avatarUrl,tokenProvider:r,onConnected:()=>m.flushPendingCanvasLinks(),onReconnect:()=>m.onRelayReconnected(),logger:e.logger});t.setRelayClient(a),t.setAgentId(n.id),m.wireRelayActionHandler(),a.connect(),e.logger.info?.(`[beeos-claw] Canvas Relay multiplexed client connected (agentId=${n.id}, name=${n.name??"none"})`)};e.registerService(createCanvasRelayService({getAgent:p,onceAgentReady:e=>sharedAgentMappings.onceReady(e),initRelay:n,stopRelay:()=>t.getRelayClient()?.disconnect(),logger:e.logger})),e.logger.info?.("[beeos-claw] Canvas Relay configured")}else e.logger.warn?.("[beeos-claw] Canvas Relay unavailable: agentGatewayUrl or agentKeys missing");const r={stateManager:t,logger:e.logger,writeObs:e=>n(e),isCanvasActive:e=>!!e&&m.isCanvasActiveForGatewayKey(e),getActiveSessionId:e=>m.getSessionIdForToolCall(e),getPlatformAgentId:e=>m.getPlatformAgentIdForToolCall(e),getGatewaySessionKey:e=>m.getGatewaySessionKeyForToolCall(e),waitForAgentSync:()=>sharedAgentMappings.waitForSynchronization()},i=new FormSchemaRegistry,s=buildCronSchemas(a,{capMs:o.cronMaxDurationCapMs});i.register(s.create),i.register(s.update);const l=new ResourceDescriptorRegistry;l.register(CRON_RESOURCE);const c=e=>async(t,r,o)=>(m.preMapToolCallSession(t),e(t,r));e.registerTool({name:RENDER_UI_TOOL_NAME,description:RENDER_UI_TOOL_DESCRIPTION,parameters:RENDER_UI_TOOL_SCHEMA,execute:c(createRenderUiHandler(r))}),e.registerTool({name:CLOSE_UI_TOOL_NAME,description:CLOSE_UI_TOOL_DESCRIPTION,parameters:CLOSE_UI_TOOL_SCHEMA,execute:c(createCloseUiHandler(r))}),m.setCanvasState(t,{schemaRegistry:i,descriptorRegistry:l});const g=m.getFormBuilder();g&&e.registerTool({name:RENDER_FORM_TOOL_NAME,description:buildRenderFormDescription(i),parameters:RENDER_FORM_TOOL_SCHEMA,execute:c(createRenderFormHandler({registry:i,builder:g,logger:e.logger}))});const d=m.getResourceRenderer();if(d){generateActionTools(l,e,async(e,t)=>m.gatewayRequest(e,t),d,e.logger)}const u=buildToolRoutingHint(BEEOS_FILE_RESOURCE_ROUTING_RULE,BEEOS_DESKTOP_SCREENSHOT_TOOL_NAME),y=new Set([RENDER_UI_TOOL_NAME,CLOSE_UI_TOOL_NAME,RENDER_FORM_TOOL_NAME]),h=256,f=new Map;if("function"==typeof e.on){try{e.on("before_prompt_build",(e,t)=>{if(t?.sessionKey){if(f.size>=h&&!f.has(t.sessionKey)){const e=f.keys().next().value;void 0!==e&&f.delete(e)}f.set(t.sessionKey,t.channelId)}const r=isThirdPartyChannel(a,{sessionKey:t?.sessionKey,channelId:t?.channelId});if(!t?.sessionKey&&!r)return{appendSystemContext:u};const o=Boolean(t?.sessionKey&&m.isCanvasActiveForGatewayKey(t.sessionKey));return{appendSystemContext:buildAppendSystemContext({thirdParty:r,canvasActive:o,canvasSystemPrompt:`${CANVAS_PRESENTATION_GATE}\n\n${CANVAS_SYSTEM_PROMPT}`,toolRoutingHint:u})}}),e.logger.debug?.("[beeos-claw] Canvas system prompt hook registered: before_prompt_build")}catch{e.logger.debug?.("[beeos-claw] before_prompt_build hook not supported")}try{e.on("before_tool_call",(e,t)=>{if(t?.sessionKey&&m.setPendingToolCallSessionKey(t.sessionKey),!y.has(e.toolName))return;const r=t?.sessionKey?f.get(t.sessionKey):void 0;return isThirdPartyChannel(a,{sessionKey:t?.sessionKey,channelId:r})?{block:!0,blockReason:"Canvas tools are not available in this channel. Respond with plain text only."}:t?.sessionKey?m.isCanvasActiveForGatewayKey(t.sessionKey)?void 0:{block:!0,blockReason:"Canvas is closed by user. Respond with plain text instead."}:{block:!0,blockReason:"Canvas session unknown. Respond with plain text instead."}}),e.logger.debug?.("[beeos-claw] Canvas tool guard registered: before_tool_call")}catch{e.logger.debug?.("[beeos-claw] before_tool_call hook not supported")}}e.logger.info?.("[beeos-claw] Canvas tools registered (render_ui, close_ui, render_form + auto-action tools)")}else e.logger.info?.("[beeos-claw] Canvas disabled by config (canvasEnabled=false)");if(o.bridge.shell.enabled&&!0===o.legacyTerminalWsEnabled){const t=createTerminalWebSocketService({config:{port:o.terminalWsPort,token:agentKeys?Buffer.from(agentKeys.publicKey).toString("base64"):"",allowedOrigins:o.terminalWsAllowedOrigins},terminalMgr:sharedTerminalManager,logger:e.logger,writeObs:n});e.registerService({id:"beeos-terminal-ws",start(){t.start(),e.logger.info?.(`[beeos-claw] Terminal WS started on port ${o.terminalWsPort}`)},stop(){t.stop(),e.logger.info?.("[beeos-claw] Terminal WS stopped")}})}else o.bridge.shell.enabled?e.logger.info?.("[beeos-claw] Legacy static-token Terminal WS disabled (default)"):e.logger.info?.("[beeos-claw] Terminal/shell disabled by config (bridge.shell.enabled=false)");if(t.openclawConfig){const r=mirrorOpenClawConfig(t.openclawConfig);r.copied?e.logger.info?.(`[beeos-claw] mirrored openclaw config to ${r.destinationPath}`):e.logger.debug?.(`[beeos-claw] openclaw config mirror skipped: ${r.reason}`)}}const plugin={id:"beeos-claw",name:"beeos-claw",description:"BeeOS platform bridge plugin for OpenClaw. Provides file upload/download, ACP bridge, and observability.",version:PLUGIN_VERSION,register(e){if(_initializing)return;const t=planPluginRegistration(e.registrationMode,_fullRuntimeRegistered);if("skip"!==t){_initializing=!0;try{if(!hasRuntimeReplyApi(e.runtime))throw e.logger.error?.("[beeos-claw] FATAL: OpenClaw runtime.channel.reply.dispatchReplyFromConfig not exposed. Upgrade OpenClaw or revert beeos-claw."),new Error("openclaw_runtime_reply_api_unavailable");const r=(t,r)=>{registerUploadTools(e,t,r),registerConnectorMcpProxy(e,t),registerArouterLatencyRouting(e),registerArouterMediaProviders(e)};if("capabilities"===t&&sharedPluginContext){if(cachedCapabilityTools.length>0){const t=resolveCapabilityToolReplay({currentRegisterTool:e.registerTool,wrappedRegisterTool:wrappedRegisterTool,originalRegisterTool:rawRegisterTool}),r=replayCachedPluginTools(r=>t.call(e,r),cachedCapabilityTools);return registerArouterLatencyRouting(e),registerArouterMediaProviders(e),void e.logger.info?.(`[beeos-claw] capability re-register mode=${e.registrationMode} tools_only count=${r}`)}const t={current:sharedAcpServer??null};return r(sharedPluginContext,t),registerServices(e,sharedPluginContext,t),void e.logger.info?.(`[beeos-claw] capability re-register mode=${e.registrationMode} capabilities_only`)}const o=initializePlugin(e);if(!o)return;const n={current:null};installUnhandledRejectionObserver(e,o.writeObs,n),ensureRegisterToolCache(e),cachedCapabilityTools.length=0,r(o,n),registerServices(e,o,n),sharedPluginContext=o,marksFullRuntimeRegistered(e.registrationMode,t)&&(_fullRuntimeRegistered=!0),_pluginRegisteredAt=Date.now(),e.logger.info?.("[beeos-claw] Plugin registered successfully")}finally{_initializing=!1}}else e.logger.info?.(`[beeos-claw] skip re-register mode=${e.registrationMode??"full"} already_registered`)}};export default plugin;
1
+ import{resolveConfigWithMeta,mirrorOpenClawConfig}from"./config.js";import{hasRuntimeReplyApi}from"./runtime/reply-router.js";import{PromptRunStore}from"./runtime/prompt-run-store.js";import{createMcpNativeProjectionNudge,fingerprintMcpServers}from"./acp-gateway/mcp-native-projection-nudge.js";import{createImHotPathDeferral}from"./runtime/im-hot-path-deferral.js";import{SessionWriteGate}from"./acp-gateway/session-write-gate.js";import{OpenClawSessionWriteQuiescence}from"./runtime/openclaw-session-write-quiescence.js";import{SessionMutationCoordinator}from"./runtime/session-mutation-coordinator.js";import{scheduleProcessRuntimeRestart}from"./runtime/runtime-restart-scheduler.js";import{createImInboundHandler}from"./im-inbound-handler.js";import{createTokenProvider}from"@beeos-ai/message-sdk/auth";import{BeeosClawMessageClientProcessOwner}from"./message-client-composition.js";import{MessageProjectionInboundConsumer}from"./message-projection-inbound-consumer.js";import{MessageClientTerminalStateRecovery}from"./runtime/message-client-terminal-state-recovery.js";import{RuntimeAuthorityMessageBusLifecycle}from"./runtime/runtime-authority-message-bus-lifecycle.js";import{MSConnectionStatusSnapshot}from"./runtime/ms-connection-status.js";import{FileInboundExecutionJournal}from"./inbound-execution-journal.js";import{completeA2ATask,registerA2ATools,TaskHandleRegistry}from"./a2a/tools.js";import{BEEOS_UPLOAD_TOOL_NAME,BEEOS_UPLOAD_TOOL_SCHEMA,BEEOS_UPLOAD_TOOL_DESCRIPTION,executeUpload}from"./upload/tool.js";import{BEEOS_DESKTOP_SCREENSHOT_TOOL_NAME,BEEOS_DESKTOP_SCREENSHOT_TOOL_SCHEMA,BEEOS_DESKTOP_SCREENSHOT_TOOL_DESCRIPTION,executeDesktopScreenshot}from"./upload/desktop-screenshot-tool.js";import{BEEOS_LIST_FILES_TOOL_NAME,BEEOS_LIST_FILES_TOOL_SCHEMA,BEEOS_LIST_FILES_TOOL_DESCRIPTION,executeListFiles}from"./files/list-tool.js";import{BEEOS_FETCH_FILE_TOOL_NAME,BEEOS_FETCH_FILE_TOOL_SCHEMA,BEEOS_FETCH_FILE_TOOL_DESCRIPTION,executeFetchFile}from"./files/fetch-tool.js";import{writeUploadToolResult,cleanupUploadToolResultCache}from"./upload/result-cache.js";import{handleFileStage}from"./upload/file-stage.js";import{handleStatusRequest}from"./status.js";import{ACPServer}from"./acp-server.js";import{GatewayClient}from"./gateway-client.js";import{createObsEventWriter}from"./observability.js";import{resolvePluginVersion}from"./plugin-version.js";import{createTerminalWebSocketService}from"./terminal/websocket.js";import{TerminalManager}from"./terminal/session-manager.js";import{loadAgentKeyPair,agentBoundFetch,agentFetch}from"./agent-auth.js";import{readDiscoveryCache,writeDiscoveryCache}from"./discovery-cache.js";import{discoverBridgeFromGW}from"./bridge-discovery.js";import{setUploadDeps}from"./upload/upload-file.js";import{registerLocalFileExtractor}from"./upload/local-file-extractors.js";import{browserScreenshotExtractor}from"./browser/screenshot-extractor.js";import{BEEOS_FILE_RESOURCE_ROUTING_RULE}from"./files/beeos-file-uri.js";import{marksFullRuntimeRegistered,planPluginRegistration,replayCachedPluginTools,resolveCapabilityToolReplay}from"./plugin-registration-plan.js";import{buildAppendSystemContext,buildToolRoutingHint}from"./prompt-system-context.js";import{CONNECTOR_MCP_PROXY_PATH,handleConnectorMcpRequest}from"./mcp-connector-proxy.js";import{CanvasStateManager}from"./canvas/state.js";import{CanvasRelayClient}from"./canvas/relay-client.js";import{createCanvasRelayService}from"./canvas/relay-service.js";import{RENDER_UI_TOOL_NAME,RENDER_UI_TOOL_DESCRIPTION,RENDER_UI_TOOL_SCHEMA,CLOSE_UI_TOOL_NAME,CLOSE_UI_TOOL_DESCRIPTION,CLOSE_UI_TOOL_SCHEMA,createRenderUiHandler,createCloseUiHandler,CANVAS_PRESENTATION_GATE,CANVAS_SYSTEM_PROMPT}from"./canvas/tools.js";import{FormSchemaRegistry}from"./surface/form-schema-registry.js";import{buildCronSchemas}from"./surface/cron-schemas.js";import{RENDER_FORM_TOOL_NAME,RENDER_FORM_TOOL_SCHEMA,buildRenderFormDescription,createRenderFormHandler}from"./surface/render-form-tool.js";import{ResourceDescriptorRegistry}from"./surface/resource-descriptor-registry.js";import{CRON_RESOURCE}from"./surface/cron-resource.js";import{generateActionTools}from"./surface/action-tool-generator.js";import{buildThirdPartyChannelSet,isThirdPartyChannel}from"./channel-detection.js";import{registerArouterMediaProviders}from"./media/index.js";import{homedir}from"node:os";import{join}from"node:path";import{RuntimeProductionAuthority}from"./runtime/runtime-production-authority.js";import{FileRuntimeCommandJournal}from"./runtime/runtime-command-journal.js";import{FileRuntimePoisonDeliveryJournal}from"./runtime/runtime-poison-delivery-journal.js";import{OpenClawNativeMcpProjection}from"./acp-gateway/openclaw-native-mcp-projection.js";import{LocalEncryptedCredentialReferenceVault}from"./acp-gateway/mcp-credential-reference-port.js";import{AgentGatewayGrantJwksReadinessAdapter,RuntimeServiceTransportReadinessAdapter,createRuntimeServiceProductionCandidate}from"./runtime/runtime-service-production-candidate.js";import{MessageServiceCronDeliveryHttpAuthority}from"./runtime/message-service-cron-delivery-http-authority.js";import{AgentMappingRegistry}from"./runtime/agent-mapping-registry.js";import{OpenClawRuntimeProcessOwner}from"./runtime/openclaw-runtime-process-owner.js";import{AgentGatewayHttpProjectionTransport,AgentGatewayTemplateOriginReporter,AgentProjectionProduction,OpenClawAgentProjectionSource}from"./runtime/agent-projection-production.js";import{MessageServiceRunOutputChannel}from"./runtime/message-service-run-output-channel.js";import{AssistantMessageCommitRouter}from"./runtime/assistant-message-commit-router.js";import{ToolResultObservationRouter}from"./runtime/tool-result-observation-router.js";import{ModelCallObservationRouter,registerModelCallObservationHooks}from"./runtime/model-call-observability.js";import{registerArouterLatencyRouting}from"./runtime/arouter-latency-routing.js";import{createPeerRunOutputRouterFactory}from"./runtime/run-output-composition.js";const PLUGIN_VERSION=resolvePluginVersion();let agentKeys=null,sharedCanvasState=null,sharedAcpServer=null;const sharedSessionWriteGate=new SessionWriteGate,sharedPromptRunStore=new PromptRunStore;let lastOpenclawConfig;const sharedMcpNativeProjectionNudge=createMcpNativeProjectionNudge({getActiveImRunCount:()=>sharedPromptRunStore.size(),nudge:()=>{sharedAcpServer?.nudgeMcpNativeProjection()}}),sharedImHotPathDeferral=createImHotPathDeferral({getActiveImRunCount:()=>sharedPromptRunStore.size()});sharedPromptRunStore.setOnBecameIdle(()=>{sharedMcpNativeProjectionNudge.flushIfIdle({fingerprint:fingerprintMcpServers(lastOpenclawConfig)}),sharedImHotPathDeferral.notifyIdle()});const sharedAssistantMessageCommitRouter=new AssistantMessageCommitRouter,sharedToolResultObservationRouter=new ToolResultObservationRouter,sharedModelCallObservationRouter=new ModelCallObservationRouter;let sharedSessionMutationCoordinator,sharedRuntimeAuthority;const sharedMSConnectionStatus=new MSConnectionStatusSnapshot,sharedAgentMappings=new AgentMappingRegistry;let sharedGatewayClient,sharedRuntimeOwner,sharedAgentProjection,sharedRuntimeAgentCapabilities,sharedMcpProjection,sharedMcpCredentialVault;const sharedMessageClientOwner=new BeeosClawMessageClientProcessOwner;let sharedActiveMessageConsumer,sharedInboundExecutionJournal;const sharedTerminalManager=new TerminalManager;let _initializing=!1,_pluginRegisteredAt=0,_fullRuntimeRegistered=!1;const cachedCapabilityTools=[];let rawRegisterTool,wrappedRegisterTool;const ensureRegisterToolCache=e=>{rawRegisterTool||(rawRegisterTool=e.registerTool.bind(e),wrappedRegisterTool=e=>(cachedCapabilityTools.push(e),rawRegisterTool(e)),e.registerTool=wrappedRegisterTool)},ED25519_PKCS8_PREFIX=Buffer.from("302e020100300506032b657004220420","hex");function agentKeyPairToIdentity(e){return{publicKeyBase64:()=>Buffer.from(e.publicKey).toString("base64"),sign:async t=>{const{createPrivateKey:r,sign:o}=await import("node:crypto"),n=r({key:32===e.privateKey.length?Buffer.concat([ED25519_PKCS8_PREFIX,Buffer.from(e.privateKey)]):Buffer.from(e.privateKey),format:"der",type:"pkcs8"});return new Uint8Array(o(null,Buffer.from(t),n))}}}let sharedPluginContext=null,unhandledRejectionObserverInstalled=!1;function installUnhandledRejectionObserver(e,t,r){unhandledRejectionObserverInstalled||(unhandledRejectionObserverInstalled=!0,process.on("unhandledRejection",r=>{const o=r instanceof Error?r.message:String(r),n=r instanceof Error?r.stack:void 0;e.logger.error?.(`[beeos-claw] unhandledRejection: ${o}${n?`\n${n}`:""}`),t({component:"plugin",domain:"fatal",name:"unhandled_rejection",severity:"error",summary:o,error:{code:"unhandled_rejection",message:o,...n?{stack:n}:{}}})}),process.on("uncaughtException",r=>{const o=r instanceof Error?r.message:String(r);e.logger.error?.(`[beeos-claw] uncaughtException: ${r instanceof Error?r.stack??r.message:String(r)}`),t({component:"plugin",domain:"fatal",name:"uncaught_exception",severity:"error",summary:o,error:{code:"uncaught_exception",message:o}})}))}function initializePlugin(e){let t,r;try{t=e.runtime?.config?.loadConfig?.()}catch{}try{r=resolveConfigWithMeta({pluginConfig:e.pluginConfig??{},openclawConfig:t})}catch(t){return e.logger.error?.(`[beeos-claw] Failed to resolve config: ${t instanceof Error?t.message:String(t)}`),null}const o=r.config,n=buildThirdPartyChannelSet(t);n.size>0&&e.logger.info?.(`[beeos-claw] Third-party channels detected: ${[...n].join(", ")}`);const a=o.log.enabled?createObsEventWriter({dir:o.log.dir,redaction:{gatewayToken:o.gateway.token},logger:e.logger}):()=>{};a({component:"plugin",domain:"lifecycle",name:"lifecycle.startup",severity:"info",payload:{pluginVersion:PLUGIN_VERSION,pid:process.pid,nodeVersion:process.version,cwd:process.cwd()}}),a({component:"plugin",domain:"config",name:"config.resolved",severity:"info",payload:{config:o,sources:r.sources,openclawConfigPath:r.openclawConfigPath}});for(const t of r.validation)a({component:"plugin",domain:"config",name:"config.validation",severity:t.severity,summary:t.message,error:{code:t.code,message:t.message,nextSteps:t.nextSteps}}),"error"===t.severity?e.logger.error?.(`[beeos-claw] ${t.message}`):e.logger.warn?.(`[beeos-claw] ${t.message}`);if(r.validation.some(e=>"error"===e.severity)&&e.logger.error?.("[beeos-claw] Plugin has configuration errors — some features may not work."),o.keyFile)try{agentKeys=loadAgentKeyPair(o.keyFile);const t=Buffer.from(agentKeys.publicKey).toString("base64");e.logger.info?.(`[beeos-claw] v${PLUGIN_VERSION} registering (publicKey: ${t})`)}catch(t){e.logger.warn?.(`[beeos-claw] Failed to load agent key pair: ${t}`),e.logger.info?.(`[beeos-claw] v${PLUGIN_VERSION} registering (no key pair — signed requests disabled)`)}else e.logger.info?.(`[beeos-claw] v${PLUGIN_VERSION} registering (no keyFile configured)`);return agentKeys&&o.agentGatewayUrl&&(setUploadDeps({gatewayUrl:o.agentGatewayUrl,keys:agentKeys,logger:e.logger}),registerLocalFileExtractor(browserScreenshotExtractor),e.logger.info?.("[beeos-claw] Upload deps + browser screenshot extractor registered")),lastOpenclawConfig=t,{cfg:o,writeObs:a,thirdPartyChannels:n,openclawConfig:t}}function registerUploadTools(e,t,r){const{cfg:o,writeObs:n}=t;cleanupUploadToolResultCache().catch(t=>{e.logger.warn?.(`[beeos_upload_file] cache cleanup failed on startup: ${String(t)}`)}),e.registerTool({name:BEEOS_UPLOAD_TOOL_NAME,description:BEEOS_UPLOAD_TOOL_DESCRIPTION,parameters:BEEOS_UPLOAD_TOOL_SCHEMA,async execute(t,r,a){e.logger.debug?.(`[beeos_upload_file] toolCallId=${t} params=${JSON.stringify(r)}`);const i=await executeUpload(r,o,agentKeys,a);return e.logger.debug?.(`[beeos_upload_file] toolCallId=${t} ok=${i.ok}`),n({component:"upload-tool",domain:"tool",name:i.ok?"upload.success":"upload.failure",severity:i.ok?"info":"warn",payload:{toolCallId:t,ok:i.ok,fileCount:i.files?.length??0,error:i.error}}),(async r=>{try{await writeUploadToolResult(t,BEEOS_UPLOAD_TOOL_NAME,r),await cleanupUploadToolResultCache()}catch(r){e.logger.warn?.(`[beeos_upload_file] persist cache failed toolCallId=${t}: ${String(r)}`)}return r})(i)}}),e.registerTool({name:BEEOS_DESKTOP_SCREENSHOT_TOOL_NAME,description:BEEOS_DESKTOP_SCREENSHOT_TOOL_DESCRIPTION,parameters:BEEOS_DESKTOP_SCREENSHOT_TOOL_SCHEMA,async execute(t,r,a){e.logger.debug?.(`[beeos_desktop_screenshot] toolCallId=${t} params=${JSON.stringify(r)}`);const i=await executeDesktopScreenshot(r,o,agentKeys,a);return e.logger.debug?.(`[beeos_desktop_screenshot] toolCallId=${t} ok=${i.ok}`),n({component:"desktop-screenshot-tool",domain:"tool",name:i.ok?"desktop_screenshot.success":"desktop_screenshot.failure",severity:i.ok?"info":"warn",payload:{toolCallId:t,ok:i.ok,path:i.path,fileCount:i.files?.length??0,error:i.error}}),(async r=>{try{await writeUploadToolResult(t,BEEOS_DESKTOP_SCREENSHOT_TOOL_NAME,r),await cleanupUploadToolResultCache()}catch(r){e.logger.warn?.(`[beeos_desktop_screenshot] persist cache failed toolCallId=${t}: ${String(r)}`)}return r})(i)}}),e.registerHttpRoute({path:"/beeos/files/stage",auth:"gateway",async handler(e,r){await handleFileStage(e,r,t.cfg)}}),e.registerHttpRoute({path:"/beeos/status",auth:"plugin",handler(e,t){handleStatusRequest(e,t,{publicKey:agentKeys?.publicKey??null,pluginVersion:PLUGIN_VERSION})}}),e.registerTool({name:BEEOS_LIST_FILES_TOOL_NAME,description:BEEOS_LIST_FILES_TOOL_DESCRIPTION,parameters:BEEOS_LIST_FILES_TOOL_SCHEMA,async execute(t,r,a){e.logger.debug?.(`[beeos_list_files] toolCallId=${t} params=${JSON.stringify(r)}`);const i=await executeListFiles(r,o,agentKeys,a);return n({component:"file-list-tool",domain:"tool",name:i.ok?"list_files.success":"list_files.failure",severity:i.ok?"info":"warn",payload:{toolCallId:t,ok:i.ok,fileCount:i.files?.length??0,error:i.error}}),i}}),e.registerTool({name:BEEOS_FETCH_FILE_TOOL_NAME,description:BEEOS_FETCH_FILE_TOOL_DESCRIPTION,parameters:BEEOS_FETCH_FILE_TOOL_SCHEMA,async execute(t,r,a){e.logger.debug?.(`[beeos_fetch_file] toolCallId=${t} params=${JSON.stringify(r)}`);const i=await executeFetchFile(r,o,agentKeys,{signal:a,resolvePath:e.resolvePath});return n({component:"file-fetch-tool",domain:"tool",name:i.ok?"fetch_file.success":"fetch_file.failure",severity:i.ok?"info":"warn",payload:{toolCallId:t,ok:i.ok,path:i.path,size:i.size,error:i.error}}),i}})}function registerConnectorMcpProxy(e,t){e.registerHttpRoute({path:CONNECTOR_MCP_PROXY_PATH,auth:"plugin",async handler(r,o){await handleConnectorMcpRequest(r,o,{agentGatewayUrl:t.cfg.agentGatewayUrl,agentKeys:agentKeys,logger:e.logger})}})}function registerServices(e,t,r){const{cfg:o,writeObs:n,thirdPartyChannels:a}=t;if("function"!=typeof e.on){if(o.messageBus.enabled)throw new Error("openclaw_before_message_write_hook_unavailable")}else registerModelCallObservationHooks((t,r)=>e.on(t,r),sharedModelCallObservationRouter),e.on("before_message_write",(t,r)=>{const o=t.sessionKey??r.sessionKey;if(!o)return void e.logger.debug?.("[before_message_write] ignored assistant write without sessionKey");sharedAssistantMessageCommitRouter.route(o,t.message)||e.logger.debug?.(`[before_message_write] no active IM commit target sessionKey=${o} active_targets=${sharedAssistantMessageCommitRouter.size()}`)}),e.on("after_tool_call",(t,r)=>{const o=r.sessionKey,n=t.toolCallId??r.toolCallId,a=t.toolName||r.toolName;if(!o||!n||!a)return void e.logger.debug?.(`[after_tool_call] ignored structured result without correlation sessionKey=${o??"missing"} toolCallId=${n??"missing"}`);sharedToolResultObservationRouter.route(o,{toolCallId:n,toolName:a,rawResult:t.result,isError:Boolean(t.error)})||e.logger.debug?.(`[after_tool_call] no active IM observation target sessionKey=${o} active_targets=${sharedToolResultObservationRouter.size()}`)});const i=agentKeys?(e,t)=>agentFetch(e,agentKeys,t):void 0,s=agentKeys?(e,t)=>agentBoundFetch(e,agentKeys,t):void 0,l=agentKeys?Buffer.from(agentKeys.publicKey).toString("base64"):void 0,c=join(process.env.OPENCLAW_HOME||join(homedir(),".openclaw"),"beeos"),g=sharedInboundExecutionJournal??=new FileInboundExecutionJournal(join(c,"inbound-execution-journal.v1.jsonl"));if(sharedMcpProjection??=new OpenClawNativeMcpProjection,!sharedMcpCredentialVault&&agentKeys&&l&&(sharedMcpCredentialVault=new LocalEncryptedCredentialReferenceVault(join(c,"mcp-credential-vault.json"),l,agentKeys.privateKey)),!sharedRuntimeAuthority&&agentKeys&&o.agentGatewayUrl&&i&&s&&(sharedRuntimeAuthority=new RuntimeProductionAuthority({stateDirectory:c,agentGatewayUrl:o.agentGatewayUrl,agentFetch:i,boundAgentFetch:s,keys:agentKeys,logger:e.logger,getMSConnectionStatus:e=>sharedMSConnectionStatus.get(e)})),sharedGatewayClient??=new GatewayClient({url:o.gateway.url,token:o.gateway.token||"",clientId:o.gateway.clientId,openclawCanvasCompat:!!o.openclawCanvasCompat,retry:{baseMs:o.retry.baseMs,maxMs:o.retry.maxMs,maxAttempts:o.retry.maxAttempts}}),sharedRuntimeAgentCapabilities??={canvas:o.canvasEnabled,terminal:!1,webSsh:!1,fileSystem:!0,image:!0,audio:!1,mcp:!0,cron:!0,streaming:!0,pushNotifications:o.messageBus.enabled},!sharedAgentProjection&&sharedRuntimeAuthority&&i&&o.agentGatewayUrl){const t=sharedRuntimeAuthority;sharedAgentProjection=new AgentProjectionProduction({authority:t,stateFile:join(t.stateDirectory,"runtime-agent-projection.json"),journal:t.journal,identities:t.identities,source:new OpenClawAgentProjectionSource(()=>sharedGatewayClient),transport:new AgentGatewayHttpProjectionTransport({agentGatewayUrl:o.agentGatewayUrl,boundAgentFetch:t.boundAgentFetch,getRuntimeLeaseCredential:e=>{const r=t.authorityLease;if(!r||r.instanceId!==e.instanceId||r.leaseId!==e.leaseId||r.runtimeEpoch!==e.runtimeEpoch||r.handlerIdentity!==e.handlerIdentity)throw new Error("agent projection fenced by current runtime lease");return r.runtimeLeaseCredential},onAccepted:t=>{0!==t.size&&sharedAgentMappings.replace(t).catch(t=>{e.logger.warn?.("[agent-projection] mapping publication failed: "+(t instanceof Error?t.message:String(t)))})}}),capabilities:sharedRuntimeAgentCapabilities,logger:e.logger})}sharedRuntimeOwner??=new OpenClawRuntimeProcessOwner(sharedGatewayClient,sharedAgentProjection,e.logger);const d=sharedRuntimeOwner;sharedSessionMutationCoordinator??=new SessionMutationCoordinator(sharedSessionWriteGate,new OpenClawSessionWriteQuiescence(e.logger,{agentId:o.gateway.agentId||"main"}),e.logger,{onRecoveryRequired:t=>{scheduleProcessRuntimeRestart(t,e.logger)}});const u=sharedSessionMutationCoordinator;if(!sharedAcpServer){const t=sharedRuntimeAuthority&&o.agentGatewayUrl?new AgentGatewayTemplateOriginReporter({agentGatewayUrl:o.agentGatewayUrl,boundAgentFetch:sharedRuntimeAuthority.boundAgentFetch}):void 0;sharedAcpServer=new ACPServer(o,e.logger,u,n,i,l,a,sharedTerminalManager,sharedRuntimeAuthority,void 0,sharedMcpProjection,sharedMcpCredentialVault,s,{gateway:d.client,agentProjection:sharedAgentProjection,markAgentProjectionDirty:()=>sharedAgentProjection?.markDirty(),templateOriginReporter:t,agentMappings:sharedAgentMappings,agentCapabilities:sharedRuntimeAgentCapabilities,createRunOutputRouter:createPeerRunOutputRouterFactory(new MessageServiceRunOutputChannel({getClient:()=>sharedMessageClientOwner.current?.client??null,logger:e.logger,writeObs:n})),runExclusive:(t,r,o)=>sharedImHotPathDeferral.runExclusive(t,r,e.logger,o)})}const m=sharedAcpServer,p=()=>sharedAgentMappings.snapshot()?.values().next().value;r.current=m,sharedMcpNativeProjectionNudge.request({registrationMode:e.registrationMode,fingerprint:fingerprintMcpServers(t.openclawConfig),logger:e.logger});const y=sharedPromptRunStore,{enabled:h,principalId:f,a2aEnabled:w}=o.messageBus;let C;o.agentGatewayUrl&&agentKeys&&(C=createTokenProvider({agentGatewayUrl:o.agentGatewayUrl,identity:agentKeyPairToIdentity(agentKeys)}));const R=process.env.MESSAGE_RUNTIME_DELIVERY_KEY?.trim(),v=f||process.env.BEEOS_PRINCIPAL_ID||"",b=C?sharedMessageClientOwner.configure({tokenProvider:C,identityId:v,...o.messageBus.serviceUrl?{serviceUrl:o.messageBus.serviceUrl}:{},logger:e.logger,...sharedRuntimeAuthority&&R?{runtimeDelivery:{scopedDeliveryKey:R,authority:{currentLease:()=>{const e=sharedRuntimeAuthority?.authorityLease,t=sharedRuntimeAuthority?.currentConsumerBinding();return e&&t?{...e,journalStoreId:t.journalStoreId,journalGeneration:t.journalGeneration}:null}}}}:{}}):void 0,_=sharedRuntimeAuthority,S=o.messageBus.serviceUrl;if(b&&_&&S&&R){const e=b.nodeTransport;m.setCronConversationDelivery({deliver:async t=>{const r=_.authorityLease;if(!r)throw new Error("current runtime authority lease unavailable for cron delivery");const o=new MessageServiceCronDeliveryHttpAuthority({messageServiceUrl:S,fetchAuthorized:async(t,o)=>await e.fetchRuntimeChannel(t,{runtimeLeaseCredential:r.runtimeLeaseCredential},o)});return await o.deliver({...t,runtimeLeaseCredential:r.runtimeLeaseCredential})}})}else m.setCronConversationDelivery(void 0);const E=sharedRuntimeAuthority?.journal??new FileRuntimeCommandJournal(join(c,"runtime-command-journal.json"),void 0,void 0,{onMaintenanceError:t=>e.logger.error?.(`[runtime-journal] event=checkpoint_failed trigger=threshold error=${t instanceof Error?t.message:String(t)}`)}),O=d.createRegistrationToken();e.registerService({id:"beeos-acp",async start(){const t=Date.now(),r=_pluginRegisteredAt?t-_pluginRegisteredAt:-1;e.logger.info?.(`[beeos-claw] beeos-acp service start() begin, sincePluginRegister=${r}ms, bridge.url=${o.bridge.url||"(empty)"}`);const n=await createRuntimeServiceProductionCandidate({...o.agentGatewayUrl&&i?{authorizationAdapter:new AgentGatewayGrantJwksReadinessAdapter({agentGatewayUrl:o.agentGatewayUrl})}:{},...b&&o.agentGatewayUrl&&s&&R?{transportAdapter:new RuntimeServiceTransportReadinessAdapter({agentGatewayUrl:o.agentGatewayUrl,messageService:b.nodeTransport,messageClient:b.client,configuredServiceUrl:o.messageBus.serviceUrl,boundAgentFetch:s,scopedDeliveryKey:R})}:{},commandJournal:E,poisonJournal:new FileRuntimePoisonDeliveryJournal(join(c,"runtime-poison-deliveries.json")),onConsumerDeliveryError:t=>{const r=t instanceof Error?`${t.name}: ${t.message}${"status"in t?` status=${t.status}`:""}${"code"in t?` code=${t.code}`:""}\n${t.stack??""}`:String(t);e.logger.error?.(`[runtime-service] delivery worker failed before acknowledgement; delivery retained for retry: ${r}`)},onConsumerLeaseStarvation:({starvedForMs:t,recovered:r})=>{const o=Math.round(t/1e3);r?e.logger.info?.(`[runtime-service] runtime authority lease restored after ${o}s without delivery reads`):e.logger.warn?.(`[runtime-service] no runtime authority lease for ${o}s; delivery reads suspended, queued runtime operations (model switch, session control) will not be consumed`)},onConsumerRequestFailure:t=>e.logger.error?.(`[runtime-service] request failed operationId=${t.operationId} method=${t.method}: ${t.error instanceof Error?t.error.stack??t.error.message:String(t.error)}`)});if(sharedRuntimeAuthority?.setRuntimeServiceMethods("ready"===n.status?n.serviceMethods:[]),sharedRuntimeAuthority?.setRuntimeResourceBindingContractRevision("ready"===n.status?n.resourceBindingContractRevision:void 0),m.setRuntimeRpcConsumerFactory("ready"===n.status?n.factory:void 0),"unavailable"===n.status?e.logger.warn?.("[runtime-service] capability fenced: "+n.blockers.map(e=>e.id).join(",")):n.capabilityBlockers.length&&e.logger.warn?.("[runtime-service] method capability fenced: "+n.capabilityBlockers.map(e=>e.method).join(",")),!o.bridge.url&&o.agentGatewayUrl&&i){const t=Date.now(),r=await discoverBridgeFromGW(o.agentGatewayUrl,i);if(e.logger.info?.(`[beeos-claw] bridge discovery took ${Date.now()-t}ms result=${r?.url??"null"}`),r)o.bridge.url=r.url,writeDiscoveryCache({bridge:{url:r.url,discoveredAt:Date.now()}}),e.logger.info?.(`[beeos-claw] Bridge URL discovered from Agent Gateway: ${r.url}`);else{const t=readDiscoveryCache();t?.bridge?.url?(o.bridge.url=t.bridge.url,e.logger.info?.(`[beeos-claw] Bridge URL from cache (GW unreachable): ${t.bridge.url}`)):e.logger.debug?.("[beeos-claw] Bridge URL not available (no GW, no cache) — bridge disabled")}}await d.acquire(O);try{await m.start(o.bridge.url||void 0)}catch(e){throw await d.release(O),e}e.logger.info?.(`[beeos-claw] ACP Server started (total service start: ${Date.now()-t}ms)`)},async stop(){try{await d.release(O)}finally{await m.stop()}await E.checkpoint(),e.logger.info?.("[runtime-service] command journal checkpoint completed"),e.logger.info?.("[beeos-claw] ACP Server stopped")}});if(h&&void 0!==C&&C){const t=sharedMessageClientOwner.createRegistrationToken(),r=async(e,t)=>{const r=await C(e);if(!r.identityId||r.identityId!==e)throw new Error("Message SDK token identity did not match the current runtime authority");return{principalId:e,runtimeEpoch:t}},a=(e,t,r)=>({getLocalAgentId:()=>t,resolveLocalAgentId:e=>e===r?t:void 0,getDefaultPlatformAgent:()=>({instanceId:e.principalId}),ensureImInboundSession:(e,t,r,o)=>{m.ensureImInboundSession(e,t,r,o)},chatAbort:(e,t)=>m.chatAbort(e,t)}),i=sharedRuntimeAuthority?new RuntimeAuthorityMessageBusLifecycle({authority:sharedRuntimeAuthority,logger:e.logger,bus:{connect:async i=>{await sharedMessageClientOwner.acquire(t,async t=>{const s=await r(i.instanceId,i.runtimeEpoch),l=t.client,c={leaseId:i.leaseId,runtimeEpoch:i.runtimeEpoch};sharedMSConnectionStatus.bind(c);const d=new Map,p=new MessageProjectionInboundConsumer({client:l,bindings:()=>{const t=sharedRuntimeAuthority?.authorityLease;if(!t||t.instanceId!==s.principalId||t.runtimeEpoch!==s.runtimeEpoch)return[];const r=sharedAgentMappings.snapshot();return r?[...r.entries()].flatMap(([r,i])=>{if(!i.id||i.instanceId!==s.principalId)return[];let c=d.get(i.id);return c&&c.localAgentId===r||(c={localAgentId:r,inbound:createImInboundHandler({runtime:e.runtime,pluginConfig:e.pluginConfig,client:l,localServer:a(s,r,i.id),promptRunStore:y,waitIfExclusive:(t,r)=>sharedImHotPathDeferral.waitIfExclusive(t,e.logger,r).then(()=>{}),assistantMessageCommitRouter:sharedAssistantMessageCommitRouter,toolResultObservationRouter:sharedToolResultObservationRouter,modelCallObservationRouter:sharedModelCallObservationRouter,sessionMutationCoordinator:u,logger:e.logger,writeObs:n,principalIdOverride:s.principalId,activeRunAuthority:m.getActiveRunAuthority(),...o.agentGatewayUrl&&agentKeys?{completeA2ATask:(t,r,n)=>completeA2ATask({agentGatewayUrl:o.agentGatewayUrl,agentKeys:agentKeys,logger:e.logger},t,r,n)}:{}})},d.set(i.id,c)),[{principalId:s.principalId,targetAgentId:i.id,runtimeEpoch:t.runtimeEpoch,inbound:c.inbound}]}):[]},executionJournal:g,logger:e.logger}),h=sharedAgentMappings.subscribe(()=>{sharedActiveMessageConsumer===p&&p.refresh()}),f=sharedRuntimeAuthority?.subscribeAuthority(()=>{sharedActiveMessageConsumer===p&&p.refresh()})??(()=>{}),w=new MessageClientTerminalStateRecovery({logger:e.logger,isCurrent:()=>{if(sharedActiveMessageConsumer!==p)return!1;const e=sharedRuntimeAuthority?.authorityLease;return null!=e&&e.instanceId===s.principalId&&e.runtimeEpoch===s.runtimeEpoch&&e.leaseId===i.leaseId&&e.handlerIdentity===i.handlerIdentity},reconnect:async()=>{if("failed"===l.getSnapshot().connection){await l.connect();try{await m.rebindRuntimeDelivery()}catch(t){e.logger.warn?.(`[beeos-claw] runtime Service delivery rebind failed after MessageClient recovery: ${t instanceof Error?t.message:String(t)}`)}}}});let C=l.getSnapshot().connection;sharedMSConnectionStatus.setIfCurrent(c,C);const R=l.subscribe(()=>{const t=l.getSnapshot();t.connection!==C&&(C=t.connection,sharedMSConnectionStatus.setIfCurrent(c,t.connection),"connected"===t.connection?(w.notifyConnected(),e.logger.info?.(`[beeos-claw] connection transport=message_service_realtime state=connected localAgentId=${m.getLocalAgentId()} principalId=${s.principalId}`)):"failed"===t.connection?(e.logger.warn?.(`[beeos-claw] connection transport=message_service_realtime state=failed principalId=${s.principalId} recoveryError=${t.recoveryError??"unknown"}`),w.notifyFailed()):"disconnected"!==t.connection&&"reconnecting"!==t.connection||e.logger.warn?.(`[beeos-claw] connection transport=message_service_realtime state=${t.connection} principalId=${s.principalId}; SDK owns reconnect/recovery`))});sharedActiveMessageConsumer=p,p.start();try{await l.connect()}catch(e){throw sharedActiveMessageConsumer===p&&(sharedActiveMessageConsumer=void 0),sharedMSConnectionStatus.resetIfCurrent(c),w.dispose(),await p.stop(),h(),f(),R(),await l.disconnect().catch(()=>{}),e}try{await m.rebindRuntimeDelivery()}catch(t){e.logger.warn?.(`[beeos-claw] runtime Service delivery rebind failed after MessageClient connect: ${t instanceof Error?t.message:String(t)}`)}return async()=>{sharedActiveMessageConsumer===p&&(sharedActiveMessageConsumer=void 0),sharedMSConnectionStatus.resetIfCurrent(c),w.dispose(),await p.stop(),f(),R(),await l.disconnect(),e.logger.info?.("[beeos-claw] MessageClient disconnected")}})},disconnect:async()=>{await sharedMessageClientOwner.release(t)}}}):void 0;e.registerService({id:"beeos-message-bus",async start(){i?i.start():e.logger.warn?.("[beeos-claw] Message bus requires runtime authority; waiting is unavailable without Agent Gateway runtime identity")},async stop(){await(i?.stop())}})}else h?e.logger.warn?.("[beeos-claw] Message bus enabled but Agent Gateway token provider is unavailable — disabled"):e.logger.info?.(`[beeos-claw] Message bus disabled (enabled=${h})`);if(!1!==w&&o.agentGatewayUrl){const t=e=>m.getGatewaySessionKeyForToolCall(e),r=e=>m.getMentionsForToolCall(e),n=(e,t)=>{m.broadcastNotification(e,t)},a=new TaskHandleRegistry;registerA2ATools(e,{agentGatewayUrl:o.agentGatewayUrl,agentKeys:agentKeys,logger:e.logger},a,t,r,n,()=>p()?.instanceId),e.logger.info?.("[beeos-claw] A2A tools registered (beeos_discover_agents, beeos_call_agent) via REST")}if(o.canvasEnabled){sharedCanvasState||(sharedCanvasState=new CanvasStateManager);const t=sharedCanvasState;if(o.agentGatewayUrl&&agentKeys){let r;t.setLogger(e.logger);{const e=o.agentGatewayUrl,t=agentKeys;r=async()=>{const r=await agentFetch(`${e}/api/v1/canvas/token`,t,{method:"POST"});if(!r.ok){const e=await r.text().catch(()=>"");throw new Error(`Canvas token returned ${r.status}: ${e}`)}return r.json()}}const n=n=>{const a=new CanvasRelayClient({canvasRelayUrl:o.canvasRelayUrl,agentKeys:agentKeys,agentId:n.id,displayName:n.displayName,avatarUrl:n.avatarUrl,tokenProvider:r,onConnected:()=>m.flushPendingCanvasLinks(),onReconnect:()=>m.onRelayReconnected(),logger:e.logger});t.setRelayClient(a),t.setAgentId(n.id),m.wireRelayActionHandler(),a.connect(),e.logger.info?.(`[beeos-claw] Canvas Relay multiplexed client connected (agentId=${n.id}, name=${n.name??"none"})`)};e.registerService(createCanvasRelayService({getAgent:p,onceAgentReady:e=>sharedAgentMappings.onceReady(e),initRelay:n,stopRelay:()=>t.getRelayClient()?.disconnect(),logger:e.logger})),e.logger.info?.("[beeos-claw] Canvas Relay configured")}else e.logger.warn?.("[beeos-claw] Canvas Relay unavailable: agentGatewayUrl or agentKeys missing");const r={stateManager:t,logger:e.logger,writeObs:e=>n(e),isCanvasActive:e=>!!e&&m.isCanvasActiveForGatewayKey(e),getActiveSessionId:e=>m.getSessionIdForToolCall(e),getPlatformAgentId:e=>m.getPlatformAgentIdForToolCall(e),getGatewaySessionKey:e=>m.getGatewaySessionKeyForToolCall(e),waitForAgentSync:()=>sharedAgentMappings.waitForSynchronization()},i=new FormSchemaRegistry,s=buildCronSchemas(a);i.register(s.create),i.register(s.update);const l=new ResourceDescriptorRegistry;l.register(CRON_RESOURCE);const c=e=>async(t,r,o)=>(m.preMapToolCallSession(t),e(t,r));e.registerTool({name:RENDER_UI_TOOL_NAME,description:RENDER_UI_TOOL_DESCRIPTION,parameters:RENDER_UI_TOOL_SCHEMA,execute:c(createRenderUiHandler(r))}),e.registerTool({name:CLOSE_UI_TOOL_NAME,description:CLOSE_UI_TOOL_DESCRIPTION,parameters:CLOSE_UI_TOOL_SCHEMA,execute:c(createCloseUiHandler(r))}),m.setCanvasState(t,{schemaRegistry:i,descriptorRegistry:l});const g=m.getFormBuilder();g&&e.registerTool({name:RENDER_FORM_TOOL_NAME,description:buildRenderFormDescription(i),parameters:RENDER_FORM_TOOL_SCHEMA,execute:c(createRenderFormHandler({registry:i,builder:g,logger:e.logger}))});const d=m.getResourceRenderer();if(d){generateActionTools(l,e,async(e,t)=>m.gatewayRequest(e,t),d,e.logger)}const u=buildToolRoutingHint(BEEOS_FILE_RESOURCE_ROUTING_RULE,BEEOS_DESKTOP_SCREENSHOT_TOOL_NAME),y=new Set([RENDER_UI_TOOL_NAME,CLOSE_UI_TOOL_NAME,RENDER_FORM_TOOL_NAME]),h=256,f=new Map;if("function"==typeof e.on){try{e.on("before_prompt_build",(e,t)=>{if(t?.sessionKey){if(f.size>=h&&!f.has(t.sessionKey)){const e=f.keys().next().value;void 0!==e&&f.delete(e)}f.set(t.sessionKey,t.channelId)}const r=isThirdPartyChannel(a,{sessionKey:t?.sessionKey,channelId:t?.channelId});if(!t?.sessionKey&&!r)return{appendSystemContext:u};const o=Boolean(t?.sessionKey&&m.isCanvasActiveForGatewayKey(t.sessionKey));return{appendSystemContext:buildAppendSystemContext({thirdParty:r,canvasActive:o,canvasSystemPrompt:`${CANVAS_PRESENTATION_GATE}\n\n${CANVAS_SYSTEM_PROMPT}`,toolRoutingHint:u})}}),e.logger.debug?.("[beeos-claw] Canvas system prompt hook registered: before_prompt_build")}catch{e.logger.debug?.("[beeos-claw] before_prompt_build hook not supported")}try{e.on("before_tool_call",(e,t)=>{if(t?.sessionKey&&m.setPendingToolCallSessionKey(t.sessionKey),!y.has(e.toolName))return;const r=t?.sessionKey?f.get(t.sessionKey):void 0;return isThirdPartyChannel(a,{sessionKey:t?.sessionKey,channelId:r})?{block:!0,blockReason:"Canvas tools are not available in this channel. Respond with plain text only."}:t?.sessionKey?m.isCanvasActiveForGatewayKey(t.sessionKey)?void 0:{block:!0,blockReason:"Canvas is closed by user. Respond with plain text instead."}:{block:!0,blockReason:"Canvas session unknown. Respond with plain text instead."}}),e.logger.debug?.("[beeos-claw] Canvas tool guard registered: before_tool_call")}catch{e.logger.debug?.("[beeos-claw] before_tool_call hook not supported")}}e.logger.info?.("[beeos-claw] Canvas tools registered (render_ui, close_ui, render_form + auto-action tools)")}else e.logger.info?.("[beeos-claw] Canvas disabled by config (canvasEnabled=false)");if(o.bridge.shell.enabled&&!0===o.legacyTerminalWsEnabled){const t=createTerminalWebSocketService({config:{port:o.terminalWsPort,token:agentKeys?Buffer.from(agentKeys.publicKey).toString("base64"):"",allowedOrigins:o.terminalWsAllowedOrigins},terminalMgr:sharedTerminalManager,logger:e.logger,writeObs:n});e.registerService({id:"beeos-terminal-ws",start(){t.start(),e.logger.info?.(`[beeos-claw] Terminal WS started on port ${o.terminalWsPort}`)},stop(){t.stop(),e.logger.info?.("[beeos-claw] Terminal WS stopped")}})}else o.bridge.shell.enabled?e.logger.info?.("[beeos-claw] Legacy static-token Terminal WS disabled (default)"):e.logger.info?.("[beeos-claw] Terminal/shell disabled by config (bridge.shell.enabled=false)");if(t.openclawConfig){const r=mirrorOpenClawConfig(t.openclawConfig);r.copied?e.logger.info?.(`[beeos-claw] mirrored openclaw config to ${r.destinationPath}`):e.logger.debug?.(`[beeos-claw] openclaw config mirror skipped: ${r.reason}`)}}const plugin={id:"beeos-claw",name:"beeos-claw",description:"BeeOS platform bridge plugin for OpenClaw. Provides file upload/download, ACP bridge, and observability.",version:PLUGIN_VERSION,register(e){if(_initializing)return;const t=planPluginRegistration(e.registrationMode,_fullRuntimeRegistered);if("skip"!==t){_initializing=!0;try{if(!hasRuntimeReplyApi(e.runtime))throw e.logger.error?.("[beeos-claw] FATAL: OpenClaw runtime.channel.reply.dispatchReplyFromConfig not exposed. Upgrade OpenClaw or revert beeos-claw."),new Error("openclaw_runtime_reply_api_unavailable");const r=(t,r)=>{registerUploadTools(e,t,r),registerConnectorMcpProxy(e,t),registerArouterLatencyRouting(e),registerArouterMediaProviders(e)};if("capabilities"===t&&sharedPluginContext){if(cachedCapabilityTools.length>0){const t=resolveCapabilityToolReplay({currentRegisterTool:e.registerTool,wrappedRegisterTool:wrappedRegisterTool,originalRegisterTool:rawRegisterTool}),r=replayCachedPluginTools(r=>t.call(e,r),cachedCapabilityTools);return registerArouterLatencyRouting(e),registerArouterMediaProviders(e),void e.logger.info?.(`[beeos-claw] capability re-register mode=${e.registrationMode} tools_only count=${r}`)}const t={current:sharedAcpServer??null};return r(sharedPluginContext,t),registerServices(e,sharedPluginContext,t),void e.logger.info?.(`[beeos-claw] capability re-register mode=${e.registrationMode} capabilities_only`)}const o=initializePlugin(e);if(!o)return;const n={current:null};installUnhandledRejectionObserver(e,o.writeObs,n),ensureRegisterToolCache(e),cachedCapabilityTools.length=0,r(o,n),registerServices(e,o,n),sharedPluginContext=o,marksFullRuntimeRegistered(e.registrationMode,t)&&(_fullRuntimeRegistered=!0),_pluginRegisteredAt=Date.now(),e.logger.info?.("[beeos-claw] Plugin registered successfully")}finally{_initializing=!1}}else e.logger.info?.(`[beeos-claw] skip re-register mode=${e.registrationMode??"full"} already_registered`)}};export default plugin;
@@ -1 +1 @@
1
- import{createHash}from"node:crypto";import{RuntimeCommandError}from"./runtime-command-dispatcher.js";export const BEEOS_CHANNEL_TAG_RE=/<!-- beeos:channel=(.+?) -->/;export const BEEOS_CRON_UID_TAG_RE=/<!-- beeos:cron-uid=(.+?) -->/;export function encodeBeeosChannelTag(e,t){const n=`\x3c!-- beeos:channel=${t} --\x3e`,i=stripBeeosChannelTag(e);return i?`${i}\n${n}`:n}export function decodeBeeosChannelId(e){if(e)return BEEOS_CHANNEL_TAG_RE.exec(e)?.[1]}export function stripBeeosChannelTag(e){return e?e.replace(/\n?<!-- beeos:channel=.+? -->/g,"").trim():""}export function encodeBeeosCronUidTag(e,t){const n=stripBeeosCronUidTag(e),i=`\x3c!-- beeos:cron-uid=${t} --\x3e`;return n?`${n}\n${i}`:i}export function decodeBeeosCronUid(e){return e?BEEOS_CRON_UID_TAG_RE.exec(e)?.[1]:void 0}export function stripBeeosCronUidTag(e){return e?e.replace(/\n?<!-- beeos:cron-uid=.+? -->/g,"").trim():""}export class RuntimeCronService{deps;ready;updateTurns=new Map;constructor(e){this.deps=e,this.ready=e.overrides.initialize(),this.ready.catch(()=>{})}async execute(e,t,n){await this.ready;const i=this.requireGateway(),s=e;switch(s.name){case"cron.list":{const e=await i.request("cron.list",{...s.params,agentId:s.context.agent.runtimeLocalKey});return await this.normalizeList(e)}case"cron.status":{const e=await i.request("cron.status",{});return await this.normalizeStatus(e)}case"cron.add":return await this.add(i,s);case"cron.update":return await this.update(i,s);case"cron.remove":{const e=s.params,t=await this.resolveLocalId(i,e.id,"acp"===e.__beeosCronIngress),n=await i.request("cron.remove",{id:t});return await this.deps.overrides.removeIdentity(e.id),this.deps.channels.forget(t),{removed:n?.removed??!0}}case"cron.run":{const e=s.params,t=await this.resolveLocalId(i,e.id,"acp"===e.__beeosCronIngress),n=await i.request("cron.run",{id:t,...void 0===e.mode?{}:{mode:e.mode}}),r=!1!==n?.ok&&("boolean"==typeof n?.enqueued?n.enqueued:"boolean"==typeof n?.ran?n.ran:!0===n?.accepted);return{runToken:s.params.runToken,accepted:r}}case"cron.runs":{const e=s.params,t="acp"===e.__beeosCronIngress,n={...void 0===e.id?{}:{id:await this.resolveLocalId(i,e.id,t)},...void 0===e.limit?{}:{limit:e.limit},...void 0===e.offset?{}:{offset:e.offset}},r=await i.request("cron.runs",n);return this.normalizeRuns(r)}}throw new RuntimeCommandError("INVALID_PARAMS",`Unsupported Cron command: ${s.name}`)}async afterCommit(e){if("cron.add"!==e.name)return;await this.ready;const t=this.requireGateway(),n=e.params.runtimeCronJobUid,i=await this.findByRuntimeUid(t,n);if(!i)throw new RuntimeCommandError("OUTCOME_UNKNOWN","committed Cron job cannot yet be recovered by runtime uid");await this.deps.overrides.bind(n,i.id)}async add(e,t){const n=cloneFields(t.params);delete n.runtimeCronJobUid;const i=takePrivateOptions(n),s=t.params.runtimeCronJobUid,r=t.context.agent.runtimeLocalKey,o="acp"===i.ingress?!0===i.internal:"isolated"===n.sessionTarget;let a;n.description=encodeBeeosCronUidTag(asString(n.description),s);const d=void 0===i.maxDurationMs?void 0:this.clampDuration(i.maxDurationMs);await this.deps.overrides.reserve(s,d);const c=await this.findByRuntimeUid(e,s);if(c)return sanitizeJob({...c,id:s},this.deps.channels,this.deps.overrides,c.id);n.agentId=r,o&&(n.delivery={mode:"none"},a=await this.deps.channels.allocate(asString(n.name)??"cron",r),a&&(n.description=encodeBeeosChannelTag(asString(n.description),a)));const u=await e.request("cron.add",n),p=requireResultId(u,s);return a&&this.deps.channels.remember(p,a),sanitizeJob({...u,id:s},this.deps.channels,this.deps.overrides,p)}async update(e,t){const n=t.params,i="acp"===n.__beeosCronIngress?"acp":void 0,s=cloneFields(n.patch??{}),r=takePrivateOptions(s,i),o="acp"===r.ingress,a=await this.resolveLocalId(e,n.id,o);return await this.withUpdateTurn(a,async()=>await this.updateResolved(e,n,s,r,o,a,t.context.agent.runtimeLocalKey))}async withUpdateTurn(e,t){const n=this.updateTurns.get(e);let i;const s=new Promise(e=>{i=e});this.updateTurns.set(e,s),n&&await n;try{return await t()}finally{i(),this.updateTurns.get(e)===s&&this.updateTurns.delete(e)}}async updateResolved(e,t,n,i,s,r,o){let a;const d=async()=>{if(void 0!==a)return a;const t=await e.request("cron.list",{}),n=t?.jobs;if(!Array.isArray(n))return;const i=n.find(e=>e&&"object"==typeof e&&e.id===r);a=asString(i?.description)??"";const s=decodeBeeosChannelId(a);return s&&this.deps.channels.remember(r,s),a};let c,u=!0===i.internal;if(s||(u="isolated"===n.sessionTarget,void 0===n.sessionTarget&&(u=!!this.deps.channels.lookup(r)||!!decodeBeeosChannelId(await d()))),u){n.delivery={mode:"none"};const e="description"in n,t=asString(n.description);c=decodeBeeosChannelId(t)??this.deps.channels.lookup(r);const i=c?void 0:await d();c??=decodeBeeosChannelId(i);let s=!1;c||(c=await this.deps.channels.allocate(asString(n.name)??"cron",o),s=!0),c&&(e?n.description=encodeBeeosChannelTag(t,c):s&&(n.description=encodeBeeosChannelTag(i,c)))}else if(!s&&"main"===n.sessionTarget){const e=await d();"description"in n||void 0===e||(n.description=stripBeeosChannelTag(e)||void 0),n.delivery={mode:"none"},this.deps.channels.forget(r)}const p=s?this.deps.overrides.runtimeUid(r):t.id;p&&"description"in n&&(n.description=encodeBeeosCronUidTag(asString(n.description),p));const l=void 0===i.maxDurationMs?void 0:this.clampDuration(i.maxDurationMs),m=void 0===l?void 0:await this.deps.overrides.setForUpdate(t.id,l);let h;try{h=await e.request("cron.update",{id:r,patch:n})}catch(e){throw m&&await this.deps.overrides.rollbackUpdate(m),e}const g=requireResultId(h,r),f=s?this.deps.overrides.runtimeUid(g)??g:t.id;return c&&this.deps.channels.remember(g,c),sanitizeJob({...h,id:f},this.deps.channels,this.deps.overrides,g)}async normalizeList(e){const t=e;await this.recoverIdentities(t?.jobs);const n=Array.isArray(t?.jobs)?t.jobs.map(e=>sanitizeJob(e,this.deps.channels,this.deps.overrides)):[];return{jobs:n,total:finiteNonNegativeInteger(t?.total)??n.length}}async normalizeStatus(e){const t=e;await this.recoverIdentities(t?.jobs);const n=finiteNonNegativeInteger(t?.jobCount)??finiteNonNegativeInteger(t?.jobs)??(Array.isArray(t?.jobs)?t.jobs.length:t?.jobs&&"object"==typeof t.jobs?Object.keys(t.jobs).length:0);return{enabled:!0===t?.enabled,jobCount:n,..."number"==typeof t?.nextWakeAtMs?{nextWakeAtMs:t.nextWakeAtMs}:{}}}clampDuration(e){if("number"!=typeof e||!Number.isFinite(e)||e<=0)throw new RuntimeCommandError("INVALID_PARAMS","cron maxDurationMs must be a positive number");return Math.max(this.deps.durationFloorMs,Math.min(this.deps.durationCapMs,Math.trunc(e)))}requireGateway(){const e=this.deps.getGateway();if(!e?.isConnected)throw new RuntimeCommandError("GATEWAY_NOT_CONNECTED","Gateway not connected");return e}async resolveLocalId(e,t,n){if(n)return t;const i=this.deps.overrides.localId(t);if(i)return i;const s=await this.findByRuntimeUid(e,t);return s?(await this.deps.overrides.bind(t,s.id),s.id):t}async findByRuntimeUid(e,t){const n=await e.request("cron.list",{}),i=n?.jobs;if(Array.isArray(i))return i.find(e=>e&&"object"==typeof e&&decodeBeeosCronUid(asString(e.description))===t)}normalizeRuns(e){const t=e,n=(Array.isArray(t?.entries)?t.entries:Array.isArray(t?.runs)?t.runs:[]).flatMap(e=>{const t=normalizeRun(e,this.deps.overrides);return t?[t]:[]});return{runs:n,total:finiteNonNegativeInteger(t?.total)??n.length}}async recoverIdentities(e){const t=Array.isArray(e)?e:e&&"object"==typeof e?Object.values(e):[];for(const e of t){if(!e||"object"!=typeof e)continue;const t=e,n=asString(t.id),i=decodeBeeosCronUid(asString(t.description));n&&i&&await this.deps.overrides.bind(i,n)}}}function takePrivateOptions(e,t){const n="acp"===e.__beeosCronIngress?"acp":t,i=!0===e._beeos_internal,s=e._beeos_meta&&"object"==typeof e._beeos_meta?e._beeos_meta:void 0,r=s?.maxDurationMs;return delete e.__beeosCronIngress,delete e._beeos_internal,delete e._beeos_meta,{ingress:n,internal:i,maxDurationMs:r}}function cloneFields(e){return e&&"object"==typeof e&&!Array.isArray(e)?{...e}:{}}function asString(e){return"string"==typeof e?e:void 0}function requireResultId(e,t){return(e&&"object"==typeof e?asString(e.id):void 0)||t}function sanitizeJob(e,t,n,i){const s=i??e.id,r=decodeBeeosChannelId(e.description);r&&t.remember(s,r);const o=decodeBeeosCronUid(e.description)??n.runtimeUid(s)??e.id,a=stripBeeosCronUidTag(stripBeeosChannelTag(e.description)),d=e,c={id:o,name:asString(d.name)??"",enabled:!0===d.enabled,schedule:sanitizeSchedule(d.schedule),sessionTarget:"isolated"===d.sessionTarget?"isolated":"main",payload:sanitizePayload(d.payload),state:sanitizeState(d.state),createdAtMs:finiteNonNegativeNumber(d.createdAtMs)??0,updatedAtMs:finiteNonNegativeNumber(d.updatedAtMs)??0};return"next-heartbeat"!==d.wakeMode&&"now"!==d.wakeMode||(c.wakeMode=d.wakeMode),void 0!==e.description&&a&&(c.description=a),c}function sanitizeSchedule(e){const t=asRecord(e);return"at"===t.kind?{kind:"at",at:asString(t.at)??""}:"cron"===t.kind?{kind:"cron",expr:asString(t.expr)??"",..."string"==typeof t.tz?{tz:t.tz}:{}}:{kind:"every",everyMs:"number"==typeof t.everyMs?t.everyMs:0}}function sanitizePayload(e){const t=asRecord(e);return{kind:asString(t.kind)??"",..."string"==typeof t.message?{message:t.message}:{},..."string"==typeof t.text?{text:t.text}:{}}}function sanitizeState(e){const t=asRecord(e),n=t.lastRunStatus??t.lastStatus;return{..."number"==typeof t.nextRunAtMs?{nextRunAtMs:t.nextRunAtMs}:{},..."number"==typeof t.lastRunAtMs?{lastRunAtMs:t.lastRunAtMs}:{},..."ok"===n||"error"===n||"skipped"===n?{lastRunStatus:n}:{},..."string"==typeof t.lastError?{lastError:t.lastError}:{},..."number"==typeof t.consecutiveErrors?{consecutiveErrors:t.consecutiveErrors}:{}}}function normalizeRun(e,t){const n=asRecord(e),i=asString(n.jobId),s=finiteNonNegativeNumber(n.startedAtMs)??finiteNonNegativeNumber(n.runAtMs)??finiteNonNegativeNumber(n.ts);if(!i||void 0===s)return;const r="ok"===n.status||"error"===n.status||"skipped"===n.status||"running"===n.status?n.status:"finished"===n.action?"error":"running",o=finiteNonNegativeNumber(n.durationMs),a=finiteNonNegativeNumber(n.completedAtMs)??(void 0===o||"running"===r?void 0:s+o),d=asString(n.id)??asString(n.runId);return{id:d&&/^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/.test(d)?d:deterministicRunId(n,i,s),jobId:t.runtimeUid(i)??i,startedAtMs:s,status:r,...void 0===a?{}:{completedAtMs:a},..."string"==typeof n.error?{error:n.error}:{}}}function deterministicRunId(e,t,n){const i=[t,n,e.ts,e.durationMs,e.sessionId,e.sessionKey,e.status].map(e=>String(e??"")).join("\0");return`run:${createHash("sha256").update(i).digest("hex").slice(0,32)}`}function asRecord(e){return e&&"object"==typeof e&&!Array.isArray(e)?e:{}}function finiteNonNegativeNumber(e){return"number"==typeof e&&Number.isFinite(e)&&e>=0?e:void 0}function finiteNonNegativeInteger(e){return"number"==typeof e&&Number.isSafeInteger(e)&&e>=0?e:void 0}
1
+ import{createHash}from"node:crypto";import{RuntimeCommandError}from"./runtime-command-dispatcher.js";export const BEEOS_CHANNEL_TAG_RE=/<!-- beeos:channel=(.+?) -->/;export const BEEOS_CRON_UID_TAG_RE=/<!-- beeos:cron-uid=(.+?) -->/;export function encodeBeeosChannelTag(e,t){const n=`\x3c!-- beeos:channel=${t} --\x3e`,i=stripBeeosChannelTag(e);return i?`${i}\n${n}`:n}export function decodeBeeosChannelId(e){if(e)return BEEOS_CHANNEL_TAG_RE.exec(e)?.[1]}export function stripBeeosChannelTag(e){return e?e.replace(/\n?<!-- beeos:channel=.+? -->/g,"").trim():""}export function encodeBeeosCronUidTag(e,t){const n=stripBeeosCronUidTag(e),i=`\x3c!-- beeos:cron-uid=${t} --\x3e`;return n?`${n}\n${i}`:i}export function decodeBeeosCronUid(e){return e?BEEOS_CRON_UID_TAG_RE.exec(e)?.[1]:void 0}export function stripBeeosCronUidTag(e){return e?e.replace(/\n?<!-- beeos:cron-uid=.+? -->/g,"").trim():""}export class RuntimeCronService{deps;ready;constructor(e){this.deps=e,this.ready=e.identities.initialize(),this.ready.catch(()=>{})}async execute(e,t,n){await this.ready;const i=this.requireGateway(),s=e;switch(s.name){case"cron.list":{const e=await i.request("cron.list",{...s.params,agentId:s.context.agent.runtimeLocalKey});return await this.normalizeList(e)}case"cron.status":{const e=await i.request("cron.status",{});return await this.normalizeStatus(e)}case"cron.add":return await this.add(i,s);case"cron.update":return await this.update(i,s);case"cron.remove":{const e=s.params,t=await this.resolveLocalId(i,e.id,"acp"===e.__beeosCronIngress),n=await i.request("cron.remove",{id:t});return await this.deps.identities.remove(e.id),this.deps.channels.forget(t),{removed:n?.removed??!0}}case"cron.run":{const e=s.params,t=await this.resolveLocalId(i,e.id,"acp"===e.__beeosCronIngress),n=await i.request("cron.run",{id:t,...void 0===e.mode?{}:{mode:e.mode}}),r=!1!==n?.ok&&("boolean"==typeof n?.enqueued?n.enqueued:"boolean"==typeof n?.ran?n.ran:!0===n?.accepted);return{runToken:s.params.runToken,accepted:r}}case"cron.runs":{const e=s.params,t="acp"===e.__beeosCronIngress,n={...void 0===e.id?{}:{id:await this.resolveLocalId(i,e.id,t)},...void 0===e.limit?{}:{limit:e.limit},...void 0===e.offset?{}:{offset:e.offset}},r=await i.request("cron.runs",n);return this.normalizeRuns(r)}}throw new RuntimeCommandError("INVALID_PARAMS",`Unsupported Cron command: ${s.name}`)}async afterCommit(e){if("cron.add"!==e.name)return;await this.ready;const t=this.requireGateway(),n=e.params.runtimeCronJobUid,i=await this.findByRuntimeUid(t,n);if(!i)throw new RuntimeCommandError("OUTCOME_UNKNOWN","committed Cron job cannot yet be recovered by runtime uid");await this.deps.identities.bind(n,i.id)}async add(e,t){const n=cloneFields(t.params);delete n.runtimeCronJobUid;const i=takePrivateOptions(n),s=t.params.runtimeCronJobUid,r=t.context.agent.runtimeLocalKey,o="acp"===i.ingress?!0===i.internal:"isolated"===n.sessionTarget;let a;n.description=encodeBeeosCronUidTag(asString(n.description),s),normalizeAgentTurnDeadline(n),await this.deps.identities.reserve(s);const d=await this.findByRuntimeUid(e,s);if(d)return sanitizeJob({...d,id:s},this.deps.channels,this.deps.identities,d.id);n.agentId=r,o&&(n.delivery={mode:"none"},a=await this.deps.channels.allocate(asString(n.name)??"cron",r),a&&(n.description=encodeBeeosChannelTag(asString(n.description),a)));const c=await e.request("cron.add",n),u=requireResultId(c,s);return a&&this.deps.channels.remember(u,a),sanitizeJob({...c,id:s},this.deps.channels,this.deps.identities,u)}async update(e,t){const n=t.params,i="acp"===n.__beeosCronIngress?"acp":void 0,s=cloneFields(n.patch??{}),r=takePrivateOptions(s,i),o="acp"===r.ingress,a=await this.resolveLocalId(e,n.id,o);return await this.updateResolved(e,n,s,r,o,a,t.context.agent.runtimeLocalKey)}async updateResolved(e,t,n,i,s,r,o){let a;const d=async()=>{if(void 0!==a)return a;const t=await e.request("cron.list",{}),n=t?.jobs;if(!Array.isArray(n))return;const i=n.find(e=>e&&"object"==typeof e&&e.id===r);a=asString(i?.description)??"";const s=decodeBeeosChannelId(a);return s&&this.deps.channels.remember(r,s),a};let c,u=!0===i.internal;if(s||(u="isolated"===n.sessionTarget,void 0===n.sessionTarget&&(u=!!this.deps.channels.lookup(r)||!!decodeBeeosChannelId(await d()))),u){n.delivery={mode:"none"};const e="description"in n,t=asString(n.description);c=decodeBeeosChannelId(t)??this.deps.channels.lookup(r);const i=c?void 0:await d();c??=decodeBeeosChannelId(i);let s=!1;c||(c=await this.deps.channels.allocate(asString(n.name)??"cron",o),s=!0),c&&(e?n.description=encodeBeeosChannelTag(t,c):s&&(n.description=encodeBeeosChannelTag(i,c)))}else if(!s&&"main"===n.sessionTarget){const e=await d();"description"in n||void 0===e||(n.description=stripBeeosChannelTag(e)||void 0),n.delivery={mode:"none"},this.deps.channels.forget(r)}const p=s?this.deps.identities.runtimeUid(r):t.id;p&&"description"in n&&(n.description=encodeBeeosCronUidTag(asString(n.description),p)),normalizeAgentTurnDeadline(n);const l=await e.request("cron.update",{id:r,patch:n}),m=requireResultId(l,r),g=s?this.deps.identities.runtimeUid(m)??m:t.id;return c&&this.deps.channels.remember(m,c),sanitizeJob({...l,id:g},this.deps.channels,this.deps.identities,m)}async normalizeList(e){const t=e;await this.recoverIdentities(t?.jobs);const n=Array.isArray(t?.jobs)?t.jobs.map(e=>sanitizeJob(e,this.deps.channels,this.deps.identities)):[];return{jobs:n,total:finiteNonNegativeInteger(t?.total)??n.length}}async normalizeStatus(e){const t=e;await this.recoverIdentities(t?.jobs);const n=finiteNonNegativeInteger(t?.jobCount)??finiteNonNegativeInteger(t?.jobs)??(Array.isArray(t?.jobs)?t.jobs.length:t?.jobs&&"object"==typeof t.jobs?Object.keys(t.jobs).length:0);return{enabled:!0===t?.enabled,jobCount:n,..."number"==typeof t?.nextWakeAtMs?{nextWakeAtMs:t.nextWakeAtMs}:{}}}requireGateway(){const e=this.deps.getGateway();if(!e?.isConnected)throw new RuntimeCommandError("GATEWAY_NOT_CONNECTED","Gateway not connected");return e}async resolveLocalId(e,t,n){if(n)return t;const i=this.deps.identities.localId(t);if(i)return i;const s=await this.findByRuntimeUid(e,t);return s?(await this.deps.identities.bind(t,s.id),s.id):t}async findByRuntimeUid(e,t){const n=await e.request("cron.list",{}),i=n?.jobs;if(Array.isArray(i))return i.find(e=>e&&"object"==typeof e&&decodeBeeosCronUid(asString(e.description))===t)}normalizeRuns(e){const t=e,n=(Array.isArray(t?.entries)?t.entries:Array.isArray(t?.runs)?t.runs:[]).flatMap(e=>{const t=normalizeRun(e,this.deps.identities);return t?[t]:[]});return{runs:n,total:finiteNonNegativeInteger(t?.total)??n.length}}async recoverIdentities(e){const t=Array.isArray(e)?e:e&&"object"==typeof e?Object.values(e):[];for(const e of t){if(!e||"object"!=typeof e)continue;const t=e,n=asString(t.id),i=decodeBeeosCronUid(asString(t.description));n&&i&&await this.deps.identities.bind(i,n)}}}function takePrivateOptions(e,t){const n="acp"===e.__beeosCronIngress?"acp":t,i=!0===e._beeos_internal;return delete e.__beeosCronIngress,delete e._beeos_internal,delete e._beeos_meta,{ingress:n,internal:i}}function normalizeAgentTurnDeadline(e){const t=e.payload;if(!t||"object"!=typeof t||Array.isArray(t))return;const n={...t};if(!("agentTurn"===n.kind||void 0===n.kind&&"string"==typeof n.message))return;const i=n.timeoutSeconds;if(null==i||""===i)n.timeoutSeconds=0;else if("number"!=typeof i||!Number.isSafeInteger(i)||i<0)throw new RuntimeCommandError("INVALID_PARAMS","cron payload.timeoutSeconds must be a non-negative integer");e.payload=n}function cloneFields(e){return e&&"object"==typeof e&&!Array.isArray(e)?{...e}:{}}function asString(e){return"string"==typeof e?e:void 0}function requireResultId(e,t){return(e&&"object"==typeof e?asString(e.id):void 0)||t}function sanitizeJob(e,t,n,i){const s=i??e.id,r=decodeBeeosChannelId(e.description);r&&t.remember(s,r);const o=decodeBeeosCronUid(e.description)??n.runtimeUid(s)??e.id,a=stripBeeosCronUidTag(stripBeeosChannelTag(e.description)),d=e,c={id:o,name:asString(d.name)??"",enabled:!0===d.enabled,schedule:sanitizeSchedule(d.schedule),sessionTarget:"isolated"===d.sessionTarget?"isolated":"main",payload:sanitizePayload(d.payload),state:sanitizeState(d.state),createdAtMs:finiteNonNegativeNumber(d.createdAtMs)??0,updatedAtMs:finiteNonNegativeNumber(d.updatedAtMs)??0};return"next-heartbeat"!==d.wakeMode&&"now"!==d.wakeMode||(c.wakeMode=d.wakeMode),void 0!==e.description&&a&&(c.description=a),c}function sanitizeSchedule(e){const t=asRecord(e);return"at"===t.kind?{kind:"at",at:asString(t.at)??""}:"cron"===t.kind?{kind:"cron",expr:asString(t.expr)??"",..."string"==typeof t.tz?{tz:t.tz}:{}}:{kind:"every",everyMs:"number"==typeof t.everyMs?t.everyMs:0}}function sanitizePayload(e){const t=asRecord(e);return{kind:asString(t.kind)??"",..."string"==typeof t.message?{message:t.message}:{},..."string"==typeof t.text?{text:t.text}:{},..."number"==typeof t.timeoutSeconds&&Number.isSafeInteger(t.timeoutSeconds)&&t.timeoutSeconds>=0?{timeoutSeconds:t.timeoutSeconds}:{}}}function sanitizeState(e){const t=asRecord(e),n=t.lastRunStatus??t.lastStatus;return{..."number"==typeof t.nextRunAtMs?{nextRunAtMs:t.nextRunAtMs}:{},..."number"==typeof t.lastRunAtMs?{lastRunAtMs:t.lastRunAtMs}:{},..."ok"===n||"error"===n||"skipped"===n?{lastRunStatus:n}:{},..."string"==typeof t.lastError?{lastError:t.lastError}:{},..."number"==typeof t.consecutiveErrors?{consecutiveErrors:t.consecutiveErrors}:{}}}function normalizeRun(e,t){const n=asRecord(e),i=asString(n.jobId),s=finiteNonNegativeNumber(n.startedAtMs)??finiteNonNegativeNumber(n.runAtMs)??finiteNonNegativeNumber(n.ts);if(!i||void 0===s)return;const r="ok"===n.status||"error"===n.status||"skipped"===n.status||"running"===n.status?n.status:"finished"===n.action?"error":"running",o=finiteNonNegativeNumber(n.durationMs),a=finiteNonNegativeNumber(n.completedAtMs)??(void 0===o||"running"===r?void 0:s+o),d=asString(n.id)??asString(n.runId);return{id:d&&/^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/.test(d)?d:deterministicRunId(n,i,s),jobId:t.runtimeUid(i)??i,startedAtMs:s,status:r,...void 0===a?{}:{completedAtMs:a},..."string"==typeof n.error?{error:n.error}:{}}}function deterministicRunId(e,t,n){const i=[t,n,e.ts,e.durationMs,e.sessionId,e.sessionKey,e.status].map(e=>String(e??"")).join("\0");return`run:${createHash("sha256").update(i).digest("hex").slice(0,32)}`}function asRecord(e){return e&&"object"==typeof e&&!Array.isArray(e)?e:{}}function finiteNonNegativeNumber(e){return"number"==typeof e&&Number.isFinite(e)&&e>=0?e:void 0}function finiteNonNegativeInteger(e){return"number"==typeof e&&Number.isSafeInteger(e)&&e>=0?e:void 0}
@@ -1 +1 @@
1
- import{CRON_DURATION_FLOOR_MS,CRON_DURATION_CAP_DEFAULT_MS}from"../config.js";function buildSharedFields(e,a){const n=a?.floorMs??CRON_DURATION_FLOOR_MS,t=a?.capMs??CRON_DURATION_CAP_DEFAULT_MS,l=[{name:"name",label:"Task Name",inputType:"text",required:!0,placeholder:"e.g. daily-report",apiPath:"name"},{name:"description",label:"Description",inputType:"textarea",placeholder:"What does this task do?",apiPath:"description"},{name:"enabled",label:"Enabled",inputType:"toggle",defaultValue:!0,apiPath:"enabled"},{name:"sessionTarget",label:"Execution Mode",inputType:"select",required:!0,defaultValue:"isolated",options:[{label:"Independent (isolated)",value:"isolated"},{label:"Main session",value:"main"}]},{name:"deliveryMode",label:"Result Delivery",inputType:"select",required:!0,defaultValue:"internal",options:[{label:"Internal (BeeOS IM channel)",value:"internal"},...e.length>0?[{label:"Channel (Telegram/Discord/…)",value:"announce"}]:[],{label:"Webhook",value:"webhook"},{label:"None (log only)",value:"none"}]}];return e.length>0&&l.push({name:"deliveryChannel",label:"Delivery Channel",inputType:"select",options:e}),l.push({name:"webhookUrl",label:"Webhook URL",inputType:"url",placeholder:"https://example.com/webhook"},{name:"scheduleKind",label:"Schedule Type",inputType:"select",required:!0,options:[{label:"Interval (every N ms)",value:"every"},{label:"Cron Expression",value:"cron"},{label:"Specific Time",value:"at"}],apiPath:"schedule.kind"},{name:"everyMs",label:"Interval (ms)",inputType:"number",placeholder:"3600000 = 1 hour",validation:{min:1e3},apiPath:"schedule.everyMs"},{name:"cronExpr",label:"Cron Expression",inputType:"text",placeholder:"e.g. 0 9 * * *",apiPath:"schedule.expr"},{name:"at",label:"Run At (ISO 8601)",inputType:"text",placeholder:"e.g. 2026-04-01T09:00:00Z",apiPath:"schedule.at"},{name:"tz",label:"Timezone",inputType:"text",placeholder:"e.g. Asia/Shanghai (optional)",apiPath:"schedule.tz"},{name:"message",label:"Agent Message",inputType:"textarea",required:!0,placeholder:"What should the agent do when this task fires?",apiPath:"payload.message"},{name:"maxDurationMs",label:"Max Execution Time (ms)",inputType:"number",placeholder:`optional, e.g. 600000 = 10min (max ${Math.round(t/6e4)}min)`,validation:{min:n,max:t},apiPath:"_beeos_meta.maxDurationMs"}),l}function cronTransformPayload(e){const a={};void 0!==e.name&&(a.name=e.name),void 0!==e.description&&(a.description=e.description),void 0!==e.enabled&&(a.enabled=e.enabled);const n=e.scheduleKind;if(n){const t={kind:n};"every"===n&&e.everyMs&&(t.everyMs=Number(e.everyMs)),"cron"===n&&e.cronExpr&&(t.expr=e.cronExpr),"at"===n&&e.at&&(t.at=e.at),e.tz&&(t.tz=e.tz),a.schedule=t}const t=e.deliveryMode??"internal",l=e.sessionTarget??"isolated",o=e.message;switch(t){case"internal":a.sessionTarget="isolated",a.delivery={mode:"none"},a._beeos_internal=!0,void 0!==o&&(a.payload={kind:"agentTurn",message:String(o)});break;case"announce":a.sessionTarget="isolated",a.delivery={mode:"announce",...e.deliveryChannel?{channel:e.deliveryChannel}:{}},void 0!==o&&(a.payload={kind:"agentTurn",message:String(o)});break;case"webhook":a.sessionTarget="isolated",a.delivery={mode:"webhook",...e.webhookUrl?{to:e.webhookUrl}:{}},void 0!==o&&(a.payload={kind:"agentTurn",message:String(o)});break;default:if(a.sessionTarget=l,a.delivery={mode:"none"},void 0!==o){const e="isolated"===l;a.payload=e?{kind:"agentTurn",message:String(o)}:{kind:"systemEvent",text:String(o)}}}const i=e.maxDurationMs;if(void 0!==i&&""!==i&&null!==i){const e=Number(i);Number.isFinite(e)&&e>0&&(a._beeos_meta={maxDurationMs:Math.trunc(e)})}return a}function cronCreateTransform(e){const a=cronTransformPayload(e);return a.name||(a.name="Untitled"),void 0===a.enabled&&(a.enabled=!0),a.schedule||(a.schedule={kind:"every",everyMs:36e5}),a.sessionTarget||(a.sessionTarget="isolated"),a.wakeMode="now",a.payload||(a.payload={kind:"agentTurn",message:""}),a.delivery||(a.delivery={mode:"none"}),void 0===a._beeos_internal&&(a._beeos_internal=!0),a}export function buildCronSchemas(e,a){const n=buildSharedFields([...e].map(e=>({label:e.charAt(0).toUpperCase()+e.slice(1),value:e})),a);return{create:{domain:"cron",operation:"create",title:"Create Scheduled Task",description:"Add a new recurring or one-time agent task.",fields:n,submitLabel:"Create",targetRpc:"cron.add",transformPayload:cronCreateTransform},update:{domain:"cron",operation:"update",title:"Edit Scheduled Task",description:"Modify an existing scheduled task.",fields:n,submitLabel:"Update",targetRpc:"cron.update",transformPayload:cronTransformPayload}}}
1
+ function buildSharedFields(e){const a=[{name:"name",label:"Task Name",inputType:"text",required:!0,placeholder:"e.g. daily-report",apiPath:"name"},{name:"description",label:"Description",inputType:"textarea",placeholder:"What does this task do?",apiPath:"description"},{name:"enabled",label:"Enabled",inputType:"toggle",defaultValue:!0,apiPath:"enabled"},{name:"sessionTarget",label:"Execution Mode",inputType:"select",required:!0,defaultValue:"isolated",options:[{label:"Independent (isolated)",value:"isolated"},{label:"Main session",value:"main"}]},{name:"deliveryMode",label:"Result Delivery",inputType:"select",required:!0,defaultValue:"internal",options:[{label:"Internal (BeeOS IM channel)",value:"internal"},...e.length>0?[{label:"Channel (Telegram/Discord/…)",value:"announce"}]:[],{label:"Webhook",value:"webhook"},{label:"None (log only)",value:"none"}]}];return e.length>0&&a.push({name:"deliveryChannel",label:"Delivery Channel",inputType:"select",options:e}),a.push({name:"webhookUrl",label:"Webhook URL",inputType:"url",placeholder:"https://example.com/webhook"},{name:"scheduleKind",label:"Schedule Type",inputType:"select",required:!0,options:[{label:"Interval (every N ms)",value:"every"},{label:"Cron Expression",value:"cron"},{label:"Specific Time",value:"at"}],apiPath:"schedule.kind"},{name:"everyMs",label:"Interval (ms)",inputType:"number",placeholder:"3600000 = 1 hour",validation:{min:1e3},apiPath:"schedule.everyMs"},{name:"cronExpr",label:"Cron Expression",inputType:"text",placeholder:"e.g. 0 9 * * *",apiPath:"schedule.expr"},{name:"at",label:"Run At (ISO 8601)",inputType:"text",placeholder:"e.g. 2026-04-01T09:00:00Z",apiPath:"schedule.at"},{name:"tz",label:"Timezone",inputType:"text",placeholder:"e.g. Asia/Shanghai (optional)",apiPath:"schedule.tz"},{name:"message",label:"Agent Message",inputType:"textarea",required:!0,placeholder:"What should the agent do when this task fires?",apiPath:"payload.message"},{name:"timeoutSeconds",label:"Execution Deadline (seconds)",inputType:"number",placeholder:"0 or blank = no deadline",validation:{min:0},apiPath:"payload.timeoutSeconds"}),a}function cronTransformPayload(e){const a={};void 0!==e.name&&(a.name=e.name),void 0!==e.description&&(a.description=e.description),void 0!==e.enabled&&(a.enabled=e.enabled);const n=e.scheduleKind;if(n){const l={kind:n};"every"===n&&e.everyMs&&(l.everyMs=Number(e.everyMs)),"cron"===n&&e.cronExpr&&(l.expr=e.cronExpr),"at"===n&&e.at&&(l.at=e.at),e.tz&&(l.tz=e.tz),a.schedule=l}const l=e.deliveryMode??"internal",t=e.sessionTarget??"isolated",o=e.message,i=()=>{const a=e.timeoutSeconds,n=null==a||""===a?0:Number(a);return{kind:"agentTurn",message:String(o??""),timeoutSeconds:Number.isSafeInteger(n)&&n>=0?n:a}};switch(l){case"internal":a.sessionTarget="isolated",a.delivery={mode:"none"},a._beeos_internal=!0,void 0!==o&&(a.payload=i());break;case"announce":a.sessionTarget="isolated",a.delivery={mode:"announce",...e.deliveryChannel?{channel:e.deliveryChannel}:{}},void 0!==o&&(a.payload=i());break;case"webhook":a.sessionTarget="isolated",a.delivery={mode:"webhook",...e.webhookUrl?{to:e.webhookUrl}:{}},void 0!==o&&(a.payload=i());break;default:if(a.sessionTarget=t,a.delivery={mode:"none"},void 0!==o){const e="isolated"===t;a.payload=e?i():{kind:"systemEvent",text:String(o)}}}return a}function cronCreateTransform(e){const a=cronTransformPayload(e);return a.name||(a.name="Untitled"),void 0===a.enabled&&(a.enabled=!0),a.schedule||(a.schedule={kind:"every",everyMs:36e5}),a.sessionTarget||(a.sessionTarget="isolated"),a.wakeMode="now",a.payload||(a.payload={kind:"agentTurn",message:"",timeoutSeconds:0}),a.delivery||(a.delivery={mode:"none"}),void 0===a._beeos_internal&&(a._beeos_internal=!0),a}export function buildCronSchemas(e){const a=buildSharedFields([...e].map(e=>({label:e.charAt(0).toUpperCase()+e.slice(1),value:e})));return{create:{domain:"cron",operation:"create",title:"Create Scheduled Task",description:"Add a new recurring or one-time agent task.",fields:a,submitLabel:"Create",targetRpc:"cron.add",transformPayload:cronCreateTransform},update:{domain:"cron",operation:"update",title:"Edit Scheduled Task",description:"Modify an existing scheduled task.",fields:a,submitLabel:"Update",targetRpc:"cron.update",transformPayload:cronTransformPayload}}}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "beeos-claw",
3
- "version": "0.6.59",
3
+ "version": "0.6.60",
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>",
@@ -87,5 +87,5 @@
87
87
  "fast-uri": ">=3.1.4",
88
88
  "brace-expansion": ">=5.0.9"
89
89
  },
90
- "gitHead": "6adcba0c318826cc047f5769e6a43c949eb22d73"
90
+ "gitHead": "15033e77dd4fc75bbff5760cd1ce6f0d34973c49"
91
91
  }
@@ -1 +0,0 @@
1
- import{randomUUID}from"node:crypto";import{mkdir,open,readFile,rename,rm}from"node:fs/promises";import{homedir}from"node:os";import{dirname,join}from"node:path";import{withOwnedDirectoryLock}from"./runtime/owned-directory-lock.js";const overridesDirectory=join(process.env.OPENCLAW_HOME||join(homedir(),".openclaw"),"beeos-claw");export const DEFAULT_CRON_OVERRIDES_PATH=join(overridesDirectory,"cron-overrides.json");const nodeFiles={mkdir:async(e,i)=>await mkdir(e,i),readFile:async(e,i)=>await readFile(e,i),open:async(e,i,r)=>await open(e,i,r),rename:async(e,i)=>await rename(e,i),rm:async(e,i)=>await rm(e,i)},ownedDirectoryLock={withFencedLock:async(e,i)=>await withOwnedDirectoryLock(e,async e=>await i({assertHeld:async()=>await e.assertOwned()}))};export function createCronOverridesStore(e={}){const i=isStoreOptions(e)?e:{logger:e},r=i.path??DEFAULT_CRON_OVERRIDES_PATH,t=i.files??nodeFiles,n=i.lock??ownedDirectoryLock,o=i.logger;let s,a=freezeSnapshot({jobs:{},identities:{},revisions:{}}),d=!1,c=Promise.resolve();const u=async e=>await(async e=>{const i=c.catch(()=>{}).then(e);return c=i.catch(()=>{}),await i})(async()=>await n.withFencedLock(`${r}.lock`,e)),v=async()=>{let e;try{e=await t.readFile(r,"utf8")}catch(e){if("ENOENT"===e.code)return freezeSnapshot({jobs:{},identities:{},revisions:{}});throw e}return parseSnapshot(e)},l=async e=>{await b(),await u(async i=>{const n=await v(),o={jobs:{...n.jobs},identities:{...n.identities},revisions:{...n.revisions}};if(!e(o))return void(a=freezeSnapshot(o));const s=freezeSnapshot(o);await(async(e,i)=>{const n=dirname(r);await t.mkdir(n,{recursive:!0,mode:448});const o=join(n,`.${r.split("/").at(-1)??"cron-overrides"}.${process.pid}.${randomUUID()}.tmp`);let s,a=!1;try{s=await t.open(o,"wx",384),await s.writeFile(`${JSON.stringify({jobs:e.jobs,identities:e.identities,revisions:e.revisions},null,2)}\n`,{encoding:"utf8"}),await s.sync(),await s.close(),s=void 0,await i.assertHeld(),await t.rename(o,r),a=!0;const d=await t.open(n,"r");try{await d.sync()}finally{await d.close()}}finally{await(s?.close().catch(()=>{})),a||await t.rm(o,{force:!0}).catch(()=>{})}})(s,i),a=s})},b=async()=>{d||(s??=u(async()=>{a=await v(),d=!0}).catch(e=>{throw s=void 0,e}),await s)};return{initialize:b,isInitialized:()=>d,snapshot:async()=>(await b(),await u(async()=>{a=await v()}),Object.freeze({jobs:a.jobs,identities:a.identities})),get:e=>{if(!d)throw new Error("cron overrides store is not initialized");return a.jobs[resolveOverrideKey(a,e)]},localId:e=>a.identities[requireJobId(e)]??void 0,runtimeUid:e=>findRuntimeUid(a.identities,requireJobId(e)),reserve:async(e,i)=>{const r=requireJobId(e),t=void 0===i?void 0:requireDuration(i);await l(e=>{const i=e.identities[r];if(null!=i)return!1;let n=!(r in e.identities);return e.identities[r]=null,void 0!==t&&e.jobs[r]!==t&&(e.jobs[r]=t,n=!0),n})},bind:async(e,i)=>{const r=requireJobId(e),t=requireJobId(i);await l(e=>{const i=e.identities[r];if(null!=i&&i!==t)throw new Error("runtime Cron identity is already bound to another local job");const n=findRuntimeUid(e.identities,t);if(n&&n!==r)throw new Error("OpenClaw Cron job is already bound to another runtime uid");return i!==t&&(e.identities[r]=t,!0)})},removeIdentity:async e=>{const i=requireJobId(e);await l(e=>{const r=i in e.identities?i:findRuntimeUid(e.identities,i);return r?(delete e.identities[r],delete e.jobs[r],e.revisions[r]=nextRevision(e.revisions[r]),!0):i in e.jobs&&(delete e.jobs[i],e.revisions[i]=nextRevision(e.revisions[i]),!0)})},set:async(e,i)=>{const r=requireJobId(e),t=requireDuration(i);await l(e=>{const i=resolveOverrideKey(e,r);return e.jobs[i]!==t&&(e.jobs[i]=t,e.revisions[i]=nextRevision(e.revisions[i]),!0)})},setForUpdate:async(e,i)=>{const r=requireJobId(e),t=requireDuration(i);let n;if(await l(e=>{const i=resolveOverrideKey(e,r),o=e.jobs[i],s=nextRevision(e.revisions[i]);return e.jobs[i]=t,e.revisions[i]=s,n=Object.freeze({key:i,previous:o,revision:s}),!0}),!n)throw new Error("cron override update token was not published");return n},rollbackUpdate:async e=>{const i=requireJobId(e.key),r=requireRevision(e.revision),t=void 0===e.previous?void 0:requireDuration(e.previous);let n=!1;return await l(e=>e.revisions[i]===r&&(void 0===t?delete e.jobs[i]:e.jobs[i]=t,e.revisions[i]=nextRevision(r),n=!0,!0)),n},delete:async e=>{const i=requireJobId(e);await l(e=>{const r=resolveOverrideKey(e,i);return r in e.jobs&&(delete e.jobs[r],e.revisions[r]=nextRevision(e.revisions[r]),!0)})},reconcile:async e=>{const i=new Set(e.map(requireJobId));await l(e=>{let r=!1;for(const[t,n]of Object.entries(e.identities))null===n||i.has(n)||(delete e.identities[t],delete e.jobs[t],e.revisions[t]=nextRevision(e.revisions[t]),r=!0);for(const t of Object.keys(e.jobs))t in e.identities||i.has(t)||(delete e.jobs[t],e.revisions[t]=nextRevision(e.revisions[t]),r=!0);return r&&o?.debug?.(`[cron-overrides] reconciled stale entries, remaining=${Object.keys(e.jobs).length}`),r})}}}export function createInMemoryCronOverridesStore(e={}){const i={...e},r={},t={};let n=!1;return{initialize:async()=>{n=!0},isInitialized:()=>n,snapshot:async()=>(n||(n=!0),Object.freeze({jobs:Object.freeze({...i}),identities:Object.freeze({...r})})),get:e=>{if(!n)throw new Error("cron overrides store is not initialized");return i[resolveOverrideKey({identities:r},e)]},localId:e=>r[requireJobId(e)]??void 0,runtimeUid:e=>findRuntimeUid(r,requireJobId(e)),reserve:async(e,t)=>{e=requireJobId(e),r[e]??=null,void 0!==t&&(i[e]=requireDuration(t))},bind:async(e,i)=>{if(e=requireJobId(e),i=requireJobId(i),r[e]&&r[e]!==i)throw new Error("identity conflict");r[e]=i},removeIdentity:async e=>{const n=e in r?e:findRuntimeUid(r,e);n?(delete r[n],delete i[n],t[n]=nextRevision(t[n])):(delete i[e],t[e]=nextRevision(t[e]))},set:async(e,o)=>{n||(n=!0);const s=requireJobId(e),a=resolveOverrideKey({identities:r},s);i[a]=requireDuration(o),t[a]=nextRevision(t[a])},setForUpdate:async(e,n)=>{const o=resolveOverrideKey({identities:r},requireJobId(e)),s=Object.freeze({key:o,previous:i[o],revision:nextRevision(t[o])});return i[o]=requireDuration(n),t[o]=s.revision,s},rollbackUpdate:async e=>t[e.key]===e.revision&&(void 0===e.previous?delete i[e.key]:i[e.key]=e.previous,t[e.key]=nextRevision(e.revision),!0),delete:async e=>{const n=resolveOverrideKey({identities:r},requireJobId(e));delete i[n],t[n]=nextRevision(t[n])},reconcile:async e=>{const n=new Set(e.map(requireJobId));for(const[e,o]of Object.entries(r))null===o||n.has(o)||(delete r[e],delete i[e],t[e]=nextRevision(t[e]));for(const e of Object.keys(i))e in r||n.has(e)||(delete i[e],t[e]=nextRevision(t[e]))}}}function isStoreOptions(e){return"path"in e||"logger"in e||"lock"in e||"files"in e}function parseSnapshot(e){const i=JSON.parse(e);if(!i||"object"!=typeof i||Array.isArray(i))throw new Error("cron overrides snapshot must be an object");const r=i.jobs;if(!r||"object"!=typeof r||Array.isArray(r))throw new Error("cron overrides snapshot jobs must be an object");const t={};for(const[e,i]of Object.entries(r))t[requireJobId(e)]=requireDuration(i);const n=i.identities,o={};if(void 0!==n){if(!n||"object"!=typeof n||Array.isArray(n))throw new Error("cron overrides snapshot identities must be an object");for(const[e,i]of Object.entries(n))o[requireJobId(e)]=null===i?null:requireJobId(i)}const s=i.revisions,a={};if(void 0!==s){if(!s||"object"!=typeof s||Array.isArray(s))throw new Error("cron overrides snapshot revisions must be an object");for(const[e,i]of Object.entries(s))a[requireJobId(e)]=requireRevision(i)}return freezeSnapshot({jobs:t,identities:o,revisions:a})}function freezeSnapshot(e){return Object.freeze({jobs:Object.freeze({...e.jobs}),identities:Object.freeze({...e.identities}),revisions:Object.freeze({...e.revisions})})}function findRuntimeUid(e,i){return Object.entries(e).find(([,e])=>e===i)?.[0]}function resolveOverrideKey(e,i){return i in e.identities?i:findRuntimeUid(e.identities,i)??i}function requireJobId(e){if("string"!=typeof e||!e.trim()||e.length>512)throw new Error("cron override job id is invalid");return e}function requireDuration(e){if("number"!=typeof e||!Number.isSafeInteger(e)||e<=0)throw new Error("cron override duration must be a positive safe integer");return e}function requireRevision(e){if("number"!=typeof e||!Number.isSafeInteger(e)||e<=0)throw new Error("cron override revision must be a positive safe integer");return e}function nextRevision(e){const i=e??0;if(!Number.isSafeInteger(i)||i<0||i>=Number.MAX_SAFE_INTEGER)throw new Error("cron override revision overflow");return i+1}