@xmoxmo/bncr 0.3.5 → 0.3.7

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.
Files changed (164) hide show
  1. package/README.md +5 -0
  2. package/dist/index.js +28 -5
  3. package/index.ts +55 -721
  4. package/package.json +8 -4
  5. package/scripts/check-pack.mjs +93 -18
  6. package/scripts/check-register-drift.mjs +35 -13
  7. package/scripts/selfcheck.mjs +80 -11
  8. package/src/bootstrap/channel-plugin-runtime.ts +81 -0
  9. package/src/bootstrap/cli.ts +97 -0
  10. package/src/bootstrap/register-runtime-gateway.ts +129 -0
  11. package/src/bootstrap/register-runtime-helpers.ts +140 -0
  12. package/src/bootstrap/register-runtime-singleton.ts +137 -0
  13. package/src/bootstrap/register-runtime.ts +201 -0
  14. package/src/bootstrap/runtime-discovery.ts +187 -0
  15. package/src/bootstrap/runtime-loader.ts +54 -0
  16. package/src/channel.ts +1590 -4967
  17. package/src/core/accounts.ts +23 -4
  18. package/src/core/dead-letter-diagnostics.ts +37 -5
  19. package/src/core/diagnostics.ts +31 -15
  20. package/src/core/downlink-health.ts +3 -11
  21. package/src/core/extended-diagnostics.ts +78 -36
  22. package/src/core/file-transfer-payloads.ts +1 -1
  23. package/src/core/logging.ts +1 -0
  24. package/src/core/outbox-enqueue.ts +13 -2
  25. package/src/core/outbox-entry-builders.ts +2 -0
  26. package/src/core/outbox-summary.ts +75 -3
  27. package/src/core/permissions.ts +15 -2
  28. package/src/core/persisted-outbox-entry.ts +21 -6
  29. package/src/core/policy.ts +45 -4
  30. package/src/core/probe.ts +3 -15
  31. package/src/core/register-trace.ts +3 -3
  32. package/src/core/status.ts +43 -4
  33. package/src/core/targets.ts +216 -205
  34. package/src/core/types.ts +221 -0
  35. package/src/core/value-sanitize.ts +29 -0
  36. package/src/messaging/inbound/commands.ts +147 -172
  37. package/src/messaging/inbound/context-facts.ts +4 -2
  38. package/src/messaging/inbound/contracts.ts +70 -0
  39. package/src/messaging/inbound/dispatch-prep.ts +303 -0
  40. package/src/messaging/inbound/dispatch.ts +49 -462
  41. package/src/messaging/inbound/gate.ts +18 -5
  42. package/src/messaging/inbound/last-route.ts +10 -4
  43. package/src/messaging/inbound/media-url-download.ts +109 -0
  44. package/src/messaging/inbound/native-command-runtime.ts +225 -0
  45. package/src/messaging/inbound/parse.ts +2 -1
  46. package/src/messaging/inbound/remote-media.ts +49 -0
  47. package/src/messaging/inbound/reply-config.ts +16 -4
  48. package/src/messaging/inbound/reply-dispatch.ts +162 -0
  49. package/src/messaging/inbound/runtime-compat.ts +31 -10
  50. package/src/messaging/inbound/session-label.ts +15 -7
  51. package/src/messaging/inbound/turn-context.ts +131 -0
  52. package/src/messaging/outbound/actions.ts +24 -10
  53. package/src/messaging/outbound/diagnostics-debug-builders.ts +365 -0
  54. package/src/messaging/outbound/diagnostics.ts +31 -355
  55. package/src/messaging/outbound/durable-message-adapter.ts +20 -16
  56. package/src/messaging/outbound/durable-queue-adapter.ts +20 -7
  57. package/src/messaging/outbound/media.ts +24 -13
  58. package/src/messaging/outbound/reply-enqueue-media.ts +181 -0
  59. package/src/messaging/outbound/reply-enqueue.ts +57 -134
  60. package/src/messaging/outbound/send-params.ts +3 -0
  61. package/src/messaging/outbound/send.ts +19 -10
  62. package/src/messaging/outbound/session-route.ts +18 -3
  63. package/src/openclaw/channel-runtime-contracts.ts +76 -0
  64. package/src/openclaw/config-runtime.ts +13 -7
  65. package/src/openclaw/inbound-session-runtime.ts +7 -3
  66. package/src/openclaw/ingress-runtime.ts +17 -27
  67. package/src/openclaw/reply-runtime.ts +54 -59
  68. package/src/openclaw/routing-runtime.ts +35 -18
  69. package/src/openclaw/runtime-surface.ts +156 -12
  70. package/src/openclaw/sdk-helpers.ts +8 -1
  71. package/src/openclaw/session-route-runtime.ts +12 -12
  72. package/src/plugin/ack-outbox-runtime-group.ts +264 -0
  73. package/src/plugin/bridge-ack-facade.ts +137 -0
  74. package/src/plugin/bridge-connection-facade.ts +111 -0
  75. package/src/plugin/bridge-diagnostics-facade.ts +23 -0
  76. package/src/plugin/bridge-drain-facade.ts +98 -0
  77. package/src/plugin/bridge-extended-diagnostics-facade.ts +149 -0
  78. package/src/plugin/bridge-file-transfer-push-facade.ts +140 -0
  79. package/src/plugin/bridge-lifecycle.ts +156 -0
  80. package/src/plugin/bridge-media-facade.ts +241 -0
  81. package/src/plugin/bridge-outbox-facade.ts +182 -0
  82. package/src/plugin/bridge-runtime-helpers.ts +266 -0
  83. package/src/plugin/bridge-runtime-snapshots.ts +104 -0
  84. package/src/plugin/bridge-runtime-surface-facade.ts +8 -0
  85. package/src/plugin/bridge-status-facade.ts +76 -0
  86. package/src/plugin/bridge-status-worker-facade.ts +72 -0
  87. package/src/plugin/bridge-support-runtime.ts +137 -0
  88. package/src/plugin/bridge-surface-handlers-group.ts +242 -0
  89. package/src/plugin/bridge-surface-helpers.ts +28 -0
  90. package/src/plugin/capabilities.ts +1 -3
  91. package/src/plugin/channel-components.ts +289 -0
  92. package/src/plugin/channel-inbound-helpers.ts +149 -0
  93. package/src/plugin/channel-plugin-bridge-group.ts +129 -0
  94. package/src/plugin/channel-plugin-surface-group.ts +202 -0
  95. package/src/plugin/channel-runtime-builders-delivery.ts +513 -0
  96. package/src/plugin/channel-runtime-builders-status.ts +331 -0
  97. package/src/plugin/channel-runtime-builders.ts +25 -0
  98. package/src/plugin/channel-runtime-constants.ts +40 -0
  99. package/src/plugin/channel-runtime-types.ts +146 -0
  100. package/src/plugin/channel-send-runtime-group.ts +37 -0
  101. package/src/plugin/channel-send.ts +226 -0
  102. package/src/plugin/channel-utils.ts +102 -0
  103. package/src/plugin/config.ts +24 -3
  104. package/src/plugin/connection-handlers-helpers.ts +254 -0
  105. package/src/plugin/connection-handlers.ts +440 -0
  106. package/src/plugin/connection-state-helpers.ts +159 -0
  107. package/src/plugin/connection-state-runtime-group.ts +51 -0
  108. package/src/plugin/connection-state.ts +527 -0
  109. package/src/plugin/diagnostics-handlers.ts +211 -0
  110. package/src/plugin/error-message.ts +15 -0
  111. package/src/plugin/file-ack-runtime.ts +284 -0
  112. package/src/plugin/file-inbound-abort.ts +112 -0
  113. package/src/plugin/file-inbound-chunk.ts +146 -0
  114. package/src/plugin/file-inbound-complete.ts +153 -0
  115. package/src/plugin/file-inbound-handlers.ts +19 -0
  116. package/src/plugin/file-inbound-init.ts +122 -0
  117. package/src/plugin/file-inbound-runtime.ts +51 -0
  118. package/src/plugin/file-inbound-state.ts +62 -0
  119. package/src/plugin/file-transfer-logs.ts +227 -0
  120. package/src/plugin/file-transfer-orchestrator-chunk.ts +135 -0
  121. package/src/plugin/file-transfer-orchestrator.ts +304 -0
  122. package/src/plugin/file-transfer-runtime-group.ts +102 -0
  123. package/src/plugin/file-transfer-send.ts +89 -0
  124. package/src/plugin/file-transfer-setup.ts +206 -0
  125. package/src/plugin/gateway-event-context.ts +41 -0
  126. package/src/plugin/gateway-runtime.ts +14 -4
  127. package/src/plugin/inbound-acceptance.ts +107 -0
  128. package/src/plugin/inbound-handlers.ts +248 -0
  129. package/src/plugin/inbound-surface-handlers-group.ts +152 -0
  130. package/src/plugin/media-dedupe-runtime.ts +90 -0
  131. package/src/plugin/media-orchestrators-runtime-group.ts +316 -0
  132. package/src/plugin/message-ack-runtime.ts +284 -0
  133. package/src/plugin/message-send.ts +16 -6
  134. package/src/plugin/messaging.ts +98 -36
  135. package/src/plugin/outbound.ts +50 -8
  136. package/src/plugin/outbox-ack-logs.ts +136 -0
  137. package/src/plugin/outbox-ack-outcome.ts +128 -0
  138. package/src/plugin/outbox-drain-ack.ts +145 -0
  139. package/src/plugin/outbox-drain-failure.ts +84 -0
  140. package/src/plugin/outbox-drain-loop.ts +554 -0
  141. package/src/plugin/outbox-drain-post-push.ts +159 -0
  142. package/src/plugin/outbox-drain-runtime.ts +141 -0
  143. package/src/plugin/outbox-drain-schedule.ts +116 -0
  144. package/src/plugin/outbox-file-push-flow.ts +69 -0
  145. package/src/plugin/outbox-push-route-runtime-group.ts +81 -0
  146. package/src/plugin/outbox-push.ts +267 -0
  147. package/src/plugin/outbox-route.ts +181 -0
  148. package/src/plugin/outbox-text-push-flow.ts +90 -0
  149. package/src/plugin/runtime-diagnostics-assembler.ts +183 -0
  150. package/src/plugin/runtime-diagnostics-helpers.ts +302 -0
  151. package/src/plugin/runtime-diagnostics-payload-builders.ts +171 -0
  152. package/src/plugin/runtime-diagnostics-snapshot.ts +31 -0
  153. package/src/plugin/setup.ts +33 -6
  154. package/src/plugin/state-store.ts +249 -0
  155. package/src/plugin/state-transient-runtime-group.ts +105 -0
  156. package/src/plugin/status-runtime.ts +251 -0
  157. package/src/plugin/status.ts +33 -7
  158. package/src/plugin/target-runtime.ts +141 -0
  159. package/src/plugin/target-status-runtime-group.ts +130 -0
  160. package/src/plugin/transient-state-runtime.ts +82 -0
  161. package/src/runtime/outbound-ack-timeout.ts +5 -3
  162. package/src/runtime/outbound-flags.ts +24 -8
  163. package/src/runtime/status-snapshots.ts +36 -7
  164. package/src/runtime/status-worker.ts +34 -4
