@openclaw/nostr 2026.3.11 → 2026.3.13

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/CHANGELOG.md CHANGED
@@ -1,8 +1,21 @@
1
1
  # Changelog
2
2
 
3
+ ## 2026.3.13
4
+
5
+ ### Changes
6
+
7
+ - Version alignment with core OpenClaw release numbers.
8
+
9
+ ## 2026.3.12
10
+
11
+ ### Changes
12
+
13
+ - Version alignment with core OpenClaw release numbers.
14
+
3
15
  ## 2026.3.11
4
16
 
5
17
  ### Changes
18
+
6
19
  - Version alignment with core OpenClaw release numbers.
7
20
 
8
21
  ## 2026.3.10
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openclaw/nostr",
3
- "version": "2026.3.11",
3
+ "version": "2026.3.13",
4
4
  "description": "OpenClaw Nostr channel plugin for NIP-04 encrypted DMs",
5
5
  "type": "module",
6
6
  "dependencies": {
package/src/channel.ts CHANGED
@@ -7,6 +7,10 @@ import {
7
7
  mapAllowFromEntries,
8
8
  type ChannelPlugin,
9
9
  } from "openclaw/plugin-sdk/nostr";
10
+ import {
11
+ buildPassiveChannelStatusSummary,
12
+ buildTrafficStatusSummary,
13
+ } from "../../shared/channel-status-summary.js";
10
14
  import type { NostrProfile } from "./config-schema.js";
11
15
  import { NostrConfigSchema } from "./config-schema.js";
12
16
  import type { MetricEvent, MetricsSnapshot } from "./metrics.js";
@@ -160,14 +164,10 @@ export const nostrPlugin: ChannelPlugin<ResolvedNostrAccount> = {
160
164
  status: {
161
165
  defaultRuntime: createDefaultChannelRuntimeState(DEFAULT_ACCOUNT_ID),
162
166
  collectStatusIssues: (accounts) => collectStatusIssuesFromLastError("nostr", accounts),
163
- buildChannelSummary: ({ snapshot }) => ({
164
- configured: snapshot.configured ?? false,
165
- publicKey: snapshot.publicKey ?? null,
166
- running: snapshot.running ?? false,
167
- lastStartAt: snapshot.lastStartAt ?? null,
168
- lastStopAt: snapshot.lastStopAt ?? null,
169
- lastError: snapshot.lastError ?? null,
170
- }),
167
+ buildChannelSummary: ({ snapshot }) =>
168
+ buildPassiveChannelStatusSummary(snapshot, {
169
+ publicKey: snapshot.publicKey ?? null,
170
+ }),
171
171
  buildAccountSnapshot: ({ account, runtime }) => ({
172
172
  accountId: account.accountId,
173
173
  name: account.name,
@@ -179,8 +179,7 @@ export const nostrPlugin: ChannelPlugin<ResolvedNostrAccount> = {
179
179
  lastStartAt: runtime?.lastStartAt ?? null,
180
180
  lastStopAt: runtime?.lastStopAt ?? null,
181
181
  lastError: runtime?.lastError ?? null,
182
- lastInboundAt: runtime?.lastInboundAt ?? null,
183
- lastOutboundAt: runtime?.lastOutboundAt ?? null,
182
+ ...buildTrafficStatusSummary(runtime),
184
183
  }),
185
184
  },
186
185
 
@@ -115,6 +115,13 @@ function createMockContext(overrides?: Partial<NostrProfileHttpContext>): NostrP
115
115
  };
116
116
  }
117
117
 
118
+ function expectOkResponse(res: ReturnType<typeof createMockResponse>) {
119
+ expect(res._getStatusCode()).toBe(200);
120
+ const data = JSON.parse(res._getData());
121
+ expect(data.ok).toBe(true);
122
+ return data;
123
+ }
124
+
118
125
  function mockSuccessfulProfileImport() {
119
126
  vi.mocked(importProfileFromRelays).mockResolvedValue({
120
127
  ok: true,
@@ -208,6 +215,22 @@ describe("nostr-profile-http", () => {
208
215
  });
209
216
 
210
217
  describe("PUT /api/channels/nostr/:accountId/profile", () => {
218
+ function mockPublishSuccess() {
219
+ vi.mocked(publishNostrProfile).mockResolvedValue({
220
+ eventId: "event123",
221
+ createdAt: 1234567890,
222
+ successes: ["wss://relay.damus.io"],
223
+ failures: [],
224
+ });
225
+ }
226
+
227
+ function expectBadRequestResponse(res: ReturnType<typeof createMockResponse>) {
228
+ expect(res._getStatusCode()).toBe(400);
229
+ const data = JSON.parse(res._getData());
230
+ expect(data.ok).toBe(false);
231
+ return data;
232
+ }
233
+
211
234
  async function expectPrivatePictureRejected(pictureUrl: string) {
212
235
  const ctx = createMockContext();
213
236
  const handler = createNostrProfileHttpHandler(ctx);
@@ -219,9 +242,7 @@ describe("nostr-profile-http", () => {
219
242
 
220
243
  await handler(req, res);
221
244
 
222
- expect(res._getStatusCode()).toBe(400);
223
- const data = JSON.parse(res._getData());
224
- expect(data.ok).toBe(false);
245
+ const data = expectBadRequestResponse(res);
225
246
  expect(data.error).toContain("private");
226
247
  }
227
248
 
@@ -235,18 +256,11 @@ describe("nostr-profile-http", () => {
235
256
  });
236
257
  const res = createMockResponse();
237
258
 
238
- vi.mocked(publishNostrProfile).mockResolvedValue({
239
- eventId: "event123",
240
- createdAt: 1234567890,
241
- successes: ["wss://relay.damus.io"],
242
- failures: [],
243
- });
259
+ mockPublishSuccess();
244
260
 
245
261
  await handler(req, res);
246
262
 
247
- expect(res._getStatusCode()).toBe(200);
248
- const data = JSON.parse(res._getData());
249
- expect(data.ok).toBe(true);
263
+ const data = expectOkResponse(res);
250
264
  expect(data.eventId).toBe("event123");
251
265
  expect(data.successes).toContain("wss://relay.damus.io");
252
266
  expect(data.persisted).toBe(true);
@@ -332,9 +346,7 @@ describe("nostr-profile-http", () => {
332
346
 
333
347
  await handler(req, res);
334
348
 
335
- expect(res._getStatusCode()).toBe(400);
336
- const data = JSON.parse(res._getData());
337
- expect(data.ok).toBe(false);
349
+ const data = expectBadRequestResponse(res);
338
350
  // The schema validation catches non-https URLs before SSRF check
339
351
  expect(data.error).toBe("Validation failed");
340
352
  expect(data.details).toBeDefined();
@@ -368,12 +380,7 @@ describe("nostr-profile-http", () => {
368
380
  const ctx = createMockContext();
369
381
  const handler = createNostrProfileHttpHandler(ctx);
370
382
 
371
- vi.mocked(publishNostrProfile).mockResolvedValue({
372
- eventId: "event123",
373
- createdAt: 1234567890,
374
- successes: ["wss://relay.damus.io"],
375
- failures: [],
376
- });
383
+ mockPublishSuccess();
377
384
 
378
385
  // Make 6 requests (limit is 5/min)
379
386
  for (let i = 0; i < 6; i++) {
@@ -384,7 +391,7 @@ describe("nostr-profile-http", () => {
384
391
  await handler(req, res);
385
392
 
386
393
  if (i < 5) {
387
- expect(res._getStatusCode()).toBe(200);
394
+ expectOkResponse(res);
388
395
  } else {
389
396
  expect(res._getStatusCode()).toBe(429);
390
397
  const data = JSON.parse(res._getData());
@@ -414,6 +421,12 @@ describe("nostr-profile-http", () => {
414
421
  });
415
422
 
416
423
  describe("POST /api/channels/nostr/:accountId/profile/import", () => {
424
+ function expectImportSuccessResponse(res: ReturnType<typeof createMockResponse>) {
425
+ const data = expectOkResponse(res);
426
+ expect(data.imported.name).toBe("imported");
427
+ return data;
428
+ }
429
+
417
430
  it("imports profile from relays", async () => {
418
431
  const ctx = createMockContext();
419
432
  const handler = createNostrProfileHttpHandler(ctx);
@@ -424,10 +437,7 @@ describe("nostr-profile-http", () => {
424
437
 
425
438
  await handler(req, res);
426
439
 
427
- expect(res._getStatusCode()).toBe(200);
428
- const data = JSON.parse(res._getData());
429
- expect(data.ok).toBe(true);
430
- expect(data.imported.name).toBe("imported");
440
+ const data = expectImportSuccessResponse(res);
431
441
  expect(data.saved).toBe(false); // autoMerge not requested
432
442
  });
433
443
 
@@ -490,8 +500,7 @@ describe("nostr-profile-http", () => {
490
500
 
491
501
  await handler(req, res);
492
502
 
493
- expect(res._getStatusCode()).toBe(200);
494
- const data = JSON.parse(res._getData());
503
+ const data = expectImportSuccessResponse(res);
495
504
  expect(data.saved).toBe(true);
496
505
  expect(ctx.updateConfigProfile).toHaveBeenCalled();
497
506
  });