@openclaw/nostr 2026.2.2 → 2026.2.9

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/CHANGELOG.md CHANGED
@@ -1,5 +1,29 @@
1
1
  # Changelog
2
2
 
3
+ ## 2026.2.6-3
4
+
5
+ ### Changes
6
+
7
+ - Version alignment with core OpenClaw release numbers.
8
+
9
+ ## 2026.2.6-2
10
+
11
+ ### Changes
12
+
13
+ - Version alignment with core OpenClaw release numbers.
14
+
15
+ ## 2026.2.6
16
+
17
+ ### Changes
18
+
19
+ - Version alignment with core OpenClaw release numbers.
20
+
21
+ ## 2026.2.4
22
+
23
+ ### Changes
24
+
25
+ - Version alignment with core OpenClaw release numbers.
26
+
3
27
  ## 2026.2.2
4
28
 
5
29
  ### Changes
package/package.json CHANGED
@@ -1,11 +1,10 @@
1
1
  {
2
2
  "name": "@openclaw/nostr",
3
- "version": "2026.2.2",
3
+ "version": "2026.2.9",
4
4
  "description": "OpenClaw Nostr channel plugin for NIP-04 encrypted DMs",
5
5
  "type": "module",
6
6
  "dependencies": {
7
7
  "nostr-tools": "^2.23.0",
8
- "openclaw": "workspace:*",
9
8
  "zod": "^4.3.6"
10
9
  },
11
10
  "devDependencies": {
package/src/channel.ts CHANGED
@@ -148,7 +148,11 @@ export const nostrPlugin: ChannelPlugin<ResolvedNostrAccount> = {
148
148
  const message = core.channel.text.convertMarkdownTables(text ?? "", tableMode);
149
149
  const normalizedTo = normalizePubkey(to);
150
150
  await bus.sendDm(normalizedTo, message);
151
- return { channel: "nostr", to: normalizedTo };
151
+ return {
152
+ channel: "nostr" as const,
153
+ to: normalizedTo,
154
+ messageId: `nostr-${Date.now()}`,
155
+ };
152
156
  },
153
157
  },
154
158
 
@@ -224,10 +228,15 @@ export const nostrPlugin: ChannelPlugin<ResolvedNostrAccount> = {
224
228
  privateKey: account.privateKey,
225
229
  relays: account.relays,
226
230
  onMessage: async (senderPubkey, text, reply) => {
227
- ctx.log?.debug(`[${account.accountId}] DM from ${senderPubkey}: ${text.slice(0, 50)}...`);
231
+ ctx.log?.debug?.(
232
+ `[${account.accountId}] DM from ${senderPubkey}: ${text.slice(0, 50)}...`,
233
+ );
228
234
 
229
235
  // Forward to OpenClaw's message pipeline
230
- await runtime.channel.reply.handleInboundMessage({
236
+ // TODO: Replace with proper dispatchReplyWithBufferedBlockDispatcher call
237
+ await (
238
+ runtime.channel.reply as { handleInboundMessage?: (params: unknown) => Promise<void> }
239
+ ).handleInboundMessage?.({
231
240
  channel: "nostr",
232
241
  accountId: account.accountId,
233
242
  senderId: senderPubkey,
@@ -240,31 +249,33 @@ export const nostrPlugin: ChannelPlugin<ResolvedNostrAccount> = {
240
249
  });
241
250
  },
242
251
  onError: (error, context) => {
243
- ctx.log?.error(`[${account.accountId}] Nostr error (${context}): ${error.message}`);
252
+ ctx.log?.error?.(`[${account.accountId}] Nostr error (${context}): ${error.message}`);
244
253
  },
245
254
  onConnect: (relay) => {
246
- ctx.log?.debug(`[${account.accountId}] Connected to relay: ${relay}`);
255
+ ctx.log?.debug?.(`[${account.accountId}] Connected to relay: ${relay}`);
247
256
  },
248
257
  onDisconnect: (relay) => {
249
- ctx.log?.debug(`[${account.accountId}] Disconnected from relay: ${relay}`);
258
+ ctx.log?.debug?.(`[${account.accountId}] Disconnected from relay: ${relay}`);
250
259
  },
251
260
  onEose: (relays) => {
252
- ctx.log?.debug(`[${account.accountId}] EOSE received from relays: ${relays}`);
261
+ ctx.log?.debug?.(`[${account.accountId}] EOSE received from relays: ${relays}`);
253
262
  },
254
263
  onMetric: (event: MetricEvent) => {
255
264
  // Log significant metrics at appropriate levels
256
265
  if (event.name.startsWith("event.rejected.")) {
257
- ctx.log?.debug(`[${account.accountId}] Metric: ${event.name}`, event.labels);
266
+ ctx.log?.debug?.(
267
+ `[${account.accountId}] Metric: ${event.name} ${JSON.stringify(event.labels)}`,
268
+ );
258
269
  } else if (event.name === "relay.circuit_breaker.open") {
259
- ctx.log?.warn(
270
+ ctx.log?.warn?.(
260
271
  `[${account.accountId}] Circuit breaker opened for relay: ${event.labels?.relay}`,
261
272
  );
262
273
  } else if (event.name === "relay.circuit_breaker.close") {
263
- ctx.log?.info(
274
+ ctx.log?.info?.(
264
275
  `[${account.accountId}] Circuit breaker closed for relay: ${event.labels?.relay}`,
265
276
  );
266
277
  } else if (event.name === "relay.error") {
267
- ctx.log?.debug(`[${account.accountId}] Relay error: ${event.labels?.relay}`);
278
+ ctx.log?.debug?.(`[${account.accountId}] Relay error: ${event.labels?.relay}`);
268
279
  }
269
280
  // Update cached metrics snapshot
270
281
  if (busHandle) {
package/src/nostr-bus.ts CHANGED
@@ -488,24 +488,28 @@ export async function startNostrBus(options: NostrBusOptions): Promise<NostrBusH
488
488
  }
489
489
  }
490
490
 
491
- const sub = pool.subscribeMany(relays, [{ kinds: [4], "#p": [pk], since }], {
492
- onevent: handleEvent,
493
- oneose: () => {
494
- // EOSE handler - called when all stored events have been received
495
- for (const relay of relays) {
496
- metrics.emit("relay.message.eose", 1, { relay });
497
- }
498
- onEose?.(relays.join(", "));
499
- },
500
- onclose: (reason) => {
501
- // Handle subscription close
502
- for (const relay of relays) {
503
- metrics.emit("relay.message.closed", 1, { relay });
504
- options.onDisconnect?.(relay);
505
- }
506
- onError?.(new Error(`Subscription closed: ${reason.join(", ")}`), "subscription");
491
+ const sub = pool.subscribeMany(
492
+ relays,
493
+ [{ kinds: [4], "#p": [pk], since }] as unknown as Parameters<typeof pool.subscribeMany>[1],
494
+ {
495
+ onevent: handleEvent,
496
+ oneose: () => {
497
+ // EOSE handler - called when all stored events have been received
498
+ for (const relay of relays) {
499
+ metrics.emit("relay.message.eose", 1, { relay });
500
+ }
501
+ onEose?.(relays.join(", "));
502
+ },
503
+ onclose: (reason) => {
504
+ // Handle subscription close
505
+ for (const relay of relays) {
506
+ metrics.emit("relay.message.closed", 1, { relay });
507
+ options.onDisconnect?.(relay);
508
+ }
509
+ onError?.(new Error(`Subscription closed: ${reason.join(", ")}`), "subscription");
510
+ },
507
511
  },
508
- });
512
+ );
509
513
 
510
514
  // Public sendDm function
511
515
  const sendDm = async (toPubkey: string, text: string): Promise<void> => {
@@ -693,7 +697,7 @@ export function normalizePubkey(input: string): string {
693
697
  throw new Error("Invalid npub key");
694
698
  }
695
699
  // Convert Uint8Array to hex string
696
- return Array.from(decoded.data)
700
+ return Array.from(decoded.data as unknown as Uint8Array)
697
701
  .map((b) => b.toString(16).padStart(2, "0"))
698
702
  .join("");
699
703
  }
@@ -229,31 +229,58 @@ function sendJson(res: ServerResponse, status: number, body: unknown): void {
229
229
  res.end(JSON.stringify(body));
230
230
  }
231
231
 
232
- async function readJsonBody(req: IncomingMessage, maxBytes = 64 * 1024): Promise<unknown> {
232
+ async function readJsonBody(
233
+ req: IncomingMessage,
234
+ maxBytes = 64 * 1024,
235
+ timeoutMs = 30_000,
236
+ ): Promise<unknown> {
233
237
  return new Promise((resolve, reject) => {
238
+ let done = false;
239
+ const finish = (fn: () => void) => {
240
+ if (done) {
241
+ return;
242
+ }
243
+ done = true;
244
+ clearTimeout(timer);
245
+ fn();
246
+ };
247
+
248
+ const timer = setTimeout(() => {
249
+ finish(() => {
250
+ const err = new Error("Request body timeout");
251
+ req.destroy(err);
252
+ reject(err);
253
+ });
254
+ }, timeoutMs);
255
+
234
256
  const chunks: Buffer[] = [];
235
257
  let totalBytes = 0;
236
258
 
237
259
  req.on("data", (chunk: Buffer) => {
238
260
  totalBytes += chunk.length;
239
261
  if (totalBytes > maxBytes) {
240
- reject(new Error("Request body too large"));
241
- req.destroy();
262
+ finish(() => {
263
+ reject(new Error("Request body too large"));
264
+ req.destroy();
265
+ });
242
266
  return;
243
267
  }
244
268
  chunks.push(chunk);
245
269
  });
246
270
 
247
271
  req.on("end", () => {
248
- try {
249
- const body = Buffer.concat(chunks).toString("utf-8");
250
- resolve(body ? JSON.parse(body) : {});
251
- } catch {
252
- reject(new Error("Invalid JSON"));
253
- }
272
+ finish(() => {
273
+ try {
274
+ const body = Buffer.concat(chunks).toString("utf-8");
275
+ resolve(body ? JSON.parse(body) : {});
276
+ } catch {
277
+ reject(new Error("Invalid JSON"));
278
+ }
279
+ });
254
280
  });
255
281
 
256
- req.on("error", reject);
282
+ req.on("error", (err) => finish(() => reject(err)));
283
+ req.on("close", () => finish(() => reject(new Error("Connection closed"))));
257
284
  });
258
285
  }
259
286
 
@@ -130,7 +130,7 @@ export async function importProfileFromRelays(
130
130
  authors: [pubkey],
131
131
  limit: 1,
132
132
  },
133
- ],
133
+ ] as unknown as Parameters<typeof pool.subscribeMany>[1],
134
134
  {
135
135
  onevent(event) {
136
136
  events.push({ event, relay });