@openclaw/nostr 2026.8.1-beta.2 → 2026.9.1-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.
Files changed (135) hide show
  1. package/dist/api.js +4 -4
  2. package/dist/{channel-BifOvAwp.js → channel-BwnvE_wr.js} +24 -72
  3. package/dist/channel-plugin-api.js +1 -1
  4. package/dist/index.js +4 -0
  5. package/dist/secret-contract-api.js +37 -0
  6. package/dist/{setup-adapter-DLthHa2q.js → setup-adapter-KJ_iGKiG.js} +33 -2
  7. package/dist/setup-api.js +1 -1
  8. package/dist/setup-entry.js +4 -0
  9. package/dist/setup-plugin-api.js +8 -11
  10. package/dist/{setup-surface-DAhyNOcb.js → setup-surface-CzjfhV72.js} +8 -38
  11. package/node_modules/nostr-tools/lib/cjs/abstract-pool.js +7 -5
  12. package/node_modules/nostr-tools/lib/cjs/abstract-pool.js.map +2 -2
  13. package/node_modules/nostr-tools/lib/cjs/abstract-relay.js +6 -4
  14. package/node_modules/nostr-tools/lib/cjs/abstract-relay.js.map +2 -2
  15. package/node_modules/nostr-tools/lib/cjs/core.js +10 -6
  16. package/node_modules/nostr-tools/lib/cjs/core.js.map +2 -2
  17. package/node_modules/nostr-tools/lib/cjs/index.js +17 -11
  18. package/node_modules/nostr-tools/lib/cjs/index.js.map +2 -2
  19. package/node_modules/nostr-tools/lib/cjs/kinds.js +2 -0
  20. package/node_modules/nostr-tools/lib/cjs/kinds.js.map +2 -2
  21. package/node_modules/nostr-tools/lib/cjs/nip04.js.map +1 -1
  22. package/node_modules/nostr-tools/lib/cjs/nip10.js +2 -0
  23. package/node_modules/nostr-tools/lib/cjs/nip10.js.map +2 -2
  24. package/node_modules/nostr-tools/lib/cjs/nip13.js.map +1 -1
  25. package/node_modules/nostr-tools/lib/cjs/nip17.js +2 -0
  26. package/node_modules/nostr-tools/lib/cjs/nip17.js.map +2 -2
  27. package/node_modules/nostr-tools/lib/cjs/nip18.js +2 -0
  28. package/node_modules/nostr-tools/lib/cjs/nip18.js.map +2 -2
  29. package/node_modules/nostr-tools/lib/cjs/nip19.js.map +1 -1
  30. package/node_modules/nostr-tools/lib/cjs/nip21.js.map +1 -1
  31. package/node_modules/nostr-tools/lib/cjs/nip22.js +2 -0
  32. package/node_modules/nostr-tools/lib/cjs/nip22.js.map +2 -2
  33. package/node_modules/nostr-tools/lib/cjs/nip25.js +2 -0
  34. package/node_modules/nostr-tools/lib/cjs/nip25.js.map +2 -2
  35. package/node_modules/nostr-tools/lib/cjs/nip27.js.map +1 -1
  36. package/node_modules/nostr-tools/lib/cjs/nip28.js +2 -0
  37. package/node_modules/nostr-tools/lib/cjs/nip28.js.map +2 -2
  38. package/node_modules/nostr-tools/lib/cjs/nip29.js +288 -12
  39. package/node_modules/nostr-tools/lib/cjs/nip29.js.map +2 -2
  40. package/node_modules/nostr-tools/lib/cjs/nip44.js.map +1 -1
  41. package/node_modules/nostr-tools/lib/cjs/nip45.js +2 -0
  42. package/node_modules/nostr-tools/lib/cjs/nip45.js.map +2 -2
  43. package/node_modules/nostr-tools/lib/cjs/nip46.js +9 -5
  44. package/node_modules/nostr-tools/lib/cjs/nip46.js.map +2 -2
  45. package/node_modules/nostr-tools/lib/cjs/nip47.js +2 -0
  46. package/node_modules/nostr-tools/lib/cjs/nip47.js.map +2 -2
  47. package/node_modules/nostr-tools/lib/cjs/nip57.js +2 -0
  48. package/node_modules/nostr-tools/lib/cjs/nip57.js.map +2 -2
  49. package/node_modules/nostr-tools/lib/cjs/nip59.js +2 -0
  50. package/node_modules/nostr-tools/lib/cjs/nip59.js.map +2 -2
  51. package/node_modules/nostr-tools/lib/cjs/nip98.js +2 -0
  52. package/node_modules/nostr-tools/lib/cjs/nip98.js.map +2 -2
  53. package/node_modules/nostr-tools/lib/cjs/nipb0.js +2 -0
  54. package/node_modules/nostr-tools/lib/cjs/nipb0.js.map +2 -2
  55. package/node_modules/nostr-tools/lib/cjs/nipb7.js +2 -0
  56. package/node_modules/nostr-tools/lib/cjs/nipb7.js.map +2 -2
  57. package/node_modules/nostr-tools/lib/cjs/pool.js +9 -5
  58. package/node_modules/nostr-tools/lib/cjs/pool.js.map +2 -2
  59. package/node_modules/nostr-tools/lib/cjs/pure.js +10 -6
  60. package/node_modules/nostr-tools/lib/cjs/pure.js.map +2 -2
  61. package/node_modules/nostr-tools/lib/cjs/references.js.map +1 -1
  62. package/node_modules/nostr-tools/lib/cjs/relay.js +8 -4
  63. package/node_modules/nostr-tools/lib/cjs/relay.js.map +2 -2
  64. package/node_modules/nostr-tools/lib/cjs/signer.js +2 -0
  65. package/node_modules/nostr-tools/lib/cjs/signer.js.map +2 -2
  66. package/node_modules/nostr-tools/lib/cjs/utils.js +2 -0
  67. package/node_modules/nostr-tools/lib/cjs/utils.js.map +2 -2
  68. package/node_modules/nostr-tools/lib/cjs/wasm.js +10 -6
  69. package/node_modules/nostr-tools/lib/cjs/wasm.js.map +2 -2
  70. package/node_modules/nostr-tools/lib/esm/abstract-pool.js +7 -5
  71. package/node_modules/nostr-tools/lib/esm/abstract-pool.js.map +2 -2
  72. package/node_modules/nostr-tools/lib/esm/abstract-relay.js +6 -4
  73. package/node_modules/nostr-tools/lib/esm/abstract-relay.js.map +2 -2
  74. package/node_modules/nostr-tools/lib/esm/core.js +10 -6
  75. package/node_modules/nostr-tools/lib/esm/core.js.map +2 -2
  76. package/node_modules/nostr-tools/lib/esm/index.js +17 -11
  77. package/node_modules/nostr-tools/lib/esm/index.js.map +2 -2
  78. package/node_modules/nostr-tools/lib/esm/kinds.js +2 -0
  79. package/node_modules/nostr-tools/lib/esm/kinds.js.map +2 -2
  80. package/node_modules/nostr-tools/lib/esm/nip04.js.map +1 -1
  81. package/node_modules/nostr-tools/lib/esm/nip10.js +2 -0
  82. package/node_modules/nostr-tools/lib/esm/nip10.js.map +2 -2
  83. package/node_modules/nostr-tools/lib/esm/nip13.js.map +1 -1
  84. package/node_modules/nostr-tools/lib/esm/nip17.js +2 -0
  85. package/node_modules/nostr-tools/lib/esm/nip17.js.map +2 -2
  86. package/node_modules/nostr-tools/lib/esm/nip18.js +2 -0
  87. package/node_modules/nostr-tools/lib/esm/nip18.js.map +2 -2
  88. package/node_modules/nostr-tools/lib/esm/nip19.js.map +1 -1
  89. package/node_modules/nostr-tools/lib/esm/nip21.js.map +1 -1
  90. package/node_modules/nostr-tools/lib/esm/nip22.js +2 -0
  91. package/node_modules/nostr-tools/lib/esm/nip22.js.map +2 -2
  92. package/node_modules/nostr-tools/lib/esm/nip25.js +2 -0
  93. package/node_modules/nostr-tools/lib/esm/nip25.js.map +2 -2
  94. package/node_modules/nostr-tools/lib/esm/nip27.js.map +1 -1
  95. package/node_modules/nostr-tools/lib/esm/nip28.js +2 -0
  96. package/node_modules/nostr-tools/lib/esm/nip28.js.map +2 -2
  97. package/node_modules/nostr-tools/lib/esm/nip29.js +288 -12
  98. package/node_modules/nostr-tools/lib/esm/nip29.js.map +2 -2
  99. package/node_modules/nostr-tools/lib/esm/nip44.js.map +1 -1
  100. package/node_modules/nostr-tools/lib/esm/nip45.js +2 -0
  101. package/node_modules/nostr-tools/lib/esm/nip45.js.map +2 -2
  102. package/node_modules/nostr-tools/lib/esm/nip46.js +9 -5
  103. package/node_modules/nostr-tools/lib/esm/nip46.js.map +2 -2
  104. package/node_modules/nostr-tools/lib/esm/nip47.js +2 -0
  105. package/node_modules/nostr-tools/lib/esm/nip47.js.map +2 -2
  106. package/node_modules/nostr-tools/lib/esm/nip57.js +2 -0
  107. package/node_modules/nostr-tools/lib/esm/nip57.js.map +2 -2
  108. package/node_modules/nostr-tools/lib/esm/nip59.js +2 -0
  109. package/node_modules/nostr-tools/lib/esm/nip59.js.map +2 -2
  110. package/node_modules/nostr-tools/lib/esm/nip98.js +2 -0
  111. package/node_modules/nostr-tools/lib/esm/nip98.js.map +2 -2
  112. package/node_modules/nostr-tools/lib/esm/nipb0.js +2 -0
  113. package/node_modules/nostr-tools/lib/esm/nipb0.js.map +2 -2
  114. package/node_modules/nostr-tools/lib/esm/nipb7.js +2 -0
  115. package/node_modules/nostr-tools/lib/esm/nipb7.js.map +2 -2
  116. package/node_modules/nostr-tools/lib/esm/pool.js +9 -5
  117. package/node_modules/nostr-tools/lib/esm/pool.js.map +2 -2
  118. package/node_modules/nostr-tools/lib/esm/pure.js +10 -6
  119. package/node_modules/nostr-tools/lib/esm/pure.js.map +2 -2
  120. package/node_modules/nostr-tools/lib/esm/references.js.map +1 -1
  121. package/node_modules/nostr-tools/lib/esm/relay.js +8 -4
  122. package/node_modules/nostr-tools/lib/esm/relay.js.map +2 -2
  123. package/node_modules/nostr-tools/lib/esm/signer.js +2 -0
  124. package/node_modules/nostr-tools/lib/esm/signer.js.map +2 -2
  125. package/node_modules/nostr-tools/lib/esm/utils.js +2 -0
  126. package/node_modules/nostr-tools/lib/esm/utils.js.map +2 -2
  127. package/node_modules/nostr-tools/lib/esm/wasm.js +10 -6
  128. package/node_modules/nostr-tools/lib/esm/wasm.js.map +2 -2
  129. package/node_modules/nostr-tools/lib/nostr.bundle.js +17 -11
  130. package/node_modules/nostr-tools/lib/nostr.bundle.js.map +2 -2
  131. package/node_modules/nostr-tools/lib/types/core.d.ts +5 -0
  132. package/node_modules/nostr-tools/lib/types/nip29.d.ts +243 -3
  133. package/node_modules/nostr-tools/lib/types/nip29.test.d.ts +1 -0
  134. package/node_modules/nostr-tools/package.json +1 -1
  135. package/package.json +5 -5
