@openclaw/nostr 2026.10.1 → 2026.10.5-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.
@@ -1,6 +1,6 @@
1
- import { a as listNostrAccountIds, c as normalizePubkey, n as nostrSetupContract, o as resolveDefaultNostrAccountId, r as nostrSetupWizard, s as resolveNostrAccount } from "./setup-surface-b-YWJSGV.mjs";
2
- import { n as NostrProfileSchema, t as NostrConfigSchema } from "./config-schema-HuEK1L2K.mjs";
3
- import { l as validatePrivateKey, u as DEFAULT_RELAYS } from "./setup-adapter-I55hhpDp.mjs";
1
+ import { a as listNostrAccountIds, c as normalizePubkey, n as nostrSetupContract, o as resolveDefaultNostrAccountId, r as nostrSetupWizard, s as resolveNostrAccount } from "./setup-surface-BAq4Fs6P.mjs";
2
+ import { n as NostrProfileSchema, t as NostrConfigSchema } from "./config-schema-CkZgmIVF.mjs";
3
+ import { l as validatePrivateKey, u as DEFAULT_RELAYS } from "./setup-adapter-B2ksCBWv.mjs";
4
4
  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";
@@ -18,6 +18,7 @@ import { chunkTextForOutbound, sanitizeAssistantVisibleText, stripMarkdown } fro
18
18
  import { SimplePool, finalizeEvent, getPublicKey, verifyEvent } from "nostr-tools";
19
19
  import { decrypt, encrypt } from "nostr-tools/nip04";
20
20
  import { createDirectDmPreCryptoGuardPolicy } from "openclaw/plugin-sdk/direct-dm-guard-policy";
21
+ import { captureEffectAuthority } from "openclaw/plugin-sdk/fetch-runtime";
21
22
  import { createFixedWindowRateLimiter } from "openclaw/plugin-sdk/webhook-ingress";
22
23
  import { retryAsync } from "openclaw/plugin-sdk/retry-runtime";
23
24
  import { sleepWithAbort } from "openclaw/plugin-sdk/runtime-env";
@@ -26,160 +27,15 @@ import { createPluginRuntimeStore } from "openclaw/plugin-sdk/runtime-store";
26
27
  import { withTimeout } from "openclaw/plugin-sdk/time-runtime";
27
28
  import { buildChannelOutboundSessionRoute, stripChannelTargetPrefix } from "openclaw/plugin-sdk/core";
28
29
  //#region extensions/nostr/src/metrics.ts
