agentdex-cli 0.3.2 → 0.4.0
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 +35 -3
- package/dist/nostr.d.ts +3 -1
- package/dist/nostr.js +11 -5
- package/package.json +1 -1
- package/src/cli.ts +35 -3
- package/src/nostr.ts +11 -6
package/dist/cli.js
CHANGED
|
@@ -5,14 +5,20 @@ import ora from 'ora';
|
|
|
5
5
|
import inquirer from 'inquirer';
|
|
6
6
|
import qrcode from 'qrcode-terminal';
|
|
7
7
|
import { readFileSync } from 'fs';
|
|
8
|
+
import { fileURLToPath } from 'url';
|
|
9
|
+
import { dirname, join } from 'path';
|
|
10
|
+
import { nip19 } from 'nostr-tools';
|
|
8
11
|
import { AgentdexClient } from './client.js';
|
|
9
12
|
import { parseSecretKey, getNpub, getPubkeyHex, createProfileEvent, createKind0Event, publishToRelays, createNote, generateAndSaveKeypair } from './nostr.js';
|
|
10
13
|
import { payInvoice } from './nwc.js';
|
|
14
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
15
|
+
const __dirname = dirname(__filename);
|
|
16
|
+
const pkg = JSON.parse(readFileSync(join(__dirname, '..', 'package.json'), 'utf-8'));
|
|
11
17
|
const program = new Command();
|
|
12
18
|
program
|
|
13
19
|
.name('agentdex')
|
|
14
20
|
.description('CLI for the agentdex AI agent directory')
|
|
15
|
-
.version(
|
|
21
|
+
.version(pkg.version);
|
|
16
22
|
/**
|
|
17
23
|
* Resolve secret key from flags, env, or key file
|
|
18
24
|
*/
|
|
@@ -60,6 +66,8 @@ program
|
|
|
60
66
|
.option('--avatar <url>', 'Avatar image URL (sets picture in kind 0 profile)')
|
|
61
67
|
.option('--lightning <addr>', 'Lightning address (sets lud16 in kind 0 profile)')
|
|
62
68
|
.option('--owner-x <handle>', 'Owner X/Twitter handle (e.g., @username)')
|
|
69
|
+
.option('--human <npub-or-hex>', 'Owner/operator Nostr pubkey (npub or hex) — enables bidirectional verification')
|
|
70
|
+
.option('--bot', 'Add ["bot"] tag to kind 0 profile (declares this pubkey as automated)')
|
|
63
71
|
.option('--portfolio <entry>', 'Portfolio URL (format: "url,name,description") — repeatable', (val, acc) => [...acc, val], [])
|
|
64
72
|
.option('--skill <skill>', 'Skill tag (repeatable)', (val, acc) => [...acc, val], [])
|
|
65
73
|
.option('--experience <exp>', 'Experience tag (repeatable)', (val, acc) => [...acc, val], [])
|
|
@@ -95,6 +103,25 @@ program
|
|
|
95
103
|
const parts = entry.split(',').map((s) => s.trim());
|
|
96
104
|
return { url: parts[0], name: parts[1], description: parts[2] };
|
|
97
105
|
});
|
|
106
|
+
// Resolve owner pubkey hex from --human flag (npub or hex)
|
|
107
|
+
let humanNpub = options.human;
|
|
108
|
+
let ownerPubkeyHex;
|
|
109
|
+
if (humanNpub) {
|
|
110
|
+
if (humanNpub.startsWith('npub')) {
|
|
111
|
+
try {
|
|
112
|
+
const decoded = nip19.decode(humanNpub);
|
|
113
|
+
ownerPubkeyHex = decoded.data;
|
|
114
|
+
}
|
|
115
|
+
catch {
|
|
116
|
+
console.error(chalk.red('Invalid --human npub'));
|
|
117
|
+
process.exit(1);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
else {
|
|
121
|
+
ownerPubkeyHex = humanNpub;
|
|
122
|
+
humanNpub = nip19.npubEncode(humanNpub);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
98
125
|
const event = createProfileEvent(sk, {
|
|
99
126
|
name,
|
|
100
127
|
description,
|
|
@@ -103,6 +130,7 @@ program
|
|
|
103
130
|
model: options.model,
|
|
104
131
|
website: options.website,
|
|
105
132
|
lightning: options.lightning,
|
|
133
|
+
human: humanNpub,
|
|
106
134
|
ownerX: options.ownerX,
|
|
107
135
|
status: 'active',
|
|
108
136
|
portfolio: portfolio.length > 0 ? portfolio : undefined,
|
|
@@ -171,6 +199,8 @@ program
|
|
|
171
199
|
about: description || undefined,
|
|
172
200
|
picture: options.avatar || undefined,
|
|
173
201
|
lud16: options.lightning || undefined,
|
|
202
|
+
ownerPubkeyHex,
|
|
203
|
+
bot: !!options.bot,
|
|
174
204
|
});
|
|
175
205
|
await publishToRelays(kind0, relays);
|
|
176
206
|
k0Spinner.succeed('Kind 0 published — visible on all Nostr clients');
|
|
@@ -210,7 +240,7 @@ program
|
|
|
210
240
|
console.log(chalk.gray(` Run ${chalk.white('agentdex claim <name>')} to get ${chalk.hex('#D4A574')('<name>@agentdex.id')}`));
|
|
211
241
|
console.log('');
|
|
212
242
|
// Publish kind 0 profile (name, about, avatar, website, lud16)
|
|
213
|
-
// Kind 0 is canonical for basic profile; kind
|
|
243
|
+
// Kind 0 is canonical for basic profile; kind 31339 is agent-specific metadata
|
|
214
244
|
const k0Spinner = ora('Publishing kind 0 profile to Nostr relays...').start();
|
|
215
245
|
try {
|
|
216
246
|
const kind0 = createKind0Event(sk, {
|
|
@@ -218,6 +248,8 @@ program
|
|
|
218
248
|
about: description || undefined,
|
|
219
249
|
picture: options.avatar || undefined,
|
|
220
250
|
lud16: options.lightning || undefined,
|
|
251
|
+
ownerPubkeyHex,
|
|
252
|
+
bot: !!options.bot,
|
|
221
253
|
});
|
|
222
254
|
await publishToRelays(kind0, relays);
|
|
223
255
|
k0Spinner.succeed('Kind 0 published — visible on all Nostr clients');
|
|
@@ -262,7 +294,7 @@ program
|
|
|
262
294
|
const sk = resolveKey(options);
|
|
263
295
|
const client = new AgentdexClient({ apiKey: options.apiKey });
|
|
264
296
|
const spinner = ora(`Claiming ${name}@agentdex.id...`).start();
|
|
265
|
-
// Sign a kind
|
|
297
|
+
// Sign a kind 31339 event for claim authentication
|
|
266
298
|
const event = createProfileEvent(sk, {
|
|
267
299
|
name,
|
|
268
300
|
status: 'active',
|
package/dist/nostr.d.ts
CHANGED
|
@@ -47,7 +47,7 @@ export declare function getNpub(sk: Uint8Array): string;
|
|
|
47
47
|
*/
|
|
48
48
|
export declare function getPubkeyHex(sk: Uint8Array): string;
|
|
49
49
|
/**
|
|
50
|
-
* Build and sign a kind
|
|
50
|
+
* Build and sign a kind 31339 agent profile event
|
|
51
51
|
*/
|
|
52
52
|
export declare function createProfileEvent(sk: Uint8Array, profile: AgentProfile): import("nostr-tools").VerifiedEvent;
|
|
53
53
|
/**
|
|
@@ -72,6 +72,8 @@ export declare function createKind0Event(sk: Uint8Array, profile: {
|
|
|
72
72
|
nip05?: string;
|
|
73
73
|
picture?: string;
|
|
74
74
|
lud16?: string;
|
|
75
|
+
ownerPubkeyHex?: string;
|
|
76
|
+
bot?: boolean;
|
|
75
77
|
}): import("nostr-tools").VerifiedEvent;
|
|
76
78
|
/**
|
|
77
79
|
* Create and sign a kind 1 note tagged #agentdex
|
package/dist/nostr.js
CHANGED
|
@@ -55,13 +55,13 @@ export function getPubkeyHex(sk) {
|
|
|
55
55
|
return getPublicKey(sk);
|
|
56
56
|
}
|
|
57
57
|
/**
|
|
58
|
-
* Build and sign a kind
|
|
58
|
+
* Build and sign a kind 31339 agent profile event
|
|
59
59
|
*/
|
|
60
60
|
export function createProfileEvent(sk, profile) {
|
|
61
61
|
const tags = [
|
|
62
62
|
['d', 'agentdex-profile'],
|
|
63
63
|
];
|
|
64
|
-
// name is optional in kind
|
|
64
|
+
// name is optional in kind 31339 (canonical source is kind 0)
|
|
65
65
|
// included for backward compatibility and standalone profiles
|
|
66
66
|
if (profile.name)
|
|
67
67
|
tags.push(['name', profile.name]);
|
|
@@ -80,7 +80,7 @@ export function createProfileEvent(sk, profile) {
|
|
|
80
80
|
tags.push(['website', profile.website]);
|
|
81
81
|
if (profile.avatar)
|
|
82
82
|
tags.push(['avatar', profile.avatar]);
|
|
83
|
-
// Lightning address belongs in kind 0 (lud16), not kind
|
|
83
|
+
// Lightning address belongs in kind 0 (lud16), not kind 31339
|
|
84
84
|
// Use --lightning flag to set lud16 in kind 0 during claim
|
|
85
85
|
if (profile.human)
|
|
86
86
|
tags.push(['human', profile.human]);
|
|
@@ -115,7 +115,7 @@ export function createProfileEvent(sk, profile) {
|
|
|
115
115
|
}
|
|
116
116
|
}
|
|
117
117
|
const event = finalizeEvent({
|
|
118
|
-
kind:
|
|
118
|
+
kind: 31339,
|
|
119
119
|
created_at: Math.floor(Date.now() / 1000),
|
|
120
120
|
tags,
|
|
121
121
|
content: '',
|
|
@@ -189,10 +189,16 @@ export async function updateKind0(sk, updates, relays = DEFAULT_RELAYS) {
|
|
|
189
189
|
* (njump, Damus, Primal) can verify the identity.
|
|
190
190
|
*/
|
|
191
191
|
export function createKind0Event(sk, profile) {
|
|
192
|
+
const tags = [];
|
|
193
|
+
if (profile.bot)
|
|
194
|
+
tags.push(["bot"]);
|
|
195
|
+
if (profile.ownerPubkeyHex) {
|
|
196
|
+
tags.push(["p", profile.ownerPubkeyHex, "", "owner"]);
|
|
197
|
+
}
|
|
192
198
|
return finalizeEvent({
|
|
193
199
|
kind: 0,
|
|
194
200
|
created_at: Math.floor(Date.now() / 1000),
|
|
195
|
-
tags
|
|
201
|
+
tags,
|
|
196
202
|
content: JSON.stringify({
|
|
197
203
|
name: profile.name,
|
|
198
204
|
...(profile.about && { about: profile.about }),
|
package/package.json
CHANGED
package/src/cli.ts
CHANGED
|
@@ -6,16 +6,23 @@ import ora from 'ora';
|
|
|
6
6
|
import inquirer from 'inquirer';
|
|
7
7
|
import qrcode from 'qrcode-terminal';
|
|
8
8
|
import { readFileSync } from 'fs';
|
|
9
|
+
import { fileURLToPath } from 'url';
|
|
10
|
+
import { dirname, join } from 'path';
|
|
11
|
+
import { nip19 } from 'nostr-tools';
|
|
9
12
|
import { AgentdexClient } from './client.js';
|
|
10
13
|
import { parseSecretKey, getNpub, getPubkeyHex, createProfileEvent, createKind0Event, publishToRelays, createNote, updateKind0, generateAndSaveKeypair } from './nostr.js';
|
|
11
14
|
import { payInvoice } from './nwc.js';
|
|
12
15
|
|
|
16
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
17
|
+
const __dirname = dirname(__filename);
|
|
18
|
+
const pkg = JSON.parse(readFileSync(join(__dirname, '..', 'package.json'), 'utf-8'));
|
|
19
|
+
|
|
13
20
|
const program = new Command();
|
|
14
21
|
|
|
15
22
|
program
|
|
16
23
|
.name('agentdex')
|
|
17
24
|
.description('CLI for the agentdex AI agent directory')
|
|
18
|
-
.version(
|
|
25
|
+
.version(pkg.version);
|
|
19
26
|
|
|
20
27
|
/**
|
|
21
28
|
* Resolve secret key from flags, env, or key file
|
|
@@ -67,6 +74,8 @@ program
|
|
|
67
74
|
.option('--avatar <url>', 'Avatar image URL (sets picture in kind 0 profile)')
|
|
68
75
|
.option('--lightning <addr>', 'Lightning address (sets lud16 in kind 0 profile)')
|
|
69
76
|
.option('--owner-x <handle>', 'Owner X/Twitter handle (e.g., @username)')
|
|
77
|
+
.option('--human <npub-or-hex>', 'Owner/operator Nostr pubkey (npub or hex) — enables bidirectional verification')
|
|
78
|
+
.option('--bot', 'Add ["bot"] tag to kind 0 profile (declares this pubkey as automated)')
|
|
70
79
|
.option('--portfolio <entry>', 'Portfolio URL (format: "url,name,description") — repeatable', (val: string, acc: string[]) => [...acc, val], [])
|
|
71
80
|
.option('--skill <skill>', 'Skill tag (repeatable)', (val: string, acc: string[]) => [...acc, val], [])
|
|
72
81
|
.option('--experience <exp>', 'Experience tag (repeatable)', (val: string, acc: string[]) => [...acc, val], [])
|
|
@@ -107,6 +116,24 @@ program
|
|
|
107
116
|
return { url: parts[0], name: parts[1], description: parts[2] };
|
|
108
117
|
});
|
|
109
118
|
|
|
119
|
+
// Resolve owner pubkey hex from --human flag (npub or hex)
|
|
120
|
+
let humanNpub = options.human;
|
|
121
|
+
let ownerPubkeyHex: string | undefined;
|
|
122
|
+
if (humanNpub) {
|
|
123
|
+
if (humanNpub.startsWith('npub')) {
|
|
124
|
+
try {
|
|
125
|
+
const decoded = nip19.decode(humanNpub);
|
|
126
|
+
ownerPubkeyHex = decoded.data as unknown as string;
|
|
127
|
+
} catch {
|
|
128
|
+
console.error(chalk.red('Invalid --human npub'));
|
|
129
|
+
process.exit(1);
|
|
130
|
+
}
|
|
131
|
+
} else {
|
|
132
|
+
ownerPubkeyHex = humanNpub;
|
|
133
|
+
humanNpub = nip19.npubEncode(humanNpub);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
110
137
|
const event = createProfileEvent(sk, {
|
|
111
138
|
name,
|
|
112
139
|
description,
|
|
@@ -115,6 +142,7 @@ program
|
|
|
115
142
|
model: options.model,
|
|
116
143
|
website: options.website,
|
|
117
144
|
lightning: options.lightning,
|
|
145
|
+
human: humanNpub,
|
|
118
146
|
ownerX: options.ownerX,
|
|
119
147
|
status: 'active',
|
|
120
148
|
portfolio: portfolio.length > 0 ? portfolio : undefined,
|
|
@@ -190,6 +218,8 @@ program
|
|
|
190
218
|
about: description || undefined,
|
|
191
219
|
picture: options.avatar || undefined,
|
|
192
220
|
lud16: options.lightning || undefined,
|
|
221
|
+
ownerPubkeyHex,
|
|
222
|
+
bot: !!options.bot,
|
|
193
223
|
});
|
|
194
224
|
await publishToRelays(kind0, relays);
|
|
195
225
|
k0Spinner.succeed('Kind 0 published — visible on all Nostr clients');
|
|
@@ -231,7 +261,7 @@ program
|
|
|
231
261
|
console.log(chalk.gray(` Run ${chalk.white('agentdex claim <name>')} to get ${chalk.hex('#D4A574')('<name>@agentdex.id')}`));
|
|
232
262
|
console.log('');
|
|
233
263
|
// Publish kind 0 profile (name, about, avatar, website, lud16)
|
|
234
|
-
// Kind 0 is canonical for basic profile; kind
|
|
264
|
+
// Kind 0 is canonical for basic profile; kind 31339 is agent-specific metadata
|
|
235
265
|
const k0Spinner = ora('Publishing kind 0 profile to Nostr relays...').start();
|
|
236
266
|
try {
|
|
237
267
|
const kind0 = createKind0Event(sk, {
|
|
@@ -239,6 +269,8 @@ program
|
|
|
239
269
|
about: description || undefined,
|
|
240
270
|
picture: options.avatar || undefined,
|
|
241
271
|
lud16: options.lightning || undefined,
|
|
272
|
+
ownerPubkeyHex,
|
|
273
|
+
bot: !!options.bot,
|
|
242
274
|
});
|
|
243
275
|
await publishToRelays(kind0, relays);
|
|
244
276
|
k0Spinner.succeed('Kind 0 published — visible on all Nostr clients');
|
|
@@ -284,7 +316,7 @@ program
|
|
|
284
316
|
|
|
285
317
|
const spinner = ora(`Claiming ${name}@agentdex.id...`).start();
|
|
286
318
|
|
|
287
|
-
// Sign a kind
|
|
319
|
+
// Sign a kind 31339 event for claim authentication
|
|
288
320
|
const event = createProfileEvent(sk, {
|
|
289
321
|
name,
|
|
290
322
|
status: 'active',
|
package/src/nostr.ts
CHANGED
|
@@ -89,14 +89,14 @@ export function getPubkeyHex(sk: Uint8Array): string {
|
|
|
89
89
|
}
|
|
90
90
|
|
|
91
91
|
/**
|
|
92
|
-
* Build and sign a kind
|
|
92
|
+
* Build and sign a kind 31339 agent profile event
|
|
93
93
|
*/
|
|
94
94
|
export function createProfileEvent(sk: Uint8Array, profile: AgentProfile) {
|
|
95
95
|
const tags: string[][] = [
|
|
96
96
|
['d', 'agentdex-profile'],
|
|
97
97
|
];
|
|
98
98
|
|
|
99
|
-
// name is optional in kind
|
|
99
|
+
// name is optional in kind 31339 (canonical source is kind 0)
|
|
100
100
|
// included for backward compatibility and standalone profiles
|
|
101
101
|
if (profile.name) tags.push(['name', profile.name]);
|
|
102
102
|
if (profile.description) tags.push(['description', profile.description]);
|
|
@@ -109,7 +109,7 @@ export function createProfileEvent(sk: Uint8Array, profile: AgentProfile) {
|
|
|
109
109
|
if (profile.model) tags.push(['model', profile.model]);
|
|
110
110
|
if (profile.website) tags.push(['website', profile.website]);
|
|
111
111
|
if (profile.avatar) tags.push(['avatar', profile.avatar]);
|
|
112
|
-
// Lightning address belongs in kind 0 (lud16), not kind
|
|
112
|
+
// Lightning address belongs in kind 0 (lud16), not kind 31339
|
|
113
113
|
// Use --lightning flag to set lud16 in kind 0 during claim
|
|
114
114
|
if (profile.human) tags.push(['human', profile.human]);
|
|
115
115
|
if (profile.ownerX) tags.push(['owner_x', profile.ownerX]);
|
|
@@ -137,7 +137,7 @@ export function createProfileEvent(sk: Uint8Array, profile: AgentProfile) {
|
|
|
137
137
|
}
|
|
138
138
|
|
|
139
139
|
const event = finalizeEvent({
|
|
140
|
-
kind:
|
|
140
|
+
kind: 31339,
|
|
141
141
|
created_at: Math.floor(Date.now() / 1000),
|
|
142
142
|
tags,
|
|
143
143
|
content: '',
|
|
@@ -219,11 +219,16 @@ export async function updateKind0(sk: Uint8Array, updates: { lud16?: string }, r
|
|
|
219
219
|
* After claiming a NIP-05 name, publish this to relays so Nostr clients
|
|
220
220
|
* (njump, Damus, Primal) can verify the identity.
|
|
221
221
|
*/
|
|
222
|
-
export function createKind0Event(sk: Uint8Array, profile: { name: string; about?: string; nip05?: string; picture?: string; lud16?: string }) {
|
|
222
|
+
export function createKind0Event(sk: Uint8Array, profile: { name: string; about?: string; nip05?: string; picture?: string; lud16?: string; ownerPubkeyHex?: string; bot?: boolean }) {
|
|
223
|
+
const tags: string[][] = [];
|
|
224
|
+
if (profile.bot) tags.push(["bot"]);
|
|
225
|
+
if (profile.ownerPubkeyHex) {
|
|
226
|
+
tags.push(["p", profile.ownerPubkeyHex, "", "owner"]);
|
|
227
|
+
}
|
|
223
228
|
return finalizeEvent({
|
|
224
229
|
kind: 0,
|
|
225
230
|
created_at: Math.floor(Date.now() / 1000),
|
|
226
|
-
tags
|
|
231
|
+
tags,
|
|
227
232
|
content: JSON.stringify({
|
|
228
233
|
name: profile.name,
|
|
229
234
|
...(profile.about && { about: profile.about }),
|