package/dist/api.js CHANGED
@@ -1,14 +1,14 @@
1
- import { s as resolveNostrAccount } from "./setup-surface-DAhyNOcb.js";
1
+ import { s as resolveNostrAccount } from "./setup-surface-CzjfhV72.js";
2
2
  import { getPluginRuntimeGatewayRequestScope } from "./runtime-api.js";
3
3
  import { n as NostrProfileSchema } from "./config-schema-CkYd8ACn.js";
4
- import { a as getNostrRuntime, i as contentToProfile, n as nostrPlugin, o as setNostrRuntime, r as publishNostrProfile, t as getNostrProfileState } from "./channel-BifOvAwp.js";
4
+ import { a as getNostrRuntime, i as contentToProfile, n as nostrPlugin, o as setNostrRuntime, r as publishNostrProfile, t as getNostrProfileState } from "./channel-BwnvE_wr.js";
5
5
  import { normalizeLowercaseStringOrEmpty, normalizeOptionalLowercaseString, readStringValue } from "openclaw/plugin-sdk/string-coerce-runtime";
6
6
  import { z } from "zod";
7
7
  import { SimplePool } from "nostr-tools";
8
+ import { createFixedWindowRateLimiter as createFixedWindowRateLimiter$1 } from "openclaw/plugin-sdk/webhook-ingress";
8
9
  import { KeyedAsyncQueue } from "openclaw/plugin-sdk/keyed-async-queue";
