agentdex-cli 0.4.2 → 0.4.4
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/cli.js +20 -14
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/nostr.d.ts +9 -3
- package/dist/nostr.js +69 -13
- package/package.json +1 -1
- package/src/cli.ts +20 -14
- package/src/index.ts +1 -1
- package/src/nostr.ts +73 -12
package/dist/cli.js
CHANGED
|
@@ -9,7 +9,7 @@ import { fileURLToPath } from 'url';
|
|
|
9
9
|
import { dirname, join } from 'path';
|
|
10
10
|
import { nip19 } from 'nostr-tools';
|
|
11
11
|
import { AgentdexClient } from './client.js';
|
|
12
|
-
import { parseSecretKey, getNpub, getPubkeyHex, createProfileEvent,
|
|
12
|
+
import { parseSecretKey, getNpub, getPubkeyHex, createProfileEvent, buildKind0Event, publishToRelays, createNote, generateAndSaveKeypair } from './nostr.js';
|
|
13
13
|
import { payInvoice } from './nwc.js';
|
|
14
14
|
const __filename = fileURLToPath(import.meta.url);
|
|
15
15
|
const __dirname = dirname(__filename);
|
|
@@ -70,7 +70,7 @@ program
|
|
|
70
70
|
.option('--owner-type <type>', 'Owner type: human, agent, org (sets owner_type tag on kind 31339)')
|
|
71
71
|
.option('--parent <npub-or-hex>', 'Parent/orchestrator agent pubkey (npub or hex)')
|
|
72
72
|
.option('--bot', 'Add ["bot"] tag to kind 0 profile (declares this pubkey as automated)')
|
|
73
|
-
.option('--portfolio <entry>', 'Portfolio
|
|
73
|
+
.option('--portfolio <entry>', 'Portfolio entry (format: "id,url,label,description") — repeatable', (val, acc) => [...acc, val], [])
|
|
74
74
|
.option('--skill <skill>', 'Skill tag (repeatable)', (val, acc) => [...acc, val], [])
|
|
75
75
|
.option('--experience <exp>', 'Experience tag (repeatable)', (val, acc) => [...acc, val], [])
|
|
76
76
|
.option('--nwc <uri>', 'Nostr Wallet Connect URI for auto-pay')
|
|
@@ -100,10 +100,16 @@ program
|
|
|
100
100
|
framework = answers.framework || framework;
|
|
101
101
|
}
|
|
102
102
|
const spinner = ora('Signing event...').start();
|
|
103
|
-
// Parse portfolio entries ("url,
|
|
103
|
+
// Parse portfolio entries ("id,url,label,description")
|
|
104
104
|
const portfolio = (options.portfolio || []).map((entry) => {
|
|
105
105
|
const parts = entry.split(',').map((s) => s.trim());
|
|
106
|
-
|
|
106
|
+
// Support both: "id,url,label,desc" (new) and "url,label,desc" (old)
|
|
107
|
+
if (parts.length >= 2 && parts[1]?.startsWith('http')) {
|
|
108
|
+
return { id: parts[0], url: parts[1], name: parts[2], description: parts[3] };
|
|
109
|
+
}
|
|
110
|
+
// Old format fallback
|
|
111
|
+
const id = (parts[1] || parts[0]).toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/(^-|-$)/g, '');
|
|
112
|
+
return { id, url: parts[0], name: parts[1], description: parts[2] };
|
|
107
113
|
});
|
|
108
114
|
// Resolve owner pubkey hex from --owner flag (npub or hex)
|
|
109
115
|
let ownerNpub = options.owner;
|
|
@@ -209,17 +215,17 @@ program
|
|
|
209
215
|
console.log(chalk.gray(` Published to: ${published.join(', ')}`));
|
|
210
216
|
console.log('');
|
|
211
217
|
console.log(chalk.gray(` Run ${chalk.white('agentdex claim <name>')} to get ${chalk.hex('#D4A574')('<name>@agentdex.id')}`));
|
|
212
|
-
// Publish kind 0 profile (
|
|
218
|
+
// Publish kind 0 profile (fetch existing, merge, republish)
|
|
213
219
|
const k0Spinner = ora('Publishing kind 0 profile to Nostr relays...').start();
|
|
214
220
|
try {
|
|
215
|
-
const kind0 =
|
|
221
|
+
const kind0 = await buildKind0Event(sk, {
|
|
216
222
|
name,
|
|
217
223
|
about: description || undefined,
|
|
218
224
|
picture: options.avatar || undefined,
|
|
219
225
|
lud16: options.lightning || undefined,
|
|
220
226
|
ownerPubkeyHex,
|
|
221
227
|
bot: !!options.bot,
|
|
222
|
-
});
|
|
228
|
+
}, relays);
|
|
223
229
|
await publishToRelays(kind0, relays);
|
|
224
230
|
k0Spinner.succeed('Kind 0 published — visible on all Nostr clients');
|
|
225
231
|
}
|
|
@@ -257,18 +263,18 @@ program
|
|
|
257
263
|
console.log('');
|
|
258
264
|
console.log(chalk.gray(` Run ${chalk.white('agentdex claim <name>')} to get ${chalk.hex('#D4A574')('<name>@agentdex.id')}`));
|
|
259
265
|
console.log('');
|
|
260
|
-
// Publish kind 0 profile (
|
|
266
|
+
// Publish kind 0 profile (fetch existing, merge, republish)
|
|
261
267
|
// Kind 0 is canonical for basic profile; kind 31339 is agent-specific metadata
|
|
262
268
|
const k0Spinner = ora('Publishing kind 0 profile to Nostr relays...').start();
|
|
263
269
|
try {
|
|
264
|
-
const kind0 =
|
|
270
|
+
const kind0 = await buildKind0Event(sk, {
|
|
265
271
|
name,
|
|
266
272
|
about: description || undefined,
|
|
267
273
|
picture: options.avatar || undefined,
|
|
268
274
|
lud16: options.lightning || undefined,
|
|
269
275
|
ownerPubkeyHex,
|
|
270
276
|
bot: !!options.bot,
|
|
271
|
-
});
|
|
277
|
+
}, relays);
|
|
272
278
|
await publishToRelays(kind0, relays);
|
|
273
279
|
k0Spinner.succeed('Kind 0 published — visible on all Nostr clients');
|
|
274
280
|
}
|
|
@@ -325,14 +331,14 @@ program
|
|
|
325
331
|
if (!options.skipKind0) {
|
|
326
332
|
const k0Spinner = ora('Publishing kind 0 profile to Nostr relays...').start();
|
|
327
333
|
try {
|
|
328
|
-
const
|
|
334
|
+
const relays = ['wss://nos.lol', 'wss://relay.damus.io', ...(options.relay || [])];
|
|
335
|
+
const kind0 = await buildKind0Event(sk, {
|
|
329
336
|
name: claim.agent?.name || name,
|
|
330
337
|
about: claim.agent?.description || undefined,
|
|
331
338
|
picture: claim.agent?.avatarUrl || undefined,
|
|
332
339
|
nip05: `${name}@agentdex.id`,
|
|
333
340
|
lud16: options.lightning || undefined,
|
|
334
|
-
});
|
|
335
|
-
const relays = ['wss://nos.lol', 'wss://relay.damus.io', ...(options.relay || [])];
|
|
341
|
+
}, relays);
|
|
336
342
|
const published = await publishToRelays(kind0, relays);
|
|
337
343
|
k0Spinner.succeed(`Kind 0 published to ${published.join(', ')}`);
|
|
338
344
|
console.log(chalk.gray(' NIP-05 will appear on njump/Damus/Primal once relays propagate (~30s)'));
|
|
@@ -392,8 +398,8 @@ program
|
|
|
392
398
|
if (!options.skipKind0) {
|
|
393
399
|
const k0Spinner = ora('Publishing kind 0 profile to Nostr relays...').start();
|
|
394
400
|
try {
|
|
395
|
-
const kind0 = createKind0Event(sk, { name, nip05: `${name}@agentdex.id` });
|
|
396
401
|
const relays = ['wss://nos.lol', 'wss://relay.damus.io', ...(options.relay || [])];
|
|
402
|
+
const kind0 = await buildKind0Event(sk, { name, nip05: `${name}@agentdex.id` }, relays);
|
|
397
403
|
await publishToRelays(kind0, relays);
|
|
398
404
|
k0Spinner.succeed('Kind 0 published — NIP-05 active on all Nostr clients');
|
|
399
405
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -11,5 +11,5 @@
|
|
|
11
11
|
*/
|
|
12
12
|
export { AgentdexClient } from './client.js';
|
|
13
13
|
export type { AgentdexConfig, RegisterOptions, VerifyResult, ClaimResult, ClaimStatus, SearchOptions, } from './client.js';
|
|
14
|
-
export { parseSecretKey, getNpub, getPubkeyHex, createProfileEvent,
|
|
14
|
+
export { parseSecretKey, getNpub, getPubkeyHex, createProfileEvent, buildKind0Event, publishToRelays, createNote, } from './nostr.js';
|
|
15
15
|
export type { AgentProfile } from './nostr.js';
|
package/dist/index.js
CHANGED
|
@@ -10,4 +10,4 @@
|
|
|
10
10
|
* ```
|
|
11
11
|
*/
|
|
12
12
|
export { AgentdexClient } from './client.js';
|
|
13
|
-
export { parseSecretKey, getNpub, getPubkeyHex, createProfileEvent,
|
|
13
|
+
export { parseSecretKey, getNpub, getPubkeyHex, createProfileEvent, buildKind0Event, publishToRelays, createNote, } from './nostr.js';
|
package/dist/nostr.d.ts
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
* Nostr utilities — event creation, signing, publishing
|
|
3
3
|
*/
|
|
4
4
|
export interface PortfolioItem {
|
|
5
|
+
id: string;
|
|
5
6
|
url: string;
|
|
6
7
|
name?: string;
|
|
7
8
|
description?: string;
|
|
@@ -64,15 +65,20 @@ export declare function updateKind0(sk: Uint8Array, updates: {
|
|
|
64
65
|
* After claiming a NIP-05 name, publish this to relays so Nostr clients
|
|
65
66
|
* (njump, Damus, Primal) can verify the identity.
|
|
66
67
|
*/
|
|
67
|
-
|
|
68
|
-
|
|
68
|
+
/**
|
|
69
|
+
* Fetch existing kind 0, merge explicit updates, and return signed event.
|
|
70
|
+
* Safe for existing Nostr users — only overwrites fields explicitly passed.
|
|
71
|
+
*/
|
|
72
|
+
export declare function buildKind0Event(sk: Uint8Array, updates: {
|
|
73
|
+
name?: string;
|
|
69
74
|
about?: string;
|
|
70
75
|
nip05?: string;
|
|
71
76
|
picture?: string;
|
|
72
77
|
lud16?: string;
|
|
78
|
+
website?: string;
|
|
73
79
|
ownerPubkeyHex?: string;
|
|
74
80
|
bot?: boolean;
|
|
75
|
-
}): import("nostr-tools").VerifiedEvent
|
|
81
|
+
}, relays?: string[]): Promise<import("nostr-tools").VerifiedEvent>;
|
|
76
82
|
/**
|
|
77
83
|
* Create and sign a kind 1 note tagged #agentdex
|
|
78
84
|
*/
|
package/dist/nostr.js
CHANGED
|
@@ -93,7 +93,8 @@ export function createProfileEvent(sk, profile) {
|
|
|
93
93
|
tags.push(['messaging_fee', String(profile.messagingFee)]);
|
|
94
94
|
if (profile.portfolio) {
|
|
95
95
|
for (const item of profile.portfolio) {
|
|
96
|
-
|
|
96
|
+
// New format: ["portfolio", id, url, label, description]
|
|
97
|
+
const tag = ['portfolio', item.id, item.url];
|
|
97
98
|
if (item.name)
|
|
98
99
|
tag.push(item.name);
|
|
99
100
|
if (item.description)
|
|
@@ -185,24 +186,79 @@ export async function updateKind0(sk, updates, relays = DEFAULT_RELAYS) {
|
|
|
185
186
|
* After claiming a NIP-05 name, publish this to relays so Nostr clients
|
|
186
187
|
* (njump, Damus, Primal) can verify the identity.
|
|
187
188
|
*/
|
|
188
|
-
|
|
189
|
+
/**
|
|
190
|
+
* Fetch existing kind 0, merge explicit updates, and return signed event.
|
|
191
|
+
* Safe for existing Nostr users — only overwrites fields explicitly passed.
|
|
192
|
+
*/
|
|
193
|
+
export async function buildKind0Event(sk, updates, relays = DEFAULT_RELAYS) {
|
|
194
|
+
const pool = new SimplePool();
|
|
195
|
+
const pubkey = getPublicKey(sk);
|
|
196
|
+
// Fetch existing kind 0 from relays
|
|
197
|
+
let existing = {};
|
|
198
|
+
let existingTags = [];
|
|
199
|
+
try {
|
|
200
|
+
const events = await Promise.race([
|
|
201
|
+
pool.querySync(relays, { kinds: [0], authors: [pubkey] }),
|
|
202
|
+
new Promise((resolve) => setTimeout(() => resolve([]), 5000)),
|
|
203
|
+
]);
|
|
204
|
+
if (events.length > 0) {
|
|
205
|
+
// Get newest event
|
|
206
|
+
const newest = events.reduce((a, b) => a.created_at > b.created_at ? a : b);
|
|
207
|
+
existing = JSON.parse(newest.content);
|
|
208
|
+
existingTags = newest.tags || [];
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
catch { }
|
|
212
|
+
pool.close(relays);
|
|
213
|
+
// Merge content fields — only overwrite if explicitly provided (not undefined)
|
|
214
|
+
if (updates.name !== undefined)
|
|
215
|
+
existing.name = updates.name;
|
|
216
|
+
if (updates.about !== undefined)
|
|
217
|
+
existing.about = updates.about;
|
|
218
|
+
if (updates.nip05 !== undefined)
|
|
219
|
+
existing.nip05 = updates.nip05;
|
|
220
|
+
if (updates.picture !== undefined)
|
|
221
|
+
existing.picture = updates.picture;
|
|
222
|
+
if (updates.lud16 !== undefined)
|
|
223
|
+
existing.lud16 = updates.lud16;
|
|
224
|
+
if (updates.website !== undefined)
|
|
225
|
+
existing.website = updates.website;
|
|
226
|
+
// Merge tags
|
|
189
227
|
const tags = [];
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
228
|
+
// Preserve existing tags that we don't manage (e.g., user's own tags)
|
|
229
|
+
for (const tag of existingTags) {
|
|
230
|
+
// Skip tags we manage — we'll re-add them below
|
|
231
|
+
if (tag[0] === 'p' && tag[3] === 'owner')
|
|
232
|
+
continue;
|
|
233
|
+
if (tag[0] === 'bot')
|
|
234
|
+
continue;
|
|
235
|
+
tags.push(tag);
|
|
236
|
+
}
|
|
237
|
+
// Add/update owner p tag
|
|
238
|
+
if (updates.ownerPubkeyHex) {
|
|
239
|
+
tags.push(['p', updates.ownerPubkeyHex, '', 'owner']);
|
|
240
|
+
}
|
|
241
|
+
else {
|
|
242
|
+
// Preserve existing owner p tag if we're not updating it
|
|
243
|
+
const existingOwner = existingTags.find(t => t[0] === 'p' && t[3] === 'owner');
|
|
244
|
+
if (existingOwner)
|
|
245
|
+
tags.push(existingOwner);
|
|
246
|
+
}
|
|
247
|
+
// Add bot tag only if explicitly requested
|
|
248
|
+
if (updates.bot) {
|
|
249
|
+
tags.push(['bot']);
|
|
250
|
+
}
|
|
251
|
+
else {
|
|
252
|
+
// Preserve existing bot tag if we're not changing it
|
|
253
|
+
const existingBot = existingTags.find(t => t[0] === 'bot');
|
|
254
|
+
if (existingBot)
|
|
255
|
+
tags.push(existingBot);
|
|
194
256
|
}
|
|
195
257
|
return finalizeEvent({
|
|
196
258
|
kind: 0,
|
|
197
259
|
created_at: Math.floor(Date.now() / 1000),
|
|
198
260
|
tags,
|
|
199
|
-
content: JSON.stringify(
|
|
200
|
-
name: profile.name,
|
|
201
|
-
...(profile.about && { about: profile.about }),
|
|
202
|
-
...(profile.nip05 && { nip05: profile.nip05 }),
|
|
203
|
-
...(profile.picture && { picture: profile.picture }),
|
|
204
|
-
...(profile.lud16 && { lud16: profile.lud16 }),
|
|
205
|
-
}),
|
|
261
|
+
content: JSON.stringify(existing),
|
|
206
262
|
}, sk);
|
|
207
263
|
}
|
|
208
264
|
/**
|
package/package.json
CHANGED
package/src/cli.ts
CHANGED
|
@@ -10,7 +10,7 @@ import { fileURLToPath } from 'url';
|
|
|
10
10
|
import { dirname, join } from 'path';
|
|
11
11
|
import { nip19 } from 'nostr-tools';
|
|
12
12
|
import { AgentdexClient } from './client.js';
|
|
13
|
-
import { parseSecretKey, getNpub, getPubkeyHex, createProfileEvent,
|
|
13
|
+
import { parseSecretKey, getNpub, getPubkeyHex, createProfileEvent, buildKind0Event, publishToRelays, createNote, updateKind0, generateAndSaveKeypair } from './nostr.js';
|
|
14
14
|
import { payInvoice } from './nwc.js';
|
|
15
15
|
|
|
16
16
|
const __filename = fileURLToPath(import.meta.url);
|
|
@@ -78,7 +78,7 @@ program
|
|
|
78
78
|
.option('--owner-type <type>', 'Owner type: human, agent, org (sets owner_type tag on kind 31339)')
|
|
79
79
|
.option('--parent <npub-or-hex>', 'Parent/orchestrator agent pubkey (npub or hex)')
|
|
80
80
|
.option('--bot', 'Add ["bot"] tag to kind 0 profile (declares this pubkey as automated)')
|
|
81
|
-
.option('--portfolio <entry>', 'Portfolio
|
|
81
|
+
.option('--portfolio <entry>', 'Portfolio entry (format: "id,url,label,description") — repeatable', (val: string, acc: string[]) => [...acc, val], [])
|
|
82
82
|
.option('--skill <skill>', 'Skill tag (repeatable)', (val: string, acc: string[]) => [...acc, val], [])
|
|
83
83
|
.option('--experience <exp>', 'Experience tag (repeatable)', (val: string, acc: string[]) => [...acc, val], [])
|
|
84
84
|
.option('--nwc <uri>', 'Nostr Wallet Connect URI for auto-pay')
|
|
@@ -112,10 +112,16 @@ program
|
|
|
112
112
|
|
|
113
113
|
const spinner = ora('Signing event...').start();
|
|
114
114
|
|
|
115
|
-
// Parse portfolio entries ("url,
|
|
115
|
+
// Parse portfolio entries ("id,url,label,description")
|
|
116
116
|
const portfolio = (options.portfolio || []).map((entry: string) => {
|
|
117
117
|
const parts = entry.split(',').map((s: string) => s.trim());
|
|
118
|
-
|
|
118
|
+
// Support both: "id,url,label,desc" (new) and "url,label,desc" (old)
|
|
119
|
+
if (parts.length >= 2 && parts[1]?.startsWith('http')) {
|
|
120
|
+
return { id: parts[0], url: parts[1], name: parts[2], description: parts[3] };
|
|
121
|
+
}
|
|
122
|
+
// Old format fallback
|
|
123
|
+
const id = (parts[1] || parts[0]).toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/(^-|-$)/g, '');
|
|
124
|
+
return { id, url: parts[0], name: parts[1], description: parts[2] };
|
|
119
125
|
});
|
|
120
126
|
|
|
121
127
|
// Resolve owner pubkey hex from --owner flag (npub or hex)
|
|
@@ -227,17 +233,17 @@ program
|
|
|
227
233
|
console.log('');
|
|
228
234
|
console.log(chalk.gray(` Run ${chalk.white('agentdex claim <name>')} to get ${chalk.hex('#D4A574')('<name>@agentdex.id')}`));
|
|
229
235
|
|
|
230
|
-
// Publish kind 0 profile (
|
|
236
|
+
// Publish kind 0 profile (fetch existing, merge, republish)
|
|
231
237
|
const k0Spinner = ora('Publishing kind 0 profile to Nostr relays...').start();
|
|
232
238
|
try {
|
|
233
|
-
const kind0 =
|
|
239
|
+
const kind0 = await buildKind0Event(sk, {
|
|
234
240
|
name,
|
|
235
241
|
about: description || undefined,
|
|
236
242
|
picture: options.avatar || undefined,
|
|
237
243
|
lud16: options.lightning || undefined,
|
|
238
244
|
ownerPubkeyHex,
|
|
239
245
|
bot: !!options.bot,
|
|
240
|
-
});
|
|
246
|
+
}, relays);
|
|
241
247
|
await publishToRelays(kind0, relays);
|
|
242
248
|
k0Spinner.succeed('Kind 0 published — visible on all Nostr clients');
|
|
243
249
|
} catch {
|
|
@@ -277,18 +283,18 @@ program
|
|
|
277
283
|
console.log('');
|
|
278
284
|
console.log(chalk.gray(` Run ${chalk.white('agentdex claim <name>')} to get ${chalk.hex('#D4A574')('<name>@agentdex.id')}`));
|
|
279
285
|
console.log('');
|
|
280
|
-
// Publish kind 0 profile (
|
|
286
|
+
// Publish kind 0 profile (fetch existing, merge, republish)
|
|
281
287
|
// Kind 0 is canonical for basic profile; kind 31339 is agent-specific metadata
|
|
282
288
|
const k0Spinner = ora('Publishing kind 0 profile to Nostr relays...').start();
|
|
283
289
|
try {
|
|
284
|
-
const kind0 =
|
|
290
|
+
const kind0 = await buildKind0Event(sk, {
|
|
285
291
|
name,
|
|
286
292
|
about: description || undefined,
|
|
287
293
|
picture: options.avatar || undefined,
|
|
288
294
|
lud16: options.lightning || undefined,
|
|
289
295
|
ownerPubkeyHex,
|
|
290
296
|
bot: !!options.bot,
|
|
291
|
-
});
|
|
297
|
+
}, relays);
|
|
292
298
|
await publishToRelays(kind0, relays);
|
|
293
299
|
k0Spinner.succeed('Kind 0 published — visible on all Nostr clients');
|
|
294
300
|
} catch {
|
|
@@ -349,14 +355,14 @@ program
|
|
|
349
355
|
if (!options.skipKind0) {
|
|
350
356
|
const k0Spinner = ora('Publishing kind 0 profile to Nostr relays...').start();
|
|
351
357
|
try {
|
|
352
|
-
const
|
|
358
|
+
const relays = ['wss://nos.lol', 'wss://relay.damus.io', ...(options.relay || [])];
|
|
359
|
+
const kind0 = await buildKind0Event(sk, {
|
|
353
360
|
name: claim.agent?.name || name,
|
|
354
361
|
about: claim.agent?.description || undefined,
|
|
355
362
|
picture: claim.agent?.avatarUrl || undefined,
|
|
356
363
|
nip05: `${name}@agentdex.id`,
|
|
357
364
|
lud16: options.lightning || undefined,
|
|
358
|
-
});
|
|
359
|
-
const relays = ['wss://nos.lol', 'wss://relay.damus.io', ...(options.relay || [])];
|
|
365
|
+
}, relays);
|
|
360
366
|
const published = await publishToRelays(kind0, relays);
|
|
361
367
|
k0Spinner.succeed(`Kind 0 published to ${published.join(', ')}`);
|
|
362
368
|
console.log(chalk.gray(' NIP-05 will appear on njump/Damus/Primal once relays propagate (~30s)'));
|
|
@@ -419,8 +425,8 @@ program
|
|
|
419
425
|
if (!options.skipKind0) {
|
|
420
426
|
const k0Spinner = ora('Publishing kind 0 profile to Nostr relays...').start();
|
|
421
427
|
try {
|
|
422
|
-
const kind0 = createKind0Event(sk, { name, nip05: `${name}@agentdex.id` });
|
|
423
428
|
const relays = ['wss://nos.lol', 'wss://relay.damus.io', ...(options.relay || [])];
|
|
429
|
+
const kind0 = await buildKind0Event(sk, { name, nip05: `${name}@agentdex.id` }, relays);
|
|
424
430
|
await publishToRelays(kind0, relays);
|
|
425
431
|
k0Spinner.succeed('Kind 0 published — NIP-05 active on all Nostr clients');
|
|
426
432
|
} catch {
|
package/src/index.ts
CHANGED
package/src/nostr.ts
CHANGED
|
@@ -10,6 +10,7 @@ import { mkdirSync, writeFileSync } from 'fs';
|
|
|
10
10
|
const DEFAULT_RELAYS = ['wss://nos.lol', 'wss://relay.damus.io'];
|
|
11
11
|
|
|
12
12
|
export interface PortfolioItem {
|
|
13
|
+
id: string;
|
|
13
14
|
url: string;
|
|
14
15
|
name?: string;
|
|
15
16
|
description?: string;
|
|
@@ -115,7 +116,8 @@ export function createProfileEvent(sk: Uint8Array, profile: AgentProfile) {
|
|
|
115
116
|
if (profile.messagingFee) tags.push(['messaging_fee', String(profile.messagingFee)]);
|
|
116
117
|
if (profile.portfolio) {
|
|
117
118
|
for (const item of profile.portfolio) {
|
|
118
|
-
|
|
119
|
+
// New format: ["portfolio", id, url, label, description]
|
|
120
|
+
const tag = ['portfolio', item.id, item.url];
|
|
119
121
|
if (item.name) tag.push(item.name);
|
|
120
122
|
if (item.description) tag.push(item.description);
|
|
121
123
|
tags.push(tag);
|
|
@@ -215,23 +217,82 @@ export async function updateKind0(sk: Uint8Array, updates: { lud16?: string }, r
|
|
|
215
217
|
* After claiming a NIP-05 name, publish this to relays so Nostr clients
|
|
216
218
|
* (njump, Damus, Primal) can verify the identity.
|
|
217
219
|
*/
|
|
218
|
-
|
|
220
|
+
/**
|
|
221
|
+
* Fetch existing kind 0, merge explicit updates, and return signed event.
|
|
222
|
+
* Safe for existing Nostr users — only overwrites fields explicitly passed.
|
|
223
|
+
*/
|
|
224
|
+
export async function buildKind0Event(sk: Uint8Array, updates: {
|
|
225
|
+
name?: string;
|
|
226
|
+
about?: string;
|
|
227
|
+
nip05?: string;
|
|
228
|
+
picture?: string;
|
|
229
|
+
lud16?: string;
|
|
230
|
+
website?: string;
|
|
231
|
+
ownerPubkeyHex?: string;
|
|
232
|
+
bot?: boolean;
|
|
233
|
+
}, relays: string[] = DEFAULT_RELAYS) {
|
|
234
|
+
const pool = new SimplePool();
|
|
235
|
+
const pubkey = getPublicKey(sk);
|
|
236
|
+
|
|
237
|
+
// Fetch existing kind 0 from relays
|
|
238
|
+
let existing: Record<string, unknown> = {};
|
|
239
|
+
let existingTags: string[][] = [];
|
|
240
|
+
try {
|
|
241
|
+
const events = await Promise.race([
|
|
242
|
+
pool.querySync(relays, { kinds: [0], authors: [pubkey] }),
|
|
243
|
+
new Promise<any[]>((resolve) => setTimeout(() => resolve([]), 5000)),
|
|
244
|
+
]);
|
|
245
|
+
if (events.length > 0) {
|
|
246
|
+
// Get newest event
|
|
247
|
+
const newest = events.reduce((a: any, b: any) => a.created_at > b.created_at ? a : b);
|
|
248
|
+
existing = JSON.parse(newest.content);
|
|
249
|
+
existingTags = newest.tags || [];
|
|
250
|
+
}
|
|
251
|
+
} catch {}
|
|
252
|
+
pool.close(relays);
|
|
253
|
+
|
|
254
|
+
// Merge content fields — only overwrite if explicitly provided (not undefined)
|
|
255
|
+
if (updates.name !== undefined) existing.name = updates.name;
|
|
256
|
+
if (updates.about !== undefined) existing.about = updates.about;
|
|
257
|
+
if (updates.nip05 !== undefined) existing.nip05 = updates.nip05;
|
|
258
|
+
if (updates.picture !== undefined) existing.picture = updates.picture;
|
|
259
|
+
if (updates.lud16 !== undefined) existing.lud16 = updates.lud16;
|
|
260
|
+
if (updates.website !== undefined) existing.website = updates.website;
|
|
261
|
+
|
|
262
|
+
// Merge tags
|
|
219
263
|
const tags: string[][] = [];
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
264
|
+
|
|
265
|
+
// Preserve existing tags that we don't manage (e.g., user's own tags)
|
|
266
|
+
for (const tag of existingTags) {
|
|
267
|
+
// Skip tags we manage — we'll re-add them below
|
|
268
|
+
if (tag[0] === 'p' && tag[3] === 'owner') continue;
|
|
269
|
+
if (tag[0] === 'bot') continue;
|
|
270
|
+
tags.push(tag);
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
// Add/update owner p tag
|
|
274
|
+
if (updates.ownerPubkeyHex) {
|
|
275
|
+
tags.push(['p', updates.ownerPubkeyHex, '', 'owner']);
|
|
276
|
+
} else {
|
|
277
|
+
// Preserve existing owner p tag if we're not updating it
|
|
278
|
+
const existingOwner = existingTags.find(t => t[0] === 'p' && t[3] === 'owner');
|
|
279
|
+
if (existingOwner) tags.push(existingOwner);
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
// Add bot tag only if explicitly requested
|
|
283
|
+
if (updates.bot) {
|
|
284
|
+
tags.push(['bot']);
|
|
285
|
+
} else {
|
|
286
|
+
// Preserve existing bot tag if we're not changing it
|
|
287
|
+
const existingBot = existingTags.find(t => t[0] === 'bot');
|
|
288
|
+
if (existingBot) tags.push(existingBot);
|
|
223
289
|
}
|
|
290
|
+
|
|
224
291
|
return finalizeEvent({
|
|
225
292
|
kind: 0,
|
|
226
293
|
created_at: Math.floor(Date.now() / 1000),
|
|
227
294
|
tags,
|
|
228
|
-
content: JSON.stringify(
|
|
229
|
-
name: profile.name,
|
|
230
|
-
...(profile.about && { about: profile.about }),
|
|
231
|
-
...(profile.nip05 && { nip05: profile.nip05 }),
|
|
232
|
-
...(profile.picture && { picture: profile.picture }),
|
|
233
|
-
...(profile.lud16 && { lud16: profile.lud16 }),
|
|
234
|
-
}),
|
|
295
|
+
content: JSON.stringify(existing),
|
|
235
296
|
}, sk);
|
|
236
297
|
}
|
|
237
298
|
|