beeos-claw 0.6.42 → 0.6.43

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "beeos-claw",
3
- "version": "0.6.42",
3
+ "version": "0.6.43",
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>",
@@ -16,3 +16,38 @@ export function bindRuntimeBlackboxLease(input) {
16
16
  journalGeneration: input.journalGeneration,
17
17
  };
18
18
  }
19
+
20
+ /**
21
+ * Construct the black-box Worker through the same one-client Message SDK
22
+ * composition used by the production plugin. Returning the direct HTTP
23
+ * channel here is not equivalent: the runtime consumer is owned by
24
+ * MessageClient.methods and requires the SDK runtime-delivery port.
25
+ */
26
+ export function createRuntimeBlackboxMessageComposition(input) {
27
+ const options = {
28
+ identityId: input.identityId,
29
+ serviceUrl: input.serviceUrl,
30
+ tokenProvider: async (requestedIdentityId) => {
31
+ if (requestedIdentityId !== input.identityId) {
32
+ throw new Error("runtime black-box MessageClient identity changed");
33
+ }
34
+ return {
35
+ token: input.token,
36
+ centrifugoUrl: input.centrifugoUrl,
37
+ serviceUrl: input.serviceUrl,
38
+ identityId: input.identityId,
39
+ expiresAt: input.expiresAt,
40
+ };
41
+ },
42
+ runtimeDelivery: {
43
+ scopedDeliveryKey: input.scopedDeliveryKey,
44
+ authority: { currentLease: input.currentLease },
45
+ },
46
+ };
47
+ const composition = input.compose(options);
48
+ if (typeof composition?.client?.methods?.consume !== "function" ||
49
+ typeof composition?.nodeTransport?.fetchRuntimeChannel !== "function") {
50
+ throw new Error("runtime black-box MessageClient composition is incomplete");
51
+ }
52
+ return composition;
53
+ }
@@ -10,10 +10,9 @@ import { FileRuntimeLeasePort } from "../dist/runtime/runtime-operation-lease.js
10
10
  import { FileRuntimeCommandJournal } from "../dist/runtime/runtime-command-journal.js";
11
11
  import { FileRuntimePoisonDeliveryJournal } from
12
12
  "../dist/runtime/runtime-poison-delivery-journal.js";
13
- import { MessageServiceRuntimeAuthenticatedFetch } from
14
- "../dist/runtime/message-service-runtime-authenticated-fetch.js";
15
13
  import { MessageServiceRuntimeMessageChannel } from
16
14
  "../dist/runtime/message-service-runtime-message-channel.js";
15
+ import { composeMessageClient } from "../dist/message-client-composition.js";
17
16
  import { AgentGatewayGrantJwksReadinessAdapter, RuntimeServiceTransportReadinessAdapter,
18
17
  createRuntimeServiceProductionCandidate } from
19
18
  "../dist/runtime/runtime-service-production-candidate.js";
@@ -21,7 +20,8 @@ import { createRuntimeBlackboxReceipt } from "./runtime-security.mjs";
21
20
  import { runRuntimePoisonBlackbox } from "./runtime-poison-blackbox-lib.mjs";
22
21
  import { closeRuntimeResources, createRuntimeBlackboxCompletionGate, selectRuntimeError, startGatewayWithinDeadline, withinDeadline } from
23
22
  "./runtime-blackbox-worker-lifecycle.mjs";
24
- import { bindRuntimeBlackboxLease } from "./runtime-blackbox-worker-lease.mjs";
23
+ import { bindRuntimeBlackboxLease, createRuntimeBlackboxMessageComposition } from
24
+ "./runtime-blackbox-worker-lease.mjs";
25
25
 
