@openclaw/nostr 2026.5.2 → 2026.5.3-beta.2
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/nostr-key-utils.ts
DELETED
|
@@ -1,94 +0,0 @@
|
|
|
1
|
-
import { getPublicKey, nip19 } from "nostr-tools";
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Validate and normalize a private key (accepts hex or nsec format)
|
|
5
|
-
*/
|
|
6
|
-
export function validatePrivateKey(key: string): Uint8Array {
|
|
7
|
-
const trimmed = key.trim();
|
|
8
|
-
|
|
9
|
-
// Handle nsec (bech32) format
|
|
10
|
-
if (trimmed.startsWith("nsec1")) {
|
|
11
|
-
const decoded = nip19.decode(trimmed);
|
|
12
|
-
if (decoded.type !== "nsec") {
|
|
13
|
-
throw new Error("Invalid nsec key: wrong type");
|
|
14
|
-
}
|
|
15
|
-
return decoded.data;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
// Handle hex format
|
|
19
|
-
if (!/^[0-9a-fA-F]{64}$/.test(trimmed)) {
|
|
20
|
-
throw new Error("Private key must be 64 hex characters or nsec bech32 format");
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
// Convert hex string to Uint8Array
|
|
24
|
-
const bytes = new Uint8Array(32);
|
|
25
|
-
for (let i = 0; i < 32; i++) {
|
|
26
|
-
bytes[i] = Number.parseInt(trimmed.slice(i * 2, i * 2 + 2), 16);
|
|
27
|
-
}
|
|
28
|
-
return bytes;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
/**
|
|
32
|
-
* Get public key from private key (hex or nsec format)
|
|
33
|
-
*/
|
|
34
|
-
export function getPublicKeyFromPrivate(privateKey: string): string {
|
|
35
|
-
const sk = validatePrivateKey(privateKey);
|
|
36
|
-
return getPublicKey(sk);
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
/**
|
|
40
|
-
* Check if a string looks like a valid Nostr pubkey (hex or npub)
|
|
41
|
-
*/
|
|
42
|
-
export function isValidPubkey(input: string): boolean {
|
|
43
|
-
if (typeof input !== "string") {
|
|
44
|
-
return false;
|
|
45
|
-
}
|
|
46
|
-
const trimmed = input.trim();
|
|
47
|
-
|
|
48
|
-
// npub format
|
|
49
|
-
if (trimmed.startsWith("npub1")) {
|
|
50
|
-
try {
|
|
51
|
-
const decoded = nip19.decode(trimmed);
|
|
52
|
-
return decoded.type === "npub";
|
|
53
|
-
} catch {
|
|
54
|
-
return false;
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
// Hex format
|
|
59
|
-
return /^[0-9a-fA-F]{64}$/.test(trimmed);
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
/**
|
|
63
|
-
* Normalize a pubkey to hex format (accepts npub or hex)
|
|
64
|
-
*/
|
|
65
|
-
export function normalizePubkey(input: string): string {
|
|
66
|
-
const trimmed = input.trim();
|
|
67
|
-
|
|
68
|
-
// npub format - decode to hex
|
|
69
|
-
if (trimmed.startsWith("npub1")) {
|
|
70
|
-
const decoded = nip19.decode(trimmed);
|
|
71
|
-
if (decoded.type !== "npub") {
|
|
72
|
-
throw new Error("Invalid npub key");
|
|
73
|
-
}
|
|
74
|
-
// Convert Uint8Array to hex string
|
|
75
|
-
return Array.from(decoded.data as unknown as Uint8Array)
|
|
76
|
-
.map((b) => b.toString(16).padStart(2, "0"))
|
|
77
|
-
.join("");
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
// Already hex - validate and return lowercase
|
|
81
|
-
if (!/^[0-9a-fA-F]{64}$/.test(trimmed)) {
|
|
82
|
-
throw new Error("Pubkey must be 64 hex characters or npub format");
|
|
83
|
-
}
|
|
84
|
-
return trimmed.toLowerCase();
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
/**
|
|
88
|
-
* Convert a hex pubkey to npub format
|
|
89
|
-
*/
|
|
90
|
-
export function pubkeyToNpub(hexPubkey: string): string {
|
|
91
|
-
const normalized = normalizePubkey(hexPubkey);
|
|
92
|
-
// npubEncode expects a hex string, not Uint8Array
|
|
93
|
-
return nip19.npubEncode(normalized);
|
|
94
|
-
}
|
|
@@ -1,134 +0,0 @@
|
|
|
1
|
-
import { type NostrProfile, NostrProfileSchema } from "./config-schema.js";
|
|
2
|
-
|
|
3
|
-
/** NIP-01 profile content (JSON inside kind:0 event). */
|
|
4
|
-
export interface ProfileContent {
|
|
5
|
-
name?: string;
|
|
6
|
-
display_name?: string;
|
|
7
|
-
about?: string;
|
|
8
|
-
picture?: string;
|
|
9
|
-
banner?: string;
|
|
10
|
-
website?: string;
|
|
11
|
-
nip05?: string;
|
|
12
|
-
lud16?: string;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* Convert our config profile schema to NIP-01 content format.
|
|
17
|
-
* Strips undefined fields and validates URLs.
|
|
18
|
-
*/
|
|
19
|
-
export function profileToContent(profile: NostrProfile): ProfileContent {
|
|
20
|
-
const validated = NostrProfileSchema.parse(profile);
|
|
21
|
-
|
|
22
|
-
const content: ProfileContent = {};
|
|
23
|
-
|
|
24
|
-
if (validated.name !== undefined) {
|
|
25
|
-
content.name = validated.name;
|
|
26
|
-
}
|
|
27
|
-
if (validated.displayName !== undefined) {
|
|
28
|
-
content.display_name = validated.displayName;
|
|
29
|
-
}
|
|
30
|
-
if (validated.about !== undefined) {
|
|
31
|
-
content.about = validated.about;
|
|
32
|
-
}
|
|
33
|
-
if (validated.picture !== undefined) {
|
|
34
|
-
content.picture = validated.picture;
|
|
35
|
-
}
|
|
36
|
-
if (validated.banner !== undefined) {
|
|
37
|
-
content.banner = validated.banner;
|
|
38
|
-
}
|
|
39
|
-
if (validated.website !== undefined) {
|
|
40
|
-
content.website = validated.website;
|
|
41
|
-
}
|
|
42
|
-
if (validated.nip05 !== undefined) {
|
|
43
|
-
content.nip05 = validated.nip05;
|
|
44
|
-
}
|
|
45
|
-
if (validated.lud16 !== undefined) {
|
|
46
|
-
content.lud16 = validated.lud16;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
return content;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
/**
|
|
53
|
-
* Convert NIP-01 content format back to our config profile schema.
|
|
54
|
-
* Useful for importing existing profiles from relays.
|
|
55
|
-
*/
|
|
56
|
-
export function contentToProfile(content: ProfileContent): NostrProfile {
|
|
57
|
-
const profile: NostrProfile = {};
|
|
58
|
-
|
|
59
|
-
if (content.name !== undefined) {
|
|
60
|
-
profile.name = content.name;
|
|
61
|
-
}
|
|
62
|
-
if (content.display_name !== undefined) {
|
|
63
|
-
profile.displayName = content.display_name;
|
|
64
|
-
}
|
|
65
|
-
if (content.about !== undefined) {
|
|
66
|
-
profile.about = content.about;
|
|
67
|
-
}
|
|
68
|
-
if (content.picture !== undefined) {
|
|
69
|
-
profile.picture = content.picture;
|
|
70
|
-
}
|
|
71
|
-
if (content.banner !== undefined) {
|
|
72
|
-
profile.banner = content.banner;
|
|
73
|
-
}
|
|
74
|
-
if (content.website !== undefined) {
|
|
75
|
-
profile.website = content.website;
|
|
76
|
-
}
|
|
77
|
-
if (content.nip05 !== undefined) {
|
|
78
|
-
profile.nip05 = content.nip05;
|
|
79
|
-
}
|
|
80
|
-
if (content.lud16 !== undefined) {
|
|
81
|
-
profile.lud16 = content.lud16;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
return profile;
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
/**
|
|
88
|
-
* Validate a profile without throwing (returns result object).
|
|
89
|
-
*/
|
|
90
|
-
export function validateProfile(profile: unknown): {
|
|
91
|
-
valid: boolean;
|
|
92
|
-
profile?: NostrProfile;
|
|
93
|
-
errors?: string[];
|
|
94
|
-
} {
|
|
95
|
-
const result = NostrProfileSchema.safeParse(profile);
|
|
96
|
-
|
|
97
|
-
if (result.success) {
|
|
98
|
-
return { valid: true, profile: result.data };
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
return {
|
|
102
|
-
valid: false,
|
|
103
|
-
errors: result.error.issues.map((e) => `${e.path.join(".")}: ${e.message}`),
|
|
104
|
-
};
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
/**
|
|
108
|
-
* Sanitize profile text fields to prevent XSS when displaying in UI.
|
|
109
|
-
* Escapes HTML special characters.
|
|
110
|
-
*/
|
|
111
|
-
export function sanitizeProfileForDisplay(profile: NostrProfile): NostrProfile {
|
|
112
|
-
const escapeHtml = (str: string | undefined): string | undefined => {
|
|
113
|
-
if (str === undefined) {
|
|
114
|
-
return undefined;
|
|
115
|
-
}
|
|
116
|
-
return str
|
|
117
|
-
.replace(/&/g, "&")
|
|
118
|
-
.replace(/</g, "<")
|
|
119
|
-
.replace(/>/g, ">")
|
|
120
|
-
.replace(/"/g, """)
|
|
121
|
-
.replace(/'/g, "'");
|
|
122
|
-
};
|
|
123
|
-
|
|
124
|
-
return {
|
|
125
|
-
name: escapeHtml(profile.name),
|
|
126
|
-
displayName: escapeHtml(profile.displayName),
|
|
127
|
-
about: escapeHtml(profile.about),
|
|
128
|
-
picture: profile.picture,
|
|
129
|
-
banner: profile.banner,
|
|
130
|
-
website: profile.website,
|
|
131
|
-
nip05: escapeHtml(profile.nip05),
|
|
132
|
-
lud16: escapeHtml(profile.lud16),
|
|
133
|
-
};
|
|
134
|
-
}
|
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
export {
|
|
2
|
-
readJsonBodyWithLimit,
|
|
3
|
-
requestBodyErrorToText,
|
|
4
|
-
} from "openclaw/plugin-sdk/webhook-request-guards";
|
|
5
|
-
export { createFixedWindowRateLimiter } from "openclaw/plugin-sdk/webhook-ingress";
|
|
6
|
-
export { getPluginRuntimeGatewayRequestScope } from "../runtime-api.js";
|