@xmoxmo/bncr 0.5.6 → 0.5.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/dist/index.js +1 -9
- package/index.ts +1 -9
- package/package.json +1 -1
- package/scripts/check-pack.mjs +0 -8
- package/scripts/selfcheck.mjs +0 -10
- package/src/bootstrap/runtime-loader.ts +1 -1
- package/src/channel.ts +118 -171
- package/src/core/accounts.ts +1 -6
- package/src/core/connection-reachability.ts +2 -6
- package/src/core/dead-letter-diagnostics.ts +4 -10
- package/src/core/logging.ts +1 -1
- package/src/core/outbox-entry-builders.ts +78 -84
- package/src/core/outbox-file-transfer-failure.ts +5 -0
- package/src/core/outbox-file-transfer-guards.ts +6 -0
- package/src/core/outbox-file-transfer-prep.ts +8 -5
- package/src/core/outbox-file-transfer-success.ts +5 -0
- package/src/core/outbox-queue.ts +1 -7
- package/src/core/outbox-summary.ts +10 -56
- package/src/core/permissions.ts +2 -10
- package/src/core/persisted-outbox-entry.ts +54 -11
- package/src/core/policy.ts +1 -11
- package/src/core/register-trace.ts +4 -7
- package/src/core/status-meta.ts +1 -9
- package/src/core/status.ts +1 -21
- package/src/core/targets.ts +52 -70
- package/src/core/types.ts +1 -17
- package/src/core/value-sanitize.ts +38 -1
- package/src/messaging/inbound/commands.ts +2 -24
- package/src/messaging/inbound/dispatch-prep.ts +1 -1
- package/src/messaging/inbound/gate.ts +2 -31
- package/src/messaging/inbound/group-history.ts +0 -75
- package/src/messaging/inbound/parse.ts +2 -32
- package/src/messaging/inbound/scene-admin.ts +8 -8
- package/src/messaging/outbound/diagnostics-debug-builders.ts +0 -15
- package/src/messaging/outbound/diagnostics.ts +1 -7
- package/src/messaging/outbound/durable-queue-adapter.ts +17 -11
- package/src/messaging/outbound/marker-parser.ts +277 -28
- package/src/messaging/outbound/media-dedupe.ts +3 -10
- package/src/messaging/outbound/media.ts +1 -6
- package/src/messaging/outbound/normalize-outbound-send.ts +329 -0
- package/src/messaging/outbound/queue-selectors.ts +1 -5
- package/src/messaging/outbound/reply-enqueue-media.ts +1 -184
- package/src/messaging/outbound/reply-enqueue.ts +313 -175
- package/src/messaging/outbound/session-route.ts +6 -11
- package/src/messaging/outbound/target-resolver.ts +1 -6
- package/src/openclaw/media-runtime.ts +1 -1
- package/src/openclaw/routing-runtime.ts +0 -11
- package/src/openclaw/sdk-helpers.ts +0 -2
- package/src/plugin/bridge-media-facade.ts +0 -4
- package/src/plugin/bridge-outbox-facade.ts +0 -1
- package/src/plugin/channel-components.ts +0 -13
- package/src/plugin/channel-plugin-surface-group.ts +52 -65
- package/src/plugin/channel-runtime-builders-delivery.ts +3 -362
- package/src/plugin/channel-runtime-builders-status.ts +0 -160
- package/src/plugin/channel-runtime-builders.ts +3 -17
- package/src/plugin/channel-runtime-constants.ts +1 -1
- package/src/plugin/channel-runtime-types.ts +2 -24
- package/src/plugin/channel-send.ts +206 -221
- package/src/plugin/channel-utils.ts +8 -29
- package/src/plugin/connection-handlers-helpers.ts +0 -15
- package/src/plugin/connection-handlers.ts +6 -5
- package/src/plugin/file-inbound-abort.ts +5 -0
- package/src/plugin/file-inbound-chunk.ts +5 -0
- package/src/plugin/file-inbound-complete.ts +5 -0
- package/src/plugin/file-transfer-orchestrator.ts +39 -2
- package/src/plugin/media-dedupe-runtime.ts +1 -6
- package/src/plugin/media-orchestrators-runtime-group.ts +20 -149
- package/src/plugin/message-send.ts +7 -8
- package/src/plugin/messaging.ts +3 -3
- package/src/plugin/outbound.ts +6 -26
- package/src/plugin/outbox-drain-loop.ts +2 -2
- package/src/plugin/outbox-push.ts +1 -52
- package/src/plugin/outbox-unified-push-flow.ts +307 -0
- package/src/plugin/scene-registry.ts +1 -1
- package/src/plugin/target-runtime.ts +1 -6
- package/src/runtime/log-dedupe.ts +3 -3
- package/src/runtime/outbound-ack-timeout.ts +3 -12
- package/src/runtime/outbox-transitions.ts +1 -4
- package/src/runtime/status-snapshots.ts +2 -2
- package/src/runtime/status-worker.ts +3 -15
- package/src/core/outbox-text-push-prep.ts +0 -36
- package/src/messaging/outbound/build-send-action.ts +0 -130
- package/src/messaging/outbound/durable-message-adapter.ts +0 -121
- package/src/messaging/outbound/send-params.ts +0 -114
- package/src/messaging/outbound/send.ts +0 -127
- package/src/openclaw/ingress-runtime.ts +0 -25
- package/src/plugin/bridge-file-transfer-push-facade.ts +0 -142
- package/src/plugin/channel-send-runtime-group.ts +0 -37
- package/src/plugin/outbox-file-push-flow.ts +0 -69
- package/src/plugin/outbox-text-push-flow.ts +0 -90
|
@@ -1,121 +0,0 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
ChannelMessageAdapterShape,
|
|
3
|
-
ChannelMessageSendMediaContext,
|
|
4
|
-
ChannelMessageSendPayloadContext,
|
|
5
|
-
ChannelMessageSendResult,
|
|
6
|
-
ChannelMessageSendTextContext,
|
|
7
|
-
} from 'openclaw/plugin-sdk/channel-message';
|
|
8
|
-
import { defineChannelMessageAdapter } from 'openclaw/plugin-sdk/channel-message';
|
|
9
|
-
import type { OpenClawConfig } from 'openclaw/plugin-sdk/core';
|
|
10
|
-
|
|
11
|
-
import {
|
|
12
|
-
buildFileTransferOutboxEntry,
|
|
13
|
-
buildTextOutboxEntry,
|
|
14
|
-
} from '../../core/outbox-entry-builders.ts';
|
|
15
|
-
import type { BncrRoute, OutboxEntry } from '../../core/types.ts';
|
|
16
|
-
import { buildBncrDurableQueuedResult } from './durable-queue-adapter.ts';
|
|
17
|
-
|
|
18
|
-
export type BncrDurableMessageQueuedAdapterDeps = {
|
|
19
|
-
enqueueText: (
|
|
20
|
-
ctx: ChannelMessageSendTextContext<OpenClawConfig>,
|
|
21
|
-
) => Promise<OutboxEntry> | OutboxEntry;
|
|
22
|
-
enqueueMedia?: (
|
|
23
|
-
ctx: ChannelMessageSendMediaContext<OpenClawConfig>,
|
|
24
|
-
) => Promise<OutboxEntry> | OutboxEntry;
|
|
25
|
-
enqueuePayload?: (
|
|
26
|
-
ctx: ChannelMessageSendPayloadContext<OpenClawConfig>,
|
|
27
|
-
) => Promise<OutboxEntry> | OutboxEntry;
|
|
28
|
-
now?: () => number;
|
|
29
|
-
};
|
|
30
|
-
|
|
31
|
-
export type BncrDurableMessageQueuedAdapterBuilderDeps = {
|
|
32
|
-
createMessageId: () => string;
|
|
33
|
-
now: () => number;
|
|
34
|
-
normalizeAccountId: (accountId?: string | null) => string;
|
|
35
|
-
normalizeReplyToId: (value?: string | null) => string;
|
|
36
|
-
resolveTarget: (ctx: ChannelMessageSendTextContext<OpenClawConfig>) => {
|
|
37
|
-
route: BncrRoute;
|
|
38
|
-
sessionKey: string;
|
|
39
|
-
accountId?: string | null;
|
|
40
|
-
};
|
|
41
|
-
filePushEvent?: string;
|
|
42
|
-
};
|
|
43
|
-
|
|
44
|
-
// This adapter intentionally models only the OpenClaw -> bncr-plugin handoff.
|
|
45
|
-
// Once a message is accepted into bncr's own outbox, OpenClaw should stop managing it;
|
|
46
|
-
// client/platform ACK, retry, and deadLetter remain owned by the bncr service framework.
|
|
47
|
-
export function createBncrDurableMessageQueuedAdapter(
|
|
48
|
-
deps: BncrDurableMessageQueuedAdapterDeps,
|
|
49
|
-
): ChannelMessageAdapterShape<OpenClawConfig, ChannelMessageSendResult> {
|
|
50
|
-
return defineChannelMessageAdapter({
|
|
51
|
-
id: 'bncr-queued-outbox',
|
|
52
|
-
receive: {
|
|
53
|
-
defaultAckPolicy: 'manual',
|
|
54
|
-
supportedAckPolicies: ['manual'],
|
|
55
|
-
},
|
|
56
|
-
send: {
|
|
57
|
-
text: async (ctx) => toChannelMessageSendResult(await deps.enqueueText(ctx), deps.now),
|
|
58
|
-
media: deps.enqueueMedia
|
|
59
|
-
? async (ctx) => toChannelMessageSendResult(await deps.enqueueMedia?.(ctx), deps.now)
|
|
60
|
-
: undefined,
|
|
61
|
-
payload: deps.enqueuePayload
|
|
62
|
-
? async (ctx) => toChannelMessageSendResult(await deps.enqueuePayload?.(ctx), deps.now)
|
|
63
|
-
: undefined,
|
|
64
|
-
},
|
|
65
|
-
});
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
export function createBncrDurableMessageQueuedAdapterFromBuilders(
|
|
69
|
-
deps: BncrDurableMessageQueuedAdapterBuilderDeps,
|
|
70
|
-
): ChannelMessageAdapterShape<OpenClawConfig, ChannelMessageSendResult> {
|
|
71
|
-
return createBncrDurableMessageQueuedAdapter({
|
|
72
|
-
now: deps.now,
|
|
73
|
-
enqueueText: (ctx) => {
|
|
74
|
-
const resolved = deps.resolveTarget(ctx);
|
|
75
|
-
return buildTextOutboxEntry({
|
|
76
|
-
createMessageId: deps.createMessageId,
|
|
77
|
-
now: deps.now,
|
|
78
|
-
normalizeAccountId: deps.normalizeAccountId,
|
|
79
|
-
normalizeReplyToId: deps.normalizeReplyToId,
|
|
80
|
-
accountId: resolved.accountId ?? ctx.accountId ?? '',
|
|
81
|
-
sessionKey: resolved.sessionKey,
|
|
82
|
-
route: resolved.route,
|
|
83
|
-
text: ctx.text,
|
|
84
|
-
kind: 'final',
|
|
85
|
-
replyToId: ctx.replyToId ?? undefined,
|
|
86
|
-
});
|
|
87
|
-
},
|
|
88
|
-
enqueueMedia: (ctx) => {
|
|
89
|
-
const resolved = deps.resolveTarget(ctx);
|
|
90
|
-
return buildFileTransferOutboxEntry({
|
|
91
|
-
createMessageId: deps.createMessageId,
|
|
92
|
-
now: deps.now,
|
|
93
|
-
normalizeAccountId: deps.normalizeAccountId,
|
|
94
|
-
pushEvent: deps.filePushEvent ?? 'bncr.file.push',
|
|
95
|
-
accountId: resolved.accountId ?? ctx.accountId ?? '',
|
|
96
|
-
sessionKey: resolved.sessionKey,
|
|
97
|
-
route: resolved.route,
|
|
98
|
-
mediaUrl: ctx.mediaUrl,
|
|
99
|
-
mediaLocalRoots: ctx.mediaLocalRoots,
|
|
100
|
-
text: ctx.text,
|
|
101
|
-
asVoice: ctx.audioAsVoice,
|
|
102
|
-
audioAsVoice: ctx.audioAsVoice,
|
|
103
|
-
kind: 'final',
|
|
104
|
-
replyToId: ctx.replyToId ?? undefined,
|
|
105
|
-
});
|
|
106
|
-
},
|
|
107
|
-
});
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
function toChannelMessageSendResult(
|
|
111
|
-
entry: OutboxEntry | undefined,
|
|
112
|
-
now?: () => number,
|
|
113
|
-
): ChannelMessageSendResult {
|
|
114
|
-
if (!entry) throw new Error('bncr durable message adapter did not receive an outbox entry');
|
|
115
|
-
const queued = buildBncrDurableQueuedResult({ entry, sentAt: now?.() });
|
|
116
|
-
const receipt: ChannelMessageSendResult['receipt'] = queued.receipt;
|
|
117
|
-
return {
|
|
118
|
-
receipt,
|
|
119
|
-
messageId: queued.receipt.primaryPlatformMessageId,
|
|
120
|
-
};
|
|
121
|
-
}
|
|
@@ -1,114 +0,0 @@
|
|
|
1
|
-
import { normalizeAccountId } from '../../core/accounts.ts';
|
|
2
|
-
import { readOpenClawBooleanParam, readOpenClawStringParam } from '../../openclaw/sdk-helpers.ts';
|
|
3
|
-
import { extractConsumptionFields, parseBncrMarker } from './marker-parser.ts';
|
|
4
|
-
|
|
5
|
-
export type NormalizedBncrSendParams = {
|
|
6
|
-
to: string;
|
|
7
|
-
accountId: string;
|
|
8
|
-
message: string;
|
|
9
|
-
caption: string;
|
|
10
|
-
mediaUrl?: string;
|
|
11
|
-
mediaUrls?: string[];
|
|
12
|
-
asVoice: boolean;
|
|
13
|
-
audioAsVoice: boolean;
|
|
14
|
-
type?: string;
|
|
15
|
-
downloadMedia?: boolean;
|
|
16
|
-
extra?: Record<string, unknown>;
|
|
17
|
-
};
|
|
18
|
-
|
|
19
|
-
function isPlainObject(value: unknown): value is Record<string, unknown> {
|
|
20
|
-
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
export function normalizeBncrSendParams(input: {
|
|
24
|
-
params: unknown;
|
|
25
|
-
accountId: string;
|
|
26
|
-
}): NormalizedBncrSendParams {
|
|
27
|
-
const paramsObj = isPlainObject(input.params) ? input.params : {};
|
|
28
|
-
const to = readOpenClawStringParam(paramsObj, 'to', { required: true });
|
|
29
|
-
const resolvedAccountId = normalizeAccountId(
|
|
30
|
-
readOpenClawStringParam(paramsObj, 'accountId') ?? input.accountId,
|
|
31
|
-
);
|
|
32
|
-
|
|
33
|
-
const rawMessage = readOpenClawStringParam(paramsObj, 'message', { allowEmpty: true }) ?? '';
|
|
34
|
-
const rawCaption = readOpenClawStringParam(paramsObj, 'caption', { allowEmpty: true }) ?? '';
|
|
35
|
-
// Parse [BncrParam:...] markers from message and caption text
|
|
36
|
-
const { cleanText: parsedMessage, params: msgMarker } = parseBncrMarker(rawMessage);
|
|
37
|
-
const { cleanText: parsedCaption, params: capMarker } = parseBncrMarker(rawCaption);
|
|
38
|
-
const message = parsedMessage;
|
|
39
|
-
const caption = parsedCaption;
|
|
40
|
-
const mediaUrl =
|
|
41
|
-
readOpenClawStringParam(paramsObj, 'media', { trim: false }) ??
|
|
42
|
-
readOpenClawStringParam(paramsObj, 'path', { trim: false }) ??
|
|
43
|
-
readOpenClawStringParam(paramsObj, 'filePath', { trim: false }) ??
|
|
44
|
-
readOpenClawStringParam(paramsObj, 'mediaUrl', { trim: false });
|
|
45
|
-
const rawMediaUrls = paramsObj.mediaUrls;
|
|
46
|
-
const mediaUrls = Array.isArray(rawMediaUrls)
|
|
47
|
-
? Array.from(
|
|
48
|
-
new Set(rawMediaUrls.map((v) => (typeof v === 'string' ? v.trim() : '')).filter(Boolean)),
|
|
49
|
-
)
|
|
50
|
-
: undefined;
|
|
51
|
-
// 如果 mediaUrl 已经在 mediaUrls 中,去重避免重复发送
|
|
52
|
-
const dedupedMediaUrls = mediaUrls?.length
|
|
53
|
-
? mediaUrl && mediaUrls.includes(mediaUrl)
|
|
54
|
-
? mediaUrls
|
|
55
|
-
: mediaUrl
|
|
56
|
-
? [mediaUrl, ...mediaUrls]
|
|
57
|
-
: mediaUrls
|
|
58
|
-
: undefined;
|
|
59
|
-
const asVoice = readOpenClawBooleanParam(paramsObj, 'asVoice') ?? false;
|
|
60
|
-
const audioAsVoice = readOpenClawBooleanParam(paramsObj, 'audioAsVoice') ?? false;
|
|
61
|
-
const type = readOpenClawStringParam(paramsObj, 'type') || undefined;
|
|
62
|
-
const rawExtra = paramsObj.extra;
|
|
63
|
-
const paramsExtra = isPlainObject(rawExtra) ? { ...rawExtra } : {};
|
|
64
|
-
// Merge marker params into extra (marker takes priority over direct extra)
|
|
65
|
-
const mergedExtra = { ...paramsExtra, ...msgMarker, ...capMarker };
|
|
66
|
-
const extra = Object.keys(mergedExtra).length > 0 ? mergedExtra : undefined;
|
|
67
|
-
|
|
68
|
-
// Extract consumption fields from merged extra - marker-specified values override params
|
|
69
|
-
const { consumed, remaining } = extractConsumptionFields(
|
|
70
|
-
Object.keys(mergedExtra).length > 0 ? mergedExtra : undefined,
|
|
71
|
-
);
|
|
72
|
-
const effectiveAsVoice = consumed.asVoice ?? asVoice;
|
|
73
|
-
const effectiveAudioAsVoice = consumed.audioAsVoice ?? audioAsVoice;
|
|
74
|
-
const effectiveDownloadMedia = consumed.downloadMedia === true;
|
|
75
|
-
const effectiveType = consumed.type ?? type;
|
|
76
|
-
const cleanedExtra = Object.keys(remaining).length > 0 ? remaining : undefined;
|
|
77
|
-
|
|
78
|
-
const hasMedia = Boolean(mediaUrl || dedupedMediaUrls?.length);
|
|
79
|
-
|
|
80
|
-
if (effectiveAsVoice && !hasMedia) throw new Error('send voice requires media path');
|
|
81
|
-
|
|
82
|
-
const normalizedMessage = hasMedia ? '' : message || caption || '';
|
|
83
|
-
const normalizedCaption = hasMedia ? caption || message || '' : '';
|
|
84
|
-
|
|
85
|
-
if (!normalizedMessage.trim() && !normalizedCaption.trim() && !hasMedia && !extra) {
|
|
86
|
-
throw new Error('send requires message, media, or extra params');
|
|
87
|
-
}
|
|
88
|
-
console.log(
|
|
89
|
-
'[bncr] normalizeBncrSendParams rawMessage=' +
|
|
90
|
-
JSON.stringify(rawMessage) +
|
|
91
|
-
'|msgMarker=' +
|
|
92
|
-
JSON.stringify(msgMarker) +
|
|
93
|
-
'|consumed=' +
|
|
94
|
-
JSON.stringify(consumed) +
|
|
95
|
-
'|effectiveDownloadMedia=' +
|
|
96
|
-
effectiveDownloadMedia +
|
|
97
|
-
'|mediaUrl=' +
|
|
98
|
-
JSON.stringify(mediaUrl),
|
|
99
|
-
);
|
|
100
|
-
|
|
101
|
-
return {
|
|
102
|
-
to,
|
|
103
|
-
accountId: resolvedAccountId,
|
|
104
|
-
message: normalizedMessage,
|
|
105
|
-
caption: normalizedCaption,
|
|
106
|
-
mediaUrl: dedupedMediaUrls?.length ? undefined : mediaUrl || undefined,
|
|
107
|
-
mediaUrls: dedupedMediaUrls,
|
|
108
|
-
asVoice: effectiveAsVoice,
|
|
109
|
-
...(effectiveDownloadMedia ? { downloadMedia: true } : {}),
|
|
110
|
-
audioAsVoice: effectiveAudioAsVoice,
|
|
111
|
-
...(effectiveType ? { type: effectiveType } : {}),
|
|
112
|
-
...(cleanedExtra ? { extra: cleanedExtra } : {}),
|
|
113
|
-
};
|
|
114
|
-
}
|
|
@@ -1,127 +0,0 @@
|
|
|
1
|
-
import type { BncrRoute } from '../../core/types.ts';
|
|
2
|
-
|
|
3
|
-
function normalizeReplyKind(value: unknown): 'tool' | 'block' | 'final' | undefined {
|
|
4
|
-
return value === 'tool' || value === 'block' || value === 'final' ? value : undefined;
|
|
5
|
-
}
|
|
6
|
-
|
|
7
|
-
export async function sendBncrText(params: {
|
|
8
|
-
channelId: string;
|
|
9
|
-
extra?: Record<string, unknown>;
|
|
10
|
-
accountId: string;
|
|
11
|
-
to: string;
|
|
12
|
-
text: string;
|
|
13
|
-
kind?: string;
|
|
14
|
-
replyToId?: string;
|
|
15
|
-
mediaLocalRoots?: readonly string[];
|
|
16
|
-
resolveVerifiedTarget: (
|
|
17
|
-
to: string,
|
|
18
|
-
accountId: string,
|
|
19
|
-
) => { sessionKey: string; route: BncrRoute; displayScope: string };
|
|
20
|
-
rememberSessionRoute: (sessionKey: string, accountId: string, route: BncrRoute) => void;
|
|
21
|
-
enqueueFromReply: (args: {
|
|
22
|
-
accountId: string;
|
|
23
|
-
sessionKey: string;
|
|
24
|
-
route: BncrRoute;
|
|
25
|
-
payload: {
|
|
26
|
-
text?: string;
|
|
27
|
-
mediaUrl?: string;
|
|
28
|
-
mediaUrls?: string[];
|
|
29
|
-
kind?: 'tool' | 'block' | 'final';
|
|
30
|
-
extra?: Record<string, unknown>;
|
|
31
|
-
replyToId?: string;
|
|
32
|
-
};
|
|
33
|
-
mediaLocalRoots?: readonly string[];
|
|
34
|
-
}) => Promise<void>;
|
|
35
|
-
createMessageId: () => string;
|
|
36
|
-
}) {
|
|
37
|
-
const verified = params.resolveVerifiedTarget(params.to, params.accountId);
|
|
38
|
-
params.rememberSessionRoute(verified.sessionKey, params.accountId, verified.route);
|
|
39
|
-
|
|
40
|
-
await params.enqueueFromReply({
|
|
41
|
-
accountId: params.accountId,
|
|
42
|
-
sessionKey: verified.sessionKey,
|
|
43
|
-
route: verified.route,
|
|
44
|
-
payload: {
|
|
45
|
-
text: params.text,
|
|
46
|
-
kind: normalizeReplyKind(params.kind),
|
|
47
|
-
replyToId: params.replyToId,
|
|
48
|
-
extra: params.extra,
|
|
49
|
-
},
|
|
50
|
-
mediaLocalRoots: params.mediaLocalRoots,
|
|
51
|
-
});
|
|
52
|
-
|
|
53
|
-
return {
|
|
54
|
-
channel: params.channelId,
|
|
55
|
-
messageId: params.createMessageId(),
|
|
56
|
-
chatId: verified.sessionKey,
|
|
57
|
-
};
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
export async function sendBncrMedia(params: {
|
|
61
|
-
channelId: string;
|
|
62
|
-
accountId: string;
|
|
63
|
-
to: string;
|
|
64
|
-
text?: string;
|
|
65
|
-
mediaUrl?: string;
|
|
66
|
-
mediaUrls?: string[];
|
|
67
|
-
asVoice?: boolean;
|
|
68
|
-
audioAsVoice?: boolean;
|
|
69
|
-
type?: string;
|
|
70
|
-
downloadMedia?: boolean;
|
|
71
|
-
extra?: Record<string, unknown>;
|
|
72
|
-
kind?: string;
|
|
73
|
-
replyToId?: string;
|
|
74
|
-
mediaLocalRoots?: readonly string[];
|
|
75
|
-
resolveVerifiedTarget: (
|
|
76
|
-
to: string,
|
|
77
|
-
accountId: string,
|
|
78
|
-
) => { sessionKey: string; route: BncrRoute; displayScope: string };
|
|
79
|
-
rememberSessionRoute: (sessionKey: string, accountId: string, route: BncrRoute) => void;
|
|
80
|
-
enqueueFromReply: (args: {
|
|
81
|
-
accountId: string;
|
|
82
|
-
sessionKey: string;
|
|
83
|
-
route: BncrRoute;
|
|
84
|
-
payload: {
|
|
85
|
-
text?: string;
|
|
86
|
-
mediaUrl?: string;
|
|
87
|
-
mediaUrls?: string[];
|
|
88
|
-
asVoice?: boolean;
|
|
89
|
-
audioAsVoice?: boolean;
|
|
90
|
-
type?: string;
|
|
91
|
-
downloadMedia?: boolean;
|
|
92
|
-
extra?: Record<string, unknown>;
|
|
93
|
-
kind?: 'tool' | 'block' | 'final';
|
|
94
|
-
replyToId?: string;
|
|
95
|
-
};
|
|
96
|
-
mediaLocalRoots?: readonly string[];
|
|
97
|
-
}) => Promise<void>;
|
|
98
|
-
createMessageId: () => string;
|
|
99
|
-
}) {
|
|
100
|
-
const verified = params.resolveVerifiedTarget(params.to, params.accountId);
|
|
101
|
-
params.rememberSessionRoute(verified.sessionKey, params.accountId, verified.route);
|
|
102
|
-
|
|
103
|
-
await params.enqueueFromReply({
|
|
104
|
-
accountId: params.accountId,
|
|
105
|
-
sessionKey: verified.sessionKey,
|
|
106
|
-
route: verified.route,
|
|
107
|
-
payload: {
|
|
108
|
-
text: params.text || '',
|
|
109
|
-
mediaUrl: params.mediaUrl || '',
|
|
110
|
-
mediaUrls: params.mediaUrls?.length ? params.mediaUrls : undefined,
|
|
111
|
-
asVoice: params.asVoice === true ? true : undefined,
|
|
112
|
-
audioAsVoice: params.audioAsVoice === true ? true : undefined,
|
|
113
|
-
downloadMedia: params.downloadMedia === true ? true : undefined,
|
|
114
|
-
type: params.type,
|
|
115
|
-
extra: params.extra,
|
|
116
|
-
kind: normalizeReplyKind(params.kind),
|
|
117
|
-
replyToId: params.replyToId,
|
|
118
|
-
},
|
|
119
|
-
mediaLocalRoots: params.mediaLocalRoots,
|
|
120
|
-
});
|
|
121
|
-
|
|
122
|
-
return {
|
|
123
|
-
channel: params.channelId,
|
|
124
|
-
messageId: params.createMessageId(),
|
|
125
|
-
chatId: verified.sessionKey,
|
|
126
|
-
};
|
|
127
|
-
}
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
defineStableChannelIngressIdentity as sdkDefineStableChannelIngressIdentity,
|
|
3
|
-
resolveChannelMessageIngress as sdkResolveChannelMessageIngress,
|
|
4
|
-
} from 'openclaw/plugin-sdk/channel-ingress-runtime';
|
|
5
|
-
|
|
6
|
-
type StableChannelIngressIdentityParams = Parameters<
|
|
7
|
-
typeof sdkDefineStableChannelIngressIdentity
|
|
8
|
-
>[0];
|
|
9
|
-
|
|
10
|
-
type ResolveChannelMessageIngressParams = Parameters<typeof sdkResolveChannelMessageIngress>[0];
|
|
11
|
-
type ResolveChannelMessageIngressResult = Awaited<
|
|
12
|
-
ReturnType<typeof sdkResolveChannelMessageIngress>
|
|
13
|
-
>;
|
|
14
|
-
|
|
15
|
-
export function defineOpenClawStableChannelIngressIdentity(
|
|
16
|
-
params: StableChannelIngressIdentityParams,
|
|
17
|
-
) {
|
|
18
|
-
return sdkDefineStableChannelIngressIdentity(params);
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
export async function resolveOpenClawChannelMessageIngress(
|
|
22
|
-
params: ResolveChannelMessageIngressParams,
|
|
23
|
-
): Promise<ResolveChannelMessageIngressResult> {
|
|
24
|
-
return sdkResolveChannelMessageIngress(params);
|
|
25
|
-
}
|
|
@@ -1,142 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
buildFileTransferPushOkArgs,
|
|
3
|
-
buildFileTransferPushSuccessArgs,
|
|
4
|
-
} from '../core/outbox-file-transfer-bookkeeping.ts';
|
|
5
|
-
import {
|
|
6
|
-
buildFileTransferBroadcastPayload,
|
|
7
|
-
buildFileTransferRouteSelectArgs,
|
|
8
|
-
} from '../core/outbox-file-transfer-success.ts';
|
|
9
|
-
import type { BncrConnection, BncrRoute, OutboxEntry } from '../core/types.ts';
|
|
10
|
-
|
|
11
|
-
export function createBncrBridgeFileTransferPushFacade(runtime: {
|
|
12
|
-
pushEvent: string;
|
|
13
|
-
getGatewayContext: () => {
|
|
14
|
-
broadcastToConnIds: (event: string, payload: unknown, connIds: ReadonlySet<string>) => void;
|
|
15
|
-
} | null;
|
|
16
|
-
transferMediaToBncrClient: (params: {
|
|
17
|
-
accountId: string;
|
|
18
|
-
sessionKey: string;
|
|
19
|
-
route: BncrRoute;
|
|
20
|
-
mediaUrl: string;
|
|
21
|
-
mediaLocalRoots?: readonly string[];
|
|
22
|
-
downloadMedia?: boolean;
|
|
23
|
-
}) => Promise<{
|
|
24
|
-
mode: 'base64' | 'chunk';
|
|
25
|
-
mimeType?: string;
|
|
26
|
-
fileName?: string;
|
|
27
|
-
base64?: string;
|
|
28
|
-
path?: string;
|
|
29
|
-
}>;
|
|
30
|
-
buildFileTransferOutboundFrame: (params: {
|
|
31
|
-
entry: OutboxEntry;
|
|
32
|
-
meta: Record<string, unknown>;
|
|
33
|
-
media: { fileName?: string; mimeType?: string; path?: string; base64?: string; type?: string };
|
|
34
|
-
mediaUrl: string;
|
|
35
|
-
}) => Record<string, unknown>;
|
|
36
|
-
logOutboxRouteSelect: (args: {
|
|
37
|
-
messageId: string;
|
|
38
|
-
accountId: string;
|
|
39
|
-
kind?: 'file-transfer';
|
|
40
|
-
routeReason: string;
|
|
41
|
-
connIds: Iterable<string>;
|
|
42
|
-
ownerConnId: string;
|
|
43
|
-
ownerClientId: string;
|
|
44
|
-
recentInboundReachable: boolean;
|
|
45
|
-
event: string;
|
|
46
|
-
}) => void;
|
|
47
|
-
recordOutboxPushSuccess: (args: {
|
|
48
|
-
entry: OutboxEntry;
|
|
49
|
-
connIds: Iterable<string>;
|
|
50
|
-
ownerConnId?: string;
|
|
51
|
-
ownerClientId?: string;
|
|
52
|
-
clearLastError?: boolean;
|
|
53
|
-
}) => void;
|
|
54
|
-
logOutboxPushOkSummary: (messageId: string) => void;
|
|
55
|
-
logOutboxPushOk: (args: {
|
|
56
|
-
messageId: string;
|
|
57
|
-
accountId: string;
|
|
58
|
-
kind?: 'file-transfer';
|
|
59
|
-
connIds: Iterable<string>;
|
|
60
|
-
ownerConnId: string;
|
|
61
|
-
ownerClientId: string;
|
|
62
|
-
recentInboundReachable: boolean;
|
|
63
|
-
event: string;
|
|
64
|
-
}) => void;
|
|
65
|
-
handleFileTransferPushFailure: (args: { entry: OutboxEntry; error: unknown }) => void;
|
|
66
|
-
handleFileTransferPushGuardFailure: (args: {
|
|
67
|
-
entry: OutboxEntry;
|
|
68
|
-
guard: {
|
|
69
|
-
reason: 'media-url-missing' | 'no-gateway-context' | 'no-active-connection';
|
|
70
|
-
lastError: string;
|
|
71
|
-
recentInboundReachable?: boolean;
|
|
72
|
-
};
|
|
73
|
-
}) => void;
|
|
74
|
-
}) {
|
|
75
|
-
const pushFileTransferSuccessPath = async (args: {
|
|
76
|
-
entry: OutboxEntry;
|
|
77
|
-
meta: Record<string, unknown>;
|
|
78
|
-
owner: BncrConnection | null;
|
|
79
|
-
connIds: Iterable<string>;
|
|
80
|
-
recentInboundReachable: boolean;
|
|
81
|
-
routeReason: string;
|
|
82
|
-
mediaUrl: string;
|
|
83
|
-
}): Promise<void> => {
|
|
84
|
-
const media = await runtime.transferMediaToBncrClient({
|
|
85
|
-
accountId: args.entry.accountId,
|
|
86
|
-
sessionKey: args.entry.sessionKey,
|
|
87
|
-
route: args.entry.route,
|
|
88
|
-
mediaUrl: args.mediaUrl,
|
|
89
|
-
mediaLocalRoots: Array.isArray(args.meta.mediaLocalRoots)
|
|
90
|
-
? args.meta.mediaLocalRoots.filter((v): v is string => typeof v === 'string')
|
|
91
|
-
: undefined,
|
|
92
|
-
downloadMedia: args.meta.downloadMedia === true,
|
|
93
|
-
});
|
|
94
|
-
const frame = runtime.buildFileTransferOutboundFrame({
|
|
95
|
-
entry: args.entry,
|
|
96
|
-
meta: args.meta,
|
|
97
|
-
media,
|
|
98
|
-
mediaUrl: args.mediaUrl,
|
|
99
|
-
});
|
|
100
|
-
|
|
101
|
-
runtime.getGatewayContext()!.broadcastToConnIds(
|
|
102
|
-
runtime.pushEvent,
|
|
103
|
-
buildFileTransferBroadcastPayload({
|
|
104
|
-
frame,
|
|
105
|
-
messageId: args.entry.messageId,
|
|
106
|
-
}),
|
|
107
|
-
new Set(args.connIds),
|
|
108
|
-
);
|
|
109
|
-
runtime.logOutboxRouteSelect(
|
|
110
|
-
buildFileTransferRouteSelectArgs({
|
|
111
|
-
entry: args.entry,
|
|
112
|
-
connIds: args.connIds,
|
|
113
|
-
routeReason: args.routeReason,
|
|
114
|
-
recentInboundReachable: args.recentInboundReachable,
|
|
115
|
-
owner: args.owner,
|
|
116
|
-
event: runtime.pushEvent,
|
|
117
|
-
}),
|
|
118
|
-
);
|
|
119
|
-
runtime.recordOutboxPushSuccess(
|
|
120
|
-
buildFileTransferPushSuccessArgs({
|
|
121
|
-
entry: args.entry,
|
|
122
|
-
connIds: args.connIds,
|
|
123
|
-
owner: args.owner,
|
|
124
|
-
}),
|
|
125
|
-
);
|
|
126
|
-
runtime.logOutboxPushOkSummary(args.entry.messageId);
|
|
127
|
-
runtime.logOutboxPushOk(
|
|
128
|
-
buildFileTransferPushOkArgs({
|
|
129
|
-
entry: args.entry,
|
|
130
|
-
connIds: args.connIds,
|
|
131
|
-
recentInboundReachable: args.recentInboundReachable,
|
|
132
|
-
event: runtime.pushEvent,
|
|
133
|
-
}),
|
|
134
|
-
);
|
|
135
|
-
};
|
|
136
|
-
|
|
137
|
-
return {
|
|
138
|
-
pushFileTransferSuccessPath,
|
|
139
|
-
handleFileTransferPushFailure: runtime.handleFileTransferPushFailure,
|
|
140
|
-
handleFileTransferPushGuardFailure: runtime.handleFileTransferPushGuardFailure,
|
|
141
|
-
};
|
|
142
|
-
}
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
import type { BncrRoute } from '../core/types.ts';
|
|
2
|
-
import { createBncrChannelSendRuntimeComponent } from './channel-components.ts';
|
|
3
|
-
|
|
4
|
-
export function createBncrChannelSendRuntimeGroup(runtime: {
|
|
5
|
-
channelId: string;
|
|
6
|
-
asString: (value: unknown, fallback?: string) => string;
|
|
7
|
-
syncDebugFlag: () => Promise<void>;
|
|
8
|
-
logInfo: (scope: string, message: string, options?: Record<string, unknown>) => void;
|
|
9
|
-
resolveVerifiedTarget: (
|
|
10
|
-
to: string,
|
|
11
|
-
accountId: string,
|
|
12
|
-
) => {
|
|
13
|
-
sessionKey: string;
|
|
14
|
-
route: BncrRoute;
|
|
15
|
-
displayScope: string;
|
|
16
|
-
};
|
|
17
|
-
rememberSessionRoute: (sessionKey: string, accountId: string, route: BncrRoute) => void;
|
|
18
|
-
enqueueFromReply: Parameters<typeof createBncrChannelSendRuntimeComponent>[0]['enqueueFromReply'];
|
|
19
|
-
listOutboxEntries: Parameters<
|
|
20
|
-
typeof createBncrChannelSendRuntimeComponent
|
|
21
|
-
>[0]['listOutboxEntries'];
|
|
22
|
-
}) {
|
|
23
|
-
const channelSendRuntime = createBncrChannelSendRuntimeComponent({
|
|
24
|
-
channelId: runtime.channelId,
|
|
25
|
-
asString: runtime.asString,
|
|
26
|
-
syncDebugFlag: runtime.syncDebugFlag,
|
|
27
|
-
logInfo: runtime.logInfo,
|
|
28
|
-
resolveVerifiedTarget: runtime.resolveVerifiedTarget,
|
|
29
|
-
rememberSessionRoute: runtime.rememberSessionRoute,
|
|
30
|
-
enqueueFromReply: runtime.enqueueFromReply,
|
|
31
|
-
listOutboxEntries: runtime.listOutboxEntries,
|
|
32
|
-
});
|
|
33
|
-
|
|
34
|
-
return {
|
|
35
|
-
channelSendRuntime,
|
|
36
|
-
};
|
|
37
|
-
}
|
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
import { resolveFileTransferGuard } from '../core/outbox-file-transfer-guards.ts';
|
|
2
|
-
import { prepareFileTransferRouteSelection } from '../core/outbox-file-transfer-prep.ts';
|
|
3
|
-
import type { BncrConnection, OutboxEntry } from '../core/types.ts';
|
|
4
|
-
import { selectOutboxFileTransferRouteCandidates } from '../messaging/outbound/queue-selectors.ts';
|
|
5
|
-
|
|
6
|
-
export async function runBncrFileTransferOutboxPush(args: {
|
|
7
|
-
entry: OutboxEntry;
|
|
8
|
-
meta: Record<string, unknown>;
|
|
9
|
-
gatewayContext: unknown;
|
|
10
|
-
owner: BncrConnection | null;
|
|
11
|
-
resolvePushConnIds: (accountId: string) => Set<string>;
|
|
12
|
-
resolveRecentInboundConnIds: (accountId: string) => Set<string>;
|
|
13
|
-
hasRecentInboundReachability: (accountId: string) => boolean;
|
|
14
|
-
isRevalidatedAttemptedConn: (connId: string) => boolean;
|
|
15
|
-
handleFileTransferPushGuardFailure: (args: {
|
|
16
|
-
entry: OutboxEntry;
|
|
17
|
-
guard: Exclude<ReturnType<typeof resolveFileTransferGuard>, { ok: true }>;
|
|
18
|
-
}) => void;
|
|
19
|
-
pushFileTransferSuccessPath: (args: {
|
|
20
|
-
entry: OutboxEntry;
|
|
21
|
-
meta: Record<string, unknown>;
|
|
22
|
-
owner: BncrConnection | null;
|
|
23
|
-
connIds: Iterable<string>;
|
|
24
|
-
recentInboundReachable: boolean;
|
|
25
|
-
routeReason: string;
|
|
26
|
-
mediaUrl: string;
|
|
27
|
-
}) => Promise<void>;
|
|
28
|
-
handleFileTransferPushFailure: (args: { entry: OutboxEntry; error: unknown }) => void;
|
|
29
|
-
}) {
|
|
30
|
-
const selection = prepareFileTransferRouteSelection({
|
|
31
|
-
entry: args.entry,
|
|
32
|
-
owner: args.owner,
|
|
33
|
-
resolvePushConnIds: args.resolvePushConnIds,
|
|
34
|
-
resolveRecentInboundConnIds: args.resolveRecentInboundConnIds,
|
|
35
|
-
hasRecentInboundReachability: args.hasRecentInboundReachability,
|
|
36
|
-
isRevalidatedAttemptedConn: args.isRevalidatedAttemptedConn,
|
|
37
|
-
selectOutboxFileTransferRouteCandidates,
|
|
38
|
-
});
|
|
39
|
-
const guard = resolveFileTransferGuard({
|
|
40
|
-
gatewayContext: args.gatewayContext,
|
|
41
|
-
entry: args.entry,
|
|
42
|
-
owner: args.owner,
|
|
43
|
-
routeSelection: selection,
|
|
44
|
-
mediaUrl: String(args.meta.mediaUrl || '').trim(),
|
|
45
|
-
});
|
|
46
|
-
if (!guard.ok) {
|
|
47
|
-
args.handleFileTransferPushGuardFailure({
|
|
48
|
-
entry: args.entry,
|
|
49
|
-
guard,
|
|
50
|
-
});
|
|
51
|
-
return false;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
try {
|
|
55
|
-
await args.pushFileTransferSuccessPath({
|
|
56
|
-
entry: args.entry,
|
|
57
|
-
meta: args.meta,
|
|
58
|
-
owner: args.owner,
|
|
59
|
-
connIds: guard.connIds,
|
|
60
|
-
recentInboundReachable: guard.recentInboundReachable,
|
|
61
|
-
routeReason: guard.routeReason,
|
|
62
|
-
mediaUrl: guard.mediaUrl,
|
|
63
|
-
});
|
|
64
|
-
return true;
|
|
65
|
-
} catch (error) {
|
|
66
|
-
args.handleFileTransferPushFailure({ entry: args.entry, error });
|
|
67
|
-
return false;
|
|
68
|
-
}
|
|
69
|
-
}
|