26
26
  export async function main() {
27
27
  const contractPath = requiredEnvironment("RUNTIME_BLACKBOX_CONTRACT_FILE");
@@ -61,6 +61,17 @@ export async function main() {
61
61
  const journalStoreId = nonempty(leaseClaims.journalStoreId, "runtime lease journalStoreId");
62
62
  const journalGeneration = decimal(leaseClaims.journalGeneration, "runtime lease journalGeneration");
63
63
  const messageServiceUrl = strictBaseUrl(requiredEnvironment("MESSAGE_SERVICE_URL"), "MESSAGE_SERVICE_URL");
64
+ const messageServiceOrigin = new URL(messageServiceUrl).origin;
65
+ const messageIdentityId = nonempty(authority.handlerIdentity, "authority.handlerIdentity");
66
+ const scopedDeliveryKey = requiredEnvironment("MESSAGE_RUNTIME_DELIVERY_KEY");
67
+ const runtimeLease = () => bindRuntimeBlackboxLease({
68
+ instanceId, leaseId: nonempty(authority.leaseId, "authority.leaseId"),
69
+ handlerIdentity: messageIdentityId,
70
+ runtimeEpoch: decimal(authority.runtimeEpoch, "authority.runtimeEpoch"),
71
+ runtimeLeaseCredential: nonempty(authority.runtimeLeaseCredential, "authority.runtimeLeaseCredential"),
72
+ leaseExpiresAt: new Date(Number(leaseClaims.exp) * 1_000).toISOString(),
73
+ journalStoreId, journalGeneration,
74
+ });
64
75
  const agentGatewayUrl = strictBaseUrl(requiredEnvironment(
65
76
  nonempty(contract.authorization?.http?.baseUrlEnvironment,
66
77
  "contract.authorization.http.baseUrlEnvironment")), "Agent Gateway base URL");
@@ -72,17 +83,23 @@ export async function main() {
72
83
 
73
84
  let httpRequests = 0;
74
85
  let terminalEvent;
75
- const countedFetch = async (url, init = {}) => {
76
- httpRequests += 1;
77
- return await fetchWithDeadline(url, init, deadline);
86
+ const processFetch = globalThis.fetch;
87
+ globalThis.fetch = async (url, init = {}) => {
88
+ const requested = new URL(url instanceof Request ? url.url : url);
89
+ if (requested.origin === messageServiceOrigin) httpRequests += 1;
90
+ return await processFetch(url, init);
78
91
  };
79
- const messageFetch = new MessageServiceRuntimeAuthenticatedFetch({
80
- configuredServiceUrl: messageServiceUrl,
81
- tokenProvider: async () => ({ token: requiredEnvironment("MESSAGE_API_KEY"),
82
- identityId: nonempty(authority.handlerIdentity, "authority.handlerIdentity"),
83
- serviceUrl: messageServiceUrl, expiresAt: Math.floor(deadline / 1_000) + 60 }),
84
- fetcher: countedFetch,
92
+ const messageComposition = createRuntimeBlackboxMessageComposition({
93
+ compose: composeMessageClient,
94
+ identityId: messageIdentityId,
95
+ serviceUrl: messageServiceUrl,
96
+ token: requiredEnvironment("MESSAGE_API_KEY"),
97
+ centrifugoUrl: strictWebSocketUrl(requiredEnvironment("CENTRIFUGO_WS_URL"), "CENTRIFUGO_WS_URL"),
98
+ expiresAt: Math.floor(deadline / 1_000) + 60,
99
+ scopedDeliveryKey,
100
+ currentLease: runtimeLease,
85
101
  });
102
+ const messageFetch = messageComposition.nodeTransport;
86
103
  const boundAgentFetch = async (url, init = {}) => {
87
104
  const headers = new Headers(init.headers);
88
105
  headers.set("X-Instance-ID", instanceId);
@@ -99,7 +116,6 @@ export async function main() {
99
116
  // candidate.status=unavailable (message_channel_transport) — the
100
117
  // 0.6.20–0.6.22 worker false red that blocks backend#842 even when the
101
118
  // harness fixture is correct. Reused later for the post-ack renew proof.
102
- const scopedDeliveryKey = requiredEnvironment("MESSAGE_RUNTIME_DELIVERY_KEY");
103
119
  const authorityChannel = new MessageServiceRuntimeMessageChannel({
104
120
  serviceUrl: messageServiceUrl,
105
121
  transport: messageFetch,
@@ -109,7 +125,7 @@ export async function main() {
109
125
  authorizationAdapter: new AgentGatewayGrantJwksReadinessAdapter({ agentGatewayUrl }),
110
126
  transportAdapter: new RuntimeServiceTransportReadinessAdapter({ agentGatewayUrl,
111
127
  messageService: messageFetch, boundAgentFetch,
112
- messageClient: authorityChannel,
128
+ messageClient: messageComposition.client,
113
129
  configuredServiceUrl: messageServiceUrl,
114
130
  scopedDeliveryKey }),
115
131
  commandJournal,
@@ -130,14 +146,6 @@ export async function main() {
130
146
  let consumer;
131
147
  let primaryError;
132
148
  let releaseGatewayTransportObserver;
133
- const runtimeLease = () => bindRuntimeBlackboxLease({
134
- instanceId, leaseId: nonempty(authority.leaseId, "authority.leaseId"),
135
- handlerIdentity: nonempty(authority.handlerIdentity, "authority.handlerIdentity"),
136
- runtimeEpoch: decimal(authority.runtimeEpoch, "authority.runtimeEpoch"),
137
- runtimeLeaseCredential: nonempty(authority.runtimeLeaseCredential, "authority.runtimeLeaseCredential"),
138
- leaseExpiresAt: new Date(Number(leaseClaims.exp) * 1_000).toISOString(),
139
- journalStoreId, journalGeneration,
140
- });
141
149
  try {
142
150
  releaseGatewayTransportObserver = await startGatewayWithinDeadline(gateway, deadline);
143
151
  const identities = new FileRuntimeAgentIdentityRegistry(`${statePrefix}-identities.json`);
@@ -168,6 +176,7 @@ export async function main() {
168
176
  } finally {
169
177
  primaryError = selectRuntimeError(primaryError,
170
178
  await closeRuntimeResources({ consumer, gateway, releaseGatewayTransportObserver }));
179
+ globalThis.fetch = processFetch;
171
180
  try { completionGate.assertNoFailure(); }
172
181
  catch (error) { primaryError ??= error; }
173
182
  }
@@ -213,13 +222,6 @@ function decodeRuntimeLease(token) {
213
222
  }
214
223
  return claims;
215
224
  }
216
- async function fetchWithDeadline(url, init, deadline) {
217
- const requested = new URL(url);
218
- const response = await withinDeadline((signal) =>
219
- fetch(requested, { ...init, redirect: "error", signal }), deadline);
220
- if (response.url && new URL(response.url).origin !== requested.origin) throw new Error("HTTP origin changed");
221
- return response;
222
- }
223
225
  function requiredEnvironment(name) {
224
226
  const value = process.env[name]?.trim();
225
227
  if (!value) throw new Error(`${name} is required`);
@@ -232,6 +234,13 @@ function strictBaseUrl(value, label) {
232
234
  }
233
235
  return url.toString();
234
236
  }
237
+ function strictWebSocketUrl(value, label) {
238
+ const url = new URL(value);
239
+ if (!/^wss?:$/.test(url.protocol) || url.username || url.password || url.search || url.hash) {
240
+ throw new Error(`${label} must be an absolute query-free WebSocket URL`);
241
+ }
242
+ return url.toString();
243
+ }
235
244
  function parseObject(raw, label) {
236
245
  const value = JSON.parse(raw);
237
246
  if (!value || typeof value !== "object" || Array.isArray(value)) throw new Error(`${label} must be an object`);