@xmoxmo/bncr 0.1.7 → 0.1.9
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/index.ts +18 -13
- package/package.json +1 -1
- package/src/channel.ts +305 -214
- package/src/core/logging.ts +65 -0
- package/src/messaging/inbound/commands.ts +7 -2
- package/src/messaging/inbound/dispatch.ts +4 -3
- package/src/messaging/outbound/media.ts +2 -0
- package/src/messaging/outbound/send.ts +11 -1
package/index.ts
CHANGED
|
@@ -4,6 +4,7 @@ import { createRequire } from 'node:module';
|
|
|
4
4
|
import path from 'node:path';
|
|
5
5
|
import { fileURLToPath } from 'node:url';
|
|
6
6
|
import { BncrConfigSchema } from './src/core/config-schema.ts';
|
|
7
|
+
import { emitBncrLogLine } from './src/core/logging.ts';
|
|
7
8
|
|
|
8
9
|
const pluginFile = fileURLToPath(import.meta.url);
|
|
9
10
|
const pluginDir = path.dirname(pluginFile);
|
|
@@ -254,12 +255,12 @@ const ensureGatewayMethodRegistered = (
|
|
|
254
255
|
) => {
|
|
255
256
|
const meta = getRegisterMeta(api);
|
|
256
257
|
if (meta.methods?.has(name)) {
|
|
257
|
-
debugLog(`
|
|
258
|
+
debugLog(`register method skip ${name} (already registered on this api)`);
|
|
258
259
|
return;
|
|
259
260
|
}
|
|
260
261
|
api.registerGatewayMethod(name, handler);
|
|
261
262
|
meta.methods?.add(name);
|
|
262
|
-
debugLog(`
|
|
263
|
+
debugLog(`register method ok ${name}`);
|
|
263
264
|
};
|
|
264
265
|
|
|
265
266
|
const getBridgeSingleton = (api: OpenClawPluginApi) => {
|
|
@@ -291,14 +292,18 @@ const plugin = {
|
|
|
291
292
|
registryFingerprint: meta.registryFingerprint,
|
|
292
293
|
});
|
|
293
294
|
const debugLog = (...args: any[]) => {
|
|
294
|
-
|
|
295
|
-
|
|
295
|
+
const rendered = args
|
|
296
|
+
.map((arg) => (typeof arg === 'string' ? arg : JSON.stringify(arg)))
|
|
297
|
+
.join(' ')
|
|
298
|
+
.trim();
|
|
299
|
+
if (!rendered) return;
|
|
300
|
+
emitBncrLogLine('info', `[bncr] debug ${rendered}`, { debugOnly: true }, () =>
|
|
301
|
+
Boolean(bridge.isDebugEnabled?.()),
|
|
302
|
+
);
|
|
296
303
|
};
|
|
297
304
|
|
|
298
|
-
debugLog(
|
|
299
|
-
|
|
300
|
-
);
|
|
301
|
-
if (!created) debugLog('bncr bridge api rebound');
|
|
305
|
+
debugLog(`register begin bridge=${bridge.getBridgeId?.() || 'unknown'} created=${created}`);
|
|
306
|
+
if (!created) debugLog('bridge api rebound');
|
|
302
307
|
|
|
303
308
|
const resolveDebug = async () => {
|
|
304
309
|
try {
|
|
@@ -319,17 +324,17 @@ const plugin = {
|
|
|
319
324
|
stop: bridge.stopService,
|
|
320
325
|
});
|
|
321
326
|
meta.service = true;
|
|
322
|
-
debugLog('
|
|
327
|
+
debugLog('register service ok');
|
|
323
328
|
} else {
|
|
324
|
-
debugLog('
|
|
329
|
+
debugLog('register service skip (already registered on this api)');
|
|
325
330
|
}
|
|
326
331
|
|
|
327
332
|
if (!meta.channel) {
|
|
328
333
|
api.registerChannel({ plugin: runtime.createBncrChannelPlugin(bridge) });
|
|
329
334
|
meta.channel = true;
|
|
330
|
-
debugLog('
|
|
335
|
+
debugLog('register channel ok');
|
|
331
336
|
} else {
|
|
332
|
-
debugLog('
|
|
337
|
+
debugLog('register channel skip (already registered on this api)');
|
|
333
338
|
}
|
|
334
339
|
|
|
335
340
|
ensureGatewayMethodRegistered(
|
|
@@ -387,7 +392,7 @@ const plugin = {
|
|
|
387
392
|
(opts) => bridge.handleFileAck(opts),
|
|
388
393
|
debugLog,
|
|
389
394
|
);
|
|
390
|
-
debugLog('
|
|
395
|
+
debugLog('register done');
|
|
391
396
|
},
|
|
392
397
|
};
|
|
393
398
|
|
package/package.json
CHANGED
package/src/channel.ts
CHANGED
|
@@ -26,6 +26,7 @@ import {
|
|
|
26
26
|
resolveDefaultDisplayName,
|
|
27
27
|
} from './core/accounts.ts';
|
|
28
28
|
import { BncrConfigSchema } from './core/config-schema.ts';
|
|
29
|
+
import { emitBncrLog, emitBncrLogLine } from './core/logging.ts';
|
|
29
30
|
import { buildBncrPermissionSummary } from './core/permissions.ts';
|
|
30
31
|
import { resolveBncrChannelPolicy } from './core/policy.ts';
|
|
31
32
|
import { probeBncrAccount } from './core/probe.ts';
|
|
@@ -367,6 +368,51 @@ class BncrBridgeRuntime {
|
|
|
367
368
|
return this.bridgeId;
|
|
368
369
|
}
|
|
369
370
|
|
|
371
|
+
private logInfo(scope: string | undefined, message: string, options?: { debugOnly?: boolean }) {
|
|
372
|
+
emitBncrLog('info', scope, message, options, () => this.isDebugEnabled());
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
private logWarn(scope: string | undefined, message: string, options?: { debugOnly?: boolean }) {
|
|
376
|
+
emitBncrLog('warn', scope, message, options, () => this.isDebugEnabled());
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
private logError(scope: string | undefined, message: string, options?: { debugOnly?: boolean }) {
|
|
380
|
+
emitBncrLog('error', scope, message, options, () => this.isDebugEnabled());
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
private summarizeTextPreview(raw: string, limit = 8) {
|
|
384
|
+
const compact = asString(raw || '')
|
|
385
|
+
.replace(/\s+/g, ' ')
|
|
386
|
+
.trim();
|
|
387
|
+
if (!compact) return '-';
|
|
388
|
+
const chars = Array.from(compact);
|
|
389
|
+
return chars.length > limit ? `${chars.slice(0, Math.max(1, limit)).join('')}…` : compact;
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
private summarizeScope(route: BncrRoute) {
|
|
393
|
+
return formatDisplayScope(route);
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
private logInboundSummary(params: {
|
|
397
|
+
accountId: string;
|
|
398
|
+
route: BncrRoute;
|
|
399
|
+
msgType: string;
|
|
400
|
+
text: string;
|
|
401
|
+
hasMedia: boolean;
|
|
402
|
+
}) {
|
|
403
|
+
const type = params.hasMedia ? `${params.msgType}+media` : params.msgType;
|
|
404
|
+
const preview = this.summarizeTextPreview(params.text);
|
|
405
|
+
this.logInfo('inbound', [type, this.summarizeScope(params.route), preview].join('|'));
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
private logOutboundSummary(entry: OutboxEntry) {
|
|
409
|
+
const msg = (entry.payload as any)?.message || {};
|
|
410
|
+
const type = asString(msg.type || (entry.payload as any)?.type || 'unknown');
|
|
411
|
+
const text = asString(msg.msg || '');
|
|
412
|
+
const preview = this.summarizeTextPreview(text);
|
|
413
|
+
this.logInfo('outbound', [type, this.summarizeScope(entry.route), preview].join('|'));
|
|
414
|
+
}
|
|
415
|
+
|
|
370
416
|
private classifyRegisterTrace(stack: string) {
|
|
371
417
|
if (
|
|
372
418
|
stack.includes('prepareSecretsRuntimeSnapshot') ||
|
|
@@ -509,9 +555,7 @@ class BncrBridgeRuntime {
|
|
|
509
555
|
const summary = this.buildRegisterTraceSummary();
|
|
510
556
|
if (summary.postWarmupRegisterCount > 0) this.captureDriftSnapshot(summary);
|
|
511
557
|
|
|
512
|
-
|
|
513
|
-
this.api.logger.info?.(`[bncr-register-trace] ${JSON.stringify(trace)}`);
|
|
514
|
-
}
|
|
558
|
+
this.logInfo('debug', `register-trace ${JSON.stringify(trace)}`, { debugOnly: true });
|
|
515
559
|
}
|
|
516
560
|
|
|
517
561
|
private createLeaseId() {
|
|
@@ -595,8 +639,10 @@ class BncrBridgeRuntime {
|
|
|
595
639
|
this.staleCounters.staleFileAbort += 1;
|
|
596
640
|
break;
|
|
597
641
|
}
|
|
598
|
-
this.
|
|
599
|
-
|
|
642
|
+
this.logWarn(
|
|
643
|
+
'stale',
|
|
644
|
+
`observed kind=${kind} lease=${leaseId || '-'} epoch=${connectionEpoch ?? '-'} currentLease=${this.primaryLeaseId || '-'} currentEpoch=${this.connectionEpoch}`,
|
|
645
|
+
{ debugOnly: true },
|
|
600
646
|
);
|
|
601
647
|
return { stale: true, reason: 'mismatch' as const };
|
|
602
648
|
}
|
|
@@ -656,12 +702,7 @@ class BncrBridgeRuntime {
|
|
|
656
702
|
}
|
|
657
703
|
|
|
658
704
|
isDebugEnabled(): boolean {
|
|
659
|
-
|
|
660
|
-
const cfg = (this.api.runtime.config?.get?.() as any) || {};
|
|
661
|
-
return Boolean(cfg?.channels?.[CHANNEL_ID]?.debug?.verbose);
|
|
662
|
-
} catch {
|
|
663
|
-
return false;
|
|
664
|
-
}
|
|
705
|
+
return BNCR_DEBUG_VERBOSE;
|
|
665
706
|
}
|
|
666
707
|
|
|
667
708
|
startService = async (ctx: OpenClawPluginServiceContext, debug?: boolean) => {
|
|
@@ -674,12 +715,17 @@ class BncrBridgeRuntime {
|
|
|
674
715
|
// ignore startup canonical agent initialization errors
|
|
675
716
|
}
|
|
676
717
|
if (typeof debug === 'boolean') BNCR_DEBUG_VERBOSE = debug;
|
|
718
|
+
await this.refreshDebugFlagFromConfig({ forceLog: true });
|
|
677
719
|
const bootDiag = this.buildIntegratedDiagnostics(BNCR_DEFAULT_ACCOUNT_ID);
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
720
|
+
this.logInfo(
|
|
721
|
+
'startup',
|
|
722
|
+
`bridge=${this.bridgeId} routes=${bootDiag.regression.totalKnownRoutes}`,
|
|
723
|
+
);
|
|
724
|
+
this.logInfo(
|
|
725
|
+
'debug',
|
|
726
|
+
`service started bridge=${this.bridgeId} diag.ok=${bootDiag.regression.ok} routes=${bootDiag.regression.totalKnownRoutes} pending=${bootDiag.health.pending} dead=${bootDiag.health.deadLetter} debug=${BNCR_DEBUG_VERBOSE}`,
|
|
727
|
+
{ debugOnly: true },
|
|
728
|
+
);
|
|
683
729
|
};
|
|
684
730
|
|
|
685
731
|
stopService = async () => {
|
|
@@ -688,9 +734,7 @@ class BncrBridgeRuntime {
|
|
|
688
734
|
this.pushTimer = null;
|
|
689
735
|
}
|
|
690
736
|
await this.flushState();
|
|
691
|
-
|
|
692
|
-
this.api.logger.info('bncr-channel service stopped');
|
|
693
|
-
}
|
|
737
|
+
this.logInfo('debug', 'service stopped', { debugOnly: true });
|
|
694
738
|
};
|
|
695
739
|
|
|
696
740
|
private scheduleSave() {
|
|
@@ -710,20 +754,25 @@ class BncrBridgeRuntime {
|
|
|
710
754
|
return map.get(normalizeAccountId(accountId)) || 0;
|
|
711
755
|
}
|
|
712
756
|
|
|
713
|
-
private async
|
|
757
|
+
private async refreshDebugFlagFromConfig(options?: { forceLog?: boolean }) {
|
|
714
758
|
try {
|
|
715
759
|
const cfg = await this.api.runtime.config.loadConfig();
|
|
716
760
|
const raw = (cfg as any)?.channels?.[CHANNEL_ID]?.debug?.verbose;
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
761
|
+
const next = typeof raw === 'boolean' ? raw : false;
|
|
762
|
+
const changed = next !== BNCR_DEBUG_VERBOSE;
|
|
763
|
+
BNCR_DEBUG_VERBOSE = next;
|
|
764
|
+
if (changed || options?.forceLog) {
|
|
765
|
+
this.logInfo('debug', `verbose=${BNCR_DEBUG_VERBOSE}`, { debugOnly: true });
|
|
721
766
|
}
|
|
722
767
|
} catch {
|
|
723
768
|
// ignore config read errors
|
|
724
769
|
}
|
|
725
770
|
}
|
|
726
771
|
|
|
772
|
+
private async syncDebugFlag() {
|
|
773
|
+
await this.refreshDebugFlagFromConfig();
|
|
774
|
+
}
|
|
775
|
+
|
|
727
776
|
private tryResolveBindingAgentId(args: {
|
|
728
777
|
cfg: any;
|
|
729
778
|
accountId: string;
|
|
@@ -777,8 +826,10 @@ class BncrBridgeRuntime {
|
|
|
777
826
|
this.canonicalAgentId = 'main';
|
|
778
827
|
this.canonicalAgentSource = 'fallback-main';
|
|
779
828
|
this.canonicalAgentResolvedAt = now();
|
|
780
|
-
this.
|
|
781
|
-
'
|
|
829
|
+
this.logWarn(
|
|
830
|
+
'target',
|
|
831
|
+
'binding agent unresolved; fallback to main for current process lifetime',
|
|
832
|
+
{ debugOnly: true },
|
|
782
833
|
);
|
|
783
834
|
return this.canonicalAgentId;
|
|
784
835
|
}
|
|
@@ -1146,29 +1197,29 @@ class BncrBridgeRuntime {
|
|
|
1146
1197
|
private tryPushEntry(entry: OutboxEntry): boolean {
|
|
1147
1198
|
const ctx = this.gatewayContext;
|
|
1148
1199
|
if (!ctx) {
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1200
|
+
this.logInfo(
|
|
1201
|
+
'outbox',
|
|
1202
|
+
`push-skip ${JSON.stringify({
|
|
1203
|
+
messageId: entry.messageId,
|
|
1204
|
+
accountId: entry.accountId,
|
|
1205
|
+
reason: 'no-gateway-context',
|
|
1206
|
+
})}`,
|
|
1207
|
+
{ debugOnly: true },
|
|
1208
|
+
);
|
|
1158
1209
|
return false;
|
|
1159
1210
|
}
|
|
1160
1211
|
|
|
1161
1212
|
const connIds = this.resolvePushConnIds(entry.accountId);
|
|
1162
1213
|
if (!connIds.size) {
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1214
|
+
this.logInfo(
|
|
1215
|
+
'outbox',
|
|
1216
|
+
`push-skip ${JSON.stringify({
|
|
1217
|
+
messageId: entry.messageId,
|
|
1218
|
+
accountId: entry.accountId,
|
|
1219
|
+
reason: 'no-active-connection',
|
|
1220
|
+
})}`,
|
|
1221
|
+
{ debugOnly: true },
|
|
1222
|
+
);
|
|
1172
1223
|
return false;
|
|
1173
1224
|
}
|
|
1174
1225
|
|
|
@@ -1179,16 +1230,16 @@ class BncrBridgeRuntime {
|
|
|
1179
1230
|
};
|
|
1180
1231
|
|
|
1181
1232
|
ctx.broadcastToConnIds(BNCR_PUSH_EVENT, payload, connIds);
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1233
|
+
this.logInfo(
|
|
1234
|
+
'outbox',
|
|
1235
|
+
`push-ok ${JSON.stringify({
|
|
1236
|
+
messageId: entry.messageId,
|
|
1237
|
+
accountId: entry.accountId,
|
|
1238
|
+
connIds: Array.from(connIds),
|
|
1239
|
+
event: BNCR_PUSH_EVENT,
|
|
1240
|
+
})}`,
|
|
1241
|
+
{ debugOnly: true },
|
|
1242
|
+
);
|
|
1192
1243
|
this.lastOutboundByAccount.set(entry.accountId, now());
|
|
1193
1244
|
this.markActivity(entry.accountId);
|
|
1194
1245
|
this.scheduleSave();
|
|
@@ -1196,15 +1247,15 @@ class BncrBridgeRuntime {
|
|
|
1196
1247
|
} catch (error) {
|
|
1197
1248
|
entry.lastError = asString((error as any)?.message || error || 'push-error');
|
|
1198
1249
|
this.outbox.set(entry.messageId, entry);
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1250
|
+
this.logInfo(
|
|
1251
|
+
'outbox',
|
|
1252
|
+
`push-fail ${JSON.stringify({
|
|
1253
|
+
messageId: entry.messageId,
|
|
1254
|
+
accountId: entry.accountId,
|
|
1255
|
+
error: entry.lastError,
|
|
1256
|
+
})}`,
|
|
1257
|
+
{ debugOnly: true },
|
|
1258
|
+
);
|
|
1208
1259
|
return false;
|
|
1209
1260
|
}
|
|
1210
1261
|
}
|
|
@@ -1227,37 +1278,37 @@ class BncrBridgeRuntime {
|
|
|
1227
1278
|
Array.from(this.outbox.values()).map((entry) => normalizeAccountId(entry.accountId)),
|
|
1228
1279
|
),
|
|
1229
1280
|
);
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1281
|
+
this.logInfo(
|
|
1282
|
+
'outbox',
|
|
1283
|
+
`flush ${JSON.stringify({
|
|
1284
|
+
bridge: this.bridgeId,
|
|
1285
|
+
accountId: filterAcc,
|
|
1286
|
+
targetAccounts,
|
|
1287
|
+
outboxSize: this.outbox.size,
|
|
1288
|
+
})}`,
|
|
1289
|
+
{ debugOnly: true },
|
|
1290
|
+
);
|
|
1240
1291
|
|
|
1241
1292
|
let globalNextDelay: number | null = null;
|
|
1242
1293
|
|
|
1243
1294
|
for (const acc of targetAccounts) {
|
|
1244
1295
|
if (!acc || this.pushDrainRunningAccounts.has(acc)) continue;
|
|
1245
1296
|
const online = this.isOnline(acc);
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1297
|
+
this.logInfo(
|
|
1298
|
+
'outbox',
|
|
1299
|
+
`online ${JSON.stringify({
|
|
1300
|
+
bridge: this.bridgeId,
|
|
1301
|
+
accountId: acc,
|
|
1302
|
+
online,
|
|
1303
|
+
connections: Array.from(this.connections.values()).map((c) => ({
|
|
1304
|
+
accountId: c.accountId,
|
|
1305
|
+
connId: c.connId,
|
|
1306
|
+
clientId: c.clientId,
|
|
1307
|
+
lastSeenAt: c.lastSeenAt,
|
|
1308
|
+
})),
|
|
1309
|
+
})}`,
|
|
1310
|
+
{ debugOnly: true },
|
|
1311
|
+
);
|
|
1261
1312
|
this.pushDrainRunningAccounts.add(acc);
|
|
1262
1313
|
try {
|
|
1263
1314
|
let localNextDelay: number | null = null;
|
|
@@ -1375,19 +1426,19 @@ class BncrBridgeRuntime {
|
|
|
1375
1426
|
const staleBefore = t - CONNECT_TTL_MS * 2;
|
|
1376
1427
|
for (const [key, c] of this.connections.entries()) {
|
|
1377
1428
|
if (c.lastSeenAt < staleBefore) {
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1429
|
+
this.logInfo(
|
|
1430
|
+
'connection',
|
|
1431
|
+
`gc ${JSON.stringify({
|
|
1432
|
+
bridge: this.bridgeId,
|
|
1433
|
+
key,
|
|
1434
|
+
accountId: c.accountId,
|
|
1435
|
+
connId: c.connId,
|
|
1436
|
+
clientId: c.clientId,
|
|
1437
|
+
lastSeenAt: c.lastSeenAt,
|
|
1438
|
+
staleBefore,
|
|
1439
|
+
})}`,
|
|
1440
|
+
{ debugOnly: true },
|
|
1441
|
+
);
|
|
1391
1442
|
this.connections.delete(key);
|
|
1392
1443
|
}
|
|
1393
1444
|
}
|
|
@@ -1428,18 +1479,18 @@ class BncrBridgeRuntime {
|
|
|
1428
1479
|
};
|
|
1429
1480
|
|
|
1430
1481
|
this.connections.set(key, nextConn);
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1482
|
+
this.logInfo(
|
|
1483
|
+
'connection',
|
|
1484
|
+
`seen ${JSON.stringify({
|
|
1485
|
+
bridge: this.bridgeId,
|
|
1486
|
+
accountId: acc,
|
|
1487
|
+
connId,
|
|
1488
|
+
clientId: nextConn.clientId,
|
|
1489
|
+
connectedAt: nextConn.connectedAt,
|
|
1490
|
+
lastSeenAt: nextConn.lastSeenAt,
|
|
1491
|
+
})}`,
|
|
1492
|
+
{ debugOnly: true },
|
|
1493
|
+
);
|
|
1443
1494
|
|
|
1444
1495
|
const current = this.activeConnectionByAccount.get(acc);
|
|
1445
1496
|
if (!current) {
|
|
@@ -1541,9 +1592,7 @@ class BncrBridgeRuntime {
|
|
|
1541
1592
|
const raw = asString(rawTarget).trim();
|
|
1542
1593
|
if (!raw) throw new Error('bncr invalid target(empty)');
|
|
1543
1594
|
|
|
1544
|
-
|
|
1545
|
-
this.api.logger.info?.(`[bncr-target-incoming] raw=${raw} accountId=${acc}`);
|
|
1546
|
-
}
|
|
1595
|
+
this.logInfo('target', `incoming raw=${raw} accountId=${acc}`, { debugOnly: true });
|
|
1547
1596
|
|
|
1548
1597
|
let route: BncrRoute | null = null;
|
|
1549
1598
|
|
|
@@ -1555,8 +1604,10 @@ class BncrBridgeRuntime {
|
|
|
1555
1604
|
}
|
|
1556
1605
|
|
|
1557
1606
|
if (!route) {
|
|
1558
|
-
this.
|
|
1559
|
-
|
|
1607
|
+
this.logWarn(
|
|
1608
|
+
'target',
|
|
1609
|
+
`invalid raw=${raw} accountId=${acc} reason=unparseable-or-unknown standardTo=Bncr:<platform>:<groupId>:<userId>|Bncr:<platform>:<userId> standardSessionKey=agent:<agentId>:bncr:direct:<hex(scope)>`,
|
|
1610
|
+
{ debugOnly: true },
|
|
1560
1611
|
);
|
|
1561
1612
|
throw new Error(
|
|
1562
1613
|
`bncr invalid target(standard: Bncr:<platform>:<groupId>:<userId> | Bncr:<platform>:<userId>): ${raw}`,
|
|
@@ -1577,11 +1628,11 @@ class BncrBridgeRuntime {
|
|
|
1577
1628
|
displayScope: formatDisplayScope(route),
|
|
1578
1629
|
};
|
|
1579
1630
|
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1631
|
+
this.logInfo(
|
|
1632
|
+
'target',
|
|
1633
|
+
`canonical raw=${raw} accountId=${acc} verified=${JSON.stringify(verified)}`,
|
|
1634
|
+
{ debugOnly: true },
|
|
1635
|
+
);
|
|
1585
1636
|
|
|
1586
1637
|
// 发送链路命中目标时,同步刷新 lastSession,避免状态页显示过期会话。
|
|
1587
1638
|
this.lastSessionByAccount.set(acc, {
|
|
@@ -1805,22 +1856,25 @@ class BncrBridgeRuntime {
|
|
|
1805
1856
|
}
|
|
1806
1857
|
|
|
1807
1858
|
private enqueueOutbound(entry: OutboxEntry) {
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1859
|
+
const msg = (entry.payload as any)?.message || {};
|
|
1860
|
+
const type = asString(msg.type || (entry.payload as any)?.type || 'unknown');
|
|
1861
|
+
const text = asString(msg.msg || '');
|
|
1862
|
+
const displayScope = formatDisplayScope(entry.route);
|
|
1863
|
+
this.logInfo(
|
|
1864
|
+
'outbound',
|
|
1865
|
+
JSON.stringify({
|
|
1866
|
+
bridge: this.bridgeId,
|
|
1867
|
+
messageId: entry.messageId,
|
|
1868
|
+
accountId: entry.accountId,
|
|
1869
|
+
sessionKey: entry.sessionKey,
|
|
1870
|
+
scope: displayScope,
|
|
1871
|
+
type,
|
|
1872
|
+
textLen: text.length,
|
|
1873
|
+
textPreview: text.slice(0, 120),
|
|
1874
|
+
}),
|
|
1875
|
+
{ debugOnly: true },
|
|
1876
|
+
);
|
|
1877
|
+
this.logOutboundSummary(entry);
|
|
1824
1878
|
this.outbox.set(entry.messageId, entry);
|
|
1825
1879
|
this.scheduleSave();
|
|
1826
1880
|
this.wakeAccountWaiters(entry.accountId);
|
|
@@ -2133,6 +2187,7 @@ class BncrBridgeRuntime {
|
|
|
2133
2187
|
asVoice?: boolean;
|
|
2134
2188
|
audioAsVoice?: boolean;
|
|
2135
2189
|
kind?: 'tool' | 'block' | 'final';
|
|
2190
|
+
replyToId?: string;
|
|
2136
2191
|
};
|
|
2137
2192
|
mediaLocalRoots?: readonly string[];
|
|
2138
2193
|
}) {
|
|
@@ -2171,6 +2226,7 @@ class BncrBridgeRuntime {
|
|
|
2171
2226
|
}),
|
|
2172
2227
|
hintedType: wantsVoice ? 'voice' : undefined,
|
|
2173
2228
|
kind: payload.kind,
|
|
2229
|
+
replyToId: asString(payload.replyToId || '').trim() || undefined,
|
|
2174
2230
|
now: now(),
|
|
2175
2231
|
});
|
|
2176
2232
|
|
|
@@ -2198,6 +2254,7 @@ class BncrBridgeRuntime {
|
|
|
2198
2254
|
messageId,
|
|
2199
2255
|
idempotencyKey: messageId,
|
|
2200
2256
|
sessionKey,
|
|
2257
|
+
replyToId: asString(payload.replyToId || '').trim() || undefined,
|
|
2201
2258
|
message: {
|
|
2202
2259
|
platform: route.platform,
|
|
2203
2260
|
groupId: route.groupId,
|
|
@@ -2225,21 +2282,22 @@ class BncrBridgeRuntime {
|
|
|
2225
2282
|
}
|
|
2226
2283
|
|
|
2227
2284
|
handleConnect = async ({ params, respond, client, context }: GatewayRequestHandlerOptions) => {
|
|
2285
|
+
await this.syncDebugFlag();
|
|
2228
2286
|
const accountId = normalizeAccountId(asString(params?.accountId || ''));
|
|
2229
2287
|
const connId = asString(client?.connId || '').trim() || `no-conn-${Date.now()}`;
|
|
2230
2288
|
const clientId = asString((params as any)?.clientId || '').trim() || undefined;
|
|
2231
2289
|
|
|
2232
|
-
|
|
2233
|
-
|
|
2234
|
-
|
|
2235
|
-
|
|
2236
|
-
|
|
2237
|
-
|
|
2238
|
-
|
|
2239
|
-
|
|
2240
|
-
|
|
2241
|
-
|
|
2242
|
-
|
|
2290
|
+
this.logInfo(
|
|
2291
|
+
'connection',
|
|
2292
|
+
`connect ${JSON.stringify({
|
|
2293
|
+
bridge: this.bridgeId,
|
|
2294
|
+
accountId,
|
|
2295
|
+
connId,
|
|
2296
|
+
clientId,
|
|
2297
|
+
hasContext: Boolean(context),
|
|
2298
|
+
})}`,
|
|
2299
|
+
{ debugOnly: true },
|
|
2300
|
+
);
|
|
2243
2301
|
|
|
2244
2302
|
this.rememberGatewayContext(context);
|
|
2245
2303
|
this.markSeen(accountId, connId, clientId);
|
|
@@ -2272,6 +2330,7 @@ class BncrBridgeRuntime {
|
|
|
2272
2330
|
};
|
|
2273
2331
|
|
|
2274
2332
|
handleAck = async ({ params, respond, client, context }: GatewayRequestHandlerOptions) => {
|
|
2333
|
+
await this.syncDebugFlag();
|
|
2275
2334
|
const accountId = normalizeAccountId(asString(params?.accountId || ''));
|
|
2276
2335
|
const connId = asString(client?.connId || '').trim() || `no-conn-${Date.now()}`;
|
|
2277
2336
|
const clientId = asString((params as any)?.clientId || '').trim() || undefined;
|
|
@@ -2282,17 +2341,17 @@ class BncrBridgeRuntime {
|
|
|
2282
2341
|
this.incrementCounter(this.ackEventsByAccount, accountId);
|
|
2283
2342
|
|
|
2284
2343
|
const messageId = asString(params?.messageId || '').trim();
|
|
2285
|
-
|
|
2286
|
-
|
|
2287
|
-
|
|
2288
|
-
|
|
2289
|
-
|
|
2290
|
-
|
|
2291
|
-
|
|
2292
|
-
|
|
2293
|
-
|
|
2294
|
-
|
|
2295
|
-
|
|
2344
|
+
this.logInfo(
|
|
2345
|
+
'outbox',
|
|
2346
|
+
`ack ${JSON.stringify({
|
|
2347
|
+
accountId,
|
|
2348
|
+
messageId,
|
|
2349
|
+
ok: params?.ok !== false,
|
|
2350
|
+
fatal: params?.fatal === true,
|
|
2351
|
+
error: asString(params?.error || ''),
|
|
2352
|
+
})}`,
|
|
2353
|
+
{ debugOnly: true },
|
|
2354
|
+
);
|
|
2296
2355
|
if (!messageId) {
|
|
2297
2356
|
respond(false, { error: 'messageId required' });
|
|
2298
2357
|
return;
|
|
@@ -2335,23 +2394,23 @@ class BncrBridgeRuntime {
|
|
|
2335
2394
|
};
|
|
2336
2395
|
|
|
2337
2396
|
handleActivity = async ({ params, respond, client, context }: GatewayRequestHandlerOptions) => {
|
|
2338
|
-
|
|
2397
|
+
await this.syncDebugFlag();
|
|
2339
2398
|
const accountId = normalizeAccountId(asString(params?.accountId || ''));
|
|
2340
2399
|
const connId = asString(client?.connId || '').trim() || `no-conn-${Date.now()}`;
|
|
2341
2400
|
const clientId = asString((params as any)?.clientId || '').trim() || undefined;
|
|
2342
2401
|
this.observeLease('activity', params ?? {});
|
|
2343
2402
|
this.lastActivityAtGlobal = now();
|
|
2344
|
-
|
|
2345
|
-
|
|
2346
|
-
|
|
2347
|
-
|
|
2348
|
-
|
|
2349
|
-
|
|
2350
|
-
|
|
2351
|
-
|
|
2352
|
-
|
|
2353
|
-
|
|
2354
|
-
|
|
2403
|
+
this.logInfo(
|
|
2404
|
+
'activity',
|
|
2405
|
+
`event ${JSON.stringify({
|
|
2406
|
+
bridge: this.bridgeId,
|
|
2407
|
+
accountId,
|
|
2408
|
+
connId,
|
|
2409
|
+
clientId,
|
|
2410
|
+
hasContext: Boolean(context),
|
|
2411
|
+
})}`,
|
|
2412
|
+
{ debugOnly: true },
|
|
2413
|
+
);
|
|
2355
2414
|
this.rememberGatewayContext(context);
|
|
2356
2415
|
this.markSeen(accountId, connId, clientId);
|
|
2357
2416
|
this.markActivity(accountId);
|
|
@@ -2696,6 +2755,7 @@ class BncrBridgeRuntime {
|
|
|
2696
2755
|
};
|
|
2697
2756
|
|
|
2698
2757
|
handleInbound = async ({ params, respond, client, context }: GatewayRequestHandlerOptions) => {
|
|
2758
|
+
await this.syncDebugFlag();
|
|
2699
2759
|
const parsed = parseBncrInboundParams(params);
|
|
2700
2760
|
const {
|
|
2701
2761
|
accountId,
|
|
@@ -2771,6 +2831,30 @@ class BncrBridgeRuntime {
|
|
|
2771
2831
|
resolvedRoute.sessionKey;
|
|
2772
2832
|
const taskSessionKey = withTaskSessionKey(baseSessionKey, extracted.taskKey);
|
|
2773
2833
|
const sessionKey = taskSessionKey || baseSessionKey;
|
|
2834
|
+
const inboundText = asString(extracted.text || text || '');
|
|
2835
|
+
this.logInfo(
|
|
2836
|
+
'inbound',
|
|
2837
|
+
JSON.stringify({
|
|
2838
|
+
accountId,
|
|
2839
|
+
msgId: msgId ?? null,
|
|
2840
|
+
platform,
|
|
2841
|
+
chatType: peer.kind,
|
|
2842
|
+
scope: formatDisplayScope(route),
|
|
2843
|
+
sessionKey,
|
|
2844
|
+
msgType,
|
|
2845
|
+
textLen: inboundText.length,
|
|
2846
|
+
textPreview: inboundText.slice(0, 120),
|
|
2847
|
+
hasMedia: Boolean(mediaBase64 || mediaPathFromTransfer),
|
|
2848
|
+
}),
|
|
2849
|
+
{ debugOnly: true },
|
|
2850
|
+
);
|
|
2851
|
+
this.logInboundSummary({
|
|
2852
|
+
accountId,
|
|
2853
|
+
route,
|
|
2854
|
+
msgType,
|
|
2855
|
+
text: inboundText,
|
|
2856
|
+
hasMedia: Boolean(mediaBase64 || mediaPathFromTransfer),
|
|
2857
|
+
});
|
|
2774
2858
|
|
|
2775
2859
|
respond(true, {
|
|
2776
2860
|
accepted: true,
|
|
@@ -2794,9 +2878,12 @@ class BncrBridgeRuntime {
|
|
|
2794
2878
|
this.markActivity(accountId, at);
|
|
2795
2879
|
},
|
|
2796
2880
|
scheduleSave: () => this.scheduleSave(),
|
|
2797
|
-
logger:
|
|
2881
|
+
logger: {
|
|
2882
|
+
warn: (msg: string) => emitBncrLogLine('warn', msg),
|
|
2883
|
+
error: (msg: string) => emitBncrLogLine('error', msg),
|
|
2884
|
+
},
|
|
2798
2885
|
}).catch((err) => {
|
|
2799
|
-
this.
|
|
2886
|
+
this.logError('inbound', `process failed: ${String(err)}`, { debugOnly: true });
|
|
2800
2887
|
});
|
|
2801
2888
|
};
|
|
2802
2889
|
|
|
@@ -2844,33 +2931,35 @@ class BncrBridgeRuntime {
|
|
|
2844
2931
|
};
|
|
2845
2932
|
|
|
2846
2933
|
channelSendText = async (ctx: any) => {
|
|
2934
|
+
await this.syncDebugFlag();
|
|
2847
2935
|
const accountId = normalizeAccountId(ctx.accountId);
|
|
2848
2936
|
const to = asString(ctx.to || '').trim();
|
|
2849
2937
|
|
|
2850
|
-
|
|
2851
|
-
|
|
2852
|
-
|
|
2853
|
-
|
|
2854
|
-
|
|
2855
|
-
|
|
2856
|
-
|
|
2857
|
-
|
|
2858
|
-
|
|
2859
|
-
|
|
2860
|
-
|
|
2861
|
-
|
|
2862
|
-
|
|
2863
|
-
|
|
2864
|
-
|
|
2865
|
-
|
|
2866
|
-
|
|
2867
|
-
|
|
2938
|
+
this.logInfo(
|
|
2939
|
+
'outbound',
|
|
2940
|
+
`send-entry:text ${JSON.stringify({
|
|
2941
|
+
accountId,
|
|
2942
|
+
to,
|
|
2943
|
+
text: asString(ctx?.text || ''),
|
|
2944
|
+
mediaUrl: asString(ctx?.mediaUrl || ''),
|
|
2945
|
+
sessionKey: asString(ctx?.sessionKey || ''),
|
|
2946
|
+
mirrorSessionKey: asString(ctx?.mirror?.sessionKey || ''),
|
|
2947
|
+
rawCtx: {
|
|
2948
|
+
to: ctx?.to,
|
|
2949
|
+
accountId: ctx?.accountId,
|
|
2950
|
+
threadId: ctx?.threadId,
|
|
2951
|
+
replyToId: ctx?.replyToId,
|
|
2952
|
+
},
|
|
2953
|
+
})}`,
|
|
2954
|
+
{ debugOnly: true },
|
|
2955
|
+
);
|
|
2868
2956
|
|
|
2869
2957
|
return sendBncrText({
|
|
2870
2958
|
channelId: CHANNEL_ID,
|
|
2871
2959
|
accountId,
|
|
2872
2960
|
to,
|
|
2873
2961
|
text: asString(ctx.text || ''),
|
|
2962
|
+
replyToId: asString(ctx?.replyToId || ctx?.replyToMessageId || '').trim() || undefined,
|
|
2874
2963
|
mediaLocalRoots: ctx.mediaLocalRoots,
|
|
2875
2964
|
resolveVerifiedTarget: (to, accountId) => this.resolveVerifiedTarget(to, accountId),
|
|
2876
2965
|
rememberSessionRoute: (sessionKey, accountId, route) =>
|
|
@@ -2881,32 +2970,33 @@ class BncrBridgeRuntime {
|
|
|
2881
2970
|
};
|
|
2882
2971
|
|
|
2883
2972
|
channelSendMedia = async (ctx: any) => {
|
|
2973
|
+
await this.syncDebugFlag();
|
|
2884
2974
|
const accountId = normalizeAccountId(ctx.accountId);
|
|
2885
2975
|
const to = asString(ctx.to || '').trim();
|
|
2886
2976
|
const asVoice = ctx?.asVoice === true;
|
|
2887
2977
|
const audioAsVoice = ctx?.audioAsVoice === true;
|
|
2888
2978
|
|
|
2889
|
-
|
|
2890
|
-
|
|
2891
|
-
|
|
2892
|
-
|
|
2893
|
-
|
|
2894
|
-
|
|
2895
|
-
|
|
2896
|
-
|
|
2897
|
-
|
|
2898
|
-
|
|
2899
|
-
|
|
2900
|
-
|
|
2901
|
-
|
|
2902
|
-
|
|
2903
|
-
|
|
2904
|
-
|
|
2905
|
-
|
|
2906
|
-
|
|
2907
|
-
|
|
2908
|
-
|
|
2909
|
-
|
|
2979
|
+
this.logInfo(
|
|
2980
|
+
'outbound',
|
|
2981
|
+
`send-entry:media ${JSON.stringify({
|
|
2982
|
+
accountId,
|
|
2983
|
+
to,
|
|
2984
|
+
text: asString(ctx?.text || ''),
|
|
2985
|
+
mediaUrl: asString(ctx?.mediaUrl || ''),
|
|
2986
|
+
mediaUrls: Array.isArray(ctx?.mediaUrls) ? ctx.mediaUrls : undefined,
|
|
2987
|
+
asVoice,
|
|
2988
|
+
audioAsVoice,
|
|
2989
|
+
sessionKey: asString(ctx?.sessionKey || ''),
|
|
2990
|
+
mirrorSessionKey: asString(ctx?.mirror?.sessionKey || ''),
|
|
2991
|
+
rawCtx: {
|
|
2992
|
+
to: ctx?.to,
|
|
2993
|
+
accountId: ctx?.accountId,
|
|
2994
|
+
threadId: ctx?.threadId,
|
|
2995
|
+
replyToId: ctx?.replyToId,
|
|
2996
|
+
},
|
|
2997
|
+
})}`,
|
|
2998
|
+
{ debugOnly: true },
|
|
2999
|
+
);
|
|
2910
3000
|
|
|
2911
3001
|
return sendBncrMedia({
|
|
2912
3002
|
channelId: CHANNEL_ID,
|
|
@@ -2916,6 +3006,7 @@ class BncrBridgeRuntime {
|
|
|
2916
3006
|
mediaUrl: asString(ctx.mediaUrl || ''),
|
|
2917
3007
|
asVoice,
|
|
2918
3008
|
audioAsVoice,
|
|
3009
|
+
replyToId: asString(ctx?.replyToId || ctx?.replyToMessageId || '').trim() || undefined,
|
|
2919
3010
|
mediaLocalRoots: ctx.mediaLocalRoots,
|
|
2920
3011
|
resolveVerifiedTarget: (to, accountId) => this.resolveVerifiedTarget(to, accountId),
|
|
2921
3012
|
rememberSessionRoute: (sessionKey, accountId, route) =>
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
export type BncrLogLevel = 'info' | 'warn' | 'error';
|
|
2
|
+
export type BncrLogOptions = { debugOnly?: boolean };
|
|
3
|
+
|
|
4
|
+
const BNCR_PREFIX = '[bncr]';
|
|
5
|
+
|
|
6
|
+
type DebugGate = () => boolean;
|
|
7
|
+
|
|
8
|
+
type ConsoleMethod = 'log' | 'warn' | 'error';
|
|
9
|
+
|
|
10
|
+
function resolveConsoleMethod(level: BncrLogLevel): ConsoleMethod {
|
|
11
|
+
switch (level) {
|
|
12
|
+
case 'warn':
|
|
13
|
+
return 'warn';
|
|
14
|
+
case 'error':
|
|
15
|
+
return 'error';
|
|
16
|
+
default:
|
|
17
|
+
return 'log';
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function emitConsole(method: ConsoleMethod, line: string) {
|
|
22
|
+
if (method === 'warn') {
|
|
23
|
+
console.warn(line);
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
if (method === 'error') {
|
|
27
|
+
console.error(line);
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
console.log(line);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function normalizeBncrLogLine(raw: string | undefined) {
|
|
34
|
+
const text = String(raw || '').trim();
|
|
35
|
+
if (!text) return BNCR_PREFIX;
|
|
36
|
+
return text.startsWith(BNCR_PREFIX) ? text : `${BNCR_PREFIX} ${text}`;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function formatBncrLogLine(scope: string | undefined, message: string | undefined) {
|
|
40
|
+
const normalizedScope = String(scope || '').trim();
|
|
41
|
+
const normalizedMessage = String(message || '').trim();
|
|
42
|
+
const prefix = normalizedScope ? `${BNCR_PREFIX} ${normalizedScope}` : BNCR_PREFIX;
|
|
43
|
+
return normalizedMessage ? `${prefix} ${normalizedMessage}` : prefix;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export function emitBncrLog(
|
|
47
|
+
level: BncrLogLevel,
|
|
48
|
+
scope: string | undefined,
|
|
49
|
+
message: string | undefined,
|
|
50
|
+
options?: BncrLogOptions,
|
|
51
|
+
isDebugEnabled?: DebugGate,
|
|
52
|
+
) {
|
|
53
|
+
if (options?.debugOnly && !(isDebugEnabled?.() ?? false)) return;
|
|
54
|
+
emitConsole(resolveConsoleMethod(level), formatBncrLogLine(scope, message));
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export function emitBncrLogLine(
|
|
58
|
+
level: BncrLogLevel,
|
|
59
|
+
line: string | undefined,
|
|
60
|
+
options?: BncrLogOptions,
|
|
61
|
+
isDebugEnabled?: DebugGate,
|
|
62
|
+
) {
|
|
63
|
+
if (options?.debugOnly && !(isDebugEnabled?.() ?? false)) return;
|
|
64
|
+
emitConsole(resolveConsoleMethod(level), normalizeBncrLogLine(line));
|
|
65
|
+
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { emitBncrLogLine } from '../../core/logging.ts';
|
|
1
2
|
import {
|
|
2
3
|
formatDisplayScope,
|
|
3
4
|
normalizeInboundSessionKey,
|
|
@@ -78,7 +79,7 @@ export async function handleBncrNativeCommand(params: {
|
|
|
78
79
|
const displayTo = formatDisplayScope(route);
|
|
79
80
|
const body = command.body;
|
|
80
81
|
if (!clientId) {
|
|
81
|
-
|
|
82
|
+
emitBncrLogLine('warn', '[bncr] inbound missing clientId for native command identity');
|
|
82
83
|
return { handled: false };
|
|
83
84
|
}
|
|
84
85
|
const senderIdForContext = clientId;
|
|
@@ -119,7 +120,10 @@ export async function handleBncrNativeCommand(params: {
|
|
|
119
120
|
sessionKey,
|
|
120
121
|
ctx: ctxPayload,
|
|
121
122
|
onRecordError: (err: unknown) => {
|
|
122
|
-
|
|
123
|
+
emitBncrLogLine(
|
|
124
|
+
'warn',
|
|
125
|
+
`[bncr] inbound record native command session failed: ${String(err)}`,
|
|
126
|
+
);
|
|
123
127
|
},
|
|
124
128
|
});
|
|
125
129
|
|
|
@@ -155,6 +159,7 @@ export async function handleBncrNativeCommand(params: {
|
|
|
155
159
|
payload: {
|
|
156
160
|
...payload,
|
|
157
161
|
kind: kind as 'tool' | 'block' | 'final' | undefined,
|
|
162
|
+
replyToId: msgId || undefined,
|
|
158
163
|
},
|
|
159
164
|
});
|
|
160
165
|
},
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { emitBncrLogLine } from '../../core/logging.ts';
|
|
1
2
|
import {
|
|
2
3
|
formatDisplayScope,
|
|
3
4
|
normalizeInboundSessionKey,
|
|
@@ -132,7 +133,7 @@ export async function dispatchBncrInbound(params: {
|
|
|
132
133
|
|
|
133
134
|
const displayTo = formatDisplayScope(route);
|
|
134
135
|
if (!clientId) {
|
|
135
|
-
|
|
136
|
+
emitBncrLogLine('warn', '[bncr] inbound missing clientId for chat identity');
|
|
136
137
|
return {
|
|
137
138
|
accountId,
|
|
138
139
|
sessionKey,
|
|
@@ -171,7 +172,7 @@ export async function dispatchBncrInbound(params: {
|
|
|
171
172
|
sessionKey,
|
|
172
173
|
ctx: ctxPayload,
|
|
173
174
|
onRecordError: (err: unknown) => {
|
|
174
|
-
|
|
175
|
+
emitBncrLogLine('warn', `[bncr] inbound record session failed: ${String(err)}`);
|
|
175
176
|
},
|
|
176
177
|
});
|
|
177
178
|
|
|
@@ -207,7 +208,7 @@ export async function dispatchBncrInbound(params: {
|
|
|
207
208
|
});
|
|
208
209
|
},
|
|
209
210
|
onError: (err: unknown) => {
|
|
210
|
-
|
|
211
|
+
emitBncrLogLine('error', `[bncr] outbound reply failed: ${String(err)}`);
|
|
211
212
|
},
|
|
212
213
|
},
|
|
213
214
|
replyOptions: {
|
|
@@ -56,6 +56,7 @@ export function buildBncrMediaOutboundFrame(params: {
|
|
|
56
56
|
fileName: string;
|
|
57
57
|
hintedType?: string;
|
|
58
58
|
kind?: 'tool' | 'block' | 'final';
|
|
59
|
+
replyToId?: string;
|
|
59
60
|
now: number;
|
|
60
61
|
}) {
|
|
61
62
|
return {
|
|
@@ -63,6 +64,7 @@ export function buildBncrMediaOutboundFrame(params: {
|
|
|
63
64
|
messageId: params.messageId,
|
|
64
65
|
idempotencyKey: params.messageId,
|
|
65
66
|
sessionKey: params.sessionKey,
|
|
67
|
+
replyToId: asString(params.replyToId || '').trim() || undefined,
|
|
66
68
|
message: {
|
|
67
69
|
platform: params.route.platform,
|
|
68
70
|
groupId: params.route.groupId,
|
|
@@ -3,6 +3,7 @@ export async function sendBncrText(params: {
|
|
|
3
3
|
accountId: string;
|
|
4
4
|
to: string;
|
|
5
5
|
text: string;
|
|
6
|
+
replyToId?: string;
|
|
6
7
|
mediaLocalRoots?: readonly string[];
|
|
7
8
|
resolveVerifiedTarget: (
|
|
8
9
|
to: string,
|
|
@@ -13,7 +14,12 @@ export async function sendBncrText(params: {
|
|
|
13
14
|
accountId: string;
|
|
14
15
|
sessionKey: string;
|
|
15
16
|
route: any;
|
|
16
|
-
payload: {
|
|
17
|
+
payload: {
|
|
18
|
+
text?: string;
|
|
19
|
+
mediaUrl?: string;
|
|
20
|
+
mediaUrls?: string[];
|
|
21
|
+
replyToId?: string;
|
|
22
|
+
};
|
|
17
23
|
mediaLocalRoots?: readonly string[];
|
|
18
24
|
}) => Promise<void>;
|
|
19
25
|
createMessageId: () => string;
|
|
@@ -27,6 +33,7 @@ export async function sendBncrText(params: {
|
|
|
27
33
|
route: verified.route,
|
|
28
34
|
payload: {
|
|
29
35
|
text: params.text,
|
|
36
|
+
replyToId: params.replyToId,
|
|
30
37
|
},
|
|
31
38
|
mediaLocalRoots: params.mediaLocalRoots,
|
|
32
39
|
});
|
|
@@ -46,6 +53,7 @@ export async function sendBncrMedia(params: {
|
|
|
46
53
|
mediaUrl?: string;
|
|
47
54
|
asVoice?: boolean;
|
|
48
55
|
audioAsVoice?: boolean;
|
|
56
|
+
replyToId?: string;
|
|
49
57
|
mediaLocalRoots?: readonly string[];
|
|
50
58
|
resolveVerifiedTarget: (
|
|
51
59
|
to: string,
|
|
@@ -62,6 +70,7 @@ export async function sendBncrMedia(params: {
|
|
|
62
70
|
mediaUrls?: string[];
|
|
63
71
|
asVoice?: boolean;
|
|
64
72
|
audioAsVoice?: boolean;
|
|
73
|
+
replyToId?: string;
|
|
65
74
|
};
|
|
66
75
|
mediaLocalRoots?: readonly string[];
|
|
67
76
|
}) => Promise<void>;
|
|
@@ -79,6 +88,7 @@ export async function sendBncrMedia(params: {
|
|
|
79
88
|
mediaUrl: params.mediaUrl || '',
|
|
80
89
|
asVoice: params.asVoice === true ? true : undefined,
|
|
81
90
|
audioAsVoice: params.audioAsVoice === true ? true : undefined,
|
|
91
|
+
replyToId: params.replyToId,
|
|
82
92
|
},
|
|
83
93
|
mediaLocalRoots: params.mediaLocalRoots,
|
|
84
94
|
});
|