@xmoxmo/bncr 0.2.6 → 0.2.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.
Files changed (45) hide show
  1. package/README.md +7 -1
  2. package/index.ts +30 -15
  3. package/package.json +4 -3
  4. package/scripts/check-pack.mjs +77 -0
  5. package/scripts/selfcheck.mjs +10 -0
  6. package/src/channel.ts +398 -642
  7. package/src/core/extended-diagnostics.ts +10 -0
  8. package/src/core/file-ack.ts +9 -0
  9. package/src/core/file-transfer-payloads.ts +72 -0
  10. package/src/core/register-trace.ts +79 -0
  11. package/src/core/targets.ts +10 -1
  12. package/src/messaging/inbound/commands.ts +20 -10
  13. package/src/messaging/inbound/context-facts.ts +200 -0
  14. package/src/messaging/inbound/dispatch.ts +66 -14
  15. package/src/messaging/inbound/gate.ts +66 -26
  16. package/src/messaging/inbound/runtime-compat.ts +41 -0
  17. package/src/messaging/inbound/session-label.ts +7 -7
  18. package/src/messaging/outbound/durable-message-adapter.ts +107 -0
  19. package/src/messaging/outbound/durable-queue-adapter.ts +157 -0
  20. package/src/messaging/outbound/session-route.ts +2 -2
  21. package/src/openclaw/config-runtime.ts +52 -0
  22. package/src/openclaw/inbound-session-runtime.ts +94 -0
  23. package/src/openclaw/ingress-runtime.ts +35 -0
  24. package/src/openclaw/media-runtime.ts +73 -0
  25. package/src/openclaw/reply-runtime.ts +104 -0
  26. package/src/openclaw/routing-runtime.ts +48 -0
  27. package/src/openclaw/sdk-helpers.ts +20 -0
  28. package/src/openclaw/session-route-runtime.ts +15 -0
  29. package/src/plugin/capabilities.ts +8 -0
  30. package/src/plugin/config.ts +35 -0
  31. package/src/plugin/gateway-methods.ts +12 -0
  32. package/src/plugin/gateway-runtime.ts +11 -0
  33. package/src/plugin/message-policy.ts +4 -0
  34. package/src/plugin/message-send.ts +13 -0
  35. package/src/plugin/messaging.ts +142 -0
  36. package/src/plugin/meta.ts +10 -0
  37. package/src/plugin/outbound.ts +51 -0
  38. package/src/plugin/setup.ts +24 -0
  39. package/src/plugin/status.ts +38 -0
  40. package/src/runtime/log-dedupe.ts +56 -0
  41. package/src/runtime/outbound-ack-timeout.ts +96 -0
  42. package/src/runtime/outbound-flags.ts +81 -0
  43. package/src/runtime/outbox-transitions.ts +119 -0
  44. package/src/runtime/status-snapshots.ts +108 -0
  45. package/src/runtime/status-worker.ts +172 -0
package/README.md CHANGED
@@ -34,7 +34,7 @@ openclaw plugins update bncr
34
34
  openclaw gateway restart
35
35
  ```
36
36
 
37
- > 兼容范围:`openclaw >= 2026.5.3-1`
37
+ > 兼容范围:`openclaw >= 2026.5.27`
38
38
  >
39
39
  > 如果你是从精确版本升级,或本地安装记录仍钉在旧版本,也可以显式执行:
40
40
  >
@@ -95,6 +95,10 @@ bncr 当前采用两层模型:
95
95
  - 在 OpenClaw 内部按正式 `channel plugin` 建模
96
96
  - 负责入站解析、消息分发、出站适配、状态与治理
97
97
 
98
+ 出站可靠投递的边界:bncr 后面仍有自己的服务框架和 outbox / ACK / retry / deadLetter 体系。对 OpenClaw 宿主来说,消息成功交给 bncr 插件并进入 bncr 自管 outbox,即表示频道 handoff 完成;这不等价于客户端 ACK 或目标平台最终送达。后续可靠投递由 bncr 自身负责。
99
+
100
+ 当前已注册生产 `channel.message` 作为 bncr 的频道专用 handoff adapter:`text` / `media` / `payload` 会转换为 bncr outbox entry。原有通用 `message.send` / `channel.actions.send` 发送能力继续保留;`channel.message` 是频道专用入口,不替代通用发送入口。该 adapter 仍不启用 `durableFinal`;进入 outbox 后的客户端 ACK、目标平台送达、retry、deadLetter 继续由 bncr 服务框架负责。
101
+
98
102
  当前代码结构:
99
103
 
100
104
  ```text
