@xmoxmo/bncr 0.2.1 → 0.2.2

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 CHANGED
@@ -122,7 +122,8 @@ plugins/bncr/src/
122
122
  补充:
123
123
 
124
124
  - `dmPolicy` / `groupPolicy` 支持:`open | allowlist | disabled`
125
- - `outboundRequireAck` 控制文本外发是否等待 `bncr.ack` 再出队
125
+ - `outboundRequireAck` 是当前**单账号场景**使用的顶层字段:`channels.bncr.outboundRequireAck`
126
+ - `outboundRequireAck=true` 时,文本外发会等待 `bncr.ack` 再出队;关闭后不再强制等待文本 ACK,超时类错误会显示为 `push-delivery-unconfirmed`
126
127
  - `requireMention` 当前仍是保留字段
127
128
 
128
129
  ---
@@ -142,6 +143,9 @@ openclaw health --json
142
143
  - `pending`
143
144
  - `deadLetter`
144
145
  - diagnostics / probe / status 摘要
146
+ - diagnostics 里的 `runtimeFlags.outboundRequireAck`
147
+ - diagnostics 里的 `runtimeFlags.ackPolicySource`
148
+ - diagnostics 里的 `waiters.messageAck` / `waiters.fileAck`
145
149
 
146
150
  ---
147
151
 
package/index.ts CHANGED
@@ -22,6 +22,10 @@ type LoadedRuntime = {
22
22
  };
23
23
 
24
24
  const BNCR_REGISTER_META = Symbol.for('bncr.register.meta');