29
- function createZeroMetricsState() {
30
- return {
31
- eventsReceived: 0,
32
- eventsProcessed: 0,
33
- eventsDuplicate: 0,
34
- eventsRejected: {
35
- invalidShape: 0,
36
- wrongKind: 0,
37
- stale: 0,
38
- future: 0,
39
- rateLimited: 0,
40
- invalidSignature: 0,
41
- oversizedCiphertext: 0,
42
- oversizedPlaintext: 0,
43
- decryptFailed: 0,
44
- selfMessage: 0
45
- },
46
- relays: /* @__PURE__ */ new Map(),
47
- rateLimiting: {
48
- perSenderHits: 0,
49
- globalHits: 0
50
- },
51
- decrypt: {
52
- success: 0,
53
- failure: 0
54
- },
55
- memory: {
56
- seenTrackerSize: 0,
57
- rateLimiterEntries: 0
58
- }
59
- };
60
- }
61
- function createMetricsSnapshot(state, snapshotAt) {
62
- const relays = {};
63
- for (const [url, stats] of state.relays) relays[url] = {
64
- ...stats,
65
- messagesReceived: { ...stats.messagesReceived }
66
- };
67
- return {
68
- ...state,
69
- eventsRejected: { ...state.eventsRejected },
70
- relays,
71
- rateLimiting: { ...state.rateLimiting },
72
- decrypt: { ...state.decrypt },
73
- memory: { ...state.memory },
74
- snapshotAt: snapshotAt ?? Date.now()
75
- };
76
- }
77
- /**
78
- * Create a metrics collector instance.
79
- * Optionally pass an onMetric callback to receive real-time metric events.
80
- */
81
30
  function createMetrics(onMetric) {
82
- let state = createZeroMetricsState();
83
- function getOrCreateRelay(url) {
84
- let relay = state.relays.get(url);
85
- if (!relay) {
86
- relay = {
87
- connects: 0,
88
- disconnects: 0,
89
- reconnects: 0,
90
- errors: 0,
91
- messagesReceived: {
92
- event: 0,
93
- eose: 0,
94
- closed: 0,
95
- notice: 0,
96
- ok: 0,
97
- auth: 0
98
- },
99
- circuitBreakerState: "closed",
100
- circuitBreakerOpens: 0,
101
- circuitBreakerCloses: 0
102
- };
103
- state.relays.set(url, relay);
104
- }
105
- return relay;
106
- }
107
- const relayMetric = (update) => (value, relayUrl) => {
108
- if (relayUrl) update(getOrCreateRelay(relayUrl), value);
109
- };
110
- const updates = /* @__PURE__ */ new Map([
111
- ["event.received", (value) => state.eventsReceived += value],
112
- ["event.processed", (value) => state.eventsProcessed += value],
113
- ["event.duplicate", (value) => state.eventsDuplicate += value],
114
- ["event.rejected.invalid_shape", (value) => state.eventsRejected.invalidShape += value],
115
- ["event.rejected.wrong_kind", (value) => state.eventsRejected.wrongKind += value],
116
- ["event.rejected.stale", (value) => state.eventsRejected.stale += value],
117
- ["event.rejected.future", (value) => state.eventsRejected.future += value],
118
- ["event.rejected.rate_limited", (value) => state.eventsRejected.rateLimited += value],
119
- ["event.rejected.invalid_signature", (value) => state.eventsRejected.invalidSignature += value],
120
- ["event.rejected.oversized_ciphertext", (value) => state.eventsRejected.oversizedCiphertext += value],
121
- ["event.rejected.oversized_plaintext", (value) => state.eventsRejected.oversizedPlaintext += value],
122
- ["event.rejected.decrypt_failed", (value) => state.eventsRejected.decryptFailed += value],
123
- ["event.rejected.self_message", (value) => state.eventsRejected.selfMessage += value],
124
- ["rate_limit.per_sender", (value) => state.rateLimiting.perSenderHits += value],
125
- ["rate_limit.global", (value) => state.rateLimiting.globalHits += value],
126
- ["decrypt.success", (value) => state.decrypt.success += value],
127
- ["decrypt.failure", (value) => state.decrypt.failure += value],
128
- ["memory.seen_tracker_size", (value) => state.memory.seenTrackerSize = value],
129
- ["memory.rate_limiter_entries", (value) => state.memory.rateLimiterEntries = value],
130
- ["relay.connect", relayMetric((relay, value) => relay.connects += value)],
131
- ["relay.disconnect", relayMetric((relay, value) => relay.disconnects += value)],
132
- ["relay.reconnect", relayMetric((relay, value) => relay.reconnects += value)],
133
- ["relay.error", relayMetric((relay, value) => relay.errors += value)],
134
- ["relay.message.event", relayMetric((relay, value) => relay.messagesReceived.event += value)],
135
- ["relay.message.eose", relayMetric((relay, value) => relay.messagesReceived.eose += value)],
136
- ["relay.message.closed", relayMetric((relay, value) => relay.messagesReceived.closed += value)],
137
- ["relay.message.notice", relayMetric((relay, value) => relay.messagesReceived.notice += value)],
138
- ["relay.message.ok", relayMetric((relay, value) => relay.messagesReceived.ok += value)],
139
- ["relay.message.auth", relayMetric((relay, value) => relay.messagesReceived.auth += value)],
140
- ["relay.circuit_breaker.open", relayMetric((relay, value) => {
141
- relay.circuitBreakerState = "open";
142
- relay.circuitBreakerOpens += value;
143
- })],
144
- ["relay.circuit_breaker.close", relayMetric((relay, value) => {
145
- relay.circuitBreakerState = "closed";
146
- relay.circuitBreakerCloses += value;
147
- })],
148
- ["relay.circuit_breaker.half_open", relayMetric((relay) => {
149
- relay.circuitBreakerState = "half_open";
150
- })]
151
- ]);
152
- function emit(name, value = 1, labels) {
31
+ return { emit(name, value = 1, labels) {
153
32
  onMetric?.({
154
33
  name,
155
34
  value,
156
35
  timestamp: Date.now(),
157
36
  labels
158
37
  });
159
- updates.get(name)?.(value, labels?.relay);
160
- }
161
- function getSnapshot() {
162
- return createMetricsSnapshot(state);
163
- }
164
- function reset() {
165
- state = createZeroMetricsState();
166
- }
167
- return {
168
- emit,
169
- getSnapshot,
170
- reset
171
- };
172
- }
173
- function createNoopMetrics() {
174
- const emptySnapshot = createMetricsSnapshot(createZeroMetricsState(), 0);
175
- return {
176
- emit: () => {},
177
- getSnapshot: () => ({
178
- ...emptySnapshot,
179
- snapshotAt: Date.now()
180
- }),
181
- reset: () => {}
182
- };
38
+ } };
183
39
  }
