agentdex-cli 0.3.3 → 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 CHANGED
@@ -7,6 +7,7 @@ import qrcode from 'qrcode-terminal';
7
7
  import { readFileSync } from 'fs';
8
8
  import { fileURLToPath } from 'url';
9
9
  import { dirname, join } from 'path';
10
+ import { nip19 } from 'nostr-tools';
10
11
  import { AgentdexClient } from './client.js';
11
12
  import { parseSecretKey, getNpub, getPubkeyHex, createProfileEvent, createKind0Event, publishToRelays, createNote, generateAndSaveKeypair } from './nostr.js';
12
13
  import { payInvoice } from './nwc.js';
@@ -65,6 +66,8 @@ program
65
66
  .option('--avatar <url>', 'Avatar image URL (sets picture in kind 0 profile)')
66
67
  .option('--lightning <addr>', 'Lightning address (sets lud16 in kind 0 profile)')
67
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)')
68
71
  .option('--portfolio <entry>', 'Portfolio URL (format: "url,name,description") — repeatable', (val, acc) => [...acc, val], [])
69
72
  .option('--skill <skill>', 'Skill tag (repeatable)', (val, acc) => [...acc, val], [])
70
73
  .option('--experience <exp>', 'Experience tag (repeatable)', (val, acc) => [...acc, val], [])
@@ -100,6 +103,25 @@ program
100
103
  const parts = entry.split(',').map((s) => s.trim());
101
104
  return { url: parts[0], name: parts[1], description: parts[2] };
102
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
+ }
103
125
  const event = createProfileEvent(sk, {
104
126
  name,
105
127
  description,
@@ -108,6 +130,7 @@ program
108
130
  model: options.model,
109
131
  website: options.website,
110
132
  lightning: options.lightning,
133
+ human: humanNpub,
111
134
  ownerX: options.ownerX,
112
135
  status: 'active',
113
136
  portfolio: portfolio.length > 0 ? portfolio : undefined,
@@ -176,6 +199,8 @@ program
176
199
  about: description || undefined,
177
200
  picture: options.avatar || undefined,
178
201
  lud16: options.lightning || undefined,
202
+ ownerPubkeyHex,
203
+ bot: !!options.bot,
179
204
  });
180
205
  await publishToRelays(kind0, relays);
181
206
  k0Spinner.succeed('Kind 0 published — visible on all Nostr clients');
@@ -215,7 +240,7 @@ program
215
240
  console.log(chalk.gray(` Run ${chalk.white('agentdex claim <name>')} to get ${chalk.hex('#D4A574')('<name>@agentdex.id')}`));
216
241
  console.log('');
217
242
  // Publish kind 0 profile (name, about, avatar, website, lud16)
218
- // Kind 0 is canonical for basic profile; kind 31337 is agent-specific metadata
243
+ // Kind 0 is canonical for basic profile; kind 31339 is agent-specific metadata
219
244
  const k0Spinner = ora('Publishing kind 0 profile to Nostr relays...').start();