25
+ const BNCR_GLOBAL_REGISTER_TRACE = Symbol.for('bncr.global.register.trace');
26
+ const BNCR_BRIDGE_OWNER = Symbol.for('bncr.bridge.owner');
27
+ const BNCR_GATEWAY_RUNTIME = Symbol.for('bncr.gateway.runtime');
28
+ const MODULE_EPOCH = `${process.pid}-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
25
29
 
26
30
  type RegisterMeta = {
27
31
  service?: boolean;
@@ -29,12 +33,74 @@ type RegisterMeta = {
29
33
  methods?: Set<string>;
30
34
  apiInstanceId?: string;
31
35
  registryFingerprint?: string;
36
+ registrationMode?: string;
37
+ };
38
+
39
+ type GlobalRegisterTrace = {
40
+ lastApiInstanceId?: string;
41
+ lastRegistryFingerprint?: string;
42
+ seenRegistryFingerprints: Set<string>;
43
+ seenApiInstanceIds: Set<string>;
44
+ };
45
+
46
+ type BridgeOwner = {
47
+ moduleEpoch: string;
48
+ bridgeFactoryId: string;
49
+ apiInstanceId: string;
50
+ registryFingerprint: string;
51
+ registrationMode?: string;
52
+ };
53
+
54
+ type BridgeRegisterStateSnapshot = {
55
+ registerCount: number;
56
+ apiGeneration: number;
57
+ firstRegisterAt: number | null;
58
+ lastRegisterAt: number | null;
59
+ lastApiRebindAt: number | null;
60
+ pluginSource: string | null;
61
+ pluginVersion: string | null;
62
+ lastApiInstanceId: string | null;
63
+ lastRegistryFingerprint: string | null;
64
+ lastDriftSnapshot: unknown;
65
+ registerTraceRecent: Array<Record<string, unknown>>;
66
+ };
67
+
68
+ type GatewayMethodName =
69
+ | 'bncr.connect'
70
+ | 'bncr.inbound'
71
+ | 'bncr.activity'
72
+ | 'bncr.ack'
73
+ | 'bncr.diagnostics'
74
+ | 'bncr.file.init'
75
+ | 'bncr.file.chunk'
76
+ | 'bncr.file.complete'
77
+ | 'bncr.file.abort'
78
+ | 'bncr.file.ack';
79
+
80
+ type BridgeSingletonWithOwner = BridgeSingleton & {
81
+ [BNCR_BRIDGE_OWNER]?: BridgeOwner;
82
+ registerCount?: number;
83
+ apiGeneration?: number;
84
+ firstRegisterAt?: number | null;
85
+ lastRegisterAt?: number | null;
86
+ lastApiRebindAt?: number | null;
87
+ pluginSource?: string | null;
88
+ pluginVersion?: string | null;
89
+ lastApiInstanceId?: string | null;
90
+ lastRegistryFingerprint?: string | null;
91
+ lastDriftSnapshot?: unknown;
92
+ registerTraceRecent?: Array<Record<string, unknown>>;
32
93
  };
33
94
 
34
95
  type OpenClawPluginApiWithMeta = OpenClawPluginApi & {
35
96
  [BNCR_REGISTER_META]?: RegisterMeta;
36
97
  };
37
98
 
99
+ type BncrGatewayRuntime = {
100
+ currentBridge?: BridgeSingletonWithOwner;
101
+ registeredMethodsByRegistry: Map<string, Set<GatewayMethodName>>;
102
+ };
103
+
38
104
  let runtime: LoadedRuntime | null = null;
39
105
  const identityIds = new WeakMap<object, string>();
40
106
  let identitySeq = 0;
@@ -218,7 +284,7 @@ const loadRuntimeSync = (): LoadedRuntime => {
218
284
  const getIdentityId = (obj: object, prefix: string) => {
219
285
  const existing = identityIds.get(obj);
220
286
  if (existing) return existing;
221
- const next = `${prefix}_${++identitySeq}`;
287
+ const next = `${prefix}_${MODULE_EPOCH}_${++identitySeq}`;
222
288
  identityIds.set(obj, next);
223
289
  return next;
224
290
  };
@@ -247,33 +313,219 @@ const getRegisterMeta = (api: OpenClawPluginApi): RegisterMeta => {
247
313
  return host[BNCR_REGISTER_META]!;
248
314
  };
249
315
 
316
+ const getProcessStore = () => {
317
+ const p = process as NodeJS.Process & {
318
+ [BNCR_GLOBAL_REGISTER_TRACE]?: GlobalRegisterTrace;
319
+ [BNCR_GATEWAY_RUNTIME]?: BncrGatewayRuntime;
320
+ };
321
+ return p;
322
+ };
323
+
324
+ const getGlobalRegisterTrace = () => {
325
+ const p = getProcessStore();
326
+ if (!p[BNCR_GLOBAL_REGISTER_TRACE]) {
327
+ p[BNCR_GLOBAL_REGISTER_TRACE] = {
328
+ seenRegistryFingerprints: new Set<string>(),
329
+ seenApiInstanceIds: new Set<string>(),
330
+ };
331
+ }
332
+ return p[BNCR_GLOBAL_REGISTER_TRACE]!;
333
+ };
334
+
335
+ const getGatewayRuntime = (): BncrGatewayRuntime => {
336
+ const p = getProcessStore();
337
+ if (!p[BNCR_GATEWAY_RUNTIME]) {
338
+ p[BNCR_GATEWAY_RUNTIME] = {
339
+ registeredMethodsByRegistry: new Map<string, Set<GatewayMethodName>>(),
340
+ };
341
+ }
342
+ return p[BNCR_GATEWAY_RUNTIME]!;
343
+ };
344
+
345
+ const gatewayMethodDispatchers: Record<
346
+ GatewayMethodName,
347
+ (bridge: BridgeSingletonWithOwner, opts: any) => any
348
+ > = {
349
+ 'bncr.connect': (bridge, opts) => bridge.handleConnect(opts),
350
+ 'bncr.inbound': (bridge, opts) => bridge.handleInbound(opts),
351
+ 'bncr.activity': (bridge, opts) => bridge.handleActivity(opts),
352
+ 'bncr.ack': (bridge, opts) => bridge.handleAck(opts),
353
+ 'bncr.diagnostics': (bridge, opts) => bridge.handleDiagnostics(opts),
354
+ 'bncr.file.init': (bridge, opts) => bridge.handleFileInit(opts),
355
+ 'bncr.file.chunk': (bridge, opts) => bridge.handleFileChunk(opts),
356
+ 'bncr.file.complete': (bridge, opts) => bridge.handleFileComplete(opts),
357
+ 'bncr.file.abort': (bridge, opts) => bridge.handleFileAbort(opts),
358
+ 'bncr.file.ack': (bridge, opts) => bridge.handleFileAck(opts),
359
+ };
360
+
361
+ const dispatchGatewayMethod = (name: GatewayMethodName, opts: any) => {
362
+ const gatewayRuntime = getGatewayRuntime();
363
+ const bridge = gatewayRuntime.currentBridge;
364
+ if (!bridge) {
365
+ throw new Error(`bncr gateway runtime unavailable for ${name}`);
366
+ }
367
+ return gatewayMethodDispatchers[name](bridge, opts);
368
+ };
369
+
370
+ const mirrorGatewayMethodForMockApi = (api: OpenClawPluginApi, name: GatewayMethodName) => {
371
+ const host = api as OpenClawPluginApi & {
372
+ methods?: Array<{ name: string; handler: (opts: any) => any }>;
373
+ };
374
+ if (!Array.isArray(host.methods)) return;
375
+ if (host.methods.some((item) => item?.name === name)) return;
376
+ host.methods.push({ name, handler: (opts) => dispatchGatewayMethod(name, opts) });
377
+ };
378
+
250
379
  const ensureGatewayMethodRegistered = (
251
380
  api: OpenClawPluginApi,
252
- name: string,
253
- handler: (opts: any) => any,
381
+ name: GatewayMethodName,
254
382
  debugLog: (...args: any[]) => void,
255
383
  ) => {
256
384
  const meta = getRegisterMeta(api);
385
+ const gatewayRuntime = getGatewayRuntime();
386
+ const registryFingerprint = meta.registryFingerprint || getRegistryFingerprint(api);
387
+ let registryMethods = gatewayRuntime.registeredMethodsByRegistry.get(registryFingerprint);
388
+ if (!registryMethods) {
389
+ registryMethods = new Set<GatewayMethodName>();
390
+ gatewayRuntime.registeredMethodsByRegistry.set(registryFingerprint, registryMethods);
391
+ }
257
392
  if (meta.methods?.has(name)) {
258
393
  debugLog(`register method skip ${name} (already registered on this api)`);
259
394
  return;
260
395
  }
261
- api.registerGatewayMethod(name, handler);
396
+ if (registryMethods.has(name)) {
397
+ mirrorGatewayMethodForMockApi(api, name);
398
+ meta.methods?.add(name);
399
+ debugLog(`register method reuse ${name} (already registered in registry)`);
400
+ return;
401
+ }
402
+ api.registerGatewayMethod(name, (opts) => dispatchGatewayMethod(name, opts));
403
+ mirrorGatewayMethodForMockApi(api, name);
404
+ registryMethods.add(name);
262
405
  meta.methods?.add(name);
263
406
  debugLog(`register method ok ${name}`);
264
407
  };
265
408
 
409
+ const getBridgeOwner = (api: OpenClawPluginApi, loaded: LoadedRuntime): BridgeOwner => {
410
+ const meta = getRegisterMeta(api);
411
+ return {
412
+ moduleEpoch: MODULE_EPOCH,
413
+ bridgeFactoryId: getIdentityId(loaded.createBncrBridge as object, 'bridgeFactory'),
414
+ apiInstanceId: meta.apiInstanceId || 'unknown',
415
+ registryFingerprint: meta.registryFingerprint || 'unknown',
416
+ registrationMode: meta.registrationMode,
417
+ };
418
+ };
419
+
420
+ const sameBridgeOwner = (left?: BridgeOwner, right?: BridgeOwner) => {
421
+ if (!left || !right) return false;
422
+ return (
423
+ left.moduleEpoch === right.moduleEpoch &&
424
+ left.bridgeFactoryId === right.bridgeFactoryId &&
425
+ left.apiInstanceId === right.apiInstanceId &&
426
+ left.registryFingerprint === right.registryFingerprint
427
+ );
428
+ };
429
+
430
+ const snapshotBridgeRegisterState = (
431
+ bridge?: BridgeSingletonWithOwner,
432
+ ): BridgeRegisterStateSnapshot | null => {
433
+ if (!bridge) return null;
434
+ return {
435
+ registerCount: Number(bridge.registerCount || 0),
436
+ apiGeneration: Number(bridge.apiGeneration || 0),
437
+ firstRegisterAt:
438
+ typeof bridge.firstRegisterAt === 'number'
439
+ ? bridge.firstRegisterAt
440
+ : (bridge.firstRegisterAt ?? null),
441
+ lastRegisterAt:
442
+ typeof bridge.lastRegisterAt === 'number'
443
+ ? bridge.lastRegisterAt
444
+ : (bridge.lastRegisterAt ?? null),
445
+ lastApiRebindAt:
446
+ typeof bridge.lastApiRebindAt === 'number'
447
+ ? bridge.lastApiRebindAt
448
+ : (bridge.lastApiRebindAt ?? null),
449
+ pluginSource: typeof bridge.pluginSource === 'string' ? bridge.pluginSource : null,
450
+ pluginVersion: typeof bridge.pluginVersion === 'string' ? bridge.pluginVersion : null,
451
+ lastApiInstanceId:
452
+ typeof bridge.lastApiInstanceId === 'string' ? bridge.lastApiInstanceId : null,
453
+ lastRegistryFingerprint:
454
+ typeof bridge.lastRegistryFingerprint === 'string' ? bridge.lastRegistryFingerprint : null,
455
+ lastDriftSnapshot: bridge.lastDriftSnapshot ?? null,
456
+ registerTraceRecent: Array.isArray(bridge.registerTraceRecent)
457
+ ? bridge.registerTraceRecent.map((trace) => ({ ...trace }))
458
+ : [],
459
+ };
460
+ };
461
+
462
+ const hydrateBridgeRegisterState = (
463
+ bridge: BridgeSingletonWithOwner,
464
+ snapshot: BridgeRegisterStateSnapshot | null,
465
+ ) => {
466
+ if (!snapshot) return bridge;
467
+ bridge.registerCount = snapshot.registerCount;
468
+ bridge.apiGeneration = snapshot.apiGeneration;
469
+ bridge.firstRegisterAt = snapshot.firstRegisterAt;
470
+ bridge.lastRegisterAt = snapshot.lastRegisterAt;
471
+ bridge.lastApiRebindAt = snapshot.lastApiRebindAt;
472
+ bridge.pluginSource = snapshot.pluginSource;
473
+ bridge.pluginVersion = snapshot.pluginVersion;
474
+ bridge.lastApiInstanceId = snapshot.lastApiInstanceId;
475
+ bridge.lastRegistryFingerprint = snapshot.lastRegistryFingerprint;
476
+ bridge.lastDriftSnapshot = snapshot.lastDriftSnapshot;
477
+ bridge.registerTraceRecent = snapshot.registerTraceRecent.map((trace) => ({ ...trace }));
478
+ return bridge;
479
+ };
480
+
481
+ const assignBridgeOwner = (bridge: BridgeSingleton, owner: BridgeOwner) => {
482
+ (bridge as BridgeSingletonWithOwner)[BNCR_BRIDGE_OWNER] = owner;
483
+ return bridge as BridgeSingletonWithOwner;
484
+ };
485
+
266
486
  const getBridgeSingleton = (api: OpenClawPluginApi) => {
267
487
  const loaded = loadRuntimeSync();
268
- const g = globalThis as typeof globalThis & { __bncrBridge?: BridgeSingleton };
488
+ const g = globalThis as typeof globalThis & { __bncrBridge?: BridgeSingletonWithOwner };
489
+ const owner = getBridgeOwner(api, loaded);
490
+ const previousOwner = g.__bncrBridge?.[BNCR_BRIDGE_OWNER];
491
+
269
492
  let created = false;
270
- if (!g.__bncrBridge) {
271
- g.__bncrBridge = loaded.createBncrBridge(api);
272
- created = true;
493
+ let rebuilt = false;
494
+
495
+ if (g.__bncrBridge) {
496
+ const mustRebuild =
497
+ !sameBridgeOwner(previousOwner, owner) &&
498
+ (previousOwner?.moduleEpoch !== owner.moduleEpoch ||
499
+ previousOwner?.bridgeFactoryId !== owner.bridgeFactoryId ||
500
+ previousOwner?.registrationMode !== owner.registrationMode ||
501
+ previousOwner?.apiInstanceId !== owner.apiInstanceId ||
502
+ previousOwner?.registryFingerprint !== owner.registryFingerprint);
503
+
504
+ if (mustRebuild) {
505
+ const registerState = snapshotBridgeRegisterState(g.__bncrBridge);
506
+ try {
507
+ g.__bncrBridge.stopService?.();
508
+ } catch {
509
+ // ignore stop errors during hot-restart recovery
510
+ }
511
+ g.__bncrBridge = hydrateBridgeRegisterState(
512
+ assignBridgeOwner(loaded.createBncrBridge(api), owner),
513
+ registerState,
514
+ );
515
+ created = true;
516
+ rebuilt = true;
517
+ } else {
518
+ g.__bncrBridge.bindApi?.(api);
519
+ assignBridgeOwner(g.__bncrBridge, owner);
520
+ created = false;
521
+ rebuilt = false;
522
+ }
273
523
  } else {
274
- g.__bncrBridge.bindApi?.(api);
524
+ g.__bncrBridge = assignBridgeOwner(loaded.createBncrBridge(api), owner);
525
+ created = true;
275
526
  }
276
- return { bridge: g.__bncrBridge, runtime: loaded, created };
527
+
528
+ return { bridge: g.__bncrBridge, runtime: loaded, created, rebuilt, owner, previousOwner };
277
529
  };
278
530
 
279
531
  const isPlainObject = (value: unknown): value is Record<string, unknown> =>
@@ -290,10 +542,11 @@ const registerBncrCli = (api: OpenClawPluginApi & { registerCli?: (...args: any[
290
542
  'Seed minimal channels.bncr config (adds enabled=true and allowTool=false only when missing)',
291
543
  )
292
544
  .action(async () => {
293
- const cfg = (await api.runtime.config.loadConfig()) as Record<string, unknown>;
294
- if (!isPlainObject(cfg.channels)) cfg.channels = {};
545
+ const cfg = api.runtime.config.current() as Record<string, unknown>;
546
+ const next = structuredClone(cfg);
547
+ if (!isPlainObject(next.channels)) next.channels = {};
295
548
 
296
- const existing = isPlainObject(cfg.channels.bncr) ? cfg.channels.bncr : {};
549
+ const existing = isPlainObject(next.channels.bncr) ? next.channels.bncr : {};
297
550
  const bncrCfg: Record<string, unknown> = { ...existing };
298
551
  const added: string[] = [];
299
552
 
@@ -307,14 +560,14 @@ const registerBncrCli = (api: OpenClawPluginApi & { registerCli?: (...args: any[
307
560
  added.push('allowTool=false');
308
561
  }
309
562
 
310
- cfg.channels.bncr = bncrCfg;
563
+ next.channels.bncr = bncrCfg;
311
564
 
312
565
  if (added.length === 0) {
313
566
  console.log('Minimal bncr config already present. No changes made.');
314
567
  return;
315
568
  }
316
569
 
317
- await api.runtime.config.writeConfigFile(cfg);
570
+ await api.runtime.config.writeConfigFile(next);
318
571
  console.log('Seeded minimal bncr config at channels.bncr.');
319
572
  console.log(`Added missing fields: ${added.join(', ')}`);
320
573
  console.log('Restart the gateway to apply changes.');
@@ -324,6 +577,9 @@ const registerBncrCli = (api: OpenClawPluginApi & { registerCli?: (...args: any[
324
577
  );
325
578
  };
326
579
 
580
+ const shouldSkipNonRuntimeRegister = (mode?: string) =>
581
+ mode === 'cli-metadata' || mode === 'discovery';
582
+
327
583
  const plugin = {
328
584
  id: 'bncr',
329
585
  name: 'Bncr',
@@ -333,14 +589,31 @@ const plugin = {
333
589
  api: OpenClawPluginApi & { registerCli?: (...args: any[]) => void; registrationMode?: string },
334
590
  ) {
335
591
  registerBncrCli(api);
336
- if (api.registrationMode === 'cli-metadata') return;
592
+ if (shouldSkipNonRuntimeRegister(api.registrationMode)) return;
337
593
 
338
594
  const meta = getRegisterMeta(api);
339
- const { bridge, runtime, created } = getBridgeSingleton(api);
595
+ meta.registrationMode = api.registrationMode;
596
+ const globalTrace = getGlobalRegisterTrace();
597
+ const previousApiInstanceId = globalTrace.lastApiInstanceId;
598
+ const previousRegistryFingerprint = globalTrace.lastRegistryFingerprint;
599
+ const apiInstanceId = meta.apiInstanceId || 'unknown';
600
+ const registryFingerprint = meta.registryFingerprint || 'unknown';
601
+ const sameApiAsPrevious = previousApiInstanceId === apiInstanceId;
602
+ const sameRegistryAsPrevious = previousRegistryFingerprint === registryFingerprint;
603
+ const firstSeenApi = !globalTrace.seenApiInstanceIds.has(apiInstanceId);
604
+ const firstSeenRegistry = !globalTrace.seenRegistryFingerprints.has(registryFingerprint);
605
+
606
+ const { bridge, runtime, created, rebuilt, owner, previousOwner } = getBridgeSingleton(api);
607
+ getGatewayRuntime().currentBridge = bridge;
608
+
609
+ globalTrace.seenApiInstanceIds.add(apiInstanceId);
610
+ globalTrace.seenRegistryFingerprints.add(registryFingerprint);
611
+ globalTrace.lastApiInstanceId = apiInstanceId;
612
+ globalTrace.lastRegistryFingerprint = registryFingerprint;
340
613
  bridge.noteRegister?.({
341
614
  source: '~/.openclaw/workspace/plugins/bncr/index.ts',
342
615
  pluginVersion,
343
- apiRebound: !created,
616
+ apiRebound: !created && !rebuilt,
344
617
  apiInstanceId: meta.apiInstanceId,
345
618
  registryFingerprint: meta.registryFingerprint,
346
619
  });
@@ -355,12 +628,22 @@ const plugin = {
355
628
  );
356
629
  };
357
630
 
358
- debugLog(`register begin bridge=${bridge.getBridgeId?.() || 'unknown'} created=${created}`);
359
- if (!created) debugLog('bridge api rebound');
631
+ debugLog(
632
+ `register begin bridge=${bridge.getBridgeId?.() || 'unknown'} created=${created} rebuilt=${rebuilt} ` +
633
+ `ownerApi=${owner.apiInstanceId} ownerRegistry=${owner.registryFingerprint} ` +
634
+ `previousOwnerApi=${previousOwner?.apiInstanceId || 'none'} previousOwnerRegistry=${previousOwner?.registryFingerprint || 'none'}`,
635
+ );
636
+ debugLog(
637
+ `register classify mode=${meta.registrationMode || 'unknown'} api=${apiInstanceId} registry=${registryFingerprint} ` +
638
+ `sameApiAsPrevious=${sameApiAsPrevious} sameRegistryAsPrevious=${sameRegistryAsPrevious} ` +
639
+ `firstSeenApi=${firstSeenApi} firstSeenRegistry=${firstSeenRegistry}`,
640
+ );
641
+ if (!created && !rebuilt) debugLog('bridge api rebound');
642
+ if (rebuilt) debugLog('bridge rebuilt due to owner/runtime change');
360
643
 
361
644
  const resolveDebug = async () => {
362
645
  try {
363
- const cfg = await api.runtime.config.loadConfig();
646
+ const cfg = api.runtime.config.current();
364
647
  return Boolean((cfg as any)?.channels?.bncr?.debug?.verbose);
365
648
  } catch {
366
649
  return false;
@@ -390,61 +673,16 @@ const plugin = {
390
673
  debugLog('register channel skip (already registered on this api)');
391
674
  }
392
675
 
393
- ensureGatewayMethodRegistered(
394
- api,
395
- 'bncr.connect',
396
- (opts) => bridge.handleConnect(opts),
397
- debugLog,
398
- );
399
- ensureGatewayMethodRegistered(
400
- api,
401
- 'bncr.inbound',
402
- (opts) => bridge.handleInbound(opts),
403
- debugLog,
404
- );
405
- ensureGatewayMethodRegistered(
406
- api,
407
- 'bncr.activity',
408
- (opts) => bridge.handleActivity(opts),
409
- debugLog,
410
- );
411
- ensureGatewayMethodRegistered(api, 'bncr.ack', (opts) => bridge.handleAck(opts), debugLog);
412
- ensureGatewayMethodRegistered(
413
- api,
414
- 'bncr.diagnostics',
415
- (opts) => bridge.handleDiagnostics(opts),
416
- debugLog,
417
- );
418
- ensureGatewayMethodRegistered(
419
- api,
420
- 'bncr.file.init',
421
- (opts) => bridge.handleFileInit(opts),
422
- debugLog,
423
- );
424
- ensureGatewayMethodRegistered(
425
- api,
426
- 'bncr.file.chunk',
427
- (opts) => bridge.handleFileChunk(opts),
428
- debugLog,
429
- );
430
- ensureGatewayMethodRegistered(
431
- api,
432
- 'bncr.file.complete',
433
- (opts) => bridge.handleFileComplete(opts),
434
- debugLog,
435
- );
436
- ensureGatewayMethodRegistered(
437
- api,
438
- 'bncr.file.abort',
439
- (opts) => bridge.handleFileAbort(opts),
440
- debugLog,
441
- );
442
- ensureGatewayMethodRegistered(
443
- api,
444
- 'bncr.file.ack',
445
- (opts) => bridge.handleFileAck(opts),
446
- debugLog,
447
- );
676
+ ensureGatewayMethodRegistered(api, 'bncr.connect', debugLog);
677
+ ensureGatewayMethodRegistered(api, 'bncr.inbound', debugLog);
678
+ ensureGatewayMethodRegistered(api, 'bncr.activity', debugLog);
679
+ ensureGatewayMethodRegistered(api, 'bncr.ack', debugLog);
680
+ ensureGatewayMethodRegistered(api, 'bncr.diagnostics', debugLog);
681
+ ensureGatewayMethodRegistered(api, 'bncr.file.init', debugLog);
682
+ ensureGatewayMethodRegistered(api, 'bncr.file.chunk', debugLog);
683
+ ensureGatewayMethodRegistered(api, 'bncr.file.complete', debugLog);
684
+ ensureGatewayMethodRegistered(api, 'bncr.file.abort', debugLog);
685
+ ensureGatewayMethodRegistered(api, 'bncr.file.ack', debugLog);
448
686
  debugLog('register done');
449
687
  },
450
688
  };
@@ -36,5 +36,64 @@
36
36
  }
37
37
  }
38
38
  }
39
+ },
40
+ "channelConfigs": {
41
+ "bncr": {
42
+ "schema": {
43
+ "type": "object",
44
+ "additionalProperties": true,
45
+ "properties": {
46
+ "enabled": { "type": "boolean" },
47
+ "dmPolicy": {
48
+ "type": "string",
49
+ "enum": ["open", "allowlist", "disabled"]
50
+ },
51
+ "groupPolicy": {
52
+ "type": "string",
53
+ "enum": ["open", "allowlist", "disabled"]
54
+ },
55
+ "allowFrom": {
56
+ "type": "array",
57
+ "items": { "type": "string" }
58
+ },
59
+ "groupAllowFrom": {
60
+ "type": "array",
61
+ "items": { "type": "string" }
62
+ },
63
+ "debug": {
64
+ "type": "object",
65
+ "additionalProperties": true,
66
+ "properties": {
67
+ "verbose": {
68
+ "type": "boolean",
69
+ "default": false,
70
+ "description": "Enable verbose debug logs for bncr channel runtime."
71
+ }
72
+ }
73
+ },
74
+ "allowTool": {
75
+ "type": "boolean",
76
+ "default": false,
77
+ "description": "Allow tool messages to be forwarded when streaming is enabled. Defaults to false; only explicit true enables forwarding. When enabled, bncr also requests upstream tool summaries/results."
78
+ },
79
+ "requireMention": {
80
+ "type": "boolean",
81
+ "default": false,
82
+ "description": "Whether group messages must explicitly mention the bot before bncr handles them. Default false. Current version keeps this as a reserved field and does not enforce it yet."
83
+ },
84
+ "accounts": {
85
+ "type": "object",
86
+ "additionalProperties": {
87
+ "type": "object",
88
+ "additionalProperties": true,
89
+ "properties": {
90
+ "enabled": { "type": "boolean" },
91
+ "name": { "type": "string" }
92
+ }
93
+ }
94
+ }
95
+ }
96
+ }
97
+ }
39
98
  }
40
99
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xmoxmo/bncr",
3
- "version": "0.2.1",
3
+ "version": "0.2.2",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -20,8 +20,8 @@
20
20
  "openclaw.plugin.json",
21
21
  "README.md",
22
22
  "LICENSE",
23
- "src",
24
- "scripts"
23
+ "src/**/*.ts",
24
+ "scripts/**/*.mjs"
25
25
  ],
26
26
  "scripts": {
27
27
  "selfcheck": "node ./scripts/selfcheck.mjs",