beeos-claw 0.6.72 → 0.6.73
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/README.md +0 -13
- package/dist/a2a/tools.js +1 -1
- package/dist/acp-gateway/beeos-file-resolver.js +1 -1
- package/dist/acp-gateway/bridge-core.js +1 -1
- package/dist/acp-gateway/gateway-events.js +1 -1
- package/dist/acp-gateway/local-session-history.js +1 -1
- package/dist/acp-gateway/mcp-desired-state-controller.js +1 -1
- package/dist/acp-gateway/mcp-desired-state-store.js +1 -1
- package/dist/acp-gateway/mcp-service.js +1 -1
- package/dist/acp-server.js +1 -1
- package/dist/agent-sync.js +1 -1
- package/dist/approval/bridge.js +1 -0
- package/dist/approval/presentation.js +1 -0
- package/dist/approval/protocol.generated.js +1 -0
- package/dist/auto-reply-tokens.js +1 -1
- package/dist/automation/tools.js +1 -1
- package/dist/canvas/relay-client.js +1 -1
- package/dist/canvas/static-html.js +1 -1
- package/dist/canvas/tools.js +1 -1
- package/dist/canvas/ws-provider.js +1 -1
- package/dist/canvas-orchestrator.js +1 -1
- package/dist/chat-envelope.js +1 -0
- package/dist/connectors/harness-connector-mcp.js +1 -0
- package/dist/files/fetch-tool.js +1 -1
- package/dist/files/list-tool.js +1 -1
- package/dist/gateway-client.js +1 -1
- package/dist/im-inbound-handler.js +1 -1
- package/dist/im-runtime-dispatch.js +1 -1
- package/dist/index.js +1 -1
- package/dist/media/arouter-config.js +1 -1
- package/dist/media/image-provider.js +1 -1
- package/dist/media/video-provider.js +1 -1
- package/dist/message-client-composition.js +1 -1
- package/dist/message-projection-inbound-consumer.js +1 -1
- package/dist/plugin-registration-plan.js +1 -1
- package/dist/question/bridge.js +1 -0
- package/dist/runtime/agent-projection-production.js +1 -1
- package/dist/runtime/assistant-message-commit-router.js +1 -1
- package/dist/runtime/callback-queue.js +1 -1
- package/dist/runtime/chat-handler-port.js +1 -0
- package/dist/runtime/conversation-title.js +1 -1
- package/dist/runtime/core-execution-attempt-context.js +1 -1
- package/dist/runtime/deepseek-reasoning-stream.js +1 -1
- package/dist/runtime/im-reply-sink.js +1 -1
- package/dist/runtime/message-service-conversation-clear-http-authority.js +1 -1
- package/dist/runtime/message-service-conversation-run-boundary.js +1 -1
- package/dist/runtime/message-service-cron-delivery-http-authority.js +1 -1
- package/dist/runtime/message-service-runtime-authenticated-fetch.js +1 -1
- package/dist/runtime/message-service-runtime-claim-authority.js +1 -1
- package/dist/runtime/message-service-runtime-message-channel.js +1 -1
- package/dist/runtime/message-service-session-model-http-authority.js +1 -1
- package/dist/runtime/ms-runtime-message-channel-adapter.js +1 -1
- package/dist/runtime/ms-runtime-message-channel-consumer-factory.js +1 -1
- package/dist/runtime/ms-runtime-rpc-adapter.js +1 -1
- package/dist/runtime/native-cloud-chat-composition.js +1 -0
- package/dist/runtime/native-cloud-prompt-bridge.js +1 -0
- package/dist/runtime/native-input-files-http.js +1 -0
- package/dist/runtime/native-input-files.js +1 -0
- package/dist/runtime/native-output-file-attempt.js +1 -0
- package/dist/runtime/native-prompt-authority.js +1 -0
- package/dist/runtime/native-prompt-preflight.js +1 -0
- package/dist/runtime/native-session-new-bridge.js +1 -0
- package/dist/runtime/openclaw-runtime-capabilities.js +1 -1
- package/dist/runtime/openclaw-session-model.js +1 -0
- package/dist/runtime/openclaw-session-write-quiescence.js +1 -1
- package/dist/runtime/reply-router.js +1 -1
- package/dist/runtime/runtime-command-dispatcher.js +1 -1
- package/dist/runtime/runtime-command-journal.js +1 -1
- package/dist/runtime/runtime-error-part.js +1 -0
- package/dist/runtime/runtime-local-parity-matrix.js +1 -1
- package/dist/runtime/runtime-message-channel-contract.js +1 -1
- package/dist/runtime/runtime-operation-resource-resolver.js +1 -1
- package/dist/runtime/runtime-parity-manifest-digest.js +1 -1
- package/dist/runtime/runtime-parity-manifest.js +1 -1
- package/dist/runtime/runtime-production-authority.js +1 -1
- package/dist/runtime/runtime-service-contract-compatibility.js +1 -1
- package/dist/runtime/runtime-service-production-candidate.js +1 -1
- package/dist/runtime/runtime-session-control-service.js +1 -1
- package/dist/runtime/runtime-skills-service.js +1 -1
- package/dist/runtime/session-mutation-coordinator.js +1 -1
- package/dist/runtime/session-new-runtime-delivery-bridge.js +1 -1
- package/dist/runtime/session-set-model-mapping.js +1 -1
- package/dist/runtime/write-runtime-failure.js +1 -1
- package/dist/terminal/runtime-terminal-outbound-registration.js +1 -1
- package/dist/terminal/runtime-terminal-service.js +1 -1
- package/dist/terminal/terminal-authority-admission.js +1 -1
- package/dist/terminal/terminal-authority-contract.js +1 -1
- package/dist/terminal/terminal-lease-bound-bridge-client.js +1 -1
- package/dist/upload/desktop-screenshot-tool.js +1 -1
- package/dist/upload/finalize-hook.js +1 -1
- package/dist/upload/native-output-tool-hook.js +1 -0
- package/dist/upload/tool-artifact-enrichment.js +1 -1
- package/dist/upload/tool.js +1 -1
- package/dist/upload/upload-file.js +1 -1
- package/openclaw.plugin.json +1 -1
- package/package.json +12 -11
- package/runtime-service-wire-identity.v1.json +12 -12
- package/scripts/runtime-blackbox-worker-main.mjs +5 -2
- package/dist/agent-auth.js +0 -1
- package/dist/bridge-discovery.js +0 -1
- package/dist/canvas/static-html-result.js +0 -1
- package/dist/mcp-connector-proxy.js +0 -1
- package/dist/runtime/agent-gateway-runtime-attempt-authorization.js +0 -1
- package/dist/runtime/message-service-runtime-invocation-authority.js +0 -1
- package/dist/runtime/message-service-session-reasoning-http-authority.js +0 -1
- package/dist/runtime/openclaw-native-sessions.js +0 -1
- package/dist/runtime/openclaw-question-adapter.js +0 -1
- package/dist/runtime/question-wire.js +0 -1
- package/dist/runtime/runtime-jwks-verifier.js +0 -1
- package/dist/runtime/runtime-reasoning.js +0 -1
- package/dist/runtime/runtime-registration.js +0 -1
- package/dist/runtime/runtime-transport-failure-policy.js +0 -1
- package/dist/runtime/split-runtime-authorization-verifier.js +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
import{createHash,randomUUID}from"node:crypto";import{EventEmitter}from"node:events";import WebSocket from"ws";import{
|
|
1
|
+
import{createHash,randomUUID}from"node:crypto";import{EventEmitter}from"node:events";import WebSocket from"ws";import{createTerminalAgentAuth,terminalAgentAuthSigningMessage as harnessTerminalAgentAuthSigningMessage,terminalAgentWebSocketUrl}from"@beeos-ai/cloud-harness-sdk";export function terminalAgentAuthSigningMessage(e){return harnessTerminalAgentAuthSigningMessage({publicKey:e.public_key,timestamp:e.timestamp,nonce:e.nonce,runtimeLeaseCredential:e.runtimeLeaseCredential,handlerIdentity:e.handlerIdentity,runtimeEpoch:e.runtimeEpoch,leaseId:e.leaseId})}export function createTerminalLeaseBoundAgentAuth(e){if(!(32===e.keys.publicKey.length&&[32,48].includes(e.keys.privateKey.length)&&e.lease.instanceId&&e.lease.leaseId&&e.lease.handlerIdentity&&e.lease.runtimeLeaseCredential&&isPositiveRuntimeEpoch(e.lease.runtimeEpoch)&&Number.isSafeInteger(e.timestamp)&&!(e.timestamp<0)&&e.nonce))throw new Error("invalid Terminal runtime lease authority binding");return createTerminalAgentAuth({lease:{instanceId:e.lease.instanceId,leaseId:e.lease.leaseId,runtimeEpoch:e.lease.runtimeEpoch,runtimeLeaseCredential:e.lease.runtimeLeaseCredential,handlerIdentity:e.lease.handlerIdentity},keys:e.keys,timestamp:e.timestamp,nonce:e.nonce})}export class TerminalLeaseBoundBridgeClient extends EventEmitter{options;state="idle";socket;unsubscribe;expiryTimer;reconnectTimer;generation=0;stopped=!0;authenticatedBindingHash;authorityEpochBindingHash;transition=Promise.resolve();constructor(e){super(),this.options=e}async connect(){this.unsubscribe||(this.unsubscribe=this.options.authority.subscribeAuthority((e,t)=>this.onAuthorityChanged(e,t))),this.stopped=!1;const e=this.options.authority.authorityLease;if(!e)throw new Error("active runtime registration lease required for Terminal auth");await this.queueTransition(()=>this.replaceConnection(e))}async send(e){const t=this.socket;if("connected"!==this.state||!t||t.readyState!==WebSocket.OPEN)throw new Error("Terminal lease-bound Bridge is not connected");await new Promise((i,n)=>t.send(e,e=>e?n(e):i()))}async close(e=!0){this.stopped=!0,this.clearTimers(),this.unsubscribe?.(),this.unsubscribe=void 0,this.authenticatedBindingHash=void 0,this.authorityEpochBindingHash=void 0,this.generation++,await this.closeSocket(e),this.state="closed"}onAuthorityChanged(e,t){if(this.stopped)return;if(!e)return void this.fenceAuthority(void 0===t);this.scheduleExpiryFence(e);const i=authorityEpochHash(e),n=authorityBindingHash(e);return this.authorityEpochBindingHash&&i!==this.authorityEpochBindingHash?(this.authorityEpochBindingHash=i,this.emit("transportEpochChanged"),void this.queueTransition(()=>this.replaceConnection(e)).catch(e=>this.emitError(e))):n!==this.authenticatedBindingHash?"connected"===this.state&&this.authenticatedBindingHash?(this.authenticatedBindingHash=n,void(this.authorityEpochBindingHash??=i)):void this.queueTransition(()=>this.replaceConnection(e)).catch(e=>this.emitError(e)):void 0}async replaceConnection(e){if(this.stopped||null===this.options.authority.authorityLease)return;this.clearReconnectTimer();const t=++this.generation;if(await this.closeSocket(!1),this.stopped||t!==this.generation)return;this.state="connecting";const i=new WebSocket(terminalAgentWebSocketUrl(this.options.bridgeUrl));this.socket=i;const n=authorityBindingHash(e);await new Promise((s,r)=>{let o=!1;const a=setTimeout(()=>{c(new Error("Terminal auth timeout")),i.readyState!==WebSocket.CLOSED&&i.terminate()},this.options.authTimeoutMs??1e4),h=()=>{clearTimeout(a),i.off("open",u)},c=e=>{h(),o||r(e)},u=()=>{const e=this.options.authority.authorityLease;if(!e||authorityBindingHash(e)!==n||t!==this.generation)return c(new Error("Terminal runtime lease changed during auth")),void i.close();const s=createTerminalLeaseBoundAgentAuth({lease:e,keys:this.options.keys,timestamp:Math.floor((this.options.now??Date.now)()/1e3),nonce:(this.options.nonce??randomUUID)()});i.send(JSON.stringify(s),e=>{e&&c(e)})};i.on("open",u),i.on("message",r=>{if(!o){try{if("auth_ok"!==JSON.parse(r.toString()).type)return}catch{return}return o=!0,h(),t!==this.generation?void i.close():(this.authenticatedBindingHash=n,this.authorityEpochBindingHash=authorityEpochHash(e),this.state="connected",this.scheduleExpiryFence(e),this.emit("connected",{handlerIdentity:e.handlerIdentity}),void s())}this.emit("message",r)}),i.on("error",e=>{this.emitError(e),c(e)}),i.on("close",(e,i)=>{h(),o||r(new Error(`Terminal auth rejected: ${i.toString()||"connection closed"}`)),t===this.generation&&(this.socket=void 0,this.authenticatedBindingHash=void 0,this.state="idle",this.emit("disconnected",i.toString()||"connection closed"),1008!==e?!this.stopped&&this.options.authority.authorityLease&&this.scheduleReconnect():this.fenceAuthority(!o))})})}fenceAuthority(e){this.clearTimers(),this.authenticatedBindingHash=void 0,this.authorityEpochBindingHash=void 0,this.generation++;const t=this.socket;this.socket=void 0,this.state="idle",t&&t.readyState!==WebSocket.CLOSED&&t.terminate(),this.emit("authorityLost",{recoverable:e})}scheduleExpiryFence(e){this.expiryTimer&&clearTimeout(this.expiryTimer);const t=authorityBindingHash(e);this.expiryTimer=setTimeout(()=>{const e=this.options.authority.authorityLease;e?authorityBindingHash(e)===t&&(Date.parse(e.leaseExpiresAt)>(this.options.now??Date.now)()?this.scheduleExpiryFence(e):this.fenceAuthority(!0)):this.fenceAuthority(!0)},Math.min(2147483647,Math.max(0,Date.parse(e.leaseExpiresAt)-(this.options.now??Date.now)()))),this.expiryTimer.unref?.()}scheduleReconnect(){this.reconnectTimer||this.stopped||(this.reconnectTimer=setTimeout(()=>{this.reconnectTimer=void 0;const e=this.options.authority.authorityLease;e&&this.queueTransition(()=>this.replaceConnection(e)).catch(e=>{this.emitError(e),this.scheduleReconnect()})},this.options.reconnectDelayMs??1e3),this.reconnectTimer.unref?.())}async closeSocket(e){const t=this.socket;this.socket=void 0,t&&t.readyState!==WebSocket.CLOSED&&(e&&t.readyState===WebSocket.OPEN?await new Promise(e=>{t.once("close",()=>e()),t.close()}):t.terminate())}queueTransition(e){const t=this.transition.then(e,e);return this.transition=t.catch(()=>{}),t}clearReconnectTimer(){this.reconnectTimer&&clearTimeout(this.reconnectTimer),this.reconnectTimer=void 0}clearTimers(){this.clearReconnectTimer(),this.expiryTimer&&clearTimeout(this.expiryTimer),this.expiryTimer=void 0}emitError(e){this.listenerCount("error")>0&&this.emit("error",e instanceof Error?e:new Error(String(e)))}}function authorityEpochHash(e){return createHash("sha256").update(`${e.instanceId}\0${e.leaseId}\0${e.handlerIdentity}\0${e.runtimeEpoch}`).digest("hex")}function authorityBindingHash(e){return createHash("sha256").update(`${e.instanceId}\0${e.leaseId}\0${e.handlerIdentity}\0${e.runtimeEpoch}\0${e.runtimeLeaseCredential}\0${e.leaseExpiresAt}`).digest("hex")}function isPositiveRuntimeEpoch(e){return"string"==typeof e&&/^[1-9][0-9]*$/.test(e)&&BigInt(e)<=18446744073709551615n}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{access,constants}from"node:fs/promises";import{execFile}from"node:child_process";import{promisify}from"node:util";import{uploadLocalFile}from"./upload-file.js";const execFileAsync=promisify(execFile);export const BEEOS_DESKTOP_SCREENSHOT_TOOL_NAME="beeos_desktop_screenshot";export const BEEOS_DESKTOP_SCREENSHOT_TOOL_DESCRIPTION="Capture the agent desktop (DISPLAY) via platform beeos-screenshot, then upload for the user. Prefer this over shell/python X11 dumps. Do not invent raw Xlib/PIL capture — those produce RGB striping. Requires openclaw-desktop image with scrot (or ImageMagick import) and /usr/local/bin/beeos-screenshot.";export const BEEOS_DESKTOP_SCREENSHOT_TOOL_SCHEMA={title:"BeeOS desktop screenshot",type:"object",description:BEEOS_DESKTOP_SCREENSHOT_TOOL_DESCRIPTION,additionalProperties:!1,properties:{path:{type:"string",minLength:1,description:"Local filesystem path for the PNG capture (default: /tmp/desktop.png)."},upload:{type:"boolean",description:"When true (default), upload the capture so the user sees a chat preview."}}};const DEFAULT_PATH="/tmp/desktop.png",REQUEST_TIMEOUT_MS=12e4,CAPTURE_TIMEOUT_MS=3e4,SCREENSHOT_BINARIES=["/usr/local/bin/beeos-screenshot","beeos-screenshot"],MISSING_BINARY_MSG="beeos-screenshot not found on PATH. Use the openclaw-desktop image with scrot installed, or install scrot + /usr/local/bin/beeos-screenshot. Do not fall back to raw Xlib/PIL capture.";let _deps={};export function setDesktopScreenshotDeps(e){_deps=e}export function resetDesktopScreenshotDeps(){_deps={}}export async function executeDesktopScreenshot(e,t,r
|
|
1
|
+
import{access,constants}from"node:fs/promises";import{execFile}from"node:child_process";import{promisify}from"node:util";import{uploadLocalFile}from"./upload-file.js";const execFileAsync=promisify(execFile);export const BEEOS_DESKTOP_SCREENSHOT_TOOL_NAME="beeos_desktop_screenshot";export const BEEOS_DESKTOP_SCREENSHOT_TOOL_DESCRIPTION="Capture the agent desktop (DISPLAY) via platform beeos-screenshot, then upload for the user. Prefer this over shell/python X11 dumps. Do not invent raw Xlib/PIL capture — those produce RGB striping. Requires openclaw-desktop image with scrot (or ImageMagick import) and /usr/local/bin/beeos-screenshot.";export const BEEOS_DESKTOP_SCREENSHOT_TOOL_SCHEMA={title:"BeeOS desktop screenshot",type:"object",description:BEEOS_DESKTOP_SCREENSHOT_TOOL_DESCRIPTION,additionalProperties:!1,properties:{path:{type:"string",minLength:1,description:"Local filesystem path for the PNG capture (default: /tmp/desktop.png)."},upload:{type:"boolean",description:"When true (default), upload the capture so the user sees a chat preview."}}};const DEFAULT_PATH="/tmp/desktop.png",REQUEST_TIMEOUT_MS=12e4,CAPTURE_TIMEOUT_MS=3e4,SCREENSHOT_BINARIES=["/usr/local/bin/beeos-screenshot","beeos-screenshot"],MISSING_BINARY_MSG="beeos-screenshot not found on PATH. Use the openclaw-desktop image with scrot installed, or install scrot + /usr/local/bin/beeos-screenshot. Do not fall back to raw Xlib/PIL capture.";let _deps={};export function setDesktopScreenshotDeps(e){_deps=e}export function resetDesktopScreenshotDeps(){_deps={}}export async function executeDesktopScreenshot(e,t,r){const o=parseParams(e);if(!o.ok)return errorResult(o.error);const{path:s,upload:n}=o,a=_deps.execFile??execFileAsync,i=_deps.resolveBinary?await _deps.resolveBinary():await resolveScreenshotBinary();if(!i)return errorResult(MISSING_BINARY_MSG);try{await a(i,[s],{timeout:3e4,env:{...process.env,DISPLAY:process.env.DISPLAY||":1"},signal:r})}catch(e){if(r?.aborted)throw e;if("ENOENT"===e.code)return errorResult(MISSING_BINARY_MSG);return errorResult(`desktop capture failed: ${e instanceof Error?e.message:String(e)}`)}try{await access(s,constants.R_OK)}catch{return errorResult(`capture produced no readable file at ${s}`)}if(!n){const e={ok:!0,path:s};return{...e,content:[{type:"text",text:`Desktop captured to ${s} (not uploaded)`}],output:e,result:e,details:e,isError:!1}}if(!t)return errorResult("Agent Gateway client unavailable — file upload requires agentGatewayUrl and a key pair");const p=_deps.uploadLocalFile??uploadLocalFile;try{const e=await p(s,t,{signal:r,timeoutMs:12e4,fileNameOverride:`desktop-${Date.now()}.png`}),o=[{uri:e.uri,name:e.name,mimeType:e.mimeType,fileId:e.fileId}],n=`Desktop screenshot uploaded: ${o[0].name}`,a={ok:!0,path:s,files:o};return{...a,content:[{type:"text",text:n},{type:"resource_link",uri:o[0].uri,name:o[0].name,mimeType:o[0].mimeType}],output:a,result:a,details:a,isError:!1}}catch(e){if(r?.aborted)throw e;return errorResult(`capture ok at ${s} but upload failed: ${e instanceof Error?e.message:String(e)}`)}}async function resolveScreenshotBinary(){for(const e of SCREENSHOT_BINARIES){if(!e.includes("/"))return e;try{return await access(e,constants.X_OK),e}catch{continue}}return null}function parseParams(e){if(null==e)return{ok:!0,path:DEFAULT_PATH,upload:!0};if("object"!=typeof e)return{ok:!1,error:"params must be an object"};const t=e,r=new Set(["path","upload"]),o=Object.keys(t).filter(e=>!r.has(e));if(o.length>0)return{ok:!1,error:`unknown params: ${o.join(", ")}`};let s=DEFAULT_PATH;if(void 0!==t.path){if("string"!=typeof t.path||""===t.path.trim())return{ok:!1,error:"path must be a non-empty string"};s=t.path.trim()}let n=!0;if(void 0!==t.upload){if("boolean"!=typeof t.upload)return{ok:!1,error:"upload must be a boolean"};n=t.upload}return{ok:!0,path:s,upload:n}}function errorResult(e){const t={ok:!1,error:e};return{...t,content:[{type:"text",text:e}],output:t,result:t,details:t,isError:!0}}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{outstandingDeliverables,rememberSessionWorkspace,stashFinalizeAttachments}from"./delivery-ledger.js";import{getUploadDeps,uploadLocalFile}from"./upload-file.js";const MAX_FINALIZE_UPLOADS=5;export async function fileDeliveryFinalizeDecision(e){if(e.stopHookActive||!e.sessionKey)return;e.cwd&&rememberSessionWorkspace(e.sessionKey,e.cwd);const s=getUploadDeps();if(!s)return;const i=outstandingDeliverables(e.sessionKey).slice(0,5),o=[];for(const e of i)try{const i=await uploadLocalFile(e,s.
|
|
1
|
+
import{outstandingDeliverables,rememberSessionWorkspace,stashFinalizeAttachments}from"./delivery-ledger.js";import{getUploadDeps,uploadLocalFile}from"./upload-file.js";const MAX_FINALIZE_UPLOADS=5;export async function fileDeliveryFinalizeDecision(e){if(e.stopHookActive||!e.sessionKey)return;e.cwd&&rememberSessionWorkspace(e.sessionKey,e.cwd);const s=getUploadDeps();if(!s)return;const i=outstandingDeliverables(e.sessionKey).slice(0,5),o=[];for(const e of i)try{const i=await uploadLocalFile(e,s.gateway);o.push({uri:i.uri,name:i.name,...i.mimeType?{mimeType:i.mimeType}:{}})}catch(i){s.logger?.warn?.(`[file-delivery] finalize upload failed path=${e}: ${i instanceof Error?i.message:String(i)}`)}stashFinalizeAttachments(e.sessionKey,o)}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{prepareNativeOutputTool}from"../runtime/native-output-file-attempt.js";import{RENDER_UI_TOOL_NAME}from"../canvas/tools.js";import{BEEOS_UPLOAD_TOOL_NAME}from"./tool.js";export function needsNativeOutputCapability(t,o){return t===BEEOS_UPLOAD_TOOL_NAME||t===RENDER_UI_TOOL_NAME&&Object.prototype.hasOwnProperty.call(o,"html")}export function nativeOutputToolCallGate(t,o){if(needsNativeOutputCapability(t.toolName,t.params)){if(t.runId&&o.runId&&t.runId!==o.runId||t.toolCallId&&o.toolCallId&&t.toolCallId!==o.toolCallId)return{block:!0,blockReason:"native output file Host correlation conflict"};try{prepareNativeOutputTool(t.runId??o.runId,t.toolCallId??o.toolCallId)}catch{return{block:!0,blockReason:"native output file trusted capability unavailable"}}}}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{basename}from"node:path";import{BEEOS_UPLOAD_TOOL_NAME}from"./tool.js";import{BEEOS_DESKTOP_SCREENSHOT_TOOL_NAME}from"./desktop-screenshot-tool.js";import{extractUploadResourceLinks}from"./extract-upload-uris.js";import{resolveUploadToolResultForEnrichment,writeEnrichmentCache}from"./result-cache.js";import{tryExtractLocalFiles}from"./local-file-extractors.js";import{getUploadDeps,uploadLocalFile}from"./upload-file.js";const UPLOAD_TOOL_NAMES=new Set([BEEOS_UPLOAD_TOOL_NAME,BEEOS_DESKTOP_SCREENSHOT_TOOL_NAME]);export function isUploadArtifactToolName(e){if(!e)return!1;if(UPLOAD_TOOL_NAMES.has(e))return!0;const t=e.toLowerCase();return t.includes("beeos_upload")||t.includes("beeos upload file")||"beeos file upload"===t||t.includes("beeos_desktop_screenshot")||t.includes("desktop screenshot")}export function isRemoteOrPlatformMediaUri(e){const t=e.trim();return!!t&&(!!t.startsWith("beeos-file://")||(!(!t.startsWith("http://")&&!t.startsWith("https://"))||!!t.startsWith("data:")))}export function isLocalMediaPath(e){const t=e.trim();return!(!t||isRemoteOrPlatformMediaUri(t))&&(!!t.startsWith("file://")||!!t.startsWith("/"))}function filePathFromMediaUri(e){const t=e.trim();if(t.startsWith("file://"))try{return decodeURIComponent(new URL(t).pathname)}catch{return t.slice(7)}return t}function linkNameFromUri(e){if(e.startsWith("beeos-file://"))return e.slice(13)||"file";try{const t=e.includes("://")?new URL(e).pathname:e;return basename(t)||"file"}catch{return"file"}}function pushUnique(e,t,o){o.uri&&!t.has(o.uri)&&(t.add(o.uri),e.push(o))}export async function resolveToolArtifactLinks(e){const t=[],o=new Set,a=getUploadDeps(),i=a?.logger;let r=!1;if(isUploadArtifactToolName(e.toolName))try{const a=await resolveUploadToolResultForEnrichment(e.toolCallId,e.toolName===BEEOS_DESKTOP_SCREENSHOT_TOOL_NAME?BEEOS_DESKTOP_SCREENSHOT_TOOL_NAME:BEEOS_UPLOAD_TOOL_NAME);if(null!=a)for(const e of extractUploadResourceLinks(a))pushUnique(t,o,e)}catch(t){i?.warn?.(`[enrich] resolveUploadToolResultForEnrichment failed toolCallId=${e.toolCallId}: ${t instanceof Error?t.message:String(t)}`)}if(a){const l=new Set,s=tryExtractLocalFiles({toolCallId:e.toolCallId,toolName:e.toolName,rawResult:e.rawResult,rawText:e.rawText,params:e.params,cwd:e.cwd});for(const n of s)if(!l.has(n.filePath))try{i?.debug?.(`[enrich] Uploading local file ${n.filePath} for toolCallId=${e.toolCallId}`);const s=await uploadLocalFile(n.filePath,a.
|
|
1
|
+
import{basename}from"node:path";import{BEEOS_UPLOAD_TOOL_NAME}from"./tool.js";import{BEEOS_DESKTOP_SCREENSHOT_TOOL_NAME}from"./desktop-screenshot-tool.js";import{extractUploadResourceLinks}from"./extract-upload-uris.js";import{resolveUploadToolResultForEnrichment,writeEnrichmentCache}from"./result-cache.js";import{tryExtractLocalFiles}from"./local-file-extractors.js";import{getUploadDeps,uploadLocalFile}from"./upload-file.js";const UPLOAD_TOOL_NAMES=new Set([BEEOS_UPLOAD_TOOL_NAME,BEEOS_DESKTOP_SCREENSHOT_TOOL_NAME]);export function isUploadArtifactToolName(e){if(!e)return!1;if(UPLOAD_TOOL_NAMES.has(e))return!0;const t=e.toLowerCase();return t.includes("beeos_upload")||t.includes("beeos upload file")||"beeos file upload"===t||t.includes("beeos_desktop_screenshot")||t.includes("desktop screenshot")}export function isRemoteOrPlatformMediaUri(e){const t=e.trim();return!!t&&(!!t.startsWith("beeos-file://")||(!(!t.startsWith("http://")&&!t.startsWith("https://"))||!!t.startsWith("data:")))}export function isLocalMediaPath(e){const t=e.trim();return!(!t||isRemoteOrPlatformMediaUri(t))&&(!!t.startsWith("file://")||!!t.startsWith("/"))}function filePathFromMediaUri(e){const t=e.trim();if(t.startsWith("file://"))try{return decodeURIComponent(new URL(t).pathname)}catch{return t.slice(7)}return t}function linkNameFromUri(e){if(e.startsWith("beeos-file://"))return e.slice(13)||"file";try{const t=e.includes("://")?new URL(e).pathname:e;return basename(t)||"file"}catch{return"file"}}function pushUnique(e,t,o){o.uri&&!t.has(o.uri)&&(t.add(o.uri),e.push(o))}export async function resolveToolArtifactLinks(e){const t=[],o=new Set,a=getUploadDeps(),i=a?.logger;let r=!1;if(isUploadArtifactToolName(e.toolName))try{const a=await resolveUploadToolResultForEnrichment(e.toolCallId,e.toolName===BEEOS_DESKTOP_SCREENSHOT_TOOL_NAME?BEEOS_DESKTOP_SCREENSHOT_TOOL_NAME:BEEOS_UPLOAD_TOOL_NAME);if(null!=a)for(const e of extractUploadResourceLinks(a))pushUnique(t,o,e)}catch(t){i?.warn?.(`[enrich] resolveUploadToolResultForEnrichment failed toolCallId=${e.toolCallId}: ${t instanceof Error?t.message:String(t)}`)}if(a){const l=new Set,s=tryExtractLocalFiles({toolCallId:e.toolCallId,toolName:e.toolName,rawResult:e.rawResult,rawText:e.rawText,params:e.params,cwd:e.cwd});for(const n of s)if(!l.has(n.filePath))try{i?.debug?.(`[enrich] Uploading local file ${n.filePath} for toolCallId=${e.toolCallId}`);const s=await uploadLocalFile(n.filePath,a.gateway,{fileNameOverride:n.suggestedName});i?.info?.(`[enrich] Uploaded: fileId=${s.fileId} toolCallId=${e.toolCallId}`),l.add(n.filePath),pushUnique(t,o,{uri:s.uri,name:s.name,...s.mimeType?{mimeType:s.mimeType}:{}}),r=!0}catch(e){i?.warn?.(`[enrich] Upload failed for ${n.filePath}: ${e instanceof Error?e.message:String(e)}, skipping`)}for(const s of e.payloadMediaUrls??[]){if(!isLocalMediaPath(s))continue;const n=filePathFromMediaUri(s);if(!l.has(n))try{i?.debug?.(`[enrich] Uploading payload media path ${n} for toolCallId=${e.toolCallId}`);const s=await uploadLocalFile(n,a.gateway);i?.info?.(`[enrich] Uploaded payload media: fileId=${s.fileId} toolCallId=${e.toolCallId}`),l.add(n),pushUnique(t,o,{uri:s.uri,name:s.name,...s.mimeType?{mimeType:s.mimeType}:{}}),r=!0}catch(e){i?.warn?.(`[enrich] Payload media upload failed for ${n}: ${e instanceof Error?e.message:String(e)}, skipping`)}}}for(const a of e.payloadMediaUrls??[])isRemoteOrPlatformMediaUri(a)&&pushUnique(t,o,{uri:a.trim(),name:linkNameFromUri(a.trim())});if(t.length>0&&(r||t.some(e=>e.uri.startsWith("beeos-file://")))){const o=t.map(e=>({uri:e.uri,name:e.name,...e.mimeType?{mimeType:e.mimeType}:{}}));writeEnrichmentCache(e.toolCallId,o).catch(t=>{i?.warn?.(`[enrich] Cache write failed for toolCallId=${e.toolCallId}: ${t instanceof Error?t.message:String(t)}`)})}return t}
|
package/dist/upload/tool.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{constants}from"node:fs";import{access,stat}from"node:fs/promises";import{uploadLocalFile}from"./upload-file.js";import{isBeeosFileUri}from"../files/beeos-file-uri.js";export const BEEOS_UPLOAD_TOOL_NAME="beeos_upload_file";export const BEEOS_UPLOAD_TOOL_DESCRIPTION="Upload 1-5 local files from this machine to the user via the BeeOS platform. Accepts only local filesystem paths; never pass beeos-file:// URIs because they are already BeeOS platform resources. If the call succeeds, the files are already delivered to the user — do not use any other tool to send them again. Upload modified deliverables again even if the same path was uploaded earlier; unchanged contents may reuse an existing platform file. Any file type is accepted: text, images, documents, PDFs, archives, binaries.";export const BEEOS_UPLOAD_TOOL_SCHEMA={title:"BeeOS file upload",type:"object",description:BEEOS_UPLOAD_TOOL_DESCRIPTION,additionalProperties:!1,required:["paths"],properties:{paths:{type:"array",minItems:1,maxItems:5,description:"Local filesystem paths (1-5). Absolute or workspace-relative. Do not pass beeos-file:// URIs.",items:{type:"string",minLength:1,description:"Single local filesystem path, not a beeos-file:// URI."}}}};const MAX_PATHS=5
|
|
1
|
+
import{constants}from"node:fs";import{access,stat}from"node:fs/promises";import{uploadLocalFile,NativeOutputFilesError}from"./upload-file.js";import{isBeeosFileUri}from"../files/beeos-file-uri.js";export const BEEOS_UPLOAD_TOOL_NAME="beeos_upload_file";export const BEEOS_UPLOAD_TOOL_DESCRIPTION="Upload 1-5 local files from this machine to the user via the BeeOS platform. This is the only way to deliver a file to the user: whenever you produce or modify a file the user asked for, call this tool with its path. Writing the file to disk or printing it through a shell command does not deliver it. Accepts only local filesystem paths; never pass beeos-file:// URIs because they are already BeeOS platform resources. If the call succeeds, the files are already delivered to the user — do not use any other tool to send them again. Upload modified deliverables again even if the same path was uploaded earlier; unchanged contents may reuse an existing platform file. Any file type is accepted: text, images, documents, PDFs, archives, binaries.";export const BEEOS_UPLOAD_TOOL_SCHEMA={title:"BeeOS file upload",type:"object",description:BEEOS_UPLOAD_TOOL_DESCRIPTION,additionalProperties:!1,required:["paths"],properties:{paths:{type:"array",minItems:1,maxItems:5,description:"Local filesystem paths (1-5). Absolute or workspace-relative. Do not pass beeos-file:// URIs.",items:{type:"string",minLength:1,description:"Single local filesystem path, not a beeos-file:// URI."}}}};const MAX_PATHS=5;export const REQUEST_TIMEOUT_MS=12e4;const MAX_CONCURRENCY=3,BEEOS_FILE_URI_UPLOAD_ERROR="beeos-file:// is already a BeeOS platform file URI, not a local path. Do not upload it; display it directly.";async function runWithConcurrency(e,t){const r=new Array(e.length);let a=0;async function o(){for(;a<e.length;){const t=a++;r[t]=await e[t]()}}const i=[];for(let r=0;r<Math.min(t,e.length);r++)i.push(o());return await Promise.all(i),r}async function validateFiles(e){return Promise.all(e.map(async e=>{try{await access(e,constants.R_OK);return(await stat(e)).isFile()?{path:e,ok:!0}:{path:e,ok:!1,error:"not a regular file"}}catch(t){const r=t.code;let a;return a="ENOENT"===r?"file not found":"ENOTDIR"===r?"path component is not a directory":"EACCES"===r?"permission denied":t instanceof Error?t.message:String(t),{path:e,ok:!1,error:a}}}))}export async function executeUpload(e,t,r,a,o){const i=parseParams(e);if(!i.ok)return errorResult(i.error);if(!t&&!a)return errorResult("Agent Gateway client unavailable — file upload requires agentGatewayUrl and a key pair");const s=i.paths,n=(await validateFiles(s)).find(e=>!e.ok);if(n)return errorResult(`${n.path}: ${n.error}`);const l=[],c=s.map((e,i)=>async()=>{try{if(a){const t=await a(e,i);return{fileId:t.fileId,downloadUrl:"",fileName:t.name,mimeType:t.mimeType,size:t.size}}return await uploadSingleFile(e,t,r)}catch(t){if(r?.aborted)throw t;const{category:s,detail:n}=classifyUploadFailure(t,Boolean(a));return o?.warn?.(`[beeos_upload_file] upload failed index=${i} native=${Boolean(a)} category=${s} ${n}`),l.push(a?t instanceof NativeOutputFilesError?t.message:"native output Files transport or local read failed":`${e}: ${t instanceof Error?t.message:String(t)}`),null}}),p=(await runWithConcurrency(c,3)).filter(e=>null!==e);if(0===p.length)return errorResult(`All uploads failed: ${l.join("; ")}`);const u=p.map(e=>({uri:`beeos-file://${e.fileId}`,name:e.fileName,mimeType:e.mimeType,fileId:e.fileId})),f=p.length===s.length?`Uploaded ${p.length} file(s): ${u.map(e=>e.name).join(", ")}`:`Uploaded ${p.length}/${s.length} file(s): ${u.map(e=>e.name).join(", ")}. Failed: ${l.join("; ")}`,m={ok:!0,files:u};return{...m,content:[{type:"text",text:f},...u.map(e=>({type:"resource_link",uri:e.uri,name:e.name,mimeType:e.mimeType}))],output:m,result:m,details:m,isError:!1}}async function uploadSingleFile(e,t,r){const a=await uploadLocalFile(e,t,{signal:r,timeoutMs:12e4});return{fileId:a.fileId,downloadUrl:"",fileName:a.name,mimeType:a.mimeType,size:a.size}}function parseParams(e){if(!e||"object"!=typeof e)return{ok:!1,error:"params must be an object"};const t=e;if(Object.keys(t).filter(e=>"paths"!==e).length>0)return{ok:!1,error:"only paths is allowed"};if(!Array.isArray(t.paths))return{ok:!1,error:"paths must be an array"};if(t.paths.length<1)return{ok:!1,error:"paths must contain at least 1 item"};if(t.paths.length>5)return{ok:!1,error:"paths must contain no more than 5 items"};const r=[];for(let e=0;e<t.paths.length;e++){const a=t.paths[e];if("string"!=typeof a||""===a.trim())return{ok:!1,error:`paths[${e}] must be a non-empty string`};const o=a.trim();if(isBeeosFileUri(o))return{ok:!1,error:BEEOS_FILE_URI_UPLOAD_ERROR};r.push(o)}return{ok:!0,paths:r}}function classifyUploadFailure(e,t){const r=e instanceof Error?e.message:String(e),a=e instanceof Error&&e.cause instanceof Error?` cause=${e.cause.message}`:"";if(e instanceof NativeOutputFilesError)return{category:e.category,detail:`${e.detail??""} message=${r}`.trim()};if(e instanceof DOMException&&("AbortError"===e.name||"TimeoutError"===e.name))return{category:"aborted",detail:`name=${e.name} message=${r}`};if(e instanceof TypeError)return{category:"transport_rejected",detail:`message=${r}${a}`};const o=e?.code;return"string"==typeof o?{category:"local_read_failed",detail:`code=${o} message=${r}`}:{category:t?"attempt_or_lease_fenced":"unclassified",detail:`message=${r}${a}`}}function errorResult(e){const t={ok:!1,error:e};return{...t,content:[{type:"text",text:e}],output:t,result:t,details:t,isError:!0}}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{access,constants,readFile,stat}from"node:fs/promises";import{createHash}from"node:crypto";import{basename,extname,resolve}from"node:path";import{
|
|
1
|
+
import{access,constants,readFile,stat}from"node:fs/promises";import{request as httpsRequest}from"node:https";import{createHash}from"node:crypto";import{basename,extname,resolve}from"node:path";import{HarnessProtocolError,RuntimeLeaseHttp,RuntimeOperationFiles,putPresignedUpload}from"@beeos-ai/cloud-harness-sdk";import{noteUploadedDeliverable}from"./delivery-ledger.js";const DEFAULT_TIMEOUT_MS=3e4,UPLOAD_REUSE_TTL_MS=18e5,MAX_REUSED_UPLOADS=256,completedUploads=new Map;let claimedCloudRuntimeInstanceId,_deps=null;export function setClaimedCloudRuntimeInstanceId(e){claimedCloudRuntimeInstanceId=e}export function setUploadDeps(e){_deps=e}export function getUploadDeps(){return _deps}export async function uploadLocalFile(e,t,r){if(void 0!==claimedCloudRuntimeInstanceId)throw new Error("native output file trusted tool capability required");await access(e,constants.R_OK);if(!(await stat(e)).isFile())throw new Error("not a regular file");const i=basename(e),a=r?.fileNameOverride??i,s=inferMimeType(a),n=await readFile(e);r?.signal?.throwIfAborted();const o=createHash("sha256").update(n).digest("hex"),p=JSON.stringify([t.baseUrl.origin,resolve(e),a,o]),d=Date.now();for(const[e,t]of completedUploads)t.expiresAt<=d&&completedUploads.delete(e);const l=completedUploads.get(p);if(l)return noteUploadedDeliverable(e),{...l.result};const c=AbortSignal.timeout(r?.timeoutMs??3e4),u=r?.signal?AbortSignal.any([r.signal,c]):c,m=await t.files.upload({fileName:a,mimeType:s,data:n,signal:u}),f={fileId:m.fileId,uri:m.uri,name:m.fileName,mimeType:m.mimeType,size:n.byteLength};if(!f.fileId)throw new Error("confirm returned no fileId");return completedUploads.size>=256&&completedUploads.delete(completedUploads.keys().next().value),completedUploads.set(p,{result:f,expiresAt:Date.now()+18e5}),noteUploadedDeliverable(e),{...f}}export class NativeOutputFilesError extends Error{category;detail;constructor(e,t,r){super(e),this.category=t,this.detail=r,this.name="NativeOutputFilesError"}}export async function uploadNativeOutputFile(e,t,r,i,a,s){r.assertAttempt(),await access(e,constants.R_OK);const n=await stat(e);if(!n.isFile()||n.size<=0||n.size>104857600)throw new Error("native output file size invalid");const o=await readFile(e);if(r.assertAttempt(),o.byteLength!==n.size)throw new Error("native output file changed while reading");const p=basename(e),d=inferMimeType(p),l=createHash("sha256").update(o).digest("hex"),c=createHash("sha256").update("beeos/native-output-file/v1\0"+r.operationId+"\0"+r.toolCallId+"\0"+i).digest("hex"),u=new URL(t);if(!["http:","https:"].includes(u.protocol)||u.username||u.password||"/"!==u.pathname||u.search||u.hash)throw new Error("native output Files origin invalid");const m=AbortSignal.any([r.signal,AbortSignal.timeout(s??3e4),...a?[a]:[]]),f={requestMessageId:r.requestMessageId,paramsHash:r.paramsHash,activeRunRegistrationId:r.activeRunRegistrationId,idempotencyKey:c},h=e=>!!e&&"object"==typeof e&&!Array.isArray(e),g=new RuntimeOperationFiles(new RuntimeLeaseHttp({origin:u.origin})),w=async(e,t)=>{r.assertAttempt();const i={runtimeLeaseCredential:r.getLease().runtimeLeaseCredential},a={...f,...t};let s;try{s="presign"===e?await g.presignOutput(i,r.operationId,a,m):await g.confirmOutput(i,r.operationId,a,m)}catch(t){if(t instanceof Error&&"runtime lease response origin changed"===t.message)throw new NativeOutputFilesError("native output Files response origin changed","control_response_origin",`stage=${e}`);if(!(t instanceof HarnessProtocolError))throw t;let r;const i=t.status??0;if(t.body)try{const e=JSON.parse(t.body);h(e)&&h(e.error)&&"string"==typeof e.error.code&&/^[A-Za-z0-9_.:-]{1,128}$/.test(e.error.code)&&(r=e.error.code)}catch(t){if(!(t instanceof SyntaxError))throw new NativeOutputFilesError("native output Files error decode failed","control_error_decode",`stage=${e} http_status=${i}`)}throw new NativeOutputFilesError("native output Files "+e+" failed ("+i+(r?","+r:"")+")","control_http_error",`stage=${e} http_status=${i} code=${r??"none"}`)}if(r.assertAttempt(),"string"==typeof s||void 0===s)throw new SyntaxError("native output Files response is not JSON");if(!h(n=s)||n.operationId!==r.operationId||n.requestMessageId!==f.requestMessageId||n.paramsHash!==f.paramsHash||n.activeRunRegistrationId!==f.activeRunRegistrationId)throw new NativeOutputFilesError("native output Files response binding invalid","control_response_binding",`stage=${e}`);var n;return s},y=await w("presign",{filename:p,content_type:d,size_bytes:o.byteLength,checksum_sha256:l}),v=y.upload,_=y.transferPolicy;if(!h(v)||!h(_)||"string"!=typeof _.allowedOrigin||"upload"!==v.direction||"PUT"!==v.http_method||"string"!=typeof v.file_id||!v.file_id||"string"!=typeof v.url||"string"!=typeof v.expires_at||!Number.isFinite(Date.parse(v.expires_at))||Date.parse(v.expires_at)<=Date.now()||v.content_type!==d||v.size_bytes!==o.byteLength||v.checksum_sha256!==l||!h(v.required_headers)||Object.values(v.required_headers).some(e=>"string"!=typeof e))throw new NativeOutputFilesError("native output Files transfer descriptor invalid","transfer_descriptor_invalid",descriptorDefect(v,_,d,o.byteLength,l));const b=new URL(v.url),x=new URL(_.allowedOrigin);if("https:"!==b.protocol||b.username||b.password||b.hash||"https:"!==x.protocol||x.username||x.password||"/"!==x.pathname||x.search||x.hash||x.origin!==_.allowedOrigin||b.origin!==x.origin)throw new NativeOutputFilesError("native output Files transfer origin invalid","transfer_origin_invalid",`target_matches_allowed=${b.origin===x.origin}`);r.assertAttempt();const I=v.required_headers,O=(await putPresignedUpload({url:b.toString(),allowedOrigin:x.origin,body:o,requiredHeaders:I,signal:m,fetchImpl:()=>putRequiredHeaders(b,I,o,m)})).status;if(r.assertAttempt(),O<200||O>=300)throw new NativeOutputFilesError("native output Files PUT failed ("+O+")","put_rejected",`http_status=${O} bytes=${o.byteLength} headers=${Object.keys(I).join(",")}`);let E;try{E=await w("confirm",{fileId:v.file_id,checksumSha256:l})}catch(e){if(r.assertAttempt(),m.throwIfAborted(),!(e instanceof TypeError||e instanceof SyntaxError))throw e;E=await w("confirm",{fileId:v.file_id,checksumSha256:l})}if(E.fileId!==v.file_id||E.fileName!==p||E.mimeType!==d||E.size!==o.byteLength)throw new NativeOutputFilesError("native output Files ready metadata mismatch","ready_metadata_mismatch",`fileId=${E.fileId===v.file_id} fileName=${E.fileName===p} mimeType=${E.mimeType===d} size=${E.size===o.byteLength}`);r.assertAttempt();const F={fileId:v.file_id,uri:"beeos-file://"+v.file_id,name:p,mimeType:d,size:o.byteLength};return noteUploadedDeliverable(e),F}function putRequiredHeaders(e,t,r,i){const a=Object.keys(t).find(e=>"content-length"===e.toLowerCase());if(void 0!==a&&t[a]!==String(r.byteLength))throw new NativeOutputFilesError("native output Files transfer descriptor invalid","transfer_descriptor_invalid",`field=required_headers.content-length bytes=${r.byteLength}`);return new Promise((a,s)=>{const n=e=>{s(e instanceof Error&&"AbortError"===e.name?e:new TypeError("fetch failed",{cause:e}))},o=httpsRequest(e,{method:"PUT",headers:t,signal:i},e=>{e.on("error",n),e.resume(),e.on("end",()=>a(new Response(null,{status:e.statusCode??0})))});o.on("error",n),o.end(r)})}function descriptorDefect(e,t,r,i,a){const s=e=>!!e&&"object"==typeof e&&!Array.isArray(e);if(!s(e))return"upload=absent";if(!s(t)||"string"!=typeof t.allowedOrigin)return"transferPolicy.allowedOrigin=invalid";const n=[["direction","upload"===e.direction],["http_method","PUT"===e.http_method],["file_id","string"==typeof e.file_id&&e.file_id.length>0],["url","string"==typeof e.url],["expires_at","string"==typeof e.expires_at&&Number.isFinite(Date.parse(e.expires_at))&&Date.parse(e.expires_at)>Date.now()],["content_type",e.content_type===r],["size_bytes",e.size_bytes===i],["checksum_sha256",e.checksum_sha256===a],["required_headers",s(e.required_headers)&&Object.values(e.required_headers).every(e=>"string"==typeof e)]];return`field=${n.find(([,e])=>!e)?.[0]??"none"} bytes=${i}`}function inferMimeType(e){switch(extname(e).toLowerCase()){case".txt":return"text/plain";case".md":return"text/markdown";case".json":return"application/json";case".html":return"text/html";case".css":return"text/css";case".js":case".mjs":case".cjs":return"text/javascript";case".ts":return"text/x.typescript";case".tsx":case".jsx":return"text/jsx";case".xml":return"application/xml";case".yaml":case".yml":return"application/yaml";case".toml":return"application/toml";case".pdf":return"application/pdf";case".doc":return"application/msword";case".docx":return"application/vnd.openxmlformats-officedocument.wordprocessingml.document";case".xls":return"application/vnd.ms-excel";case".xlsx":return"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";case".ppt":return"application/vnd.ms-powerpoint";case".pptx":return"application/vnd.openxmlformats-officedocument.presentationml.presentation";case".rtf":return"application/rtf";case".odt":return"application/vnd.oasis.opendocument.text";case".ods":return"application/vnd.oasis.opendocument.spreadsheet";case".odp":return"application/vnd.oasis.opendocument.presentation";case".png":return"image/png";case".jpg":case".jpeg":return"image/jpeg";case".gif":return"image/gif";case".webp":return"image/webp";case".svg":return"image/svg+xml";case".mp4":return"video/mp4";case".mov":return"video/quicktime";case".webm":return"video/webm";case".mkv":return"video/x-matroska";case".avi":return"video/x-msvideo";case".m4v":return"video/x-m4v";case".mp3":return"audio/mpeg";case".wav":return"audio/wav";case".flac":return"audio/flac";case".aac":return"audio/aac";case".ogg":return"audio/ogg";case".m4a":return"audio/mp4";case".csv":return"text/csv";case".zip":return"application/zip";case".tar":return"application/x-tar";case".gz":case".tgz":return"application/gzip";default:return"application/octet-stream"}}
|
package/openclaw.plugin.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"id": "beeos-claw",
|
|
3
3
|
"name": "beeos-claw",
|
|
4
4
|
"description": "BeeOS platform bridge plugin for OpenClaw. Provides file upload/download, ACP bridge, and observability.",
|
|
5
|
-
"version": "0.6.
|
|
5
|
+
"version": "0.6.48-staging.1",
|
|
6
6
|
"activation": {
|
|
7
7
|
"onStartup": true
|
|
8
8
|
},
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "beeos-claw",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.73",
|
|
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>",
|
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
"verify:release-evidence": "node ./scripts/verify-release-evidence.mjs",
|
|
45
45
|
"verify:release-types-pin": "node ./scripts/verify-release-types-pin.mjs",
|
|
46
46
|
"verify:release-package": "npm run build:release && node ./scripts/verify-release-package.mjs",
|
|
47
|
-
"test:release-workflows": "node ./scripts/test-release-workflows.mjs && node --test ./scripts/run-ci-gates.test.mjs ./scripts/openclaw-promotion-receipt.test.mjs ./scripts/verify-tag-release-request.test.mjs ./scripts/openclaw-release-authority.test.mjs ./scripts/openclaw-release-dispatch.test.mjs ./scripts/openclaw-release-finalizer.test.mjs ./scripts/openclaw-release-completion.test.mjs ./scripts/openclaw-release-recovery.test.mjs ./scripts/verify-openclaw-dispatch-contract.test.mjs ./scripts/verify-promotion-evidence.test.mjs ./scripts/verify-package-equivalence.test.mjs ./scripts/verify-package-equivalence.e2e.test.mjs",
|
|
47
|
+
"test:release-workflows": "node ./scripts/test-release-workflows.mjs && node --test ./scripts/run-ci-gates.test.mjs ./scripts/openclaw-promotion-receipt.test.mjs ./scripts/verify-tag-release-request.test.mjs ./scripts/openclaw-release-authority.test.mjs ./scripts/openclaw-release-dispatch.test.mjs ./scripts/openclaw-release-finalizer.test.mjs ./scripts/openclaw-release-completion.test.mjs ./scripts/openclaw-release-recovery.test.mjs ./scripts/verify-openclaw-dispatch-contract.test.mjs ./scripts/verify-promotion-evidence.test.mjs ./scripts/verify-package-equivalence.test.mjs ./scripts/verify-package-equivalence.e2e.test.mjs ./scripts/cloud-staging-publish.test.mjs",
|
|
48
48
|
"ci:gates": "node ./scripts/run-ci-gates.mjs",
|
|
49
49
|
"check:runtime-blackbox-worker": "node --check ./scripts/runtime-blackbox-worker.mjs && node --check ./scripts/runtime-blackbox-worker-main.mjs && node --check ./scripts/runtime-blackbox-worker-lease.mjs && node --check ./scripts/runtime-blackbox-worker-lifecycle.mjs && node --check ./scripts/runtime-journal-checkpoint.mjs && node --test ./scripts/runtime-blackbox-worker-lease.test.mjs ./scripts/runtime-blackbox-worker-lifecycle.test.mjs ./scripts/runtime-blackbox-worker-launcher.test.mjs",
|
|
50
50
|
"prepublishOnly": "npm run verify:release-types-pin",
|
|
@@ -58,18 +58,19 @@
|
|
|
58
58
|
"build": "npm run clean && tsc -p tsconfig.build.json",
|
|
59
59
|
"build:release": "npm run build && npm run minify",
|
|
60
60
|
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
61
|
+
"question:verify": "node ./src/contracts/question/verify.mjs",
|
|
61
62
|
"lint": "npm run question:verify && npm run typecheck && eslint \"src/**/*.ts\"",
|
|
62
63
|
"minify": "find dist -name '*.js' -exec sh -c 'for file do terser \"$file\" --compress drop_console=true,drop_debugger=true --mangle --output \"$file.min\" && mv \"$file.min\" \"$file\" || exit 1; done' sh {} + && find dist -name '*.d.ts' -delete && find dist -name '*.d.ts.map' -delete",
|
|
63
64
|
"test": "npm run test:ci",
|
|
64
65
|
"test:ci": "node ./scripts/test-ci.mjs",
|
|
65
|
-
"test:watch": "vitest"
|
|
66
|
-
"question:verify": "node src/contracts/question/verify.mjs"
|
|
66
|
+
"test:watch": "vitest"
|
|
67
67
|
},
|
|
68
68
|
"dependencies": {
|
|
69
|
-
"@beeos-ai/beeos-types": "4.4.2-
|
|
69
|
+
"@beeos-ai/beeos-types": "4.4.2-cloud.3",
|
|
70
70
|
"@beeos-ai/bridge-client": "^0.1.0",
|
|
71
|
-
"@beeos-ai/
|
|
72
|
-
"@
|
|
71
|
+
"@beeos-ai/cloud-harness-sdk": "1.0.0-cloud.6",
|
|
72
|
+
"@beeos-ai/message-sdk": "2.0.16",
|
|
73
|
+
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
73
74
|
"adm-zip": "0.6.1",
|
|
74
75
|
"ws": "^8.18.0"
|
|
75
76
|
},
|
|
@@ -82,11 +83,11 @@
|
|
|
82
83
|
"eslint": "^9.39.4",
|
|
83
84
|
"terser": "^5.39.0",
|
|
84
85
|
"typescript": "^5.6.3",
|
|
85
|
-
"vitest": "^4.1.
|
|
86
|
+
"vitest": "^4.1.0"
|
|
86
87
|
},
|
|
87
88
|
"overrides": {
|
|
88
|
-
"fast-uri": ">=
|
|
89
|
-
"brace-expansion": ">=5.0.
|
|
89
|
+
"fast-uri": ">=3.1.4",
|
|
90
|
+
"brace-expansion": ">=5.0.9"
|
|
90
91
|
},
|
|
91
|
-
"gitHead": "
|
|
92
|
+
"gitHead": "c9034e954aec4d52d050f07cf3c8a4718a7a9947"
|
|
92
93
|
}
|
|
@@ -4,32 +4,32 @@
|
|
|
4
4
|
"hashAlgorithm": "SHA-256",
|
|
5
5
|
"canonicalTypes": {
|
|
6
6
|
"package": "@beeos-ai/beeos-types",
|
|
7
|
-
"version": "4.4.2-
|
|
7
|
+
"version": "4.4.2-cloud.3",
|
|
8
8
|
"runtimeRpcContractRevision": "2026-07-14.3",
|
|
9
|
-
"runtimeMethodManifestRawSHA256": "
|
|
10
|
-
"runtimeMethodManifestJcsSHA256": "
|
|
9
|
+
"runtimeMethodManifestRawSHA256": "f75832c57a18ca614e946a3b1d194828df76d600907f378efe28633df94146fc",
|
|
10
|
+
"runtimeMethodManifestJcsSHA256": "2e58dbc13b8bef2491b95f89920945cb905085828b2fa7a4d4f621624971b186"
|
|
11
11
|
},
|
|
12
12
|
"artifacts": [
|
|
13
13
|
{
|
|
14
14
|
"name": "runtime-parity-manifest",
|
|
15
15
|
"path": "openapi/runtime-parity-manifest-v1.json",
|
|
16
|
-
"revision": "beeos.runtime-parity.v1.
|
|
17
|
-
"rawSHA256": "
|
|
18
|
-
"jcsSHA256": "
|
|
16
|
+
"revision": "beeos.runtime-parity.v1.1.0",
|
|
17
|
+
"rawSHA256": "f1030e7bf2084f584d558c22b9bffde0f823d6950c696a6f0571adaffcdbcee0",
|
|
18
|
+
"jcsSHA256": "c98ed2aa822b29530cb02badd51654f2766f1dc0ff2c53b660be6431b1f23747"
|
|
19
19
|
},
|
|
20
20
|
{
|
|
21
21
|
"name": "runtime-message-channel",
|
|
22
22
|
"path": "openapi/runtime-message-channel-v1.contract.json",
|
|
23
|
-
"revision": "beeos.runtime-message-channel.v2.
|
|
24
|
-
"rawSHA256": "
|
|
25
|
-
"jcsSHA256": "
|
|
23
|
+
"revision": "beeos.runtime-message-channel.v2.0.0",
|
|
24
|
+
"rawSHA256": "dd791ce5f49b7bee1dba21a71e7b2cfbf4db7d77cb787aab5f04c7f4b0e3101b",
|
|
25
|
+
"jcsSHA256": "e950f1a86bf2bd8bf472603a91111c15b920fcac4c06fd8ac70e2c3f4c3a408f"
|
|
26
26
|
},
|
|
27
27
|
{
|
|
28
28
|
"name": "runtime-authority-ports",
|
|
29
29
|
"path": "openapi/runtime-authority-ports-v1.contract.json",
|
|
30
|
-
"revision": "beeos.runtime-authority-ports.v2.
|
|
31
|
-
"rawSHA256": "
|
|
32
|
-
"jcsSHA256": "
|
|
30
|
+
"revision": "beeos.runtime-authority-ports.v2.0.0",
|
|
31
|
+
"rawSHA256": "a2e9565ae617bcaeb4d837fe176a3602be041913d28a3f0a3a9ec2e3dae0d7b4",
|
|
32
|
+
"jcsSHA256": "3bd582f1f6babcdbe953a082faf813247c960f6bbf4c0f713e7fd92e27d163f5"
|
|
33
33
|
}
|
|
34
34
|
]
|
|
35
35
|
}
|
|
@@ -2,7 +2,7 @@ import { readFile } from "node:fs/promises";
|
|
|
2
2
|
import { createHash } from "node:crypto";
|
|
3
3
|
import { join } from "node:path";
|
|
4
4
|
import { GatewayClient } from "../dist/gateway-client.js";
|
|
5
|
-
import {
|
|
5
|
+
import { agentIdentityFromKeyPair, authenticatedAgentFetch, loadAgentKeyPair } from "@beeos-ai/cloud-harness-sdk";
|
|
6
6
|
import { OpenClawRuntimeCapabilities } from "../dist/runtime/openclaw-runtime-capabilities.js";
|
|
7
7
|
import { OpenClawRuntimeCommandDispatcher } from "../dist/runtime/runtime-command-dispatcher.js";
|
|
8
8
|
import { FileRuntimeAgentIdentityRegistry } from "../dist/runtime/runtime-agent-identity-registry.js";
|
|
@@ -48,6 +48,7 @@ export async function main() {
|
|
|
48
48
|
if (agentKeys.publicKey.length !== 32 || agentKeys.privateKey.length !== 32) {
|
|
49
49
|
throw new Error("blackbox Agent identity must contain 32-byte Ed25519 public/seed keys");
|
|
50
50
|
}
|
|
51
|
+
const agentIdentity = agentIdentityFromKeyPair(agentKeys);
|
|
51
52
|
const leaseClaims = decodeRuntimeLease(nonempty(authority.runtimeLeaseCredential,
|
|
52
53
|
"authority.runtimeLeaseCredential"));
|
|
53
54
|
const instanceId = requiredEnvironment("FIXTURE_INSTANCE_ID");
|
|
@@ -101,11 +102,13 @@ export async function main() {
|
|
|
101
102
|
});
|
|
102
103
|
const messageFetch = messageComposition.nodeTransport;
|
|
103
104
|
const boundAgentFetch = async (url, init = {}) => {
|
|
105
|
+
// The Gateway v2 signing preimage covers the path only, so a query would be unsigned.
|
|
106
|
+
if (new URL(url).search !== "") throw new Error("body-bound agent authority routes forbid query parameters");
|
|
104
107
|
const headers = new Headers(init.headers);
|
|
105
108
|
headers.set("X-Instance-ID", instanceId);
|
|
106
109
|
headers.set("X-Instance-Owner-ID", ownerId);
|
|
107
110
|
httpRequests += 1;
|
|
108
|
-
return await withinDeadline((signal) =>
|
|
111
|
+
return await withinDeadline((signal) => authenticatedAgentFetch(url, agentIdentity, { ...init, headers, signal }), deadline);
|
|
109
112
|
};
|
|
110
113
|
const statePrefix = join(receiptDir, "claw-worker");
|
|
111
114
|
const commandJournal = new FileRuntimeCommandJournal(`${statePrefix}-commands.json`);
|
package/dist/agent-auth.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import*as crypto from"node:crypto";import*as fs from"node:fs";import*as path from"node:path";export const EMPTY_BODY_SHA256="e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855";const ED25519_PKCS8_PREFIX=Buffer.from("302e020100300506032b657004220420","hex");function wrapPrivateKey(e){return 32===e.length?Buffer.concat([ED25519_PKCS8_PREFIX,Buffer.from(e)]):Buffer.from(e)}const ED25519_SPKI_PREFIX=Buffer.from("302a300506032b6570032100","hex");function derivePublicKey(e){const r=crypto.createPrivateKey({key:wrapPrivateKey(e),format:"der",type:"pkcs8"}),t=crypto.createPublicKey(r).export({type:"spki",format:"der"});return 44===t.length?new Uint8Array(t.subarray(12)):new Uint8Array(t)}export function loadAgentKeyPair(e){if(!fs.existsSync(e))return generateAgentKeyPair(e);const r=fs.readFileSync(e,"utf-8").trim();try{const e=JSON.parse(r);if(e.publicKey&&e.privateKey)return{publicKey:new Uint8Array(Buffer.from(e.publicKey,"base64")),privateKey:new Uint8Array(Buffer.from(e.privateKey,"base64"))}}catch{}const t=new Uint8Array(Buffer.from(r,"base64"));if(32===t.length)return{publicKey:derivePublicKey(t),privateKey:t};throw new Error(`Invalid key file format at ${e}`)}function generateAgentKeyPair(e){const r=crypto.generateKeyPairSync("ed25519",{publicKeyEncoding:{type:"spki",format:"der"},privateKeyEncoding:{type:"pkcs8",format:"der"}}),t=r.publicKey,n=r.privateKey,o=new Uint8Array(44===t.length?t.subarray(12):t),a=new Uint8Array(48===n.length?n.subarray(16):n),i=path.dirname(e);return fs.existsSync(i)||fs.mkdirSync(i,{recursive:!0}),fs.writeFileSync(e,JSON.stringify({publicKey:Buffer.from(o).toString("base64"),privateKey:Buffer.from(a).toString("base64")},null,2),{mode:384}),{publicKey:o,privateKey:a}}export function signAgentMessage(e,r){const t=crypto.createPrivateKey({key:wrapPrivateKey(r),format:"der",type:"pkcs8"});return new Uint8Array(crypto.sign(null,Buffer.from(e),t))}export function bodyHashHex(e){const r=null==e?Buffer.alloc(0):"string"==typeof e?Buffer.from(e,"utf8"):Buffer.from(e);return crypto.createHash("sha256").update(r).digest("hex")}export function agentAuthHeaders(e,r,t,n){const o=Math.floor(Date.now()/1e3).toString(),a=crypto.randomUUID(),i=bodyHashHex(n),f=signAgentMessage(`${e.toUpperCase()}|${r}||${i}|${o}|${a}`,t.privateKey);return{"X-Agent-Public-Key":Buffer.from(t.publicKey).toString("base64"),"X-Agent-Signature":Buffer.from(f).toString("base64"),"X-Agent-Timestamp":o,"X-Agent-Nonce":a}}export function agentBoundAuthHeaders(e,r,t,n){return agentAuthHeaders(e,r,n,t)}export async function agentFetch(e,r,t){const n="string"==typeof e?new URL(e):e,o=t?.method?.toUpperCase()??"GET",a=decodeURIComponent(n.pathname);let i;if(void 0!==t?.body&&null!==t.body){if(!("string"==typeof t.body||t.body instanceof Uint8Array))throw new Error("agent-authenticated request body must be string or Uint8Array for exact signing");i=t.body}const f=agentAuthHeaders(o,a,r,i),s=new Headers(t?.headers);for(const[e,r]of Object.entries(f))s.set(e,r);s.delete("X-Agent-Body-SHA256");const c=await fetch(n,{...t,method:o,headers:s,redirect:"error"});return assertFixedOrigin(n,c),c}export async function agentBoundFetch(e,r,t){if(""!==("string"==typeof e?new URL(e):e).search)throw new Error("body-bound agent authority routes forbid query parameters");return agentFetch(e,r,t)}function assertFixedOrigin(e,r){if(r.url&&new URL(r.url).origin!==e.origin)throw new Error("agent-authenticated response origin changed")}
|
package/dist/bridge-discovery.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export async function discoverBridgeFromGW(o,n,r){try{const i=r?`?region=${encodeURIComponent(r)}`:"",e=await n(`${o}/api/v1/bridge/config${i}`,{method:"GET"});if(e.ok){const o=await e.json();if(o.url)return{url:o.url,region:o.region}}}catch{}}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
const FILE_ID=/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;export function staticHtmlResultOf(t,e=0){if(e>8||null==t)return;if("string"==typeof t)try{return staticHtmlResultOf(JSON.parse(t),e+1)}catch{return}if(Array.isArray(t)){for(const i of t){const t=staticHtmlResultOf(i,e+1);if(t)return t}return}if("object"!=typeof t)return;const i=t;if(!0!==i.isError&&!1!==i.success){if(!0===i.success&&"static_html"===i.presentation){const t=i.artifact;return"canvas"===t?.kind&&"string"==typeof t.fileId&&FILE_ID.test(t.fileId)&&t.uri===`beeos-file://${t.fileId}`&&"text/html"===t.mimeType&&"string"==typeof t.title&&t.title.trim()?{success:!0,presentation:"static_html",..."string"==typeof i.surfaceId?{surfaceId:i.surfaceId}:{},artifact:{kind:"canvas",fileId:t.fileId,uri:t.uri,mimeType:"text/html",title:t.title.trim()}}:void 0}for(const t of["result","details","output","content","text"]){const r=staticHtmlResultOf(i[t],e+1);if(r)return r}}}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{agentFetch}from"./agent-auth.js";export const CONNECTOR_MCP_PROXY_PATH="/beeos/connectors/mcp";export const CONNECTOR_MCP_UPSTREAM_PATH="/api/v1/agent/connectors/mcp";export const CONNECTOR_MCP_MAX_BODY_BYTES=262144;class RequestBodyTooLargeError extends Error{}export function isLoopbackAddress(e){return"127.0.0.1"===e||"::1"===e||"::ffff:127.0.0.1"===e}export function isLoopbackRequest(e){return isLoopbackAddress(e.socket.remoteAddress)}export function readBoundedRawBody(e,t=262144){return new Promise((r,o)=>{const n=[];let s=0,a=!1;const c=()=>{e.off("data",u),e.off("end",i),e.off("error",p),e.off("aborted",f)},d=e=>{a||(a=!0,c(),o(e))},u=r=>{const o=Buffer.isBuffer(r)?r:Buffer.from(r);if(s+=o.byteLength,s>t)return c(),e.resume(),void d(new RequestBodyTooLargeError("connector MCP request body is too large"));n.push(o)},i=()=>{a||(a=!0,c(),r(Buffer.concat(n,s)))},p=e=>d(e),f=()=>d(new Error("connector MCP request was aborted"));e.on("data",u),e.on("end",i),e.on("error",p),e.on("aborted",f)})}function writeTextResponse(e,t,r){e.statusCode=t,e.setHeader("Content-Type","text/plain; charset=utf-8"),e.end(r)}function copyRequestHeader(e,t,r){const o=e.headers[r.toLowerCase()];"string"==typeof o?t.set(r,o):Array.isArray(o)&&t.set(r,o.join(", "))}export async function handleConnectorMcpRequest(e,t,r){if(!isLoopbackRequest(e))return e.resume(),void writeTextResponse(t,403,"loopback access required");if("POST"!==e.method)return e.resume(),t.setHeader("Allow","POST"),void writeTextResponse(t,405,"method not allowed");if(e.url?.includes("?"))return e.resume(),void writeTextResponse(t,400,"query parameters are not supported");if(!r.agentKeys||!r.agentGatewayUrl)return e.resume(),void writeTextResponse(t,503,"connector MCP proxy is not configured");let o;try{o=await readBoundedRawBody(e)}catch(e){return e instanceof RequestBodyTooLargeError?void writeTextResponse(t,413,"request body too large"):(r.logger?.warn?.(`[connector-mcp] failed to read request: ${String(e)}`),void(t.writableEnded||writeTextResponse(t,400,"invalid request body")))}const n=new Headers;copyRequestHeader(e,n,"Content-Type"),copyRequestHeader(e,n,"Accept"),copyRequestHeader(e,n,"MCP-Protocol-Version");const s=`${r.agentGatewayUrl.replace(/\/+$/,"")}${CONNECTOR_MCP_UPSTREAM_PATH}`;try{const e=await(r.fetcher??agentFetch)(s,r.agentKeys,{method:"POST",headers:n,body:new Uint8Array(o)}),a=Buffer.from(await e.arrayBuffer());t.statusCode=e.status;const c=e.headers.get("content-type");c&&t.setHeader("Content-Type",c),t.end(a)}catch(e){r.logger?.warn?.(`[connector-mcp] upstream request failed: ${String(e)}`),t.writableEnded||writeTextResponse(t,502,"connector MCP upstream unavailable")}}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{canonicalizeJcs}from"@beeos-ai/beeos-types/runtime";const GRANT_ISSUER="beeos-gateway",BOUNDARY_ISSUER="message-service";export class AgentGatewayRuntimeAttemptAuthorizationClient{options;constructor(e){this.options=e}async authorize(e){if(!sameLeaseFence(e.lease,e.fence)||e.request.payload.instanceId!==e.lease.instanceId)return{status:"fenced"};const t="start",n=JSON.stringify({executionGrantRef:e.request.payload.executionGrantRef,operationId:e.request.operationId,requestMessageId:e.request.messageId,method:e.request.payload.request.method,paramsHash:e.request.payload.paramsHash,runtimeLeaseCredential:e.lease.runtimeLeaseCredential,fence:e.fence,journalAttestationHash:e.journal.journalAttestationHash,journalRevisionBeforeAttempt:e.journal.journalRevisionBeforeAttempt,preparedEntryHash:e.journal.preparedEntryHash,executionMode:t}),a=await this.options.boundAgentFetch(new URL(this.options.authorizationUrl),{method:"POST",headers:{"content-type":"application/json"},body:n});if(401===a.status||403===a.status)return{status:"fenced"};if(!a.ok)throw new Error(`Agent Gateway runtime authorization failed (${a.status})`);const o=await a.json();if(isTerminalAuthorization(o))return{status:o.status};if(!isAuthorizedResponse(o))return{status:"invalid"};if(o.operationId!==e.request.operationId||o.requestMessageId!==e.request.messageId||o.method!==e.request.payload.request.method||o.paramsHash!==e.request.payload.paramsHash||canonicalizeJcs(o.command)!==canonicalizeJcs(e.request.payload.command))return{status:"invalid"};await this.options.verifier.refresh();let i=this.options.verifier.verifyAndDecodeGrant(o.runtimeExecutionGrant),r=this.options.verifier.verifyAndDecodeBoundary(o.executionBoundaryAssertion);if(i&&r||(await this.options.verifier.refresh(!0),i=this.options.verifier.verifyAndDecodeGrant(o.runtimeExecutionGrant),r=this.options.verifier.verifyAndDecodeBoundary(o.executionBoundaryAssertion)),!i||!r||o.executionBoundaryId!==r.claims.executionBoundaryId||o.executionBoundaryId!==r.claims.jti)return{status:"invalid"};const s=invocationContext(e.request,e.fence,i.claims,t,this.options.messageServicePublisherIdentityId??BOUNDARY_ISSUER,Math.floor((this.options.now??Date.now)()/1e3));if(!s)return{status:"invalid"};const d={grant:{header:i.header,claims:i.claims,grantJwt:o.runtimeExecutionGrant},boundary:{header:r.header,claims:r.claims,assertionJwt:o.executionBoundaryAssertion}};return{status:"authorized",attempt:{command:o.command,validationContext:s,authorization:d,derivationContext:{invocation:s,boundaryIssuer:BOUNDARY_ISSUER,boundarySubject:e.request.payload.instanceId,nowEpochSeconds:s.nowEpochSeconds,localJournalRevision:e.journal.journalRevisionBeforeAttempt,localPreparedEntryHash:e.journal.preparedEntryHash},executionBoundaryId:o.executionBoundaryId,boundaryAssertionHash:r.compactHash,executionMode:t}}}}function invocationContext(e,t,n,a,o,i){const r=e.payload.command,s={operationId:e.operationId,requestMessageId:e.messageId,dispatchEpoch:r.context.dispatchEpoch,activeRuntimeEpoch:t.runtimeEpoch,requestId:e.payload.request.id,method:e.payload.request.method,gatewayPublisherIdentityId:e.publisherIdentityId,messageServicePublisherIdentityId:o,runtimePublisherIdentityId:t.handlerIdentity,requestHash:e.payload.requestHash,paramsHash:e.payload.paramsHash,commandHash:e.payload.commandHash,idempotencyKeyHash:e.payload.idempotencyKeyHash,idempotencyFingerprint:e.payload.idempotencyFingerprint,callerIntentHash:e.payload.callerIntentHash,callerUserId:e.payload.callerUserId,callerIdentityId:e.payload.callerIdentityId,instanceId:e.payload.instanceId,runtimeInstanceUid:r.context.runtimeInstanceUid,executionGrantRef:e.payload.executionGrantRef,executionGrantHash:e.payload.executionGrantHash,claimDeadlineAt:e.payload.claimDeadlineAt,executionDeadlineAt:e.payload.executionDeadlineAt,grantIssuer:GRANT_ISSUER,grantSubject:t.handlerIdentity,grantJti:`${t.claimId}:${a}`,grantStartDeadline:Math.floor(Date.parse(e.payload.claimDeadlineAt)/1e3),grantExecutionMode:a,nowEpochSeconds:i,activeFence:t};return Number.isSafeInteger(s.grantStartDeadline)?"instance"===n.scope?{...s,scope:"instance",...void 0===n.runtimeAgentUid?{}:{runtimeAgentUid:n.runtimeAgentUid}}:"agent"===n.scope&&"agent"===r.context.scope&&n.platformAgentId&&n.runtimeAgentUid?{...s,scope:"agent",platformAgentId:n.platformAgentId,runtimeAgentUid:n.runtimeAgentUid,runtimeLocalKey:r.context.agent.runtimeLocalKey}:"conversation"===n.scope&&"conversation"===r.context.scope&&n.platformAgentId&&n.runtimeAgentUid&&n.platformConversationId&&n.runtimeConversationUid&&n.runtimeSessionKey?{...s,scope:"conversation",platformAgentId:n.platformAgentId,runtimeAgentUid:n.runtimeAgentUid,platformConversationId:n.platformConversationId,runtimeConversationUid:n.runtimeConversationUid,runtimeSessionKey:n.runtimeSessionKey}:null:null}function sameLeaseFence(e,t){return e.runtimeEpoch===t.runtimeEpoch&&e.handlerIdentity===t.handlerIdentity}function isTerminalAuthorization(e){return isRecord(e)&&1===Object.keys(e).length&&("expired"===e.status||"terminal"===e.status)}function isAuthorizedResponse(e){if(!isRecord(e))return!1;const t=Object.keys(e).sort(),n=["authorizedAt","command","disposition","executionBoundaryAssertion","executionBoundaryId","expiresAt","method","operationId","paramsHash","requestMessageId","runtimeExecutionGrant","status"].sort();return t.length===n.length&&t.every((e,t)=>e===n[t])&&"authorized"===e.status&&("created"===e.disposition||"reused"===e.disposition)&&[e.operationId,e.requestMessageId,e.method,e.paramsHash,e.runtimeExecutionGrant,e.executionBoundaryAssertion,e.executionBoundaryId,e.authorizedAt,e.expiresAt].every(e=>"string"==typeof e&&e.length>0)&&isRecord(e.command)&&Number.isFinite(Date.parse(e.authorizedAt))&&Number.isFinite(Date.parse(e.expiresAt))}function isRecord(e){return null!==e&&"object"==typeof e&&!Array.isArray(e)}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{createHash}from"node:crypto";import{RUNTIME_ERROR_DESCRIPTORS,RUNTIME_HASH_DOMAINS,runtimeHashPreimage}from"@beeos-ai/beeos-types/runtime";import{MessageServiceRuntimeClaimHttpAuthority}from"./message-service-runtime-claim-authority.js";const QUERY_METHODS=new Set(["skills/list","models/list","cron/list","cron/status","cron/runs","mcp/list"]),PROJECTION_METHODS=new Set(["agent/create","agent/update","agent/delete","agent/applyTemplate","session/set_model","session/set_reasoning"]);export class MessageServiceRuntimeInvocationHttpAuthority{deps;constructor(e){this.deps=e}claimNext=e=>this.claimAuthority().claimNext(e);claimExact=e=>this.claimAuthority().claimExact(e);renewClaim=e=>this.claimAuthority().renewClaim(e);releaseClaim=e=>this.claimAuthority().releaseClaim(e);authorize=e=>this.deps.authorization.authorize(e);async appendStarted(e){await this.append(e,"runtime_operation_started",{claimedAt:this.now(),leaseExpiresAt:e.claimLeaseExpiresAt,executionMode:e.attempt.executionMode,executionBoundaryId:e.attempt.executionBoundaryId,boundaryAssertionHash:e.attempt.boundaryAssertionHash,...e.attempt.handoffId?{handoffId:e.attempt.handoffId}:{}})}async appendProgress(e){await this.append(e,"runtime_operation_progress",{executionBoundaryId:e.attempt.executionBoundaryId,phase:e.progress.phase,...void 0===e.progress.current?{}:{current:e.progress.current},...void 0===e.progress.total?{}:{total:e.progress.total}})}async commitResult(e){const t=e.request.payload.request.method;if(PROJECTION_METHODS.has(t))throw new Error(`projection-required runtime method is not enabled by this authority: ${t}`);const o=this.now(),a=e.result,s=hash(RUNTIME_HASH_DOMAINS.serviceResult,{method:t,result:a});let r;if(!QUERY_METHODS.has(t)){const s=hash(RUNTIME_HASH_DOMAINS.coreResult,{method:t,result:a}),n={schemaVersion:1,operationId:e.operationId,method:t,coreResultRef:`sha256:${s}`,coreResultHash:s},i={schemaVersion:1,operationId:e.operationId,requestMessageId:e.requestMessageId,method:t,commandHash:e.request.payload.commandHash,executionBoundaryId:e.attempt.executionBoundaryId,boundaryAssertionHash:e.attempt.boundaryAssertionHash,fence:e.fence,journalRevision:"3",coreResultHash:s,effectRecordHash:hash(RUNTIME_HASH_DOMAINS.effectRecord,n),projection:{state:"not_required"},committedAt:o};r=hash(RUNTIME_HASH_DOMAINS.effectReceipt,i),await this.append(e,"runtime_effect_committed",{executionBoundaryId:e.attempt.executionBoundaryId,committedAt:o,effectReceipt:i,effectReceiptHash:r})}const n={schemaVersion:1,operationId:e.operationId,method:t,outcome:"succeeded",serviceResultRef:`sha256:${s}`,serviceResultHash:s},i={schemaVersion:1,operationId:e.operationId,requestMessageId:e.requestMessageId,method:t,commandHash:e.request.payload.commandHash,executionBoundaryId:e.attempt.executionBoundaryId,boundaryAssertionHash:e.attempt.boundaryAssertionHash,fence:e.fence,journalRevision:"3",terminalRecordHash:hash(RUNTIME_HASH_DOMAINS.terminalRecord,n),completedAt:o,outcome:"succeeded",serviceResultHash:s,...r?{effectReceiptHash:r}:{}};await this.appendTerminal(e,i,{outcome:"succeeded",response:{jsonrpc:"2.0",id:e.request.payload.request.id,result:a},serviceResultHash:s,terminalAuthority:r?{kind:"effect_receipt",effectReceiptHash:r}:{kind:"query_execution",executionBoundaryId:e.attempt.executionBoundaryId,boundaryAssertionHash:e.attempt.boundaryAssertionHash},completedAt:o})}async commitFailure(e){const t=e.request.payload.request.method,o=this.now(),a=normalizeErrorCode(e.outcome,e.code),s=RUNTIME_ERROR_DESCRIPTORS[a],r="outcome_unknown"===e.outcome?"outcome_unknown":"cancelled"===e.outcome?"cancelled":"expired"===e.outcome?"expired":"failed",n={code:s.jsonRpcCode,message:a,data:{code:a,operationId:e.operationId,effectState:r,retryMode:s.retryMode}},i=hash(RUNTIME_HASH_DOMAINS.terminalRecord,{operationId:e.operationId,method:t,outcome:e.outcome,code:a}),c="outcome_unknown"===e.outcome?{schemaVersion:1,operationId:e.operationId,method:t,outcome:e.outcome,uncertaintyRef:`sha256:${i}`,uncertaintyHash:i}:{schemaVersion:1,operationId:e.operationId,method:t,outcome:e.outcome,errorRef:`sha256:${i}`,errorHash:i},d={schemaVersion:1,operationId:e.operationId,requestMessageId:e.requestMessageId,method:t,commandHash:e.request.payload.commandHash,executionBoundaryId:e.attempt.executionBoundaryId,boundaryAssertionHash:e.attempt.boundaryAssertionHash,fence:e.fence,journalRevision:"2",terminalRecordHash:hash(RUNTIME_HASH_DOMAINS.terminalRecord,c),completedAt:o},u="outcome_unknown"===e.outcome?{...d,outcome:"outcome_unknown",uncertaintyHash:i}:{...d,outcome:e.outcome,noEffect:!0,errorHash:i},m={jsonrpc:"2.0",id:e.request.payload.request.id,error:n},p=terminalFailurePayload(e.outcome,o,m,e.attempt.executionBoundaryId,e.attempt.boundaryAssertionHash);await this.appendTerminal(e,u,p)}claimAuthority(){return new MessageServiceRuntimeClaimHttpAuthority({messageServiceUrl:this.deps.messageServiceUrl,fetchAuthorized:(e,t)=>this.deps.fetchMessageService(e,t)})}async append(e,t,o){await this.post(e.operationId,{schemaVersion:1,type:t,operationId:e.operationId,conversationId:e.operationId,requestMessageId:e.requestMessageId,dispatchEpoch:e.request.payload.command.context.dispatchEpoch,fence:e.fence,payload:o})}async appendTerminal(e,t,o){await this.post(e.operationId,{schemaVersion:1,type:"runtime_rpc_response",operationId:e.operationId,conversationId:e.operationId,requestMessageId:e.requestMessageId,dispatchEpoch:e.request.payload.command.context.dispatchEpoch,fence:e.fence,payload:o,replyTo:e.requestMessageId,terminalReceipt:t,terminalReceiptHash:hash(RUNTIME_HASH_DOMAINS.terminalReceipt,t)})}async post(e,t){const o=await this.deps.fetchMessageService(new URL(`/api/v1/runtime/operations/${encodeURIComponent(e)}/events`,this.deps.messageServiceUrl),{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(t)});if(201!==o.status)throw new Error(`Message Service runtime event append failed (${o.status})`)}now(){return(this.deps.now??(()=>new Date))().toISOString()}}function hash(e,t){return createHash("sha256").update(runtimeHashPreimage(e,t)).digest("hex")}function normalizeErrorCode(e,t){return"outcome_unknown"===e?"OUTCOME_UNKNOWN":"cancelled"===e?"OPERATION_CANCELLED":"expired"===e?"COMMAND_EXPIRED":t in RUNTIME_ERROR_DESCRIPTORS?t:"RUNTIME_EXECUTION_FAILED"}function terminalFailurePayload(e,t,o,a,s){if("outcome_unknown"===e)return{outcome:e,response:o,terminalAuthority:{kind:"uncertain_effect",executionBoundaryId:a,boundaryAssertionHash:s},completedAt:t};const r={kind:"definitive_no_effect",journalPhase:"prepared",observedAt:t};return"cancelled"===e?{outcome:e,response:o,cancellationEvidence:r,completedAt:t}:"expired"===e?{outcome:e,response:o,expirationEvidence:r,completedAt:t}:{outcome:e,response:o,failureEvidence:r,completedAt:t}}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export async function projectSessionReasoning(e){const n=await e.fetchAuthorized(new URL(`/api/v1/runtime/conversations/${encodeURIComponent(e.conversationId)}/metadata/reasoning`,e.messageServiceUrl),{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({schemaVersion:1,operationId:e.operationId,requestMessageId:e.requestMessageId,requestHash:e.requestHash,runtimeLeaseCredential:e.runtimeLeaseCredential,reasoning:e.reasoning})}),s=await n.json(),o=s.data??s;if(!n.ok||"projected"!==o.status||o.conversationId!==e.conversationId||!o.reasoning||Object.keys(e.reasoning).some(n=>o.reasoning[n]!==e.reasoning[n]))throw new Error("Message Service reasoning projection receipt mismatch")}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{existsSync,readFileSync,readdirSync,realpathSync}from"node:fs";import{createRequire}from"node:module";import{dirname,join,resolve}from"node:path";const adapters=new Map,SUPPORTED_HOST_VERSIONS=new Set(["2026.9.8","2026.9.9","2026.10.3"]);export function getOpenClawNativeSessions(){const e=process.env.OPENCLAW_ROOT?.trim()||hostRoot();if(!e)return;const n=JSON.parse(readFileSync(join(e,"package.json"),"utf8"));if("openclaw"!==n.name)return;if(!SUPPORTED_HOST_VERSIONS.has(n.version??""))throw new Error(`unsupported OpenClaw native session contract: ${n.version}`);const t=adapters.get(e);if(t)return t;const r=createRequire(join(e,"package.json")),s=r("openclaw/plugin-sdk/session-store-runtime"),o=join(e,"dist"),a=new Set(readdirSync(o).flatMap(e=>{if(!/^runs-.*\.mjs$/u.test(e))return[];const n=runEndExportName(readFileSync(join(o,e),"utf8"));return n?[r(join(o,e))[n]]:[]}));if(1!==a.size)throw new Error("OpenClaw native run-end facade is missing or ambiguous");const[i]=a;if("function"!=typeof s.getSessionEntry||"function"!=typeof s.loadTranscriptEventsSync||"function"!=typeof s.readTranscriptStatsSync||"function"!=typeof i)throw new Error("OpenClaw native session contract is unavailable");const c=i,p={getEntry:(e,n)=>s.getSessionEntry({agentId:e,sessionKey:n,readConsistency:"latest"}),readEvents:(e,n,t)=>{const r={agentId:e,sessionKey:n,sessionId:t},o=s.readTranscriptStatsSync(r);if(!Number.isSafeInteger(o.sizeBytes)||o.sizeBytes<0||o.sizeBytes>1048576||!Number.isSafeInteger(o.eventCount)||o.eventCount<0||o.eventCount>6400)throw new Error("native transcript exceeds the local recovery budget; use Gateway history pagination");return s.loadTranscriptEventsSync(r)},waitForRunEnd:(e,n)=>c(e,n)};return adapters.set(e,p),p}function runEndExportName(e){for(const n of e.matchAll(/export \{([^}]*)\}/gu))for(const e of n[1].split(",")){const[n,t=n]=e.trim().split(/\s+as\s+/u);if("waitForEmbeddedAgentRunEnd"===n)return t}}function hostRoot(){if(!process.argv[1]||!existsSync(process.argv[1]))return;let e=dirname(realpathSync(resolve(process.argv[1])));for(let n=0;n<4;n++){const n=join(e,"package.json");if(existsSync(n)&&"openclaw"===JSON.parse(readFileSync(n,"utf8")).name)return e;const t=dirname(e);if(t===e)break;e=t}}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{decodeResponse,QuestionError}from"../contracts/question/codec.js";import{createHash,randomUUID}from"node:crypto";import{isDeepStrictEqual}from"node:util";import{asRecord,nativeAnswers,nonEmpty,projectQuestion,questionRecord,USER_INPUT_SCHEMA}from"./question-wire.js";export function observeQuestionTool(e,t,s){const i=asRecord(t),n=asRecord(s);if(!i||!n)return;const r=i.toolName;if("ask_user"!==r)return;const o=n.runId??i.runId,a=n.toolCallId??i.toolCallId;nonEmpty(n.sessionKey)&&nonEmpty(a)&&nonEmpty(o)&&(void 0===n.agentId||nonEmpty(n.agentId))&&e.observeNativeTool(r,{sessionKey:n.sessionKey,toolCallId:a,runId:o,agentId:n.agentId})}export class OpenClawQuestionAdapter{options;turns=new Set;allowed=new Map;routes=new Map;byWire=new Map;tasks=new Set;timer;polling=!1;started=!1;onEvent=(...e)=>{if("question.requested"===e[0]&&this.background(this.accept(e[1])),"question.resolved"===e[0]){const t=asRecord(e[1])?.id;"string"==typeof t&&this.background(this.refreshRoute(t))}};onConnected=()=>{this.background(this.reconcile())};constructor(e){this.options=e}start(){this.started||(this.started=!0,this.options.gateway.on("gateway-event",this.onEvent),this.options.gateway.on("connected",this.onConnected),this.timer=setInterval(()=>this.background(this.reconcile()),this.options.heartbeatMs??3e4),this.timer.unref?.())}beginTurn(e){if(!this.started||!e.senderId.startsWith("user:")||e.senderId.length<=5)throw new Error("Question turn requires an authenticated human source");e.assertCurrent();const t=asRecord(e.deliveryContext),s=asRecord(t?.scope);if(t?.parent_message_id!==e.turnMessageId||t?.target_agent_id!==e.targetAgentId||t?.delivery_principal!==e.instanceId||t?.runtime_epoch!==e.runtimeEpoch||!["task","conversation_turn"].includes(String(s?.kind))||!nonEmpty(s?.id))throw new Error("Question origin is not source bound");const i={context:e,live:!0,ids:new Set};return this.turns.add(i),{bindRun:e=>{i.live&&(i.context.runId=e)},observeTool:(e,t)=>{this.observeTool(i,e,t)},close:()=>this.closeTurn(i)}}observeNativeTool(e,t){if(t.sessionKey&&t.toolCallId)for(const s of this.turns){const i=s.context;i.sessionKey!==t.sessionKey||t.runId&&i.runId!==t.runId||t.agentId&&i.localAgentId!==t.agentId||this.observeTool(s,e,t.toolCallId)}}observeTool(e,t,s){if("ask_user"!==t||!s||!this.current(e))return;const i=`ask_${createHash("sha256").update(`${e.context.runId}\0${s}`).digest("hex").slice(0,32)}`;e.ids.add(i),this.allowed.set(i,e),this.background(this.refreshRoute(i))}async stop(){this.started=!1,this.timer&&clearInterval(this.timer),this.options.gateway.off("gateway-event",this.onEvent),this.options.gateway.off("connected",this.onConnected),await Promise.allSettled([...this.turns].map(async e=>{try{await e.context.cancelRun("question_adapter_shutdown")}finally{this.closeTurn(e)}})),await Promise.allSettled([...this.tasks])}async handleContinue(e){const t=asRecord(e.content),s="string"==typeof t?.request_id?this.byWire.get(t.request_id):void 0;if(!s)return this.rejectMissing(e);if(!this.authorized(s,e))return;const i=(s.answering??Promise.resolve()).then(async()=>{if(!this.authorized(s,e))return;if(s.terminal)return void this.reject(s,e.id,"expired"===s.terminal?"PENDING_EXPIRED":"PENDING_ALREADY_RESOLVED");if(!s.delivered)return void this.reject(s,e.id,"PENDING_NOT_FOUND");if(s.attempt?.uncertain&&(await this.receipt(s),s.attempt?.uncertain&&!s.terminal))return"deferred";let t;try{t=decodeResponse(e.content,e.type),nativeAnswers(s.native,t)}catch(t){return void this.reject(s,e.id,t instanceof QuestionError?t.code:"INVALID_CONTENT")}await this.submit(s,e,t)});s.answering=i;try{return await i}finally{s.answering===i&&(s.answering=void 0)}}async reconcile(){if(this.started&&!this.polling&&0!==this.allowed.size){this.polling=!0;try{const e=asRecord(await this.options.gateway.request("question.list",{},5e3));if(!Array.isArray(e?.questions))throw new Error("Invalid question.list result");const t=new Set;for(const s of e.questions){const e=asRecord(s)?.id;if("string"!=typeof e||!this.allowed.has(e))continue;t.add(e),await this.accept(s);const i=this.routes.get(e);i&&i.attempt?.uncertain&&await this.receipt(i)}for(const[e,s]of this.routes)!this.current(s.turn)||s.terminal||t.has(e)||await this.refreshRoute(e)}finally{this.polling=!1}}}current(e){if(!this.started||!e.live||e.context.signal.aborted)return!1;try{return e.context.assertCurrent(),!0}catch{return!1}}authorized(e,t){return this.authorizedTurn(e.turn,t)}authorizedTurn(e,t){const s=e.context,i=asRecord(t.deliveryContext),n=asRecord(s.deliveryContext),r=asRecord(i?.scope),o=asRecord(n?.scope);return this.current(e)&&t.conversationId===s.conversationId&&t.senderId===s.senderId&&i?.target_agent_id===s.targetAgentId&&i.delivery_principal===s.instanceId&&i.runtime_epoch===s.runtimeEpoch&&i.parent_message_id===s.turnMessageId&&r?.kind===o?.kind&&r?.id===o?.id&&void 0!==o?.id&&void 0===t.replyTo&&(void 0===s.historyGeneration||t.historyGeneration===s.historyGeneration)}async rejectMissing(e){if(!this.started)return;const t=asRecord(e.content);if(!nonEmpty(t?.request_id))return;let s="PENDING_NOT_FOUND";try{decodeResponse(e.content,e.type)}catch(e){s=e instanceof QuestionError?e.code:"INVALID_CONTENT"}for(const i of this.turns)if(this.authorizedTurn(i,e))return void i.context.publishStatus({request_id:t.request_id,status:"rejected",response_message_id:e.id,error_code:s,updated_at_ms:Date.now()});const i=asRecord(e.deliveryContext);if(this.options.assertDeliveryCurrent&&i&&nonEmpty(i.parent_message_id)&&void 0===e.replyTo&&e.senderId?.startsWith("user:"))try{this.options.assertDeliveryCurrent(e);const n=await this.options.client.messages.getEnvelope(e.conversationId,i.parent_message_id);this.options.assertDeliveryCurrent(e);const r=asRecord(n.deliveryContext);if("chat_message"!==n.type||n.conversationId!==e.conversationId||n.senderId!==e.senderId||n.historyGeneration!==e.historyGeneration||!sameOrigin(r,e.deliveryContext))return;const o=`receipt_${createHash("sha256").update(`${e.conversationId}\0${e.id}\0${i.delivery_principal}`).digest("hex").slice(0,32)}`,a={conversationId:e.conversationId,clientMessageId:o,idempotencyKey:o,type:"agent_reply",deliveryContext:e.deliveryContext,content:{}},d=this.options.client.messages.startStream(a),c=await d.opened();if(this.options.assertDeliveryCurrent(e),"duplicate"===c.outcome)return;d.addPart({type:"custom",kind:"message_processing_receipt",data:{request_id:t.request_id,response_message_id:e.id,status:"rejected",error_code:s,observed_at_ms:Date.now()}}),await d.finalize()}catch{return"deferred"}}owns(e,t){const s=e.context;return t.agentId===s.localAgentId&&t.sessionKey===s.sessionKey&&t.runId===s.runId}async refreshRoute(e){const t=this.allowed.get(e);if(!t||!this.current(t))return;const s=this.routes.get(e);try{const t=asRecord(await this.options.gateway.request("question.get",{id:e},5e3));if(asRecord(t?.question)?.id!==e)return void this.markUnavailable(e,s);await this.accept(t?.question);const i=questionRecord(t?.question);if(!i||i.id!==e)return void this.markUnavailable(e,s)}catch(t){if("INVALID_REQUEST"===asRecord(t)?.code)return void this.markUnavailable(e,s);throw t}}markUnavailable(e,t){const s=this.routes.get(e);t&&s===t&&!s.terminal&&this.current(s.turn)&&(this.status(s,"unavailable"),s.terminal="unavailable")}async accept(e){const t=questionRecord(e);if(!t){const t=asRecord(e),s="string"==typeof t?.id?this.allowed.get(t.id):void 0;if(!s||!this.current(s)||t?.agentId!==s.context.localAgentId||t.sessionKey!==s.context.sessionKey||t.runId!==s.context.runId||"pending"!==t.status)return;const i=Array.isArray(t.questions)&&t.questions.some(e=>{const t=asRecord(e);return!0===t?.isSecret||void 0!==t?.secretStore});return this.sourceError(s,i?"SECRET_INPUT_UNSUPPORTED":"INVALID_CONTENT"),void await s.context.cancelRun("question_profile_unsupported")}const s=this.allowed.get(t.id);if(!s||!this.current(s)||!this.owns(s,t))return;let i=this.routes.get(t.id);if(i){if(i.terminal)return;if(i.turn!==s||JSON.stringify(i.native.questions)!==JSON.stringify(t.questions))return;i.native=t}else{if("pending"!==t.status)return;try{projectQuestion(t,"profile_validation")}catch{return this.sourceError(s,"INVALID_CONTENT"),void await s.context.cancelRun("question_profile_unsupported")}if([...this.routes.values()].some(e=>e.turn===s&&!e.terminal))return this.sourceError(s,"PENDING_INPUT_EXISTS"),void await s.context.cancelRun("question_profile_multiple_pending");i={turn:s,native:t,requestId:`qreq_${randomUUID()}`,messageId:randomUUID(),delivered:!1},this.routes.set(t.id,i),this.byWire.set(i.requestId,i)}"pending"===t.status?(i.delivered||await this.deliver(i),i.attempt?i.attempt.uncertain&&this.status(i,"unconfirmed",{submission_message_id:i.attempt.messageId}):this.status(i,i.delivered?"pending":"delivery_unconfirmed")):"answered"===t.status?i.attempt?await this.receipt(i):(this.status(i,"answered_elsewhere"),i.terminal="answered_elsewhere"):i.attempt&&"cancelled"===t.status?(this.status(i,"unconfirmed",{submission_message_id:i.attempt.messageId,native_status:"cancelled"}),i.terminal="unconfirmed"):i.attempt&&"expired"===t.status?(this.reject(i,i.attempt.messageId,"PENDING_EXPIRED"),i.terminal="expired"):(this.status(i,t.status),i.terminal=t.status)}async deliver(e){if(e.sending)return await e.sending,void(e.delivered||await this.reconcileDelivery(e));const t=(async()=>{const t=e.turn.context;if(this.current(e.turn))try{const s={conversationId:t.conversationId,agentId:t.targetAgentId,clientMessageId:e.messageId,idempotencyKey:e.messageId,type:"agent.input_required",deliveryContext:t.deliveryContext,content:projectQuestion(e.native,e.requestId)};"outcome_unknown"===(await this.options.client.messages.send(s)).outcome?await this.reconcileDelivery(e):this.current(e.turn)&&(e.delivered=!0)}catch(t){await this.reconcileDelivery(e),e.delivered||(this.status(e,"delivery_unconfirmed"),this.options.logger.warn?.("[question] durable prompt delivery unconfirmed"))}})();e.sending=t,await t}async reconcileDelivery(e){if(!this.current(e.turn))return;const t=e.turn.context;try{const s=await this.options.client.messages.getEnvelope(t.conversationId,e.messageId);!this.current(e.turn)||"agent.input_required"!==s.type||s.conversationId!==t.conversationId||s.senderId!==t.instanceId&&s.senderId!==t.targetAgentId||void 0!==t.historyGeneration&&s.historyGeneration!==t.historyGeneration||!sameOrigin(asRecord(s.deliveryContext),t.deliveryContext)||!isDeepStrictEqual(s.content,projectQuestion(e.native,e.requestId))||(e.delivered=!0)}catch{}}async submit(e,t,s){let i=!1;try{const n=asRecord(await this.options.gateway.request("question.get",{id:e.native.id},5e3)),r=questionRecord(n?.question);if(!r||!this.owns(e.turn,r)||!this.authorized(e,t))return;if("pending"!==r.status)return await this.accept(r),void this.reject(e,t.id,"expired"===r.status?"PENDING_EXPIRED":"PENDING_ALREADY_RESOLVED");if(JSON.stringify(r.questions)!==JSON.stringify(e.native.questions))return;if(Date.now()>=r.expiresAtMs)return void this.reject(e,t.id,"PENDING_EXPIRED");const o=nativeAnswers(r,s);if(!this.authorized(e,t))return;e.attempt={messageId:t.id,resolutionId:randomUUID(),uncertain:!1},this.status(e,"submitted",{submission_message_id:t.id}),i=!0;const a=asRecord(await this.options.gateway.request("question.resolve","cancelled"===s.outcome?{id:r.id,cancel:!0,resolvedBy:t.senderId}:{id:r.id,answers:o,resolvedBy:t.senderId,resolutionId:e.attempt.resolutionId},5e3));if(a?.status!==("cancelled"===s.outcome?"cancelled":"answered"))throw new Error("Invalid question.resolve result");if(!this.current(e.turn))return;this.status(e,"cancelled"===s.outcome?"cancelled":"accepted",{submission_message_id:t.id}),e.terminal="cancelled"===s.outcome?"cancelled":"accepted"}catch(s){if(!this.current(e.turn)||e.terminal)return;i&&e.attempt?!0===asRecord(s)?.gatewayRejected?(e.attempt=void 0,this.reject(e,t.id,"INVALID_CONTENT"),await this.refreshRoute(e.native.id)):(e.attempt.uncertain=!0,this.status(e,"unconfirmed",{submission_message_id:t.id}),await this.receipt(e)):this.status(e,"pending",{error_code:"question_read_unavailable"})}}async receipt(e){const t=e.attempt;if(t&&!e.terminal&&this.current(e.turn))try{const s=asRecord(await this.options.gateway.request("question.waitAnswer",{id:e.native.id,timeoutMs:1,includeResolutionId:!0},3e3));if(!this.current(e.turn)||e.attempt!==t||e.terminal)return;if("answered"===s?.status){const i=nonEmpty(s.resolutionId)?s.resolutionId===t.resolutionId?"accepted":"answered_elsewhere":"unconfirmed";"answered_elsewhere"===i?this.reject(e,t.messageId,"PENDING_ALREADY_RESOLVED"):this.status(e,i,{submission_message_id:t.messageId,native_status:"answered"}),e.terminal=i}else"cancelled"!==s?.status&&"expired"!==s?.status||("expired"===s.status?this.reject(e,t.messageId,"PENDING_EXPIRED"):this.status(e,"unconfirmed",{submission_message_id:t.messageId,native_status:"cancelled"}),e.terminal="expired"===s.status?"expired":"unconfirmed")}catch{}}sourceError(e,t){this.current(e)&&e.context.publishStatus({request_id:`qreq_${randomUUID()}`,status:"unsupported",error_code:t,updated_at_ms:Date.now()})}status(e,t,s={}){this.current(e.turn)&&e.turn.context.publishStatus({schema_version:USER_INPUT_SCHEMA,request_id:e.requestId,status:t,updated_at_ms:Date.now(),...s})}reject(e,t,s){this.status(e,"rejected",{response_message_id:t,error_code:s})}closeTurn(e){e.live=!1,this.turns.delete(e);for(const t of e.ids){this.allowed.get(t)===e&&this.allowed.delete(t);const s=this.routes.get(t);s?.turn===e&&(this.routes.delete(t),this.byWire.delete(s.requestId))}}background(e){const t=e.catch(()=>{this.options.logger.warn?.("[question] native question observation unavailable")}).finally(()=>this.tasks.delete(t));this.tasks.add(t)}}function sameOrigin(e,t){const s=asRecord(t),i=asRecord(e?.scope),n=asRecord(s?.scope);return!!(e&&s&&i&&n&&e.target_agent_id===s.target_agent_id&&e.delivery_principal===s.delivery_principal&&e?.runtime_epoch===s.runtime_epoch&&e?.parent_message_id===s.parent_message_id&&i.kind===n.kind&&i.id===n.id)}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{QUESTION_SCHEMA,decodeQuestion,decodeResponse,validateResponse,QuestionError}from"../contracts/question/codec.js";export const USER_INPUT_SCHEMA=QUESTION_SCHEMA;export const LEGACY_USER_INPUT_SCHEMA="openclaw.user_input.v1";export const questionRecord=e=>{const t=asRecord(e);if(!(t&&nonEmpty(t.id)&&nonEmpty(t.agentId)&&nonEmpty(t.sessionKey)&&nonEmpty(t.runId)&&["pending","answered","cancelled","expired"].includes(String(t.status))&&"number"==typeof t.expiresAtMs&&Number.isFinite(t.expiresAtMs)&&Array.isArray(t.questions)&&0!==t.questions.length))return;const o=[];for(const e of t.questions){const t=asRecord(e);if(!(t&&!0!==t.isSecret&&void 0===t.secretStore&&void 0===t.url&&nonEmpty(t.questionId)&&"string"==typeof t.header&&nonEmpty(t.question)&&Array.isArray(t.options)))return;const s=[];for(const e of t.options){const t=asRecord(e);if(!t||!nonEmpty(t.label)||void 0!==t.description&&"string"!=typeof t.description)return;s.push({label:t.label,..."string"==typeof t.description?{description:t.description}:{}})}if(void 0!==t.multiSelect&&"boolean"!=typeof t.multiSelect||void 0!==t.isOther&&"boolean"!=typeof t.isOther)return;o.push({questionId:t.questionId,header:t.header,question:t.question,options:s,multiSelect:!0===t.multiSelect,isOther:!0===t.isOther})}return new Set(o.map(e=>e.questionId)).size===o.length?{id:t.id,agentId:t.agentId,sessionKey:t.sessionKey,runId:t.runId,status:t.status,expiresAtMs:t.expiresAtMs,questions:o}:void 0};export function projectQuestion(e,t){for(const t of e.questions)if(t.options.length<2||new Set(t.options.map(e=>e.label)).size!==t.options.length)throw new QuestionError("INVALID_CONTENT");return decodeQuestion({schema_version:USER_INPUT_SCHEMA,kind:"question",request_id:t,message:"请回答以下问题,以继续当前任务。",expires_at:new Date(e.expiresAtMs).toISOString(),questions:e.questions.map((e,t)=>({id:e.questionId,header:e.header||`问题 ${t+1}`,text:e.question,input:{type:e.multiSelect?"multi_select":"single_select",allow_other:!0===e.isOther,options:e.options.map((e,t)=>({value:`option_${t+1}`,...e}))}}))})}export function parseContinue(e){try{return decodeResponse(e)}catch{return}}export function nativeAnswers(e,t){if(validateResponse(projectQuestion(e,t.request_id),t),"cancelled"===t.outcome)return;const o=Object.create(null);for(const s of e.questions){const e=t.answers[s.questionId];o[s.questionId]=void 0!==e.text?[e.text]:e.values.map(e=>{const t=Number(e.slice(7))-1;return s.options[t].label})}return{answers:o}}export const asRecord=e=>"object"!=typeof e||null===e||Array.isArray(e)?void 0:e;export const nonEmpty=e=>"string"==typeof e&&e.length>0;
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{createHash,createPublicKey,verify}from"node:crypto";import{validateRuntimeExecutionGrant}from"@beeos-ai/beeos-types/runtime";export class RuntimeJwksVerifier{jwksUrl;fetcher;ttlMs;now;keys=new Map;expiresAt=0;inflight;provenance;keyMaterials=Object.freeze([]);keysetUrl;constructor(e,t=fetch,r=3e5,s=Date.now){this.jwksUrl=e,this.fetcher=t,this.ttlMs=r,this.now=s,this.keysetUrl=new URL(e).toString()}async refresh(e=!1){if(!(!e&&this.keys.size>0&&this.now()<this.expiresAt)){if(this.inflight)return await this.inflight;this.inflight=this.load();try{await this.inflight}finally{this.inflight=void 0}}}hasKid(e){return this.keys.has(e)}trustedKids(){return Object.freeze([...this.keys.keys()].sort())}trustedKeyMaterials(){return this.keyMaterials}documentProvenance(){return this.provenance}verifyAndDecodeGrant(e){const t=this.verifyCompact(e);return t?{header:t.header,claims:t.claims,compactHash:sha256(e)}:null}verifyAndValidateGrant(e,t){const r=this.verifyAndDecodeGrant(e);return r&&0===validateRuntimeExecutionGrant(r.claims,t).length?r:null}verifyAndDecodeBoundary(e){const t=this.verifyCompact(e);return t?{header:t.header,claims:t.claims,compactHash:sha256(e)}:null}async load(){const e=await this.fetcher(this.jwksUrl,{signal:AbortSignal.timeout(1e4),redirect:"error"});if(!e.ok)throw new Error(`JWKS fetch failed: HTTP ${e.status}`);const t=await e.json(),r=new Map,s=[];for(const e of t.keys??[]){if("RSA"!==e.kty||"sig"!==e.use||"RS256"!==e.alg||"string"!=typeof e.kid||!e.kid)continue;if(r.has(e.kid))throw new Error(`JWKS contains duplicate kid: ${e.kid}`);const t=createPublicKey({key:e,format:"jwk"});if((t.asymmetricKeyDetails?.modulusLength??0)<2048)continue;const i=t.export({format:"jwk"});"RSA"===i.kty&&"string"==typeof i.n&&"string"==typeof i.e&&(r.set(e.kid,t),s.push(Object.freeze({jwkThumbprint:sha256(JSON.stringify({e:i.e,kty:"RSA",n:i.n})),spkiSha256:createHash("sha256").update(t.export({format:"der",type:"spki"})).digest("hex")})))}if(0===r.size)throw new Error("JWKS contains no trusted RS256 keys");const i=e.url?new URL(e.url).toString():this.keysetUrl;if(i!==this.keysetUrl)throw new Error("JWKS response provenance URL mismatch");this.keys=r,this.keyMaterials=Object.freeze(s.sort((e,t)=>e.jwkThumbprint.localeCompare(t.jwkThumbprint)||e.spkiSha256.localeCompare(t.spkiSha256))),this.provenance=Object.freeze({sourceUrl:i,etag:e.headers.get("etag")??"",issuer:"string"==typeof t.issuer?t.issuer:"",audience:"string"==typeof t.audience?t.audience:"",purpose:"string"==typeof t.purpose?t.purpose:"",maxClaimTtlSeconds:"number"==typeof t.maxClaimTtlSeconds?t.maxClaimTtlSeconds:Number.NaN}),this.expiresAt=this.now()+this.ttlMs}verifyCompact(e){const t=e.split(".");if(3!==t.length)return null;try{if(!t.every(isCanonicalBase64Url))return null;const e=JSON.parse(decodeBase64Url(t[0]));if("RS256"!==e.alg||"JWT"!==e.typ||"string"!=typeof e.kid)return null;const r=this.keys.get(e.kid);if(!r)return null;const s=Buffer.from(`${t[0]}.${t[1]}`),i=Buffer.from(t[2],"base64url");if(!verify("RSA-SHA256",s,r,i))return null;const n=JSON.parse(decodeBase64Url(t[1]));return null===n||"object"!=typeof n||Array.isArray(n)?null:{header:e,claims:n}}catch{return null}}}function isCanonicalBase64Url(e){if(!e||!/^[A-Za-z0-9_-]+$/.test(e))return!1;return Buffer.from(e,"base64url").toString("base64url")===e}function decodeBase64Url(e){return Buffer.from(e,"base64url").toString("utf8")}function sha256(e){return createHash("sha256").update(e).digest("hex")}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export function runtimeReasoningOptions(e){const t=e.reasoningOptions&&"object"==typeof e.reasoningOptions?e.reasoningOptions:void 0,n=t?.levels??e.thinkingLevels;if(!Array.isArray(n)||!n.length||n.length>32)return;const i=new Map;for(const e of n){const t="string"==typeof e?{id:e}:e;if(!t||"object"!=typeof t||"string"!=typeof t.id||!t.id.trim()||t.id.length>64||/\s/.test(t.id)||i.has(t.id))return;if(void 0!==t.label&&("string"!=typeof t.label||t.label.length>128))return;i.set(t.id,{id:t.id,..."string"==typeof t.label?{label:t.label}:{}})}const l=t&&"defaultLevel"in t?t.defaultLevel:e.thinkingDefault;return null!=l&&"string"!=typeof l||"string"==typeof l&&!i.has(l)?void 0:{levels:[...i.values()],defaultLevel:"string"==typeof l?l:null}}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{createHash,randomBytes,sign}from"node:crypto";import{RUNTIME_INTERNAL_API_V1_ROUTE_CONTRACTS,RUNTIME_HASH_DOMAINS,RUNTIME_RPC_PROTOCOL_VERSION,runtimeHashPreimage,runtimeIdentitySignatureMessage,validateRuntimeInternalRegistrationResult}from"@beeos-ai/beeos-types/runtime";import{RuntimeTransportFailurePolicy}from"./runtime-transport-failure-policy.js";export class RuntimeRegistrationLeaseExpiredError extends Error{constructor(){super("Instance authority returned an expired active lease"),this.name="RuntimeRegistrationLeaseExpiredError"}}export class RuntimeRegistrationHandoffRequiredError extends Error{retryAfterMs;constructor(e=5e3){super("Instance authority requires waiting for the active runtime lease to expire"),this.name="RuntimeRegistrationHandoffRequiredError",this.retryAfterMs=e}}export class RuntimeRegistrationOwnerResetRequiredError extends Error{constructor(){super("Instance authority requires owner reset authorization after a journal store change"),this.name="RuntimeRegistrationOwnerResetRequiredError"}}export class RuntimeRegistrationJournalAdvanceRequiredError extends Error{currentJournalGeneration;expectedJournalGeneration;constructor(e){super("Instance authority requires journal generation to advance exactly once after lease expiry"),this.name="RuntimeRegistrationJournalAdvanceRequiredError",this.currentJournalGeneration=e?.currentJournalGeneration,this.expectedJournalGeneration=e?.expectedJournalGeneration}}export class RuntimeRegistrationCoordinator{transport;config;onLeaseLost;now;active=null;timer;expiryTimer;stopped=!1;generation=0;heartbeatTransportFailurePolicy=new RuntimeTransportFailurePolicy;authorityListeners=new Set;constructor(e,t,i,r=Date.now){this.transport=e,this.config=t,this.onLeaseLost=i,this.now=r}get lease(){return!this.active||Date.parse(this.active.leaseExpiresAt)<=this.now()?null:this.active}get canClaimMutations(){return null!==this.lease}get authorityLease(){const e=this.lease;return e?Object.freeze({...e,handlerIdentity:this.config.handlerIdentity}):null}subscribeAuthority(e){return this.authorityListeners.add(e),()=>{this.authorityListeners.delete(e)}}async start(){const e=++this.generation;return this.stopped=!1,await this.registerOnce(e)}async registerOnce(e){if(!this.isCurrent(e))return null;this.timer=void 0,this.active&&(this.clearLeaseExpiry(),this.active=null,this.notifyAuthority());const t={registrationId:this.config.registrationId,handlerIdentity:this.config.handlerIdentity,contractRevision:this.config.contractRevision,runtimeRpcProtocolVersion:RUNTIME_RPC_PROTOCOL_VERSION,runtimeMethods:this.config.runtimeMethods,capabilities:this.config.capabilities,manifestDigest:this.config.manifestDigest,journalStoreId:this.config.journalStoreId,journalGeneration:this.config.journalGeneration};let i;try{i=await this.transport.register(this.signProof("runtime.register",t))}catch(t){if(!this.isCurrent(e))return null;if(t instanceof RuntimeRegistrationLeaseExpiredError||t instanceof RuntimeRegistrationJournalAdvanceRequiredError||t instanceof RuntimeRegistrationOwnerResetRequiredError)return this.onLeaseLost(t instanceof RuntimeRegistrationLeaseExpiredError?"expired":"heartbeat_failed",t),null;this.onLeaseLost("heartbeat_failed",t);const i=t instanceof RuntimeRegistrationHandoffRequiredError?t.retryAfterMs:5e3;return this.scheduleRegistration(i,e),null}return this.isCurrent(e)?"active"!==i.status?("fenced"===i.status?this.onLeaseLost("fenced"):this.scheduleRegistration(i.retryAfterMs,e),null):(this.active={instanceId:i.instanceId,leaseId:i.leaseId,runtimeEpoch:i.runtimeEpoch,runtimeLeaseCredential:i.runtimeLeaseCredential,leaseExpiresAt:i.leaseExpiresAt},this.heartbeatTransportFailurePolicy.recordSuccess(),this.notifyAuthority(),this.scheduleLeaseExpiry(this.active,e),this.schedule(i.heartbeatIntervalMs,e),this.active):null}stop(){this.generation++,this.stopped=!0,this.timer&&clearTimeout(this.timer),this.expiryTimer&&clearTimeout(this.expiryTimer),this.timer=void 0,this.expiryTimer=void 0,this.active=null,this.notifyAuthority()}schedule(e,t){if(!this.isCurrent(t))return;const i=this.active;i&&(this.timer=setTimeout(()=>{this.sendHeartbeat(e,t,i)},e),this.timer.unref?.())}scheduleRegistration(e,t){this.isCurrent(t)&&(this.timer=setTimeout(()=>{this.registerOnce(t)},e))}async sendHeartbeat(e,t,i){if(!this.isCurrentLease(t,i))return;const r={leaseId:i.leaseId,handlerIdentity:this.config.handlerIdentity,runtimeEpoch:i.runtimeEpoch},s=this.config.getMSConnectionStatus?{...r,msConnectionStatus:this.config.getMSConnectionStatus(i)}:r;try{const r=await this.transport.heartbeat(this.signProof("runtime.heartbeat",s));if(!this.isCurrentLease(t,i))return;if("renewed"!==r.status)return this.heartbeatTransportFailurePolicy.recordSuccess(),this.active=null,this.clearLeaseExpiry(),this.notifyAuthority(),void this.onLeaseLost(r.status);this.heartbeatTransportFailurePolicy.recordSuccess(),this.active={...i,runtimeLeaseCredential:r.runtimeLeaseCredential,leaseExpiresAt:r.leaseExpiresAt},this.notifyAuthority(),this.scheduleLeaseExpiry(this.active,t),this.schedule(e,t)}catch(r){if(!this.isCurrentLease(t,i))return;if(!this.heartbeatTransportFailurePolicy.recordFailure())return void this.schedule(Math.min(e,5e3),t);this.active=null,this.clearLeaseExpiry(),this.notifyAuthority(),this.onLeaseLost("heartbeat_failed",r),this.scheduleRegistration(5e3,t)}}scheduleLeaseExpiry(e,t){this.clearLeaseExpiry();const i=`${e.leaseId}\0${e.runtimeEpoch}\0${e.leaseExpiresAt}`,r=()=>{const s=Date.parse(e.leaseExpiresAt)-this.now();if(s>0)return this.expiryTimer=setTimeout(r,Math.min(2147483647,s)),void this.expiryTimer.unref?.();const n=this.active;this.isCurrentLease(t,e)&&n&&`${n.leaseId}\0${n.runtimeEpoch}\0${n.leaseExpiresAt}`===i&&(this.active=null,this.expiryTimer=void 0,this.notifyAuthority(),this.onLeaseLost("expired"))};r()}clearLeaseExpiry(){this.expiryTimer&&clearTimeout(this.expiryTimer),this.expiryTimer=void 0}isCurrent(e){return!this.stopped&&e===this.generation}isCurrentLease(e,t){return this.isCurrent(e)&&this.active===t}notifyAuthority(){const e=this.authorityLease;for(const t of this.authorityListeners)try{t(e)}catch{}}signProof(e,t){const i=new Date(this.now()).toISOString(),r=randomBytes(18).toString("base64url"),s="runtime.register"===e?RUNTIME_HASH_DOMAINS.registration:RUNTIME_HASH_DOMAINS.heartbeat,n=createHash("sha256").update(runtimeHashPreimage(s,t)).digest("hex"),a=runtimeIdentitySignatureMessage({purpose:e,instanceIdentityKeyId:this.config.instanceIdentityKeyId,signedAt:i,nonce:r,payloadHash:n});return{...t,signaturePurpose:e,instanceIdentityKeyId:this.config.instanceIdentityKeyId,signedAt:i,nonce:r,payloadHash:n,signature:sign(null,a,this.config.privateKey).toString("base64url")}}}export class InstanceAuthorityRuntimeRegistrationTransport{options;constructor(e){this.options=e}async register(e){if(e.registrationId!==this.options.registrationId)throw new Error("runtime registration id mismatch");const t=await this.post(RUNTIME_INTERNAL_API_V1_ROUTE_CONTRACTS.runtimeRegister.path,e);if("active"===t.status&&Number.isFinite(Date.parse(t.leaseExpiresAt))&&Date.parse(t.leaseExpiresAt)<=Date.now())throw new RuntimeRegistrationLeaseExpiredError;if(validateRuntimeInternalRegistrationResult(t)||"fenced"!==t.status&&(t.journalStoreId!==e.journalStoreId||t.journalGeneration!==e.journalGeneration)||"active"===t.status&&(!t.instanceId||!t.leaseId||!t.runtimeLeaseCredential||!Number.isInteger(t.heartbeatIntervalMs)||!Number.isFinite(Date.parse(t.leaseExpiresAt))||Date.parse(t.leaseExpiresAt)<=Date.now()))throw new Error("Instance authority returned an invalid registration binding");return t}async heartbeat(e){const t=await this.post(RUNTIME_INTERNAL_API_V1_ROUTE_CONTRACTS.runtimeHeartbeat.path.replace("{registrationId}",encodeURIComponent(this.options.registrationId)),e);if(!(t&&["renewed","fenced","expired"].includes(t.status)&&("renewed"!==t.status||t.runtimeLeaseCredential&&Number.isFinite(Date.parse(t.leaseExpiresAt)))))throw new Error("Instance authority returned an invalid heartbeat result");return t}async post(e,t){const i=new URL(e,this.options.agentGatewayUrl),r=await this.options.agentFetch(i,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(t)});if(!r.ok){const e=await r.text().catch(()=>"");if(409===r.status&&i.pathname.endsWith("/runtime/registrations"))throw classifyRuntimeRegistrationConflict(e);throw new Error(`runtime Instance authority request failed (${r.status} ${r.statusText}) ${i.pathname}`+(e?`: ${e.slice(0,240)}`:""))}return await r.json()}}export function classifyRuntimeRegistrationConflict(e){const t=extractConflictMessage(e);if(/journal store change requires owner reset authorization/i.test(t))return new RuntimeRegistrationOwnerResetRequiredError;if(/journal generation must advance exactly once/i.test(t)){const e=t.match(/\bcurrent=(\d+)\b/i)?.[1],i=t.match(/\bexpected=(\d+)\b/i)?.[1];return new RuntimeRegistrationJournalAdvanceRequiredError({currentJournalGeneration:e&&/^[1-9][0-9]*$/.test(e)?e:void 0,expectedJournalGeneration:i&&/^[1-9][0-9]*$/.test(i)?i:void 0})}return/active runtime lease requires explicit handoff/i.test(t)||/waiting for the active runtime lease/i.test(t),new RuntimeRegistrationHandoffRequiredError}function extractConflictMessage(e){if(!e)return"";try{const t=JSON.parse(e),i=t.error?.message;if("string"==typeof i)return i;if("string"==typeof t.message)return t.message}catch{}return e}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export class RuntimeTransportFailurePolicy{threshold;consecutiveFailures=0;constructor(e=3){if(this.threshold=e,!Number.isSafeInteger(e)||e<=0)throw new Error("transport failure threshold must be a positive integer")}recordSuccess(){this.consecutiveFailures=0}recordFailure(){return this.consecutiveFailures+=1,!(this.consecutiveFailures<this.threshold)&&(this.consecutiveFailures=0,!0)}}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
const GATEWAY_GRANT_PROVENANCE=Object.freeze({issuer:"beeos-gateway",audience:"beeos-runtime-command",purpose:"runtime-execution-grant",maxClaimTtlSeconds:60}),MESSAGE_BOUNDARY_PROVENANCE=Object.freeze({issuer:"message-service",audience:"beeos-runtime-execution-boundary",purpose:"runtime-execution-boundary",maxClaimTtlSeconds:60});export class GatewayGrantJwksVerifier{verifier;purpose="gateway-runtime-grant";keysetProvenance;constructor(e){this.verifier=e,this.keysetProvenance=e.keysetUrl}async refresh(e=!1){await this.verifier.refresh(e),assertResponseProvenance(this.verifier,GATEWAY_GRANT_PROVENANCE)}trustedKeyMaterials(){return this.verifier.trustedKeyMaterials()}responseProvenance(){return this.verifier.documentProvenance()}verifyAndDecodeGrant(e){const r=this.verifier.verifyAndDecodeGrant(e);return"beeos-gateway"===r?.claims.iss&&"beeos-runtime-command"===r.claims.aud&&"runtime-execution-grant"===r.claims.purpose?r:null}}export class MessageServiceBoundaryJwksVerifier{verifier;purpose="message-service-execution-boundary";keysetProvenance;constructor(e){this.verifier=e,this.keysetProvenance=e.keysetUrl}async refresh(e=!1){await this.verifier.refresh(e),assertResponseProvenance(this.verifier,MESSAGE_BOUNDARY_PROVENANCE)}trustedKeyMaterials(){return this.verifier.trustedKeyMaterials()}responseProvenance(){return this.verifier.documentProvenance()}verifyAndDecodeBoundary(e){const r=this.verifier.verifyAndDecodeBoundary(e);return"message-service"===r?.claims.iss&&"beeos-runtime-execution-boundary"===r.claims.aud?r:null}}export class SplitIssuerRuntimeAuthorizationVerifier{grant;boundary;constructor(e,r){if(this.grant=e,this.boundary=r,e.keysetProvenance===r.keysetProvenance)throw new Error("grant and boundary JWKS keysets require distinct provenance")}async refresh(e=!1){await Promise.all([this.grant.refresh(e),this.boundary.refresh(e)]);const r=this.grant.responseProvenance(),s=this.boundary.responseProvenance();if(!r||!s||r.sourceUrl===s.sourceUrl||r.etag===s.etag)throw new Error("grant and boundary JWKS responses require distinct provenance");const n=new Set(this.boundary.trustedKeyMaterials().flatMap(e=>[e.jwkThumbprint,e.spkiSha256]));if(this.grant.trustedKeyMaterials().some(e=>n.has(e.jwkThumbprint)||n.has(e.spkiSha256)))throw new Error("grant and boundary JWKS keysets reuse public-key material")}verifyAndDecodeGrant(e){return this.grant.verifyAndDecodeGrant(e)}verifyAndDecodeBoundary(e){return this.boundary.verifyAndDecodeBoundary(e)}}function assertResponseProvenance(e,r){const s=e.documentProvenance();if(!s||!s.etag||s.sourceUrl!==e.keysetUrl||s.issuer!==r.issuer||s.audience!==r.audience||s.purpose!==r.purpose||s.maxClaimTtlSeconds!==r.maxClaimTtlSeconds)throw new Error(`JWKS response provenance mismatch for ${r.purpose}`)}
|