@openclaw/buzz 2026.9.1-beta.1 → 2026.9.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.
Files changed (63) hide show
  1. package/dist/{adapter.runtime-DA6guOQ_.js → adapter.runtime-CaDdxQ2C.js} +4 -4
  2. package/dist/api.js +2 -2
  3. package/dist/{channel-BulZqShF.js → channel-Czma0_CC.js} +200 -66
  4. package/dist/channel-plugin-api.js +1 -1
  5. package/dist/{directory-config-BesCse0q.js → directory-config-B0shiBa4.js} +1 -1
  6. package/dist/directory-contract-api.js +1 -1
  7. package/dist/{message-event-CFw7gGLi.js → message-event-X_aBiM1X.js} +5 -8
  8. package/dist/qa-runner-api.js +1 -1
  9. package/dist/{room-membership-DSAkyE0a.js → room-membership-a8IHfdgL.js} +7 -5
  10. package/dist/secret-contract-DNuqbK64.js +50 -0
  11. package/dist/secret-contract-api.js +1 -1
  12. package/dist/setup-plugin-api.js +6 -3
  13. package/dist/{setup-surface-BvQgEJ7O.js → setup-surface-DDUmAluL.js} +197 -170
  14. package/dist/types-CeupBG0P.js +160 -0
  15. package/node_modules/nostr-tools/lib/cjs/index.js +21 -5
  16. package/node_modules/nostr-tools/lib/cjs/index.js.map +2 -2
  17. package/node_modules/nostr-tools/lib/cjs/nip17.js +19 -3
  18. package/node_modules/nostr-tools/lib/cjs/nip17.js.map +2 -2
  19. package/node_modules/nostr-tools/lib/cjs/nip46.js +7 -3
  20. package/node_modules/nostr-tools/lib/cjs/nip46.js.map +2 -2
  21. package/node_modules/nostr-tools/lib/cjs/nip59.js +19 -3
  22. package/node_modules/nostr-tools/lib/cjs/nip59.js.map +2 -2
  23. package/node_modules/nostr-tools/lib/cjs/nip98.js +1 -1
  24. package/node_modules/nostr-tools/lib/cjs/nip98.js.map +2 -2
  25. package/node_modules/nostr-tools/lib/cjs/nipad.js +55 -0
  26. package/node_modules/nostr-tools/lib/cjs/nipad.js.map +7 -0
  27. package/node_modules/nostr-tools/lib/cjs/nipb7.js +1003 -98
  28. package/node_modules/nostr-tools/lib/cjs/nipb7.js.map +4 -4
  29. package/node_modules/nostr-tools/lib/cjs/relay.js +1 -1
  30. package/node_modules/nostr-tools/lib/cjs/relay.js.map +2 -2
  31. package/node_modules/nostr-tools/lib/esm/index.js +21 -5
  32. package/node_modules/nostr-tools/lib/esm/index.js.map +2 -2
  33. package/node_modules/nostr-tools/lib/esm/nip17.js +19 -3
  34. package/node_modules/nostr-tools/lib/esm/nip17.js.map +2 -2
  35. package/node_modules/nostr-tools/lib/esm/nip46.js +7 -3
  36. package/node_modules/nostr-tools/lib/esm/nip46.js.map +2 -2
  37. package/node_modules/nostr-tools/lib/esm/nip59.js +19 -3
  38. package/node_modules/nostr-tools/lib/esm/nip59.js.map +2 -2
  39. package/node_modules/nostr-tools/lib/esm/nip98.js +1 -1
  40. package/node_modules/nostr-tools/lib/esm/nip98.js.map +2 -2
  41. package/node_modules/nostr-tools/lib/esm/nipad.js +34 -0
  42. package/node_modules/nostr-tools/lib/esm/nipad.js.map +7 -0
  43. package/node_modules/nostr-tools/lib/esm/nipb7.js +1009 -98
  44. package/node_modules/nostr-tools/lib/esm/nipb7.js.map +4 -4
  45. package/node_modules/nostr-tools/lib/esm/relay.js +1 -1
  46. package/node_modules/nostr-tools/lib/esm/relay.js.map +2 -2
  47. package/node_modules/nostr-tools/lib/nostr.bundle.js +21 -5
  48. package/node_modules/nostr-tools/lib/nostr.bundle.js.map +2 -2
  49. package/node_modules/nostr-tools/lib/types/nip98.d.ts +1 -1
  50. package/node_modules/nostr-tools/lib/types/nipad.d.ts +16 -0
  51. package/node_modules/nostr-tools/lib/types/nipb7.d.ts +112 -5
  52. package/node_modules/nostr-tools/lib/types/relay.d.ts +1 -1
  53. package/node_modules/nostr-tools/package.json +7 -7
  54. package/openclaw.plugin.json +346 -0
  55. package/package.json +8 -5
  56. package/dist/secret-contract-D19RiJv_.js +0 -12
  57. package/dist/types-BN_HOsnq.js +0 -101
  58. package/node_modules/nostr-tools/lib/cjs/nipb0.js +0 -1087
  59. package/node_modules/nostr-tools/lib/cjs/nipb0.js.map +0 -7
  60. package/node_modules/nostr-tools/lib/esm/nipb0.js +0 -1072
  61. package/node_modules/nostr-tools/lib/esm/nipb0.js.map +0 -7
  62. package/node_modules/nostr-tools/lib/types/nipb0.d.ts +0 -128
  63. /package/node_modules/nostr-tools/lib/types/{nipb0.test.d.ts → nip46.test.d.ts} +0 -0