184
40
  //#endregion
185
41
  //#region extensions/nostr/src/nostr-cursor.ts
@@ -554,10 +410,7 @@ const PROFILE_FIELDS = [
554
410
  ["nip05", "nip05"],
555
411
  ["lud16", "lud16"]
556
412
  ];
557
- /**
558
- * Convert our config profile schema to NIP-01 content format.
559
- * Strips undefined fields and validates URLs.
560
- */
413
+ /** Validates URLs and omits undefined fields for NIP-01 content. */
561
414
  function profileToContent(profile) {
562
415
  const validated = NostrProfileSchema.parse(profile);
563
416
  const content = {};
@@ -567,10 +420,6 @@ function profileToContent(profile) {
567
420
  }
568
421
  return content;
569
422
  }
570
- /**
571
- * Convert NIP-01 content format back to our config profile schema.
572
- * Useful for importing existing profiles from relays.
573
- */
574
423
  function contentToProfile(content) {
575
424
  const profile = {};
576
425
  for (const [configKey, contentKey] of PROFILE_FIELDS) {
@@ -610,6 +459,7 @@ async function publishProfile(pool, sk, relays, profile, lastPublishedAt) {
610
459
  eventId: event.id,
611
460
  successes,
612
461
  failures,
462
+ /** Unix timestamp when the event was created. */
613
463
  createdAt: event.created_at
614
464
  };
615
465
  }
@@ -623,7 +473,6 @@ function createNostrRelaySubscriptionGroup(options) {
623
473
  const deadlineTimers = /* @__PURE__ */ new Set();
624
474
  let locallyClosing = false;
625
475
  const backfillStatus = new Map(relays.map((relay) => [relay, "pending"]));
626
- const confirmDeadlineMs = DEFAULT_EOSE_CONFIRM_DEADLINE_MS;
627
476
  const settleBackfill = (relay, status) => {
628
477
  if (backfillStatus.get(relay) !== "pending") return;
629
478
  backfillStatus.set(relay, status);
@@ -643,7 +492,7 @@ function createNostrRelaySubscriptionGroup(options) {
643
492
  deadlineTimers.delete(deadlineTimer);
644
493
  deadlineReached = true;
645
494
  settleBackfill(relay, "incomplete");
646
- }, confirmDeadlineMs);
495
+ }, DEFAULT_EOSE_CONFIRM_DEADLINE_MS);
647
496
  deadlineTimer.unref?.();
