@openclaw/buzz 2026.9.1-beta.1 → 2026.9.1
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/dist/{adapter.runtime-DA6guOQ_.js → adapter.runtime-CaDdxQ2C.js} +4 -4
- package/dist/api.js +2 -2
- package/dist/{channel-BulZqShF.js → channel-B50upNWy.js} +189 -66
- package/dist/channel-plugin-api.js +1 -1
- package/dist/{directory-config-BesCse0q.js → directory-config-B0shiBa4.js} +1 -1
- package/dist/directory-contract-api.js +1 -1
- package/dist/{message-event-CFw7gGLi.js → message-event-X_aBiM1X.js} +5 -8
- package/dist/qa-runner-api.js +1 -1
- package/dist/{room-membership-DSAkyE0a.js → room-membership-a8IHfdgL.js} +7 -5
- package/dist/secret-contract-DNuqbK64.js +50 -0
- package/dist/secret-contract-api.js +1 -1
- package/dist/setup-plugin-api.js +6 -3
- package/dist/{setup-surface-BvQgEJ7O.js → setup-surface-DDUmAluL.js} +197 -170
- package/dist/types-CeupBG0P.js +160 -0
- package/node_modules/nostr-tools/lib/cjs/index.js +20 -4
- package/node_modules/nostr-tools/lib/cjs/index.js.map +2 -2
- package/node_modules/nostr-tools/lib/cjs/nip17.js +19 -3
- package/node_modules/nostr-tools/lib/cjs/nip17.js.map +2 -2
- package/node_modules/nostr-tools/lib/cjs/nip46.js +7 -3
- package/node_modules/nostr-tools/lib/cjs/nip46.js.map +2 -2
- package/node_modules/nostr-tools/lib/cjs/nip59.js +19 -3
- package/node_modules/nostr-tools/lib/cjs/nip59.js.map +2 -2
- package/node_modules/nostr-tools/lib/cjs/nip98.js +1 -1
- package/node_modules/nostr-tools/lib/cjs/nip98.js.map +2 -2
- package/node_modules/nostr-tools/lib/cjs/nipad.js +55 -0
- package/node_modules/nostr-tools/lib/cjs/nipad.js.map +7 -0
- package/node_modules/nostr-tools/lib/esm/index.js +20 -4
- package/node_modules/nostr-tools/lib/esm/index.js.map +2 -2
- package/node_modules/nostr-tools/lib/esm/nip17.js +19 -3
- package/node_modules/nostr-tools/lib/esm/nip17.js.map +2 -2
- package/node_modules/nostr-tools/lib/esm/nip46.js +7 -3
- package/node_modules/nostr-tools/lib/esm/nip46.js.map +2 -2
- package/node_modules/nostr-tools/lib/esm/nip59.js +19 -3
- package/node_modules/nostr-tools/lib/esm/nip59.js.map +2 -2
- package/node_modules/nostr-tools/lib/esm/nip98.js +1 -1
- package/node_modules/nostr-tools/lib/esm/nip98.js.map +2 -2
- package/node_modules/nostr-tools/lib/esm/nipad.js +34 -0
- package/node_modules/nostr-tools/lib/esm/nipad.js.map +7 -0
- package/node_modules/nostr-tools/lib/nostr.bundle.js +20 -4
- package/node_modules/nostr-tools/lib/nostr.bundle.js.map +2 -2
- package/node_modules/nostr-tools/lib/types/nip46.test.d.ts +1 -0
- package/node_modules/nostr-tools/lib/types/nip98.d.ts +1 -1
- package/node_modules/nostr-tools/lib/types/nipad.d.ts +16 -0
- package/node_modules/nostr-tools/package.json +7 -1
- package/openclaw.plugin.json +346 -0
- package/package.json +8 -5
- package/dist/secret-contract-D19RiJv_.js +0 -12
- package/dist/types-BN_HOsnq.js +0 -101
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
import { createAccountListHelpers, mergeAccountConfig } from "openclaw/plugin-sdk/account-helpers";
|
|
2
|
+
import { GroupPolicySchema, MarkdownConfigSchema, buildChannelConfigSchema } from "openclaw/plugin-sdk/channel-config-schema";
|
|
3
|
+
import { buildSecretInputSchema, resolveSecretInputString } from "openclaw/plugin-sdk/secret-input";
|
|
4
|
+
import { z } from "zod";
|
|
5
|
+
import { getPublicKey, nip19 } from "nostr-tools";
|
|
6
|
+
import { DEFAULT_ACCOUNT_ID, normalizeAccountId, normalizeOptionalAccountId } from "openclaw/plugin-sdk/account-id";
|
|
7
|
+
import { assertSecretOwnerAvailable } from "openclaw/plugin-sdk/channel-secret-owner-runtime";
|
|
8
|
+
import { normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
|
|
9
|
+
//#region extensions/buzz/src/target.ts
|
|
10
|
+
const BUZZ_CHANNEL_ID_PATTERN = /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-5][0-9a-fA-F]{3}-[89aAbB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}$/u;
|
|
11
|
+
function normalizeBuzzTarget(target) {
|
|
12
|
+
return target.trim().replace(/^buzz:/iu, "").replace(/^channel:/iu, "");
|
|
13
|
+
}
|
|
14
|
+
function parseBuzzTarget(target) {
|
|
15
|
+
const channelId = normalizeBuzzTarget(target);
|
|
16
|
+
if (!BUZZ_CHANNEL_ID_PATTERN.test(channelId)) throw new Error("Buzz target must be a channel UUID");
|
|
17
|
+
return channelId.toLowerCase();
|
|
18
|
+
}
|
|
19
|
+
function isConfiguredBuzzChannel(configuredChannelIds, channelId) {
|
|
20
|
+
try {
|
|
21
|
+
return configuredChannelIds.has(parseBuzzTarget(channelId));
|
|
22
|
+
} catch {
|
|
23
|
+
return false;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
function buildBuzzTarget(channelId) {
|
|
27
|
+
return `buzz:${parseBuzzTarget(channelId)}`;
|
|
28
|
+
}
|
|
29
|
+
function looksLikeBuzzTarget(target) {
|
|
30
|
+
try {
|
|
31
|
+
parseBuzzTarget(target);
|
|
32
|
+
return true;
|
|
33
|
+
} catch {
|
|
34
|
+
return false;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
//#endregion
|
|
38
|
+
//#region extensions/buzz/src/config-schema.ts
|
|
39
|
+
const BuzzGroupConfigSchema = z.object({
|
|
40
|
+
enabled: z.boolean().optional(),
|
|
41
|
+
requireMention: z.boolean().optional(),
|
|
42
|
+
groupPolicy: GroupPolicySchema.optional(),
|
|
43
|
+
groupAllowFrom: z.array(z.union([z.string(), z.number()])).optional()
|
|
44
|
+
}).strict();
|
|
45
|
+
const BuzzAccountIdSchema = z.string().regex(/^(?!(?:constructor|prototype)$)[a-z0-9][a-z0-9_-]{0,63}$/u, "Buzz account IDs must be canonical lowercase account keys");
|
|
46
|
+
const BuzzAccountConfigSchema = z.object({
|
|
47
|
+
name: z.string().optional(),
|
|
48
|
+
enabled: z.boolean().optional(),
|
|
49
|
+
configWrites: z.boolean().optional(),
|
|
50
|
+
responsePrefix: z.string().optional(),
|
|
51
|
+
replyToMode: z.enum(["off", "all"]).optional(),
|
|
52
|
+
markdown: MarkdownConfigSchema,
|
|
53
|
+
relayUrl: z.string().url().and(z.string().regex(/^[wW][sS][sS]?:\/\//, "Buzz relay URL must use ws:// or wss://")).optional(),
|
|
54
|
+
privateKey: buildSecretInputSchema().optional(),
|
|
55
|
+
authTag: buildSecretInputSchema().optional(),
|
|
56
|
+
groupPolicy: GroupPolicySchema.optional(),
|
|
57
|
+
groupAllowFrom: z.array(z.union([z.string(), z.number()])).optional(),
|
|
58
|
+
groups: z.record(z.string().regex(BUZZ_CHANNEL_ID_PATTERN, "Buzz group key must be a channel UUID"), BuzzGroupConfigSchema).optional(),
|
|
59
|
+
historyLimit: z.number().int().min(0).max(20).optional(),
|
|
60
|
+
defaultTo: z.string().optional()
|
|
61
|
+
}).strict();
|
|
62
|
+
const RawBuzzConfigSchema = BuzzAccountConfigSchema.extend({
|
|
63
|
+
groupPolicy: GroupPolicySchema.optional().default("allowlist"),
|
|
64
|
+
accounts: z.record(BuzzAccountIdSchema, BuzzAccountConfigSchema).optional(),
|
|
65
|
+
defaultAccount: BuzzAccountIdSchema.optional()
|
|
66
|
+
});
|
|
67
|
+
const BuzzConfigSchema = buildChannelConfigSchema(RawBuzzConfigSchema);
|
|
68
|
+
//#endregion
|
|
69
|
+
//#region extensions/buzz/src/types.ts
|
|
70
|
+
function resolveChannelConfig(cfg) {
|
|
71
|
+
return cfg.channels?.buzz;
|
|
72
|
+
}
|
|
73
|
+
const { listAccountIds: listBuzzAccountIds, resolveDefaultAccountId: resolveDefaultBuzzAccountId } = createAccountListHelpers("buzz", {
|
|
74
|
+
normalizeAccountId,
|
|
75
|
+
fallbackAccountIdWhenEmpty: false,
|
|
76
|
+
implicitDefaultAccount: {
|
|
77
|
+
channelKeys: ["relayUrl", "privateKey"],
|
|
78
|
+
envVars: ["BUZZ_RELAY_URL", "BUZZ_PRIVATE_KEY"]
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
function resolveBuzzAccountConfig(params) {
|
|
82
|
+
const requestedId = params.accountId?.trim();
|
|
83
|
+
const accountId = requestedId ? normalizeOptionalAccountId(requestedId) : resolveDefaultBuzzAccountId(params.cfg);
|
|
84
|
+
if (!accountId || !BuzzAccountIdSchema.safeParse(accountId).success) throw new Error("Buzz account ID must be a valid account key");
|
|
85
|
+
const root = resolveChannelConfig(params.cfg) ?? {};
|
|
86
|
+
const allowEnv = accountId === DEFAULT_ACCOUNT_ID && !Object.hasOwn(root.accounts ?? {}, accountId);
|
|
87
|
+
const account = allowEnv ? void 0 : root.accounts?.[accountId];
|
|
88
|
+
const merged = mergeAccountConfig({
|
|
89
|
+
channelConfig: root,
|
|
90
|
+
accountConfig: account,
|
|
91
|
+
omitKeys: ["defaultAccount", ...allowEnv ? [] : [
|
|
92
|
+
"name",
|
|
93
|
+
"relayUrl",
|
|
94
|
+
"privateKey",
|
|
95
|
+
"authTag",
|
|
96
|
+
"groups",
|
|
97
|
+
"defaultTo"
|
|
98
|
+
]]
|
|
99
|
+
});
|
|
100
|
+
return {
|
|
101
|
+
accountId,
|
|
102
|
+
allowEnv,
|
|
103
|
+
configPath: allowEnv ? "channels.buzz" : `channels.buzz.accounts.${accountId}`,
|
|
104
|
+
config: {
|
|
105
|
+
...merged,
|
|
106
|
+
groupPolicy: merged.groupPolicy ?? "allowlist",
|
|
107
|
+
enabled: root.enabled !== false && account?.enabled !== false,
|
|
108
|
+
groups: normalizeBuzzGroups(merged.groups)
|
|
109
|
+
}
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
function normalizeBuzzGroups(groups) {
|
|
113
|
+
if (!groups) return;
|
|
114
|
+
return Object.fromEntries(Object.entries(groups).map(([channelId, group]) => [parseBuzzTarget(channelId), group]));
|
|
115
|
+
}
|
|
116
|
+
function decodeBuzzPrivateKey(value) {
|
|
117
|
+
const trimmed = value.trim();
|
|
118
|
+
if (/^[0-9a-f]{64}$/iu.test(trimmed)) return Uint8Array.from(Buffer.from(trimmed, "hex"));
|
|
119
|
+
const decoded = nip19.decode(trimmed);
|
|
120
|
+
if (decoded.type !== "nsec") throw new Error("Buzz private key must be nsec or 64-character hex");
|
|
121
|
+
return decoded.data;
|
|
122
|
+
}
|
|
123
|
+
function resolveBuzzPublicKey(privateKey) {
|
|
124
|
+
return getPublicKey(decodeBuzzPrivateKey(privateKey));
|
|
125
|
+
}
|
|
126
|
+
function resolveBuzzAccount(params) {
|
|
127
|
+
const { accountId, config, configPath, allowEnv } = resolveBuzzAccountConfig(params);
|
|
128
|
+
const relayUrl = config.relayUrl?.trim() || (allowEnv ? process.env.BUZZ_RELAY_URL?.trim() : "") || "";
|
|
129
|
+
const resolveCredential = (field) => resolveSecretInputString({
|
|
130
|
+
value: config[field],
|
|
131
|
+
path: `${configPath}.${field}`,
|
|
132
|
+
mode: "inspect"
|
|
133
|
+
});
|
|
134
|
+
const privateKeyResolution = resolveCredential("privateKey");
|
|
135
|
+
const authTagResolution = resolveCredential("authTag");
|
|
136
|
+
const privateKey = privateKeyResolution.value ?? (allowEnv && privateKeyResolution.status === "missing" ? process.env.BUZZ_PRIVATE_KEY?.trim() || "" : "");
|
|
137
|
+
const authTag = authTagResolution.value ?? (allowEnv && authTagResolution.status === "missing" ? process.env.BUZZ_AUTH_TAG?.trim() || "" : "");
|
|
138
|
+
let publicKey = "";
|
|
139
|
+
if (privateKey) try {
|
|
140
|
+
publicKey = resolveBuzzPublicKey(privateKey);
|
|
141
|
+
} catch {}
|
|
142
|
+
return {
|
|
143
|
+
accountId,
|
|
144
|
+
name: normalizeOptionalString(config.name) ?? "OpenClaw",
|
|
145
|
+
enabled: config.enabled !== false,
|
|
146
|
+
configured: Boolean(relayUrl && (privateKey || privateKeyResolution.ref)),
|
|
147
|
+
relayUrl,
|
|
148
|
+
privateKey,
|
|
149
|
+
authTag,
|
|
150
|
+
publicKey,
|
|
151
|
+
tokenStatus: privateKeyResolution.ref || authTagResolution.ref ? "configured_unavailable" : privateKey ? "available" : "missing",
|
|
152
|
+
config
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
function assertBuzzAccountAvailable(account) {
|
|
156
|
+
assertSecretOwnerAvailable("account", `buzz:${account.accountId}`);
|
|
157
|
+
if (account.tokenStatus === "configured_unavailable") throw new Error(`Buzz credentials for account "${account.accountId}" are configured but unavailable.`);
|
|
158
|
+
}
|
|
159
|
+
//#endregion
|
|
160
|
+
export { resolveBuzzAccountConfig as a, BuzzConfigSchema as c, isConfiguredBuzzChannel as d, looksLikeBuzzTarget as f, resolveBuzzAccount as i, BUZZ_CHANNEL_ID_PATTERN as l, parseBuzzTarget as m, decodeBuzzPrivateKey as n, resolveBuzzPublicKey as o, normalizeBuzzTarget as p, listBuzzAccountIds as r, resolveDefaultBuzzAccountId as s, assertBuzzAccountAvailable as t, buildBuzzTarget as u };
|
|
@@ -2589,13 +2589,29 @@ function wrapManyEvents(event, senderPrivateKey, recipientsPublicKeys) {
|
|
|
2589
2589
|
return wrappeds;
|
|
2590
2590
|
}
|
|
2591
2591
|
function unwrapEvent(wrap, recipientPrivateKey) {
|
|
2592
|
-
|
|
2593
|
-
|
|
2592
|
+
if (wrap.kind !== GiftWrap) {
|
|
2593
|
+
throw new Error(`unexpected wrap kind ${wrap.kind}, expected ${GiftWrap}`);
|
|
2594
|
+
}
|
|
2595
|
+
const seal = nip44Decrypt(wrap, recipientPrivateKey);
|
|
2596
|
+
if (seal.kind !== Seal) {
|
|
2597
|
+
throw new Error(`unexpected seal kind ${seal.kind}, expected ${Seal}`);
|
|
2598
|
+
}
|
|
2599
|
+
if (!verifyEvent(seal)) {
|
|
2600
|
+
throw new Error("seal signature is invalid");
|
|
2601
|
+
}
|
|
2602
|
+
const rumor = nip44Decrypt(seal, recipientPrivateKey);
|
|
2603
|
+
if (rumor.pubkey !== seal.pubkey) {
|
|
2604
|
+
throw new Error(`rumor pubkey ${rumor.pubkey} does not match seal pubkey ${seal.pubkey}`);
|
|
2605
|
+
}
|
|
2606
|
+
return rumor;
|
|
2594
2607
|
}
|
|
2595
2608
|
function unwrapManyEvents(wrappedEvents, recipientPrivateKey) {
|
|
2596
2609
|
let unwrappedEvents = [];
|
|
2597
2610
|
wrappedEvents.forEach((e) => {
|
|
2598
|
-
|
|
2611
|
+
try {
|
|
2612
|
+
unwrappedEvents.push(unwrapEvent(e, recipientPrivateKey));
|
|
2613
|
+
} catch (_err) {
|
|
2614
|
+
}
|
|
2599
2615
|
});
|
|
2600
2616
|
unwrappedEvents.sort((a, b) => a.created_at - b.created_at);
|
|
2601
2617
|
return unwrappedEvents;
|
|
@@ -3987,7 +4003,7 @@ function validateEventTimestamp(event) {
|
|
|
3987
4003
|
if (!event.created_at) {
|
|
3988
4004
|
return false;
|
|
3989
4005
|
}
|
|
3990
|
-
return Math.round(new Date().getTime() / 1e3) - event.created_at < 60;
|
|
4006
|
+
return Math.abs(Math.round(new Date().getTime() / 1e3) - event.created_at) < 60;
|
|
3991
4007
|
}
|
|
3992
4008
|
function validateEventKind(event) {
|
|
3993
4009
|
return event.kind === HTTPAuth;
|