9
10
  import { isBlockedHostnameOrIp, isLoopbackHost } from "openclaw/plugin-sdk/ssrf-runtime";
10
11
  import { readJsonBodyWithLimit, requestBodyErrorToText } from "openclaw/plugin-sdk/webhook-request-guards";
11
- import { createFixedWindowRateLimiter } from "openclaw/plugin-sdk/webhook-ingress";
12
12
  import { resolveTimerTimeoutMs } from "openclaw/plugin-sdk/number-runtime";
13
13
  //#region extensions/nostr/src/nostr-profile-url-safety.ts
14
14
  function validateUrlSafety(urlStr) {
@@ -184,7 +184,7 @@ function mergeProfiles(local, imported) {
184
184
  }
185
185
  //#endregion
186
186
  //#region extensions/nostr/src/nostr-profile-http.ts
187
- const profileRateLimiter = createFixedWindowRateLimiter({
187
+ const profileRateLimiter = createFixedWindowRateLimiter$1({
188
188
  windowMs: 6e4,
189
189
  maxRequests: 5,
190
190
  maxTrackedKeys: 2048
@@ -1,6 +1,6 @@
1
- import { a as listNostrAccountIds, c as normalizePubkey, l as validatePrivateKey, n as nostrSetupContract, o as resolveDefaultNostrAccountId, r as nostrSetupWizard, s as resolveNostrAccount } from "./setup-surface-DAhyNOcb.js";
1
+ import { a as listNostrAccountIds, c as normalizePubkey, n as nostrSetupContract, o as resolveDefaultNostrAccountId, r as nostrSetupWizard, s as resolveNostrAccount } from "./setup-surface-CzjfhV72.js";
2
2
  import { a as collectStatusIssuesFromLastError, i as buildChannelConfigSchema, n as NostrProfileSchema, o as createDefaultChannelRuntimeState, r as DEFAULT_ACCOUNT_ID, s as formatPairingApproveHint, t as NostrConfigSchema } from "./config-schema-CkYd8ACn.js";
3
- import { s as DEFAULT_RELAYS } from "./setup-adapter-DLthHa2q.js";
3
+ import { c as validatePrivateKey, l as DEFAULT_RELAYS } from "./setup-adapter-KJ_iGKiG.js";
4
4
  import { t as normalizeNostrStateAccountId } from "./state-account-id-CvBZ9s6P.js";
5
5
  import { describeAccountSnapshot } from "openclaw/plugin-sdk/account-helpers";
6
6
  import { createScopedDmSecurityResolver, createTopLevelChannelConfigAdapter } from "openclaw/plugin-sdk/channel-config-helpers";
@@ -18,6 +18,9 @@ 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 { createFixedWindowRateLimiter } from "openclaw/plugin-sdk/webhook-ingress";
22
+ import { retryAsync } from "openclaw/plugin-sdk/retry-runtime";
23
+ import { sleepWithAbort } from "openclaw/plugin-sdk/runtime-env";
21
24
  import { formatErrorMessage } from "openclaw/plugin-sdk/error-runtime";
22
25
  import { createPluginRuntimeStore } from "openclaw/plugin-sdk/runtime-store";
23
26
  import { buildChannelOutboundSessionRoute, stripChannelTargetPrefix } from "openclaw/plugin-sdk/core";
@@ -289,19 +292,16 @@ function createNostrCursorStateWriter(options) {
289
292
  let activeFlush;
290
293
  let recoveryFlush;
291
294
  const writeWithRetry = async (cursor) => {
292
- let lastError;
293
- for (const delayMs of CURSOR_WRITE_RETRY_MS) {
294
- if (delayMs > 0) await new Promise((resolve) => {
295
- setTimeout(resolve, delayMs);
295
+ try {
296
+ await retryAsync(() => options.write(cursor), {
297
+ attempts: CURSOR_WRITE_RETRY_MS.length,
298
+ minDelayMs: 0,
299
+ delayMs: ({ attempt }) => CURSOR_WRITE_RETRY_MS[attempt] ?? 0,
300
+ sleep: (delayMs) => sleepWithAbort(delayMs)
296
301
  });
297
- try {
298
- await options.write(cursor);
299
- return;
300
- } catch (error) {
301
- lastError = error;
302
- }
302
+ } catch (error) {
303
+ throw new Error("Nostr cursor state write failed.", { cause: error });
303
304
  }
304
- throw new Error("Nostr cursor state write failed.", { cause: lastError });
305
305
  };
306
306
  const enqueueWrite = (cursor) => {
307
307
  const write = writeTail.then(() => writeWithRetry(cursor));
@@ -548,12 +548,8 @@ function createNostrIngress(options) {
548
548
  const pending = await getQueue().listPending({ limit: options.maxPendingEvents });
549
549
  const claims = await getQueue().listClaims();
550
550
  if (pending.length + claims.length >= options.maxPendingEvents) throw new NostrIngressAdmissionRejectedError("backpressure", "Nostr event exceeds the durable ingress backlog.");
551
- let lastError;
552
- for (const delayMs of NOSTR_INGRESS_APPEND_RETRY_MS) {
553
- if (delayMs > 0) await new Promise((resolve) => {
554
- setTimeout(resolve, delayMs);
555
- });
556
- try {
551
+ try {
552
+ return await retryAsync(async () => {
557
553
  const result = await getQueue().enqueue(prepared.facts.eventId, prepared.payload, {
558
554
  receivedAt: prepared.receivedAt,
559
555
  laneKey: prepared.facts.laneKey
@@ -561,11 +557,15 @@ function createNostrIngress(options) {
561
557
  options.afterDurableAppend(prepared.event);
562
558
  monitor.requestDrain();
563
559
  return result.kind === "accepted" ? "accepted" : "duplicate";
564
- } catch (error) {
565
- lastError = error;
566
- }
560
+ }, {
561
+ attempts: NOSTR_INGRESS_APPEND_RETRY_MS.length,
562
+ minDelayMs: 0,
563
+ delayMs: ({ attempt }) => NOSTR_INGRESS_APPEND_RETRY_MS[attempt] ?? 0,
564
+ sleep: (delayMs) => sleepWithAbort(delayMs)
565
+ });
566
+ } catch (error) {
567
+ throw new Error(`Nostr durable admission failed: ${formatErrorMessage(error)}`, { cause: error });
567
568
  }
568
- throw new Error(`Nostr durable admission failed: ${formatErrorMessage(lastError)}`, { cause: lastError });
569
569
  };
570
570
  return {
571
571
  ready: async () => {
@@ -742,48 +742,6 @@ async function publishProfile(pool, sk, relays, profile, lastPublishedAt) {
742
742
  return publishProfileEvent(pool, relays, createProfileEvent(sk, profile, lastPublishedAt));
743
743
  }
744
744
  //#endregion
745
- //#region extensions/nostr/src/nostr-rate-limiter.ts
746
- function createFixedWindowRateLimiter(params) {
747
- const windowMs = Math.max(1, Math.floor(params.windowMs));
748
- const maxRequests = Math.max(1, Math.floor(params.maxRequests));
749
- const maxTrackedKeys = Math.max(1, Math.floor(params.maxTrackedKeys));
750
- const state = /* @__PURE__ */ new Map();
751
- const touch = (key, value) => {
752
- state.delete(key);
753
- state.set(key, value);
754
- };
755
- const prune = (nowMs) => {
756
- for (const [key, entry] of state) if (nowMs - entry.windowStartMs >= windowMs) state.delete(key);
757
- while (state.size > maxTrackedKeys) {
758
- const oldest = state.keys().next().value;
759
- if (!oldest) break;
760
- state.delete(oldest);
761
- }
762
- };
763
- return {
764
- isRateLimited: (key, nowMs = Date.now()) => {
765
- if (!key) return false;
766
- prune(nowMs);
767
- const existing = state.get(key);
768
- if (!existing || nowMs - existing.windowStartMs >= windowMs) {
769
- touch(key, {
770
- count: 1,
771
- windowStartMs: nowMs
772
- });
773
- return false;
774
- }
775
- const nextCount = existing.count + 1;
776
- touch(key, {
777
- count: nextCount,
778
- windowStartMs: existing.windowStartMs
779
- });
780
- return nextCount > maxRequests;
781
- },
782
- size: () => state.size,
783
- clear: () => state.clear()
784
- };
785
- }
786
- //#endregion
787
745
  //#region extensions/nostr/src/nostr-relay-subscription.ts
788
746
  const DEFAULT_EOSE_CONFIRM_DEADLINE_MS = 1e4;
789
747
  const LIBRARY_EOSE_TIMEOUT_MARGIN_MS = 1e3;
@@ -1357,7 +1315,6 @@ async function sendEncryptedDm(pool, sk, toPubkey, text, relays, metrics, circui
1357
1315
  //#endregion
1358
1316
  //#region extensions/nostr/src/gateway.ts
1359
1317
  const activeBuses = /* @__PURE__ */ new Map();
1360
- const metricsSnapshots = /* @__PURE__ */ new Map();
1361
1318
  const ACCESS_GROUP_PREFIX = "accessGroup:";
1362
1319
  function normalizeRelayLifecycleKey(relay) {
1363
1320
  return new URL(relay).toString();
@@ -1426,7 +1383,6 @@ const startNostrGatewayAccount = async (ctx) => {
1426
1383
  allowFrom: account.config.allowFrom,
1427
1384
  command: runtime.channel.commands.shouldComputeCommandAuthorized(rawBody, ctx.cfg) ? { modeWhenAccessGroupsOff: "configured" } : void 0
1428
1385
  });
1429
- let busHandle = null;
1430
1386
  const connectedRelays = /* @__PURE__ */ new Set();
1431
1387
  const authorizeSender = async (input) => {
1432
1388
  const resolved = await resolveInboundAccess(input.senderId, "");
@@ -1534,17 +1490,13 @@ const startNostrGatewayAccount = async (ctx) => {
1534
1490
  else if (event.name === "relay.circuit_breaker.open") ctx.log?.warn?.(`[${account.accountId}] Circuit breaker opened for relay: ${event.labels?.relay}`);
1535
1491
  else if (event.name === "relay.circuit_breaker.close") ctx.log?.info?.(`[${account.accountId}] Circuit breaker closed for relay: ${event.labels?.relay}`);
1536
1492
  else if (event.name === "relay.error") ctx.log?.debug?.(`[${account.accountId}] Relay error: ${event.labels?.relay}`);
1537
- if (busHandle) metricsSnapshots.set(account.accountId, busHandle.getMetrics());
1538
1493
  }
1539
1494
  });
1540
- busHandle = bus;
1541
1495
  activeBuses.set(account.accountId, bus);
1542
1496
  ctx.log?.info?.(`[${account.accountId}] Nostr provider started with ${account.relays.length} configured relay(s)`);
1543
1497
  return { stop: async () => {
1544
- await bus.close();
1545
- if (busHandle === bus) busHandle = null;
1546
1498
  if (activeBuses.get(account.accountId) === bus) activeBuses.delete(account.accountId);
1547
- metricsSnapshots.delete(account.accountId);
1499
+ await bus.close();
1548
1500
  ctx.log?.info?.(`[${account.accountId}] Nostr provider stopped`);
1549
1501
  } };
1550
1502
  },
@@ -1,2 +1,2 @@
1
- import { n as nostrPlugin } from "./channel-BifOvAwp.js";
1
+ import { n as nostrPlugin } from "./channel-BwnvE_wr.js";
2
2
  export { nostrPlugin };
package/dist/index.js CHANGED
@@ -27,6 +27,10 @@ var nostr_default = defineBundledChannelEntry({
27
27
  specifier: "./channel-plugin-api.js",
28
28
  exportName: "nostrPlugin"
29
29
  },
30
+ secrets: {
31
+ specifier: "./secret-contract-api.js",
32
+ exportName: "channelSecrets"
33
+ },
30
34
  runtime: {
31
35
  specifier: "./api.js",
32
36
  exportName: "setNostrRuntime"
@@ -0,0 +1,37 @@
1
+ import { normalizeAccountId } from "openclaw/plugin-sdk/account-id";
2
+ import { collectSecretInputAssignment, createChannelSecretTargetRegistryEntries, getChannelRecord } from "openclaw/plugin-sdk/channel-secret-basic-runtime";
3
+ //#region extensions/nostr/src/secret-contract.ts
4
+ const secretTargetRegistryEntries = createChannelSecretTargetRegistryEntries({
5
+ channelKey: "nostr",
6
+ channel: ["privateKey"]
7
+ });
8
+ function collectRuntimeConfigAssignments(params) {
9
+ const nostr = getChannelRecord(params.config, "nostr");
10
+ if (!nostr) return;
11
+ const accountId = normalizeAccountId(typeof nostr.defaultAccount === "string" ? nostr.defaultAccount : void 0);
12
+ collectSecretInputAssignment({
13
+ value: nostr.privateKey,
14
+ path: "channels.nostr.privateKey",
15
+ expected: "string",
16
+ defaults: params.defaults,
17
+ context: params.context,
18
+ active: nostr.enabled !== false,
19
+ inactiveReason: "Nostr channel is disabled.",
20
+ owner: {
21
+ ownerKind: "account",
22
+ ownerId: `nostr:${accountId}`,
23
+ requiredForGateway: false,
24
+ disposition: "isolate",
25
+ contract: nostr
26
+ },
27
+ apply: (value) => {
28
+ nostr.privateKey = value;
29
+ }
30
+ });
31
+ }
32
+ const channelSecrets = {
33
+ secretTargetRegistryEntries,
34
+ collectRuntimeConfigAssignments
35
+ };
36
+ //#endregion
37
+ export { channelSecrets, collectRuntimeConfigAssignments, secretTargetRegistryEntries };
@@ -1,5 +1,6 @@
1
1
  import { uniqueStrings } from "openclaw/plugin-sdk/string-coerce-runtime";
2
2
  import { hasConfiguredSecretInput, normalizeSecretInputString } from "openclaw/plugin-sdk/secret-input";
3
+ import { decode } from "nostr-tools/nip19";
3
4
  import { DEFAULT_ACCOUNT_ID } from "openclaw/plugin-sdk/routing";
4
5
  import { createSetupTranslator, createStandardChannelSetupStatus, patchTopLevelChannelConfigSection, splitSetupEntries } from "openclaw/plugin-sdk/setup";
5
6
  import { defineChannelSetupContract } from "openclaw/plugin-sdk/channel-setup";
@@ -8,6 +9,32 @@ const DEFAULT_RELAYS = ["wss://relay.damus.io", "wss://nos.lol"];
8
9
  //#endregion
9
10
  //#region extensions/nostr/src/private-key.ts
10
11
  const NOSTR_PRIVATE_KEY_ENV_VAR = "NOSTR_PRIVATE_KEY";
12
+ const SECP256K1_ORDER = BigInt("0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141");
13
+ /** Validate and normalize a private key (hex or NIP-19 nsec). */
14
+ function validatePrivateKey(key) {
15
+ const trimmed = key.trim();
16
+ let bytes;
17
+ if (trimmed.startsWith("nsec1") || trimmed.startsWith("NSEC1")) {
18
+ let decoded;
19
+ try {
20
+ decoded = decode(trimmed);
21
+ } catch {
22
+ throw new Error("Invalid nsec private key");
23
+ }
24
+ if (decoded.type !== "nsec") throw new Error("Invalid nsec key type");
25
+ bytes = decoded.data;
26
+ } else {
27
+ if (!/^[0-9a-fA-F]{64}$/.test(trimmed)) throw new Error("Private key must be 64 hex characters or nsec bech32 format");
28
+ bytes = Uint8Array.from({ length: 32 }, (_, index) => Number.parseInt(trimmed.slice(index * 2, index * 2 + 2), 16));
29
+ }
30
+ if (bytes.length !== 32) throw new Error("Private key must decode to 32 bytes");
31
+ const scalar = Array.from(bytes).reduce((value, byte) => value << 8n | BigInt(byte), 0n);
32
+ if (scalar === 0n || scalar >= SECP256K1_ORDER) throw new Error("Private key scalar is out of range");
33
+ return bytes;
34
+ }
35
+ function hasConfiguredNostrPrivateKey(value) {
36
+ return hasConfiguredSecretInput(value) || Boolean(process.env["NOSTR_PRIVATE_KEY"]?.trim());
37
+ }
11
38
  function resolveNostrPrivateKey(value) {
12
39
  const configured = normalizeSecretInputString(value);
13
40
  if (configured || hasConfiguredSecretInput(value)) return configured ?? "";
@@ -53,7 +80,11 @@ function createNostrSetupAdapter(params) {
53
80
  if (!input.useEnv) {
54
81
  const privateKey = input.privateKey?.trim();
55
82
  if (!privateKey) return "Nostr requires --private-key or --use-env.";
56
- if (!params.validatePrivateKey(privateKey)) return "Nostr private key must be valid nsec or 64-character hex.";
83
+ try {
84
+ validatePrivateKey(privateKey);
85
+ } catch {
86
+ return "Nostr private key must be valid nsec or 64-character hex.";
87
+ }
57
88
  }
58
89
  if (input.relayUrls?.trim()) return parseRelayUrls(input.relayUrls).error ?? null;
59
90
  return null;
@@ -124,4 +155,4 @@ function createNostrSetupStatus(resolveAccount) {
124
155
  });
125
156
  }
126
157
  //#endregion
127
- export { parseRelayUrls as a, createNostrSetupStatus as i, createNostrSetupAdapter as n, resolveNostrPrivateKey as o, createNostrSetupContract as r, DEFAULT_RELAYS as s, buildNostrSetupPatch as t };
158
+ export { parseRelayUrls as a, validatePrivateKey as c, createNostrSetupStatus as i, DEFAULT_RELAYS as l, createNostrSetupAdapter as n, hasConfiguredNostrPrivateKey as o, createNostrSetupContract as r, resolveNostrPrivateKey as s, buildNostrSetupPatch as t };
package/dist/setup-api.js CHANGED
@@ -1,2 +1,2 @@
1
- import { r as nostrSetupWizard, t as nostrSetupAdapter } from "./setup-surface-DAhyNOcb.js";
1
+ import { r as nostrSetupWizard, t as nostrSetupAdapter } from "./setup-surface-CzjfhV72.js";
2
2
  export { nostrSetupAdapter, nostrSetupWizard };
@@ -5,6 +5,10 @@ var setup_entry_default = defineBundledChannelSetupEntry({
5
5
  plugin: {
6
6
  specifier: "./setup-plugin-api.js",
7
7
  exportName: "nostrSetupPlugin"
8
+ },
9
+ secrets: {
10
+ specifier: "./secret-contract-api.js",
11
+ exportName: "channelSecrets"
8
12
  }
9
13
  });
10
14
  //#endregion
@@ -1,21 +1,21 @@
1
1
  import { i as buildChannelConfigSchema, t as NostrConfigSchema } from "./config-schema-CkYd8ACn.js";
2
- import { i as createNostrSetupStatus, n as createNostrSetupAdapter, o as resolveNostrPrivateKey, r as createNostrSetupContract, s as DEFAULT_RELAYS } from "./setup-adapter-DLthHa2q.js";
2
+ import { i as createNostrSetupStatus, l as DEFAULT_RELAYS, n as createNostrSetupAdapter, o as hasConfiguredNostrPrivateKey, r as createNostrSetupContract, s as resolveNostrPrivateKey } from "./setup-adapter-KJ_iGKiG.js";
3
3
  import { describeAccountSnapshot } from "openclaw/plugin-sdk/account-helpers";
4
- import { DEFAULT_ACCOUNT_ID, createDelegatedSetupWizardProxy } from "openclaw/plugin-sdk/setup-runtime";
4
+ import { normalizeAccountId } from "openclaw/plugin-sdk/account-id";
5
+ import { createDelegatedSetupWizardProxy } from "openclaw/plugin-sdk/setup-runtime";
5
6
  //#region extensions/nostr/src/channel.setup.ts
6
7
  const channel = "nostr";
7
8
  function getNostrConfig(cfg) {
8
9
  return cfg.channels?.nostr;
9
10
  }
10
11
  function resolveDefaultSetupNostrAccountId(cfg) {
11
- const configured = getNostrConfig(cfg)?.defaultAccount;
12
- return typeof configured === "string" && configured.trim() ? configured.trim() : DEFAULT_ACCOUNT_ID;
12
+ return normalizeAccountId(getNostrConfig(cfg)?.defaultAccount);
13
13
  }
14
14
  function resolveSetupNostrAccount(params) {
15
15
  const nostrCfg = getNostrConfig(params.cfg);
16
- const accountId = params.accountId?.trim() || resolveDefaultSetupNostrAccountId(params.cfg);
16
+ const accountId = normalizeAccountId(params.accountId ?? resolveDefaultSetupNostrAccountId(params.cfg));
17
17
  const privateKey = resolveNostrPrivateKey(nostrCfg?.privateKey);
18
- const configured = Boolean(privateKey);
18
+ const configured = hasConfiguredNostrPrivateKey(nostrCfg?.privateKey);
19
19
  return {
20
20
  accountId,
21
21
  name: typeof nostrCfg?.name === "string" ? nostrCfg.name : void 0,
@@ -38,7 +38,7 @@ function resolveSetupNostrAccount(params) {
38
38
  }
39
39
  const nostrSetupWizard = createDelegatedSetupWizardProxy({
40
40
  channel,
41
- loadWizard: async () => (await import("./setup-surface-DAhyNOcb.js").then((n) => n.i)).nostrSetupWizard,
41
+ loadWizard: async () => (await import("./setup-surface-CzjfhV72.js").then((n) => n.i)).nostrSetupWizard,
42
42
  status: createNostrSetupStatus(resolveSetupNostrAccount),
43
43
  resolveShouldPromptAccountIds: () => false,
44
44
  delegatePrepare: true,
@@ -61,10 +61,7 @@ const nostrSetupPlugin = {
61
61
  },
62
62
  reload: { configPrefixes: ["channels.nostr"] },
63
63
  configSchema: buildChannelConfigSchema(NostrConfigSchema),
64
- setupContract: createNostrSetupContract(createNostrSetupAdapter({
65
- resolveAccountId: (cfg, accountId) => accountId?.trim() || resolveDefaultSetupNostrAccountId(cfg),
66
- validatePrivateKey: (privateKey) => /^(?:nsec1|NSEC1)|^[0-9a-fA-F]{64}$/u.test(privateKey)
67
- })),
64
+ setupContract: createNostrSetupContract(createNostrSetupAdapter({ resolveAccountId: (cfg, accountId) => accountId?.trim() || resolveDefaultSetupNostrAccountId(cfg) })),
68
65
  setupWizard: nostrSetupWizard,
69
66
  config: {
70
67
  listAccountIds: (cfg) => resolveSetupNostrAccount({ cfg }).configured ? [resolveDefaultSetupNostrAccountId(cfg)] : [],
@@ -1,8 +1,9 @@
1
- import { a as parseRelayUrls, i as createNostrSetupStatus, n as createNostrSetupAdapter, o as resolveNostrPrivateKey, r as createNostrSetupContract, s as DEFAULT_RELAYS, t as buildNostrSetupPatch } from "./setup-adapter-DLthHa2q.js";
1
+ import { a as parseRelayUrls, c as validatePrivateKey, i as createNostrSetupStatus, l as DEFAULT_RELAYS, n as createNostrSetupAdapter, o as hasConfiguredNostrPrivateKey, r as createNostrSetupContract, s as resolveNostrPrivateKey, t as buildNostrSetupPatch } from "./setup-adapter-KJ_iGKiG.js";
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";
5
- import { getPublicKey, nip19 } from "nostr-tools";
5
+ import { decode } from "nostr-tools/nip19";
6
+ import { getPublicKey } from "nostr-tools/pure";
6
7
  import { DEFAULT_ACCOUNT_ID, normalizeAccountId, normalizeOptionalAccountId } from "openclaw/plugin-sdk/account-id";
7
8
  import { DEFAULT_ACCOUNT_ID as DEFAULT_ACCOUNT_ID$1 } from "openclaw/plugin-sdk/routing";
8
9
  import { createSetupTranslator, createTopLevelChannelDmPolicy, createTopLevelChannelParsedAllowFromPrompt, defineTokenCredential, formatDocsLink, mergeAllowFromEntries, parseSetupEntriesWithParser, patchTopLevelChannelConfigSection, setSetupChannelEnabled } from "openclaw/plugin-sdk/setup";
@@ -19,34 +20,13 @@ var __exportAll = (all, no_symbols) => {
19
20
  };
20
21
  //#endregion
21
22
  //#region extensions/nostr/src/nostr-key-utils.ts
22
- /**
23
- * Validate and normalize a private key (accepts hex or nsec format)
24
- */
25
- function validatePrivateKey(key) {
26
- const trimmed = key.trim();
27
- if (trimmed.startsWith("nsec1") || trimmed.startsWith("NSEC1")) {
28
- const decoded = nip19.decode(trimmed);
29
- if (decoded.type !== "nsec") throw new Error("Invalid nsec key: wrong type");
30
- return decoded.data;
31
- }
32
- if (!/^[0-9a-fA-F]{64}$/.test(trimmed)) throw new Error("Private key must be 64 hex characters or nsec bech32 format");
33
- const bytes = /* @__PURE__ */ new Uint8Array(32);
34
- for (let i = 0; i < 32; i++) bytes[i] = Number.parseInt(trimmed.slice(i * 2, i * 2 + 2), 16);
35
- return bytes;
36
- }
37
- /**
38
- * Get public key from private key (hex or nsec format)
39
- */
40
23
  function getPublicKeyFromPrivate(privateKey) {
41
24
  return getPublicKey(validatePrivateKey(privateKey));
42
25
  }
43
- /**
44
- * Normalize a pubkey to hex format (accepts npub or hex)
45
- */
46
26
  function normalizePubkey(input) {
47
27
  const trimmed = input.trim();
48
28
  if (trimmed.startsWith("npub1") || trimmed.startsWith("NPUB1")) {
49
- const decoded = nip19.decode(trimmed);
29
+ const decoded = decode(trimmed);
50
30
  if (decoded.type !== "npub" || typeof decoded.data !== "string") throw new Error("Invalid npub key");
51
31
  return decoded.data.toLowerCase();
52
32
  }
@@ -59,7 +39,7 @@ const { listAccountIds: listNostrAccountIds, resolveDefaultAccountId: resolveDef
59
39
  fallbackAccountIdWhenEmpty: false,
60
40
  resolveImplicitAccountId: (cfg) => {
61
41
  const account = cfg.channels?.nostr;
62
- return resolveNostrPrivateKey(account?.privateKey) ? normalizeOptionalAccountId(account?.defaultAccount) ?? DEFAULT_ACCOUNT_ID : void 0;
42
+ return hasConfiguredNostrPrivateKey(account?.privateKey) ? normalizeOptionalAccountId(account?.defaultAccount) ?? DEFAULT_ACCOUNT_ID : void 0;
63
43
  }
64
44
  });
65
45
  /**
@@ -70,7 +50,7 @@ function resolveNostrAccount(opts) {
70
50
  const nostrCfg = opts.cfg.channels?.nostr;
71
51
  const baseEnabled = nostrCfg?.enabled !== false;
72
52
  const privateKey = resolveNostrPrivateKey(nostrCfg?.privateKey);
73
- const configured = Boolean(privateKey);
53
+ const configured = hasConfiguredNostrPrivateKey(nostrCfg?.privateKey);
74
54
  let publicKey = "";
75
55
  if (privateKey) try {
76
56
  publicKey = getPublicKeyFromPrivate(privateKey);
@@ -146,17 +126,7 @@ const nostrDmPolicy = createTopLevelChannelDmPolicy({
146
126
  mergeEntries: ({ existing, parsed }) => mergeAllowFromEntries(existing, parsed)
147
127
  })
148
128
  });
149
- const nostrSetupAdapter = createNostrSetupAdapter({
150
- resolveAccountId: (cfg, accountId) => accountId?.trim() || resolveDefaultNostrAccountId(cfg),
151
- validatePrivateKey: (privateKey) => {
152
- try {
153
- getPublicKeyFromPrivate(privateKey);
154
- return true;
155
- } catch {
156
- return false;
157
- }
158
- }
159
- });
129
+ const nostrSetupAdapter = createNostrSetupAdapter({ resolveAccountId: (cfg, accountId) => accountId?.trim() || resolveDefaultNostrAccountId(cfg) });
160
130
  const nostrSetupContract = createNostrSetupContract(nostrSetupAdapter);
161
131
  const nostrSetupWizard = {
162
132
  channel,
@@ -244,4 +214,4 @@ const nostrSetupWizard = {
244
214
  disable: (cfg) => setSetupChannelEnabled(cfg, channel, false)
245
215
  };
246
216
  //#endregion
247
- export { listNostrAccountIds as a, normalizePubkey as c, setup_surface_exports as i, validatePrivateKey as l, nostrSetupContract as n, resolveDefaultNostrAccountId as o, nostrSetupWizard as r, resolveNostrAccount as s, nostrSetupAdapter as t };
217
+ export { listNostrAccountIds as a, normalizePubkey as c, setup_surface_exports as i, nostrSetupContract as n, resolveDefaultNostrAccountId as o, nostrSetupWizard as r, resolveNostrAccount as s, nostrSetupAdapter as t };
@@ -650,10 +650,12 @@ var Subscription = class {
650
650
  this.closed = true;
651
651
  }
652
652
  this.relay.openSubs.delete(this.id);
653
- this.relay.ongoingOperations--;
654
- if (this.relay.ongoingOperations === 0) {
655
- this.relay.idleSince = Date.now();
656
- this.relay.scheduleIdleClose();
653
+ if (!this.id.startsWith("<forced-ping>")) {
654
+ this.relay.ongoingOperations--;
655
+ if (this.relay.ongoingOperations === 0) {
656
+ this.relay.idleSince = Date.now();
657
+ this.relay.scheduleIdleClose();
658
+ }
657
659
  }
658
660
  this.onclose?.(reason);
659
661
  }
@@ -1004,7 +1006,7 @@ var AbstractSimplePool = class {
1004
1006
  });
1005
1007
  } catch (err) {
1006
1008
  this.onRelayConnectionFailure?.(url);
1007
- return String("connection failure: " + String(err));
1009
+ return Promise.reject("connection failure: " + String(err));
1008
1010
  }
1009
1011
  return r.publish(event).catch(async (err) => {
1010
1012
  if (err instanceof Error && err.message.startsWith("auth-required: ") && params?.onauth) {