@@ -0,0 +1,102 @@
1
+ import {
2
+ createBncrFileAckRuntime,
3
+ type FileAckPayloadState,
4
+ type FileAckWaiter,
5
+ } from './file-ack-runtime.ts';
6
+ import { createBncrFileTransferLogs } from './file-transfer-logs.ts';
7
+ import { createBncrFileTransferSend } from './file-transfer-send.ts';
8
+ import {
9
+ type BncrFileTransferRouteDiagnostics,
10
+ createBncrFileTransferSetup,
11
+ } from './file-transfer-setup.ts';
12
+
13
+ export function createBncrFileTransferRuntimeGroup(runtime: {
14
+ bridgeId: string;
15
+ now: () => number;
16
+ asString: (value: unknown, fallback?: string) => string;
17
+ clampFiniteNumber: (value: unknown, fallback: number, min?: number, max?: number) => number;
18
+ fileAckTimeoutMs: number;
19
+ maxEarlyFileAcks: number;
20
+ fileAckWaiters: Map<string, FileAckWaiter>;
21
+ earlyFileAcks: Map<string, FileAckPayloadState>;
22
+ getFileAckOwnerInfo: (transferId: string) => Record<string, unknown>;
23
+ fileForceChunk: boolean;
24
+ fileInlineThreshold: number;
25
+ normalizeAccountId: (accountId: string) => string;
26
+ loadOutboundTransferMedia: (args: {
27
+ mediaUrl: string;
28
+ mediaLocalRoots?: readonly string[];
29
+ }) => Promise<{ loaded: { buffer: Buffer }; size: number; mimeType?: string; fileName: string }>;
30
+ resolveOutboxPushOwner: (accountId: string) => { connId?: string; clientId?: string } | null;
31
+ hasRecentInboundReachability: (accountId: string) => boolean;
32
+ buildTransferRouteDiagnostics: (args: {
33
+ accountId: string;
34
+ recentInboundReachable: boolean;
35
+ }) => BncrFileTransferRouteDiagnostics;
36
+ selectTransferConnIds: (args: {
37
+ directConnIds: Set<string>;
38
+ recentConnIds: Set<string>;
39
+ recentInboundReachable: boolean;
40
+ }) => Set<string>;
41
+ broadcastToConnIds: (event: string, payload: unknown, connIds: ReadonlySet<string>) => void;
42
+ chunkEvent: string;
43
+ completeEvent: string;
44
+ logInfo: (scope: string | undefined, message: string, options?: { debugOnly?: boolean }) => void;
45
+ logWarn: (scope: string | undefined, message: string, options?: { debugOnly?: boolean }) => void;
46
+ }) {
47
+ const fileAckRuntime = createBncrFileAckRuntime({
48
+ bridgeId: runtime.bridgeId,
49
+ now: runtime.now,
50
+ asString: runtime.asString,
51
+ clampFiniteNumber: (value, fallback, min, max) =>
52
+ runtime.clampFiniteNumber(value, fallback, min ?? fallback, max ?? fallback),
53
+ fileAckTimeoutMs: runtime.fileAckTimeoutMs,
54
+ maxEarlyFileAcks: runtime.maxEarlyFileAcks,
55
+ fileAckWaiters: runtime.fileAckWaiters,
56
+ earlyFileAcks: runtime.earlyFileAcks,
57
+ getFileAckOwnerInfo: runtime.getFileAckOwnerInfo,
58
+ logInfo: runtime.logInfo,
59
+ logWarn: runtime.logWarn,
60
+ });
61
+
62
+ const fileTransferLogs = createBncrFileTransferLogs({
63
+ bridgeId: runtime.bridgeId,
64
+ now: runtime.now,
65
+ logInfo: (scope, message, options) => runtime.logInfo(scope, message, options),
66
+ logWarn: (scope, message, options) => runtime.logWarn(scope, message, options),
67
+ });
68
+
69
+ const fileTransferSetup = createBncrFileTransferSetup({
70
+ fileForceChunk: runtime.fileForceChunk,
71
+ fileInlineThreshold: runtime.fileInlineThreshold,
72
+ normalizeAccountId: runtime.normalizeAccountId,
73
+ loadOutboundTransferMedia: runtime.loadOutboundTransferMedia,
74
+ resolveOutboxPushOwner: runtime.resolveOutboxPushOwner,
75
+ hasRecentInboundReachability: runtime.hasRecentInboundReachability,
76
+ buildTransferRouteDiagnostics: runtime.buildTransferRouteDiagnostics,
77
+ selectTransferConnIds: runtime.selectTransferConnIds,
78
+ logFileChunkDiag: fileTransferLogs.logFileChunkDiag,
79
+ logFileTransferStart: fileTransferLogs.logFileTransferStart,
80
+ buildInitialFileSendTransferState: (args) =>
81
+ fileTransferLogs.buildInitialFileSendTransferState({
82
+ ...args,
83
+ normalizeAccountId: runtime.normalizeAccountId,
84
+ }),
85
+ });
86
+
87
+ const fileTransferSend = createBncrFileTransferSend({
88
+ now: runtime.now,
89
+ broadcastToConnIds: runtime.broadcastToConnIds,
90
+ chunkEvent: runtime.chunkEvent,
91
+ completeEvent: runtime.completeEvent,
92
+ logFileTransferChunkSend: fileTransferLogs.logFileTransferChunkSend,
93
+ logFileTransferCompleteSend: fileTransferLogs.logFileTransferCompleteSend,
94
+ });
95
+
96
+ return {
97
+ fileAckRuntime,
98
+ fileTransferLogs,
99
+ fileTransferSetup,
100
+ fileTransferSend,
101
+ };
102
+ }
@@ -0,0 +1,89 @@
1
+ import {
2
+ buildFileTransferChunkPayload,
3
+ buildFileTransferCompletePayload,
4
+ } from '../core/file-transfer-payloads.ts';
5
+
6
+ export type BncrFileTransferSendRuntime = {
7
+ now: () => number;
8
+ broadcastToConnIds: (event: string, payload: unknown, connIds: ReadonlySet<string>) => void;
9
+ chunkEvent: string;
10
+ completeEvent: string;
11
+ logFileTransferChunkSend: (args: {
12
+ transferId: string;
13
+ accountId: string;
14
+ chunkIndex: number;
15
+ attempt: number;
16
+ offset: number;
17
+ size: number;
18
+ connIds: ReadonlySet<string>;
19
+ }) => void;
20
+ logFileTransferCompleteSend: (args: {
21
+ transferId: string;
22
+ accountId: string;
23
+ connIds: ReadonlySet<string>;
24
+ }) => void;
25
+ };
26
+
27
+ export function createBncrFileTransferSend(runtime: BncrFileTransferSendRuntime) {
28
+ const sendChunk = (args: {
29
+ transferId: string;
30
+ accountId: string;
31
+ chunkIndex: number;
32
+ attempt: number;
33
+ offset: number;
34
+ size: number;
35
+ chunkSha256: string;
36
+ base64: string;
37
+ connIds: ReadonlySet<string>;
38
+ }) => {
39
+ runtime.broadcastToConnIds(
40
+ runtime.chunkEvent,
41
+ buildFileTransferChunkPayload({
42
+ transferId: args.transferId,
43
+ chunkIndex: args.chunkIndex,
44
+ offset: args.offset,
45
+ size: args.size,
46
+ chunkSha256: args.chunkSha256,
47
+ base64: args.base64,
48
+ ts: runtime.now(),
49
+ }),
50
+ args.connIds,
51
+ );
52
+
53
+ runtime.logFileTransferChunkSend({
54
+ transferId: args.transferId,
55
+ accountId: args.accountId,
56
+ chunkIndex: args.chunkIndex,
57
+ attempt: args.attempt,
58
+ offset: args.offset,
59
+ size: args.size,
60
+ connIds: args.connIds,
61
+ });
62
+ };
63
+
64
+ const sendComplete = (args: {
65
+ transferId: string;
66
+ accountId: string;
67
+ connIds: ReadonlySet<string>;
68
+ }) => {
69
+ runtime.broadcastToConnIds(
70
+ runtime.completeEvent,
71
+ buildFileTransferCompletePayload({
72
+ transferId: args.transferId,
73
+ ts: runtime.now(),
74
+ }),
75
+ args.connIds,
76
+ );
77
+
78
+ runtime.logFileTransferCompleteSend({
79
+ transferId: args.transferId,
80
+ accountId: args.accountId,
81
+ connIds: args.connIds,
82
+ });
83
+ };
84
+
85
+ return {
86
+ sendChunk,
87
+ sendComplete,
88
+ };
89
+ }
@@ -0,0 +1,206 @@
1
+ import { createHash, randomUUID } from 'node:crypto';
2
+ import type { BncrRoute } from '../core/types.ts';
3
+
4
+ export type BncrFileTransferRouteDiagnostics = {
5
+ activeConnectionKey: string | null;
6
+ directConnIds: Set<string>;
7
+ recentConnIds: Set<string>;
8
+ accountConnections: Array<{
9
+ connId: string;
10
+ clientId?: string;
11
+ connectedAt: number;
12
+ lastSeenAt: number;
13
+ }>;
14
+ };
15
+
16
+ export type BncrFileTransferSetupRuntime = {
17
+ fileForceChunk: boolean;
18
+ fileInlineThreshold: number;
19
+ normalizeAccountId: (accountId: string) => string;
20
+ loadOutboundTransferMedia: (args: {
21
+ mediaUrl: string;
22
+ mediaLocalRoots?: readonly string[];
23
+ }) => Promise<{ loaded: { buffer: Buffer }; size: number; mimeType?: string; fileName: string }>;
24
+ resolveOutboxPushOwner: (accountId: string) => { connId?: string; clientId?: string } | null;
25
+ hasRecentInboundReachability: (accountId: string) => boolean;
26
+ buildTransferRouteDiagnostics: (args: {
27
+ accountId: string;
28
+ recentInboundReachable: boolean;
29
+ }) => BncrFileTransferRouteDiagnostics;
30
+ selectTransferConnIds: (args: {
31
+ directConnIds: Set<string>;
32
+ recentConnIds: Set<string>;
33
+ recentInboundReachable: boolean;
34
+ }) => Set<string>;
35
+ logFileChunkDiag: (args: {
36
+ accountId: string;
37
+ sessionKey: string;
38
+ mediaUrl: string;
39
+ hasGatewayContext: boolean;
40
+ activeConnectionKey: string | null;
41
+ ownerConnId?: string;
42
+ ownerClientId?: string;
43
+ directConnIds: Iterable<string>;
44
+ recentInboundReachable: boolean;
45
+ recentConnIds: Iterable<string>;
46
+ accountConnections: Array<{
47
+ connId: string;
48
+ clientId?: string;
49
+ connectedAt: number;
50
+ lastSeenAt: number;
51
+ }>;
52
+ }) => void;
53
+ logFileTransferStart: (args: {
54
+ transferId: string;
55
+ accountId: string;
56
+ sessionKey: string;
57
+ mediaUrl: string;
58
+ fileName: string;
59
+ mimeType?: string;
60
+ fileSize: number;
61
+ chunkSize: number;
62
+ totalChunks: number;
63
+ connIds: ReadonlySet<string>;
64
+ ownerConnId?: string;
65
+ ownerClientId?: string;
66
+ }) => void;
67
+ buildInitialFileSendTransferState: (args: {
68
+ transferId: string;
69
+ accountId: string;
70
+ sessionKey: string;
71
+ route: BncrRoute;
72
+ fileName: string;
73
+ mimeType?: string;
74
+ fileSize: number;
75
+ chunkSize: number;
76
+ totalChunks: number;
77
+ fileSha256: string;
78
+ ownerConnId?: string;
79
+ ownerClientId?: string;
80
+ }) => {
81
+ transferId: string;
82
+ accountId: string;
83
+ sessionKey: string;
84
+ route: BncrRoute;
85
+ fileName: string;
86
+ mimeType: string;
87
+ fileSize: number;
88
+ chunkSize: number;
89
+ totalChunks: number;
90
+ fileSha256: string;
91
+ startedAt: number;
92
+ status: string;
93
+ ownerConnId?: string;
94
+ ownerClientId?: string;
95
+ ackedChunks: Set<number>;
96
+ failedChunks: Map<number, string>;
97
+ };
98
+ };
99
+
100
+ export function createBncrFileTransferSetup(runtime: BncrFileTransferSetupRuntime) {
101
+ const prepareOutboundTransfer = async (args: {
102
+ accountId: string;
103
+ sessionKey: string;
104
+ route: BncrRoute;
105
+ mediaUrl: string;
106
+ mediaLocalRoots?: readonly string[];
107
+ hasGatewayContext: boolean;
108
+ }) => {
109
+ const { loaded, size, mimeType, fileName } = await runtime.loadOutboundTransferMedia({
110
+ mediaUrl: args.mediaUrl,
111
+ mediaLocalRoots: args.mediaLocalRoots,
112
+ });
113
+
114
+ if (!runtime.fileForceChunk && size <= runtime.fileInlineThreshold) {
115
+ return {
116
+ mode: 'base64' as const,
117
+ mimeType,
118
+ fileName,
119
+ mediaBase64: loaded.buffer.toString('base64'),
120
+ };
121
+ }
122
+
123
+ const owner = runtime.resolveOutboxPushOwner(args.accountId);
124
+ const recentInboundReachable = runtime.hasRecentInboundReachability(args.accountId);
125
+ const normalizedAccountId = runtime.normalizeAccountId(args.accountId);
126
+ const routeDiagnostics = runtime.buildTransferRouteDiagnostics({
127
+ accountId: normalizedAccountId,
128
+ recentInboundReachable,
129
+ });
130
+ runtime.logFileChunkDiag({
131
+ accountId: normalizedAccountId,
132
+ sessionKey: args.sessionKey,
133
+ mediaUrl: args.mediaUrl,
134
+ hasGatewayContext: args.hasGatewayContext,
135
+ activeConnectionKey: routeDiagnostics.activeConnectionKey,
136
+ ownerConnId: owner?.connId,
137
+ ownerClientId: owner?.clientId,
138
+ directConnIds: routeDiagnostics.directConnIds,
139
+ recentInboundReachable,
140
+ recentConnIds: routeDiagnostics.recentConnIds,
141
+ accountConnections: routeDiagnostics.accountConnections,
142
+ });
143
+
144
+ const connIds = runtime.selectTransferConnIds({
145
+ directConnIds: routeDiagnostics.directConnIds,
146
+ recentConnIds: routeDiagnostics.recentConnIds,
147
+ recentInboundReachable,
148
+ });
149
+ const transferId = randomUUID();
150
+ const chunkSize = 256 * 1024;
151
+ const totalChunks = Math.ceil(size / chunkSize);
152
+ const fileSha256 = createHash('sha256').update(loaded.buffer).digest('hex');
153
+
154
+ runtime.logFileTransferStart({
155
+ transferId,
156
+ accountId: normalizedAccountId,
157
+ sessionKey: args.sessionKey,
158
+ mediaUrl: args.mediaUrl,
159
+ fileName,
160
+ mimeType,
161
+ fileSize: size,
162
+ chunkSize,
163
+ totalChunks,
164
+ connIds,
165
+ ownerConnId: owner?.connId,
166
+ ownerClientId: owner?.clientId,
167
+ });
168
+
169
+ const state = runtime.buildInitialFileSendTransferState({
170
+ transferId,
171
+ accountId: args.accountId,
172
+ sessionKey: args.sessionKey,
173
+ route: args.route,
174
+ fileName,
175
+ mimeType,
176
+ fileSize: size,
177
+ chunkSize,
178
+ totalChunks,
179
+ fileSha256,
180
+ ownerConnId: owner?.connId,
181
+ ownerClientId: owner?.clientId,
182
+ });
183
+
184
+ return {
185
+ mode: 'chunk' as const,
186
+ loaded,
187
+ size,
188
+ mimeType,
189
+ fileName,
190
+ owner,
191
+ recentInboundReachable,
192
+ accountId: normalizedAccountId,
193
+ routeDiagnostics,
194
+ connIds,
195
+ transferId,
196
+ chunkSize,
197
+ totalChunks,
198
+ fileSha256,
199
+ state,
200
+ };
201
+ };
202
+
203
+ return {
204
+ prepareOutboundTransfer,
205
+ };
206
+ }
@@ -0,0 +1,41 @@
1
+ import type { GatewayRequestHandlerOptions } from 'openclaw/plugin-sdk/core';
2
+ import type { BncrGatewayCapabilityFlags } from '../core/types.ts';
3
+
4
+ export type BncrGatewayEventContextParams = {
5
+ accountId?: unknown;
6
+ clientId?: unknown;
7
+ outboundReady?: unknown;
8
+ preferredForOutbound?: unknown;
9
+ inboundOnly?: unknown;
10
+ };
11
+
12
+ export type BncrGatewayEventContext = {
13
+ accountId: string;
14
+ connId: string;
15
+ clientId?: string;
16
+ context: GatewayRequestHandlerOptions['context'];
17
+ } & BncrGatewayCapabilityFlags;
18
+
19
+ export function buildBncrGatewayEventContext(args: {
20
+ params: BncrGatewayEventContextParams | null | undefined;
21
+ client: GatewayRequestHandlerOptions['client'];
22
+ context: GatewayRequestHandlerOptions['context'];
23
+ asString: (value: unknown, fallback?: string) => string;
24
+ normalizeAccountId: (value: string) => string;
25
+ now?: () => number;
26
+ }): BncrGatewayEventContext {
27
+ const accountId = args.normalizeAccountId(args.asString(args.params?.accountId || ''));
28
+ const connId =
29
+ args.asString(args.client?.connId || '').trim() || `no-conn-${(args.now || Date.now)()}`;
30
+ const clientId = args.asString(args.params?.clientId || '').trim() || undefined;
31
+
32
+ return {
33
+ accountId,
34
+ connId,
35
+ clientId,
36
+ context: args.context,
37
+ outboundReady: args.params?.outboundReady === true,
38
+ preferredForOutbound: args.params?.preferredForOutbound === true,
39
+ inboundOnly: args.params?.inboundOnly === true,
40
+ };
41
+ }
@@ -1,11 +1,21 @@
1
+ import type { ChannelAccountSnapshot, ChannelGatewayContext } from 'openclaw/plugin-sdk';
2
+
3
+ type GatewayAccountContext = Pick<
4
+ ChannelGatewayContext,
5
+ 'accountId' | 'abortSignal' | 'getStatus' | 'setStatus'
6
+ > & {
7
+ getStatus?: () => ChannelAccountSnapshot;
8
+ setStatus?: (status: ChannelAccountSnapshot) => void;
9
+ };
10
+
1
11
  export type BncrGatewayAccountBridge = {
2
- channelStartAccount: (ctx: any) => unknown | Promise<unknown>;
3
- channelStopAccount: (ctx: any) => unknown | Promise<unknown>;
12
+ channelStartAccount: (ctx: GatewayAccountContext) => void | Promise<void>;
13
+ channelStopAccount: (ctx: GatewayAccountContext) => void | Promise<void>;
4
14
  };
