@openclaw/nostr 2026.5.27-beta.1 → 2026.5.28-beta.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/api.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { o as resolveNostrAccount } from "./setup-surface-CVEYWXAG.js";
2
2
  import { getPluginRuntimeGatewayRequestScope } from "./runtime-api.js";
3
3
  import { n as NostrProfileSchema } from "./config-schema-DIiXiBKr.js";
4
- import { a as setNostrRuntime, i as getNostrRuntime, n as nostrPlugin, o as contentToProfile, r as publishNostrProfile, t as getNostrProfileState } from "./channel-aEou1Q6d.js";
4
+ import { a as setNostrRuntime, i as getNostrRuntime, n as nostrPlugin, o as contentToProfile, r as publishNostrProfile, t as getNostrProfileState } from "./channel-UK7t4qb8.js";
5
5
  import { normalizeLowercaseStringOrEmpty, normalizeOptionalLowercaseString, readStringValue } from "openclaw/plugin-sdk/string-coerce-runtime";
6
6
  import { z } from "zod";
7
7
  import { SimplePool, verifyEvent } from "nostr-tools";
@@ -86,11 +86,12 @@ async function importProfileFromRelays(opts) {
86
86
  let completed = 0;
87
87
  for (const relay of relays) {
88
88
  relaysQueried.push(relay);
89
- const sub = pool.subscribeMany([relay], [{
89
+ const profileFilter = {
90
90
  kinds: [0],
91
91
  authors: [pubkey],
92
92
  limit: 1
93
- }], {
93
+ };
94
+ const sub = pool.subscribeMany([relay], profileFilter, {
94
95
  onevent(event) {
95
96
  events.push({
96
97
  event,
@@ -5,7 +5,7 @@ import { describeAccountSnapshot } from "openclaw/plugin-sdk/account-helpers";
5
5
  import { createScopedDmSecurityResolver, createTopLevelChannelConfigAdapter } from "openclaw/plugin-sdk/channel-config-helpers";
6
6
  import { createChatChannelPlugin } from "openclaw/plugin-sdk/channel-core";
7
7
  import { createChannelMessageAdapterFromOutbound } from "openclaw/plugin-sdk/channel-outbound";
8
- import { buildPassiveChannelStatusSummary, buildTrafficStatusSummary, safeParseJsonWithSchema } from "openclaw/plugin-sdk/extension-shared";
8
+ import { buildPassiveChannelStatusSummary, buildTrafficStatusSummary, runStoppablePassiveMonitor, safeParseJsonWithSchema } from "openclaw/plugin-sdk/extension-shared";
9
9
  import { createComputedAccountStatusAdapter } from "openclaw/plugin-sdk/status-helpers";
10
10
  import { normalizeStringEntries } from "openclaw/plugin-sdk/string-coerce-runtime";
11
11
  import { z } from "zod";
@@ -991,11 +991,13 @@ async function startNostrBus(options) {
991
991
  inflight.delete(event.id);
992
992
  }
993
993
  }
994
- const sub = pool.subscribeMany(relays, [{
994
+ const dmFilter = {
995
995
  kinds: [4],
996
996
  "#p": [pk],
997
997
  since
998
- }], {
998
+ };
999
+ const relayAbort = new AbortController();
1000
+ const sub = pool.subscribeMany(relays, dmFilter, {
999
1001
  onevent: handleEvent,
1000
1002
  oneose: () => {
1001
1003
  for (const relay of relays) metrics.emit("relay.message.eose", 1, { relay });
@@ -1007,7 +1009,8 @@ async function startNostrBus(options) {
1007
1009
  options.onDisconnect?.(relay);
1008
1010
  }
1009
1011
  onError?.(/* @__PURE__ */ new Error(`Subscription closed: ${reason.join(", ")}`), "subscription");
1010
- }
1012
+ },
1013
+ abort: relayAbort.signal
1011
1014
  });
1012
1015
  const sendDm = async (toPubkey, text) => {
1013
1016
  await sendEncryptedDm(pool, sk, toPubkey, text, relays, metrics, circuitBreakers, healthTracker, onError);
@@ -1035,7 +1038,10 @@ async function startNostrBus(options) {
1035
1038
  };
1036
1039
  return {
1037
1040
  close: () => {
1038
- sub.close();
1041
+ relayAbort.abort("closed by caller");
1042
+ Promise.resolve(sub.close("closed by caller")).catch((err) => onError?.(err, "close subscription")).finally(() => {
1043
+ pool.close(relays);
1044
+ });
1039
1045
  seen.stop();
1040
1046
  perSenderRateLimiter.clear();
1041
1047
  globalRateLimiter.clear();
@@ -1180,86 +1186,95 @@ const startNostrGatewayAccount = async (ctx) => {
1180
1186
  ctx.log?.debug?.(`[${account.accountId}] blocked Nostr sender ${input.senderId} (${resolved.senderAccess.reasonCode})`);
1181
1187
  return "block";
1182
1188
  };
1183
- const bus = await startNostrBus({
1184
- accountId: account.accountId,
1185
- privateKey: account.privateKey,
1186
- relays: account.relays,
1187
- authorizeSender: async ({ senderPubkey, reply }) => await authorizeSender({
1188
- senderId: senderPubkey,
1189
- reply
1190
- }),
1191
- onMessage: async (senderPubkey, text, reply, meta) => {
1192
- const resolvedAccess = await resolveInboundAccess(senderPubkey, text);
1193
- if (resolvedAccess.senderAccess.decision !== "allow") {
1194
- ctx.log?.warn?.(`[${account.accountId}] dropping Nostr DM after preflight drift (${senderPubkey}, ${resolvedAccess.senderAccess.reasonCode})`);
1195
- return;
1196
- }
1197
- const { dispatchInboundDirectDmWithRuntime } = await import("./inbound-direct-dm-runtime-EdLLVtkh.js");
1198
- await dispatchInboundDirectDmWithRuntime({
1199
- cfg: ctx.cfg,
1200
- runtime,
1201
- channel: "nostr",
1202
- channelLabel: "Nostr",
1189
+ await runStoppablePassiveMonitor({
1190
+ abortSignal: ctx.abortSignal,
1191
+ start: async () => {
1192
+ const bus = await startNostrBus({
1203
1193
  accountId: account.accountId,
1204
- peer: {
1205
- kind: "direct",
1206
- id: senderPubkey
1207
- },
1208
- senderId: senderPubkey,
1209
- senderAddress: `nostr:${senderPubkey}`,
1210
- recipientAddress: `nostr:${account.publicKey}`,
1211
- conversationLabel: senderPubkey,
1212
- rawBody: text,
1213
- messageId: meta.eventId,
1214
- timestamp: meta.createdAt * 1e3,
1215
- commandAuthorized: resolvedAccess.commandAccess.requested ? resolvedAccess.commandAccess.authorized : void 0,
1216
- deliver: async (payload) => {
1217
- const outboundText = payload && typeof payload === "object" && "text" in payload ? payload.text ?? "" : "";
1218
- if (!outboundText.trim()) return;
1219
- const tableMode = runtime.channel.text.resolveMarkdownTableMode({
1194
+ privateKey: account.privateKey,
1195
+ relays: account.relays,
1196
+ authorizeSender: async ({ senderPubkey, reply }) => await authorizeSender({
1197
+ senderId: senderPubkey,
1198
+ reply
1199
+ }),
1200
+ onMessage: async (senderPubkey, text, reply, meta) => {
1201
+ const resolvedAccess = await resolveInboundAccess(senderPubkey, text);
1202
+ if (resolvedAccess.senderAccess.decision !== "allow") {
1203
+ ctx.log?.warn?.(`[${account.accountId}] dropping Nostr DM after preflight drift (${senderPubkey}, ${resolvedAccess.senderAccess.reasonCode})`);
1204
+ return;
1205
+ }
1206
+ const { dispatchInboundDirectDmWithRuntime } = await import("./inbound-direct-dm-runtime-EdLLVtkh.js");
1207
+ await dispatchInboundDirectDmWithRuntime({
1220
1208
  cfg: ctx.cfg,
1209
+ runtime,
1221
1210
  channel: "nostr",
1222
- accountId: account.accountId
1211
+ channelLabel: "Nostr",
1212
+ accountId: account.accountId,
1213
+ peer: {
1214
+ kind: "direct",
1215
+ id: senderPubkey
1216
+ },
1217
+ senderId: senderPubkey,
1218
+ senderAddress: `nostr:${senderPubkey}`,
1219
+ recipientAddress: `nostr:${account.publicKey}`,
1220
+ conversationLabel: senderPubkey,
1221
+ rawBody: text,
1222
+ messageId: meta.eventId,
1223
+ timestamp: meta.createdAt * 1e3,
1224
+ commandAuthorized: resolvedAccess.commandAccess.requested ? resolvedAccess.commandAccess.authorized : void 0,
1225
+ deliver: async (payload) => {
1226
+ const outboundText = payload && typeof payload === "object" && "text" in payload ? payload.text ?? "" : "";
1227
+ if (!outboundText.trim()) return;
1228
+ const tableMode = runtime.channel.text.resolveMarkdownTableMode({
1229
+ cfg: ctx.cfg,
1230
+ channel: "nostr",
1231
+ accountId: account.accountId
1232
+ });
1233
+ await reply(runtime.channel.text.convertMarkdownTables(outboundText, tableMode));
1234
+ },
1235
+ onRecordError: (err) => {
1236
+ ctx.log?.error?.(`[${account.accountId}] failed recording Nostr inbound session: ${String(err)}`);
1237
+ },
1238
+ onDispatchError: (err, info) => {
1239
+ ctx.log?.error?.(`[${account.accountId}] Nostr ${info.kind} reply failed: ${String(err)}`);
1240
+ }
1223
1241
  });
1224
- await reply(runtime.channel.text.convertMarkdownTables(outboundText, tableMode));
1225
1242
  },
1226
- onRecordError: (err) => {
1227
- ctx.log?.error?.(`[${account.accountId}] failed recording Nostr inbound session: ${String(err)}`);
1243
+ onError: (error, context) => {
1244
+ ctx.log?.error?.(`[${account.accountId}] Nostr error (${context}): ${error.message}`);
1228
1245
  },
1229
- onDispatchError: (err, info) => {
1230
- ctx.log?.error?.(`[${account.accountId}] Nostr ${info.kind} reply failed: ${String(err)}`);
1246
+ onConnect: (relay) => {
1247
+ ctx.log?.debug?.(`[${account.accountId}] Connected to relay: ${relay}`);
1248
+ },
1249
+ onDisconnect: (relay) => {
1250
+ ctx.log?.debug?.(`[${account.accountId}] Disconnected from relay: ${relay}`);
1251
+ },
1252
+ onEose: (relays) => {
1253
+ ctx.log?.debug?.(`[${account.accountId}] EOSE received from relays: ${relays}`);
1254
+ },
1255
+ onMetric: (event) => {
1256
+ if (event.name.startsWith("event.rejected.")) ctx.log?.debug?.(`[${account.accountId}] Metric: ${event.name} ${JSON.stringify(event.labels)}`);
1257
+ else if (event.name === "relay.circuit_breaker.open") ctx.log?.warn?.(`[${account.accountId}] Circuit breaker opened for relay: ${event.labels?.relay}`);
1258
+ else if (event.name === "relay.circuit_breaker.close") ctx.log?.info?.(`[${account.accountId}] Circuit breaker closed for relay: ${event.labels?.relay}`);
1259
+ else if (event.name === "relay.error") ctx.log?.debug?.(`[${account.accountId}] Relay error: ${event.labels?.relay}`);
1260
+ if (busHandle) metricsSnapshots.set(account.accountId, busHandle.getMetrics());
1231
1261
  }
1232
1262
  });
1233
- },
1234
- onError: (error, context) => {
1235
- ctx.log?.error?.(`[${account.accountId}] Nostr error (${context}): ${error.message}`);
1236
- },
1237
- onConnect: (relay) => {
1238
- ctx.log?.debug?.(`[${account.accountId}] Connected to relay: ${relay}`);
1239
- },
1240
- onDisconnect: (relay) => {
1241
- ctx.log?.debug?.(`[${account.accountId}] Disconnected from relay: ${relay}`);
1242
- },
1243
- onEose: (relays) => {
1244
- ctx.log?.debug?.(`[${account.accountId}] EOSE received from relays: ${relays}`);
1245
- },
1246
- onMetric: (event) => {
1247
- if (event.name.startsWith("event.rejected.")) ctx.log?.debug?.(`[${account.accountId}] Metric: ${event.name} ${JSON.stringify(event.labels)}`);
1248
- else if (event.name === "relay.circuit_breaker.open") ctx.log?.warn?.(`[${account.accountId}] Circuit breaker opened for relay: ${event.labels?.relay}`);
1249
- else if (event.name === "relay.circuit_breaker.close") ctx.log?.info?.(`[${account.accountId}] Circuit breaker closed for relay: ${event.labels?.relay}`);
1250
- else if (event.name === "relay.error") ctx.log?.debug?.(`[${account.accountId}] Relay error: ${event.labels?.relay}`);
1251
- if (busHandle) metricsSnapshots.set(account.accountId, busHandle.getMetrics());
1263
+ let stopped = false;
1264
+ busHandle = bus;
1265
+ activeBuses.set(account.accountId, bus);
1266
+ ctx.log?.info?.(`[${account.accountId}] Nostr provider started, connected to ${account.relays.length} relay(s)`);
1267
+ return { stop: () => {
1268
+ if (stopped) return;
1269
+ stopped = true;
1270
+ bus.close();
1271
+ if (busHandle === bus) busHandle = null;
1272
+ if (activeBuses.get(account.accountId) === bus) activeBuses.delete(account.accountId);
1273
+ metricsSnapshots.delete(account.accountId);
1274
+ ctx.log?.info?.(`[${account.accountId}] Nostr provider stopped`);
1275
+ } };
1252
1276
  }
1253
1277
  });
1254
- busHandle = bus;
1255
- activeBuses.set(account.accountId, bus);
1256
- ctx.log?.info?.(`[${account.accountId}] Nostr provider started, connected to ${account.relays.length} relay(s)`);
1257
- return { stop: () => {
1258
- bus.close();
1259
- activeBuses.delete(account.accountId);
1260
- metricsSnapshots.delete(account.accountId);
1261
- ctx.log?.info?.(`[${account.accountId}] Nostr provider stopped`);
1262
- } };
1263
1278
  };
1264
1279
  const nostrPairingTextAdapter = {
1265
1280
  idLabel: "nostrPubkey",
@@ -1,2 +1,2 @@
1
- import { n as nostrPlugin } from "./channel-aEou1Q6d.js";
1
+ import { n as nostrPlugin } from "./channel-UK7t4qb8.js";
2
2
  export { nostrPlugin };
@@ -1,18 +1,18 @@
1
1
  {
2
2
  "name": "@openclaw/nostr",
3
- "version": "2026.5.27-beta.1",
3
+ "version": "2026.5.28-beta.1",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "@openclaw/nostr",
9
- "version": "2026.5.27-beta.1",
9
+ "version": "2026.5.28-beta.1",
10
10
  "dependencies": {
11
11
  "nostr-tools": "2.23.5",
12
12
  "zod": "4.4.3"
13
13
  },
14
14
  "peerDependencies": {
15
- "openclaw": ">=2026.5.27-beta.1"
15
+ "openclaw": ">=2026.5.28-beta.1"
16
16
  },
17
17
  "peerDependenciesMeta": {
18
18
  "openclaw": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openclaw/nostr",
3
- "version": "2026.5.27-beta.1",
3
+ "version": "2026.5.28-beta.1",
4
4
  "description": "OpenClaw Nostr channel plugin for NIP-04 encrypted direct messages.",
5
5
  "repository": {
6
6
  "type": "git",
@@ -12,7 +12,7 @@
12
12
  "zod": "4.4.3"
13
13
  },
14
14
  "peerDependencies": {
15
- "openclaw": ">=2026.5.27-beta.1"
15
+ "openclaw": ">=2026.5.28-beta.1"
16
16
  },
17
17
  "peerDependenciesMeta": {
18
18
  "openclaw": {
@@ -50,10 +50,10 @@
50
50
  "minHostVersion": ">=2026.4.10"
51
51
  },
52
52
  "compat": {
53
- "pluginApi": ">=2026.5.27-beta.1"
53
+ "pluginApi": ">=2026.5.28-beta.1"
54
54
  },
55
55
  "build": {
56
- "openclawVersion": "2026.5.27-beta.1"
56
+ "openclawVersion": "2026.5.28-beta.1"
57
57
  },
58
58
  "release": {
59
59
  "publishToClawHub": true,
package/dist/test-api.js DELETED
@@ -1,2 +0,0 @@
1
- import { n as nostrPlugin } from "./channel-aEou1Q6d.js";
2
- export { nostrPlugin };