648
497
  deadlineTimers.add(deadlineTimer);
649
498
  subscriptions.push(options.pool.subscribeMany([relay], options.filter, {
@@ -835,7 +684,7 @@ async function startNostrBus(options) {
835
684
  const accountId = options.accountId ?? pk.slice(0, 16);
836
685
  const gatewayStartedAt = Math.floor(Date.now() / 1e3);
837
686
  const guardPolicy = createDirectDmPreCryptoGuardPolicy(options.guardPolicy);
838
- const metrics = onMetric ? createMetrics(onMetric) : createNoopMetrics();
687
+ const metrics = createMetrics(onMetric);
839
688
  const circuitBreakers = /* @__PURE__ */ new Map();
840
689
  const healthTracker = createRelayHealthTracker();
841
690
  for (const relay of relays) circuitBreakers.set(relay, createCircuitBreaker(relay, metrics));
@@ -1099,6 +948,7 @@ async function startNostrBus(options) {
1099
948
  return closePromise;
1100
949
  };
1101
950
  async function sendEncryptedDm(toPubkey, text, sendOptions) {
951
+ const effect = captureEffectAuthority();
1102
952
  const ciphertext = encrypt(sk, toPubkey, text);
1103
953
  const tags = [["p", toPubkey]];
1104
954
  if (sendOptions?.replyToEventId) tags.push(["e", sendOptions.replyToEventId]);
@@ -1135,13 +985,19 @@ async function startNostrBus(options) {
1135
985
  await sendOptions.onPlatformSendDispatch();
1136
986
  sendOptions.assertDirectAdapterHandoff?.();
1137
987
  }
988
+ let initiated = false;
1138
989
  try {
1139
- await connection.publish(reply);
990
+ await effect.initiate(() => {
991
+ sendOptions?.assertDirectAdapterHandoff?.();
992
+ initiated = true;
993
+ return connection.publish(reply);
994
+ });
1140
995
  const latency = Date.now() - startTime;
1141
996
  cb?.recordSuccess();
1142
997
  healthTracker.recordSuccess(relay, latency);
1143
998
  return reply.id;
1144
999
  } catch (err) {
1000
+ if (!initiated) throw err;
1145
1001
  recordFailure(err);
1146
1002
  }
1147
1003
  }
@@ -1151,7 +1007,6 @@ async function startNostrBus(options) {
1151
1007
  close,
1152
1008
  publicKey: pk,
1153
1009
  sendDm: sendEncryptedDm,
1154
- getMetrics: () => metrics.getSnapshot(),
1155
1010
  publishProfile: publishProfile$1,
1156
1011
  getProfileState
1157
1012
  };
@@ -1188,11 +1043,12 @@ const nostrIngressIdentity = {
1188
1043
  sensitivity: "pii",
1189
1044
  entryIdPrefix: "nostr-entry"
1190
1045
  };
1191
- const startNostrGatewayAccount = async (ctx) => {
1046
+ async function startNostrGatewayAccount(ctx) {
1192
1047
  const account = ctx.account;
1193
1048
  ctx.setStatus({
1194
1049
  accountId: account.accountId,
1195
1050
  publicKey: account.publicKey,
1051
+ profile: account.profile,
1196
1052
  lifecycle: "starting"
1197
1053
  });
1198
1054
  ctx.log?.info?.(`[${account.accountId}] starting Nostr provider (pubkey: ${account.publicKey})`);
@@ -1223,23 +1079,23 @@ const startNostrGatewayAccount = async (ctx) => {
1223
1079
  });
1224
1080
  const connectedRelays = /* @__PURE__ */ new Set();
1225
1081
  const authorizeSender = async (input) => {
1226
- const resolved = await resolveInboundAccess(input.senderId, "");
1082
+ const resolved = await resolveInboundAccess(input.senderPubkey, "");
1227
1083
  if (resolved.senderAccess.decision === "allow") return "allow";
1228
1084
  if (resolved.senderAccess.decision === "pairing") {
1229
1085
  await pairing.issueChallenge({
1230
- senderId: input.senderId,
1231
- senderIdLine: `Your Nostr pubkey: ${input.senderId}`,
1086
+ senderId: input.senderPubkey,
1087
+ senderIdLine: `Your Nostr pubkey: ${input.senderPubkey}`,
1232
1088
  sendPairingReply: input.reply,
1233
1089
  onCreated: () => {
1234
- ctx.log?.debug?.(`[${account.accountId}] nostr pairing request sender=${input.senderId}`);
1090
+ ctx.log?.debug?.(`[${account.accountId}] nostr pairing request sender=${input.senderPubkey}`);
1235
1091
  },
1236
1092
  onReplyError: (err) => {
1237
- ctx.log?.warn?.(`[${account.accountId}] nostr pairing reply failed for ${input.senderId}: ${String(err)}`);
1093
+ ctx.log?.warn?.(`[${account.accountId}] nostr pairing reply failed for ${input.senderPubkey}: ${String(err)}`);
1238
1094
  }
1239
1095
  });
1240
1096
  return "pairing";
1241
1097
  }
1242
- ctx.log?.debug?.(`[${account.accountId}] blocked Nostr sender ${input.senderId} (${resolved.senderAccess.reasonCode})`);
1098
+ ctx.log?.debug?.(`[${account.accountId}] blocked Nostr sender ${input.senderPubkey} (${resolved.senderAccess.reasonCode})`);
1243
1099
  return "block";
1244
1100
  };
1245
1101
  await runPassiveAccountLifecycle({
@@ -1249,10 +1105,7 @@ const startNostrGatewayAccount = async (ctx) => {
1249
1105
  accountId: account.accountId,
1250
1106
  privateKey: account.privateKey,
1251
1107
  relays: account.relays,
1252
- authorizeSender: async ({ senderPubkey, reply }) => await authorizeSender({
1253
- senderId: senderPubkey,
1254
- reply
1255
- }),
1108
+ authorizeSender,
1256
1109
  onMessage: async (senderPubkey, text, reply, meta, lifecycle) => {
1257
1110
  const resolvedAccess = await resolveInboundAccess(senderPubkey, text);
1258
1111
  if (resolvedAccess.senderAccess.decision !== "allow") {
@@ -1342,7 +1195,7 @@ const startNostrGatewayAccount = async (ctx) => {
1342
1195
  await monitor.stop();
1343
1196
  }
1344
1197
  });
1345
- };
1198
+ }
1346
1199
  const nostrPairingTextAdapter = {
1347
1200
  idLabel: "nostrPubkey",
1348
1201
  message: "Your pairing request has been approved!",
@@ -2,13 +2,7 @@ import { AllowFromListSchema, DmPolicySchema, MarkdownConfigSchema } from "openc
2
2
  import { buildSecretInputSchema } from "openclaw/plugin-sdk/secret-input";
3
3
  import { z } from "zod";
4
4
  //#region extensions/nostr/src/config-schema.ts
5
- const safeUrlSchema = z.string().url().refine((url) => {
6
- try {
7
- return new URL(url).protocol === "https:";
8
- } catch {
9
- return false;
10
- }
11
- }, { message: "URL must use https:// protocol" });
5
+ const safeUrlSchema = z.string().url().refine((url) => URL.parse(url)?.protocol === "https:", { message: "URL must use https:// protocol" });
12
6
  /**
13
7
  * NIP-01 profile metadata schema
14
8
  * https://github.com/nostr-protocol/nips/blob/master/01.md
@@ -80,18 +80,15 @@ function buildNostrSetupPatch(accountId, patch) {
80
80
  function parseRelayUrls(raw) {
81
81
  const relays = [];
82
82
  for (const entry of splitSetupEntries(raw)) {
83
- try {
84
- const parsed = new URL(entry);
85
- if (parsed.protocol !== "ws:" && parsed.protocol !== "wss:") return {
86
- relays: [],
87
- error: `Relay must use ws:// or wss:// (${entry})`
88
- };
89
- } catch {
90
- return {
91
- relays: [],
92
- error: `Invalid relay URL: ${entry}`
93
- };
94
- }
83
+ const parsed = URL.parse(entry);
84
+ if (!parsed) return {
85
+ relays: [],
86
+ error: `Invalid relay URL: ${entry}`
87
+ };
88
+ if (parsed.protocol !== "ws:" && parsed.protocol !== "wss:") return {
89
+ relays: [],
90
+ error: `Relay must use ws:// or wss:// (${entry})`
91
+ };
95
92
  relays.push(entry);
96
93
  }
97
94
  return { relays: uniqueStrings(relays) };
@@ -0,0 +1,2 @@
1
+ import { t as nostrSetupPlugin } from "./setup-plugin-api-CZ-PzJZP.mjs";
2
+ export { nostrSetupPlugin };
@@ -1,5 +1,5 @@
1
- import { t as NostrConfigSchema } from "./config-schema-HuEK1L2K.mjs";
2
- import { i as createNostrSetupStatus, n as createNostrSetupAdapter, o as getNostrConfig, r as createNostrSetupContract, s as resolveNostrAccountBase } from "./setup-adapter-I55hhpDp.mjs";
1
+ import { t as NostrConfigSchema } from "./config-schema-CkZgmIVF.mjs";
2
+ import { i as createNostrSetupStatus, n as createNostrSetupAdapter, o as getNostrConfig, r as createNostrSetupContract, s as resolveNostrAccountBase } from "./setup-adapter-B2ksCBWv.mjs";
3
3
  import { describeAccountSnapshot } from "openclaw/plugin-sdk/account-helpers";
4
4
  import { buildChannelConfigSchema } from "openclaw/plugin-sdk/channel-plugin-common";
5
5
  import { normalizeAccountId } from "openclaw/plugin-sdk/account-id";
@@ -15,7 +15,7 @@ function resolveSetupNostrAccount(params) {
15
15
  }
16
16
  const nostrSetupWizard = createDelegatedSetupWizardProxy({
17
17
  channel,
18
- loadWizard: async () => (await import("./setup-surface-b-YWJSGV.mjs").then((n) => n.i)).nostrSetupWizard,
18
+ loadWizard: async () => (await import("./setup-surface-BAq4Fs6P.mjs").then((n) => n.i)).nostrSetupWizard,
19
19
  status: createNostrSetupStatus(resolveSetupNostrAccount),
20
20
  resolveShouldPromptAccountIds: () => false,
21
21
  delegatePrepare: true,
@@ -1,4 +1,4 @@
1
- import { a as parseRelayUrls, c as hasConfiguredNostrPrivateKey, i as createNostrSetupStatus, l as validatePrivateKey, n as createNostrSetupAdapter, o as getNostrConfig, r as createNostrSetupContract, s as resolveNostrAccountBase, t as buildNostrSetupPatch, u as DEFAULT_RELAYS } from "./setup-adapter-I55hhpDp.mjs";
1
+ import { a as parseRelayUrls, c as hasConfiguredNostrPrivateKey, i as createNostrSetupStatus, l as validatePrivateKey, n as createNostrSetupAdapter, o as getNostrConfig, r as createNostrSetupContract, s as resolveNostrAccountBase, t as buildNostrSetupPatch, u as DEFAULT_RELAYS } from "./setup-adapter-B2ksCBWv.mjs";
2
2
  import { createAccountListHelpers } from "openclaw/plugin-sdk/account-helpers";
3
3
  import { normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
4
4
  import { hasConfiguredSecretInput, normalizeSecretInputString } from "openclaw/plugin-sdk/secret-input";
package/dist/api.js CHANGED
@@ -1,7 +1,7 @@
1
- import { s as resolveNostrAccount } from "./.setup/setup-surface-b-YWJSGV.mjs";
1
+ import { s as resolveNostrAccount } from "./.setup/setup-surface-BAq4Fs6P.mjs";
2
2
  import { getPluginRuntimeGatewayRequestScope } from "./runtime-api.js";
3
- import { n as NostrProfileSchema } from "./.setup/config-schema-HuEK1L2K.mjs";
4
- import { a as getNostrRuntime, i as contentToProfile, n as nostrPlugin, o as setNostrRuntime, r as publishNostrProfile, t as getNostrProfileState } from "./.setup/channel-BaBU_Q15.mjs";
3
+ import { n as NostrProfileSchema } from "./.setup/config-schema-CkZgmIVF.mjs";
4
+ import { a as getNostrRuntime, i as contentToProfile, n as nostrPlugin, o as setNostrRuntime, r as publishNostrProfile, t as getNostrProfileState } from "./.setup/channel-B_rRGVN_.mjs";
5
5
  import { getPluginRuntimeGatewayRequestScope as getPluginRuntimeGatewayRequestScope$1 } from "openclaw/plugin-sdk/plugin-runtime";
6
6
  import { isRecord, normalizeLowercaseStringOrEmpty, normalizeOptionalLowercaseString } from "openclaw/plugin-sdk/string-coerce-runtime";
7
7
  import { z } from "zod";
@@ -218,10 +218,10 @@ function sendJson(res, status, body) {
218
218
  res.setHeader("Content-Type", "application/json; charset=utf-8");
219
219
  res.end(JSON.stringify(body));
220
220
  }
221
- async function readJsonBody(req, maxBytes = 65536, timeoutMs = 3e4) {
221
+ async function readJsonBody(req) {
222
222
  const result = await readJsonBodyWithLimit(req, {
223
- maxBytes,
224
- timeoutMs,
223
+ maxBytes: 65536,
224
+ timeoutMs: 3e4,
225
225
  emptyObjectOnEmpty: true
226
226
  });
227
227
  if (result.ok) return result.value;
@@ -229,9 +229,6 @@ async function readJsonBody(req, maxBytes = 65536, timeoutMs = 3e4) {
229
229
  if (result.code === "REQUEST_BODY_TIMEOUT" || result.code === "CONNECTION_CLOSED") throw new Error(requestBodyErrorToText(result.code));
230
230
  throw new Error(result.code === "INVALID_JSON" ? "Invalid JSON" : result.error);
231
231
  }
232
- function parseAccountIdFromPath(pathname) {
233
- return pathname.match(/^\/api\/channels\/nostr\/([^/]+)\/profile/)?.[1] ?? null;
234
- }
235
232
  function isLoopbackRemoteAddress(remoteAddress) {
236
233
  if (!remoteAddress) return false;
237
234
  const ipLower = normalizeLowercaseStringOrEmpty(remoteAddress).replace(/^\[|\]$/g, "");
@@ -298,12 +295,21 @@ function createNostrProfileHttpHandler(ctx) {
298
295
  return async (req, res) => {
299
296
  const url = new URL(req.url ?? "/", `http://${req.headers.host ?? "localhost"}`);
300
297
  if (!url.pathname.startsWith("/api/channels/nostr/")) return false;
301
- const accountId = parseAccountIdFromPath(url.pathname);
298
+ const accountId = url.pathname.match(/^\/api\/channels\/nostr\/([^/]+)\/profile/)?.[1];
302
299
  if (!accountId) return false;
303
300
  const isImport = url.pathname.endsWith("/profile/import");
304
301
  if (!(url.pathname.endsWith("/profile") || isImport)) return false;
305
302
  try {
306
- if (req.method === "GET" && !isImport) return await handleGetProfile(accountId, ctx, res);
303
+ if (req.method === "GET" && !isImport) {
304
+ const configProfile = ctx.getConfigProfile(accountId);
305
+ const publishState = await getNostrProfileState(accountId);
306
+ sendJson(res, 200, {
307
+ ok: true,
308
+ profile: configProfile ?? null,
309
+ publishState: publishState ?? null
310
+ });
311
+ return true;
312
+ }
307
313
  if (req.method === "PUT" && !isImport || req.method === "POST" && isImport) {
308
314
  if (!enforceGatewayMutationScope(ctx, accountId, res)) return true;
309
315
  const rejection = resolveLoopbackMutationRejection(req);
@@ -333,16 +339,6 @@ function createNostrProfileHttpHandler(ctx) {
333
339
  }
334
340
  };
335
341
  }
336
- async function handleGetProfile(accountId, ctx, res) {
337
- const configProfile = ctx.getConfigProfile(accountId);
338
- const publishState = await getNostrProfileState(accountId);
339
- sendJson(res, 200, {
340
- ok: true,
341
- profile: configProfile ?? null,
342
- publishState: publishState ?? null
343
- });
344
- return true;
345
- }
346
342
  async function handleUpdateProfile(accountId, ctx, req, res) {
347
343
  if (profileRateLimiter.isRateLimited(accountId)) {
348
344
  sendJson(res, 429, {
@@ -1,2 +1,2 @@
1
- import { n as nostrPlugin } from "./.setup/channel-BaBU_Q15.mjs";
1
+ import { n as nostrPlugin } from "./.setup/channel-B_rRGVN_.mjs";
2
2
  export { nostrPlugin };
package/dist/setup-api.js CHANGED
@@ -1,2 +1,2 @@
1
- import { r as nostrSetupWizard, t as nostrSetupAdapter } from "./.setup/setup-surface-b-YWJSGV.mjs";
1
+ import { r as nostrSetupWizard, t as nostrSetupAdapter } from "./.setup/setup-surface-BAq4Fs6P.mjs";
2
2
  export { nostrSetupAdapter, nostrSetupWizard };
@@ -4,7 +4,7 @@ import { fileURLToPath } from "node:url";
4
4
  var setup_entry_default = defineBundledChannelSetupEntry({
5
5
  importMetaUrl: import.meta.url,
6
6
  plugin: {
7
- specifier: fileURLToPath(new URL(".setup/setup-plugin-api-BUsavy-i.mjs", import.meta.url).href),
7
+ specifier: fileURLToPath(new URL(".setup/setup-plugin-api-CNjlfjWb.mjs", import.meta.url).href),
8
8
  exportName: "nostrSetupPlugin"
9
9
  },
10
10
  secrets: {
@@ -1,2 +1,2 @@
1
- import { t as nostrSetupPlugin } from "./.setup/setup-plugin-api-C_HK3q8q.mjs";
1
+ import { t as nostrSetupPlugin } from "./.setup/setup-plugin-api-CZ-PzJZP.mjs";
2
2
  export { nostrSetupPlugin };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openclaw/nostr",
3
- "version": "2026.10.1",
3
+ "version": "2026.10.5-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.6.5"
13
13
  },
14
14
  "peerDependencies": {
15
- "openclaw": ">=2026.10.1"
15
+ "openclaw": ">=2026.10.5-beta.1"
16
16
  },
17
17
  "peerDependenciesMeta": {
18
18
  "openclaw": {
@@ -80,10 +80,10 @@
80
80
  "minHostVersion": ">=2026.4.10"
81
81
  },
82
82
  "compat": {
83
- "pluginApi": ">=2026.10.1"
83
+ "pluginApi": ">=2026.10.5-beta.1"
84
84
  },
85
85
  "build": {
86
- "openclawVersion": "2026.10.1"
86
+ "openclawVersion": "2026.10.5-beta.1"
87
87
  },
88
88
  "release": {
89
89
  "publishToClawHub": true,
@@ -1,2 +0,0 @@
1
- import { t as nostrSetupPlugin } from "./setup-plugin-api-C_HK3q8q.mjs";
2
- export { nostrSetupPlugin };