5
15
 
6
16
  export function createBncrGatewayRuntime(getBridge: () => BncrGatewayAccountBridge) {
7
17
  return {
8
- startAccount: async (ctx: any) => getBridge().channelStartAccount(ctx),
9
- stopAccount: async (ctx: any) => getBridge().channelStopAccount(ctx),
18
+ startAccount: async (ctx: GatewayAccountContext) => getBridge().channelStartAccount(ctx),
19
+ stopAccount: async (ctx: GatewayAccountContext) => getBridge().channelStopAccount(ctx),
10
20
  };
11
21
  }
@@ -0,0 +1,107 @@
1
+ import { resolveAccount } from '../core/accounts.ts';
2
+ import { checkBncrMessageGate } from '../messaging/inbound/gate.ts';
3
+ import type { parseBncrInboundParams } from '../messaging/inbound/parse.ts';
4
+ import type { OpenClawResolvedAgentRoute } from '../openclaw/routing-runtime.ts';
5
+ import type { buildInboundResponsePayload } from './channel-inbound-helpers.ts';
6
+ import { resolveInboundSessionContext } from './channel-inbound-helpers.ts';
7
+ import type { BncrChannelConfigRoot } from './channel-runtime-types.ts';
8
+
9
+ type InboundAcceptanceResponsePayload = ReturnType<typeof buildInboundResponsePayload>;
10
+
11
+ export async function prepareBncrInboundAcceptance(args: {
12
+ api: unknown;
13
+ parsed: ReturnType<typeof parseBncrInboundParams>;
14
+ canonicalAgentId: string;
15
+ asString: (value: unknown, fallback?: string) => string;
16
+ getRuntimeConfig: (api: unknown) => BncrChannelConfigRoot;
17
+ resolveAgentRoute: (params: {
18
+ cfg: BncrChannelConfigRoot;
19
+ channel: string;
20
+ accountId: string;
21
+ peer: unknown;
22
+ }) => OpenClawResolvedAgentRoute;
23
+ buildInboundResponsePayload: (
24
+ args:
25
+ | { kind: 'invalid-peer' }
26
+ | { kind: 'duplicated'; accountId: string; msgId?: string | null }
27
+ | { kind: 'gate-denied'; accountId: string; msgId?: string | null; reason: string },
28
+ ) => InboundAcceptanceResponsePayload;
29
+ markInboundDedupSeen: (key: string) => boolean;
30
+ }) {
31
+ const { parsed, canonicalAgentId } = args;
32
+ const {
33
+ accountId,
34
+ platform,
35
+ groupId,
36
+ userId,
37
+ sessionKeyfromroute,
38
+ route,
39
+ text,
40
+ mediaBase64,
41
+ mediaPathFromTransfer,
42
+ msgId,
43
+ peer,
44
+ extracted,
45
+ dedupKey,
46
+ } = parsed;
47
+
48
+ if (!platform || (!userId && !groupId)) {
49
+ return {
50
+ ok: false as const,
51
+ status: false,
52
+ payload: args.buildInboundResponsePayload({ kind: 'invalid-peer' }),
53
+ };
54
+ }
55
+ if (args.markInboundDedupSeen(dedupKey)) {
56
+ return {
57
+ ok: false as const,
58
+ status: true,
59
+ payload: args.buildInboundResponsePayload({
60
+ kind: 'duplicated',
61
+ accountId,
62
+ msgId: msgId ?? null,
63
+ }),
64
+ };
65
+ }
66
+
67
+ const cfg = args.getRuntimeConfig(args.api);
68
+ const gate = await checkBncrMessageGate({
69
+ parsed,
70
+ cfg,
71
+ account: resolveAccount(cfg, accountId),
72
+ });
73
+ if (!gate.allowed) {
74
+ return {
75
+ ok: false as const,
76
+ status: true,
77
+ payload: args.buildInboundResponsePayload({
78
+ kind: 'gate-denied',
79
+ accountId,
80
+ msgId: msgId ?? null,
81
+ reason: gate.reason,
82
+ }),
83
+ };
84
+ }
85
+
86
+ const { sessionKey, inboundText } = resolveInboundSessionContext({
87
+ cfg,
88
+ accountId,
89
+ peer,
90
+ route,
91
+ sessionKeyFromRoute: sessionKeyfromroute,
92
+ canonicalAgentId,
93
+ taskKey: extracted.taskKey ?? undefined,
94
+ text,
95
+ extractedText: extracted.text,
96
+ asString: args.asString,
97
+ resolveAgentRoute: args.resolveAgentRoute,
98
+ });
99
+
100
+ return {
101
+ ok: true as const,
102
+ accountId,
103
+ sessionKey,
104
+ inboundText,
105
+ hasMedia: Boolean(mediaBase64 || mediaPathFromTransfer),
106
+ };
107
+ }