220
245
  try {
221
246
  const kind0 = createKind0Event(sk, {
@@ -223,6 +248,8 @@ program
223
248
  about: description || undefined,
224
249
  picture: options.avatar || undefined,
225
250
  lud16: options.lightning || undefined,
251
+ ownerPubkeyHex,
252
+ bot: !!options.bot,
226
253
  });
227
254
  await publishToRelays(kind0, relays);
228
255
  k0Spinner.succeed('Kind 0 published — visible on all Nostr clients');
@@ -267,7 +294,7 @@ program
267
294
  const sk = resolveKey(options);
268
295
  const client = new AgentdexClient({ apiKey: options.apiKey });
269
296
  const spinner = ora(`Claiming ${name}@agentdex.id...`).start();
270
- // Sign a kind 31337 event for claim authentication
297
+ // Sign a kind 31339 event for claim authentication
271
298
  const event = createProfileEvent(sk, {
272
299
  name,
273
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 31337 agent profile event
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 31337 agent profile event
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 31337 (canonical source is kind 0)
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 31337
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: 31337,
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agentdex-cli",
3
- "version": "0.3.3",
3
+ "version": "0.4.0",
4
4
  "description": "CLI and SDK for the agentdex AI agent directory",
5
5
  "type": "module",
6
6
  "bin": {
package/src/cli.ts CHANGED
@@ -8,6 +8,7 @@ import qrcode from 'qrcode-terminal';
8
8
  import { readFileSync } from 'fs';
9
9
  import { fileURLToPath } from 'url';
10
10
  import { dirname, join } from 'path';
11
+ import { nip19 } from 'nostr-tools';
11
12
  import { AgentdexClient } from './client.js';
12
13
  import { parseSecretKey, getNpub, getPubkeyHex, createProfileEvent, createKind0Event, publishToRelays, createNote, updateKind0, generateAndSaveKeypair } from './nostr.js';
13
14
  import { payInvoice } from './nwc.js';
@@ -73,6 +74,8 @@ program
73
74
  .option('--avatar <url>', 'Avatar image URL (sets picture in kind 0 profile)')
74
75
  .option('--lightning <addr>', 'Lightning address (sets lud16 in kind 0 profile)')
75
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)')
76
79
  .option('--portfolio <entry>', 'Portfolio URL (format: "url,name,description") — repeatable', (val: string, acc: string[]) => [...acc, val], [])
77
80
  .option('--skill <skill>', 'Skill tag (repeatable)', (val: string, acc: string[]) => [...acc, val], [])
78
81
  .option('--experience <exp>', 'Experience tag (repeatable)', (val: string, acc: string[]) => [...acc, val], [])
@@ -113,6 +116,24 @@ program
113
116
  return { url: parts[0], name: parts[1], description: parts[2] };
114
117
  });
115
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
+
116
137
  const event = createProfileEvent(sk, {
117
138
  name,
118
139
  description,
@@ -121,6 +142,7 @@ program
121
142
  model: options.model,
122
143
  website: options.website,
123
144
  lightning: options.lightning,
145
+ human: humanNpub,
124
146
  ownerX: options.ownerX,
125
147
  status: 'active',
126
148
  portfolio: portfolio.length > 0 ? portfolio : undefined,
@@ -196,6 +218,8 @@ program
196
218
  about: description || undefined,
197
219
  picture: options.avatar || undefined,
198
220
  lud16: options.lightning || undefined,
221
+ ownerPubkeyHex,
222
+ bot: !!options.bot,
199
223
  });
200
224
  await publishToRelays(kind0, relays);
201
225
  k0Spinner.succeed('Kind 0 published — visible on all Nostr clients');
@@ -237,7 +261,7 @@ program
237
261
  console.log(chalk.gray(` Run ${chalk.white('agentdex claim <name>')} to get ${chalk.hex('#D4A574')('<name>@agentdex.id')}`));
238
262
  console.log('');
239
263
  // Publish kind 0 profile (name, about, avatar, website, lud16)
240
- // Kind 0 is canonical for basic profile; kind 31337 is agent-specific metadata
264
+ // Kind 0 is canonical for basic profile; kind 31339 is agent-specific metadata
241
265
  const k0Spinner = ora('Publishing kind 0 profile to Nostr relays...').start();
242
266
  try {
243
267
  const kind0 = createKind0Event(sk, {
@@ -245,6 +269,8 @@ program
245
269
  about: description || undefined,
246
270
  picture: options.avatar || undefined,
247
271
  lud16: options.lightning || undefined,
272
+ ownerPubkeyHex,
273
+ bot: !!options.bot,
248
274
  });
249
275
  await publishToRelays(kind0, relays);
250
276
  k0Spinner.succeed('Kind 0 published — visible on all Nostr clients');
@@ -290,7 +316,7 @@ program
290
316
 
291
317
  const spinner = ora(`Claiming ${name}@agentdex.id...`).start();
292
318
 
293
- // Sign a kind 31337 event for claim authentication
319
+ // Sign a kind 31339 event for claim authentication
294
320
  const event = createProfileEvent(sk, {
295
321
  name,
296
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 31337 agent profile event
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 31337 (canonical source is kind 0)
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 31337
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: 31337,
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 }),