@openclaw/buzz 2026.9.6 → 2026.9.7
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/assets/icon.png +0 -0
- package/dist/.setup/{adapter.runtime-FIC9NMFI.mjs → adapter.runtime-Bpx9a0g6.mjs} +2 -2
- package/dist/.setup/{channel-qV8kQiPi.mjs → channel-B7r1381j.mjs} +86 -243
- package/dist/.setup/directory-config-M9KSexdh.mjs +24 -0
- package/dist/.setup/directory-query-D1BNu8nO.mjs +22 -0
- package/dist/.setup/room-membership-7of91SyC.mjs +537 -0
- package/dist/.setup/{secret-contract-DjibkTDL.mjs → secret-contract-B2-Mrm4v.mjs} +1 -1
- package/dist/.setup/{secret-contract-api-DcFDI-eR.mjs → secret-contract-api-CfJYRXZf.mjs} +1 -1
- package/dist/.setup/setup-plugin-api-CtM1M1Oa.mjs +2 -0
- package/dist/.setup/{setup-plugin-api-DI8WfvKQ.mjs → setup-plugin-api-m1pk2Cow.mjs} +2 -2
- package/dist/.setup/{setup-surface-DOHxdPor.mjs → setup-surface-CVXY6_50.mjs} +10 -24
- package/dist/.setup/{types-CeupBG0P.mjs → types-DRUZMyTx.mjs} +10 -2
- package/dist/api.js +2 -2
- package/dist/channel-plugin-api.js +1 -1
- package/dist/directory-contract-api.js +1 -1
- package/dist/qa-runner-api.js +1 -1
- package/dist/secret-contract-api.js +1 -1
- package/dist/setup-entry.js +2 -2
- package/dist/setup-plugin-api.js +1 -1
- package/openclaw.plugin.json +19 -3
- package/package.json +4 -4
- package/dist/.setup/directory-config-CzHz6fzo.mjs +0 -43
- package/dist/.setup/room-membership-tqLQznFE.mjs +0 -276
- package/dist/.setup/setup-plugin-api-H-XWBzaF.mjs +0 -2
- package/dist/runtime-api.js +0 -1
|
@@ -0,0 +1,537 @@
|
|
|
1
|
+
import { l as BUZZ_CHANNEL_ID_PATTERN, m as parseBuzzTarget, u as buildBuzzTarget } from "./types-DRUZMyTx.mjs";
|
|
2
|
+
import { t as applyBuzzDirectoryQueryAndLimit } from "./directory-query-D1BNu8nO.mjs";
|
|
3
|
+
import { Relay, finalizeEvent } from "nostr-tools";
|
|
4
|
+
import { truncateUtf16Safe } from "openclaw/plugin-sdk/text-utility-runtime";
|
|
5
|
+
import { readProviderJsonObjectResponse } from "openclaw/plugin-sdk/provider-http";
|
|
6
|
+
import { fetchWithSsrFGuard, ssrfPolicyFromHttpBaseUrlAllowedOrigin } from "openclaw/plugin-sdk/ssrf-runtime";
|
|
7
|
+
//#region extensions/buzz/src/event-order.ts
|
|
8
|
+
function isNewerBuzzRevision(candidate, current) {
|
|
9
|
+
return !current || candidate.createdAt > current.createdAt || candidate.createdAt === current.createdAt && candidate.eventId < current.eventId;
|
|
10
|
+
}
|
|
11
|
+
const BUZZ_ROOM_METADATA_KIND = 39e3;
|
|
12
|
+
const DEFAULT_BUZZ_DIRECTORY_PROFILE_LIMIT = 2e3;
|
|
13
|
+
const HEX_PUBLIC_KEY_PATTERN$2 = /^[0-9a-f]{64}$/u;
|
|
14
|
+
const MAX_DIRECTORY_NAME_CHARS = 512;
|
|
15
|
+
const MAX_DIRECTORY_HANDLE_CHARS = 320;
|
|
16
|
+
const MAX_DIRECTORY_URL_CHARS = 4096;
|
|
17
|
+
function normalizeBoundedString(value, maxChars) {
|
|
18
|
+
if (typeof value !== "string") return;
|
|
19
|
+
const trimmed = value.trim();
|
|
20
|
+
if (!trimmed) return;
|
|
21
|
+
return truncateUtf16Safe(trimmed, maxChars);
|
|
22
|
+
}
|
|
23
|
+
function readPreferredString(params) {
|
|
24
|
+
if (Object.hasOwn(params.content, params.primary)) return normalizeBoundedString(params.content[params.primary], params.maxChars);
|
|
25
|
+
return normalizeBoundedString(params.content[params.fallback], params.maxChars);
|
|
26
|
+
}
|
|
27
|
+
function fallbackPublicKeyLabel(publicKey) {
|
|
28
|
+
return `${publicKey.slice(0, 8)}...${publicKey.slice(-6)}`;
|
|
29
|
+
}
|
|
30
|
+
function parseBuzzDirectoryProfileEvent(event) {
|
|
31
|
+
const publicKey = event.pubkey.trim().toLowerCase();
|
|
32
|
+
if (event.kind !== 0 || !HEX_PUBLIC_KEY_PATTERN$2.test(publicKey)) return;
|
|
33
|
+
let content;
|
|
34
|
+
try {
|
|
35
|
+
const parsed = JSON.parse(event.content);
|
|
36
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) return;
|
|
37
|
+
content = parsed;
|
|
38
|
+
} catch {
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
return {
|
|
42
|
+
publicKey,
|
|
43
|
+
displayName: readPreferredString({
|
|
44
|
+
content,
|
|
45
|
+
primary: "display_name",
|
|
46
|
+
fallback: "name",
|
|
47
|
+
maxChars: MAX_DIRECTORY_NAME_CHARS
|
|
48
|
+
}),
|
|
49
|
+
handle: normalizeBoundedString(content.nip05, MAX_DIRECTORY_HANDLE_CHARS),
|
|
50
|
+
avatarUrl: readPreferredString({
|
|
51
|
+
content,
|
|
52
|
+
primary: "picture",
|
|
53
|
+
fallback: "image",
|
|
54
|
+
maxChars: MAX_DIRECTORY_URL_CHARS
|
|
55
|
+
}),
|
|
56
|
+
createdAt: event.created_at,
|
|
57
|
+
eventId: event.id
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
function parseBuzzDirectoryRoomEvent(event) {
|
|
61
|
+
if (event.kind !== 39e3) return;
|
|
62
|
+
const roomId = event.tags.find((tag) => tag[0] === "d")?.[1]?.trim().toLowerCase();
|
|
63
|
+
if (!roomId) return;
|
|
64
|
+
try {
|
|
65
|
+
parseBuzzTarget(roomId);
|
|
66
|
+
} catch {
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
return {
|
|
70
|
+
roomId,
|
|
71
|
+
name: normalizeBoundedString(event.tags.find((tag) => tag[0] === "name")?.[1], MAX_DIRECTORY_NAME_CHARS),
|
|
72
|
+
archived: event.tags.some((tag) => tag[0] === "archived" && tag[1] === "true"),
|
|
73
|
+
createdAt: event.created_at,
|
|
74
|
+
eventId: event.id
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
var BuzzDirectoryState = class {
|
|
78
|
+
#publicKey;
|
|
79
|
+
#fallbackProfileName;
|
|
80
|
+
#configuredRoomIds;
|
|
81
|
+
#profileLimit;
|
|
82
|
+
#memberships = /* @__PURE__ */ new Map();
|
|
83
|
+
#profilePublicKeys = /* @__PURE__ */ new Set();
|
|
84
|
+
#profiles = /* @__PURE__ */ new Map();
|
|
85
|
+
#rooms = /* @__PURE__ */ new Map();
|
|
86
|
+
constructor(params) {
|
|
87
|
+
this.#publicKey = params.publicKey.trim().toLowerCase();
|
|
88
|
+
this.#fallbackProfileName = params.fallbackProfileName.trim() || "OpenClaw";
|
|
89
|
+
this.#configuredRoomIds = new Set(params.channelIds.map(parseBuzzTarget));
|
|
90
|
+
const requestedProfileLimit = params.profileLimit ?? DEFAULT_BUZZ_DIRECTORY_PROFILE_LIMIT;
|
|
91
|
+
this.#profileLimit = Number.isFinite(requestedProfileLimit) && requestedProfileLimit >= 0 ? Math.floor(requestedProfileLimit) : DEFAULT_BUZZ_DIRECTORY_PROFILE_LIMIT;
|
|
92
|
+
if (this.#profileLimit > 0) this.#profilePublicKeys.add(this.#publicKey);
|
|
93
|
+
}
|
|
94
|
+
replaceMemberships(memberships) {
|
|
95
|
+
const nextMemberships = /* @__PURE__ */ new Map();
|
|
96
|
+
const memberPublicKeys = /* @__PURE__ */ new Set();
|
|
97
|
+
for (const roomId of this.#configuredRoomIds) {
|
|
98
|
+
const membership = memberships.get(roomId);
|
|
99
|
+
if (!membership) continue;
|
|
100
|
+
nextMemberships.set(roomId, membership);
|
|
101
|
+
for (const publicKey of membership.members) memberPublicKeys.add(publicKey);
|
|
102
|
+
}
|
|
103
|
+
memberPublicKeys.delete(this.#publicKey);
|
|
104
|
+
const nextProfilePublicKeys = this.#profileLimit === 0 ? /* @__PURE__ */ new Set() : /* @__PURE__ */ new Set([this.#publicKey, ...[...memberPublicKeys].toSorted().slice(0, this.#profileLimit - 1)]);
|
|
105
|
+
const profileSelectionChanged = nextProfilePublicKeys.size !== this.#profilePublicKeys.size || [...nextProfilePublicKeys].some((publicKey) => !this.#profilePublicKeys.has(publicKey));
|
|
106
|
+
this.#memberships = nextMemberships;
|
|
107
|
+
this.#profilePublicKeys = nextProfilePublicKeys;
|
|
108
|
+
for (const publicKey of this.#profiles.keys()) if (!nextProfilePublicKeys.has(publicKey)) this.#profiles.delete(publicKey);
|
|
109
|
+
return profileSelectionChanged;
|
|
110
|
+
}
|
|
111
|
+
profilePublicKeys() {
|
|
112
|
+
return [...this.#profilePublicKeys];
|
|
113
|
+
}
|
|
114
|
+
activeRoomIds() {
|
|
115
|
+
return [...this.#configuredRoomIds].filter((roomId) => !this.#rooms.get(roomId)?.archived);
|
|
116
|
+
}
|
|
117
|
+
isRoomArchived(roomId) {
|
|
118
|
+
return this.#rooms.get(parseBuzzTarget(roomId))?.archived === true;
|
|
119
|
+
}
|
|
120
|
+
isBotMember(roomId, publicKey) {
|
|
121
|
+
return this.isMember(roomId, publicKey) && this.#memberships.get(parseBuzzTarget(roomId))?.roles.get(publicKey) === "bot";
|
|
122
|
+
}
|
|
123
|
+
isMember(roomId, publicKey) {
|
|
124
|
+
const normalizedRoomId = parseBuzzTarget(roomId);
|
|
125
|
+
return !this.#rooms.get(normalizedRoomId)?.archived && this.#memberships.get(normalizedRoomId)?.members.has(publicKey) === true;
|
|
126
|
+
}
|
|
127
|
+
applyProfileEvent(event) {
|
|
128
|
+
const profile = parseBuzzDirectoryProfileEvent(event);
|
|
129
|
+
if (!profile || !this.#profilePublicKeys.has(profile.publicKey) || !isNewerBuzzRevision(profile, this.#profiles.get(profile.publicKey))) return false;
|
|
130
|
+
this.#profiles.set(profile.publicKey, profile);
|
|
131
|
+
return true;
|
|
132
|
+
}
|
|
133
|
+
applyRoomEvent(event) {
|
|
134
|
+
const room = parseBuzzDirectoryRoomEvent(event);
|
|
135
|
+
if (!room || !this.#configuredRoomIds.has(room.roomId) || !isNewerBuzzRevision(room, this.#rooms.get(room.roomId))) return false;
|
|
136
|
+
this.#rooms.set(room.roomId, room);
|
|
137
|
+
return true;
|
|
138
|
+
}
|
|
139
|
+
resolveSenderName(publicKey) {
|
|
140
|
+
const normalized = publicKey.trim().toLowerCase();
|
|
141
|
+
return this.#profiles.get(normalized)?.displayName ?? fallbackPublicKeyLabel(normalized);
|
|
142
|
+
}
|
|
143
|
+
resolveRoomName(roomId) {
|
|
144
|
+
const normalized = parseBuzzTarget(roomId);
|
|
145
|
+
return this.#rooms.get(normalized)?.name ?? normalized;
|
|
146
|
+
}
|
|
147
|
+
self() {
|
|
148
|
+
return this.#buildUserEntry(this.#publicKey);
|
|
149
|
+
}
|
|
150
|
+
listPeers(params) {
|
|
151
|
+
const peers = /* @__PURE__ */ new Set();
|
|
152
|
+
for (const roomId of this.activeRoomIds()) {
|
|
153
|
+
const membership = this.#memberships.get(roomId);
|
|
154
|
+
if (!membership) continue;
|
|
155
|
+
for (const publicKey of membership.members) if (publicKey !== this.#publicKey) peers.add(publicKey);
|
|
156
|
+
}
|
|
157
|
+
const entries = [...peers].map((publicKey) => this.#buildUserEntry(publicKey)).toSorted(compareDirectoryEntries);
|
|
158
|
+
return applyBuzzDirectoryQueryAndLimit(entries, params);
|
|
159
|
+
}
|
|
160
|
+
listGroups(params) {
|
|
161
|
+
const entries = this.activeRoomIds().map((roomId) => this.#buildRoomEntry(roomId)).toSorted(compareDirectoryEntries);
|
|
162
|
+
return applyBuzzDirectoryQueryAndLimit(entries, params);
|
|
163
|
+
}
|
|
164
|
+
listGroupMembers(params) {
|
|
165
|
+
let roomId;
|
|
166
|
+
try {
|
|
167
|
+
roomId = parseBuzzTarget(params.groupId);
|
|
168
|
+
} catch {
|
|
169
|
+
return [];
|
|
170
|
+
}
|
|
171
|
+
if (this.#rooms.get(roomId)?.archived) return [];
|
|
172
|
+
const membership = this.#memberships.get(roomId);
|
|
173
|
+
if (!membership) return [];
|
|
174
|
+
const entries = [...membership.members].map((publicKey) => {
|
|
175
|
+
const entry = this.#buildUserEntry(publicKey);
|
|
176
|
+
entry.raw = {
|
|
177
|
+
publicKey,
|
|
178
|
+
role: membership.roles.get(publicKey),
|
|
179
|
+
roomId
|
|
180
|
+
};
|
|
181
|
+
return entry;
|
|
182
|
+
}).toSorted(compareDirectoryEntries);
|
|
183
|
+
return applyBuzzDirectoryQueryAndLimit(entries, { limit: params.limit });
|
|
184
|
+
}
|
|
185
|
+
mentionMembers(roomId) {
|
|
186
|
+
const normalized = parseBuzzTarget(roomId);
|
|
187
|
+
if (this.#rooms.get(normalized)?.archived) return;
|
|
188
|
+
const membership = this.#memberships.get(normalized);
|
|
189
|
+
if (!membership) return;
|
|
190
|
+
return [...membership.members].map((publicKey) => ({
|
|
191
|
+
publicKey,
|
|
192
|
+
displayName: this.#profiles.get(publicKey)?.displayName
|
|
193
|
+
})).toSorted((left, right) => left.publicKey.localeCompare(right.publicKey));
|
|
194
|
+
}
|
|
195
|
+
#buildUserEntry(publicKey) {
|
|
196
|
+
const profile = this.#profiles.get(publicKey);
|
|
197
|
+
return {
|
|
198
|
+
kind: "user",
|
|
199
|
+
id: publicKey,
|
|
200
|
+
name: profile?.displayName ?? (publicKey === this.#publicKey ? this.#fallbackProfileName : fallbackPublicKeyLabel(publicKey)),
|
|
201
|
+
handle: profile?.handle,
|
|
202
|
+
avatarUrl: profile?.avatarUrl,
|
|
203
|
+
raw: { publicKey }
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
#buildRoomEntry(roomId) {
|
|
207
|
+
const room = this.#rooms.get(roomId);
|
|
208
|
+
return {
|
|
209
|
+
kind: "group",
|
|
210
|
+
id: buildBuzzTarget(roomId),
|
|
211
|
+
name: room?.name ?? roomId,
|
|
212
|
+
handle: room?.name ? `#${room.name}` : void 0,
|
|
213
|
+
raw: { roomId }
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
};
|
|
217
|
+
function compareDirectoryEntries(a, b) {
|
|
218
|
+
const aLabel = a.name ?? a.handle ?? a.id;
|
|
219
|
+
const bLabel = b.name ?? b.handle ?? b.id;
|
|
220
|
+
return aLabel.localeCompare(bLabel) || a.id.localeCompare(b.id);
|
|
221
|
+
}
|
|
222
|
+
//#endregion
|
|
223
|
+
//#region extensions/buzz/src/subscription-budget.ts
|
|
224
|
+
const BUZZ_DIRECTORY_MAX_PROFILE_SUBSCRIPTIONS = 10;
|
|
225
|
+
const BUZZ_MAX_CONFIGURED_ROOMS = 1020;
|
|
226
|
+
function resolveBuzzSubscriptionBudget(roomCount) {
|
|
227
|
+
if (!Number.isSafeInteger(roomCount) || roomCount < 0) throw new Error("Buzz configured room count must be a non-negative integer");
|
|
228
|
+
const availableProfileSubscriptions = 1020 - roomCount;
|
|
229
|
+
if (availableProfileSubscriptions < 0) throw new Error(`Buzz supports at most ${BUZZ_MAX_CONFIGURED_ROOMS} configured rooms per account`);
|
|
230
|
+
return { profileLimit: Math.min(BUZZ_DIRECTORY_MAX_PROFILE_SUBSCRIPTIONS, availableProfileSubscriptions) * 200 };
|
|
231
|
+
}
|
|
232
|
+
//#endregion
|
|
233
|
+
//#region extensions/buzz/src/relay-subscription.ts
|
|
234
|
+
const snapshotQueries = /* @__PURE__ */ new WeakMap();
|
|
235
|
+
function openBuzzRelaySubscription(relay, filters, params, requestFilters = filters) {
|
|
236
|
+
relay.idleSince = void 0;
|
|
237
|
+
relay.ongoingOperations += 1;
|
|
238
|
+
let subscription;
|
|
239
|
+
try {
|
|
240
|
+
subscription = relay.prepareSubscription(filters, params);
|
|
241
|
+
} catch (error) {
|
|
242
|
+
relay.ongoingOperations -= 1;
|
|
243
|
+
if (relay.ongoingOperations === 0) {
|
|
244
|
+
relay.idleSince = Date.now();
|
|
245
|
+
relay.scheduleIdleClose();
|
|
246
|
+
}
|
|
247
|
+
throw error;
|
|
248
|
+
}
|
|
249
|
+
const frame = JSON.stringify([
|
|
250
|
+
"REQ",
|
|
251
|
+
subscription.id,
|
|
252
|
+
...requestFilters
|
|
253
|
+
]);
|
|
254
|
+
relay.send(frame).catch((error) => {
|
|
255
|
+
if (subscription.closed || relay.openSubs.get(subscription.id) !== subscription) return;
|
|
256
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
257
|
+
subscription.close(`Buzz relay subscription request failed: ${message}`);
|
|
258
|
+
});
|
|
259
|
+
return subscription;
|
|
260
|
+
}
|
|
261
|
+
async function queryBuzzRelaySnapshot(params) {
|
|
262
|
+
params.signal?.throwIfAborted();
|
|
263
|
+
const queries = snapshotQueries.get(params.relay) ?? {
|
|
264
|
+
active: 0,
|
|
265
|
+
waiting: []
|
|
266
|
+
};
|
|
267
|
+
snapshotQueries.set(params.relay, queries);
|
|
268
|
+
if (queries.active >= 3) await new Promise((resolve, reject) => {
|
|
269
|
+
const grant = () => {
|
|
270
|
+
params.signal?.removeEventListener("abort", onAbort);
|
|
271
|
+
resolve();
|
|
272
|
+
};
|
|
273
|
+
const onAbort = () => {
|
|
274
|
+
const index = queries.waiting.indexOf(grant);
|
|
275
|
+
if (index < 0) return;
|
|
276
|
+
queries.waiting.splice(index, 1);
|
|
277
|
+
params.signal?.removeEventListener("abort", onAbort);
|
|
278
|
+
const reason = params.signal?.reason ?? new Error(params.abortMessage);
|
|
279
|
+
reject(reason instanceof Error ? reason : new Error(params.failureMessage, { cause: reason }));
|
|
280
|
+
};
|
|
281
|
+
queries.waiting.push(grant);
|
|
282
|
+
params.signal?.addEventListener("abort", onAbort, { once: true });
|
|
283
|
+
if (params.signal?.aborted) onAbort();
|
|
284
|
+
});
|
|
285
|
+
else queries.active += 1;
|
|
286
|
+
try {
|
|
287
|
+
params.signal?.throwIfAborted();
|
|
288
|
+
return await queryBuzzRelaySnapshotNow(params);
|
|
289
|
+
} finally {
|
|
290
|
+
const next = queries.waiting.shift();
|
|
291
|
+
if (next) next();
|
|
292
|
+
else {
|
|
293
|
+
queries.active -= 1;
|
|
294
|
+
if (queries.active === 0) snapshotQueries.delete(params.relay);
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
async function queryBuzzRelaySnapshotNow(params) {
|
|
299
|
+
return await new Promise((resolve, reject) => {
|
|
300
|
+
let settled = false;
|
|
301
|
+
let receivedEose = false;
|
|
302
|
+
let subscriptionClosed = false;
|
|
303
|
+
let subscription;
|
|
304
|
+
const timeout = setTimeout(() => {
|
|
305
|
+
const error = new Error(params.timeoutMessage);
|
|
306
|
+
finish(error);
|
|
307
|
+
params.onTimeout?.(error);
|
|
308
|
+
if (params.closeRelayOnTimeout !== false) params.relay.close();
|
|
309
|
+
}, params.timeoutMs ?? 1e4);
|
|
310
|
+
const closeAfterRealEose = () => {
|
|
311
|
+
if (receivedEose && subscription && !subscriptionClosed) {
|
|
312
|
+
subscriptionClosed = true;
|
|
313
|
+
subscription.close(params.closeReason);
|
|
314
|
+
}
|
|
315
|
+
};
|
|
316
|
+
const finish = (error) => {
|
|
317
|
+
if (settled) return;
|
|
318
|
+
settled = true;
|
|
319
|
+
clearTimeout(timeout);
|
|
320
|
+
params.signal?.removeEventListener("abort", onAbort);
|
|
321
|
+
closeAfterRealEose();
|
|
322
|
+
if (error === void 0) resolve(params.result());
|
|
323
|
+
else reject(error instanceof Error ? error : new Error(params.failureMessage, { cause: error }));
|
|
324
|
+
};
|
|
325
|
+
const onAbort = () => finish(params.signal?.reason ?? new Error(params.abortMessage));
|
|
326
|
+
params.signal?.addEventListener("abort", onAbort, { once: true });
|
|
327
|
+
try {
|
|
328
|
+
subscription = openBuzzRelaySubscription(params.relay, params.filters, {
|
|
329
|
+
onevent: params.onEvent,
|
|
330
|
+
oneose: () => {
|
|
331
|
+
receivedEose = true;
|
|
332
|
+
if (settled) closeAfterRealEose();
|
|
333
|
+
else finish();
|
|
334
|
+
},
|
|
335
|
+
onclose: (reason) => {
|
|
336
|
+
if (reason !== params.closeReason) finish(new Error(params.closeMessage(reason)));
|
|
337
|
+
}
|
|
338
|
+
});
|
|
339
|
+
} catch (error) {
|
|
340
|
+
finish(error);
|
|
341
|
+
return;
|
|
342
|
+
}
|
|
343
|
+
closeAfterRealEose();
|
|
344
|
+
if (params.checkAbortAfterSubscribe && params.signal?.aborted) onAbort();
|
|
345
|
+
});
|
|
346
|
+
}
|
|
347
|
+
//#endregion
|
|
348
|
+
//#region extensions/buzz/src/relay-auth.ts
|
|
349
|
+
const AUTH_CHALLENGE_TIMEOUT_MS = 2e4;
|
|
350
|
+
const AUTH_CHALLENGE_POLL_MS = 25;
|
|
351
|
+
const RELAY_SESSION_SETUP_TIMEOUT_MS = 2e4;
|
|
352
|
+
const HEX_PUBLIC_KEY_PATTERN$1 = /^[0-9a-f]{64}$/u;
|
|
353
|
+
const BUZZ_RELAY_SOFTWARE = "https://github.com/block/buzz";
|
|
354
|
+
const BUZZ_LOCAL_DEV_RELAY_PUBLIC_KEY = "79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798";
|
|
355
|
+
function parseBuzzAuthTag(raw) {
|
|
356
|
+
if (!raw.trim()) return;
|
|
357
|
+
const parsed = JSON.parse(raw);
|
|
358
|
+
if (!Array.isArray(parsed) || parsed.length !== 4 || parsed[0] !== "auth" || parsed.some((value) => typeof value !== "string")) throw new Error("Buzz authTag must be [\"auth\",\"<pubkey>\",\"<conditions>\",\"<signature>\"]");
|
|
359
|
+
return parsed;
|
|
360
|
+
}
|
|
361
|
+
async function waitWithSignal(promise, signal) {
|
|
362
|
+
signal.throwIfAborted();
|
|
363
|
+
return await new Promise((resolve, reject) => {
|
|
364
|
+
const onAbort = () => {
|
|
365
|
+
const reason = signal.reason;
|
|
366
|
+
reject(reason instanceof Error ? reason : new Error("Buzz relay authentication aborted", { cause: reason }));
|
|
367
|
+
};
|
|
368
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
369
|
+
promise.then(resolve, reject).finally(() => signal.removeEventListener("abort", onAbort));
|
|
370
|
+
});
|
|
371
|
+
}
|
|
372
|
+
function createBuzzAuthSigner(params) {
|
|
373
|
+
return async (template) => finalizeEvent({
|
|
374
|
+
...template,
|
|
375
|
+
tags: params.authTag ? [...template.tags, params.authTag] : template.tags
|
|
376
|
+
}, params.secretKey);
|
|
377
|
+
}
|
|
378
|
+
function isLoopbackRelayUrl(relayUrl) {
|
|
379
|
+
const hostname = new URL(relayUrl).hostname.toLowerCase();
|
|
380
|
+
return hostname === "localhost" || hostname === "127.0.0.1" || hostname === "[::1]";
|
|
381
|
+
}
|
|
382
|
+
async function resolveBuzzRelayPublicKey(params) {
|
|
383
|
+
const infoUrl = new URL(params.relayUrl);
|
|
384
|
+
infoUrl.protocol = infoUrl.protocol === "wss:" ? "https:" : "http:";
|
|
385
|
+
const url = infoUrl.toString();
|
|
386
|
+
const { response, release } = await fetchWithSsrFGuard({
|
|
387
|
+
url,
|
|
388
|
+
init: { headers: { Accept: "application/nostr+json" } },
|
|
389
|
+
signal: params.signal,
|
|
390
|
+
policy: ssrfPolicyFromHttpBaseUrlAllowedOrigin(url),
|
|
391
|
+
auditContext: "buzz.relay_info"
|
|
392
|
+
});
|
|
393
|
+
try {
|
|
394
|
+
if (!response.ok) {
|
|
395
|
+
await response.body?.cancel().catch(() => void 0);
|
|
396
|
+
throw new Error(`Buzz relay information request failed with HTTP ${response.status}`);
|
|
397
|
+
}
|
|
398
|
+
const document = await readProviderJsonObjectResponse(response, "Buzz relay information");
|
|
399
|
+
const relayPublicKey = typeof document.self === "string" ? document.self.trim().toLowerCase() : "";
|
|
400
|
+
if (HEX_PUBLIC_KEY_PATTERN$1.test(relayPublicKey)) return relayPublicKey;
|
|
401
|
+
if (document.software === BUZZ_RELAY_SOFTWARE && isLoopbackRelayUrl(params.relayUrl)) return BUZZ_LOCAL_DEV_RELAY_PUBLIC_KEY;
|
|
402
|
+
throw new Error("Buzz relay information document is missing a valid NIP-11 self public key");
|
|
403
|
+
} finally {
|
|
404
|
+
await release();
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
async function connectAndAuthenticateBuzzRelay(params) {
|
|
408
|
+
const signAuth = createBuzzAuthSigner({
|
|
409
|
+
secretKey: params.secretKey,
|
|
410
|
+
authTag: params.authTag
|
|
411
|
+
});
|
|
412
|
+
await params.relay.connect({ abort: params.signal });
|
|
413
|
+
await authenticateBuzzRelay({
|
|
414
|
+
relay: params.relay,
|
|
415
|
+
signAuth,
|
|
416
|
+
signal: params.signal
|
|
417
|
+
});
|
|
418
|
+
params.relay.onauth = signAuth;
|
|
419
|
+
}
|
|
420
|
+
async function connectAuthenticatedBuzzRelay(params) {
|
|
421
|
+
const relay = new Relay(params.relayUrl, { enableReconnect: false });
|
|
422
|
+
try {
|
|
423
|
+
await connectAndAuthenticateBuzzRelay({
|
|
424
|
+
...params,
|
|
425
|
+
relay
|
|
426
|
+
});
|
|
427
|
+
return relay;
|
|
428
|
+
} catch (error) {
|
|
429
|
+
relay.close();
|
|
430
|
+
throw error;
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
async function connectAuthenticatedBuzzRelaySession(params) {
|
|
434
|
+
const relay = new Relay(params.relayUrl, { enableReconnect: false });
|
|
435
|
+
const setupAbort = new AbortController();
|
|
436
|
+
const signal = params.signal ? AbortSignal.any([params.signal, setupAbort.signal]) : setupAbort.signal;
|
|
437
|
+
let setupTimedOut = false;
|
|
438
|
+
const setupTimeout = setTimeout(() => {
|
|
439
|
+
setupTimedOut = true;
|
|
440
|
+
setupAbort.abort(/* @__PURE__ */ new Error("Timed out setting up Buzz relay session"));
|
|
441
|
+
}, RELAY_SESSION_SETUP_TIMEOUT_MS);
|
|
442
|
+
const authPromise = connectAndAuthenticateBuzzRelay({
|
|
443
|
+
...params,
|
|
444
|
+
relay,
|
|
445
|
+
signal
|
|
446
|
+
});
|
|
447
|
+
const relayIdentityPromise = resolveBuzzRelayPublicKey({
|
|
448
|
+
relayUrl: params.relayUrl,
|
|
449
|
+
signal
|
|
450
|
+
});
|
|
451
|
+
try {
|
|
452
|
+
const [, relayPublicKey] = await Promise.all([authPromise, relayIdentityPromise]);
|
|
453
|
+
return {
|
|
454
|
+
relay,
|
|
455
|
+
relayPublicKey
|
|
456
|
+
};
|
|
457
|
+
} catch (error) {
|
|
458
|
+
setupAbort.abort(error);
|
|
459
|
+
relay.close();
|
|
460
|
+
await Promise.allSettled([authPromise, relayIdentityPromise]);
|
|
461
|
+
if (setupTimedOut && !params.signal?.aborted) throw new Error("Timed out setting up Buzz relay session", { cause: error });
|
|
462
|
+
throw error;
|
|
463
|
+
} finally {
|
|
464
|
+
clearTimeout(setupTimeout);
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
async function authenticateBuzzRelay(params) {
|
|
468
|
+
const challengeTimeout = AbortSignal.timeout(AUTH_CHALLENGE_TIMEOUT_MS);
|
|
469
|
+
const signal = params.signal ? AbortSignal.any([params.signal, challengeTimeout]) : challengeTimeout;
|
|
470
|
+
try {
|
|
471
|
+
while (true) {
|
|
472
|
+
signal.throwIfAborted();
|
|
473
|
+
try {
|
|
474
|
+
await waitWithSignal(params.relay.auth(params.signAuth), signal);
|
|
475
|
+
return;
|
|
476
|
+
} catch (error) {
|
|
477
|
+
if (!(error instanceof Error && error.message === "can't perform auth, no challenge was received")) throw error;
|
|
478
|
+
await waitWithSignal(new Promise((resolve) => {
|
|
479
|
+
setTimeout(resolve, AUTH_CHALLENGE_POLL_MS);
|
|
480
|
+
}), signal);
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
} catch (error) {
|
|
484
|
+
if (challengeTimeout.aborted && !params.signal?.aborted) throw new Error("Timed out waiting for Buzz NIP-42 authentication challenge", { cause: error });
|
|
485
|
+
throw error;
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
//#endregion
|
|
489
|
+
//#region extensions/buzz/src/room-membership.ts
|
|
490
|
+
const BUZZ_ROOM_MEMBERSHIP_KIND = 39002;
|
|
491
|
+
const BUZZ_ROOM_SYSTEM_KIND = 40099;
|
|
492
|
+
const HEX_PUBLIC_KEY_PATTERN = /^[0-9a-f]{64}$/u;
|
|
493
|
+
const MEMBERSHIP_CHANGE_TYPES = /* @__PURE__ */ new Set([
|
|
494
|
+
"member_joined",
|
|
495
|
+
"member_left",
|
|
496
|
+
"member_removed"
|
|
497
|
+
]);
|
|
498
|
+
function parseBuzzRoomMembershipEvent(event, relayPublicKey) {
|
|
499
|
+
if (event.kind !== 39002 || event.pubkey.toLowerCase() !== relayPublicKey) return;
|
|
500
|
+
const roomId = event.tags.find((tag) => tag[0] === "d")?.[1]?.trim().toLowerCase();
|
|
501
|
+
if (!roomId || !BUZZ_CHANNEL_ID_PATTERN.test(roomId)) return;
|
|
502
|
+
const members = /* @__PURE__ */ new Set();
|
|
503
|
+
const roles = /* @__PURE__ */ new Map();
|
|
504
|
+
for (const tag of event.tags) {
|
|
505
|
+
if (tag[0] !== "p") continue;
|
|
506
|
+
const publicKey = tag[1]?.trim().toLowerCase();
|
|
507
|
+
if (!publicKey || !HEX_PUBLIC_KEY_PATTERN.test(publicKey)) continue;
|
|
508
|
+
members.add(publicKey);
|
|
509
|
+
const role = tag[3]?.trim().toLowerCase();
|
|
510
|
+
if (role) roles.set(publicKey, role);
|
|
511
|
+
}
|
|
512
|
+
return {
|
|
513
|
+
roomId,
|
|
514
|
+
createdAt: event.created_at,
|
|
515
|
+
eventId: event.id,
|
|
516
|
+
publisherPublicKey: event.pubkey.toLowerCase(),
|
|
517
|
+
members,
|
|
518
|
+
roles
|
|
519
|
+
};
|
|
520
|
+
}
|
|
521
|
+
function parseBuzzRoomMembershipChangeEvent(event, membership) {
|
|
522
|
+
if (event.kind !== 40099 || event.pubkey.toLowerCase() !== membership.publisherPublicKey || !event.tags.some((tag) => tag[0] === "h" && tag[1]?.toLowerCase() === membership.roomId)) return;
|
|
523
|
+
try {
|
|
524
|
+
const content = JSON.parse(event.content);
|
|
525
|
+
if (typeof content.type !== "string" || !MEMBERSHIP_CHANGE_TYPES.has(content.type)) return;
|
|
526
|
+
const target = typeof content.target === "string" ? content.target.trim().toLowerCase() : content.type === "member_left" && typeof content.actor === "string" ? content.actor.trim().toLowerCase() : "";
|
|
527
|
+
if (!HEX_PUBLIC_KEY_PATTERN.test(target)) return;
|
|
528
|
+
return {
|
|
529
|
+
type: content.type,
|
|
530
|
+
targetPublicKey: target
|
|
531
|
+
};
|
|
532
|
+
} catch {
|
|
533
|
+
return;
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
//#endregion
|
|
537
|
+
export { connectAuthenticatedBuzzRelay as a, openBuzzRelaySubscription as c, resolveBuzzSubscriptionBudget as d, BUZZ_ROOM_METADATA_KIND as f, parseBuzzRoomMembershipEvent as i, queryBuzzRelaySnapshot as l, isNewerBuzzRevision as m, BUZZ_ROOM_SYSTEM_KIND as n, connectAuthenticatedBuzzRelaySession as o, BuzzDirectoryState as p, parseBuzzRoomMembershipChangeEvent as r, parseBuzzAuthTag as s, BUZZ_ROOM_MEMBERSHIP_KIND as t, BUZZ_MAX_CONFIGURED_ROOMS as u };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as resolveBuzzAccountConfig } from "./types-
|
|
1
|
+
import { a as resolveBuzzAccountConfig } from "./types-DRUZMyTx.mjs";
|
|
2
2
|
import { collectSecretInputAssignment, createChannelSecretTargetRegistryEntries, getChannelRecord, isRecord } from "openclaw/plugin-sdk/channel-secret-basic-runtime";
|
|
3
3
|
//#region extensions/buzz/src/secret-contract.ts
|
|
4
4
|
const fields = ["privateKey", "authTag"];
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { n as collectRuntimeConfigAssignments, r as secretTargetRegistryEntries, t as channelSecrets } from "./secret-contract-
|
|
1
|
+
import { n as collectRuntimeConfigAssignments, r as secretTargetRegistryEntries, t as channelSecrets } from "./secret-contract-B2-Mrm4v.mjs";
|
|
2
2
|
import "../secret-contract-api.js";
|
|
3
3
|
export { channelSecrets, collectRuntimeConfigAssignments, secretTargetRegistryEntries };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { c as BuzzConfigSchema, i as resolveBuzzAccount, r as listBuzzAccountIds, s as resolveDefaultBuzzAccountId } from "./types-
|
|
2
|
-
import { n as buzzSetupContract, t as buzzSetupWizard } from "./setup-surface-
|
|
1
|
+
import { c as BuzzConfigSchema, i as resolveBuzzAccount, r as listBuzzAccountIds, s as resolveDefaultBuzzAccountId } from "./types-DRUZMyTx.mjs";
|
|
2
|
+
import { n as buzzSetupContract, t as buzzSetupWizard } from "./setup-surface-CVXY6_50.mjs";
|
|
3
3
|
import { describeAccountSnapshot } from "openclaw/plugin-sdk/account-helpers";
|
|
4
4
|
//#region extensions/buzz/src/channel.setup.ts
|
|
5
5
|
const buzzSetupPlugin = {
|
|
@@ -1,5 +1,5 @@
|
|
|
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-
|
|
2
|
-
import { c as openBuzzRelaySubscription, i as parseBuzzRoomMembershipEvent, l as queryBuzzRelaySnapshot, o as connectAuthenticatedBuzzRelaySession, s as parseBuzzAuthTag, t as BUZZ_ROOM_MEMBERSHIP_KIND
|
|
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-DRUZMyTx.mjs";
|
|
2
|
+
import { c as openBuzzRelaySubscription, i as parseBuzzRoomMembershipEvent, l as queryBuzzRelaySnapshot, m as isNewerBuzzRevision, o as connectAuthenticatedBuzzRelaySession, s as parseBuzzAuthTag, t as BUZZ_ROOM_MEMBERSHIP_KIND } from "./room-membership-7of91SyC.mjs";
|
|
3
3
|
import { generateSecretKey, nip19 } from "nostr-tools";
|
|
4
4
|
import { sleep } from "openclaw/plugin-sdk/runtime-env";
|
|
5
5
|
import { defineChannelSetupContract } from "openclaw/plugin-sdk/channel-setup";
|
|
@@ -393,9 +393,7 @@ function hasSuccessfulBuzzProbe(payload, accountId, target) {
|
|
|
393
393
|
}
|
|
394
394
|
function isGatewayNotRunningError(error) {
|
|
395
395
|
const message = error instanceof Error ? error.message.toLowerCase() : String(error).toLowerCase();
|
|
396
|
-
|
|
397
|
-
if (identifiesMissingListener && typeof error === "object" && error !== null && "name" in error && "kind" in error && "code" in error && error.name === "GatewayTransportError" && error.kind === "closed" && error.code === 1006) return true;
|
|
398
|
-
return identifiesMissingListener;
|
|
396
|
+
return message.includes("econnrefused") || message.includes("connection refused") || message.includes("no listener");
|
|
399
397
|
}
|
|
400
398
|
async function verifyBuzzAfterSetup(params) {
|
|
401
399
|
try {
|
|
@@ -466,35 +464,23 @@ function isRemoteInsecureRelayUrl(value) {
|
|
|
466
464
|
const isLoopback = hostname === "localhost" || hostname.endsWith(".localhost") || hostname === "::1" || isIpv4Loopback;
|
|
467
465
|
return url.protocol === "ws:" && !isLoopback;
|
|
468
466
|
}
|
|
469
|
-
async function
|
|
467
|
+
async function resolveRelayUrl(params) {
|
|
468
|
+
const configuredValue = params.configuredValue?.trim();
|
|
469
|
+
let relayUrl = configuredValue && validateRelayUrl(configuredValue) === void 0 ? configuredValue : void 0;
|
|
470
470
|
while (true) {
|
|
471
|
-
|
|
471
|
+
relayUrl ??= (await params.prompter.text({
|
|
472
472
|
message: "Buzz relay WebSocket URL",
|
|
473
473
|
placeholder: "wss://buzz.example.com",
|
|
474
|
-
initialValue:
|
|
474
|
+
initialValue: configuredValue || void 0,
|
|
475
475
|
validate: validateRelayUrl
|
|
476
476
|
})).trim();
|
|
477
|
-
if (!isRemoteInsecureRelayUrl(relayUrl)
|
|
478
|
-
if (await params.prompter.confirm({
|
|
477
|
+
if (!isRemoteInsecureRelayUrl(relayUrl) || await params.prompter.confirm({
|
|
479
478
|
message: "This remote ws:// relay is unencrypted. Continue anyway?",
|
|
480
479
|
initialValue: false
|
|
481
480
|
})) return relayUrl;
|
|
481
|
+
relayUrl = void 0;
|
|
482
482
|
}
|
|
483
483
|
}
|
|
484
|
-
async function resolveRelayUrl(params) {
|
|
485
|
-
const configuredValue = params.configuredValue?.trim();
|
|
486
|
-
if (configuredValue && validateRelayUrl(configuredValue) === void 0) {
|
|
487
|
-
if (!isRemoteInsecureRelayUrl(configuredValue)) return configuredValue;
|
|
488
|
-
if (await params.prompter.confirm({
|
|
489
|
-
message: "This remote ws:// relay is unencrypted. Continue anyway?",
|
|
490
|
-
initialValue: false
|
|
491
|
-
})) return configuredValue;
|
|
492
|
-
}
|
|
493
|
-
return await promptRelayUrl({
|
|
494
|
-
...configuredValue ? { initialValue: configuredValue } : {},
|
|
495
|
-
prompter: params.prompter
|
|
496
|
-
});
|
|
497
|
-
}
|
|
498
484
|
async function resolveSetupCredential(params) {
|
|
499
485
|
const { config, configPath, allowEnv } = resolveBuzzAccountConfig(params);
|
|
500
486
|
const { resolveConfiguredSecretInputWithFallback } = await import("openclaw/plugin-sdk/secret-input-runtime");
|
|
@@ -39,6 +39,7 @@ function looksLikeBuzzTarget(target) {
|
|
|
39
39
|
const BuzzGroupConfigSchema = z.object({
|
|
40
40
|
enabled: z.boolean().optional(),
|
|
41
41
|
requireMention: z.boolean().optional(),
|
|
42
|
+
requireMentionInBotThreads: z.boolean().optional(),
|
|
42
43
|
groupPolicy: GroupPolicySchema.optional(),
|
|
43
44
|
groupAllowFrom: z.array(z.union([z.string(), z.number()])).optional()
|
|
44
45
|
}).strict();
|
|
@@ -50,7 +51,7 @@ const BuzzAccountConfigSchema = z.object({
|
|
|
50
51
|
responsePrefix: z.string().optional(),
|
|
51
52
|
replyToMode: z.enum(["off", "all"]).optional(),
|
|
52
53
|
markdown: MarkdownConfigSchema,
|
|
53
|
-
relayUrl: z.string().url().and(z.string().regex(/^[wW][sS][sS]
|
|
54
|
+
relayUrl: z.string().url().and(z.string().regex(/^[wW][sS][sS]?:\/\/.+/, "Buzz relay URL must use ws:// or wss://")).optional(),
|
|
54
55
|
privateKey: buildSecretInputSchema().optional(),
|
|
55
56
|
authTag: buildSecretInputSchema().optional(),
|
|
56
57
|
groupPolicy: GroupPolicySchema.optional(),
|
|
@@ -64,7 +65,14 @@ const RawBuzzConfigSchema = BuzzAccountConfigSchema.extend({
|
|
|
64
65
|
accounts: z.record(BuzzAccountIdSchema, BuzzAccountConfigSchema).optional(),
|
|
65
66
|
defaultAccount: BuzzAccountIdSchema.optional()
|
|
66
67
|
});
|
|
67
|
-
const
|
|
68
|
+
const botThreadMentionHint = {
|
|
69
|
+
label: "Require Mention in Bot Threads",
|
|
70
|
+
help: "Override mention gating in threads whose root message was signed by this bot in the same room. False allows unmentioned replies; true requires a mention. Omit to preserve the room's mention policy. Sender restrictions still apply."
|
|
71
|
+
};
|
|
72
|
+
const BuzzConfigSchema = buildChannelConfigSchema(RawBuzzConfigSchema, { uiHints: {
|
|
73
|
+
"groups.*.requireMentionInBotThreads": botThreadMentionHint,
|
|
74
|
+
"accounts.*.groups.*.requireMentionInBotThreads": botThreadMentionHint
|
|
75
|
+
} });
|
|
68
76
|
//#endregion
|
|
69
77
|
//#region extensions/buzz/src/types.ts
|
|
70
78
|
function resolveChannelConfig(cfg) {
|
package/dist/api.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { i as resolveBuzzAccount, r as listBuzzAccountIds, s as resolveDefaultBuzzAccountId } from "./.setup/types-
|
|
2
|
-
import { n as getBuzzRuntime, r as setBuzzRuntime, t as buzzPlugin } from "./.setup/channel-
|
|
1
|
+
import { i as resolveBuzzAccount, r as listBuzzAccountIds, s as resolveDefaultBuzzAccountId } from "./.setup/types-DRUZMyTx.mjs";
|
|
2
|
+
import { n as getBuzzRuntime, r as setBuzzRuntime, t as buzzPlugin } from "./.setup/channel-B7r1381j.mjs";
|
|
3
3
|
export { buzzPlugin, getBuzzRuntime, listBuzzAccountIds, resolveBuzzAccount, resolveDefaultBuzzAccountId, setBuzzRuntime };
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { t as buzzPlugin } from "./.setup/channel-
|
|
1
|
+
import { t as buzzPlugin } from "./.setup/channel-B7r1381j.mjs";
|
|
2
2
|
export { buzzPlugin };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { n as listBuzzDirectoryPeersFromConfig, t as listBuzzDirectoryGroupsFromConfig } from "./.setup/directory-config-
|
|
1
|
+
import { n as listBuzzDirectoryPeersFromConfig, t as listBuzzDirectoryGroupsFromConfig } from "./.setup/directory-config-M9KSexdh.mjs";
|
|
2
2
|
//#region extensions/buzz/directory-contract-api.ts
|
|
3
3
|
const buzzDirectoryContractPlugin = {
|
|
4
4
|
id: "buzz",
|