@xfxstudio/claworld 2026.7.1-testing.2 → 2026.7.2-testing.1
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/openclaw.plugin.json
CHANGED
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
},
|
|
18
18
|
"name": "Claworld Persona Relay",
|
|
19
19
|
"description": "Claworld relay world channel plugin for OpenClaw.",
|
|
20
|
-
"version": "2026.7.
|
|
20
|
+
"version": "2026.7.2-testing.1",
|
|
21
21
|
"configSchema": {
|
|
22
22
|
"type": "object",
|
|
23
23
|
"additionalProperties": false,
|
package/package.json
CHANGED
|
@@ -122,13 +122,17 @@ Useful questions:
|
|
|
122
122
|
|
|
123
123
|
Use `details` for the developer-facing summary: concise evidence, relevant observations, why this looks like product/runtime work, and anything the human specifically cares about. Use `reproductionSteps` for repeatable steps. Use `context` and `runtimeContext` for lookup metadata.
|
|
124
124
|
|
|
125
|
-
Use a direct HTTP POST to the configured Claworld backend feedback URL
|
|
125
|
+
Use a direct HTTP POST to the configured Claworld backend feedback URL. Read the
|
|
126
|
+
active Claworld channel/account configuration first and use its configured
|
|
127
|
+
backend when present:
|
|
126
128
|
|
|
127
129
|
```text
|
|
128
|
-
|
|
130
|
+
<configured Claworld server URL>/v1/feedback
|
|
129
131
|
```
|
|
130
132
|
|
|
131
|
-
|
|
133
|
+
For a fresh setup with no configured backend yet, use the package default:
|
|
134
|
+
testing packages default to `https://staging.claworld.love`, and stable packages
|
|
135
|
+
default to `https://claworld.love`.
|
|
132
136
|
|
|
133
137
|
The `accountId`, `apiKey`, and app token come from the active Claworld channel/account configuration. Do not print secrets to the human. If an app token is configured, send it as `Authorization: Bearer <appToken>` and `x-claworld-app-token: <appToken>`. If an API key is configured, send `x-api-key: <apiKey>`.
|
|
134
138
|
|
|
@@ -137,7 +141,7 @@ The clean authenticated path is an app token that resolves to the account's back
|
|
|
137
141
|
Example:
|
|
138
142
|
|
|
139
143
|
```bash
|
|
140
|
-
CLAWORLD_SERVER_URL="${CLAWORLD_SERVER_URL:-https://
|
|
144
|
+
CLAWORLD_SERVER_URL="${CLAWORLD_SERVER_URL:-${CONFIGURED_CLAWORLD_SERVER_URL:-https://claworld.love}}"
|
|
141
145
|
|
|
142
146
|
headers=(-H "content-type: application/json")
|
|
143
147
|
if [ -n "${CLAWORLD_APP_TOKEN:-}" ]; then
|
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
import os from 'os';
|
|
2
2
|
import path from 'path';
|
|
3
|
+
import {
|
|
4
|
+
CLAWORLD_PLUGIN_CURRENT_VERSION,
|
|
5
|
+
normalizeClaworldPluginVersion,
|
|
6
|
+
} from '../plugin-version.js';
|
|
3
7
|
import {
|
|
4
8
|
CLAWORLD_MINIMAL_OPENCLAW_TOOL_NAMES,
|
|
5
9
|
CLAWORLD_PUBLIC_TOOL_NAMES,
|
|
@@ -7,7 +11,18 @@ import {
|
|
|
7
11
|
CLAWORLD_TOOL_PROFILES,
|
|
8
12
|
} from '../runtime/tool-inventory.js';
|
|
9
13
|
|
|
10
|
-
export const
|
|
14
|
+
export const CLAWORLD_STAGING_SERVER_URL = 'https://staging.claworld.love';
|
|
15
|
+
export const CLAWORLD_PRODUCTION_SERVER_URL = 'https://claworld.love';
|
|
16
|
+
export function isClaworldTestingPluginVersion(version = CLAWORLD_PLUGIN_CURRENT_VERSION) {
|
|
17
|
+
const normalized = normalizeClaworldPluginVersion(version, null);
|
|
18
|
+
return Boolean(normalized && /-testing(?:\.|$)/.test(normalized));
|
|
19
|
+
}
|
|
20
|
+
export function resolveDefaultClaworldServerUrl(version = CLAWORLD_PLUGIN_CURRENT_VERSION) {
|
|
21
|
+
return isClaworldTestingPluginVersion(version)
|
|
22
|
+
? CLAWORLD_STAGING_SERVER_URL
|
|
23
|
+
: CLAWORLD_PRODUCTION_SERVER_URL;
|
|
24
|
+
}
|
|
25
|
+
export const DEFAULT_CLAWORLD_SERVER_URL = resolveDefaultClaworldServerUrl();
|
|
11
26
|
export const DEFAULT_CLAWORLD_API_KEY = 'local-test';
|
|
12
27
|
export const DEFAULT_CLAWORLD_AGENT_ID = 'main';
|
|
13
28
|
export const DEFAULT_CLAWORLD_ACCOUNT_ID = 'claworld';
|
|
@@ -32,6 +32,10 @@ import {
|
|
|
32
32
|
|
|
33
33
|
const DELIVERY_VISIBILITY_RETRY_ATTEMPTS = 20;
|
|
34
34
|
const DELIVERY_VISIBILITY_RETRY_DELAY_MS = 10;
|
|
35
|
+
const DEFAULT_RELAY_AUTH_TIMEOUT_MS = 30_000;
|
|
36
|
+
const DEFAULT_RECONNECT_BASE_DELAY_MS = 1_000;
|
|
37
|
+
const DEFAULT_RECONNECT_MAX_DELAY_MS = 60_000;
|
|
38
|
+
const DEFAULT_RECONNECT_JITTER_RATIO = 0.2;
|
|
35
39
|
|
|
36
40
|
function isDeliveryVisibilityMiss(result = {}) {
|
|
37
41
|
return Number(result?.status) === 404
|
|
@@ -53,6 +57,11 @@ export class ClaworldRelayClient extends EventEmitter {
|
|
|
53
57
|
protocol = createRelayEventProtocol(),
|
|
54
58
|
wsFactory = (url) => new WebSocket(url),
|
|
55
59
|
httpFetch = globalThis.fetch,
|
|
60
|
+
authTimeoutMs = DEFAULT_RELAY_AUTH_TIMEOUT_MS,
|
|
61
|
+
reconnectBaseDelayMs = DEFAULT_RECONNECT_BASE_DELAY_MS,
|
|
62
|
+
reconnectMaxDelayMs = DEFAULT_RECONNECT_MAX_DELAY_MS,
|
|
63
|
+
reconnectJitterRatio = DEFAULT_RECONNECT_JITTER_RATIO,
|
|
64
|
+
random = Math.random,
|
|
56
65
|
} = {}) {
|
|
57
66
|
super();
|
|
58
67
|
this.logger = logger;
|
|
@@ -61,6 +70,11 @@ export class ClaworldRelayClient extends EventEmitter {
|
|
|
61
70
|
this.protocol = protocol;
|
|
62
71
|
this.wsFactory = wsFactory;
|
|
63
72
|
this.httpFetch = httpFetch;
|
|
73
|
+
this.authTimeoutMs = Math.max(1, Math.floor(Number(authTimeoutMs) || DEFAULT_RELAY_AUTH_TIMEOUT_MS));
|
|
74
|
+
this.reconnectBaseDelayMs = Math.max(1, Math.floor(Number(reconnectBaseDelayMs) || DEFAULT_RECONNECT_BASE_DELAY_MS));
|
|
75
|
+
this.reconnectMaxDelayMs = Math.max(this.reconnectBaseDelayMs, Math.floor(Number(reconnectMaxDelayMs) || DEFAULT_RECONNECT_MAX_DELAY_MS));
|
|
76
|
+
this.reconnectJitterRatio = Math.max(0, Math.min(1, Number(reconnectJitterRatio) || 0));
|
|
77
|
+
this.random = typeof random === 'function' ? random : Math.random;
|
|
64
78
|
this.ws = null;
|
|
65
79
|
this.events = [];
|
|
66
80
|
this.heartbeatTimer = null;
|
|
@@ -103,9 +117,17 @@ export class ClaworldRelayClient extends EventEmitter {
|
|
|
103
117
|
this.reconnectTimer = null;
|
|
104
118
|
}
|
|
105
119
|
|
|
106
|
-
resolveReconnectDelayMs() {
|
|
107
|
-
const
|
|
108
|
-
|
|
120
|
+
resolveReconnectDelayMs(attempt = this.reconnectAttempts + 1) {
|
|
121
|
+
const normalizedAttempt = Math.max(1, Math.floor(Number(attempt) || 1));
|
|
122
|
+
const exponent = Math.min(normalizedAttempt - 1, 10);
|
|
123
|
+
const baseDelayMs = Math.min(
|
|
124
|
+
this.reconnectMaxDelayMs,
|
|
125
|
+
this.reconnectBaseDelayMs * (2 ** exponent),
|
|
126
|
+
);
|
|
127
|
+
const jitterWindowMs = Math.floor(baseDelayMs * this.reconnectJitterRatio);
|
|
128
|
+
if (jitterWindowMs <= 0) return baseDelayMs;
|
|
129
|
+
const jitterMs = Math.floor(Math.max(0, Math.min(1, Number(this.random()) || 0)) * jitterWindowMs);
|
|
130
|
+
return Math.min(this.reconnectMaxDelayMs, baseDelayMs + jitterMs);
|
|
109
131
|
}
|
|
110
132
|
|
|
111
133
|
shouldAutoReconnect(disconnectInfo = null) {
|
|
@@ -285,9 +307,59 @@ export class ClaworldRelayClient extends EventEmitter {
|
|
|
285
307
|
return await new Promise((resolve, reject) => {
|
|
286
308
|
let settled = false;
|
|
287
309
|
let suppressCloseHandler = false;
|
|
310
|
+
let authTimer = null;
|
|
288
311
|
const ws = this.wsFactory(wsUrl);
|
|
289
312
|
this.ws = ws;
|
|
290
313
|
|
|
314
|
+
const clearAuthTimer = () => {
|
|
315
|
+
if (authTimer) clearTimeout(authTimer);
|
|
316
|
+
authTimer = null;
|
|
317
|
+
};
|
|
318
|
+
|
|
319
|
+
const closeSocketAfterFailedAuth = (reason = 'auth_failed') => {
|
|
320
|
+
try {
|
|
321
|
+
if (this.ws === ws) this.ws = null;
|
|
322
|
+
if (ws.readyState !== 3) ws.close(1008, reason);
|
|
323
|
+
} catch {
|
|
324
|
+
// No-op.
|
|
325
|
+
}
|
|
326
|
+
};
|
|
327
|
+
|
|
328
|
+
const settleAuthResolve = (message) => {
|
|
329
|
+
if (settled) return;
|
|
330
|
+
settled = true;
|
|
331
|
+
clearAuthTimer();
|
|
332
|
+
resolve(message);
|
|
333
|
+
};
|
|
334
|
+
|
|
335
|
+
const settleAuthReject = (error, { suppressClose = false, closeReason = null } = {}) => {
|
|
336
|
+
if (settled) return;
|
|
337
|
+
settled = true;
|
|
338
|
+
clearAuthTimer();
|
|
339
|
+
if (suppressClose) suppressCloseHandler = true;
|
|
340
|
+
this.connectionState = 'error';
|
|
341
|
+
if (closeReason) closeSocketAfterFailedAuth(closeReason);
|
|
342
|
+
reject(error);
|
|
343
|
+
};
|
|
344
|
+
|
|
345
|
+
authTimer = setTimeout(() => {
|
|
346
|
+
const timeoutError = createRuntimeBoundaryError({
|
|
347
|
+
code: 'relay_auth_timeout',
|
|
348
|
+
category: 'transport',
|
|
349
|
+
status: 504,
|
|
350
|
+
message: `timed out waiting for relay authentication after ${this.authTimeoutMs}ms`,
|
|
351
|
+
publicMessage: 'relay authentication timed out',
|
|
352
|
+
recoverable: true,
|
|
353
|
+
context: this.buildBoundaryContext({
|
|
354
|
+
stage: 'auth',
|
|
355
|
+
timeoutMs: this.authTimeoutMs,
|
|
356
|
+
}),
|
|
357
|
+
});
|
|
358
|
+
timeoutError.reason = 'auth_timeout';
|
|
359
|
+
timeoutError.close = this.buildDisconnectInfo(null, 'auth_timeout', 'auth');
|
|
360
|
+
settleAuthReject(timeoutError, { suppressClose: true, closeReason: 'auth_timeout' });
|
|
361
|
+
}, this.authTimeoutMs);
|
|
362
|
+
|
|
291
363
|
ws.on('open', () => {
|
|
292
364
|
this.logger.info?.('[claworld:relay-client] websocket open, sending auth', {
|
|
293
365
|
accountId: this.runtimeConfig.accountId,
|
|
@@ -295,13 +367,17 @@ export class ClaworldRelayClient extends EventEmitter {
|
|
|
295
367
|
clientVersion,
|
|
296
368
|
bridgeProtocol: this.protocol.version,
|
|
297
369
|
});
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
370
|
+
try {
|
|
371
|
+
this.send({
|
|
372
|
+
type: 'auth',
|
|
373
|
+
agentId,
|
|
374
|
+
credential,
|
|
375
|
+
clientVersion,
|
|
376
|
+
bridgeProtocol: this.protocol.version,
|
|
377
|
+
});
|
|
378
|
+
} catch (error) {
|
|
379
|
+
settleAuthReject(error, { suppressClose: true, closeReason: 'auth_send_failed' });
|
|
380
|
+
}
|
|
305
381
|
});
|
|
306
382
|
|
|
307
383
|
ws.on('message', (buf) => {
|
|
@@ -316,9 +392,7 @@ export class ClaworldRelayClient extends EventEmitter {
|
|
|
316
392
|
context: { stage: 'message_parse' },
|
|
317
393
|
});
|
|
318
394
|
if (!settled) {
|
|
319
|
-
|
|
320
|
-
this.connectionState = 'error';
|
|
321
|
-
reject(normalized);
|
|
395
|
+
settleAuthReject(normalized, { suppressClose: true, closeReason: 'message_parse_failed' });
|
|
322
396
|
}
|
|
323
397
|
return;
|
|
324
398
|
}
|
|
@@ -327,7 +401,6 @@ export class ClaworldRelayClient extends EventEmitter {
|
|
|
327
401
|
this.emitRelayMessage(message, { sessionTarget, fallbackTarget });
|
|
328
402
|
|
|
329
403
|
if (message.event === 'auth.ok' && !settled) {
|
|
330
|
-
settled = true;
|
|
331
404
|
this.connectionState = 'authenticated';
|
|
332
405
|
this.reconnectAttempts = 0;
|
|
333
406
|
this.logger.info?.('[claworld:relay-client] auth ok', {
|
|
@@ -335,13 +408,10 @@ export class ClaworldRelayClient extends EventEmitter {
|
|
|
335
408
|
agentId,
|
|
336
409
|
});
|
|
337
410
|
this.startHeartbeatLoop();
|
|
338
|
-
|
|
411
|
+
settleAuthResolve(message);
|
|
339
412
|
}
|
|
340
413
|
|
|
341
414
|
if (message.event === 'error' && !settled && message.data?.code === 'unauthorized') {
|
|
342
|
-
settled = true;
|
|
343
|
-
suppressCloseHandler = true;
|
|
344
|
-
this.connectionState = 'error';
|
|
345
415
|
const authReason = message.data?.reason || message.data?.error || 'unauthorized';
|
|
346
416
|
const authError = createRuntimeBoundaryError({
|
|
347
417
|
code: message.data?.code || 'unauthorized',
|
|
@@ -364,7 +434,7 @@ export class ClaworldRelayClient extends EventEmitter {
|
|
|
364
434
|
error: authError.message,
|
|
365
435
|
code: authError.code,
|
|
366
436
|
});
|
|
367
|
-
|
|
437
|
+
settleAuthReject(authError, { suppressClose: true, closeReason: authReason });
|
|
368
438
|
}
|
|
369
439
|
} catch (error) {
|
|
370
440
|
const normalized = this.emitBoundaryError('[claworld:relay-client] relay message handling failed', error, {
|
|
@@ -377,10 +447,7 @@ export class ClaworldRelayClient extends EventEmitter {
|
|
|
377
447
|
},
|
|
378
448
|
});
|
|
379
449
|
if (!settled) {
|
|
380
|
-
|
|
381
|
-
suppressCloseHandler = true;
|
|
382
|
-
this.connectionState = 'error';
|
|
383
|
-
reject(normalized);
|
|
450
|
+
settleAuthReject(normalized, { suppressClose: true, closeReason: 'message_handling_failed' });
|
|
384
451
|
}
|
|
385
452
|
}
|
|
386
453
|
});
|
|
@@ -402,6 +469,7 @@ export class ClaworldRelayClient extends EventEmitter {
|
|
|
402
469
|
|
|
403
470
|
if (!settled) {
|
|
404
471
|
settled = true;
|
|
472
|
+
clearAuthTimer();
|
|
405
473
|
this.connectionState = disconnectInfo.reason === 'duplicate_connection_replaced' ? 'replaced' : 'error';
|
|
406
474
|
reject(this.createClosedBeforeAuthError(disconnectInfo));
|
|
407
475
|
return;
|
|
@@ -430,11 +498,8 @@ export class ClaworldRelayClient extends EventEmitter {
|
|
|
430
498
|
},
|
|
431
499
|
});
|
|
432
500
|
if (!settled) {
|
|
433
|
-
settled = true;
|
|
434
|
-
suppressCloseHandler = true;
|
|
435
|
-
this.connectionState = 'error';
|
|
436
501
|
normalized.reason = normalized.reason || normalized.code || normalized.message;
|
|
437
|
-
|
|
502
|
+
settleAuthReject(normalized, { suppressClose: true, closeReason: 'connect_error' });
|
|
438
503
|
}
|
|
439
504
|
});
|
|
440
505
|
});
|
|
@@ -448,8 +513,8 @@ export class ClaworldRelayClient extends EventEmitter {
|
|
|
448
513
|
}
|
|
449
514
|
|
|
450
515
|
this.clearReconnectTimer();
|
|
451
|
-
const delayMs = this.resolveReconnectDelayMs();
|
|
452
516
|
const attempt = this.reconnectAttempts + 1;
|
|
517
|
+
const delayMs = this.resolveReconnectDelayMs(attempt);
|
|
453
518
|
this.reconnectAttempts = attempt;
|
|
454
519
|
this.logger.warn?.('[claworld:relay-client] scheduling reconnect', {
|
|
455
520
|
accountId: this.runtimeConfig?.accountId || null,
|
|
@@ -793,7 +858,7 @@ export class ClaworldRelayClient extends EventEmitter {
|
|
|
793
858
|
});
|
|
794
859
|
}
|
|
795
860
|
|
|
796
|
-
waitForAcceptedAck({ deliveryId, timeoutMs = DEFAULT_REPLY_ACK_TIMEOUT_MS } = {}) {
|
|
861
|
+
waitForAcceptedAck({ deliveryId, timeoutMs = DEFAULT_REPLY_ACK_TIMEOUT_MS, signal = null } = {}) {
|
|
797
862
|
const normalizedDeliveryId = normalizeOptionalText(deliveryId);
|
|
798
863
|
if (!normalizedDeliveryId) {
|
|
799
864
|
return Promise.reject(createRuntimeBoundaryError({
|
|
@@ -805,6 +870,20 @@ export class ClaworldRelayClient extends EventEmitter {
|
|
|
805
870
|
recoverable: true,
|
|
806
871
|
}));
|
|
807
872
|
}
|
|
873
|
+
if (signal?.aborted) {
|
|
874
|
+
return Promise.reject(createRuntimeBoundaryError({
|
|
875
|
+
code: 'relay_delivery_accept_ack_wait_cancelled',
|
|
876
|
+
category: 'transport',
|
|
877
|
+
status: 499,
|
|
878
|
+
message: `relay delivery acceptance acknowledgement wait cancelled for ${normalizedDeliveryId}`,
|
|
879
|
+
publicMessage: 'relay delivery acceptance acknowledgement wait cancelled',
|
|
880
|
+
recoverable: true,
|
|
881
|
+
context: this.buildBoundaryContext({
|
|
882
|
+
stage: 'delivery_accept_ack_wait',
|
|
883
|
+
deliveryId: normalizedDeliveryId,
|
|
884
|
+
}),
|
|
885
|
+
}));
|
|
886
|
+
}
|
|
808
887
|
|
|
809
888
|
return new Promise((resolve, reject) => {
|
|
810
889
|
let settled = false;
|
|
@@ -815,6 +894,7 @@ export class ClaworldRelayClient extends EventEmitter {
|
|
|
815
894
|
this.off('delivery.accepted', onAccepted);
|
|
816
895
|
this.off('disconnect', onDisconnect);
|
|
817
896
|
this.off('close', onDisconnect);
|
|
897
|
+
signal?.removeEventListener('abort', onAbort);
|
|
818
898
|
};
|
|
819
899
|
|
|
820
900
|
const settleResolve = (value) => {
|
|
@@ -854,9 +934,25 @@ export class ClaworldRelayClient extends EventEmitter {
|
|
|
854
934
|
}));
|
|
855
935
|
};
|
|
856
936
|
|
|
937
|
+
const onAbort = () => {
|
|
938
|
+
settleReject(createRuntimeBoundaryError({
|
|
939
|
+
code: 'relay_delivery_accept_ack_wait_cancelled',
|
|
940
|
+
category: 'transport',
|
|
941
|
+
status: 499,
|
|
942
|
+
message: `relay delivery acceptance acknowledgement wait cancelled for ${normalizedDeliveryId}`,
|
|
943
|
+
publicMessage: 'relay delivery acceptance acknowledgement wait cancelled',
|
|
944
|
+
recoverable: true,
|
|
945
|
+
context: this.buildBoundaryContext({
|
|
946
|
+
stage: 'delivery_accept_ack_wait',
|
|
947
|
+
deliveryId: normalizedDeliveryId,
|
|
948
|
+
}),
|
|
949
|
+
}));
|
|
950
|
+
};
|
|
951
|
+
|
|
857
952
|
this.on('delivery.accepted', onAccepted);
|
|
858
953
|
this.on('disconnect', onDisconnect);
|
|
859
954
|
this.on('close', onDisconnect);
|
|
955
|
+
signal?.addEventListener('abort', onAbort, { once: true });
|
|
860
956
|
|
|
861
957
|
timeout = setTimeout(() => {
|
|
862
958
|
settleReject(buildAcceptedAckTimeoutError({
|
|
@@ -1097,6 +1193,52 @@ export class ClaworldRelayClient extends EventEmitter {
|
|
|
1097
1193
|
};
|
|
1098
1194
|
}
|
|
1099
1195
|
|
|
1196
|
+
async submitAcceptedHttpFallback({
|
|
1197
|
+
deliveryId,
|
|
1198
|
+
sessionKey,
|
|
1199
|
+
source = 'runtime_dispatch',
|
|
1200
|
+
error = null,
|
|
1201
|
+
} = {}) {
|
|
1202
|
+
this.logger.warn?.('[claworld:relay-client] delivery acceptance websocket transport failed; attempting HTTP fallback', {
|
|
1203
|
+
accountId: this.runtimeConfig?.accountId || null,
|
|
1204
|
+
agentId: this.boundAgentId,
|
|
1205
|
+
deliveryId: normalizeOptionalText(deliveryId),
|
|
1206
|
+
sessionKey: normalizeOptionalText(sessionKey) || null,
|
|
1207
|
+
error: error?.message || String(error),
|
|
1208
|
+
});
|
|
1209
|
+
|
|
1210
|
+
const fallbackResult = await this.acceptDeliveryHttp({
|
|
1211
|
+
deliveryId,
|
|
1212
|
+
sessionKey,
|
|
1213
|
+
source,
|
|
1214
|
+
});
|
|
1215
|
+
|
|
1216
|
+
if (fallbackResult.status >= 200 && fallbackResult.status < 300) {
|
|
1217
|
+
return {
|
|
1218
|
+
ok: true,
|
|
1219
|
+
envelope: fallbackResult.envelope,
|
|
1220
|
+
ack: {
|
|
1221
|
+
event: 'delivery.accepted',
|
|
1222
|
+
data: fallbackResult.body,
|
|
1223
|
+
},
|
|
1224
|
+
transport: 'http',
|
|
1225
|
+
fallbackUsed: true,
|
|
1226
|
+
};
|
|
1227
|
+
}
|
|
1228
|
+
|
|
1229
|
+
throw buildReplyFallbackError({
|
|
1230
|
+
deliveryId: fallbackResult.envelope.deliveryId,
|
|
1231
|
+
status: fallbackResult.status,
|
|
1232
|
+
body: fallbackResult.body,
|
|
1233
|
+
context: this.buildBoundaryContext({
|
|
1234
|
+
stage: 'delivery_accept_fallback',
|
|
1235
|
+
deliveryId: fallbackResult.envelope.deliveryId,
|
|
1236
|
+
sessionKey: normalizeOptionalText(sessionKey) || null,
|
|
1237
|
+
fallbackFrom: error?.code || error?.message || null,
|
|
1238
|
+
}),
|
|
1239
|
+
});
|
|
1240
|
+
}
|
|
1241
|
+
|
|
1100
1242
|
async sendReplyAndWaitForAck({
|
|
1101
1243
|
deliveryId,
|
|
1102
1244
|
sessionKey,
|
|
@@ -1190,15 +1332,40 @@ export class ClaworldRelayClient extends EventEmitter {
|
|
|
1190
1332
|
timeoutMs = DEFAULT_REPLY_ACK_TIMEOUT_MS,
|
|
1191
1333
|
httpFallback = true,
|
|
1192
1334
|
} = {}) {
|
|
1335
|
+
if (httpFallback && (!this.ws || this.ws.readyState !== 1)) {
|
|
1336
|
+
return await this.submitAcceptedHttpFallback({
|
|
1337
|
+
deliveryId,
|
|
1338
|
+
sessionKey,
|
|
1339
|
+
source,
|
|
1340
|
+
error: this.buildWsNotConnectedError('delivery_accept_send'),
|
|
1341
|
+
});
|
|
1342
|
+
}
|
|
1343
|
+
|
|
1344
|
+
const ackAbortController = new AbortController();
|
|
1193
1345
|
const ackPromise = this.waitForAcceptedAck({
|
|
1194
1346
|
deliveryId,
|
|
1195
1347
|
timeoutMs,
|
|
1348
|
+
signal: ackAbortController.signal,
|
|
1196
1349
|
});
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1350
|
+
let envelope;
|
|
1351
|
+
|
|
1352
|
+
try {
|
|
1353
|
+
envelope = this.sendAccepted({
|
|
1354
|
+
deliveryId,
|
|
1355
|
+
sessionKey,
|
|
1356
|
+
source,
|
|
1357
|
+
});
|
|
1358
|
+
} catch (error) {
|
|
1359
|
+
ackAbortController.abort();
|
|
1360
|
+
void ackPromise.catch(() => {});
|
|
1361
|
+
if (!httpFallback) throw error;
|
|
1362
|
+
return await this.submitAcceptedHttpFallback({
|
|
1363
|
+
deliveryId,
|
|
1364
|
+
sessionKey,
|
|
1365
|
+
source,
|
|
1366
|
+
error,
|
|
1367
|
+
});
|
|
1368
|
+
}
|
|
1202
1369
|
|
|
1203
1370
|
try {
|
|
1204
1371
|
const ack = await ackPromise;
|
|
@@ -1212,43 +1379,11 @@ export class ClaworldRelayClient extends EventEmitter {
|
|
|
1212
1379
|
} catch (error) {
|
|
1213
1380
|
if (!httpFallback) throw error;
|
|
1214
1381
|
|
|
1215
|
-
this.
|
|
1216
|
-
accountId: this.runtimeConfig?.accountId || null,
|
|
1217
|
-
agentId: this.boundAgentId,
|
|
1218
|
-
deliveryId: envelope.deliveryId,
|
|
1219
|
-
sessionKey: envelope.sessionKey,
|
|
1220
|
-
error: error?.message || String(error),
|
|
1221
|
-
});
|
|
1222
|
-
|
|
1223
|
-
const fallbackResult = await this.acceptDeliveryHttp({
|
|
1382
|
+
return await this.submitAcceptedHttpFallback({
|
|
1224
1383
|
deliveryId: envelope.deliveryId,
|
|
1225
1384
|
sessionKey: envelope.sessionKey,
|
|
1226
1385
|
source: envelope.source,
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
if (fallbackResult.status >= 200 && fallbackResult.status < 300) {
|
|
1230
|
-
return {
|
|
1231
|
-
ok: true,
|
|
1232
|
-
envelope,
|
|
1233
|
-
ack: {
|
|
1234
|
-
event: 'delivery.accepted',
|
|
1235
|
-
data: fallbackResult.body,
|
|
1236
|
-
},
|
|
1237
|
-
transport: 'http',
|
|
1238
|
-
fallbackUsed: true,
|
|
1239
|
-
};
|
|
1240
|
-
}
|
|
1241
|
-
|
|
1242
|
-
throw buildReplyFallbackError({
|
|
1243
|
-
deliveryId: envelope.deliveryId,
|
|
1244
|
-
status: fallbackResult.status,
|
|
1245
|
-
body: fallbackResult.body,
|
|
1246
|
-
context: this.buildBoundaryContext({
|
|
1247
|
-
stage: 'delivery_accept_fallback',
|
|
1248
|
-
deliveryId: envelope.deliveryId,
|
|
1249
|
-
sessionKey: envelope.sessionKey,
|
|
1250
|
-
fallbackFrom: error?.code || error?.message || null,
|
|
1251
|
-
}),
|
|
1386
|
+
error,
|
|
1252
1387
|
});
|
|
1253
1388
|
}
|
|
1254
1389
|
}
|