@openclaw/nostr 2026.10.1-beta.2 → 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.
- package/dist/.setup/{channel-BaBU_Q15.mjs → channel-B_rRGVN_.mjs} +28 -175
- package/dist/.setup/{config-schema-HuEK1L2K.mjs → config-schema-CkZgmIVF.mjs} +1 -7
- package/dist/.setup/{setup-adapter-I55hhpDp.mjs → setup-adapter-B2ksCBWv.mjs} +9 -12
- package/dist/.setup/setup-plugin-api-CNjlfjWb.mjs +2 -0
- package/dist/.setup/{setup-plugin-api-C_HK3q8q.mjs → setup-plugin-api-CZ-PzJZP.mjs} +3 -3
- package/dist/.setup/{setup-surface-b-YWJSGV.mjs → setup-surface-BAq4Fs6P.mjs} +1 -1
- package/dist/api.js +17 -21
- package/dist/channel-plugin-api.js +1 -1
- package/dist/setup-api.js +1 -1
- package/dist/setup-entry.js +1 -1
- package/dist/setup-plugin-api.js +1 -1
- package/package.json +4 -4
- package/dist/.setup/setup-plugin-api-BUsavy-i.mjs +0 -2
|
@@ -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-
|
|
2
|
-
import { n as NostrProfileSchema, t as NostrConfigSchema } from "./config-schema-
|
|
3
|
-
import { l as validatePrivateKey, u as DEFAULT_RELAYS } from "./setup-adapter-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
},
|
|
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 =
|
|
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
|
|
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
|
-
|
|
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.
|
|
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.
|
|
1231
|
-
senderIdLine: `Your Nostr pubkey: ${input.
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
|
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
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
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) };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { t as NostrConfigSchema } from "./config-schema-
|
|
2
|
-
import { i as createNostrSetupStatus, n as createNostrSetupAdapter, o as getNostrConfig, r as createNostrSetupContract, s as resolveNostrAccountBase } from "./setup-adapter-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
4
|
-
import { a as getNostrRuntime, i as contentToProfile, n as nostrPlugin, o as setNostrRuntime, r as publishNostrProfile, t as getNostrProfileState } from "./.setup/channel-
|
|
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
|
|
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 =
|
|
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)
|
|
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-
|
|
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-
|
|
1
|
+
import { r as nostrSetupWizard, t as nostrSetupAdapter } from "./.setup/setup-surface-BAq4Fs6P.mjs";
|
|
2
2
|
export { nostrSetupAdapter, nostrSetupWizard };
|
package/dist/setup-entry.js
CHANGED
|
@@ -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-
|
|
7
|
+
specifier: fileURLToPath(new URL(".setup/setup-plugin-api-CNjlfjWb.mjs", import.meta.url).href),
|
|
8
8
|
exportName: "nostrSetupPlugin"
|
|
9
9
|
},
|
|
10
10
|
secrets: {
|
package/dist/setup-plugin-api.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { t as nostrSetupPlugin } from "./.setup/setup-plugin-api-
|
|
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.
|
|
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.
|
|
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.
|
|
83
|
+
"pluginApi": ">=2026.10.5-beta.1"
|
|
84
84
|
},
|
|
85
85
|
"build": {
|
|
86
|
-
"openclawVersion": "2026.10.
|
|
86
|
+
"openclawVersion": "2026.10.5-beta.1"
|
|
87
87
|
},
|
|
88
88
|
"release": {
|
|
89
89
|
"publishToClawHub": true,
|