@xmoxmo/bncr 0.2.7 → 0.2.8
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 +1 -1
- package/scripts/check-pack.mjs +21 -5
- package/src/channel.ts +286 -614
- package/src/core/extended-diagnostics.ts +10 -0
- package/src/core/file-ack.ts +9 -0
- package/src/core/file-transfer-payloads.ts +72 -0
- package/src/core/register-trace.ts +79 -0
- package/src/messaging/inbound/runtime-compat.ts +3 -1
- package/src/plugin/capabilities.ts +8 -0
- package/src/plugin/config.ts +35 -0
- package/src/plugin/gateway-methods.ts +12 -0
- package/src/plugin/gateway-runtime.ts +11 -0
- package/src/plugin/message-policy.ts +4 -0
- package/src/plugin/message-send.ts +13 -0
- package/src/plugin/messaging.ts +142 -0
- package/src/plugin/meta.ts +10 -0
- package/src/plugin/outbound.ts +51 -0
- package/src/plugin/setup.ts +24 -0
- package/src/plugin/status.ts +38 -0
- package/src/runtime/log-dedupe.ts +56 -0
- package/src/runtime/outbound-ack-timeout.ts +96 -0
- package/src/runtime/outbound-flags.ts +81 -0
- package/src/runtime/outbox-transitions.ts +119 -0
- package/src/runtime/status-snapshots.ts +108 -0
- package/src/runtime/status-worker.ts +172 -0
package/src/channel.ts
CHANGED
|
@@ -12,7 +12,6 @@ import {
|
|
|
12
12
|
listAccountIds,
|
|
13
13
|
normalizeAccountId,
|
|
14
14
|
resolveAccount,
|
|
15
|
-
resolveDefaultDisplayName,
|
|
16
15
|
} from './core/accounts.ts';
|
|
17
16
|
import { BncrConfigSchema } from './core/config-schema.ts';
|
|
18
17
|
import {
|
|
@@ -67,6 +66,13 @@ import { buildDownlinkHealth as buildDownlinkHealthFromRuntime } from './core/do
|
|
|
67
66
|
import { buildExtendedDiagnostics as buildExtendedDiagnosticsFromRuntime } from './core/extended-diagnostics.ts';
|
|
68
67
|
import { observeLeaseState, matchesTransferOwner as matchesTransferOwnerFromRuntime } from './core/lease-state.ts';
|
|
69
68
|
import { emitBncrLog, emitBncrLogLine } from './core/logging.ts';
|
|
69
|
+
import { buildFileAckKey } from './core/file-ack.ts';
|
|
70
|
+
import {
|
|
71
|
+
buildFileTransferAbortPayload,
|
|
72
|
+
buildFileTransferChunkPayload,
|
|
73
|
+
buildFileTransferCompletePayload,
|
|
74
|
+
buildFileTransferInitPayload,
|
|
75
|
+
} from './core/file-transfer-payloads.ts';
|
|
70
76
|
import { resolveBncrChannelPolicy, resolveBncrConfigWarnings } from './core/policy.ts';
|
|
71
77
|
import {
|
|
72
78
|
getOpenClawRuntimeConfig,
|
|
@@ -79,23 +85,21 @@ import {
|
|
|
79
85
|
} from './openclaw/media-runtime.ts';
|
|
80
86
|
import { resolveOpenClawAgentRoute } from './openclaw/routing-runtime.ts';
|
|
81
87
|
import {
|
|
82
|
-
applyOpenClawAccountNameToChannelSection,
|
|
83
|
-
createOpenClawDefaultChannelRuntimeState,
|
|
84
88
|
extractOpenClawToolSend,
|
|
85
89
|
openClawJsonResult,
|
|
86
90
|
readOpenClawBooleanParam,
|
|
87
91
|
readOpenClawJsonFileWithFallback,
|
|
88
92
|
readOpenClawStringParam,
|
|
89
|
-
setOpenClawAccountEnabledInConfigSection,
|
|
90
93
|
writeOpenClawJsonFileAtomically,
|
|
91
94
|
} from './openclaw/sdk-helpers.ts';
|
|
92
95
|
import {
|
|
96
|
+
appendBoundedRegisterTrace,
|
|
97
|
+
buildRegisterDriftSnapshot,
|
|
98
|
+
buildRegisterTraceEntry,
|
|
93
99
|
buildRegisterTraceSummary as buildRegisterTraceSummaryFromEntries,
|
|
94
|
-
classifyRegisterTrace as classifyRegisterTraceFromStack,
|
|
95
100
|
} from './core/register-trace.ts';
|
|
96
101
|
import {
|
|
97
102
|
buildAccountRuntimeSnapshot,
|
|
98
|
-
buildAccountStatusSnapshot,
|
|
99
103
|
buildIntegratedDiagnostics as buildIntegratedDiagnosticsFromRuntime,
|
|
100
104
|
buildStatusHeadlineFromRuntime,
|
|
101
105
|
buildStatusMetaFromRuntime,
|
|
@@ -103,11 +107,9 @@ import {
|
|
|
103
107
|
import {
|
|
104
108
|
buildCanonicalBncrSessionKey,
|
|
105
109
|
formatDisplayScope,
|
|
106
|
-
formatTargetDisplay,
|
|
107
110
|
isLowerHex,
|
|
108
111
|
normalizeInboundSessionKey,
|
|
109
112
|
normalizeStoredSessionKey,
|
|
110
|
-
parseExplicitTarget,
|
|
111
113
|
parseRouteFromDisplayScope,
|
|
112
114
|
parseRouteFromHexScope,
|
|
113
115
|
parseRouteFromScope,
|
|
@@ -321,11 +323,47 @@ import {
|
|
|
321
323
|
} from './messaging/outbound/retry-policy.ts';
|
|
322
324
|
import { sendBncrMedia, sendBncrText } from './messaging/outbound/send.ts';
|
|
323
325
|
import { buildBncrDurableQueuedResult } from './messaging/outbound/durable-queue-adapter.ts';
|
|
324
|
-
import {
|
|
326
|
+
import { BNCR_CHANNEL_CAPABILITIES } from './plugin/capabilities.ts';
|
|
327
|
+
import { BNCR_CONFIG_SURFACE } from './plugin/config.ts';
|
|
328
|
+
import { createBncrGatewayRuntime } from './plugin/gateway-runtime.ts';
|
|
329
|
+
import { BNCR_GATEWAY_METHODS } from './plugin/gateway-methods.ts';
|
|
330
|
+
import { BNCR_CHANNEL_META } from './plugin/meta.ts';
|
|
331
|
+
import { createBncrMessagingSurface } from './plugin/messaging.ts';
|
|
332
|
+
import { createBncrMessageSend } from './plugin/message-send.ts';
|
|
333
|
+
import { BNCR_MESSAGE_RECEIVE_POLICY } from './plugin/message-policy.ts';
|
|
334
|
+
import { createBncrOutboundRuntime } from './plugin/outbound.ts';
|
|
335
|
+
import { BNCR_SETUP_SURFACE } from './plugin/setup.ts';
|
|
336
|
+
import { createBncrStatusSurface } from './plugin/status.ts';
|
|
337
|
+
import {
|
|
338
|
+
clearAllBncrStatusWorkers,
|
|
339
|
+
clearBncrStatusWorker,
|
|
340
|
+
startBncrStatusWorker,
|
|
341
|
+
stopBncrStatusWorker,
|
|
342
|
+
type ChannelAccountWorkerHandle,
|
|
343
|
+
} from './runtime/status-worker.ts';
|
|
344
|
+
import {
|
|
345
|
+
buildBncrRuntimeAckStrategy,
|
|
346
|
+
computeBncrRecommendedAckTimeoutMs,
|
|
347
|
+
computeBncrRecommendedAckTimeoutReason,
|
|
348
|
+
} from './runtime/outbound-ack-timeout.ts';
|
|
349
|
+
import {
|
|
350
|
+
buildBncrRuntimeFlags,
|
|
351
|
+
buildBncrRuntimeStatusInput,
|
|
352
|
+
resolveBncrOutboundAckRequired,
|
|
353
|
+
} from './runtime/outbound-flags.ts';
|
|
354
|
+
import { buildRuntimeStatusSnapshots } from './runtime/status-snapshots.ts';
|
|
325
355
|
import {
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
356
|
+
applyBncrPushFailureDecisionToEntry,
|
|
357
|
+
applyBncrRetryRerouteDecisionToEntry,
|
|
358
|
+
buildBncrAckOkTelemetryPatch,
|
|
359
|
+
buildBncrAckRetryEntryPatch,
|
|
360
|
+
buildBncrOutboxFailureEntryPatch,
|
|
361
|
+
buildBncrOutboxPushSuccessEntryPatch,
|
|
362
|
+
} from './runtime/outbox-transitions.ts';
|
|
363
|
+
import {
|
|
364
|
+
pruneLogDedupeState as pruneLogDedupeStateFromRuntime,
|
|
365
|
+
shouldEmitDedupLog as shouldEmitDedupLogFromRuntime,
|
|
366
|
+
} from './runtime/log-dedupe.ts';
|
|
329
367
|
const BRIDGE_VERSION = 2;
|
|
330
368
|
const BNCR_PUSH_EVENT = 'plugin.bncr.push';
|
|
331
369
|
const BNCR_FILE_INIT_EVENT = 'plugin.bncr.file.init';
|
|
@@ -351,8 +389,6 @@ const ADAPTIVE_ACK_TIMEOUT_LOG_THROTTLE_MS = 5 * 60 * 1000;
|
|
|
351
389
|
const OUTBOUND_READY_TTL_MS = 30_000;
|
|
352
390
|
const PREFERRED_OUTBOUND_TTL_MS = 12_000;
|
|
353
391
|
const FILE_FORCE_CHUNK = true; // 统一走 WS 分块,保留 base64 仅作兜底
|
|
354
|
-
const LOG_DEDUPE_STATE_TTL_MS = 10 * 60 * 1000;
|
|
355
|
-
const LOG_DEDUPE_STATE_MAX_ENTRIES = 1_000;
|
|
356
392
|
const FILE_INLINE_THRESHOLD = 5 * 1024 * 1024; // fallback 阈值(仅 FILE_FORCE_CHUNK=false 时生效)
|
|
357
393
|
const FILE_CHUNK_SIZE = 256 * 1024; // 256KB
|
|
358
394
|
const INBOUND_FILE_TRANSFER_MAX_BYTES = 50 * 1024 * 1024;
|
|
@@ -389,12 +425,6 @@ type FileSendTransferState = {
|
|
|
389
425
|
error?: string;
|
|
390
426
|
};
|
|
391
427
|
|
|
392
|
-
type ChannelAccountWorkerHandle = {
|
|
393
|
-
timer: NodeJS.Timeout;
|
|
394
|
-
finish: (reason: string) => void;
|
|
395
|
-
cleanupAbortListener?: () => void;
|
|
396
|
-
};
|
|
397
|
-
|
|
398
428
|
type FileRecvTransferState = {
|
|
399
429
|
transferId: string;
|
|
400
430
|
accountId: string;
|
|
@@ -837,27 +867,17 @@ class BncrBridgeRuntime {
|
|
|
837
867
|
}
|
|
838
868
|
|
|
839
869
|
private pruneLogDedupeState(currentTime = now()) {
|
|
840
|
-
|
|
841
|
-
if (currentTime - entry.at > LOG_DEDUPE_STATE_TTL_MS) {
|
|
842
|
-
this.logDedupeState.delete(key);
|
|
843
|
-
}
|
|
844
|
-
}
|
|
845
|
-
|
|
846
|
-
while (this.logDedupeState.size > LOG_DEDUPE_STATE_MAX_ENTRIES) {
|
|
847
|
-
const oldestKey = this.logDedupeState.keys().next().value;
|
|
848
|
-
if (!oldestKey) break;
|
|
849
|
-
this.logDedupeState.delete(oldestKey);
|
|
850
|
-
}
|
|
870
|
+
pruneLogDedupeStateFromRuntime(this.logDedupeState, currentTime);
|
|
851
871
|
}
|
|
852
872
|
|
|
853
873
|
private shouldEmitDedupLog(key: string, sig: string, windowMs = 5 * 60 * 1000) {
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
874
|
+
return shouldEmitDedupLogFromRuntime({
|
|
875
|
+
state: this.logDedupeState,
|
|
876
|
+
key,
|
|
877
|
+
sig,
|
|
878
|
+
nowMs: now(),
|
|
879
|
+
windowMs,
|
|
880
|
+
});
|
|
861
881
|
}
|
|
862
882
|
|
|
863
883
|
private logInfoDedup(
|
|
@@ -954,39 +974,64 @@ class BncrBridgeRuntime {
|
|
|
954
974
|
);
|
|
955
975
|
}
|
|
956
976
|
|
|
977
|
+
private buildStatusWorkerRuntime() {
|
|
978
|
+
return {
|
|
979
|
+
workers: this.channelAccountWorkers,
|
|
980
|
+
bridgeId: this.bridgeId,
|
|
981
|
+
hooks: {
|
|
982
|
+
isOnline: (accountId: string) => this.isOnline(accountId),
|
|
983
|
+
hasRecentInboundReachability: (accountId: string) =>
|
|
984
|
+
this.hasRecentInboundReachability(accountId),
|
|
985
|
+
getLastActivityAt: (accountId: string, previous: Record<string, any>) =>
|
|
986
|
+
this.lastActivityByAccount.get(accountId) ||
|
|
987
|
+
this.lastInboundByAccount.get(accountId) ||
|
|
988
|
+
this.lastOutboundByAccount.get(accountId) ||
|
|
989
|
+
previous?.lastEventAt ||
|
|
990
|
+
null,
|
|
991
|
+
getActiveConnectionKey: (accountId: string) =>
|
|
992
|
+
this.activeConnectionByAccount.get(accountId) || null,
|
|
993
|
+
getActiveConnections: (accountId: string) =>
|
|
994
|
+
Array.from(this.connections.values())
|
|
995
|
+
.filter((c) => c.accountId === accountId)
|
|
996
|
+
.map((c) => ({
|
|
997
|
+
connId: c.connId,
|
|
998
|
+
clientId: c.clientId,
|
|
999
|
+
inboundOnly: c.inboundOnly === true,
|
|
1000
|
+
outboundReady: c.outboundReady === true,
|
|
1001
|
+
preferredForOutbound: c.preferredForOutbound === true,
|
|
1002
|
+
})),
|
|
1003
|
+
buildStatusMeta: (accountId: string) => this.buildStatusMeta(accountId),
|
|
1004
|
+
logInfo: (scope: string | undefined, message: string, options?: { debugOnly?: boolean }) =>
|
|
1005
|
+
this.logInfo(scope, message, options),
|
|
1006
|
+
logInfoDedup: (
|
|
1007
|
+
scope: string | undefined,
|
|
1008
|
+
message: string,
|
|
1009
|
+
options: { key: string; sig: string; debugOnly?: boolean; windowMs?: number },
|
|
1010
|
+
) => this.logInfoDedup(scope, message, options),
|
|
1011
|
+
},
|
|
1012
|
+
};
|
|
1013
|
+
}
|
|
1014
|
+
|
|
957
1015
|
private clearChannelAccountWorker(accountId: string, reason: string) {
|
|
958
|
-
|
|
959
|
-
if (!worker) return false;
|
|
960
|
-
worker.finish(reason);
|
|
961
|
-
this.logInfo(
|
|
962
|
-
'health',
|
|
963
|
-
`status-worker cleared ${JSON.stringify({ bridge: this.bridgeId, accountId, reason })}`,
|
|
964
|
-
{ debugOnly: true },
|
|
965
|
-
);
|
|
966
|
-
return true;
|
|
1016
|
+
return clearBncrStatusWorker(this.buildStatusWorkerRuntime(), accountId, reason);
|
|
967
1017
|
}
|
|
968
1018
|
|
|
969
1019
|
private clearAllChannelAccountWorkers(reason: string) {
|
|
970
|
-
|
|
971
|
-
this.clearChannelAccountWorker(accountId, reason);
|
|
972
|
-
}
|
|
1020
|
+
clearAllBncrStatusWorkers(this.buildStatusWorkerRuntime(), reason);
|
|
973
1021
|
}
|
|
974
1022
|
|
|
975
1023
|
private captureDriftSnapshot(
|
|
976
1024
|
summary: ReturnType<BncrBridgeRuntime['buildRegisterTraceSummary']>,
|
|
977
1025
|
) {
|
|
978
|
-
this.lastDriftSnapshot = {
|
|
1026
|
+
this.lastDriftSnapshot = buildRegisterDriftSnapshot({
|
|
979
1027
|
capturedAt: now(),
|
|
980
1028
|
registerCount: this.registerCount,
|
|
981
1029
|
apiGeneration: this.apiGeneration,
|
|
982
|
-
|
|
1030
|
+
summary,
|
|
983
1031
|
apiInstanceId: this.lastApiInstanceId,
|
|
984
1032
|
registryFingerprint: this.lastRegistryFingerprint,
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
traceWindowSize: this.registerTraceRecent.length,
|
|
988
|
-
traceRecent: this.registerTraceRecent.map((trace) => ({ ...trace })),
|
|
989
|
-
};
|
|
1033
|
+
traceRecent: this.registerTraceRecent,
|
|
1034
|
+
});
|
|
990
1035
|
this.scheduleSave();
|
|
991
1036
|
}
|
|
992
1037
|
|
|
@@ -1026,9 +1071,7 @@ class BncrBridgeRuntime {
|
|
|
1026
1071
|
.map((line) => line.trim())
|
|
1027
1072
|
.filter(Boolean)
|
|
1028
1073
|
.join(' <- ');
|
|
1029
|
-
const
|
|
1030
|
-
|
|
1031
|
-
const trace = {
|
|
1074
|
+
const trace = buildRegisterTraceEntry({
|
|
1032
1075
|
ts,
|
|
1033
1076
|
bridgeId: this.bridgeId,
|
|
1034
1077
|
gatewayPid: this.gatewayPid,
|
|
@@ -1040,11 +1083,8 @@ class BncrBridgeRuntime {
|
|
|
1040
1083
|
source: this.pluginSource,
|
|
1041
1084
|
pluginVersion: this.pluginVersion,
|
|
1042
1085
|
stack,
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
this.registerTraceRecent.push(trace);
|
|
1046
|
-
if (this.registerTraceRecent.length > 12)
|
|
1047
|
-
this.registerTraceRecent.splice(0, this.registerTraceRecent.length - 12);
|
|
1086
|
+
});
|
|
1087
|
+
appendBoundedRegisterTrace(this.registerTraceRecent, trace, 12);
|
|
1048
1088
|
|
|
1049
1089
|
const summary = this.buildRegisterTraceSummary();
|
|
1050
1090
|
if (summary.postWarmupRegisterCount > 0) this.captureDriftSnapshot(summary);
|
|
@@ -1147,11 +1187,29 @@ class BncrBridgeRuntime {
|
|
|
1147
1187
|
return matchesTransferOwnerFromRuntime(params);
|
|
1148
1188
|
}
|
|
1149
1189
|
|
|
1190
|
+
private buildRuntimeSurfaceDiagnostics() {
|
|
1191
|
+
const channelRuntime = (this.api as any)?.runtime?.channel;
|
|
1192
|
+
const surfaces = {
|
|
1193
|
+
inbound: Boolean(channelRuntime?.inbound),
|
|
1194
|
+
media: Boolean(channelRuntime?.media),
|
|
1195
|
+
reply: Boolean(channelRuntime?.reply),
|
|
1196
|
+
routing: Boolean(channelRuntime?.routing),
|
|
1197
|
+
session: Boolean(channelRuntime?.session),
|
|
1198
|
+
};
|
|
1199
|
+
return {
|
|
1200
|
+
channel: surfaces,
|
|
1201
|
+
missing: Object.entries(surfaces)
|
|
1202
|
+
.filter(([, present]) => !present)
|
|
1203
|
+
.map(([name]) => name),
|
|
1204
|
+
};
|
|
1205
|
+
}
|
|
1206
|
+
|
|
1150
1207
|
private buildExtendedDiagnostics(accountId: string) {
|
|
1151
1208
|
const acc = normalizeAccountId(accountId);
|
|
1152
1209
|
const diagnostics = this.buildIntegratedDiagnostics(acc) as Record<string, any>;
|
|
1153
1210
|
return buildExtendedDiagnosticsFromRuntime({
|
|
1154
1211
|
diagnostics,
|
|
1212
|
+
runtimeSurface: this.buildRuntimeSurfaceDiagnostics(),
|
|
1155
1213
|
register: {
|
|
1156
1214
|
bridgeId: this.bridgeId,
|
|
1157
1215
|
gatewayPid: this.gatewayPid,
|
|
@@ -2733,19 +2791,18 @@ class BncrBridgeRuntime {
|
|
|
2733
2791
|
outboundReady: true,
|
|
2734
2792
|
preferredForOutbound: true,
|
|
2735
2793
|
});
|
|
2736
|
-
const
|
|
2794
|
+
const telemetryPatch = buildBncrAckOkTelemetryPatch({
|
|
2795
|
+
entry: args.entry,
|
|
2796
|
+
ackAt: now(),
|
|
2797
|
+
defaultAckTimeoutMs: PUSH_ACK_TIMEOUT_MS,
|
|
2798
|
+
});
|
|
2799
|
+
const { ackAt, ackQueueLatencyMs, ackPushLatencyMs, lateAccepted } = telemetryPatch;
|
|
2737
2800
|
this.lastAckOkByAccount.set(args.accountId, ackAt);
|
|
2738
|
-
const ackQueueLatencyMs = Math.max(0, ackAt - finiteNumberOr(args.entry.createdAt, ackAt));
|
|
2739
|
-
const ackPushLatencyMs =
|
|
2740
|
-
typeof args.entry.lastPushAt === 'number'
|
|
2741
|
-
? Math.max(0, ackAt - args.entry.lastPushAt)
|
|
2742
|
-
: null;
|
|
2743
2801
|
this.lastAckQueueLatencyMsByAccount.set(args.accountId, ackQueueLatencyMs);
|
|
2744
2802
|
if (typeof ackPushLatencyMs === 'number') {
|
|
2745
2803
|
this.lastAckPushLatencyMsByAccount.set(args.accountId, ackPushLatencyMs);
|
|
2746
2804
|
}
|
|
2747
|
-
|
|
2748
|
-
if (lateAccepted) {
|
|
2805
|
+
if (telemetryPatch.shouldResetAdaptiveAckRecovery) {
|
|
2749
2806
|
this.adaptiveAckRecoveryOkCountByAccount.set(args.accountId, 0);
|
|
2750
2807
|
this.lateAckOkCountByAccount.set(
|
|
2751
2808
|
args.accountId,
|
|
@@ -2758,7 +2815,7 @@ class BncrBridgeRuntime {
|
|
|
2758
2815
|
}
|
|
2759
2816
|
args.entry.awaitingRetryPush = false;
|
|
2760
2817
|
args.entry.lastError = undefined;
|
|
2761
|
-
} else if (
|
|
2818
|
+
} else if (telemetryPatch.shouldIncrementAdaptiveAckRecovery) {
|
|
2762
2819
|
this.adaptiveAckRecoveryOkCountByAccount.set(
|
|
2763
2820
|
args.accountId,
|
|
2764
2821
|
this.getCounter(this.adaptiveAckRecoveryOkCountByAccount, args.accountId) + 1,
|
|
@@ -2800,16 +2857,18 @@ class BncrBridgeRuntime {
|
|
|
2800
2857
|
clientId?: string;
|
|
2801
2858
|
error: string;
|
|
2802
2859
|
}) {
|
|
2803
|
-
|
|
2804
|
-
|
|
2805
|
-
|
|
2806
|
-
|
|
2860
|
+
const nextEntry = buildBncrAckRetryEntryPatch({
|
|
2861
|
+
entry: args.entry,
|
|
2862
|
+
error: args.error,
|
|
2863
|
+
nextAttemptAt: now() + 1_000,
|
|
2864
|
+
});
|
|
2865
|
+
this.outbox.set(args.messageId, nextEntry);
|
|
2807
2866
|
this.scheduleSave();
|
|
2808
2867
|
this.logOutboxAckSummary('outbox ack retry', {
|
|
2809
2868
|
messageId: args.messageId,
|
|
2810
2869
|
connId: args.connId,
|
|
2811
2870
|
clientId: args.clientId,
|
|
2812
|
-
err:
|
|
2871
|
+
err: nextEntry.lastError,
|
|
2813
2872
|
});
|
|
2814
2873
|
}
|
|
2815
2874
|
|
|
@@ -3028,8 +3087,12 @@ class BncrBridgeRuntime {
|
|
|
3028
3087
|
entry: OutboxEntry;
|
|
3029
3088
|
lastError: string;
|
|
3030
3089
|
}) {
|
|
3031
|
-
|
|
3032
|
-
|
|
3090
|
+
const nextEntry = buildBncrOutboxFailureEntryPatch({
|
|
3091
|
+
entry: args.entry,
|
|
3092
|
+
lastError: args.lastError,
|
|
3093
|
+
});
|
|
3094
|
+
Object.assign(args.entry, nextEntry);
|
|
3095
|
+
this.outbox.set(nextEntry.messageId, args.entry);
|
|
3033
3096
|
}
|
|
3034
3097
|
|
|
3035
3098
|
private recordOutboxPushFailure(args: {
|
|
@@ -3038,8 +3101,12 @@ class BncrBridgeRuntime {
|
|
|
3038
3101
|
fallbackError: string;
|
|
3039
3102
|
persist?: boolean;
|
|
3040
3103
|
}) {
|
|
3041
|
-
|
|
3042
|
-
|
|
3104
|
+
const nextEntry = buildBncrOutboxFailureEntryPatch({
|
|
3105
|
+
entry: args.entry,
|
|
3106
|
+
lastError: asString((args.error as any)?.message || args.error || args.fallbackError),
|
|
3107
|
+
});
|
|
3108
|
+
Object.assign(args.entry, nextEntry);
|
|
3109
|
+
this.outbox.set(nextEntry.messageId, args.entry);
|
|
3043
3110
|
if (args.persist) this.scheduleSave();
|
|
3044
3111
|
}
|
|
3045
3112
|
|
|
@@ -3050,23 +3117,19 @@ class BncrBridgeRuntime {
|
|
|
3050
3117
|
ownerClientId?: string;
|
|
3051
3118
|
clearLastError?: boolean;
|
|
3052
3119
|
}) {
|
|
3053
|
-
const
|
|
3054
|
-
|
|
3055
|
-
|
|
3056
|
-
args.
|
|
3057
|
-
|
|
3058
|
-
|
|
3059
|
-
|
|
3060
|
-
|
|
3061
|
-
|
|
3062
|
-
|
|
3063
|
-
)
|
|
3064
|
-
|
|
3065
|
-
|
|
3066
|
-
if (args.clearLastError) args.entry.lastError = undefined;
|
|
3067
|
-
this.outbox.set(args.entry.messageId, args.entry);
|
|
3068
|
-
this.lastOutboundByAccount.set(args.entry.accountId, args.entry.lastPushAt);
|
|
3069
|
-
this.markActivity(args.entry.accountId, args.entry.lastPushAt);
|
|
3120
|
+
const pushedAt = now();
|
|
3121
|
+
const nextEntry = buildBncrOutboxPushSuccessEntryPatch({
|
|
3122
|
+
entry: args.entry,
|
|
3123
|
+
connIds: args.connIds,
|
|
3124
|
+
pushedAt,
|
|
3125
|
+
ownerConnId: args.ownerConnId,
|
|
3126
|
+
ownerClientId: args.ownerClientId,
|
|
3127
|
+
clearLastError: args.clearLastError,
|
|
3128
|
+
});
|
|
3129
|
+
Object.assign(args.entry, nextEntry);
|
|
3130
|
+
this.outbox.set(nextEntry.messageId, args.entry);
|
|
3131
|
+
this.lastOutboundByAccount.set(nextEntry.accountId, pushedAt);
|
|
3132
|
+
this.markActivity(nextEntry.accountId, pushedAt);
|
|
3070
3133
|
this.scheduleSave();
|
|
3071
3134
|
}
|
|
3072
3135
|
|
|
@@ -3101,41 +3164,19 @@ class BncrBridgeRuntime {
|
|
|
3101
3164
|
}
|
|
3102
3165
|
|
|
3103
3166
|
private isOutboundAckRequired(accountId?: string) {
|
|
3104
|
-
|
|
3105
|
-
const cfg = getOpenClawRuntimeConfig(this.api);
|
|
3106
|
-
const channelCfg = (cfg as any)?.channels?.[CHANNEL_ID];
|
|
3107
|
-
const accountCfg =
|
|
3108
|
-
accountId && channelCfg?.accounts && typeof channelCfg.accounts === 'object'
|
|
3109
|
-
? (channelCfg.accounts as Record<string, any>)[normalizeAccountId(accountId)]
|
|
3110
|
-
: null;
|
|
3111
|
-
const scoped = accountCfg?.outboundRequireAck;
|
|
3112
|
-
const global = channelCfg?.outboundRequireAck;
|
|
3113
|
-
if (typeof scoped === 'boolean') return scoped;
|
|
3114
|
-
if (typeof global === 'boolean') return global;
|
|
3115
|
-
return true;
|
|
3116
|
-
} catch {
|
|
3117
|
-
return true;
|
|
3118
|
-
}
|
|
3167
|
+
return resolveBncrOutboundAckRequired({ api: this.api, accountId });
|
|
3119
3168
|
}
|
|
3120
3169
|
|
|
3121
3170
|
private buildRuntimeFlags(accountId?: string) {
|
|
3122
|
-
|
|
3123
|
-
|
|
3124
|
-
|
|
3125
|
-
|
|
3126
|
-
if (typeof global === 'boolean') ackPolicySource = 'channel';
|
|
3127
|
-
} catch {
|
|
3128
|
-
// keep default source
|
|
3129
|
-
}
|
|
3130
|
-
return {
|
|
3131
|
-
outboundRequireAck: this.isOutboundAckRequired(accountId),
|
|
3132
|
-
ackPolicySource,
|
|
3133
|
-
messageAckTimeoutMs: this.resolveMessageAckTimeoutMs(accountId),
|
|
3171
|
+
return buildBncrRuntimeFlags({
|
|
3172
|
+
api: this.api,
|
|
3173
|
+
accountId,
|
|
3174
|
+
resolveMessageAckTimeoutMs: (acc?: string) => this.resolveMessageAckTimeoutMs(acc),
|
|
3134
3175
|
adaptiveAckTimeoutEnabled: ADAPTIVE_ACK_TIMEOUT_DEFAULT_ENABLED,
|
|
3135
3176
|
defaultMessageAckTimeoutMs: PUSH_ACK_TIMEOUT_MS,
|
|
3136
3177
|
fileAckTimeoutMs: FILE_ACK_TIMEOUT_MS,
|
|
3137
3178
|
debugVerbose: BNCR_DEBUG_VERBOSE,
|
|
3138
|
-
};
|
|
3179
|
+
});
|
|
3139
3180
|
}
|
|
3140
3181
|
|
|
3141
3182
|
private async flushPushQueue(args?: {
|
|
@@ -3356,14 +3397,8 @@ class BncrBridgeRuntime {
|
|
|
3356
3397
|
continue;
|
|
3357
3398
|
}
|
|
3358
3399
|
|
|
3359
|
-
|
|
3360
|
-
entry.
|
|
3361
|
-
entry.retryCount = decision.nextRetryCount;
|
|
3362
|
-
entry.lastAttemptAt = decision.lastAttemptAt;
|
|
3363
|
-
entry.nextAttemptAt = decision.nextAttemptAt;
|
|
3364
|
-
entry.lastError = decision.lastError;
|
|
3365
|
-
entry.routeAttemptRound = decision.routeAttemptRound;
|
|
3366
|
-
this.outbox.set(entry.messageId, entry);
|
|
3400
|
+
const nextEntry = applyBncrRetryRerouteDecisionToEntry(entry, decision);
|
|
3401
|
+
this.outbox.set(entry.messageId, nextEntry);
|
|
3367
3402
|
this.scheduleSave();
|
|
3368
3403
|
if (requireAck) {
|
|
3369
3404
|
this.lastAckTimeoutByAccount.set(acc, now());
|
|
@@ -3377,7 +3412,7 @@ class BncrBridgeRuntime {
|
|
|
3377
3412
|
localNextDelay = updateMinOutboxDelay(localNextDelay, wait);
|
|
3378
3413
|
this.logOutboxAckReroute({
|
|
3379
3414
|
accountId: acc,
|
|
3380
|
-
entry,
|
|
3415
|
+
entry: nextEntry,
|
|
3381
3416
|
requireAck,
|
|
3382
3417
|
currentConnId,
|
|
3383
3418
|
availableConnIds,
|
|
@@ -3408,11 +3443,8 @@ class BncrBridgeRuntime {
|
|
|
3408
3443
|
continue;
|
|
3409
3444
|
}
|
|
3410
3445
|
|
|
3411
|
-
|
|
3412
|
-
entry.
|
|
3413
|
-
entry.nextAttemptAt = decision.nextAttemptAt;
|
|
3414
|
-
entry.lastError = decision.lastError;
|
|
3415
|
-
this.outbox.set(entry.messageId, entry);
|
|
3446
|
+
const nextEntry = applyBncrPushFailureDecisionToEntry(entry, decision);
|
|
3447
|
+
this.outbox.set(entry.messageId, nextEntry);
|
|
3416
3448
|
this.scheduleSave();
|
|
3417
3449
|
|
|
3418
3450
|
const wait = computeOutboxRetryWait(decision.nextAttemptAt, t);
|
|
@@ -3966,9 +3998,7 @@ class BncrBridgeRuntime {
|
|
|
3966
3998
|
}
|
|
3967
3999
|
|
|
3968
4000
|
private fileAckKey(transferId: string, stage: string, chunkIndex?: number): string {
|
|
3969
|
-
|
|
3970
|
-
const idx = Number.isInteger(n) && n >= 0 ? String(n) : '-';
|
|
3971
|
-
return `${transferId}|${stage}|${idx}`;
|
|
4001
|
+
return buildFileAckKey({ transferId, stage, chunkIndex });
|
|
3972
4002
|
}
|
|
3973
4003
|
|
|
3974
4004
|
private fileAckOwnerInfo(transferId: string) {
|
|
@@ -4197,30 +4227,6 @@ class BncrBridgeRuntime {
|
|
|
4197
4227
|
}
|
|
4198
4228
|
|
|
4199
4229
|
|
|
4200
|
-
private buildRuntimeQueueSnapshot(accountId: string) {
|
|
4201
|
-
const pending = Array.from(this.outbox.values()).filter((v) => v.accountId === accountId).length;
|
|
4202
|
-
const deadLetter = this.deadLetter.filter((v) => v.accountId === accountId).length;
|
|
4203
|
-
const sessionRoutesCount = Array.from(this.sessionRoutes.values()).filter(
|
|
4204
|
-
(v) => v.accountId === accountId,
|
|
4205
|
-
).length;
|
|
4206
|
-
return {
|
|
4207
|
-
pending,
|
|
4208
|
-
deadLetter,
|
|
4209
|
-
sessionRoutesCount,
|
|
4210
|
-
invalidOutboxSessionKeys: this.countInvalidOutboxSessionKeys(accountId),
|
|
4211
|
-
legacyAccountResidue: this.countLegacyAccountResidue(accountId),
|
|
4212
|
-
};
|
|
4213
|
-
}
|
|
4214
|
-
|
|
4215
|
-
private buildRuntimeEventCounters(accountId: string) {
|
|
4216
|
-
return {
|
|
4217
|
-
connectEvents: this.getCounter(this.connectEventsByAccount, accountId),
|
|
4218
|
-
inboundEvents: this.getCounter(this.inboundEventsByAccount, accountId),
|
|
4219
|
-
activityEvents: this.getCounter(this.activityEventsByAccount, accountId),
|
|
4220
|
-
ackEvents: this.getCounter(this.ackEventsByAccount, accountId),
|
|
4221
|
-
};
|
|
4222
|
-
}
|
|
4223
|
-
|
|
4224
4230
|
private computeRecommendedAckTimeoutReason(args: {
|
|
4225
4231
|
lateAckOkCount: number;
|
|
4226
4232
|
recentAckTimeoutCount: number;
|
|
@@ -4230,26 +4236,15 @@ class BncrBridgeRuntime {
|
|
|
4230
4236
|
recommendedAckTimeoutMs?: number;
|
|
4231
4237
|
nowMs?: number;
|
|
4232
4238
|
}) {
|
|
4233
|
-
|
|
4234
|
-
|
|
4235
|
-
|
|
4236
|
-
|
|
4237
|
-
|
|
4238
|
-
|
|
4239
|
-
|
|
4240
|
-
|
|
4241
|
-
|
|
4242
|
-
) {
|
|
4243
|
-
return 'late-ack-expired';
|
|
4244
|
-
}
|
|
4245
|
-
if (
|
|
4246
|
-
typeof args.adaptiveAckRecoveryOkCount === 'number' &&
|
|
4247
|
-
args.adaptiveAckRecoveryOkCount >= ADAPTIVE_ACK_TIMEOUT_RECOVERY_OK_THRESHOLD
|
|
4248
|
-
) {
|
|
4249
|
-
return 'recovered';
|
|
4250
|
-
}
|
|
4251
|
-
if (args.recommendedAckTimeoutMs === RECOMMENDED_ACK_TIMEOUT_MAX_MS) return 'capped-max';
|
|
4252
|
-
return 'late-ack-observed';
|
|
4239
|
+
return computeBncrRecommendedAckTimeoutReason({
|
|
4240
|
+
...args,
|
|
4241
|
+
nowMs: typeof args.nowMs === 'number' ? args.nowMs : now(),
|
|
4242
|
+
defaultAckTimeoutMs: PUSH_ACK_TIMEOUT_MS,
|
|
4243
|
+
minAckTimeoutMs: RECOMMENDED_ACK_TIMEOUT_MIN_MS,
|
|
4244
|
+
maxAckTimeoutMs: RECOMMENDED_ACK_TIMEOUT_MAX_MS,
|
|
4245
|
+
lateAckObservationTtlMs: ADAPTIVE_ACK_TIMEOUT_OBSERVATION_TTL_MS,
|
|
4246
|
+
recoveryOkThreshold: ADAPTIVE_ACK_TIMEOUT_RECOVERY_OK_THRESHOLD,
|
|
4247
|
+
});
|
|
4253
4248
|
}
|
|
4254
4249
|
|
|
4255
4250
|
private computeRecommendedAckTimeoutMs(args: {
|
|
@@ -4260,29 +4255,15 @@ class BncrBridgeRuntime {
|
|
|
4260
4255
|
adaptiveAckRecoveryOkCount?: number;
|
|
4261
4256
|
nowMs?: number;
|
|
4262
4257
|
}) {
|
|
4263
|
-
|
|
4264
|
-
|
|
4265
|
-
|
|
4266
|
-
|
|
4267
|
-
|
|
4268
|
-
|
|
4269
|
-
|
|
4270
|
-
|
|
4271
|
-
|
|
4272
|
-
if (
|
|
4273
|
-
args.lateAckOkCount <= 0 ||
|
|
4274
|
-
args.recentAckTimeoutCount <= 0 ||
|
|
4275
|
-
typeof args.lastLateAckPushLatencyMs !== 'number' ||
|
|
4276
|
-
lateAckExpired ||
|
|
4277
|
-
recovered
|
|
4278
|
-
) {
|
|
4279
|
-
return PUSH_ACK_TIMEOUT_MS;
|
|
4280
|
-
}
|
|
4281
|
-
const recommended = Math.ceil(args.lastLateAckPushLatencyMs * 1.25);
|
|
4282
|
-
return Math.min(
|
|
4283
|
-
RECOMMENDED_ACK_TIMEOUT_MAX_MS,
|
|
4284
|
-
Math.max(RECOMMENDED_ACK_TIMEOUT_MIN_MS, recommended),
|
|
4285
|
-
);
|
|
4258
|
+
return computeBncrRecommendedAckTimeoutMs({
|
|
4259
|
+
...args,
|
|
4260
|
+
nowMs: typeof args.nowMs === 'number' ? args.nowMs : now(),
|
|
4261
|
+
defaultAckTimeoutMs: PUSH_ACK_TIMEOUT_MS,
|
|
4262
|
+
minAckTimeoutMs: RECOMMENDED_ACK_TIMEOUT_MIN_MS,
|
|
4263
|
+
maxAckTimeoutMs: RECOMMENDED_ACK_TIMEOUT_MAX_MS,
|
|
4264
|
+
lateAckObservationTtlMs: ADAPTIVE_ACK_TIMEOUT_OBSERVATION_TTL_MS,
|
|
4265
|
+
recoveryOkThreshold: ADAPTIVE_ACK_TIMEOUT_RECOVERY_OK_THRESHOLD,
|
|
4266
|
+
});
|
|
4286
4267
|
}
|
|
4287
4268
|
|
|
4288
4269
|
private maybeLogAdaptiveAckTimeout(args: {
|
|
@@ -4412,44 +4393,41 @@ class BncrBridgeRuntime {
|
|
|
4412
4393
|
}
|
|
4413
4394
|
|
|
4414
4395
|
private buildRuntimeAckStrategy(ackObservability: Record<string, any>) {
|
|
4415
|
-
|
|
4416
|
-
|
|
4417
|
-
|
|
4418
|
-
|
|
4419
|
-
|
|
4420
|
-
currentMs,
|
|
4421
|
-
defaultMs,
|
|
4422
|
-
maxMs: RECOMMENDED_ACK_TIMEOUT_MAX_MS,
|
|
4423
|
-
reason,
|
|
4424
|
-
active: currentMs > defaultMs,
|
|
4425
|
-
lastLateAckAgeMs: ackObservability.lastLateAckAgeMs ?? null,
|
|
4426
|
-
lateAckObservationTtlMs: ackObservability.lateAckObservationTtlMs ?? null,
|
|
4427
|
-
recovered: ackObservability.adaptiveAckRecovered === true,
|
|
4428
|
-
};
|
|
4429
|
-
}
|
|
4430
|
-
|
|
4431
|
-
private buildRuntimeActivitySnapshot(accountId: string) {
|
|
4432
|
-
return {
|
|
4433
|
-
activeConnections: this.activeConnectionCount(accountId),
|
|
4434
|
-
lastSession: this.lastSessionByAccount.get(accountId) || null,
|
|
4435
|
-
lastActivityAt: this.lastActivityByAccount.get(accountId) || null,
|
|
4436
|
-
lastInboundAt: this.lastInboundByAccount.get(accountId) || null,
|
|
4437
|
-
lastOutboundAt: this.lastOutboundByAccount.get(accountId) || null,
|
|
4438
|
-
};
|
|
4396
|
+
return buildBncrRuntimeAckStrategy({
|
|
4397
|
+
ackObservability,
|
|
4398
|
+
defaultAckTimeoutMs: PUSH_ACK_TIMEOUT_MS,
|
|
4399
|
+
maxAckTimeoutMs: RECOMMENDED_ACK_TIMEOUT_MAX_MS,
|
|
4400
|
+
});
|
|
4439
4401
|
}
|
|
4440
4402
|
|
|
4441
4403
|
private buildRuntimeStatusInput(accountId: string, overrides: { running?: boolean } = {}) {
|
|
4442
4404
|
const acc = normalizeAccountId(accountId);
|
|
4443
|
-
|
|
4405
|
+
const snapshots = buildRuntimeStatusSnapshots({
|
|
4406
|
+
accountId: acc,
|
|
4407
|
+
outboxEntries: this.outbox.values(),
|
|
4408
|
+
deadLetterEntries: this.deadLetter,
|
|
4409
|
+
sessionRouteEntries: this.sessionRoutes.values(),
|
|
4410
|
+
countInvalidOutboxSessionKeys: (snapshotAccountId) =>
|
|
4411
|
+
this.countInvalidOutboxSessionKeys(snapshotAccountId),
|
|
4412
|
+
countLegacyAccountResidue: (snapshotAccountId) => this.countLegacyAccountResidue(snapshotAccountId),
|
|
4413
|
+
connectEventsByAccount: this.connectEventsByAccount,
|
|
4414
|
+
inboundEventsByAccount: this.inboundEventsByAccount,
|
|
4415
|
+
activityEventsByAccount: this.activityEventsByAccount,
|
|
4416
|
+
ackEventsByAccount: this.ackEventsByAccount,
|
|
4417
|
+
activeConnectionCount: (snapshotAccountId) => this.activeConnectionCount(snapshotAccountId),
|
|
4418
|
+
lastSessionByAccount: this.lastSessionByAccount,
|
|
4419
|
+
lastActivityByAccount: this.lastActivityByAccount,
|
|
4420
|
+
lastInboundByAccount: this.lastInboundByAccount,
|
|
4421
|
+
lastOutboundByAccount: this.lastOutboundByAccount,
|
|
4422
|
+
});
|
|
4423
|
+
return buildBncrRuntimeStatusInput({
|
|
4444
4424
|
accountId: acc,
|
|
4445
4425
|
connected: this.isOnline(acc),
|
|
4446
|
-
...
|
|
4447
|
-
...this.buildRuntimeEventCounters(acc),
|
|
4448
|
-
...this.buildRuntimeActivitySnapshot(acc),
|
|
4426
|
+
...snapshots,
|
|
4449
4427
|
startedAt: this.startedAt,
|
|
4450
4428
|
running: overrides.running,
|
|
4451
4429
|
channelRoot: path.join(process.cwd(), 'plugins', 'bncr'),
|
|
4452
|
-
};
|
|
4430
|
+
});
|
|
4453
4431
|
}
|
|
4454
4432
|
|
|
4455
4433
|
private buildStatusMeta(accountId: string) {
|
|
@@ -4825,34 +4803,6 @@ class BncrBridgeRuntime {
|
|
|
4825
4803
|
);
|
|
4826
4804
|
}
|
|
4827
4805
|
|
|
4828
|
-
private buildFileTransferInitPayload(args: {
|
|
4829
|
-
transferId: string;
|
|
4830
|
-
sessionKey: string;
|
|
4831
|
-
route: BncrRoute;
|
|
4832
|
-
fileName: string;
|
|
4833
|
-
mimeType?: string;
|
|
4834
|
-
fileSize: number;
|
|
4835
|
-
chunkSize: number;
|
|
4836
|
-
totalChunks: number;
|
|
4837
|
-
fileSha256: string;
|
|
4838
|
-
}) {
|
|
4839
|
-
return {
|
|
4840
|
-
transferId: args.transferId,
|
|
4841
|
-
direction: 'oc2bncr' as const,
|
|
4842
|
-
sessionKey: args.sessionKey,
|
|
4843
|
-
platform: args.route.platform,
|
|
4844
|
-
groupId: args.route.groupId,
|
|
4845
|
-
userId: args.route.userId,
|
|
4846
|
-
fileName: args.fileName,
|
|
4847
|
-
mimeType: args.mimeType,
|
|
4848
|
-
fileSize: args.fileSize,
|
|
4849
|
-
chunkSize: args.chunkSize,
|
|
4850
|
-
totalChunks: args.totalChunks,
|
|
4851
|
-
fileSha256: args.fileSha256,
|
|
4852
|
-
ts: now(),
|
|
4853
|
-
};
|
|
4854
|
-
}
|
|
4855
|
-
|
|
4856
4806
|
private buildInitialFileSendTransferState(args: {
|
|
4857
4807
|
transferId: string;
|
|
4858
4808
|
accountId: string;
|
|
@@ -5043,7 +4993,7 @@ class BncrBridgeRuntime {
|
|
|
5043
4993
|
|
|
5044
4994
|
ctx.broadcastToConnIds(
|
|
5045
4995
|
BNCR_FILE_INIT_EVENT,
|
|
5046
|
-
|
|
4996
|
+
buildFileTransferInitPayload({
|
|
5047
4997
|
transferId,
|
|
5048
4998
|
sessionKey: params.sessionKey,
|
|
5049
4999
|
route: params.route,
|
|
@@ -5053,6 +5003,7 @@ class BncrBridgeRuntime {
|
|
|
5053
5003
|
chunkSize,
|
|
5054
5004
|
totalChunks,
|
|
5055
5005
|
fileSha256,
|
|
5006
|
+
ts: now(),
|
|
5056
5007
|
}),
|
|
5057
5008
|
connIds,
|
|
5058
5009
|
);
|
|
@@ -5069,7 +5020,7 @@ class BncrBridgeRuntime {
|
|
|
5069
5020
|
for (let attempt = 1; attempt <= 3; attempt++) {
|
|
5070
5021
|
ctx.broadcastToConnIds(
|
|
5071
5022
|
BNCR_FILE_CHUNK_EVENT,
|
|
5072
|
-
{
|
|
5023
|
+
buildFileTransferChunkPayload({
|
|
5073
5024
|
transferId,
|
|
5074
5025
|
chunkIndex: idx,
|
|
5075
5026
|
offset: start,
|
|
@@ -5077,7 +5028,7 @@ class BncrBridgeRuntime {
|
|
|
5077
5028
|
chunkSha256,
|
|
5078
5029
|
base64: slice.toString('base64'),
|
|
5079
5030
|
ts: now(),
|
|
5080
|
-
},
|
|
5031
|
+
}),
|
|
5081
5032
|
connIds,
|
|
5082
5033
|
);
|
|
5083
5034
|
|
|
@@ -5125,11 +5076,11 @@ class BncrBridgeRuntime {
|
|
|
5125
5076
|
this.fileSendTransfers.set(transferId, st);
|
|
5126
5077
|
ctx.broadcastToConnIds(
|
|
5127
5078
|
BNCR_FILE_ABORT_EVENT,
|
|
5128
|
-
{
|
|
5079
|
+
buildFileTransferAbortPayload({
|
|
5129
5080
|
transferId,
|
|
5130
5081
|
reason: st.error,
|
|
5131
5082
|
ts: now(),
|
|
5132
|
-
},
|
|
5083
|
+
}),
|
|
5133
5084
|
connIds,
|
|
5134
5085
|
);
|
|
5135
5086
|
throw new Error(st.error);
|
|
@@ -5138,10 +5089,10 @@ class BncrBridgeRuntime {
|
|
|
5138
5089
|
|
|
5139
5090
|
ctx.broadcastToConnIds(
|
|
5140
5091
|
BNCR_FILE_COMPLETE_EVENT,
|
|
5141
|
-
{
|
|
5092
|
+
buildFileTransferCompletePayload({
|
|
5142
5093
|
transferId,
|
|
5143
5094
|
ts: now(),
|
|
5144
|
-
},
|
|
5095
|
+
}),
|
|
5145
5096
|
connIds,
|
|
5146
5097
|
);
|
|
5147
5098
|
|
|
@@ -5575,6 +5526,17 @@ class BncrBridgeRuntime {
|
|
|
5575
5526
|
respond(false, { error: 'transfer not found' });
|
|
5576
5527
|
return;
|
|
5577
5528
|
}
|
|
5529
|
+
if (st.status === 'completed') {
|
|
5530
|
+
respond(true, {
|
|
5531
|
+
ok: true,
|
|
5532
|
+
transferId,
|
|
5533
|
+
status: 'completed',
|
|
5534
|
+
path: st.completedPath,
|
|
5535
|
+
ignored: true,
|
|
5536
|
+
terminal: true,
|
|
5537
|
+
});
|
|
5538
|
+
return;
|
|
5539
|
+
}
|
|
5578
5540
|
if (chunkIndex >= st.totalChunks) {
|
|
5579
5541
|
respond(false, { error: `chunkIndex out of range index=${chunkIndex} total=${st.totalChunks}` });
|
|
5580
5542
|
return;
|
|
@@ -5778,6 +5740,17 @@ class BncrBridgeRuntime {
|
|
|
5778
5740
|
respond(true, { ok: true, transferId, message: 'not-found' });
|
|
5779
5741
|
return;
|
|
5780
5742
|
}
|
|
5743
|
+
if (st.status === 'completed') {
|
|
5744
|
+
respond(true, {
|
|
5745
|
+
ok: true,
|
|
5746
|
+
transferId,
|
|
5747
|
+
status: 'completed',
|
|
5748
|
+
path: st.completedPath,
|
|
5749
|
+
ignored: true,
|
|
5750
|
+
terminal: true,
|
|
5751
|
+
});
|
|
5752
|
+
return;
|
|
5753
|
+
}
|
|
5781
5754
|
|
|
5782
5755
|
const staleObserved = this.observeLease('file.abort', params ?? {});
|
|
5783
5756
|
if (staleObserved.stale) {
|
|
@@ -5874,6 +5847,30 @@ class BncrBridgeRuntime {
|
|
|
5874
5847
|
? 'file.abort'
|
|
5875
5848
|
: 'file.complete';
|
|
5876
5849
|
const staleObserved = this.observeLease(staleKind, params ?? {});
|
|
5850
|
+
if (st?.status === 'completed' || st?.status === 'aborted') {
|
|
5851
|
+
respond(
|
|
5852
|
+
true,
|
|
5853
|
+
staleObserved.stale
|
|
5854
|
+
? {
|
|
5855
|
+
ok: true,
|
|
5856
|
+
transferId,
|
|
5857
|
+
stage,
|
|
5858
|
+
state: st.status,
|
|
5859
|
+
stale: true,
|
|
5860
|
+
ignored: true,
|
|
5861
|
+
terminal: true,
|
|
5862
|
+
}
|
|
5863
|
+
: {
|
|
5864
|
+
ok: true,
|
|
5865
|
+
transferId,
|
|
5866
|
+
stage,
|
|
5867
|
+
state: st.status,
|
|
5868
|
+
ignored: true,
|
|
5869
|
+
terminal: true,
|
|
5870
|
+
},
|
|
5871
|
+
);
|
|
5872
|
+
return;
|
|
5873
|
+
}
|
|
5877
5874
|
if (staleObserved.stale) {
|
|
5878
5875
|
const sameConn = !!st?.ownerConnId && st.ownerConnId === connId;
|
|
5879
5876
|
const sameClient =
|
|
@@ -6130,126 +6127,11 @@ class BncrBridgeRuntime {
|
|
|
6130
6127
|
};
|
|
6131
6128
|
|
|
6132
6129
|
channelStartAccount = async (ctx: any) => {
|
|
6133
|
-
|
|
6134
|
-
this.clearChannelAccountWorker(accountId, 'start-replace');
|
|
6135
|
-
|
|
6136
|
-
const tick = () => {
|
|
6137
|
-
const previous = ctx.getStatus?.() || {};
|
|
6138
|
-
const onlineByConn = this.isOnline(accountId);
|
|
6139
|
-
const recentInboundReachable = this.hasRecentInboundReachability(accountId);
|
|
6140
|
-
const connected = onlineByConn || recentInboundReachable;
|
|
6141
|
-
const lastActAt =
|
|
6142
|
-
this.lastActivityByAccount.get(accountId) ||
|
|
6143
|
-
this.lastInboundByAccount.get(accountId) ||
|
|
6144
|
-
this.lastOutboundByAccount.get(accountId) ||
|
|
6145
|
-
previous?.lastEventAt ||
|
|
6146
|
-
null;
|
|
6147
|
-
const healthSig = JSON.stringify({
|
|
6148
|
-
bridge: this.bridgeId,
|
|
6149
|
-
accountId,
|
|
6150
|
-
connected,
|
|
6151
|
-
onlineByConn,
|
|
6152
|
-
recentInboundReachable,
|
|
6153
|
-
activeConnectionKey: this.activeConnectionByAccount.get(accountId) || null,
|
|
6154
|
-
activeConnections: Array.from(this.connections.values())
|
|
6155
|
-
.filter((c) => c.accountId === accountId)
|
|
6156
|
-
.map((c) => ({
|
|
6157
|
-
connId: c.connId,
|
|
6158
|
-
clientId: c.clientId,
|
|
6159
|
-
inboundOnly: c.inboundOnly === true,
|
|
6160
|
-
outboundReady: c.outboundReady === true,
|
|
6161
|
-
preferredForOutbound: c.preferredForOutbound === true,
|
|
6162
|
-
})),
|
|
6163
|
-
});
|
|
6164
|
-
const conns = Array.from(this.connections.values()).filter((c) => c.accountId === accountId).length;
|
|
6165
|
-
this.logInfoDedup(
|
|
6166
|
-
'health',
|
|
6167
|
-
`status-tick ${accountId}|changed|${connected ? 'linked' : 'configured'}|onlineByConn=${onlineByConn}|recentInboundReachable=${recentInboundReachable}|conns=${conns}`,
|
|
6168
|
-
{
|
|
6169
|
-
key: `health-status-tick:${accountId}`,
|
|
6170
|
-
sig: healthSig,
|
|
6171
|
-
},
|
|
6172
|
-
);
|
|
6173
|
-
this.logInfoDedup('health', `status-tick ${healthSig}`, {
|
|
6174
|
-
key: `health-status-tick-debug:${accountId}`,
|
|
6175
|
-
sig: healthSig,
|
|
6176
|
-
debugOnly: true,
|
|
6177
|
-
});
|
|
6178
|
-
|
|
6179
|
-
ctx.setStatus?.({
|
|
6180
|
-
...previous,
|
|
6181
|
-
accountId,
|
|
6182
|
-
running: true,
|
|
6183
|
-
connected,
|
|
6184
|
-
lastEventAt: lastActAt,
|
|
6185
|
-
// 状态映射:在线=linked,离线=configured
|
|
6186
|
-
mode: connected ? 'linked' : 'configured',
|
|
6187
|
-
lastError: previous?.lastError ?? null,
|
|
6188
|
-
meta: this.buildStatusMeta(accountId),
|
|
6189
|
-
});
|
|
6190
|
-
};
|
|
6191
|
-
|
|
6192
|
-
tick();
|
|
6193
|
-
const timer = setInterval(tick, 5_000);
|
|
6194
|
-
let worker!: ChannelAccountWorkerHandle;
|
|
6195
|
-
const done = new Promise<void>((resolve) => {
|
|
6196
|
-
let settled = false;
|
|
6197
|
-
const finish = (reason: string) => {
|
|
6198
|
-
if (settled) return;
|
|
6199
|
-
settled = true;
|
|
6200
|
-
const activeWorker = this.channelAccountWorkers.get(accountId);
|
|
6201
|
-
if (activeWorker === worker) {
|
|
6202
|
-
this.channelAccountWorkers.delete(accountId);
|
|
6203
|
-
}
|
|
6204
|
-
clearInterval(timer);
|
|
6205
|
-
worker.cleanupAbortListener?.();
|
|
6206
|
-
worker.cleanupAbortListener = undefined;
|
|
6207
|
-
this.logInfo(
|
|
6208
|
-
'health',
|
|
6209
|
-
`status-worker finished ${JSON.stringify({ bridge: this.bridgeId, accountId, reason })}`,
|
|
6210
|
-
{ debugOnly: true },
|
|
6211
|
-
);
|
|
6212
|
-
this.logInfo('health', `status-worker finished ${accountId}|${reason}`);
|
|
6213
|
-
resolve();
|
|
6214
|
-
};
|
|
6215
|
-
|
|
6216
|
-
worker = { timer, finish };
|
|
6217
|
-
this.channelAccountWorkers.set(accountId, worker);
|
|
6218
|
-
|
|
6219
|
-
const onAbort = () => finish('abort');
|
|
6220
|
-
const abortSignal = ctx.abortSignal;
|
|
6221
|
-
|
|
6222
|
-
if (abortSignal?.aborted) {
|
|
6223
|
-
onAbort();
|
|
6224
|
-
return;
|
|
6225
|
-
}
|
|
6226
|
-
|
|
6227
|
-
abortSignal?.addEventListener?.('abort', onAbort, { once: true });
|
|
6228
|
-
if (abortSignal?.removeEventListener) {
|
|
6229
|
-
worker.cleanupAbortListener = () => abortSignal.removeEventListener('abort', onAbort);
|
|
6230
|
-
}
|
|
6231
|
-
});
|
|
6232
|
-
await done;
|
|
6130
|
+
await startBncrStatusWorker(this.buildStatusWorkerRuntime(), ctx);
|
|
6233
6131
|
};
|
|
6234
6132
|
|
|
6235
6133
|
channelStopAccount = async (ctx: any) => {
|
|
6236
|
-
|
|
6237
|
-
const cleared = this.clearChannelAccountWorker(accountId, 'explicit-stop');
|
|
6238
|
-
const previous = ctx?.getStatus?.() || {};
|
|
6239
|
-
ctx?.setStatus?.({
|
|
6240
|
-
...previous,
|
|
6241
|
-
accountId,
|
|
6242
|
-
running: false,
|
|
6243
|
-
restartPending: false,
|
|
6244
|
-
lastStopAt: Date.now(),
|
|
6245
|
-
meta: this.buildStatusMeta(accountId),
|
|
6246
|
-
});
|
|
6247
|
-
this.logInfo(
|
|
6248
|
-
'health',
|
|
6249
|
-
`status-stop ${JSON.stringify({ bridge: this.bridgeId, accountId, cleared })}`,
|
|
6250
|
-
{ debugOnly: true },
|
|
6251
|
-
);
|
|
6252
|
-
this.logInfo('health', `status-stop ${accountId}|cleared=${cleared}`);
|
|
6134
|
+
await stopBncrStatusWorker(this.buildStatusWorkerRuntime(), ctx);
|
|
6253
6135
|
};
|
|
6254
6136
|
|
|
6255
6137
|
private logChannelSendEntry(args: {
|
|
@@ -6504,231 +6386,21 @@ export function createBncrChannelPlugin(getBridge: () => BncrBridgeRuntime) {
|
|
|
6504
6386
|
|
|
6505
6387
|
const plugin = {
|
|
6506
6388
|
id: CHANNEL_ID,
|
|
6507
|
-
meta:
|
|
6508
|
-
id: CHANNEL_ID,
|
|
6509
|
-
label: 'Bncr',
|
|
6510
|
-
selectionLabel: 'Bncr Client',
|
|
6511
|
-
docsPath: '/channels/bncr',
|
|
6512
|
-
blurb: 'Bncr Channel.',
|
|
6513
|
-
aliases: ['bncr'],
|
|
6514
|
-
},
|
|
6389
|
+
meta: BNCR_CHANNEL_META,
|
|
6515
6390
|
actions: messageActions,
|
|
6516
6391
|
message: {
|
|
6517
|
-
receive:
|
|
6518
|
-
|
|
6519
|
-
supportedAckPolicies: ['manual'] as const,
|
|
6520
|
-
},
|
|
6521
|
-
send: {
|
|
6522
|
-
text: async (ctx: any) => getBridge().channelMessageSendText(ctx),
|
|
6523
|
-
media: async (ctx: any) => getBridge().channelMessageSendMedia(ctx),
|
|
6524
|
-
payload: async (ctx: any) => getBridge().channelMessageSendPayload(ctx),
|
|
6525
|
-
},
|
|
6526
|
-
},
|
|
6527
|
-
capabilities: {
|
|
6528
|
-
chatTypes: ['direct'] as ChatType[],
|
|
6529
|
-
media: true,
|
|
6530
|
-
reply: true,
|
|
6531
|
-
nativeCommands: true,
|
|
6532
|
-
},
|
|
6533
|
-
messaging: {
|
|
6534
|
-
// 接收任意标签输入;不在 normalize 阶段做格式门槛,统一下沉到发送前验证。
|
|
6535
|
-
normalizeTarget: (raw: string) => {
|
|
6536
|
-
const input = asString(raw).trim();
|
|
6537
|
-
return input || undefined;
|
|
6538
|
-
},
|
|
6539
|
-
parseExplicitTarget: ({ raw, accountId, cfg }: any) => {
|
|
6540
|
-
const resolvedAccountId = normalizeAccountId(
|
|
6541
|
-
asString(accountId || BNCR_DEFAULT_ACCOUNT_ID),
|
|
6542
|
-
);
|
|
6543
|
-
const runtimeBridge = getBridge();
|
|
6544
|
-
const canonicalAgentId =
|
|
6545
|
-
runtimeBridge.canonicalAgentId ||
|
|
6546
|
-
runtimeBridge.ensureCanonicalAgentId({ cfg, accountId: resolvedAccountId });
|
|
6547
|
-
return parseExplicitTarget(asString(raw).trim(), { canonicalAgentId });
|
|
6548
|
-
},
|
|
6549
|
-
formatTargetDisplay: ({ target }: any) => {
|
|
6550
|
-
return formatTargetDisplay(target);
|
|
6551
|
-
},
|
|
6552
|
-
resolveSessionTarget: ({ id, accountId, cfg }: any) => {
|
|
6553
|
-
const raw = asString(id).trim();
|
|
6554
|
-
if (!raw) return undefined;
|
|
6555
|
-
const resolvedAccountId = normalizeAccountId(
|
|
6556
|
-
asString(accountId || BNCR_DEFAULT_ACCOUNT_ID),
|
|
6557
|
-
);
|
|
6558
|
-
const runtimeBridge = getBridge();
|
|
6559
|
-
const canonicalAgentId =
|
|
6560
|
-
runtimeBridge.canonicalAgentId ||
|
|
6561
|
-
runtimeBridge.ensureCanonicalAgentId({ cfg, accountId: resolvedAccountId });
|
|
6562
|
-
|
|
6563
|
-
let parsed = parseExplicitTarget(raw, { canonicalAgentId });
|
|
6564
|
-
if (!parsed) {
|
|
6565
|
-
const route = runtimeBridge.resolveRouteBySession(raw, resolvedAccountId);
|
|
6566
|
-
if (route) {
|
|
6567
|
-
parsed = parseExplicitTarget(formatDisplayScope(route), { canonicalAgentId });
|
|
6568
|
-
}
|
|
6569
|
-
}
|
|
6570
|
-
return parsed?.displayScope || undefined;
|
|
6571
|
-
},
|
|
6572
|
-
resolveOutboundSessionRoute: (params: any) => {
|
|
6573
|
-
const accountId = normalizeAccountId(
|
|
6574
|
-
asString(params?.accountId || BNCR_DEFAULT_ACCOUNT_ID),
|
|
6575
|
-
);
|
|
6576
|
-
const runtimeBridge = getBridge();
|
|
6577
|
-
const canonicalAgentId =
|
|
6578
|
-
runtimeBridge.canonicalAgentId ||
|
|
6579
|
-
runtimeBridge.ensureCanonicalAgentId({ cfg: params?.cfg, accountId });
|
|
6580
|
-
return resolveBncrOutboundSessionRoute({
|
|
6581
|
-
...params,
|
|
6582
|
-
canonicalAgentId,
|
|
6583
|
-
resolveRouteBySession: (raw: string, acc: string) =>
|
|
6584
|
-
runtimeBridge.resolveRouteBySession(raw, acc),
|
|
6585
|
-
});
|
|
6586
|
-
},
|
|
6587
|
-
targetResolver: {
|
|
6588
|
-
looksLikeId: (raw: string, normalized?: string) => {
|
|
6589
|
-
return looksLikeBncrExplicitTarget(asString(normalized || raw).trim());
|
|
6590
|
-
},
|
|
6591
|
-
resolveTarget: async ({ accountId, input, normalized }) => {
|
|
6592
|
-
const runtimeBridge = getBridge();
|
|
6593
|
-
const resolved = resolveBncrOutboundTarget({
|
|
6594
|
-
target: asString(normalized || input).trim(),
|
|
6595
|
-
accountId: normalizeAccountId(asString(accountId || BNCR_DEFAULT_ACCOUNT_ID)),
|
|
6596
|
-
resolveRouteBySession: (raw: string, acc: string) =>
|
|
6597
|
-
runtimeBridge.resolveRouteBySession(raw, acc),
|
|
6598
|
-
});
|
|
6599
|
-
if (!resolved) return null;
|
|
6600
|
-
return {
|
|
6601
|
-
to: resolved.displayScope,
|
|
6602
|
-
kind: resolved.kind,
|
|
6603
|
-
display: resolved.displayScope,
|
|
6604
|
-
source: 'normalized' as const,
|
|
6605
|
-
};
|
|
6606
|
-
},
|
|
6607
|
-
hint: 'Standard to=Bncr:<platform>:<group>:<user> or Bncr:<platform>:<user>; sessionKey keeps existing strict/legacy compatibility, canonical sessionKey=agent:<agentId>:bncr:direct:<hex>',
|
|
6608
|
-
},
|
|
6392
|
+
receive: BNCR_MESSAGE_RECEIVE_POLICY,
|
|
6393
|
+
send: createBncrMessageSend(getBridge),
|
|
6609
6394
|
},
|
|
6395
|
+
capabilities: BNCR_CHANNEL_CAPABILITIES,
|
|
6396
|
+
messaging: createBncrMessagingSurface(getBridge),
|
|
6610
6397
|
configSchema: BncrConfigSchema,
|
|
6611
|
-
config:
|
|
6612
|
-
|
|
6613
|
-
|
|
6614
|
-
|
|
6615
|
-
|
|
6616
|
-
|
|
6617
|
-
sectionKey: CHANNEL_ID,
|
|
6618
|
-
accountId,
|
|
6619
|
-
enabled,
|
|
6620
|
-
allowTopLevel: true,
|
|
6621
|
-
}),
|
|
6622
|
-
isEnabled: (account: any, cfg: any) => {
|
|
6623
|
-
const policy = resolveBncrChannelPolicy(cfg?.channels?.[CHANNEL_ID] || {});
|
|
6624
|
-
return policy.enabled !== false && account?.enabled !== false;
|
|
6625
|
-
},
|
|
6626
|
-
isConfigured: () => true,
|
|
6627
|
-
describeAccount: (account: any) => {
|
|
6628
|
-
const displayName = resolveDefaultDisplayName(account?.name, account?.accountId);
|
|
6629
|
-
return {
|
|
6630
|
-
accountId: account.accountId,
|
|
6631
|
-
name: displayName,
|
|
6632
|
-
enabled: account.enabled !== false,
|
|
6633
|
-
configured: true,
|
|
6634
|
-
};
|
|
6635
|
-
},
|
|
6636
|
-
},
|
|
6637
|
-
setup: {
|
|
6638
|
-
applyAccountName: ({ cfg, accountId, name }: any) =>
|
|
6639
|
-
applyOpenClawAccountNameToChannelSection({
|
|
6640
|
-
cfg,
|
|
6641
|
-
channelKey: CHANNEL_ID,
|
|
6642
|
-
accountId,
|
|
6643
|
-
name,
|
|
6644
|
-
alwaysUseAccounts: true,
|
|
6645
|
-
}),
|
|
6646
|
-
applyAccountConfig: ({ cfg, accountId }: any) => {
|
|
6647
|
-
const next = { ...(cfg || {}) } as any;
|
|
6648
|
-
next.channels = next.channels || {};
|
|
6649
|
-
next.channels[CHANNEL_ID] = next.channels[CHANNEL_ID] || {};
|
|
6650
|
-
next.channels[CHANNEL_ID].accounts = next.channels[CHANNEL_ID].accounts || {};
|
|
6651
|
-
next.channels[CHANNEL_ID].accounts[accountId] = {
|
|
6652
|
-
...(next.channels[CHANNEL_ID].accounts[accountId] || {}),
|
|
6653
|
-
enabled: true,
|
|
6654
|
-
};
|
|
6655
|
-
return next;
|
|
6656
|
-
},
|
|
6657
|
-
},
|
|
6658
|
-
outbound: {
|
|
6659
|
-
deliveryMode: 'gateway' as const,
|
|
6660
|
-
sendText: async (ctx: any) => getBridge().channelSendText(ctx),
|
|
6661
|
-
sendMedia: async (ctx: any) => getBridge().channelSendMedia(ctx),
|
|
6662
|
-
replyAction: async (ctx: any) =>
|
|
6663
|
-
sendBncrReplyAction({
|
|
6664
|
-
accountId: normalizeAccountId(ctx?.accountId),
|
|
6665
|
-
to: asString(ctx?.to || '').trim(),
|
|
6666
|
-
text: asString(ctx?.text || ''),
|
|
6667
|
-
replyToMessageId:
|
|
6668
|
-
asString(ctx?.replyToId || ctx?.replyToMessageId || '').trim() || undefined,
|
|
6669
|
-
sendText: async ({ accountId, to, text }) =>
|
|
6670
|
-
getBridge().channelSendText({ accountId, to, text }),
|
|
6671
|
-
}),
|
|
6672
|
-
deleteAction: async (ctx: any) =>
|
|
6673
|
-
deleteBncrMessageAction({
|
|
6674
|
-
accountId: normalizeAccountId(ctx?.accountId),
|
|
6675
|
-
targetMessageId: asString(ctx?.messageId || ctx?.targetMessageId || '').trim(),
|
|
6676
|
-
}),
|
|
6677
|
-
reactAction: async (ctx: any) =>
|
|
6678
|
-
reactBncrMessageAction({
|
|
6679
|
-
accountId: normalizeAccountId(ctx?.accountId),
|
|
6680
|
-
targetMessageId: asString(ctx?.messageId || ctx?.targetMessageId || '').trim(),
|
|
6681
|
-
emoji: asString(ctx?.emoji || '').trim(),
|
|
6682
|
-
}),
|
|
6683
|
-
editAction: async (ctx: any) =>
|
|
6684
|
-
editBncrMessageAction({
|
|
6685
|
-
accountId: normalizeAccountId(ctx?.accountId),
|
|
6686
|
-
targetMessageId: asString(ctx?.messageId || ctx?.targetMessageId || '').trim(),
|
|
6687
|
-
text: asString(ctx?.text || ''),
|
|
6688
|
-
}),
|
|
6689
|
-
},
|
|
6690
|
-
status: {
|
|
6691
|
-
defaultRuntime: createOpenClawDefaultChannelRuntimeState(BNCR_DEFAULT_ACCOUNT_ID, {
|
|
6692
|
-
mode: 'ws-offline',
|
|
6693
|
-
}),
|
|
6694
|
-
buildChannelSummary: async ({ defaultAccountId }: any) => {
|
|
6695
|
-
return getBridge().getChannelSummary(defaultAccountId || BNCR_DEFAULT_ACCOUNT_ID);
|
|
6696
|
-
},
|
|
6697
|
-
buildAccountSnapshot: async ({ account, runtime }: any) => {
|
|
6698
|
-
const runtimeBridge = getBridge();
|
|
6699
|
-
const rt = runtime || runtimeBridge.getAccountRuntimeSnapshot(account?.accountId);
|
|
6700
|
-
return buildAccountStatusSnapshot({
|
|
6701
|
-
account,
|
|
6702
|
-
runtime: rt,
|
|
6703
|
-
healthSummary: runtimeBridge.getStatusHeadline(account?.accountId),
|
|
6704
|
-
// default 名不可隐藏时,统一展示稳定默认值
|
|
6705
|
-
displayName: resolveDefaultDisplayName(account?.name, account?.accountId),
|
|
6706
|
-
});
|
|
6707
|
-
},
|
|
6708
|
-
resolveAccountState: ({ enabled, configured, account, cfg, runtime }: any) => {
|
|
6709
|
-
if (!enabled) return 'disabled';
|
|
6710
|
-
const resolved = resolveAccount(cfg, account?.accountId);
|
|
6711
|
-
if (!(resolved.enabled && configured)) return 'not configured';
|
|
6712
|
-
const rt = runtime || getBridge().getAccountRuntimeSnapshot(account?.accountId);
|
|
6713
|
-
return rt?.connected ? 'linked' : 'configured';
|
|
6714
|
-
},
|
|
6715
|
-
},
|
|
6716
|
-
gatewayMethods: [
|
|
6717
|
-
'bncr.connect',
|
|
6718
|
-
'bncr.inbound',
|
|
6719
|
-
'bncr.activity',
|
|
6720
|
-
'bncr.ack',
|
|
6721
|
-
'bncr.diagnostics',
|
|
6722
|
-
'bncr.file.init',
|
|
6723
|
-
'bncr.file.chunk',
|
|
6724
|
-
'bncr.file.complete',
|
|
6725
|
-
'bncr.file.abort',
|
|
6726
|
-
'bncr.file.ack',
|
|
6727
|
-
],
|
|
6728
|
-
gateway: {
|
|
6729
|
-
startAccount: async (ctx: any) => getBridge().channelStartAccount(ctx),
|
|
6730
|
-
stopAccount: async (ctx: any) => getBridge().channelStopAccount(ctx),
|
|
6731
|
-
},
|
|
6398
|
+
config: BNCR_CONFIG_SURFACE,
|
|
6399
|
+
setup: BNCR_SETUP_SURFACE,
|
|
6400
|
+
outbound: createBncrOutboundRuntime(getBridge),
|
|
6401
|
+
status: createBncrStatusSurface(getBridge),
|
|
6402
|
+
gatewayMethods: BNCR_GATEWAY_METHODS,
|
|
6403
|
+
gateway: createBncrGatewayRuntime(getBridge),
|
|
6732
6404
|
};
|
|
6733
6405
|
|
|
6734
6406
|
return plugin;
|