@@ -0,0 +1,50 @@
1
+ import { a as resolveBuzzAccountConfig } from "./types-CeupBG0P.js";
2
+ import { collectSecretInputAssignment, createChannelSecretTargetRegistryEntries, getChannelRecord, isRecord } from "openclaw/plugin-sdk/channel-secret-basic-runtime";
3
+ //#region extensions/buzz/src/secret-contract.ts
4
+ const fields = ["privateKey", "authTag"];
5
+ const secretTargetRegistryEntries = createChannelSecretTargetRegistryEntries({
6
+ channelKey: "buzz",
7
+ account: fields,
8
+ channel: fields
9
+ });
10
+ function collectRuntimeConfigAssignments(params) {
11
+ const root = getChannelRecord(params.config, "buzz");
12
+ if (!root) return;
13
+ const collect = (accountId, account, rootIdentity = false) => {
14
+ const resolved = resolveBuzzAccountConfig({
15
+ cfg: params.config,
16
+ accountId
17
+ });
18
+ const active = resolved.config.enabled !== false && (!rootIdentity || resolved.allowEnv);
19
+ const configPath = rootIdentity ? "channels.buzz" : resolved.configPath;
20
+ for (const field of fields) collectSecretInputAssignment({
21
+ value: account[field],
22
+ path: `${configPath}.${field}`,
23
+ expected: "string",
24
+ defaults: params.defaults,
25
+ context: params.context,
26
+ active,
27
+ inactiveReason: "Buzz identity is disabled or replaced by accounts.default.",
28
+ owner: {
29
+ ownerKind: "account",
30
+ ownerId: `buzz:${accountId}`,
31
+ requiredForGateway: false,
32
+ disposition: "isolate",
33
+ contract: resolved.config
34
+ },
35
+ apply: (value) => {
36
+ account[field] = value;
37
+ }
38
+ });
39
+ };
40
+ collect("default", root, true);
41
+ if (isRecord(root.accounts)) {
42
+ for (const [accountId, account] of Object.entries(root.accounts).toSorted(([a], [b]) => a.localeCompare(b))) if (isRecord(account)) collect(accountId, account);
43
+ }
44
+ }
45
+ const channelSecrets = {
46
+ secretTargetRegistryEntries,
47
+ collectRuntimeConfigAssignments
48
+ };
49
+ //#endregion
50
+ export { collectRuntimeConfigAssignments as n, secretTargetRegistryEntries as r, channelSecrets as t };
@@ -1,2 +1,2 @@
1
- import { n as collectRuntimeConfigAssignments, r as secretTargetRegistryEntries, t as channelSecrets } from "./secret-contract-D19RiJv_.js";
1
+ import { n as collectRuntimeConfigAssignments, r as secretTargetRegistryEntries, t as channelSecrets } from "./secret-contract-DNuqbK64.js";
2
2
  export { channelSecrets, collectRuntimeConfigAssignments, secretTargetRegistryEntries };
@@ -1,5 +1,5 @@
1
- import { i as resolveBuzzAccount, o as resolveDefaultBuzzAccountId, r as listBuzzAccountIds } from "./types-BN_HOsnq.js";
2
- import { a as BuzzConfigSchema, n as buzzSetupContract, t as buzzSetupWizard } from "./setup-surface-BvQgEJ7O.js";
1
+ import { c as BuzzConfigSchema, i as resolveBuzzAccount, r as listBuzzAccountIds, s as resolveDefaultBuzzAccountId } from "./types-CeupBG0P.js";
2
+ import { n as buzzSetupContract, t as buzzSetupWizard } from "./setup-surface-DDUmAluL.js";
3
3
  import { describeAccountSnapshot } from "openclaw/plugin-sdk/account-helpers";
4
4
  //#region extensions/buzz/src/channel.setup.ts
