@openclaw/nostr 2026.7.2-beta.7 → 2026.8.1-beta.3
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 +5 -5
- package/dist/{channel-O6-jXoT5.js → channel-ity6au4a.js} +60 -80
- package/dist/channel-plugin-api.js +1 -1
- package/dist/{config-schema-CelPzg1N.js → config-schema-CkYd8ACn.js} +1 -0
- package/dist/doctor-contract-api.js +5 -7
- package/dist/index.js +4 -0
- package/dist/secret-contract-api.js +37 -0
- package/dist/{setup-adapter-BjKmSwdW.js → setup-adapter-ClKW2fkz.js} +43 -12
- package/dist/setup-api.js +1 -1
- package/dist/setup-entry.js +4 -0
- package/dist/setup-plugin-api.js +15 -43
- package/dist/{setup-surface-BJCFpe02.js → setup-surface-uA4hN27A.js} +6 -19
- package/openclaw.plugin.json +29 -0
- package/package.json +8 -5
package/dist/api.js
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
import { s as resolveNostrAccount } from "./setup-surface-
|
|
1
|
+
import { s as resolveNostrAccount } from "./setup-surface-uA4hN27A.js";
|
|
2
2
|
import { getPluginRuntimeGatewayRequestScope } from "./runtime-api.js";
|
|
3
|
-
import { n as NostrProfileSchema } from "./config-schema-
|
|
4
|
-
import { a as getNostrRuntime, i as contentToProfile, n as nostrPlugin, o as setNostrRuntime, r as publishNostrProfile, t as getNostrProfileState } from "./channel-
|
|
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-ity6au4a.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
|
|
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-
|
|
3
|
-
import {
|
|
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-uA4hN27A.js";
|
|
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 { c as DEFAULT_RELAYS } from "./setup-adapter-ClKW2fkz.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";
|
|
@@ -9,14 +9,18 @@ import { missingTargetError } from "openclaw/plugin-sdk/channel-feedback";
|
|
|
9
9
|
import { DEFAULT_INGRESS_ADOPTION_STALL_MS, bindIngressLifecycleToReplyOptions, createChannelIngressError, createChannelIngressMonitor, createChannelMessageAdapterFromOutbound, runPassiveAccountLifecycle } from "openclaw/plugin-sdk/channel-outbound";
|
|
10
10
|
import { buildPassiveChannelStatusSummary, buildTrafficStatusSummary } from "openclaw/plugin-sdk/extension-shared";
|
|
11
11
|
import { createComputedAccountStatusAdapter } from "openclaw/plugin-sdk/status-helpers";
|
|
12
|
-
import { normalizeStringEntries } from "openclaw/plugin-sdk/string-coerce-runtime";
|
|
12
|
+
import { isRecord as isNostrIngressRecord, normalizeStringEntries } from "openclaw/plugin-sdk/string-coerce-runtime";
|
|
13
13
|
import { resolveStableChannelMessageIngress } from "openclaw/plugin-sdk/channel-ingress-runtime";
|
|
14
14
|
import { createChannelPairingController } from "openclaw/plugin-sdk/channel-pairing";
|
|
15
15
|
import { attachChannelToResult } from "openclaw/plugin-sdk/channel-send-result";
|
|
16
|
+
import { channelReadyPatch } from "openclaw/plugin-sdk/gateway-runtime";
|
|
16
17
|
import { chunkTextForOutbound, sanitizeAssistantVisibleText, stripMarkdown } from "openclaw/plugin-sdk/text-chunking";
|
|
17
18
|
import { SimplePool, finalizeEvent, getPublicKey, verifyEvent } from "nostr-tools";
|
|
18
19
|
import { decrypt, encrypt } from "nostr-tools/nip04";
|
|
19
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";
|
|
20
24
|
import { formatErrorMessage } from "openclaw/plugin-sdk/error-runtime";
|
|
21
25
|
import { createPluginRuntimeStore } from "openclaw/plugin-sdk/runtime-store";
|
|
22
26
|
import { buildChannelOutboundSessionRoute, stripChannelTargetPrefix } from "openclaw/plugin-sdk/core";
|
|
@@ -288,19 +292,16 @@ function createNostrCursorStateWriter(options) {
|
|
|
288
292
|
let activeFlush;
|
|
289
293
|
let recoveryFlush;
|
|
290
294
|
const writeWithRetry = async (cursor) => {
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
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)
|
|
295
301
|
});
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
return;
|
|
299
|
-
} catch (error) {
|
|
300
|
-
lastError = error;
|
|
301
|
-
}
|
|
302
|
+
} catch (error) {
|
|
303
|
+
throw new Error("Nostr cursor state write failed.", { cause: error });
|
|
302
304
|
}
|
|
303
|
-
throw new Error("Nostr cursor state write failed.", { cause: lastError });
|
|
304
305
|
};
|
|
305
306
|
const enqueueWrite = (cursor) => {
|
|
306
307
|
const write = writeTail.then(() => writeWithRetry(cursor));
|
|
@@ -382,9 +383,6 @@ var NostrIngressPermanentError = class extends Error {
|
|
|
382
383
|
this.name = "NostrIngressPermanentError";
|
|
383
384
|
}
|
|
384
385
|
};
|
|
385
|
-
function isNostrIngressRecord(value) {
|
|
386
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
387
|
-
}
|
|
388
386
|
function requiredString(value, field) {
|
|
389
387
|
if (typeof value === "string" && value.trim()) return value;
|
|
390
388
|
throw new NostrIngressPermanentError("invalid-event", `Nostr event is missing ${field}.`);
|
|
@@ -550,12 +548,8 @@ function createNostrIngress(options) {
|
|
|
550
548
|
const pending = await getQueue().listPending({ limit: options.maxPendingEvents });
|
|
551
549
|
const claims = await getQueue().listClaims();
|
|
552
550
|
if (pending.length + claims.length >= options.maxPendingEvents) throw new NostrIngressAdmissionRejectedError("backpressure", "Nostr event exceeds the durable ingress backlog.");
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
if (delayMs > 0) await new Promise((resolve) => {
|
|
556
|
-
setTimeout(resolve, delayMs);
|
|
557
|
-
});
|
|
558
|
-
try {
|
|
551
|
+
try {
|
|
552
|
+
return await retryAsync(async () => {
|
|
559
553
|
const result = await getQueue().enqueue(prepared.facts.eventId, prepared.payload, {
|
|
560
554
|
receivedAt: prepared.receivedAt,
|
|
561
555
|
laneKey: prepared.facts.laneKey
|
|
@@ -563,11 +557,15 @@ function createNostrIngress(options) {
|
|
|
563
557
|
options.afterDurableAppend(prepared.event);
|
|
564
558
|
monitor.requestDrain();
|
|
565
559
|
return result.kind === "accepted" ? "accepted" : "duplicate";
|
|
566
|
-
}
|
|
567
|
-
|
|
568
|
-
|
|
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 });
|
|
569
568
|
}
|
|
570
|
-
throw new Error(`Nostr durable admission failed: ${formatErrorMessage(lastError)}`, { cause: lastError });
|
|
571
569
|
};
|
|
572
570
|
return {
|
|
573
571
|
ready: async () => {
|
|
@@ -744,48 +742,6 @@ async function publishProfile(pool, sk, relays, profile, lastPublishedAt) {
|
|
|
744
742
|
return publishProfileEvent(pool, relays, createProfileEvent(sk, profile, lastPublishedAt));
|
|
745
743
|
}
|
|
746
744
|
//#endregion
|
|
747
|
-
//#region extensions/nostr/src/nostr-rate-limiter.ts
|
|
748
|
-
function createFixedWindowRateLimiter(params) {
|
|
749
|
-
const windowMs = Math.max(1, Math.floor(params.windowMs));
|
|
750
|
-
const maxRequests = Math.max(1, Math.floor(params.maxRequests));
|
|
751
|
-
const maxTrackedKeys = Math.max(1, Math.floor(params.maxTrackedKeys));
|
|
752
|
-
const state = /* @__PURE__ */ new Map();
|
|
753
|
-
const touch = (key, value) => {
|
|
754
|
-
state.delete(key);
|
|
755
|
-
state.set(key, value);
|
|
756
|
-
};
|
|
757
|
-
const prune = (nowMs) => {
|
|
758
|
-
for (const [key, entry] of state) if (nowMs - entry.windowStartMs >= windowMs) state.delete(key);
|
|
759
|
-
while (state.size > maxTrackedKeys) {
|
|
760
|
-
const oldest = state.keys().next().value;
|
|
761
|
-
if (!oldest) break;
|
|
762
|
-
state.delete(oldest);
|
|
763
|
-
}
|
|
764
|
-
};
|
|
765
|
-
return {
|
|
766
|
-
isRateLimited: (key, nowMs = Date.now()) => {
|
|
767
|
-
if (!key) return false;
|
|
768
|
-
prune(nowMs);
|
|
769
|
-
const existing = state.get(key);
|
|
770
|
-
if (!existing || nowMs - existing.windowStartMs >= windowMs) {
|
|
771
|
-
touch(key, {
|
|
772
|
-
count: 1,
|
|
773
|
-
windowStartMs: nowMs
|
|
774
|
-
});
|
|
775
|
-
return false;
|
|
776
|
-
}
|
|
777
|
-
const nextCount = existing.count + 1;
|
|
778
|
-
touch(key, {
|
|
779
|
-
count: nextCount,
|
|
780
|
-
windowStartMs: existing.windowStartMs
|
|
781
|
-
});
|
|
782
|
-
return nextCount > maxRequests;
|
|
783
|
-
},
|
|
784
|
-
size: () => state.size,
|
|
785
|
-
clear: () => state.clear()
|
|
786
|
-
};
|
|
787
|
-
}
|
|
788
|
-
//#endregion
|
|
789
745
|
//#region extensions/nostr/src/nostr-relay-subscription.ts
|
|
790
746
|
const DEFAULT_EOSE_CONFIRM_DEADLINE_MS = 1e4;
|
|
791
747
|
const LIBRARY_EOSE_TIMEOUT_MARGIN_MS = 1e3;
|
|
@@ -1359,8 +1315,10 @@ async function sendEncryptedDm(pool, sk, toPubkey, text, relays, metrics, circui
|
|
|
1359
1315
|
//#endregion
|
|
1360
1316
|
//#region extensions/nostr/src/gateway.ts
|
|
1361
1317
|
const activeBuses = /* @__PURE__ */ new Map();
|
|
1362
|
-
const metricsSnapshots = /* @__PURE__ */ new Map();
|
|
1363
1318
|
const ACCESS_GROUP_PREFIX = "accessGroup:";
|
|
1319
|
+
function normalizeRelayLifecycleKey(relay) {
|
|
1320
|
+
return new URL(relay).toString();
|
|
1321
|
+
}
|
|
1364
1322
|
function parseNostrAccessGroupAllowFromEntry(entry) {
|
|
1365
1323
|
const trimmed = entry.trim();
|
|
1366
1324
|
if (!trimmed.startsWith(ACCESS_GROUP_PREFIX)) return null;
|
|
@@ -1396,17 +1354,20 @@ const startNostrGatewayAccount = async (ctx) => {
|
|
|
1396
1354
|
const account = ctx.account;
|
|
1397
1355
|
ctx.setStatus({
|
|
1398
1356
|
accountId: account.accountId,
|
|
1399
|
-
publicKey: account.publicKey
|
|
1357
|
+
publicKey: account.publicKey,
|
|
1358
|
+
lifecycle: "starting"
|
|
1400
1359
|
});
|
|
1401
1360
|
ctx.log?.info?.(`[${account.accountId}] starting Nostr provider (pubkey: ${account.publicKey})`);
|
|
1402
1361
|
if (!account.configured) throw new Error("Nostr private key not configured");
|
|
1362
|
+
const channelRuntime = ctx.channelRuntime;
|
|
1363
|
+
if (!channelRuntime?.inbound?.buildContext) throw new Error("Nostr requires its registered channel runtime context builder");
|
|
1403
1364
|
const runtime = getNostrRuntime();
|
|
1404
1365
|
const pairing = createChannelPairingController({
|
|
1405
1366
|
core: runtime,
|
|
1406
1367
|
channel: "nostr",
|
|
1407
1368
|
accountId: account.accountId
|
|
1408
1369
|
});
|
|
1409
|
-
const resolveInboundAccess = async (senderPubkey, rawBody) => await resolveStableChannelMessageIngress({
|
|
1370
|
+
const resolveInboundAccess = async (senderPubkey, rawBody, contextBinding) => await resolveStableChannelMessageIngress({
|
|
1410
1371
|
channelId: "nostr",
|
|
1411
1372
|
accountId: account.accountId,
|
|
1412
1373
|
identity: nostrIngressIdentity,
|
|
@@ -1417,11 +1378,12 @@ const startNostrGatewayAccount = async (ctx) => {
|
|
|
1417
1378
|
kind: "direct",
|
|
1418
1379
|
id: senderPubkey
|
|
1419
1380
|
},
|
|
1381
|
+
contextBinding,
|
|
1420
1382
|
dmPolicy: account.config.dmPolicy ?? "pairing",
|
|
1421
1383
|
allowFrom: account.config.allowFrom,
|
|
1422
1384
|
command: runtime.channel.commands.shouldComputeCommandAuthorized(rawBody, ctx.cfg) ? { modeWhenAccessGroupsOff: "configured" } : void 0
|
|
1423
1385
|
});
|
|
1424
|
-
|
|
1386
|
+
const connectedRelays = /* @__PURE__ */ new Set();
|
|
1425
1387
|
const authorizeSender = async (input) => {
|
|
1426
1388
|
const resolved = await resolveInboundAccess(input.senderId, "");
|
|
1427
1389
|
if (resolved.senderAccess.decision === "allow") return "allow";
|
|
@@ -1461,6 +1423,12 @@ const startNostrGatewayAccount = async (ctx) => {
|
|
|
1461
1423
|
}
|
|
1462
1424
|
const { dispatchInboundDirectDm } = await import("./inbound-direct-dm-runtime-DHn9NoYN.js");
|
|
1463
1425
|
await dispatchInboundDirectDm({
|
|
1426
|
+
channelRuntime,
|
|
1427
|
+
resolveChannelIngress: async (contextBinding) => {
|
|
1428
|
+
const exactAccess = await resolveInboundAccess(senderPubkey, text, contextBinding);
|
|
1429
|
+
if (!exactAccess.senderAccess.allowed) throw new Error(`Nostr sender authorization changed before dispatch (${senderPubkey})`);
|
|
1430
|
+
return exactAccess;
|
|
1431
|
+
},
|
|
1464
1432
|
cfg: ctx.cfg,
|
|
1465
1433
|
channel: "nostr",
|
|
1466
1434
|
channelLabel: "Nostr",
|
|
@@ -1501,9 +1469,17 @@ const startNostrGatewayAccount = async (ctx) => {
|
|
|
1501
1469
|
ctx.log?.error?.(`[${account.accountId}] Nostr error (${context}): ${error.message}`);
|
|
1502
1470
|
},
|
|
1503
1471
|
onConnect: (relay) => {
|
|
1472
|
+
connectedRelays.add(normalizeRelayLifecycleKey(relay));
|
|
1473
|
+
ctx.setStatus(channelReadyPatch({ accountId: account.accountId }));
|
|
1504
1474
|
ctx.log?.debug?.(`[${account.accountId}] Connected to relay: ${relay}`);
|
|
1505
1475
|
},
|
|
1506
1476
|
onDisconnect: (relay) => {
|
|
1477
|
+
connectedRelays.delete(normalizeRelayLifecycleKey(relay));
|
|
1478
|
+
if (connectedRelays.size === 0) ctx.setStatus({
|
|
1479
|
+
accountId: account.accountId,
|
|
1480
|
+
connected: false,
|
|
1481
|
+
lifecycle: "recovering"
|
|
1482
|
+
});
|
|
1507
1483
|
ctx.log?.debug?.(`[${account.accountId}] Disconnected from relay: ${relay}`);
|
|
1508
1484
|
},
|
|
1509
1485
|
onEose: (relays) => {
|
|
@@ -1514,17 +1490,13 @@ const startNostrGatewayAccount = async (ctx) => {
|
|
|
1514
1490
|
else if (event.name === "relay.circuit_breaker.open") ctx.log?.warn?.(`[${account.accountId}] Circuit breaker opened for relay: ${event.labels?.relay}`);
|
|
1515
1491
|
else if (event.name === "relay.circuit_breaker.close") ctx.log?.info?.(`[${account.accountId}] Circuit breaker closed for relay: ${event.labels?.relay}`);
|
|
1516
1492
|
else if (event.name === "relay.error") ctx.log?.debug?.(`[${account.accountId}] Relay error: ${event.labels?.relay}`);
|
|
1517
|
-
if (busHandle) metricsSnapshots.set(account.accountId, busHandle.getMetrics());
|
|
1518
1493
|
}
|
|
1519
1494
|
});
|
|
1520
|
-
busHandle = bus;
|
|
1521
1495
|
activeBuses.set(account.accountId, bus);
|
|
1522
1496
|
ctx.log?.info?.(`[${account.accountId}] Nostr provider started with ${account.relays.length} configured relay(s)`);
|
|
1523
1497
|
return { stop: async () => {
|
|
1524
|
-
await bus.close();
|
|
1525
|
-
if (busHandle === bus) busHandle = null;
|
|
1526
1498
|
if (activeBuses.get(account.accountId) === bus) activeBuses.delete(account.accountId);
|
|
1527
|
-
|
|
1499
|
+
await bus.close();
|
|
1528
1500
|
ctx.log?.info?.(`[${account.accountId}] Nostr provider stopped`);
|
|
1529
1501
|
} };
|
|
1530
1502
|
},
|
|
@@ -1618,6 +1590,14 @@ function normalizeNostrTarget(target) {
|
|
|
1618
1590
|
return target.trim();
|
|
1619
1591
|
}
|
|
1620
1592
|
}
|
|
1593
|
+
function inferNostrTargetChatType(target) {
|
|
1594
|
+
try {
|
|
1595
|
+
normalizePubkey(stripNostrTargetPrefix(target));
|
|
1596
|
+
return "direct";
|
|
1597
|
+
} catch {
|
|
1598
|
+
return;
|
|
1599
|
+
}
|
|
1600
|
+
}
|
|
1621
1601
|
const resolveNostrDmPolicy = createScopedDmSecurityResolver({
|
|
1622
1602
|
channelKey: "nostr",
|
|
1623
1603
|
resolvePolicy: (account) => account.config.dmPolicy,
|
|
@@ -1692,7 +1672,6 @@ const nostrPlugin = createChatChannelPlugin({
|
|
|
1692
1672
|
},
|
|
1693
1673
|
reload: { configPrefixes: ["channels.nostr"] },
|
|
1694
1674
|
configSchema: buildChannelConfigSchema(NostrConfigSchema),
|
|
1695
|
-
setup: nostrSetupAdapter,
|
|
1696
1675
|
setupContract: nostrSetupContract,
|
|
1697
1676
|
setupWizard: nostrSetupWizard,
|
|
1698
1677
|
config: {
|
|
@@ -1707,6 +1686,7 @@ const nostrPlugin = createChatChannelPlugin({
|
|
|
1707
1686
|
messaging: {
|
|
1708
1687
|
targetPrefixes: ["nostr"],
|
|
1709
1688
|
normalizeTarget: normalizeNostrTarget,
|
|
1689
|
+
inferTargetChatType: ({ to }) => inferNostrTargetChatType(to),
|
|
1710
1690
|
targetResolver: {
|
|
1711
1691
|
looksLikeId: (input, normalized) => {
|
|
1712
1692
|
const trimmed = normalized?.trim() || stripNostrTargetPrefix(input);
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { n as nostrPlugin } from "./channel-
|
|
1
|
+
import { n as nostrPlugin } from "./channel-ity6au4a.js";
|
|
2
2
|
export { nostrPlugin };
|
|
@@ -46,6 +46,7 @@ const NostrConfigSchema = z.object({
|
|
|
46
46
|
defaultAccount: z.string().optional(),
|
|
47
47
|
/** Whether this channel is enabled */
|
|
48
48
|
enabled: z.boolean().optional(),
|
|
49
|
+
configWrites: z.boolean().optional(),
|
|
49
50
|
/** Markdown formatting overrides (tables). */
|
|
50
51
|
markdown: MarkdownConfigSchema,
|
|
51
52
|
/** Private key in hex or nsec bech32 format */
|
|
@@ -1,22 +1,20 @@
|
|
|
1
1
|
import { t as normalizeNostrStateAccountId } from "./state-account-id-CvBZ9s6P.js";
|
|
2
|
+
import { asFiniteNumber } from "openclaw/plugin-sdk/string-coerce-runtime";
|
|
2
3
|
import fs from "node:fs/promises";
|
|
3
4
|
import path from "node:path";
|
|
4
|
-
import { archiveLegacyStateSource } from "openclaw/plugin-sdk/runtime-doctor";
|
|
5
|
+
import { archiveLegacyStateSource } from "openclaw/plugin-sdk/runtime-doctor-migrations";
|
|
5
6
|
//#region extensions/nostr/doctor-contract-api.ts
|
|
6
7
|
const BUS_STATE_NAMESPACE = "bus-state";
|
|
7
8
|
const PROFILE_STATE_NAMESPACE = "profile-state";
|
|
8
9
|
const MAX_NOSTR_STATE_ENTRIES = 256;
|
|
9
|
-
function finiteNumberOrNull(value) {
|
|
10
|
-
return typeof value === "number" && Number.isFinite(value) ? value : null;
|
|
11
|
-
}
|
|
12
10
|
function parseBusState(value) {
|
|
13
11
|
if (!value || typeof value !== "object" || Array.isArray(value)) return null;
|
|
14
12
|
const parsed = value;
|
|
15
13
|
if (parsed.version !== 1 && parsed.version !== 2) return null;
|
|
16
14
|
return {
|
|
17
15
|
version: 2,
|
|
18
|
-
lastProcessedAt:
|
|
19
|
-
gatewayStartedAt:
|
|
16
|
+
lastProcessedAt: asFiniteNumber(parsed.lastProcessedAt) ?? null,
|
|
17
|
+
gatewayStartedAt: asFiniteNumber(parsed.gatewayStartedAt) ?? null,
|
|
20
18
|
recentEventIds: parsed.version === 2 && Array.isArray(parsed.recentEventIds) ? parsed.recentEventIds.filter((entry) => typeof entry === "string") : []
|
|
21
19
|
};
|
|
22
20
|
}
|
|
@@ -31,7 +29,7 @@ function parseProfileState(value) {
|
|
|
31
29
|
}
|
|
32
30
|
return {
|
|
33
31
|
version: 1,
|
|
34
|
-
lastPublishedAt:
|
|
32
|
+
lastPublishedAt: asFiniteNumber(parsed.lastPublishedAt) ?? null,
|
|
35
33
|
lastPublishedEventId: typeof parsed.lastPublishedEventId === "string" ? parsed.lastPublishedEventId : null,
|
|
36
34
|
lastPublishResults: rawResults === null || Object.keys(lastPublishResults).length === 0 ? null : lastPublishResults
|
|
37
35
|
};
|
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,10 +1,22 @@
|
|
|
1
1
|
import { uniqueStrings } from "openclaw/plugin-sdk/string-coerce-runtime";
|
|
2
|
+
import { hasConfiguredSecretInput, normalizeSecretInputString } from "openclaw/plugin-sdk/secret-input";
|
|
2
3
|
import { DEFAULT_ACCOUNT_ID } from "openclaw/plugin-sdk/routing";
|
|
3
|
-
import { patchTopLevelChannelConfigSection, splitSetupEntries } from "openclaw/plugin-sdk/setup";
|
|
4
|
+
import { createSetupTranslator, createStandardChannelSetupStatus, patchTopLevelChannelConfigSection, splitSetupEntries } from "openclaw/plugin-sdk/setup";
|
|
4
5
|
import { defineChannelSetupContract } from "openclaw/plugin-sdk/channel-setup";
|
|
5
6
|
//#region extensions/nostr/src/default-relays.ts
|
|
6
7
|
const DEFAULT_RELAYS = ["wss://relay.damus.io", "wss://nos.lol"];
|
|
7
8
|
//#endregion
|
|
9
|
+
//#region extensions/nostr/src/private-key.ts
|
|
10
|
+
const NOSTR_PRIVATE_KEY_ENV_VAR = "NOSTR_PRIVATE_KEY";
|
|
11
|
+
function hasConfiguredNostrPrivateKey(value) {
|
|
12
|
+
return hasConfiguredSecretInput(value) || Boolean(process.env["NOSTR_PRIVATE_KEY"]?.trim());
|
|
13
|
+
}
|
|
14
|
+
function resolveNostrPrivateKey(value) {
|
|
15
|
+
const configured = normalizeSecretInputString(value);
|
|
16
|
+
if (configured || hasConfiguredSecretInput(value)) return configured ?? "";
|
|
17
|
+
return process.env["NOSTR_PRIVATE_KEY"]?.trim() ?? "";
|
|
18
|
+
}
|
|
19
|
+
//#endregion
|
|
8
20
|
//#region extensions/nostr/src/setup-adapter.ts
|
|
9
21
|
const channel = "nostr";
|
|
10
22
|
function buildNostrSetupPatch(accountId, patch) {
|
|
@@ -41,25 +53,23 @@ function createNostrSetupAdapter(params) {
|
|
|
41
53
|
patch: buildNostrSetupPatch(accountId, name?.trim() ? { name: name.trim() } : {})
|
|
42
54
|
}),
|
|
43
55
|
validateInput: ({ input }) => {
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
const privateKey = typedInput.privateKey?.trim();
|
|
56
|
+
if (!input.useEnv) {
|
|
57
|
+
const privateKey = input.privateKey?.trim();
|
|
47
58
|
if (!privateKey) return "Nostr requires --private-key or --use-env.";
|
|
48
59
|
if (!params.validatePrivateKey(privateKey)) return "Nostr private key must be valid nsec or 64-character hex.";
|
|
49
60
|
}
|
|
50
|
-
if (
|
|
61
|
+
if (input.relayUrls?.trim()) return parseRelayUrls(input.relayUrls).error ?? null;
|
|
51
62
|
return null;
|
|
52
63
|
},
|
|
53
64
|
applyAccountConfig: ({ cfg, accountId, input }) => {
|
|
54
|
-
const
|
|
55
|
-
const relayResult = typedInput.relayUrls?.trim() ? parseRelayUrls(typedInput.relayUrls) : { relays: [] };
|
|
65
|
+
const relayResult = input.relayUrls?.trim() ? parseRelayUrls(input.relayUrls) : { relays: [] };
|
|
56
66
|
return patchTopLevelChannelConfigSection({
|
|
57
67
|
cfg,
|
|
58
68
|
channel,
|
|
59
69
|
enabled: true,
|
|
60
|
-
clearFields:
|
|
70
|
+
clearFields: input.useEnv ? ["privateKey"] : void 0,
|
|
61
71
|
patch: buildNostrSetupPatch(accountId, {
|
|
62
|
-
...
|
|
72
|
+
...input.useEnv ? {} : { privateKey: input.privateKey?.trim() },
|
|
63
73
|
...relayResult.relays.length > 0 ? { relays: relayResult.relays } : {}
|
|
64
74
|
})
|
|
65
75
|
});
|
|
@@ -89,11 +99,32 @@ function createNostrSetupContract(adapter) {
|
|
|
89
99
|
cli: {
|
|
90
100
|
flags: "--use-env",
|
|
91
101
|
description: "Use NOSTR_PRIVATE_KEY"
|
|
92
|
-
}
|
|
102
|
+
},
|
|
103
|
+
envVars: [NOSTR_PRIVATE_KEY_ENV_VAR]
|
|
93
104
|
}
|
|
94
105
|
},
|
|
95
|
-
|
|
106
|
+
adapter
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
function createNostrSetupStatus(resolveAccount) {
|
|
110
|
+
const t = createSetupTranslator();
|
|
111
|
+
return createStandardChannelSetupStatus({
|
|
112
|
+
channelLabel: "Nostr",
|
|
113
|
+
configuredLabel: t("wizard.channels.statusConfigured"),
|
|
114
|
+
unconfiguredLabel: t("wizard.channels.statusNeedsPrivateKey"),
|
|
115
|
+
configuredHint: t("wizard.channels.statusConfigured"),
|
|
116
|
+
unconfiguredHint: t("wizard.channels.statusNeedsPrivateKey"),
|
|
117
|
+
configuredScore: 1,
|
|
118
|
+
unconfiguredScore: 0,
|
|
119
|
+
includeStatusLine: true,
|
|
120
|
+
resolveConfigured: ({ cfg, accountId }) => resolveAccount({
|
|
121
|
+
cfg,
|
|
122
|
+
accountId
|
|
123
|
+
}).configured,
|
|
124
|
+
resolveExtraStatusLines: ({ cfg }) => {
|
|
125
|
+
return [`Relays: ${resolveAccount({ cfg }).relays.length || DEFAULT_RELAYS.length}`];
|
|
126
|
+
}
|
|
96
127
|
});
|
|
97
128
|
}
|
|
98
129
|
//#endregion
|
|
99
|
-
export {
|
|
130
|
+
export { parseRelayUrls as a, DEFAULT_RELAYS as c, createNostrSetupStatus as i, 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-
|
|
1
|
+
import { r as nostrSetupWizard, t as nostrSetupAdapter } from "./setup-surface-uA4hN27A.js";
|
|
2
2
|
export { nostrSetupAdapter, nostrSetupWizard };
|
package/dist/setup-entry.js
CHANGED
package/dist/setup-plugin-api.js
CHANGED
|
@@ -1,27 +1,21 @@
|
|
|
1
|
-
import { i as buildChannelConfigSchema, t as NostrConfigSchema } from "./config-schema-
|
|
2
|
-
import {
|
|
1
|
+
import { i as buildChannelConfigSchema, t as NostrConfigSchema } from "./config-schema-CkYd8ACn.js";
|
|
2
|
+
import { c as DEFAULT_RELAYS, i as createNostrSetupStatus, n as createNostrSetupAdapter, o as hasConfiguredNostrPrivateKey, r as createNostrSetupContract, s as resolveNostrPrivateKey } from "./setup-adapter-ClKW2fkz.js";
|
|
3
3
|
import { describeAccountSnapshot } from "openclaw/plugin-sdk/account-helpers";
|
|
4
|
-
import {
|
|
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
|
-
const t = createSetupTranslator();
|
|
7
7
|
const channel = "nostr";
|
|
8
8
|
function getNostrConfig(cfg) {
|
|
9
9
|
return cfg.channels?.nostr;
|
|
10
10
|
}
|
|
11
|
-
function listSetupNostrAccountIds(cfg) {
|
|
12
|
-
const nostrCfg = getNostrConfig(cfg);
|
|
13
|
-
if (!(typeof nostrCfg?.privateKey === "string" ? nostrCfg.privateKey.trim() : "")) return [];
|
|
14
|
-
return [resolveDefaultSetupNostrAccountId(cfg)];
|
|
15
|
-
}
|
|
16
11
|
function resolveDefaultSetupNostrAccountId(cfg) {
|
|
17
|
-
|
|
18
|
-
return typeof configured === "string" && configured.trim() ? configured.trim() : DEFAULT_ACCOUNT_ID;
|
|
12
|
+
return normalizeAccountId(getNostrConfig(cfg)?.defaultAccount);
|
|
19
13
|
}
|
|
20
14
|
function resolveSetupNostrAccount(params) {
|
|
21
15
|
const nostrCfg = getNostrConfig(params.cfg);
|
|
22
|
-
const accountId = params.accountId
|
|
23
|
-
const privateKey =
|
|
24
|
-
const configured =
|
|
16
|
+
const accountId = normalizeAccountId(params.accountId ?? resolveDefaultSetupNostrAccountId(params.cfg));
|
|
17
|
+
const privateKey = resolveNostrPrivateKey(nostrCfg?.privateKey);
|
|
18
|
+
const configured = hasConfiguredNostrPrivateKey(nostrCfg?.privateKey);
|
|
25
19
|
return {
|
|
26
20
|
accountId,
|
|
27
21
|
name: typeof nostrCfg?.name === "string" ? nostrCfg.name : void 0,
|
|
@@ -42,34 +36,10 @@ function resolveSetupNostrAccount(params) {
|
|
|
42
36
|
}
|
|
43
37
|
};
|
|
44
38
|
}
|
|
45
|
-
function looksLikeNostrPrivateKey(privateKey) {
|
|
46
|
-
return privateKey.startsWith("nsec1") || privateKey.startsWith("NSEC1") || /^[0-9a-fA-F]{64}$/.test(privateKey);
|
|
47
|
-
}
|
|
48
|
-
const nostrSetupAdapter = createNostrSetupAdapter({
|
|
49
|
-
resolveAccountId: (cfg, accountId) => accountId?.trim() || resolveDefaultSetupNostrAccountId(cfg),
|
|
50
|
-
validatePrivateKey: looksLikeNostrPrivateKey
|
|
51
|
-
});
|
|
52
|
-
const nostrSetupContract = createNostrSetupContract(nostrSetupAdapter);
|
|
53
39
|
const nostrSetupWizard = createDelegatedSetupWizardProxy({
|
|
54
40
|
channel,
|
|
55
|
-
loadWizard: async () => (await import("./setup-surface-
|
|
56
|
-
status:
|
|
57
|
-
channelLabel: "Nostr",
|
|
58
|
-
configuredLabel: t("wizard.channels.statusConfigured"),
|
|
59
|
-
unconfiguredLabel: t("wizard.channels.statusNeedsPrivateKey"),
|
|
60
|
-
configuredHint: t("wizard.channels.statusConfigured"),
|
|
61
|
-
unconfiguredHint: t("wizard.channels.statusNeedsPrivateKey"),
|
|
62
|
-
configuredScore: 1,
|
|
63
|
-
unconfiguredScore: 0,
|
|
64
|
-
includeStatusLine: true,
|
|
65
|
-
resolveConfigured: ({ cfg, accountId }) => resolveSetupNostrAccount({
|
|
66
|
-
cfg,
|
|
67
|
-
accountId
|
|
68
|
-
}).configured,
|
|
69
|
-
resolveExtraStatusLines: ({ cfg }) => {
|
|
70
|
-
return [`Relays: ${resolveSetupNostrAccount({ cfg }).relays.length || DEFAULT_RELAYS.length}`];
|
|
71
|
-
}
|
|
72
|
-
}) },
|
|
41
|
+
loadWizard: async () => (await import("./setup-surface-uA4hN27A.js").then((n) => n.i)).nostrSetupWizard,
|
|
42
|
+
status: createNostrSetupStatus(resolveSetupNostrAccount),
|
|
73
43
|
resolveShouldPromptAccountIds: () => false,
|
|
74
44
|
delegatePrepare: true,
|
|
75
45
|
delegateFinalize: true
|
|
@@ -91,11 +61,13 @@ const nostrSetupPlugin = {
|
|
|
91
61
|
},
|
|
92
62
|
reload: { configPrefixes: ["channels.nostr"] },
|
|
93
63
|
configSchema: buildChannelConfigSchema(NostrConfigSchema),
|
|
94
|
-
|
|
95
|
-
|
|
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
|
+
})),
|
|
96
68
|
setupWizard: nostrSetupWizard,
|
|
97
69
|
config: {
|
|
98
|
-
listAccountIds:
|
|
70
|
+
listAccountIds: (cfg) => resolveSetupNostrAccount({ cfg }).configured ? [resolveDefaultSetupNostrAccountId(cfg)] : [],
|
|
99
71
|
resolveAccount: (cfg, accountId) => resolveSetupNostrAccount({
|
|
100
72
|
cfg,
|
|
101
73
|
accountId
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import { a as DEFAULT_RELAYS, i as
|
|
1
|
+
import { a as parseRelayUrls, c as DEFAULT_RELAYS, i as createNostrSetupStatus, n as createNostrSetupAdapter, o as hasConfiguredNostrPrivateKey, r as createNostrSetupContract, s as resolveNostrPrivateKey, t as buildNostrSetupPatch } from "./setup-adapter-ClKW2fkz.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
5
|
import { getPublicKey, nip19 } from "nostr-tools";
|
|
6
6
|
import { DEFAULT_ACCOUNT_ID, normalizeAccountId, normalizeOptionalAccountId } from "openclaw/plugin-sdk/account-id";
|
|
7
7
|
import { DEFAULT_ACCOUNT_ID as DEFAULT_ACCOUNT_ID$1 } from "openclaw/plugin-sdk/routing";
|
|
8
|
-
import { createSetupTranslator,
|
|
8
|
+
import { createSetupTranslator, createTopLevelChannelDmPolicy, createTopLevelChannelParsedAllowFromPrompt, defineTokenCredential, formatDocsLink, mergeAllowFromEntries, parseSetupEntriesWithParser, patchTopLevelChannelConfigSection, setSetupChannelEnabled } from "openclaw/plugin-sdk/setup";
|
|
9
9
|
//#region \0rolldown/runtime.js
|
|
10
10
|
var __defProp = Object.defineProperty;
|
|
11
11
|
var __exportAll = (all, no_symbols) => {
|
|
@@ -59,7 +59,7 @@ const { listAccountIds: listNostrAccountIds, resolveDefaultAccountId: resolveDef
|
|
|
59
59
|
fallbackAccountIdWhenEmpty: false,
|
|
60
60
|
resolveImplicitAccountId: (cfg) => {
|
|
61
61
|
const account = cfg.channels?.nostr;
|
|
62
|
-
return
|
|
62
|
+
return hasConfiguredNostrPrivateKey(account?.privateKey) ? normalizeOptionalAccountId(account?.defaultAccount) ?? DEFAULT_ACCOUNT_ID : void 0;
|
|
63
63
|
}
|
|
64
64
|
});
|
|
65
65
|
/**
|
|
@@ -69,8 +69,8 @@ function resolveNostrAccount(opts) {
|
|
|
69
69
|
const accountId = normalizeAccountId(opts.accountId ?? resolveDefaultNostrAccountId(opts.cfg));
|
|
70
70
|
const nostrCfg = opts.cfg.channels?.nostr;
|
|
71
71
|
const baseEnabled = nostrCfg?.enabled !== false;
|
|
72
|
-
const privateKey =
|
|
73
|
-
const configured =
|
|
72
|
+
const privateKey = resolveNostrPrivateKey(nostrCfg?.privateKey);
|
|
73
|
+
const configured = hasConfiguredNostrPrivateKey(nostrCfg?.privateKey);
|
|
74
74
|
let publicKey = "";
|
|
75
75
|
if (privateKey) try {
|
|
76
76
|
publicKey = getPublicKeyFromPrivate(privateKey);
|
|
@@ -162,20 +162,7 @@ const nostrSetupWizard = {
|
|
|
162
162
|
channel,
|
|
163
163
|
resolveAccountIdForConfigure: ({ accountOverride, defaultAccountId }) => accountOverride?.trim() || defaultAccountId,
|
|
164
164
|
resolveShouldPromptAccountIds: () => false,
|
|
165
|
-
status:
|
|
166
|
-
channelLabel: "Nostr",
|
|
167
|
-
configuredLabel: t("wizard.channels.statusConfigured"),
|
|
168
|
-
unconfiguredLabel: t("wizard.channels.statusNeedsPrivateKey"),
|
|
169
|
-
configuredHint: t("wizard.channels.statusConfigured"),
|
|
170
|
-
unconfiguredHint: t("wizard.channels.statusNeedsPrivateKey"),
|
|
171
|
-
configuredScore: 1,
|
|
172
|
-
unconfiguredScore: 0,
|
|
173
|
-
includeStatusLine: true,
|
|
174
|
-
resolveConfigured: ({ cfg }) => resolveNostrAccount({ cfg }).configured,
|
|
175
|
-
resolveExtraStatusLines: ({ cfg }) => {
|
|
176
|
-
return [`Relays: ${resolveNostrAccount({ cfg }).relays.length || DEFAULT_RELAYS.length}`];
|
|
177
|
-
}
|
|
178
|
-
}),
|
|
165
|
+
status: createNostrSetupStatus(resolveNostrAccount),
|
|
179
166
|
introNote: {
|
|
180
167
|
title: t("wizard.nostr.setupTitle"),
|
|
181
168
|
lines: NOSTR_SETUP_HELP_LINES
|
package/openclaw.plugin.json
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"id": "nostr",
|
|
3
|
+
"doctorContract": {
|
|
4
|
+
"stateMigrations": true
|
|
5
|
+
},
|
|
3
6
|
"name": "Nostr",
|
|
4
7
|
"description": "OpenClaw Nostr channel plugin for NIP-04 encrypted direct messages.",
|
|
5
8
|
"activation": {
|
|
@@ -28,6 +31,9 @@
|
|
|
28
31
|
"enabled": {
|
|
29
32
|
"type": "boolean"
|
|
30
33
|
},
|
|
34
|
+
"configWrites": {
|
|
35
|
+
"type": "boolean"
|
|
36
|
+
},
|
|
31
37
|
"markdown": {
|
|
32
38
|
"type": "object",
|
|
33
39
|
"properties": {
|
|
@@ -73,6 +79,29 @@
|
|
|
73
79
|
],
|
|
74
80
|
"additionalProperties": false
|
|
75
81
|
},
|
|
82
|
+
{
|
|
83
|
+
"type": "object",
|
|
84
|
+
"properties": {
|
|
85
|
+
"source": {
|
|
86
|
+
"type": "string",
|
|
87
|
+
"const": "store"
|
|
88
|
+
},
|
|
89
|
+
"provider": {
|
|
90
|
+
"type": "string",
|
|
91
|
+
"pattern": "^[a-z][a-z0-9_-]{0,63}$"
|
|
92
|
+
},
|
|
93
|
+
"id": {
|
|
94
|
+
"type": "string",
|
|
95
|
+
"pattern": "^[A-Z][A-Z0-9_]{0,127}$"
|
|
96
|
+
}
|
|
97
|
+
},
|
|
98
|
+
"required": [
|
|
99
|
+
"source",
|
|
100
|
+
"provider",
|
|
101
|
+
"id"
|
|
102
|
+
],
|
|
103
|
+
"additionalProperties": false
|
|
104
|
+
},
|
|
76
105
|
{
|
|
77
106
|
"type": "object",
|
|
78
107
|
"properties": {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openclaw/nostr",
|
|
3
|
-
"version": "2026.
|
|
3
|
+
"version": "2026.8.1-beta.3",
|
|
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.
|
|
15
|
+
"openclaw": ">=2026.8.1-beta.3"
|
|
16
16
|
},
|
|
17
17
|
"peerDependenciesMeta": {
|
|
18
18
|
"openclaw": {
|
|
@@ -65,7 +65,10 @@
|
|
|
65
65
|
"cli": {
|
|
66
66
|
"flags": "--use-env",
|
|
67
67
|
"description": "Use NOSTR_PRIVATE_KEY"
|
|
68
|
-
}
|
|
68
|
+
},
|
|
69
|
+
"envVars": [
|
|
70
|
+
"NOSTR_PRIVATE_KEY"
|
|
71
|
+
]
|
|
69
72
|
}
|
|
70
73
|
]
|
|
71
74
|
}
|
|
@@ -76,10 +79,10 @@
|
|
|
76
79
|
"minHostVersion": ">=2026.4.10"
|
|
77
80
|
},
|
|
78
81
|
"compat": {
|
|
79
|
-
"pluginApi": ">=2026.
|
|
82
|
+
"pluginApi": ">=2026.8.1-beta.3"
|
|
80
83
|
},
|
|
81
84
|
"build": {
|
|
82
|
-
"openclawVersion": "2026.
|
|
85
|
+
"openclawVersion": "2026.8.1-beta.3"
|
|
83
86
|
},
|
|
84
87
|
"release": {
|
|
85
88
|
"publishToClawHub": true,
|