@@ -246,6 +250,7 @@ npm root -g
246
250
  cd plugins/bncr
247
251
  npm test
248
252
  npm run selfcheck
253
+ npm run check-pack
249
254
  npm pack
250
255
  ```
251
256
 
@@ -253,6 +258,7 @@ npm pack
253
258
 
254
259
  - `npm test`:跑回归测试
255
260
  - `npm run selfcheck`:检查插件骨架是否完整
261
+ - `npm run check-pack`:执行 `npm pack --dry-run --json`,确认发布包包含关键入口与 OpenClaw adapter 文件
256
262
  - `npm pack`:确认当前版本可正常打包
257
263
  - `npm run check-register-drift -- --duration-sec 300 --interval-sec 15`:静置采样 `bncr.diagnostics`,观察 `registerCount / apiGeneration / postWarmupRegisterCount` 是否在 warmup 后继续增长
258
264
 
package/index.ts CHANGED
@@ -5,6 +5,10 @@ import path from 'node:path';
5
5
  import { fileURLToPath } from 'node:url';
6
6
  import { BncrConfigSchema } from './src/core/config-schema.ts';
7
7
  import { emitBncrLogLine } from './src/core/logging.ts';
8
+ import {
9
+ getOpenClawRuntimeConfig,
10
+ mutateOpenClawRuntimeConfigFile,
11
+ } from './src/openclaw/config-runtime.ts';
8
12
 
9
13
  const pluginFile = fileURLToPath(import.meta.url);
10
14
  const pluginDir = path.dirname(pluginFile);
@@ -660,35 +664,46 @@ const registerBncrCli = (api: OpenClawPluginApi & { registerCli?: (...args: any[
660
664
  'Seed minimal channels.bncr config (adds enabled=true and allowTool=false only when missing)',
661
665
  )
662
666
  .action(async () => {
663
- const cfg = api.runtime.config.current() as Record<string, unknown>;
664
- const next = structuredClone(cfg);
665
- if (!isPlainObject(next.channels)) next.channels = {};
666
-
667
- const existing = isPlainObject(next.channels.bncr) ? next.channels.bncr : {};
668
- const bncrCfg: Record<string, unknown> = { ...existing };
667
+ const cfg = getOpenClawRuntimeConfig(api) as Record<string, unknown>;
668
+ const channels = isPlainObject(cfg.channels) ? cfg.channels : {};
669
+ const existing = isPlainObject(channels.bncr) ? channels.bncr : {};
669
670
  const added: string[] = [];
670
671
 
671
- if (bncrCfg.enabled === undefined) {
672
- bncrCfg.enabled = true;
672
+ if (existing.enabled === undefined) {
673
673
  added.push('enabled=true');
674
674
  }
675
675
 
676
- if (bncrCfg.allowTool === undefined) {
677
- bncrCfg.allowTool = false;
676
+ if (existing.allowTool === undefined) {
678
677
  added.push('allowTool=false');
679
678
  }
680
679
 
681
- next.channels.bncr = bncrCfg;
682
-
683
680
  if (added.length === 0) {
684
681
  console.log('Minimal bncr config already present. No changes made.');
685
682
  return;
686
683
  }
687
684
 
688
- await api.runtime.config.writeConfigFile(next);
685
+ await mutateOpenClawRuntimeConfigFile(api, {
686
+ afterWrite: { mode: 'auto' },
687
+ mutate(draft: Record<string, unknown>) {
688
+ if (!isPlainObject(draft.channels)) draft.channels = {};
689
+ const draftChannels = draft.channels as Record<string, unknown>;
690
+ const draftExisting = isPlainObject(draftChannels.bncr) ? draftChannels.bncr : {};
691
+ const draftBncrCfg: Record<string, unknown> = { ...draftExisting };
692
+
693
+ if (draftBncrCfg.enabled === undefined) {
694
+ draftBncrCfg.enabled = true;
695
+ }
696
+
697
+ if (draftBncrCfg.allowTool === undefined) {
698
+ draftBncrCfg.allowTool = false;
699
+ }
700
+
701
+ draftChannels.bncr = draftBncrCfg;
702
+ },
703
+ });
689
704
  console.log('Seeded minimal bncr config at channels.bncr.');
690
705
  console.log(`Added missing fields: ${added.join(', ')}`);
691
- console.log('Restart the gateway to apply changes.');
706
+ console.log('Gateway will apply the config using the host afterWrite policy.');
692
707
  });
693
708
  },
694
709
  { commands: ['bncr'] },
@@ -801,7 +816,7 @@ const plugin = {
801
816
 
802
817
  const resolveDebug = async () => {
803
818
  try {
804
- const cfg = api.runtime.config.current();
819
+ const cfg = getOpenClawRuntimeConfig(api);
805
820
  return Boolean((cfg as any)?.channels?.bncr?.debug?.verbose);
806
821
  } catch {
807
822
  return false;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xmoxmo/bncr",
3
- "version": "0.2.6",
3
+ "version": "0.2.8",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -27,17 +27,18 @@
27
27
  "selfcheck": "node ./scripts/selfcheck.mjs",
28
28
  "test": "node --import ./tests/register-ts-hooks.mjs --test ./tests/*.test.mjs",
29
29
  "check-register-drift": "node ./scripts/check-register-drift.mjs",
30
+ "check-pack": "node ./scripts/check-pack.mjs",
30
31
  "format:check": "biome format --check .",
31
32
  "format": "biome format --write .",
32
33
  "lint": "biome lint .",
33
34
  "check": "biome check ."
34
35
  },
35
36
  "peerDependencies": {
36
- "openclaw": ">=2026.5.3-1"
37
+ "openclaw": ">=2026.5.27"
37
38
  },
38
39
  "devDependencies": {
39
40
  "@biomejs/biome": "^1.9.4",
40
- "openclaw": ">=2026.5.3-1"
41
+ "openclaw": ">=2026.5.27"
41
42
  },
42
43
  "openclaw": {
43
44
  "extensions": [
@@ -0,0 +1,77 @@
1
+ import { execFileSync } from 'node:child_process';
2
+ import fs from 'node:fs';
3
+ import path from 'node:path';
4
+ import { fileURLToPath } from 'node:url';
5
+
6
+ const __filename = fileURLToPath(import.meta.url);
7
+ const __dirname = path.dirname(__filename);
8
+ const root = path.resolve(__dirname, '..');
9
+
10
+ const requiredPackFiles = [
11
+ 'README.md',
12
+ 'index.ts',
13
+ 'openclaw.plugin.json',
14
+ 'package.json',
15
+ 'scripts/selfcheck.mjs',
16
+ 'scripts/check-register-drift.mjs',
17
+ 'src/channel.ts',
18
+ 'src/plugin/config.ts',
19
+ 'src/plugin/message-policy.ts',
20
+ 'src/plugin/messaging.ts',
21
+ 'src/plugin/gateway-runtime.ts',
22
+ 'src/plugin/message-send.ts',
23
+ 'src/plugin/outbound.ts',
24
+ 'src/plugin/setup.ts',
25
+ 'src/plugin/status.ts',
26
+ 'src/runtime/outbound-ack-timeout.ts',
27
+ 'src/runtime/outbound-flags.ts',
28
+ 'src/runtime/outbox-transitions.ts',
29
+ 'src/messaging/outbound/durable-message-adapter.ts',
30
+ 'src/messaging/outbound/durable-queue-adapter.ts',
31
+ 'src/openclaw/config-runtime.ts',
32
+ 'src/openclaw/inbound-session-runtime.ts',
33
+ 'src/openclaw/ingress-runtime.ts',
34
+ 'src/openclaw/media-runtime.ts',
35
+ 'src/openclaw/reply-runtime.ts',
36
+ 'src/openclaw/routing-runtime.ts',
37
+ 'src/openclaw/sdk-helpers.ts',
38
+ 'src/openclaw/session-route-runtime.ts',
39
+ ];
40
+
41
+ const pkg = JSON.parse(fs.readFileSync(path.join(root, 'package.json'), 'utf8'));
42
+ const output = execFileSync('npm', ['pack', '--dry-run', '--json'], {
43
+ cwd: root,
44
+ encoding: 'utf8',
45
+ stdio: ['ignore', 'pipe', 'pipe'],
46
+ });
47
+ const [pack] = JSON.parse(output);
48
+ const packedFiles = new Set((pack?.files ?? []).map((file) => file.path));
49
+ const missing = requiredPackFiles.filter((file) => !packedFiles.has(file));
50
+ const channelSource = fs.readFileSync(path.join(root, 'src/channel.ts'), 'utf8');
51
+ const messagePolicySource = fs.readFileSync(path.join(root, 'src/plugin/message-policy.ts'), 'utf8');
52
+ const messageSendSource = fs.readFileSync(path.join(root, 'src/plugin/message-send.ts'), 'utf8');
53
+ const channelMessageChecks = {
54
+ registered: channelSource.includes('message: {'),
55
+ text: channelSource.includes('createBncrMessageSend') && messageSendSource.includes('channelMessageSendText'),
56
+ media: channelSource.includes('createBncrMessageSend') && messageSendSource.includes('channelMessageSendMedia'),
57
+ payload: channelSource.includes('createBncrMessageSend') && messageSendSource.includes('channelMessageSendPayload'),
58
+ manualAck:
59
+ channelSource.includes('BNCR_MESSAGE_RECEIVE_POLICY') &&
60
+ messagePolicySource.includes("defaultAckPolicy: 'manual'") &&
61
+ messagePolicySource.includes("supportedAckPolicies: ['manual']"),
62
+ genericActionsPreserved: channelSource.includes('actions: messageActions'),
63
+ noDurableFinal: !channelSource.includes('durableFinal:') && !messagePolicySource.includes('durableFinal:'),
64
+ };
65
+ const channelMessageOk = Object.values(channelMessageChecks).every(Boolean);
66
+
67
+ const result = {
68
+ ok: missing.length === 0 && pkg.peerDependencies?.openclaw === '>=2026.5.27' && channelMessageOk,
69
+ package: pack?.id,
70
+ entryCount: pack?.entryCount,
71
+ missing,
72
+ openclaw: pkg.peerDependencies?.openclaw,
73
+ channelMessageChecks,
74
+ };
75
+
76
+ console.log(JSON.stringify(result, null, 2));
77
+ if (!result.ok) process.exit(1);
@@ -24,6 +24,16 @@ const requiredFiles = [
24
24
  'src/messaging/outbound/send.ts',
25
25
  'src/messaging/outbound/media.ts',
26
26
  'src/messaging/outbound/actions.ts',
27
+ 'src/messaging/outbound/durable-message-adapter.ts',
28
+ 'src/messaging/outbound/durable-queue-adapter.ts',
29
+ 'src/openclaw/config-runtime.ts',
30
+ 'src/openclaw/inbound-session-runtime.ts',
31
+ 'src/openclaw/ingress-runtime.ts',
32
+ 'src/openclaw/media-runtime.ts',
33
+ 'src/openclaw/reply-runtime.ts',
34
+ 'src/openclaw/routing-runtime.ts',
35
+ 'src/openclaw/sdk-helpers.ts',
36
+ 'src/openclaw/session-route-runtime.ts',
27
37
  ];
28
38
 
29
39
  const readPackageVersion = () => {