@openclaw/nostr 2026.5.2 → 2026.5.3-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/api.js +532 -0
- package/dist/channel-DfEqBtUh.js +1466 -0
- package/dist/channel-plugin-api.js +2 -0
- package/dist/config-schema-DIk4jlBg.js +64 -0
- package/dist/default-relays-DLwdWOTu.js +4 -0
- package/dist/inbound-direct-dm-runtime-22bZWcIW.js +2 -0
- package/dist/index.js +84 -0
- package/dist/runtime-api.js +2 -0
- package/dist/setup-api.js +2 -0
- package/dist/setup-entry.js +11 -0
- package/dist/setup-plugin-api.js +165 -0
- package/dist/setup-surface-DxAaUTyC.js +336 -0
- package/dist/test-api.js +2 -0
- package/package.json +15 -6
- package/api.ts +0 -10
- package/channel-plugin-api.ts +0 -1
- package/index.ts +0 -97
- package/runtime-api.ts +0 -6
- package/setup-api.ts +0 -1
- package/setup-entry.ts +0 -9
- package/setup-plugin-api.ts +0 -3
- package/src/channel-api.ts +0 -15
- package/src/channel.inbound.test.ts +0 -176
- package/src/channel.outbound.test.ts +0 -128
- package/src/channel.setup.ts +0 -231
- package/src/channel.test.ts +0 -519
- package/src/channel.ts +0 -207
- package/src/config-schema.ts +0 -98
- package/src/default-relays.ts +0 -1
- package/src/gateway.ts +0 -302
- package/src/inbound-direct-dm-runtime.ts +0 -1
- package/src/metrics.ts +0 -458
- package/src/nostr-bus.fuzz.test.ts +0 -360
- package/src/nostr-bus.inbound.test.ts +0 -526
- package/src/nostr-bus.integration.test.ts +0 -472
- package/src/nostr-bus.test.ts +0 -190
- package/src/nostr-bus.ts +0 -789
- package/src/nostr-key-utils.ts +0 -94
- package/src/nostr-profile-core.ts +0 -134
- package/src/nostr-profile-http-runtime.ts +0 -6
- package/src/nostr-profile-http.test.ts +0 -632
- package/src/nostr-profile-http.ts +0 -594
- package/src/nostr-profile-import.test.ts +0 -119
- package/src/nostr-profile-import.ts +0 -262
- package/src/nostr-profile-url-safety.ts +0 -21
- package/src/nostr-profile.fuzz.test.ts +0 -430
- package/src/nostr-profile.test.ts +0 -412
- package/src/nostr-profile.ts +0 -144
- package/src/nostr-state-store.test.ts +0 -237
- package/src/nostr-state-store.ts +0 -223
- package/src/runtime.ts +0 -9
- package/src/seen-tracker.ts +0 -289
- package/src/session-route.ts +0 -25
- package/src/setup-surface.ts +0 -265
- package/src/test-fixtures.ts +0 -45
- package/src/types.ts +0 -117
- package/test/setup.ts +0 -5
- package/test-api.ts +0 -1
- package/tsconfig.json +0 -16
package/src/channel.test.ts
DELETED
|
@@ -1,519 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
createPluginSetupWizardConfigure,
|
|
3
|
-
createTestWizardPrompter,
|
|
4
|
-
runSetupWizardConfigure,
|
|
5
|
-
} from "openclaw/plugin-sdk/plugin-test-runtime";
|
|
6
|
-
import type { WizardPrompter } from "openclaw/plugin-sdk/plugin-test-runtime";
|
|
7
|
-
import { describe, expect, it, vi } from "vitest";
|
|
8
|
-
import type { OpenClawConfig } from "../runtime-api.js";
|
|
9
|
-
import { nostrSetupWizard } from "./setup-surface.js";
|
|
10
|
-
import {
|
|
11
|
-
TEST_HEX_PRIVATE_KEY,
|
|
12
|
-
TEST_SETUP_RELAY_URLS,
|
|
13
|
-
buildResolvedNostrAccount,
|
|
14
|
-
createConfiguredNostrCfg,
|
|
15
|
-
} from "./test-fixtures.js";
|
|
16
|
-
import { listNostrAccountIds, resolveDefaultNostrAccountId, resolveNostrAccount } from "./types.js";
|
|
17
|
-
|
|
18
|
-
function normalizeNostrTestEntry(entry: string): string {
|
|
19
|
-
return entry
|
|
20
|
-
.trim()
|
|
21
|
-
.replace(/^nostr:/i, "")
|
|
22
|
-
.toLowerCase();
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
function resolveNostrTestDmPolicy(params: {
|
|
26
|
-
cfg: OpenClawConfig;
|
|
27
|
-
account: ReturnType<typeof resolveNostrAccount>;
|
|
28
|
-
}) {
|
|
29
|
-
return {
|
|
30
|
-
cfg: params.cfg,
|
|
31
|
-
accountId: params.account.accountId,
|
|
32
|
-
policy: params.account.config.dmPolicy ?? "pairing",
|
|
33
|
-
allowFrom: params.account.config.allowFrom ?? [],
|
|
34
|
-
normalizeEntry: normalizeNostrTestEntry,
|
|
35
|
-
};
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
const nostrTestPlugin = {
|
|
39
|
-
id: "nostr",
|
|
40
|
-
meta: {
|
|
41
|
-
label: "Nostr",
|
|
42
|
-
docsPath: "/channels/nostr",
|
|
43
|
-
blurb: "Decentralized DMs via Nostr relays (NIP-04)",
|
|
44
|
-
},
|
|
45
|
-
capabilities: {
|
|
46
|
-
chatTypes: ["direct"],
|
|
47
|
-
media: false,
|
|
48
|
-
},
|
|
49
|
-
config: {
|
|
50
|
-
listAccountIds: listNostrAccountIds,
|
|
51
|
-
resolveAccount: (cfg: OpenClawConfig, accountId?: string | null) =>
|
|
52
|
-
resolveNostrAccount({ cfg, accountId }),
|
|
53
|
-
},
|
|
54
|
-
messaging: {
|
|
55
|
-
normalizeTarget: (target: string) => normalizeNostrTestEntry(target),
|
|
56
|
-
targetResolver: {
|
|
57
|
-
looksLikeId: (input: string) => {
|
|
58
|
-
const trimmed = input.trim();
|
|
59
|
-
return trimmed.startsWith("npub1") || /^[0-9a-fA-F]{64}$/.test(trimmed);
|
|
60
|
-
},
|
|
61
|
-
},
|
|
62
|
-
},
|
|
63
|
-
outbound: {
|
|
64
|
-
deliveryMode: "direct",
|
|
65
|
-
textChunkLimit: 4000,
|
|
66
|
-
},
|
|
67
|
-
pairing: {
|
|
68
|
-
idLabel: "nostrPubkey",
|
|
69
|
-
normalizeAllowEntry: normalizeNostrTestEntry,
|
|
70
|
-
},
|
|
71
|
-
security: {
|
|
72
|
-
resolveDmPolicy: resolveNostrTestDmPolicy,
|
|
73
|
-
},
|
|
74
|
-
status: {
|
|
75
|
-
defaultRuntime: {
|
|
76
|
-
accountId: "default",
|
|
77
|
-
running: false,
|
|
78
|
-
lastStartAt: null,
|
|
79
|
-
lastStopAt: null,
|
|
80
|
-
lastError: null,
|
|
81
|
-
},
|
|
82
|
-
},
|
|
83
|
-
setupWizard: nostrSetupWizard,
|
|
84
|
-
setup: {
|
|
85
|
-
resolveAccountId: ({
|
|
86
|
-
cfg,
|
|
87
|
-
accountId,
|
|
88
|
-
}: {
|
|
89
|
-
cfg: OpenClawConfig;
|
|
90
|
-
accountId?: string;
|
|
91
|
-
input: unknown;
|
|
92
|
-
}) => accountId?.trim() || resolveDefaultNostrAccountId(cfg),
|
|
93
|
-
},
|
|
94
|
-
};
|
|
95
|
-
|
|
96
|
-
const nostrConfigure = createPluginSetupWizardConfigure(nostrTestPlugin);
|
|
97
|
-
|
|
98
|
-
function requireNostrLooksLikeId() {
|
|
99
|
-
const looksLikeId = nostrTestPlugin.messaging?.targetResolver?.looksLikeId;
|
|
100
|
-
if (!looksLikeId) {
|
|
101
|
-
throw new Error("nostr messaging.targetResolver.looksLikeId missing");
|
|
102
|
-
}
|
|
103
|
-
return looksLikeId;
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
function requireNostrNormalizeTarget() {
|
|
107
|
-
const normalize = nostrTestPlugin.messaging?.normalizeTarget;
|
|
108
|
-
if (!normalize) {
|
|
109
|
-
throw new Error("nostr messaging.normalizeTarget missing");
|
|
110
|
-
}
|
|
111
|
-
return normalize;
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
function requireNostrPairingNormalizer() {
|
|
115
|
-
const normalize = nostrTestPlugin.pairing?.normalizeAllowEntry;
|
|
116
|
-
if (!normalize) {
|
|
117
|
-
throw new Error("nostr pairing.normalizeAllowEntry missing");
|
|
118
|
-
}
|
|
119
|
-
return normalize;
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
function requireNostrResolveDmPolicy() {
|
|
123
|
-
const resolveDmPolicy = nostrTestPlugin.security?.resolveDmPolicy;
|
|
124
|
-
if (!resolveDmPolicy) {
|
|
125
|
-
throw new Error("nostr security.resolveDmPolicy missing");
|
|
126
|
-
}
|
|
127
|
-
return resolveDmPolicy;
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
describe("nostrPlugin", () => {
|
|
131
|
-
describe("meta", () => {
|
|
132
|
-
it("has correct id", () => {
|
|
133
|
-
expect(nostrTestPlugin.id).toBe("nostr");
|
|
134
|
-
});
|
|
135
|
-
|
|
136
|
-
it("has required meta fields", () => {
|
|
137
|
-
expect(nostrTestPlugin.meta.label).toBe("Nostr");
|
|
138
|
-
expect(nostrTestPlugin.meta.docsPath).toBe("/channels/nostr");
|
|
139
|
-
expect(nostrTestPlugin.meta.blurb).toContain("NIP-04");
|
|
140
|
-
});
|
|
141
|
-
});
|
|
142
|
-
|
|
143
|
-
describe("capabilities", () => {
|
|
144
|
-
it("supports direct messages", () => {
|
|
145
|
-
expect(nostrTestPlugin.capabilities.chatTypes).toContain("direct");
|
|
146
|
-
});
|
|
147
|
-
|
|
148
|
-
it("does not support groups (MVP)", () => {
|
|
149
|
-
expect(nostrTestPlugin.capabilities.chatTypes).not.toContain("group");
|
|
150
|
-
});
|
|
151
|
-
|
|
152
|
-
it("does not support media (MVP)", () => {
|
|
153
|
-
expect(nostrTestPlugin.capabilities.media).toBe(false);
|
|
154
|
-
});
|
|
155
|
-
});
|
|
156
|
-
|
|
157
|
-
describe("config adapter", () => {
|
|
158
|
-
it("listAccountIds returns empty array for unconfigured", () => {
|
|
159
|
-
const cfg = { channels: {} };
|
|
160
|
-
const ids = nostrTestPlugin.config.listAccountIds(cfg);
|
|
161
|
-
expect(ids).toEqual([]);
|
|
162
|
-
});
|
|
163
|
-
|
|
164
|
-
it("listAccountIds returns default for configured", () => {
|
|
165
|
-
const cfg = createConfiguredNostrCfg();
|
|
166
|
-
const ids = nostrTestPlugin.config.listAccountIds(cfg);
|
|
167
|
-
expect(ids).toContain("default");
|
|
168
|
-
});
|
|
169
|
-
});
|
|
170
|
-
|
|
171
|
-
describe("messaging", () => {
|
|
172
|
-
it("recognizes npub as valid target", () => {
|
|
173
|
-
const looksLikeId = requireNostrLooksLikeId();
|
|
174
|
-
|
|
175
|
-
expect(looksLikeId("npub1xyz123")).toBe(true);
|
|
176
|
-
});
|
|
177
|
-
|
|
178
|
-
it("recognizes hex pubkey as valid target", () => {
|
|
179
|
-
const looksLikeId = requireNostrLooksLikeId();
|
|
180
|
-
|
|
181
|
-
expect(looksLikeId(TEST_HEX_PRIVATE_KEY)).toBe(true);
|
|
182
|
-
});
|
|
183
|
-
|
|
184
|
-
it("rejects invalid input", () => {
|
|
185
|
-
const looksLikeId = requireNostrLooksLikeId();
|
|
186
|
-
|
|
187
|
-
expect(looksLikeId("not-a-pubkey")).toBe(false);
|
|
188
|
-
expect(looksLikeId("")).toBe(false);
|
|
189
|
-
});
|
|
190
|
-
|
|
191
|
-
it("normalizeTarget strips spaced nostr prefixes", () => {
|
|
192
|
-
const normalize = requireNostrNormalizeTarget();
|
|
193
|
-
|
|
194
|
-
expect(normalize(`nostr:${TEST_HEX_PRIVATE_KEY}`)).toBe(TEST_HEX_PRIVATE_KEY);
|
|
195
|
-
expect(normalize(` nostr:${TEST_HEX_PRIVATE_KEY} `)).toBe(TEST_HEX_PRIVATE_KEY);
|
|
196
|
-
});
|
|
197
|
-
});
|
|
198
|
-
|
|
199
|
-
describe("outbound", () => {
|
|
200
|
-
it("has correct delivery mode", () => {
|
|
201
|
-
expect(nostrTestPlugin.outbound?.deliveryMode).toBe("direct");
|
|
202
|
-
});
|
|
203
|
-
|
|
204
|
-
it("has reasonable text chunk limit", () => {
|
|
205
|
-
expect(nostrTestPlugin.outbound?.textChunkLimit).toBe(4000);
|
|
206
|
-
});
|
|
207
|
-
});
|
|
208
|
-
|
|
209
|
-
describe("pairing", () => {
|
|
210
|
-
it("has id label for pairing", () => {
|
|
211
|
-
expect(nostrTestPlugin.pairing?.idLabel).toBe("nostrPubkey");
|
|
212
|
-
});
|
|
213
|
-
|
|
214
|
-
it("normalizes spaced nostr prefixes in allow entries", () => {
|
|
215
|
-
const normalize = requireNostrPairingNormalizer();
|
|
216
|
-
|
|
217
|
-
expect(normalize(`nostr:${TEST_HEX_PRIVATE_KEY}`)).toBe(TEST_HEX_PRIVATE_KEY);
|
|
218
|
-
expect(normalize(` nostr:${TEST_HEX_PRIVATE_KEY} `)).toBe(TEST_HEX_PRIVATE_KEY);
|
|
219
|
-
});
|
|
220
|
-
});
|
|
221
|
-
|
|
222
|
-
describe("security", () => {
|
|
223
|
-
it("normalizes dm allowlist entries through the dm policy adapter", () => {
|
|
224
|
-
const resolveDmPolicy = requireNostrResolveDmPolicy();
|
|
225
|
-
|
|
226
|
-
const cfg = createConfiguredNostrCfg({
|
|
227
|
-
dmPolicy: "allowlist",
|
|
228
|
-
allowFrom: [` nostr:${TEST_HEX_PRIVATE_KEY} `],
|
|
229
|
-
});
|
|
230
|
-
const account = buildResolvedNostrAccount({
|
|
231
|
-
config: cfg.channels.nostr,
|
|
232
|
-
});
|
|
233
|
-
|
|
234
|
-
const result = resolveDmPolicy({ cfg, account });
|
|
235
|
-
if (!result) {
|
|
236
|
-
throw new Error("nostr resolveDmPolicy returned null");
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
expect(result.policy).toBe("allowlist");
|
|
240
|
-
expect(result.allowFrom).toEqual([` nostr:${TEST_HEX_PRIVATE_KEY} `]);
|
|
241
|
-
expect(result.normalizeEntry?.(` nostr:${TEST_HEX_PRIVATE_KEY} `)).toBe(
|
|
242
|
-
TEST_HEX_PRIVATE_KEY,
|
|
243
|
-
);
|
|
244
|
-
});
|
|
245
|
-
});
|
|
246
|
-
|
|
247
|
-
describe("status", () => {
|
|
248
|
-
it("has default runtime", () => {
|
|
249
|
-
expect(nostrTestPlugin.status?.defaultRuntime).toEqual({
|
|
250
|
-
accountId: "default",
|
|
251
|
-
running: false,
|
|
252
|
-
lastStartAt: null,
|
|
253
|
-
lastStopAt: null,
|
|
254
|
-
lastError: null,
|
|
255
|
-
});
|
|
256
|
-
});
|
|
257
|
-
});
|
|
258
|
-
});
|
|
259
|
-
|
|
260
|
-
describe("nostr setup wizard", () => {
|
|
261
|
-
it("configures a private key and relay URLs", async () => {
|
|
262
|
-
const prompter = createTestWizardPrompter({
|
|
263
|
-
text: vi.fn(async ({ message }: { message: string }) => {
|
|
264
|
-
if (message === "Nostr private key (nsec... or hex)") {
|
|
265
|
-
return TEST_HEX_PRIVATE_KEY;
|
|
266
|
-
}
|
|
267
|
-
if (message === "Relay URLs (comma-separated, optional)") {
|
|
268
|
-
return TEST_SETUP_RELAY_URLS.join(", ");
|
|
269
|
-
}
|
|
270
|
-
throw new Error(`Unexpected prompt: ${message}`);
|
|
271
|
-
}) as WizardPrompter["text"],
|
|
272
|
-
});
|
|
273
|
-
|
|
274
|
-
const result = await runSetupWizardConfigure({
|
|
275
|
-
configure: nostrConfigure,
|
|
276
|
-
cfg: {} as OpenClawConfig,
|
|
277
|
-
prompter,
|
|
278
|
-
options: {},
|
|
279
|
-
});
|
|
280
|
-
|
|
281
|
-
expect(result.accountId).toBe("default");
|
|
282
|
-
expect(result.cfg.channels?.nostr?.enabled).toBe(true);
|
|
283
|
-
expect(result.cfg.channels?.nostr?.privateKey).toBe(TEST_HEX_PRIVATE_KEY);
|
|
284
|
-
expect(result.cfg.channels?.nostr?.relays).toEqual(TEST_SETUP_RELAY_URLS);
|
|
285
|
-
});
|
|
286
|
-
|
|
287
|
-
it("preserves the selected named account label during setup", async () => {
|
|
288
|
-
const prompter = createTestWizardPrompter({
|
|
289
|
-
text: vi.fn(async ({ message }: { message: string }) => {
|
|
290
|
-
if (message === "Nostr private key (nsec... or hex)") {
|
|
291
|
-
return TEST_HEX_PRIVATE_KEY;
|
|
292
|
-
}
|
|
293
|
-
if (message === "Relay URLs (comma-separated, optional)") {
|
|
294
|
-
return "";
|
|
295
|
-
}
|
|
296
|
-
throw new Error(`Unexpected prompt: ${message}`);
|
|
297
|
-
}) as WizardPrompter["text"],
|
|
298
|
-
});
|
|
299
|
-
|
|
300
|
-
const result = await runSetupWizardConfigure({
|
|
301
|
-
configure: nostrConfigure,
|
|
302
|
-
cfg: {} as OpenClawConfig,
|
|
303
|
-
prompter,
|
|
304
|
-
options: {},
|
|
305
|
-
accountOverrides: {
|
|
306
|
-
nostr: "work",
|
|
307
|
-
},
|
|
308
|
-
});
|
|
309
|
-
|
|
310
|
-
expect(result.accountId).toBe("work");
|
|
311
|
-
expect(result.cfg.channels?.nostr?.defaultAccount).toBe("work");
|
|
312
|
-
expect(result.cfg.channels?.nostr?.privateKey).toBe(TEST_HEX_PRIVATE_KEY);
|
|
313
|
-
});
|
|
314
|
-
|
|
315
|
-
it("uses configured defaultAccount when setup accountId is omitted", () => {
|
|
316
|
-
expect(
|
|
317
|
-
nostrTestPlugin.setup?.resolveAccountId?.({
|
|
318
|
-
cfg: createConfiguredNostrCfg({ defaultAccount: "work" }) as OpenClawConfig,
|
|
319
|
-
accountId: undefined,
|
|
320
|
-
input: {},
|
|
321
|
-
} as never),
|
|
322
|
-
).toBe("work");
|
|
323
|
-
});
|
|
324
|
-
});
|
|
325
|
-
|
|
326
|
-
describe("nostr account helpers", () => {
|
|
327
|
-
describe("listNostrAccountIds", () => {
|
|
328
|
-
it("returns empty array when not configured", () => {
|
|
329
|
-
const cfg = { channels: {} };
|
|
330
|
-
expect(listNostrAccountIds(cfg)).toEqual([]);
|
|
331
|
-
});
|
|
332
|
-
|
|
333
|
-
it("returns empty array when nostr section exists but no privateKey", () => {
|
|
334
|
-
const cfg = { channels: { nostr: { enabled: true } } };
|
|
335
|
-
expect(listNostrAccountIds(cfg)).toEqual([]);
|
|
336
|
-
});
|
|
337
|
-
|
|
338
|
-
it("returns default when privateKey is configured", () => {
|
|
339
|
-
const cfg = createConfiguredNostrCfg();
|
|
340
|
-
expect(listNostrAccountIds(cfg)).toEqual(["default"]);
|
|
341
|
-
});
|
|
342
|
-
|
|
343
|
-
it("returns configured defaultAccount when privateKey is configured", () => {
|
|
344
|
-
const cfg = createConfiguredNostrCfg({ defaultAccount: "work" });
|
|
345
|
-
expect(listNostrAccountIds(cfg)).toEqual(["work"]);
|
|
346
|
-
});
|
|
347
|
-
|
|
348
|
-
it("does not treat unresolved SecretRef privateKey as configured", () => {
|
|
349
|
-
const cfg = {
|
|
350
|
-
channels: {
|
|
351
|
-
nostr: {
|
|
352
|
-
privateKey: {
|
|
353
|
-
source: "env",
|
|
354
|
-
provider: "default",
|
|
355
|
-
id: "NOSTR_PRIVATE_KEY",
|
|
356
|
-
},
|
|
357
|
-
},
|
|
358
|
-
},
|
|
359
|
-
};
|
|
360
|
-
expect(listNostrAccountIds(cfg)).toEqual([]);
|
|
361
|
-
});
|
|
362
|
-
});
|
|
363
|
-
|
|
364
|
-
describe("resolveDefaultNostrAccountId", () => {
|
|
365
|
-
it("returns default when configured", () => {
|
|
366
|
-
const cfg = createConfiguredNostrCfg();
|
|
367
|
-
expect(resolveDefaultNostrAccountId(cfg)).toBe("default");
|
|
368
|
-
});
|
|
369
|
-
|
|
370
|
-
it("returns default when not configured", () => {
|
|
371
|
-
const cfg = { channels: {} };
|
|
372
|
-
expect(resolveDefaultNostrAccountId(cfg)).toBe("default");
|
|
373
|
-
});
|
|
374
|
-
|
|
375
|
-
it("prefers configured defaultAccount when present", () => {
|
|
376
|
-
const cfg = createConfiguredNostrCfg({ defaultAccount: "work" });
|
|
377
|
-
expect(resolveDefaultNostrAccountId(cfg)).toBe("work");
|
|
378
|
-
});
|
|
379
|
-
});
|
|
380
|
-
|
|
381
|
-
describe("resolveNostrAccount", () => {
|
|
382
|
-
it("resolves configured account", () => {
|
|
383
|
-
const cfg = createConfiguredNostrCfg({
|
|
384
|
-
name: "Test Bot",
|
|
385
|
-
relays: ["wss://test.relay"],
|
|
386
|
-
dmPolicy: "pairing" as const,
|
|
387
|
-
});
|
|
388
|
-
const account = resolveNostrAccount({ cfg });
|
|
389
|
-
|
|
390
|
-
expect(account.accountId).toBe("default");
|
|
391
|
-
expect(account.name).toBe("Test Bot");
|
|
392
|
-
expect(account.enabled).toBe(true);
|
|
393
|
-
expect(account.configured).toBe(true);
|
|
394
|
-
expect(account.privateKey).toBe(TEST_HEX_PRIVATE_KEY);
|
|
395
|
-
expect(account.publicKey).toMatch(/^[0-9a-f]{64}$/);
|
|
396
|
-
expect(account.relays).toEqual(["wss://test.relay"]);
|
|
397
|
-
});
|
|
398
|
-
|
|
399
|
-
it("resolves unconfigured account with defaults", () => {
|
|
400
|
-
const cfg = { channels: {} };
|
|
401
|
-
const account = resolveNostrAccount({ cfg });
|
|
402
|
-
|
|
403
|
-
expect(account.accountId).toBe("default");
|
|
404
|
-
expect(account.enabled).toBe(true);
|
|
405
|
-
expect(account.configured).toBe(false);
|
|
406
|
-
expect(account.privateKey).toBe("");
|
|
407
|
-
expect(account.publicKey).toBe("");
|
|
408
|
-
expect(account.relays).toContain("wss://relay.damus.io");
|
|
409
|
-
expect(account.relays).toContain("wss://nos.lol");
|
|
410
|
-
});
|
|
411
|
-
|
|
412
|
-
it("handles disabled channel", () => {
|
|
413
|
-
const cfg = createConfiguredNostrCfg({ enabled: false });
|
|
414
|
-
const account = resolveNostrAccount({ cfg });
|
|
415
|
-
|
|
416
|
-
expect(account.enabled).toBe(false);
|
|
417
|
-
expect(account.configured).toBe(true);
|
|
418
|
-
});
|
|
419
|
-
|
|
420
|
-
it("handles custom accountId parameter", () => {
|
|
421
|
-
const cfg = createConfiguredNostrCfg();
|
|
422
|
-
const account = resolveNostrAccount({ cfg, accountId: "custom" });
|
|
423
|
-
|
|
424
|
-
expect(account.accountId).toBe("custom");
|
|
425
|
-
});
|
|
426
|
-
|
|
427
|
-
it("handles allowFrom config", () => {
|
|
428
|
-
const cfg = createConfiguredNostrCfg({
|
|
429
|
-
allowFrom: ["npub1test", "0123456789abcdef"],
|
|
430
|
-
});
|
|
431
|
-
const account = resolveNostrAccount({ cfg });
|
|
432
|
-
|
|
433
|
-
expect(account.config.allowFrom).toEqual(["npub1test", "0123456789abcdef"]);
|
|
434
|
-
});
|
|
435
|
-
|
|
436
|
-
it("handles invalid private key gracefully", () => {
|
|
437
|
-
const cfg = {
|
|
438
|
-
channels: {
|
|
439
|
-
nostr: {
|
|
440
|
-
privateKey: "invalid-key",
|
|
441
|
-
},
|
|
442
|
-
},
|
|
443
|
-
};
|
|
444
|
-
const account = resolveNostrAccount({ cfg });
|
|
445
|
-
|
|
446
|
-
expect(account.configured).toBe(true);
|
|
447
|
-
expect(account.publicKey).toBe("");
|
|
448
|
-
});
|
|
449
|
-
|
|
450
|
-
it("does not treat unresolved SecretRef privateKey as configured", () => {
|
|
451
|
-
const secretRef = {
|
|
452
|
-
source: "env" as const,
|
|
453
|
-
provider: "default",
|
|
454
|
-
id: "NOSTR_PRIVATE_KEY",
|
|
455
|
-
};
|
|
456
|
-
const cfg = {
|
|
457
|
-
channels: {
|
|
458
|
-
nostr: {
|
|
459
|
-
privateKey: secretRef,
|
|
460
|
-
},
|
|
461
|
-
},
|
|
462
|
-
};
|
|
463
|
-
const account = resolveNostrAccount({ cfg });
|
|
464
|
-
|
|
465
|
-
expect(account.configured).toBe(false);
|
|
466
|
-
expect(account.privateKey).toBe("");
|
|
467
|
-
expect(account.publicKey).toBe("");
|
|
468
|
-
expect(account.config.privateKey).toEqual(secretRef);
|
|
469
|
-
});
|
|
470
|
-
|
|
471
|
-
it("preserves all config options", () => {
|
|
472
|
-
const cfg = createConfiguredNostrCfg({
|
|
473
|
-
name: "Bot",
|
|
474
|
-
enabled: true,
|
|
475
|
-
relays: ["wss://relay1", "wss://relay2"],
|
|
476
|
-
dmPolicy: "allowlist" as const,
|
|
477
|
-
allowFrom: ["pubkey1", "pubkey2"],
|
|
478
|
-
});
|
|
479
|
-
const account = resolveNostrAccount({ cfg });
|
|
480
|
-
|
|
481
|
-
expect(account.config).toEqual({
|
|
482
|
-
privateKey: TEST_HEX_PRIVATE_KEY,
|
|
483
|
-
name: "Bot",
|
|
484
|
-
enabled: true,
|
|
485
|
-
relays: ["wss://relay1", "wss://relay2"],
|
|
486
|
-
dmPolicy: "allowlist",
|
|
487
|
-
allowFrom: ["pubkey1", "pubkey2"],
|
|
488
|
-
});
|
|
489
|
-
});
|
|
490
|
-
});
|
|
491
|
-
|
|
492
|
-
describe("setup wizard", () => {
|
|
493
|
-
it("keeps unresolved SecretRef privateKey visible without marking the account configured", () => {
|
|
494
|
-
const secretRef = {
|
|
495
|
-
source: "env" as const,
|
|
496
|
-
provider: "default",
|
|
497
|
-
id: "NOSTR_PRIVATE_KEY",
|
|
498
|
-
};
|
|
499
|
-
const cfg = {
|
|
500
|
-
channels: {
|
|
501
|
-
nostr: {
|
|
502
|
-
privateKey: secretRef,
|
|
503
|
-
},
|
|
504
|
-
},
|
|
505
|
-
};
|
|
506
|
-
const credential = nostrSetupWizard.credentials?.[0];
|
|
507
|
-
if (!credential?.inspect) {
|
|
508
|
-
throw new Error("nostr setup credential inspect missing");
|
|
509
|
-
}
|
|
510
|
-
|
|
511
|
-
expect(credential.inspect({ cfg, accountId: "default" })).toEqual({
|
|
512
|
-
accountConfigured: false,
|
|
513
|
-
hasConfiguredValue: true,
|
|
514
|
-
resolvedValue: undefined,
|
|
515
|
-
envValue: undefined,
|
|
516
|
-
});
|
|
517
|
-
});
|
|
518
|
-
});
|
|
519
|
-
});
|