beeos-claw 0.5.65-staging.3 → 0.5.65-staging.5
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.5.65-staging.
|
|
3
|
+
"version": "0.5.65-staging.5",
|
|
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>",
|
|
@@ -38,17 +38,10 @@ export async function withinDeadline(run, deadline) {
|
|
|
38
38
|
* invoking start() again would create competing reconnect loops.
|
|
39
39
|
*/
|
|
40
40
|
export async function startGatewayWithinDeadline(gateway, deadline) {
|
|
41
|
-
|
|
42
|
-
await withinDeadline(() => gateway.start(), deadline);
|
|
43
|
-
return;
|
|
44
|
-
} catch (error) {
|
|
45
|
-
if (!isRetryableGatewayHandshake(error)) throw error;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
await withinDeadline((signal) => waitForGatewayConnection(gateway, signal), deadline);
|
|
41
|
+
return await withinDeadline((signal) => startAndObserveGateway(gateway, signal), deadline);
|
|
49
42
|
}
|
|
50
43
|
|
|
51
|
-
export async function closeRuntimeResources({ consumer, gateway }, cleanupTimeoutMs = 5_000) {
|
|
44
|
+
export async function closeRuntimeResources({ consumer, gateway, releaseGatewayTransportObserver }, cleanupTimeoutMs = 5_000) {
|
|
52
45
|
let cleanupError;
|
|
53
46
|
try {
|
|
54
47
|
if (consumer) {
|
|
@@ -63,6 +56,11 @@ export async function closeRuntimeResources({ consumer, gateway }, cleanupTimeou
|
|
|
63
56
|
}
|
|
64
57
|
} catch (error) {
|
|
65
58
|
cleanupError ??= new Error(WORKER_CLEANUP_MESSAGE, { cause: error });
|
|
59
|
+
} finally {
|
|
60
|
+
// The retained observer is only needed while GatewayClient can still
|
|
61
|
+
// emit a transport error. Release it after the bounded stop completes,
|
|
62
|
+
// including the consumer-stop failure path.
|
|
63
|
+
releaseGatewayTransportObserver?.();
|
|
66
64
|
}
|
|
67
65
|
}
|
|
68
66
|
return cleanupError;
|
|
@@ -116,32 +114,41 @@ function isRetryableGatewayHandshake(error) {
|
|
|
116
114
|
((error.retryable === true) || error.code === "UNAVAILABLE");
|
|
117
115
|
}
|
|
118
116
|
|
|
119
|
-
function
|
|
117
|
+
function startAndObserveGateway(gateway, signal) {
|
|
120
118
|
return new Promise((resolve, reject) => {
|
|
121
119
|
let settled = false;
|
|
122
|
-
|
|
120
|
+
let observedTransportFailure = false;
|
|
121
|
+
const cleanup = (retainTransportObserver = false) => {
|
|
123
122
|
gateway.off("connected", onConnected);
|
|
124
123
|
gateway.off("auth-failed", onAuthFailed);
|
|
125
|
-
gateway.off("error", onError);
|
|
124
|
+
if (!retainTransportObserver) gateway.off("error", onError);
|
|
125
|
+
gateway.off("disconnected", onDisconnected);
|
|
126
126
|
signal.removeEventListener("abort", onAbort);
|
|
127
127
|
};
|
|
128
|
-
const
|
|
128
|
+
const releaseTransportObserver = () => gateway.off("error", onError);
|
|
129
|
+
const settle = (callback, value, retainTransportObserver = false) => {
|
|
129
130
|
if (settled) return;
|
|
130
131
|
settled = true;
|
|
131
|
-
cleanup();
|
|
132
|
+
cleanup(retainTransportObserver);
|
|
132
133
|
callback(value);
|
|
133
134
|
};
|
|
134
|
-
|
|
135
|
+
// `error` has special EventEmitter semantics: after the initial handshake
|
|
136
|
+
// succeeds, a later WebSocket transport error must stay observed until the
|
|
137
|
+
// worker shuts the Gateway down. GatewayClient owns reconnects; this
|
|
138
|
+
// observer only prevents that owned transient from becoming process-fatal.
|
|
139
|
+
const onConnected = () => settle(resolve, releaseTransportObserver, true);
|
|
135
140
|
const onAuthFailed = (failure) => {
|
|
136
141
|
const reason = failure && typeof failure.reason === "string"
|
|
137
142
|
? failure.reason
|
|
138
143
|
: "Gateway authentication failed";
|
|
139
144
|
settle(reject, new Error(reason));
|
|
140
145
|
};
|
|
141
|
-
// GatewayClient
|
|
142
|
-
//
|
|
143
|
-
//
|
|
144
|
-
|
|
146
|
+
// GatewayClient owns reconnect scheduling for a WebSocket-level startup
|
|
147
|
+
// failure. Install this observer before `start()` so its first error cannot
|
|
148
|
+
// become EventEmitter's fatal unhandled `error` event. The subsequent
|
|
149
|
+
// `start()` rejection is intentionally held open for that owned retry.
|
|
150
|
+
const onError = () => { observedTransportFailure = true; };
|
|
151
|
+
const onDisconnected = () => { observedTransportFailure = true; };
|
|
145
152
|
const onAbort = () => settle(reject, signal.reason ?? new Error("Gateway connection aborted"));
|
|
146
153
|
|
|
147
154
|
if (signal.aborted) {
|
|
@@ -151,7 +158,22 @@ function waitForGatewayConnection(gateway, signal) {
|
|
|
151
158
|
gateway.on("connected", onConnected);
|
|
152
159
|
gateway.on("auth-failed", onAuthFailed);
|
|
153
160
|
gateway.on("error", onError);
|
|
161
|
+
gateway.on("disconnected", onDisconnected);
|
|
154
162
|
signal.addEventListener("abort", onAbort, { once: true });
|
|
163
|
+
|
|
164
|
+
Promise.resolve()
|
|
165
|
+
.then(() => gateway.start())
|
|
166
|
+
.then(
|
|
167
|
+
() => settle(resolve, releaseTransportObserver, true),
|
|
168
|
+
(error) => {
|
|
169
|
+
// Typed handshake UNAVAILABLE and a pre-auth transport failure both
|
|
170
|
+
// leave GatewayClient's single bounded reconnect loop authoritative.
|
|
171
|
+
// A nonretryable handshake instead emits auth-failed and settles
|
|
172
|
+
// above without waiting for the worker deadline.
|
|
173
|
+
if (isRetryableGatewayHandshake(error) || observedTransportFailure) return;
|
|
174
|
+
settle(reject, error);
|
|
175
|
+
},
|
|
176
|
+
);
|
|
155
177
|
});
|
|
156
178
|
}
|
|
157
179
|
|
|
@@ -114,13 +114,14 @@ export async function main() {
|
|
|
114
114
|
retry: { baseMs: 100, maxMs: 500, maxAttempts: 60 } });
|
|
115
115
|
let consumer;
|
|
116
116
|
let primaryError;
|
|
117
|
+
let releaseGatewayTransportObserver;
|
|
117
118
|
const runtimeLease = () => ({ instanceId, leaseId: nonempty(authority.leaseId, "authority.leaseId"),
|
|
118
119
|
handlerIdentity: nonempty(authority.handlerIdentity, "authority.handlerIdentity"),
|
|
119
120
|
runtimeEpoch: decimal(authority.runtimeEpoch, "authority.runtimeEpoch"),
|
|
120
121
|
runtimeLeaseCredential: nonempty(authority.runtimeLeaseCredential, "authority.runtimeLeaseCredential"),
|
|
121
122
|
leaseExpiresAt: new Date(Number(leaseClaims.exp) * 1_000).toISOString() });
|
|
122
123
|
try {
|
|
123
|
-
await startGatewayWithinDeadline(gateway, deadline);
|
|
124
|
+
releaseGatewayTransportObserver = await startGatewayWithinDeadline(gateway, deadline);
|
|
124
125
|
const identities = new FileRuntimeAgentIdentityRegistry(`${statePrefix}-identities.json`);
|
|
125
126
|
const dispatcher = new OpenClawRuntimeCommandDispatcher({ getGateway: () => gateway,
|
|
126
127
|
resources: rejectingResources(), identities,
|
|
@@ -150,7 +151,7 @@ export async function main() {
|
|
|
150
151
|
primaryError = error;
|
|
151
152
|
} finally {
|
|
152
153
|
primaryError = selectRuntimeError(primaryError,
|
|
153
|
-
await closeRuntimeResources({ consumer, gateway }));
|
|
154
|
+
await closeRuntimeResources({ consumer, gateway, releaseGatewayTransportObserver }));
|
|
154
155
|
try { completionGate.assertNoFailure(); }
|
|
155
156
|
catch (error) { primaryError ??= error; }
|
|
156
157
|
}
|