@xmoxmo/bncr 0.3.6 → 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.
- package/README.md +5 -0
- package/dist/index.js +28 -5
- package/index.ts +55 -721
- package/package.json +8 -4
- package/scripts/check-pack.mjs +93 -18
- package/scripts/check-register-drift.mjs +35 -13
- package/scripts/selfcheck.mjs +80 -11
- package/src/bootstrap/channel-plugin-runtime.ts +81 -0
- package/src/bootstrap/cli.ts +97 -0
- package/src/bootstrap/register-runtime-gateway.ts +129 -0
- package/src/bootstrap/register-runtime-helpers.ts +140 -0
- package/src/bootstrap/register-runtime-singleton.ts +137 -0
- package/src/bootstrap/register-runtime.ts +201 -0
- package/src/bootstrap/runtime-discovery.ts +187 -0
- package/src/bootstrap/runtime-loader.ts +54 -0
- package/src/channel.ts +1590 -4967
- package/src/core/accounts.ts +23 -4
- package/src/core/dead-letter-diagnostics.ts +37 -5
- package/src/core/diagnostics.ts +31 -15
- package/src/core/downlink-health.ts +3 -11
- package/src/core/extended-diagnostics.ts +78 -36
- package/src/core/file-transfer-payloads.ts +1 -1
- package/src/core/logging.ts +1 -0
- package/src/core/outbox-enqueue.ts +13 -2
- package/src/core/outbox-entry-builders.ts +2 -0
- package/src/core/outbox-summary.ts +75 -3
- package/src/core/permissions.ts +15 -2
- package/src/core/persisted-outbox-entry.ts +21 -6
- package/src/core/policy.ts +45 -4
- package/src/core/probe.ts +3 -15
- package/src/core/register-trace.ts +3 -3
- package/src/core/status.ts +43 -4
- package/src/core/targets.ts +216 -205
- package/src/core/types.ts +221 -0
- package/src/core/value-sanitize.ts +29 -0
- package/src/messaging/inbound/commands.ts +147 -172
- package/src/messaging/inbound/context-facts.ts +4 -2
- package/src/messaging/inbound/contracts.ts +70 -0
- package/src/messaging/inbound/dispatch-prep.ts +303 -0
- package/src/messaging/inbound/dispatch.ts +49 -462
- package/src/messaging/inbound/gate.ts +18 -5
- package/src/messaging/inbound/last-route.ts +10 -4
- package/src/messaging/inbound/media-url-download.ts +109 -0
- package/src/messaging/inbound/native-command-runtime.ts +225 -0
- package/src/messaging/inbound/parse.ts +2 -1
- package/src/messaging/inbound/remote-media.ts +49 -0
- package/src/messaging/inbound/reply-config.ts +16 -4
- package/src/messaging/inbound/reply-dispatch.ts +162 -0
- package/src/messaging/inbound/runtime-compat.ts +31 -10
- package/src/messaging/inbound/session-label.ts +15 -7
- package/src/messaging/inbound/turn-context.ts +131 -0
- package/src/messaging/outbound/actions.ts +24 -10
- package/src/messaging/outbound/diagnostics-debug-builders.ts +365 -0
- package/src/messaging/outbound/diagnostics.ts +31 -355
- package/src/messaging/outbound/durable-message-adapter.ts +20 -16
- package/src/messaging/outbound/durable-queue-adapter.ts +20 -7
- package/src/messaging/outbound/media.ts +24 -13
- package/src/messaging/outbound/reply-enqueue-media.ts +181 -0
- package/src/messaging/outbound/reply-enqueue.ts +46 -155
- package/src/messaging/outbound/send-params.ts +3 -0
- package/src/messaging/outbound/send.ts +19 -10
- package/src/messaging/outbound/session-route.ts +18 -3
- package/src/openclaw/channel-runtime-contracts.ts +76 -0
- package/src/openclaw/config-runtime.ts +13 -7
- package/src/openclaw/inbound-session-runtime.ts +7 -3
- package/src/openclaw/ingress-runtime.ts +17 -27
- package/src/openclaw/reply-runtime.ts +54 -59
- package/src/openclaw/routing-runtime.ts +35 -18
- package/src/openclaw/runtime-surface.ts +156 -12
- package/src/openclaw/sdk-helpers.ts +8 -1
- package/src/openclaw/session-route-runtime.ts +12 -12
- package/src/plugin/ack-outbox-runtime-group.ts +264 -0
- package/src/plugin/bridge-ack-facade.ts +137 -0
- package/src/plugin/bridge-connection-facade.ts +111 -0
- package/src/plugin/bridge-diagnostics-facade.ts +23 -0
- package/src/plugin/bridge-drain-facade.ts +98 -0
- package/src/plugin/bridge-extended-diagnostics-facade.ts +149 -0
- package/src/plugin/bridge-file-transfer-push-facade.ts +140 -0
- package/src/plugin/bridge-lifecycle.ts +156 -0
- package/src/plugin/bridge-media-facade.ts +241 -0
- package/src/plugin/bridge-outbox-facade.ts +182 -0
- package/src/plugin/bridge-runtime-helpers.ts +266 -0
- package/src/plugin/bridge-runtime-snapshots.ts +104 -0
- package/src/plugin/bridge-runtime-surface-facade.ts +8 -0
- package/src/plugin/bridge-status-facade.ts +76 -0
- package/src/plugin/bridge-status-worker-facade.ts +72 -0
- package/src/plugin/bridge-support-runtime.ts +137 -0
- package/src/plugin/bridge-surface-handlers-group.ts +242 -0
- package/src/plugin/bridge-surface-helpers.ts +28 -0
- package/src/plugin/capabilities.ts +1 -3
- package/src/plugin/channel-components.ts +289 -0
- package/src/plugin/channel-inbound-helpers.ts +149 -0
- package/src/plugin/channel-plugin-bridge-group.ts +129 -0
- package/src/plugin/channel-plugin-surface-group.ts +202 -0
- package/src/plugin/channel-runtime-builders-delivery.ts +513 -0
- package/src/plugin/channel-runtime-builders-status.ts +331 -0
- package/src/plugin/channel-runtime-builders.ts +25 -0
- package/src/plugin/channel-runtime-constants.ts +40 -0
- package/src/plugin/channel-runtime-types.ts +146 -0
- package/src/plugin/channel-send-runtime-group.ts +37 -0
- package/src/plugin/channel-send.ts +226 -0
- package/src/plugin/channel-utils.ts +102 -0
- package/src/plugin/config.ts +24 -3
- package/src/plugin/connection-handlers-helpers.ts +254 -0
- package/src/plugin/connection-handlers.ts +440 -0
- package/src/plugin/connection-state-helpers.ts +159 -0
- package/src/plugin/connection-state-runtime-group.ts +51 -0
- package/src/plugin/connection-state.ts +527 -0
- package/src/plugin/diagnostics-handlers.ts +211 -0
- package/src/plugin/error-message.ts +15 -0
- package/src/plugin/file-ack-runtime.ts +284 -0
- package/src/plugin/file-inbound-abort.ts +112 -0
- package/src/plugin/file-inbound-chunk.ts +146 -0
- package/src/plugin/file-inbound-complete.ts +153 -0
- package/src/plugin/file-inbound-handlers.ts +19 -0
- package/src/plugin/file-inbound-init.ts +122 -0
- package/src/plugin/file-inbound-runtime.ts +51 -0
- package/src/plugin/file-inbound-state.ts +62 -0
- package/src/plugin/file-transfer-logs.ts +227 -0
- package/src/plugin/file-transfer-orchestrator-chunk.ts +135 -0
- package/src/plugin/file-transfer-orchestrator.ts +304 -0
- package/src/plugin/file-transfer-runtime-group.ts +102 -0
- package/src/plugin/file-transfer-send.ts +89 -0
- package/src/plugin/file-transfer-setup.ts +206 -0
- package/src/plugin/gateway-event-context.ts +41 -0
- package/src/plugin/gateway-runtime.ts +14 -4
- package/src/plugin/inbound-acceptance.ts +107 -0
- package/src/plugin/inbound-handlers.ts +248 -0
- package/src/plugin/inbound-surface-handlers-group.ts +152 -0
- package/src/plugin/media-dedupe-runtime.ts +90 -0
- package/src/plugin/media-orchestrators-runtime-group.ts +316 -0
- package/src/plugin/message-ack-runtime.ts +284 -0
- package/src/plugin/message-send.ts +16 -6
- package/src/plugin/messaging.ts +98 -36
- package/src/plugin/outbound.ts +50 -8
- package/src/plugin/outbox-ack-logs.ts +136 -0
- package/src/plugin/outbox-ack-outcome.ts +128 -0
- package/src/plugin/outbox-drain-ack.ts +145 -0
- package/src/plugin/outbox-drain-failure.ts +84 -0
- package/src/plugin/outbox-drain-loop.ts +554 -0
- package/src/plugin/outbox-drain-post-push.ts +159 -0
- package/src/plugin/outbox-drain-runtime.ts +141 -0
- package/src/plugin/outbox-drain-schedule.ts +116 -0
- package/src/plugin/outbox-file-push-flow.ts +69 -0
- package/src/plugin/outbox-push-route-runtime-group.ts +81 -0
- package/src/plugin/outbox-push.ts +267 -0
- package/src/plugin/outbox-route.ts +181 -0
- package/src/plugin/outbox-text-push-flow.ts +90 -0
- package/src/plugin/runtime-diagnostics-assembler.ts +183 -0
- package/src/plugin/runtime-diagnostics-helpers.ts +302 -0
- package/src/plugin/runtime-diagnostics-payload-builders.ts +171 -0
- package/src/plugin/runtime-diagnostics-snapshot.ts +31 -0
- package/src/plugin/setup.ts +33 -6
- package/src/plugin/state-store.ts +249 -0
- package/src/plugin/state-transient-runtime-group.ts +105 -0
- package/src/plugin/status-runtime.ts +251 -0
- package/src/plugin/status.ts +33 -7
- package/src/plugin/target-runtime.ts +141 -0
- package/src/plugin/target-status-runtime-group.ts +130 -0
- package/src/plugin/transient-state-runtime.ts +82 -0
- package/src/runtime/outbound-ack-timeout.ts +5 -3
- package/src/runtime/outbound-flags.ts +24 -8
- package/src/runtime/status-snapshots.ts +36 -7
- package/src/runtime/status-worker.ts +34 -4
|
@@ -148,6 +148,7 @@ export type BncrStructuredContextFactsFromInboundPartsInput = {
|
|
|
148
148
|
rawBody: string;
|
|
149
149
|
body?: string;
|
|
150
150
|
mediaPath?: string | null;
|
|
151
|
+
mediaContentType?: string;
|
|
151
152
|
};
|
|
152
153
|
senderIdForContext: string;
|
|
153
154
|
senderDisplayName?: string;
|
|
@@ -157,6 +158,7 @@ export function buildBncrStructuredContextFactsFromInboundParts(
|
|
|
157
158
|
input: BncrStructuredContextFactsFromInboundPartsInput,
|
|
158
159
|
) {
|
|
159
160
|
const mediaPath = input.prepared.mediaPath || undefined;
|
|
161
|
+
const mediaContentType = input.prepared.mediaContentType || input.parsed.mimeType;
|
|
160
162
|
return buildBncrStructuredContextFacts({
|
|
161
163
|
channelId: input.channelId,
|
|
162
164
|
accountId: input.parsed.accountId,
|
|
@@ -190,8 +192,8 @@ export function buildBncrStructuredContextFactsFromInboundParts(
|
|
|
190
192
|
? [
|
|
191
193
|
{
|
|
192
194
|
path: mediaPath,
|
|
193
|
-
contentType:
|
|
194
|
-
kind: inferBncrStructuredMediaKind(
|
|
195
|
+
contentType: mediaContentType,
|
|
196
|
+
kind: inferBncrStructuredMediaKind(mediaContentType),
|
|
195
197
|
messageId: input.parsed.msgId,
|
|
196
198
|
},
|
|
197
199
|
]
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import type { OpenClawPluginApi } from 'openclaw/plugin-sdk/core';
|
|
2
|
+
import type { BncrRoute } from '../../core/types.ts';
|
|
3
|
+
import type {
|
|
4
|
+
OpenClawChannelPeer,
|
|
5
|
+
OpenClawChannelRuntimeContext,
|
|
6
|
+
} from '../../openclaw/channel-runtime-contracts.ts';
|
|
7
|
+
import type {
|
|
8
|
+
BncrChannelConfigRoot,
|
|
9
|
+
BncrChannelConfigSection,
|
|
10
|
+
} from '../../plugin/channel-runtime-types.ts';
|
|
11
|
+
import type { ReplyPayloadInput } from '../outbound/reply-enqueue.ts';
|
|
12
|
+
import type { OutboundReplyTargetPolicy } from '../outbound/reply-target-policy.ts';
|
|
13
|
+
|
|
14
|
+
export type BncrInboundParsedPeer = OpenClawChannelPeer;
|
|
15
|
+
|
|
16
|
+
export type BncrInboundParamsInput = {
|
|
17
|
+
accountId?: unknown;
|
|
18
|
+
platform?: unknown;
|
|
19
|
+
groupId?: unknown;
|
|
20
|
+
userId?: unknown;
|
|
21
|
+
sessionKey?: unknown;
|
|
22
|
+
originatingTo?: unknown;
|
|
23
|
+
providedOriginatingTo?: unknown;
|
|
24
|
+
to?: unknown;
|
|
25
|
+
clientId?: unknown;
|
|
26
|
+
msg?: unknown;
|
|
27
|
+
type?: unknown;
|
|
28
|
+
base64?: unknown;
|
|
29
|
+
path?: unknown;
|
|
30
|
+
mimeType?: unknown;
|
|
31
|
+
fileName?: unknown;
|
|
32
|
+
msgId?: unknown;
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
export type BncrRememberSessionRoute = (
|
|
36
|
+
sessionKey: string,
|
|
37
|
+
accountId: string,
|
|
38
|
+
route: BncrRoute,
|
|
39
|
+
) => void;
|
|
40
|
+
|
|
41
|
+
export type BncrEnqueueFromReply = (args: {
|
|
42
|
+
accountId: string;
|
|
43
|
+
sessionKey: string;
|
|
44
|
+
route: BncrRoute;
|
|
45
|
+
payload: ReplyPayloadInput;
|
|
46
|
+
mediaLocalRoots?: readonly string[];
|
|
47
|
+
replyTargetPolicy?: OutboundReplyTargetPolicy;
|
|
48
|
+
}) => Promise<void>;
|
|
49
|
+
|
|
50
|
+
export type BncrInboundLogger = { warn?: (msg: string) => void; error?: (msg: string) => void };
|
|
51
|
+
|
|
52
|
+
export type BncrInboundApi = OpenClawPluginApi;
|
|
53
|
+
|
|
54
|
+
export type BncrInboundConfig = BncrChannelConfigRoot & {
|
|
55
|
+
session?: { store?: string; dmScope?: unknown };
|
|
56
|
+
channels?: Record<string, BncrChannelConfigSection | undefined>;
|
|
57
|
+
agents?: {
|
|
58
|
+
defaults?: {
|
|
59
|
+
blockStreamingDefault?: unknown;
|
|
60
|
+
blockStreamingBreak?: unknown;
|
|
61
|
+
blockStreamingChunk?: unknown;
|
|
62
|
+
[key: string]: unknown;
|
|
63
|
+
};
|
|
64
|
+
[key: string]: unknown;
|
|
65
|
+
};
|
|
66
|
+
accessGroups?: unknown;
|
|
67
|
+
[key: string]: unknown;
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
export type BncrInboundContextPayload = OpenClawChannelRuntimeContext;
|
|
@@ -0,0 +1,303 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import {
|
|
3
|
+
formatDisplayScope,
|
|
4
|
+
normalizeInboundSessionKey,
|
|
5
|
+
withTaskSessionKey,
|
|
6
|
+
} from '../../core/targets.ts';
|
|
7
|
+
import {
|
|
8
|
+
readBncrSessionUpdatedAt,
|
|
9
|
+
resolveBncrInboundSessionStorePath,
|
|
10
|
+
} from '../../openclaw/inbound-session-runtime.ts';
|
|
11
|
+
import { saveOpenClawChannelMediaBuffer } from '../../openclaw/media-runtime.ts';
|
|
12
|
+
import {
|
|
13
|
+
formatOpenClawAgentEnvelope,
|
|
14
|
+
resolveOpenClawEnvelopeFormatOptions,
|
|
15
|
+
} from '../../openclaw/reply-runtime.ts';
|
|
16
|
+
import {
|
|
17
|
+
type OpenClawResolvedAgentRoute,
|
|
18
|
+
resolveOpenClawAgentRoute,
|
|
19
|
+
} from '../../openclaw/routing-runtime.ts';
|
|
20
|
+
import type { BncrInboundApi, BncrInboundConfig, BncrRememberSessionRoute } from './contracts.ts';
|
|
21
|
+
import { INBOUND_MEDIA_URL_MAX_BYTES, isHttpMediaUrl } from './media-url-download.ts';
|
|
22
|
+
import { loadInboundRemoteMedia } from './remote-media.ts';
|
|
23
|
+
|
|
24
|
+
export type ParsedInbound = ReturnType<typeof import('./parse.ts')['parseBncrInboundParams']>;
|
|
25
|
+
|
|
26
|
+
export type BncrInboundConversationResolution = {
|
|
27
|
+
accountId: string;
|
|
28
|
+
chatType: 'direct' | 'group';
|
|
29
|
+
route: ParsedInbound['route'];
|
|
30
|
+
resolvedRoute: {
|
|
31
|
+
sessionKey: string;
|
|
32
|
+
agentId: string;
|
|
33
|
+
mainSessionKey?: string;
|
|
34
|
+
};
|
|
35
|
+
canonicalTo: string;
|
|
36
|
+
rawTo: string;
|
|
37
|
+
originatingTo: string;
|
|
38
|
+
baseSessionKey: string;
|
|
39
|
+
taskSessionKey?: string;
|
|
40
|
+
dispatchSessionKey: string;
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
export type BncrInboundReplyRouteFact = {
|
|
44
|
+
accountId: string;
|
|
45
|
+
sessionKey: string;
|
|
46
|
+
route: ParsedInbound['route'];
|
|
47
|
+
canonicalTo: string;
|
|
48
|
+
originatingTo: string;
|
|
49
|
+
chatType: 'direct' | 'group';
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
export type BncrPreparedInboundSessionContext = {
|
|
53
|
+
storePath: string;
|
|
54
|
+
mediaPath?: string;
|
|
55
|
+
mediaContentType?: string;
|
|
56
|
+
rawBody: string;
|
|
57
|
+
body: string;
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
export type BncrInboundPreparation = {
|
|
61
|
+
resolution: BncrInboundConversationResolution;
|
|
62
|
+
prepared: BncrPreparedInboundSessionContext;
|
|
63
|
+
replyRouteFact: BncrInboundReplyRouteFact;
|
|
64
|
+
senderIdForContext: string;
|
|
65
|
+
senderDisplayName: string;
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
const INBOUND_MEDIA_MAX_BYTES = 30 * 1024 * 1024;
|
|
69
|
+
|
|
70
|
+
function assertResolvedAgentRoute(resolvedRoute: OpenClawResolvedAgentRoute): {
|
|
71
|
+
sessionKey: string;
|
|
72
|
+
agentId: string;
|
|
73
|
+
mainSessionKey?: string;
|
|
74
|
+
} {
|
|
75
|
+
const sessionKey =
|
|
76
|
+
typeof resolvedRoute.sessionKey === 'string' ? resolvedRoute.sessionKey.trim() : '';
|
|
77
|
+
const agentId = typeof resolvedRoute.agentId === 'string' ? resolvedRoute.agentId.trim() : '';
|
|
78
|
+
if (!sessionKey) throw new Error('OpenClaw resolveAgentRoute returned empty sessionKey');
|
|
79
|
+
if (!agentId) throw new Error('OpenClaw resolveAgentRoute returned empty agentId');
|
|
80
|
+
return {
|
|
81
|
+
sessionKey,
|
|
82
|
+
agentId,
|
|
83
|
+
...(typeof resolvedRoute.mainSessionKey === 'string' && resolvedRoute.mainSessionKey.trim()
|
|
84
|
+
? { mainSessionKey: resolvedRoute.mainSessionKey }
|
|
85
|
+
: {}),
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function formatRawBncrInboundTarget(route: ParsedInbound['route']): string {
|
|
90
|
+
return `Bncr:${String(route.platform || '').trim()}:${String(route.groupId || '').trim()}:${String(route.userId || '').trim()}`;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export function resolveBncrInboundConversation(args: {
|
|
94
|
+
api: BncrInboundApi;
|
|
95
|
+
cfg: BncrInboundConfig;
|
|
96
|
+
channelId: string;
|
|
97
|
+
parsed: ParsedInbound;
|
|
98
|
+
canonicalAgentId: string;
|
|
99
|
+
}) {
|
|
100
|
+
const { api, cfg, channelId, parsed, canonicalAgentId } = args;
|
|
101
|
+
const { accountId, route, peer, sessionKeyfromroute, providedOriginatingTo, extracted } = parsed;
|
|
102
|
+
|
|
103
|
+
const resolvedRoute = assertResolvedAgentRoute(
|
|
104
|
+
resolveOpenClawAgentRoute(api, {
|
|
105
|
+
cfg,
|
|
106
|
+
channel: channelId,
|
|
107
|
+
accountId,
|
|
108
|
+
peer,
|
|
109
|
+
}),
|
|
110
|
+
);
|
|
111
|
+
|
|
112
|
+
const baseSessionKey =
|
|
113
|
+
normalizeInboundSessionKey(sessionKeyfromroute, route, canonicalAgentId) ||
|
|
114
|
+
resolvedRoute.sessionKey;
|
|
115
|
+
const taskSessionKey = withTaskSessionKey(baseSessionKey, extracted.taskKey);
|
|
116
|
+
const dispatchSessionKey = taskSessionKey || baseSessionKey;
|
|
117
|
+
const rawTo = formatRawBncrInboundTarget(route);
|
|
118
|
+
const canonicalTo = formatDisplayScope(route);
|
|
119
|
+
const originatingTo = providedOriginatingTo || rawTo;
|
|
120
|
+
|
|
121
|
+
return {
|
|
122
|
+
accountId,
|
|
123
|
+
chatType: peer.kind,
|
|
124
|
+
route,
|
|
125
|
+
resolvedRoute,
|
|
126
|
+
canonicalTo,
|
|
127
|
+
rawTo,
|
|
128
|
+
originatingTo,
|
|
129
|
+
baseSessionKey,
|
|
130
|
+
...(taskSessionKey ? { taskSessionKey } : {}),
|
|
131
|
+
dispatchSessionKey,
|
|
132
|
+
} satisfies BncrInboundConversationResolution;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
export function estimateBase64DecodedBytes(value: string): number {
|
|
136
|
+
const normalized = String(value || '').replace(/\s+/g, '');
|
|
137
|
+
if (!normalized) return 0;
|
|
138
|
+
const padding = normalized.endsWith('==') ? 2 : normalized.endsWith('=') ? 1 : 0;
|
|
139
|
+
return Math.max(0, Math.floor((normalized.length * 3) / 4) - padding);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
export function assertInboundMediaBase64Size(value: string, maxBytes = INBOUND_MEDIA_MAX_BYTES) {
|
|
143
|
+
const estimatedBytes = estimateBase64DecodedBytes(value);
|
|
144
|
+
if (estimatedBytes > maxBytes) {
|
|
145
|
+
throw new Error(
|
|
146
|
+
`inbound media too large: estimated ${estimatedBytes} bytes exceeds ${maxBytes} bytes`,
|
|
147
|
+
);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
export function decodeInboundMediaBase64(
|
|
152
|
+
value: string,
|
|
153
|
+
maxBytes = INBOUND_MEDIA_MAX_BYTES,
|
|
154
|
+
): Buffer {
|
|
155
|
+
assertInboundMediaBase64Size(value, maxBytes);
|
|
156
|
+
const normalized = String(value || '').replace(/\s+/g, '');
|
|
157
|
+
const mediaBuf = Buffer.from(normalized, 'base64');
|
|
158
|
+
if (!mediaBuf.length) {
|
|
159
|
+
throw new Error('inbound media base64 decoded to empty buffer');
|
|
160
|
+
}
|
|
161
|
+
if (mediaBuf.length > maxBytes) {
|
|
162
|
+
throw new Error(
|
|
163
|
+
`inbound media too large: decoded ${mediaBuf.length} bytes exceeds ${maxBytes} bytes`,
|
|
164
|
+
);
|
|
165
|
+
}
|
|
166
|
+
return mediaBuf;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
export async function prepareBncrInboundSessionContext(args: {
|
|
170
|
+
api: BncrInboundApi;
|
|
171
|
+
cfg: BncrInboundConfig;
|
|
172
|
+
parsed: ParsedInbound;
|
|
173
|
+
resolution: BncrInboundConversationResolution;
|
|
174
|
+
rememberSessionRoute: BncrRememberSessionRoute;
|
|
175
|
+
}) {
|
|
176
|
+
const { api, cfg, parsed, resolution, rememberSessionRoute } = args;
|
|
177
|
+
const {
|
|
178
|
+
msgType,
|
|
179
|
+
mediaBase64,
|
|
180
|
+
mediaPathFromTransfer,
|
|
181
|
+
mimeType,
|
|
182
|
+
fileName,
|
|
183
|
+
extracted,
|
|
184
|
+
platform,
|
|
185
|
+
groupId,
|
|
186
|
+
userId,
|
|
187
|
+
} = parsed;
|
|
188
|
+
const { accountId, route, resolvedRoute, baseSessionKey, taskSessionKey, dispatchSessionKey } =
|
|
189
|
+
resolution;
|
|
190
|
+
|
|
191
|
+
rememberSessionRoute(baseSessionKey, accountId, route);
|
|
192
|
+
if (taskSessionKey && taskSessionKey !== baseSessionKey) {
|
|
193
|
+
rememberSessionRoute(taskSessionKey, accountId, route);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
const storePath = resolveBncrInboundSessionStorePath({
|
|
197
|
+
storeConfig: cfg?.session?.store,
|
|
198
|
+
agentId: resolvedRoute.agentId,
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
let mediaPath: string | undefined;
|
|
202
|
+
let mediaContentType = mimeType;
|
|
203
|
+
if (mediaBase64) {
|
|
204
|
+
const mediaBuf = decodeInboundMediaBase64(mediaBase64);
|
|
205
|
+
const saved = await saveOpenClawChannelMediaBuffer(
|
|
206
|
+
api,
|
|
207
|
+
mediaBuf,
|
|
208
|
+
mimeType,
|
|
209
|
+
'inbound',
|
|
210
|
+
30 * 1024 * 1024,
|
|
211
|
+
fileName,
|
|
212
|
+
);
|
|
213
|
+
mediaPath = saved.path;
|
|
214
|
+
} else if (mediaPathFromTransfer && isHttpMediaUrl(mediaPathFromTransfer)) {
|
|
215
|
+
const downloaded = await loadInboundRemoteMedia(
|
|
216
|
+
api,
|
|
217
|
+
mediaPathFromTransfer,
|
|
218
|
+
INBOUND_MEDIA_URL_MAX_BYTES,
|
|
219
|
+
);
|
|
220
|
+
mediaContentType = downloaded.contentType || mimeType;
|
|
221
|
+
const saved = await saveOpenClawChannelMediaBuffer(
|
|
222
|
+
api,
|
|
223
|
+
downloaded.buffer,
|
|
224
|
+
mediaContentType,
|
|
225
|
+
'inbound',
|
|
226
|
+
INBOUND_MEDIA_URL_MAX_BYTES,
|
|
227
|
+
fileName,
|
|
228
|
+
);
|
|
229
|
+
mediaPath = saved.path;
|
|
230
|
+
} else if (mediaPathFromTransfer && fs.existsSync(mediaPathFromTransfer)) {
|
|
231
|
+
mediaPath = mediaPathFromTransfer;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
const rawBody = extracted.text || (msgType === 'text' ? '' : `[${msgType}]`);
|
|
235
|
+
const body = formatOpenClawAgentEnvelope(api, {
|
|
236
|
+
channel: 'Bncr',
|
|
237
|
+
from: `${platform}:${groupId}:${userId}`,
|
|
238
|
+
timestamp: Date.now(),
|
|
239
|
+
previousTimestamp: readBncrSessionUpdatedAt(api, {
|
|
240
|
+
storePath,
|
|
241
|
+
sessionKey: dispatchSessionKey,
|
|
242
|
+
}),
|
|
243
|
+
envelope: resolveOpenClawEnvelopeFormatOptions(api, cfg),
|
|
244
|
+
body: rawBody,
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
return {
|
|
248
|
+
storePath,
|
|
249
|
+
mediaPath,
|
|
250
|
+
mediaContentType,
|
|
251
|
+
rawBody,
|
|
252
|
+
body,
|
|
253
|
+
} satisfies BncrPreparedInboundSessionContext;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
export function buildBncrInboundReplyRouteFact(
|
|
257
|
+
resolution: BncrInboundConversationResolution,
|
|
258
|
+
): BncrInboundReplyRouteFact {
|
|
259
|
+
return {
|
|
260
|
+
accountId: resolution.accountId,
|
|
261
|
+
sessionKey: resolution.dispatchSessionKey,
|
|
262
|
+
route: resolution.route,
|
|
263
|
+
canonicalTo: resolution.canonicalTo,
|
|
264
|
+
originatingTo: resolution.originatingTo,
|
|
265
|
+
chatType: resolution.chatType,
|
|
266
|
+
};
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
export async function prepareBncrInboundDispatch(args: {
|
|
270
|
+
api: BncrInboundApi;
|
|
271
|
+
channelId: string;
|
|
272
|
+
cfg: BncrInboundConfig;
|
|
273
|
+
parsed: ParsedInbound;
|
|
274
|
+
canonicalAgentId: string;
|
|
275
|
+
rememberSessionRoute: BncrRememberSessionRoute;
|
|
276
|
+
}) {
|
|
277
|
+
const { api, channelId, cfg, parsed, canonicalAgentId, rememberSessionRoute } = args;
|
|
278
|
+
const resolution = resolveBncrInboundConversation({
|
|
279
|
+
api,
|
|
280
|
+
cfg,
|
|
281
|
+
channelId,
|
|
282
|
+
parsed,
|
|
283
|
+
canonicalAgentId,
|
|
284
|
+
});
|
|
285
|
+
const prepared = await prepareBncrInboundSessionContext({
|
|
286
|
+
api,
|
|
287
|
+
cfg,
|
|
288
|
+
parsed,
|
|
289
|
+
resolution,
|
|
290
|
+
rememberSessionRoute,
|
|
291
|
+
});
|
|
292
|
+
const replyRouteFact = buildBncrInboundReplyRouteFact(resolution);
|
|
293
|
+
const senderIdForContext = parsed.clientId || resolution.canonicalTo;
|
|
294
|
+
const senderDisplayName = parsed.clientId ? 'bncr-client' : resolution.canonicalTo;
|
|
295
|
+
|
|
296
|
+
return {
|
|
297
|
+
resolution,
|
|
298
|
+
prepared,
|
|
299
|
+
replyRouteFact,
|
|
300
|
+
senderIdForContext,
|
|
301
|
+
senderDisplayName,
|
|
302
|
+
} satisfies BncrInboundPreparation;
|
|
303
|
+
}
|