5
5
  const buzzSetupPlugin = {
@@ -18,7 +18,10 @@ const buzzSetupPlugin = {
18
18
  chatTypes: ["group"],
19
19
  threads: true
20
20
  },
21
- reload: { configPrefixes: ["channels.buzz"] },
21
+ reload: {
22
+ configPrefixes: ["channels.buzz"],
23
+ accountScopedRestart: true
24
+ },
22
25
  configSchema: BuzzConfigSchema,
23
26
  setupContract: buzzSetupContract,
24
27
  setupWizard: buzzSetupWizard,
@@ -1,36 +1,10 @@
1
- import { a as resolveBuzzPublicKey, f as parseBuzzTarget, i as resolveBuzzAccount, n as decodeBuzzPrivateKey, s as BUZZ_CHANNEL_ID_PATTERN } from "./types-BN_HOsnq.js";
2
- import { a as parseBuzzRoomMembershipEvent, c as parseBuzzAuthTag, l as openBuzzRelaySubscription, s as connectAuthenticatedBuzzRelaySession, t as BUZZ_ROOM_MEMBERSHIP_KIND, u as queryBuzzRelaySnapshot } from "./room-membership-DSAkyE0a.js";
3
- import { GroupPolicySchema, MarkdownConfigSchema, buildChannelConfigSchema } from "openclaw/plugin-sdk/channel-config-schema";
4
- import { buildSecretInputSchema } from "openclaw/plugin-sdk/secret-input";
5
- import { z } from "zod";
1
+ import { a as resolveBuzzAccountConfig, i as resolveBuzzAccount, l as BUZZ_CHANNEL_ID_PATTERN, m as parseBuzzTarget, n as decodeBuzzPrivateKey, o as resolveBuzzPublicKey, r as listBuzzAccountIds } from "./types-CeupBG0P.js";
2
+ import { c as openBuzzRelaySubscription, i as parseBuzzRoomMembershipEvent, l as queryBuzzRelaySnapshot, o as connectAuthenticatedBuzzRelaySession, s as parseBuzzAuthTag, t as BUZZ_ROOM_MEMBERSHIP_KIND, u as isNewerBuzzRevision } from "./room-membership-a8IHfdgL.js";
6
3
  import { generateSecretKey, nip19 } from "nostr-tools";
7
4
  import { sleep } from "openclaw/plugin-sdk/runtime-env";
8
5
  import { defineChannelSetupContract } from "openclaw/plugin-sdk/channel-setup";
9
- import { DEFAULT_ACCOUNT_ID, applyAccountNameToChannelSection, hasConfiguredSecretInput as hasConfiguredSecretInput$1, patchTopLevelChannelConfigSection, runSingleChannelSecretStep } from "openclaw/plugin-sdk/setup";
10
- import { DEFAULT_ACCOUNT_ID as DEFAULT_ACCOUNT_ID$1 } from "openclaw/plugin-sdk/setup-runtime";
6
+ import { patchScopedAccountConfig, patchTopLevelChannelConfigSection, promptAccountId, runSingleChannelSecretStep } from "openclaw/plugin-sdk/setup";
11
7
  import { isIP } from "node:net";
12
- //#region extensions/buzz/src/config-schema.ts
13
- const BuzzGroupConfigSchema = z.object({
14
- enabled: z.boolean().optional(),
15
- requireMention: z.boolean().optional(),
16
- groupPolicy: GroupPolicySchema.optional(),
17
- groupAllowFrom: z.array(z.union([z.string(), z.number()])).optional()
18
- }).strict();
19
- const BuzzConfigSchema = buildChannelConfigSchema(z.object({
20
- name: z.string().optional(),
21
- enabled: z.boolean().optional(),
22
- configWrites: z.boolean().optional(),
23
- responsePrefix: z.string().optional(),
24
- markdown: MarkdownConfigSchema,
25
- relayUrl: z.string().url().and(z.string().regex(/^[wW][sS][sS]?:\/\//, "Buzz relay URL must use ws:// or wss://")).optional(),
26
- privateKey: buildSecretInputSchema().optional(),
27
- authTag: buildSecretInputSchema().optional(),
28
- groupPolicy: GroupPolicySchema.optional().default("allowlist"),
29
- groupAllowFrom: z.array(z.union([z.string(), z.number()])).optional(),
30
- groups: z.record(z.string().regex(BUZZ_CHANNEL_ID_PATTERN, "Buzz group key must be a channel UUID"), BuzzGroupConfigSchema).optional(),
31
- defaultTo: z.string().optional()
32
- }).strict());
33
- //#endregion
34
8
  //#region extensions/buzz/src/room-membership-notification.ts
35
9
  const BUZZ_MEMBER_ADDED_NOTIFICATION_KIND = 44100;
36
10
  const BUZZ_MEMBER_REMOVED_NOTIFICATION_KIND = 44101;
@@ -126,7 +100,13 @@ async function discoverBuzzRoomsOnRelay(params) {
126
100
  for (const event of metadataEvents) {
127
101
  const roomId = tagValue(event, "d")?.toLowerCase();
128
102
  const current = roomId ? latestMetadata.get(roomId) : void 0;
129
- if (event.kind !== METADATA_KIND || event.pubkey.toLowerCase() !== params.relayPublicKey || !roomId || !roomIds.includes(roomId) || current && (current.created_at > event.created_at || current.created_at === event.created_at && current.id <= event.id)) continue;
103
+ if (event.kind !== METADATA_KIND || event.pubkey.toLowerCase() !== params.relayPublicKey || !roomId || !roomIds.includes(roomId) || !isNewerBuzzRevision({
104
+ createdAt: event.created_at,
105
+ eventId: event.id
106
+ }, current ? {
107
+ createdAt: current.created_at,
108
+ eventId: current.id
109
+ } : void 0)) continue;
130
110
  latestMetadata.set(roomId, event);
131
111
  }
132
112
  return roomIds.flatMap((id) => {
@@ -176,10 +156,25 @@ function validRelayUrl(value) {
176
156
  return false;
177
157
  }
178
158
  }
179
- function resolveComparableCurrentKey(cfg) {
180
- const configured = cfg.channels?.buzz?.privateKey;
159
+ function patchBuzzAccountConfig(params) {
160
+ const { accountId, allowEnv } = resolveBuzzAccountConfig(params);
161
+ return patchScopedAccountConfig({
162
+ ...params,
163
+ accountId,
164
+ channelKey: "buzz",
165
+ scopeDefaultToAccounts: !allowEnv,
166
+ ensureChannelEnabled: false,
167
+ ensureAccountEnabled: false
168
+ });
169
+ }
170
+ function resolveComparableCurrentKey(cfg, accountId) {
171
+ const { config, allowEnv } = resolveBuzzAccountConfig({
172
+ cfg,
173
+ accountId
174
+ });
175
+ const configured = config.privateKey;
181
176
  if (configured !== void 0) return typeof configured === "string" ? configured.trim() || void 0 : void 0;
182
- return process.env.BUZZ_PRIVATE_KEY?.trim() || void 0;
177
+ return allowEnv ? process.env.BUZZ_PRIVATE_KEY?.trim() || void 0 : void 0;
183
178
  }
184
179
  function isSameBuzzIdentity(currentKey, nextKey) {
185
180
  if (!currentKey || !nextKey) return false;
@@ -216,16 +211,22 @@ const buzzSetupContract = defineChannelSetupContract({
216
211
  }
217
212
  },
218
213
  adapter: {
219
- applyAccountName: ({ cfg, accountId, name }) => applyAccountNameToChannelSection({
214
+ configPromotion: "preserve-root",
215
+ resolveAccountId: ({ cfg, accountId }) => resolveBuzzAccountConfig({
216
+ cfg,
217
+ accountId
218
+ }).accountId,
219
+ applyAccountName: ({ cfg, accountId, name }) => name?.trim() ? patchBuzzAccountConfig({
220
220
  cfg,
221
- channelKey: "buzz",
222
221
  accountId,
223
- name
224
- }),
225
- validateInput: ({ accountId, input }) => {
226
- if (accountId !== DEFAULT_ACCOUNT_ID$1) return "Buzz currently supports only the default account.";
222
+ patch: { name: name.trim() }
223
+ }) : cfg,
224
+ validateInput: ({ cfg, accountId, input }) => {
227
225
  if (!validRelayUrl(input.relayUrl)) return "Buzz requires --relay-url with a ws:// or wss:// URL.";
228
- if (input.useEnv) return null;
226
+ if (input.useEnv) return resolveBuzzAccountConfig({
227
+ cfg,
228
+ accountId
229
+ }).allowEnv ? null : "Buzz --use-env is only supported for the root default identity; use an explicit private key or SecretRef for this account.";
229
230
  const privateKey = input.privateKey?.trim();
230
231
  if (!privateKey) return "Buzz requires --private-key or --use-env.";
231
232
  try {
@@ -235,22 +236,18 @@ const buzzSetupContract = defineChannelSetupContract({
235
236
  }
236
237
  return null;
237
238
  },
238
- applyAccountConfig: ({ cfg, input }) => {
239
- const keepAuthTag = isSameBuzzIdentity(resolveComparableCurrentKey(cfg), input.useEnv ? process.env.BUZZ_PRIVATE_KEY?.trim() : input.privateKey?.trim());
240
- const { privateKey: _privateKey, authTag, ...existing } = cfg.channels?.buzz ?? {};
241
- return {
242
- ...cfg,
243
- channels: {
244
- ...cfg.channels,
245
- buzz: {
246
- ...existing,
247
- enabled: true,
248
- relayUrl: input.relayUrl?.trim(),
249
- ...keepAuthTag && authTag !== void 0 ? { authTag } : {},
250
- ...input.useEnv ? {} : { privateKey: input.privateKey?.trim() }
251
- }
239
+ applyAccountConfig: ({ cfg, accountId, input }) => {
240
+ return patchBuzzAccountConfig({
241
+ cfg,
242
+ accountId,
243
+ clearFields: ["privateKey", ...isSameBuzzIdentity(resolveComparableCurrentKey(cfg, accountId), input.useEnv ? process.env.BUZZ_PRIVATE_KEY?.trim() : input.privateKey?.trim()) ? [] : ["authTag"]],
244
+ patch: {
245
+ enabled: true,
246
+ ...input.name?.trim() ? { name: input.name.trim() } : {},
247
+ relayUrl: input.relayUrl?.trim(),
248
+ ...input.useEnv ? {} : { privateKey: input.privateKey?.trim() }
252
249
  }
253
- };
250
+ });
254
251
  }
255
252
  }
256
253
  });
@@ -454,13 +451,6 @@ async function verifyBuzzAfterSetup(params) {
454
451
  //#endregion
455
452
  //#region extensions/buzz/src/setup-surface.ts
456
453
  const channel = "buzz";
457
- function patchBuzzConfig(cfg, patch) {
458
- return patchTopLevelChannelConfigSection({
459
- cfg,
460
- channel,
461
- patch
462
- });
463
- }
464
454
  function validateRelayUrl(value) {
465
455
  try {
466
456
  const url = new URL(value.trim());
@@ -476,10 +466,6 @@ function isRemoteInsecureRelayUrl(value) {
476
466
  const isLoopback = hostname === "localhost" || hostname.endsWith(".localhost") || hostname === "::1" || isIpv4Loopback;
477
467
  return url.protocol === "ws:" && !isLoopback;
478
468
  }
479
- function isBuzzSetupConfigured(cfg) {
480
- const buzzConfig = cfg.channels?.buzz;
481
- return Boolean((buzzConfig?.relayUrl?.trim() || process.env.BUZZ_RELAY_URL?.trim()) && (hasConfiguredSecretInput$1(buzzConfig?.privateKey, cfg.secrets?.defaults) || process.env.BUZZ_PRIVATE_KEY?.trim()));
482
- }
483
469
  async function promptRelayUrl(params) {
484
470
  while (true) {
485
471
  const relayUrl = (await params.prompter.text({
@@ -509,35 +495,53 @@ async function resolveRelayUrl(params) {
509
495
  prompter: params.prompter
510
496
  });
511
497
  }
512
- function resolvedConfiguredKey(cfg) {
513
- const value = cfg.channels?.buzz?.privateKey;
514
- return typeof value === "string" && value.trim() ? value.trim() : void 0;
515
- }
516
- function resolvedCurrentKey(cfg) {
517
- return cfg.channels?.buzz?.privateKey === void 0 ? process.env.BUZZ_PRIVATE_KEY?.trim() || void 0 : resolvedConfiguredKey(cfg);
498
+ async function resolveSetupCredential(params) {
499
+ const { config, configPath, allowEnv } = resolveBuzzAccountConfig(params);
500
+ const { resolveConfiguredSecretInputWithFallback } = await import("openclaw/plugin-sdk/secret-input-runtime");
501
+ const resolved = await resolveConfiguredSecretInputWithFallback({
502
+ config: params.cfg,
503
+ env: process.env,
504
+ value: config[params.field],
505
+ path: `${configPath}.${params.field}`,
506
+ unresolvedReasonStyle: "detailed",
507
+ readFallback: allowEnv ? () => params.field === "privateKey" ? process.env.BUZZ_PRIVATE_KEY : process.env.BUZZ_AUTH_TAG : void 0
508
+ });
509
+ if (resolved.unresolvedRefReason) throw new Error(`${resolved.unresolvedRefReason} Make the secret available and rerun Buzz setup.`);
510
+ return resolved.value;
518
511
  }
519
512
  async function resolvePrivateKey(params) {
520
- const hasExistingIdentity = hasConfiguredSecretInput$1(params.cfg.channels?.buzz?.privateKey, params.cfg.secrets?.defaults) || Boolean(process.env.BUZZ_PRIVATE_KEY?.trim());
521
- const currentPrivateKey = resolvedCurrentKey(params.cfg);
522
- if (hasExistingIdentity) {
523
- const resolvedPrivateKey = currentPrivateKey;
524
- if (resolvedPrivateKey) decodeBuzzPrivateKey(resolvedPrivateKey);
513
+ const { allowEnv } = resolveBuzzAccountConfig(params);
514
+ const currentPrivateKey = await resolveSetupCredential({
515
+ ...params,
516
+ field: "privateKey"
517
+ });
518
+ if (currentPrivateKey) {
519
+ decodeBuzzPrivateKey(currentPrivateKey);
525
520
  return {
526
521
  cfg: params.cfg,
527
- resolvedPrivateKey
522
+ resolvedPrivateKey: currentPrivateKey
528
523
  };
529
524
  }
530
525
  if (params.secretInputMode !== "ref") {
531
- let privateKey = params.generatedPrivateKeys.get(params.prompter);
526
+ let accountKeys = params.generatedPrivateKeys.get(params.prompter);
527
+ if (!accountKeys) {
528
+ accountKeys = /* @__PURE__ */ new Map();
529
+ params.generatedPrivateKeys.set(params.prompter, accountKeys);
530
+ }
531
+ let privateKey = accountKeys.get(params.accountId);
532
532
  if (!privateKey) {
533
533
  privateKey = nip19.nsecEncode(params.generate());
534
- params.generatedPrivateKeys.set(params.prompter, privateKey);
534
+ accountKeys.set(params.accountId, privateKey);
535
535
  }
536
536
  return {
537
- cfg: patchBuzzConfig(params.cfg, {
538
- enabled: true,
539
- privateKey,
540
- authTag: void 0
537
+ cfg: patchBuzzAccountConfig({
538
+ cfg: params.cfg,
539
+ accountId: params.accountId,
540
+ patch: {
541
+ enabled: true,
542
+ privateKey
543
+ },
544
+ clearFields: ["authTag"]
541
545
  }),
542
546
  resolvedPrivateKey: privateKey
543
547
  };
@@ -550,36 +554,31 @@ async function resolvePrivateKey(params) {
550
554
  secretInputMode: params.secretInputMode,
551
555
  accountConfigured: false,
552
556
  hasConfigToken: false,
553
- allowEnv: true,
554
- envValue: process.env.BUZZ_PRIVATE_KEY,
557
+ allowEnv,
558
+ envValue: allowEnv ? process.env.BUZZ_PRIVATE_KEY : void 0,
555
559
  envPrompt: "Use BUZZ_PRIVATE_KEY?",
556
560
  keepPrompt: "Keep the existing Buzz bot private key?",
557
561
  inputPrompt: "Buzz bot private key (nsec or 64-character hex)",
558
- preferredEnvVar: "BUZZ_PRIVATE_KEY",
559
- applyUseEnv: (cfg) => {
560
- const envPrivateKey = process.env.BUZZ_PRIVATE_KEY?.trim();
561
- const keepAuthTag = isSameBuzzIdentity(currentPrivateKey, envPrivateKey);
562
- const { privateKey: _privateKey, authTag, ...buzz } = cfg.channels?.buzz ?? {};
563
- return {
564
- ...cfg,
565
- channels: {
566
- ...cfg.channels,
567
- buzz: {
568
- ...buzz,
569
- enabled: true,
570
- ...keepAuthTag && authTag !== void 0 ? { authTag } : {}
571
- }
572
- }
573
- };
574
- },
575
- applySet: (cfg, value, resolvedValue) => patchBuzzConfig(cfg, {
576
- enabled: true,
577
- privateKey: value,
578
- ...isSameBuzzIdentity(currentPrivateKey, resolvedValue) ? {} : { authTag: void 0 }
562
+ ...allowEnv ? { preferredEnvVar: "BUZZ_PRIVATE_KEY" } : {},
563
+ applyUseEnv: (cfg) => patchBuzzAccountConfig({
564
+ cfg,
565
+ accountId: params.accountId,
566
+ patch: { enabled: true },
567
+ clearFields: ["privateKey", "authTag"]
568
+ }),
569
+ applySet: (cfg, value) => patchBuzzAccountConfig({
570
+ cfg,
571
+ accountId: params.accountId,
572
+ patch: {
573
+ enabled: true,
574
+ privateKey: value
575
+ },
576
+ clearFields: ["authTag"]
579
577
  })
580
578
  });
581
- const resolvedPrivateKey = secretStep.resolvedValue ?? (secretStep.action === "keep" ? resolvedConfiguredKey(secretStep.cfg) ?? currentPrivateKey : void 0);
582
- if (resolvedPrivateKey) decodeBuzzPrivateKey(resolvedPrivateKey);
579
+ const resolvedPrivateKey = secretStep.resolvedValue;
580
+ if (!resolvedPrivateKey) throw new Error("Buzz bot private key could not be resolved. Make the secret available and rerun setup.");
581
+ decodeBuzzPrivateKey(resolvedPrivateKey);
583
582
  return {
584
583
  cfg: secretStep.cfg,
585
584
  resolvedPrivateKey
@@ -589,37 +588,34 @@ async function promptRooms(params) {
589
588
  if (params.rooms.length === 1) return [params.rooms[0].id];
590
589
  const configuredRooms = new Set(params.configuredRoomIds);
591
590
  const preservedRoomIds = params.rooms.map((room) => room.id).filter((roomId) => configuredRooms.has(roomId));
592
- return await params.prompter.multiselect({
593
- message: "Select authorized Buzz rooms",
594
- options: params.rooms.map((room) => ({
595
- value: room.id,
596
- label: room.name,
597
- hint: room.about ?? room.id
598
- })),
599
- initialValues: preservedRoomIds.length > 0 ? preservedRoomIds : params.rooms.map((room) => room.id)
600
- });
601
- }
602
- function pauseBuzzSetup(cfg) {
603
- return {
604
- cfg: patchBuzzConfig(cfg, { enabled: false }),
605
- completion: "paused"
606
- };
591
+ while (true) {
592
+ const roomIds = await params.prompter.multiselect({
593
+ message: "Select authorized Buzz rooms",
594
+ options: params.rooms.map((room) => ({
595
+ value: room.id,
596
+ label: room.name,
597
+ hint: room.about ?? room.id
598
+ })),
599
+ initialValues: preservedRoomIds.length > 0 ? preservedRoomIds : params.rooms.map((room) => room.id)
600
+ });
601
+ if (roomIds.length > 0) return roomIds;
602
+ await params.prompter.note("Select at least one authorized room, or go back to leave setup.", "Buzz room selection required");
603
+ }
607
604
  }
608
605
  async function noteBuzzAccessInstructions(params) {
609
- const npub = params.publicKey ? nip19.npubEncode(params.publicKey) : "<BOT_PUBLIC_KEY>";
610
- const hex = params.publicKey ?? "<64_CHAR_BOT_PUBLIC_KEY>";
606
+ const npub = nip19.npubEncode(params.publicKey);
611
607
  await params.prompter.note([
612
608
  ...params.discoveryError ? [`Status: ${params.discoveryError}`, ""] : [],
613
609
  `Relay: ${params.relayUrl}`,
614
610
  `Bot npub: ${npub}`,
615
- `Bot hex public key: ${hex}`,
611
+ `Bot hex public key: ${params.publicKey}`,
616
612
  "",
617
613
  "Run as the existing human room owner/admin:",
618
- `buzz channels add-member --channel <ROOM_UUID> --pubkey ${hex} --role bot`,
614
+ `buzz channels add-member --channel <ROOM_UUID> --pubkey ${params.publicKey} --role bot`,
619
615
  "",
620
616
  "OpenClaw is waiting for Buzz to confirm the Bot role automatically.",
621
617
  "Local `just dev` needs no separate community-member step.",
622
- `Closed relay only: first run buzz-admin add-member --pubkey ${hex} --role member.`,
618
+ `Closed relay only: first run buzz-admin add-member --pubkey ${params.publicKey} --role member.`,
623
619
  "Never paste that human private key into OpenClaw."
624
620
  ].join("\n"), "Buzz room access required");
625
621
  }
@@ -632,10 +628,11 @@ function createBuzzSetupWizard(dependencies = {}) {
632
628
  const generatedPrivateKeys = /* @__PURE__ */ new WeakMap();
633
629
  return {
634
630
  channel,
635
- getStatus: async ({ cfg }) => {
636
- const buzzConfig = cfg.channels?.buzz;
637
- const configured = isBuzzSetupConfigured(cfg);
638
- const enabled = buzzConfig?.enabled !== false;
631
+ getStatus: async ({ cfg, accountOverrides }) => {
632
+ const { configured, enabled } = resolveBuzzAccount({
633
+ cfg,
634
+ accountId: accountOverrides.buzz
635
+ });
639
636
  const status = !configured ? "needs relay URL and bot identity" : enabled ? "configured" : "configured but disabled";
640
637
  return {
641
638
  channel,
@@ -644,20 +641,47 @@ function createBuzzSetupWizard(dependencies = {}) {
644
641
  selectionHint: status
645
642
  };
646
643
  },
647
- configure: async ({ cfg, prompter, options }) => {
648
- const existingBuzzConfig = cfg.channels?.buzz;
649
- const hasExistingAccessConfig = existingBuzzConfig?.groupPolicy !== void 0 || existingBuzzConfig?.groupAllowFrom !== void 0 || existingBuzzConfig?.groups !== void 0;
650
- const useFreshAccessDefaults = !isBuzzSetupConfigured(cfg) && !hasExistingAccessConfig;
644
+ configure: async ({ cfg, prompter, options, accountOverrides, shouldPromptAccountIds }) => {
645
+ let accountId = resolveBuzzAccountConfig({
646
+ cfg,
647
+ accountId: accountOverrides.buzz
648
+ }).accountId;
649
+ if (shouldPromptAccountIds && !accountOverrides.buzz?.trim()) accountId = await promptAccountId({
650
+ cfg,
651
+ prompter,
652
+ label: "Buzz",
653
+ currentId: accountId,
654
+ listAccountIds: listBuzzAccountIds,
655
+ defaultAccountId: accountId
656
+ });
657
+ const { config: existingBuzzConfig } = resolveBuzzAccountConfig({
658
+ cfg,
659
+ accountId
660
+ });
661
+ const rootPolicy = cfg.channels?.buzz;
662
+ const rawAccount = rootPolicy?.accounts?.[accountId];
663
+ const hasExistingAccessConfig = rootPolicy?.groupPolicy !== void 0 || rootPolicy?.groupAllowFrom !== void 0 || rawAccount?.groupPolicy !== void 0 || rawAccount?.groupAllowFrom !== void 0 || existingBuzzConfig.groups !== void 0;
664
+ const existingAccount = resolveBuzzAccount({
665
+ cfg,
666
+ accountId
667
+ });
668
+ const useFreshAccessDefaults = !existingAccount.configured && !hasExistingAccessConfig;
669
+ const configuredRelayUrl = existingAccount.relayUrl;
651
670
  const relayUrl = await resolveRelayUrl({
652
- configuredValue: existingBuzzConfig?.relayUrl?.trim() || process.env.BUZZ_RELAY_URL?.trim(),
671
+ configuredValue: configuredRelayUrl,
653
672
  prompter
654
673
  });
655
- let next = patchBuzzConfig(cfg, {
656
- enabled: true,
657
- relayUrl
674
+ let next = patchBuzzAccountConfig({
675
+ cfg,
676
+ accountId,
677
+ patch: {
678
+ enabled: true,
679
+ relayUrl
680
+ }
658
681
  });
659
682
  const identity = await resolvePrivateKey({
660
683
  cfg: next,
684
+ accountId,
661
685
  prompter,
662
686
  secretInputMode: options?.secretInputMode,
663
687
  generate,
@@ -666,15 +690,14 @@ function createBuzzSetupWizard(dependencies = {}) {
666
690
  });
667
691
  next = identity.cfg;
668
692
  const privateKey = identity.resolvedPrivateKey;
669
- let publicKey;
670
- if (privateKey) publicKey = resolveBuzzPublicKey(privateKey);
671
- if (!privateKey) {
672
- await prompter.note("OpenClaw cannot resolve the configured private-key reference during setup, so room access cannot be verified. The relay URL and identity reference will be saved with Buzz disabled. Make the secret available and rerun setup.", "Buzz setup paused");
673
- return pauseBuzzSetup(next);
674
- }
693
+ const publicKey = resolveBuzzPublicKey(privateKey);
675
694
  let discoveredRooms = [];
676
695
  let discoveryError;
677
- const authTag = resolveBuzzAccount({ cfg: next }).authTag;
696
+ const authTag = await resolveSetupCredential({
697
+ cfg: next,
698
+ accountId,
699
+ field: "authTag"
700
+ });
678
701
  const discoverAuthorizedRooms = async () => {
679
702
  try {
680
703
  const rooms = await discoverRooms({
@@ -725,17 +748,13 @@ function createBuzzSetupWizard(dependencies = {}) {
725
748
  progress.stop(discoveredRooms.length > 0 ? "Buzz room access confirmed" : "Buzz room access not found");
726
749
  if (discoveredRooms.length === 0 && discoveryError) await prompter.note(discoveryError, "Buzz room access not ready");
727
750
  }
728
- const configuredGroups = cfg.channels?.buzz?.groups ?? {};
751
+ const configuredGroups = existingBuzzConfig.groups ?? {};
729
752
  const roomIds = await promptRooms({
730
753
  rooms: discoveredRooms,
731
754
  configuredRoomIds: Object.keys(configuredGroups),
732
755
  prompter
733
756
  });
734
- if (roomIds.length === 0) {
735
- await prompter.note("No rooms were selected. Relay URL and bot identity will be saved with Buzz disabled.", "Buzz setup paused");
736
- return pauseBuzzSetup(next);
737
- }
738
- const existingDefault = cfg.channels?.buzz?.defaultTo;
757
+ const existingDefault = existingBuzzConfig.defaultTo;
739
758
  const defaultTo = roomIds.length === 1 ? roomIds[0] : await prompter.select({
740
759
  message: "Choose the default Buzz room target",
741
760
  options: roomIds.map((roomId) => {
@@ -747,35 +766,43 @@ function createBuzzSetupWizard(dependencies = {}) {
747
766
  }),
748
767
  initialValue: existingDefault && roomIds.includes(existingDefault) ? existingDefault : roomIds[0]
749
768
  });
750
- next = patchBuzzConfig(next, {
751
- ...useFreshAccessDefaults ? {
752
- groupPolicy: "open",
753
- groupAllowFrom: void 0
754
- } : {},
755
- groups: Object.fromEntries(roomIds.map((roomId) => [roomId, {
756
- ...configuredGroups[roomId],
757
- enabled: configuredGroups[roomId]?.enabled ?? true,
758
- requireMention: configuredGroups[roomId]?.requireMention ?? !useFreshAccessDefaults
759
- }])),
760
- defaultTo
769
+ next = patchBuzzAccountConfig({
770
+ cfg: next,
771
+ accountId,
772
+ patch: {
773
+ ...useFreshAccessDefaults ? {
774
+ groupPolicy: "open",
775
+ groupAllowFrom: void 0
776
+ } : {},
777
+ groups: Object.fromEntries(roomIds.map((roomId) => [roomId, {
778
+ ...configuredGroups[roomId],
779
+ enabled: configuredGroups[roomId]?.enabled ?? true,
780
+ requireMention: configuredGroups[roomId]?.requireMention ?? !useFreshAccessDefaults
781
+ }])),
782
+ defaultTo
783
+ }
761
784
  });
762
785
  options?.onPostWriteHook?.({
763
786
  channel,
764
- accountId: DEFAULT_ACCOUNT_ID,
787
+ accountId,
765
788
  run: async ({ runtime }) => await verifyAfterWrite({
766
- accountId: DEFAULT_ACCOUNT_ID,
789
+ accountId,
767
790
  target: defaultTo,
768
791
  runtime
769
792
  })
770
793
  });
771
794
  return {
772
795
  cfg: next,
773
- accountId: DEFAULT_ACCOUNT_ID
796
+ accountId
774
797
  };
775
798
  },
776
- disable: (cfg) => patchBuzzConfig(cfg, { enabled: false })
799
+ disable: (cfg) => patchTopLevelChannelConfigSection({
800
+ cfg,
801
+ channel,
802
+ patch: { enabled: false }
803
+ })
777
804
  };
778
805
  }
779
806
  const buzzSetupWizard = createBuzzSetupWizard();
780
807
  //#endregion
781
- export { BuzzConfigSchema as a, startBuzzRoomMembershipNotifications as i, buzzSetupContract as n, discoverBuzzRooms as r, buzzSetupWizard as t };
808
+ export { startBuzzRoomMembershipNotifications as i, buzzSetupContract as n, discoverBuzzRooms as r, buzzSetupWizard as t };