agentdex-cli 0.3.3 → 0.4.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/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,10 @@ 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('--owner <npub-or-hex>', 'Owner/operator Nostr pubkey (npub or hex) — sets kind 0 p tag for bidirectional verification')
70
+ .option('--owner-type <type>', 'Owner type: human, agent, org (sets owner_type tag on kind 31339)')
71
+ .option('--parent <npub-or-hex>', 'Parent/orchestrator agent pubkey (npub or hex)')
72
+ .option('--bot', 'Add ["bot"] tag to kind 0 profile (declares this pubkey as automated)')
68
73
  .option('--portfolio <entry>', 'Portfolio URL (format: "url,name,description") — repeatable', (val, acc) => [...acc, val], [])
69
74
  .option('--skill <skill>', 'Skill tag (repeatable)', (val, acc) => [...acc, val], [])
70
75
  .option('--experience <exp>', 'Experience tag (repeatable)', (val, acc) => [...acc, val], [])
@@ -100,15 +105,51 @@ program
100
105
  const parts = entry.split(',').map((s) => s.trim());
101
106
  return { url: parts[0], name: parts[1], description: parts[2] };
102
107
  });
108
+ // Resolve owner pubkey hex from --owner flag (npub or hex)
109
+ let ownerNpub = options.owner;
110
+ let ownerPubkeyHex;
111
+ if (ownerNpub) {
112
+ if (ownerNpub.startsWith('npub')) {
113
+ try {
114
+ const decoded = nip19.decode(ownerNpub);
115
+ ownerPubkeyHex = decoded.data;
116
+ }
117
+ catch {
118
+ console.error(chalk.red('Invalid --owner npub'));
119
+ process.exit(1);
120
+ }
121
+ }
122
+ else {
123
+ ownerPubkeyHex = ownerNpub;
124
+ ownerNpub = nip19.npubEncode(ownerNpub);
125
+ }
126
+ }
127
+ // Resolve parent pubkey hex from --parent flag
128
+ let parentHex;
129
+ if (options.parent) {
130
+ if (options.parent.startsWith('npub')) {
131
+ try {
132
+ const decoded = nip19.decode(options.parent);
133
+ parentHex = decoded.data;
134
+ }
135
+ catch {
136
+ console.error(chalk.red('Invalid --parent npub'));
137
+ process.exit(1);
138
+ }
139
+ }
140
+ else {
141
+ parentHex = options.parent;
142
+ }
143
+ }
103
144
  const event = createProfileEvent(sk, {
104
145
  name,
105
146
  description,
106
147
  capabilities,
107
148
  framework,
108
149
  model: options.model,
109
- website: options.website,
110
- lightning: options.lightning,
150
+ ownerType: options.ownerType,
111
151
  ownerX: options.ownerX,
152
+ parent: parentHex,
112
153
  status: 'active',
113
154
  portfolio: portfolio.length > 0 ? portfolio : undefined,
114
155
  skills: options.skill?.length > 0 ? options.skill : undefined,
@@ -176,6 +217,8 @@ program
176
217
  about: description || undefined,
177
218
  picture: options.avatar || undefined,
178
219
  lud16: options.lightning || undefined,
220
+ ownerPubkeyHex,
221
+ bot: !!options.bot,
179
222
  });
180
223
  await publishToRelays(kind0, relays);
181
224
  k0Spinner.succeed('Kind 0 published — visible on all Nostr clients');
@@ -215,7 +258,7 @@ program
215
258
  console.log(chalk.gray(` Run ${chalk.white('agentdex claim <name>')} to get ${chalk.hex('#D4A574')('<name>@agentdex.id')}`));
216
259
  console.log('');
217
260
  // Publish kind 0 profile (name, about, avatar, website, lud16)
218
- // Kind 0 is canonical for basic profile; kind 31337 is agent-specific metadata
261
+ // Kind 0 is canonical for basic profile; kind 31339 is agent-specific metadata
219
262
  const k0Spinner = ora('Publishing kind 0 profile to Nostr relays...').start();
220
263
  try {
221
264
  const kind0 = createKind0Event(sk, {
@@ -223,6 +266,8 @@ program
223
266
  about: description || undefined,
224
267
  picture: options.avatar || undefined,
225
268
  lud16: options.lightning || undefined,
269
+ ownerPubkeyHex,
270
+ bot: !!options.bot,
226
271
  });
227
272
  await publishToRelays(kind0, relays);
228
273
  k0Spinner.succeed('Kind 0 published — visible on all Nostr clients');
@@ -267,7 +312,7 @@ program
267
312
  const sk = resolveKey(options);
268
313
  const client = new AgentdexClient({ apiKey: options.apiKey });
269
314
  const spinner = ora(`Claiming ${name}@agentdex.id...`).start();
270
- // Sign a kind 31337 event for claim authentication
315
+ // Sign a kind 31339 event for claim authentication
271
316
  const event = createProfileEvent(sk, {
272
317
  name,
273
318
  status: 'active',
package/dist/nostr.d.ts CHANGED
@@ -7,17 +7,15 @@ export interface PortfolioItem {
7
7
  description?: string;
8
8
  }
9
9
  export interface AgentProfile {
10
- name: string;
10
+ name?: string;
11
11
  description?: string;
12
12
  capabilities?: string[];
13
13
  framework?: string;
14
14
  model?: string;
15
- website?: string;
16
- avatar?: string;
17
- lightning?: string;
18
- human?: string;
15
+ ownerType?: string;
19
16
  ownerX?: string;
20
17
  status?: string;
18
+ parent?: string;
21
19
  messagingPolicy?: string;
22
20
  messagingMinTrust?: number;
23
21
  messagingFee?: number;
@@ -47,7 +45,7 @@ export declare function getNpub(sk: Uint8Array): string;
47
45
  */
48
46
  export declare function getPubkeyHex(sk: Uint8Array): string;
49
47
  /**
50
- * Build and sign a kind 31337 agent profile event
48
+ * Build and sign a kind 31339 agent profile event
51
49
  */
52
50
  export declare function createProfileEvent(sk: Uint8Array, profile: AgentProfile): import("nostr-tools").VerifiedEvent;
53
51
  /**
@@ -72,6 +70,8 @@ export declare function createKind0Event(sk: Uint8Array, profile: {
72
70
  nip05?: string;
73
71
  picture?: string;
74
72
  lud16?: string;
73
+ ownerPubkeyHex?: string;
74
+ bot?: boolean;
75
75
  }): import("nostr-tools").VerifiedEvent;
76
76
  /**
77
77
  * Create and sign a kind 1 note tagged #agentdex
package/dist/nostr.js CHANGED
@@ -55,16 +55,14 @@ 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)
65
- // included for backward compatibility and standalone profiles
66
- if (profile.name)
67
- tags.push(['name', profile.name]);
64
+ // Kind 31339 is agent-specific metadata ONLY
65
+ // Basic profile (name, avatar, website, nip05, lud16) lives exclusively in kind 0
68
66
  if (profile.description)
69
67
  tags.push(['description', profile.description]);
70
68
  if (profile.capabilities) {
@@ -76,16 +74,12 @@ export function createProfileEvent(sk, profile) {
76
74
  tags.push(['framework', profile.framework]);
77
75
  if (profile.model)
78
76
  tags.push(['model', profile.model]);
79
- if (profile.website)
80
- tags.push(['website', profile.website]);
81
- if (profile.avatar)
82
- tags.push(['avatar', profile.avatar]);
83
- // Lightning address belongs in kind 0 (lud16), not kind 31337
84
- // Use --lightning flag to set lud16 in kind 0 during claim
85
- if (profile.human)
86
- tags.push(['human', profile.human]);
77
+ if (profile.ownerType)
78
+ tags.push(['owner_type', profile.ownerType]);
87
79
  if (profile.ownerX)
88
80
  tags.push(['owner_x', profile.ownerX]);
81
+ if (profile.parent)
82
+ tags.push(['parent', profile.parent]);
89
83
  if (profile.status)
90
84
  tags.push(['status', profile.status || 'active']);
91
85
  if (profile.messagingPolicy)
@@ -115,7 +109,7 @@ export function createProfileEvent(sk, profile) {
115
109
  }
116
110
  }
117
111
  const event = finalizeEvent({
118
- kind: 31337,
112
+ kind: 31339,
119
113
  created_at: Math.floor(Date.now() / 1000),
120
114
  tags,
121
115
  content: '',
@@ -189,10 +183,16 @@ export async function updateKind0(sk, updates, relays = DEFAULT_RELAYS) {
189
183
  * (njump, Damus, Primal) can verify the identity.
190
184
  */
191
185
  export function createKind0Event(sk, profile) {
186
+ const tags = [];
187
+ if (profile.bot)
188
+ tags.push(["bot"]);
189
+ if (profile.ownerPubkeyHex) {
190
+ tags.push(["p", profile.ownerPubkeyHex, "", "owner"]);
191
+ }
192
192
  return finalizeEvent({
193
193
  kind: 0,
194
194
  created_at: Math.floor(Date.now() / 1000),
195
- tags: [],
195
+ tags,
196
196
  content: JSON.stringify({
197
197
  name: profile.name,
198
198
  ...(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.1",
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,10 @@ 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('--owner <npub-or-hex>', 'Owner/operator Nostr pubkey (npub or hex) — sets kind 0 p tag for bidirectional verification')
78
+ .option('--owner-type <type>', 'Owner type: human, agent, org (sets owner_type tag on kind 31339)')
79
+ .option('--parent <npub-or-hex>', 'Parent/orchestrator agent pubkey (npub or hex)')
80
+ .option('--bot', 'Add ["bot"] tag to kind 0 profile (declares this pubkey as automated)')
76
81
  .option('--portfolio <entry>', 'Portfolio URL (format: "url,name,description") — repeatable', (val: string, acc: string[]) => [...acc, val], [])
77
82
  .option('--skill <skill>', 'Skill tag (repeatable)', (val: string, acc: string[]) => [...acc, val], [])
78
83
  .option('--experience <exp>', 'Experience tag (repeatable)', (val: string, acc: string[]) => [...acc, val], [])
@@ -113,15 +118,49 @@ program
113
118
  return { url: parts[0], name: parts[1], description: parts[2] };
114
119
  });
115
120
 
121
+ // Resolve owner pubkey hex from --owner flag (npub or hex)
122
+ let ownerNpub = options.owner;
123
+ let ownerPubkeyHex: string | undefined;
124
+ if (ownerNpub) {
125
+ if (ownerNpub.startsWith('npub')) {
126
+ try {
127
+ const decoded = nip19.decode(ownerNpub);
128
+ ownerPubkeyHex = decoded.data as unknown as string;
129
+ } catch {
130
+ console.error(chalk.red('Invalid --owner npub'));
131
+ process.exit(1);
132
+ }
133
+ } else {
134
+ ownerPubkeyHex = ownerNpub;
135
+ ownerNpub = nip19.npubEncode(ownerNpub);
136
+ }
137
+ }
138
+
139
+ // Resolve parent pubkey hex from --parent flag
140
+ let parentHex: string | undefined;
141
+ if (options.parent) {
142
+ if (options.parent.startsWith('npub')) {
143
+ try {
144
+ const decoded = nip19.decode(options.parent);
145
+ parentHex = decoded.data as unknown as string;
146
+ } catch {
147
+ console.error(chalk.red('Invalid --parent npub'));
148
+ process.exit(1);
149
+ }
150
+ } else {
151
+ parentHex = options.parent;
152
+ }
153
+ }
154
+
116
155
  const event = createProfileEvent(sk, {
117
156
  name,
118
157
  description,
119
158
  capabilities,
120
159
  framework,
121
160
  model: options.model,
122
- website: options.website,
123
- lightning: options.lightning,
161
+ ownerType: options.ownerType,
124
162
  ownerX: options.ownerX,
163
+ parent: parentHex,
125
164
  status: 'active',
126
165
  portfolio: portfolio.length > 0 ? portfolio : undefined,
127
166
  skills: options.skill?.length > 0 ? options.skill : undefined,
@@ -196,6 +235,8 @@ program
196
235
  about: description || undefined,
197
236
  picture: options.avatar || undefined,
198
237
  lud16: options.lightning || undefined,
238
+ ownerPubkeyHex,
239
+ bot: !!options.bot,
199
240
  });
200
241
  await publishToRelays(kind0, relays);
201
242
  k0Spinner.succeed('Kind 0 published — visible on all Nostr clients');
@@ -237,7 +278,7 @@ program
237
278
  console.log(chalk.gray(` Run ${chalk.white('agentdex claim <name>')} to get ${chalk.hex('#D4A574')('<name>@agentdex.id')}`));
238
279
  console.log('');
239
280
  // Publish kind 0 profile (name, about, avatar, website, lud16)
240
- // Kind 0 is canonical for basic profile; kind 31337 is agent-specific metadata
281
+ // Kind 0 is canonical for basic profile; kind 31339 is agent-specific metadata
241
282
  const k0Spinner = ora('Publishing kind 0 profile to Nostr relays...').start();
242
283
  try {
243
284
  const kind0 = createKind0Event(sk, {
@@ -245,6 +286,8 @@ program
245
286
  about: description || undefined,
246
287
  picture: options.avatar || undefined,
247
288
  lud16: options.lightning || undefined,
289
+ ownerPubkeyHex,
290
+ bot: !!options.bot,
248
291
  });
249
292
  await publishToRelays(kind0, relays);
250
293
  k0Spinner.succeed('Kind 0 published — visible on all Nostr clients');
@@ -290,7 +333,7 @@ program
290
333
 
291
334
  const spinner = ora(`Claiming ${name}@agentdex.id...`).start();
292
335
 
293
- // Sign a kind 31337 event for claim authentication
336
+ // Sign a kind 31339 event for claim authentication
294
337
  const event = createProfileEvent(sk, {
295
338
  name,
296
339
  status: 'active',
package/src/nostr.ts CHANGED
@@ -16,17 +16,15 @@ export interface PortfolioItem {
16
16
  }
17
17
 
18
18
  export interface AgentProfile {
19
- name: string;
19
+ name?: string;
20
20
  description?: string;
21
21
  capabilities?: string[];
22
22
  framework?: string;
23
23
  model?: string;
24
- website?: string;
25
- avatar?: string;
26
- lightning?: string;
27
- human?: string;
24
+ ownerType?: string;
28
25
  ownerX?: string;
29
26
  status?: string;
27
+ parent?: string;
30
28
  messagingPolicy?: string;
31
29
  messagingMinTrust?: number;
32
30
  messagingFee?: number;
@@ -89,16 +87,15 @@ export function getPubkeyHex(sk: Uint8Array): string {
89
87
  }
90
88
 
91
89
  /**
92
- * Build and sign a kind 31337 agent profile event
90
+ * Build and sign a kind 31339 agent profile event
93
91
  */
94
92
  export function createProfileEvent(sk: Uint8Array, profile: AgentProfile) {
95
93
  const tags: string[][] = [
96
94
  ['d', 'agentdex-profile'],
97
95
  ];
98
96
 
99
- // name is optional in kind 31337 (canonical source is kind 0)
100
- // included for backward compatibility and standalone profiles
101
- if (profile.name) tags.push(['name', profile.name]);
97
+ // Kind 31339 is agent-specific metadata ONLY
98
+ // Basic profile (name, avatar, website, nip05, lud16) lives exclusively in kind 0
102
99
  if (profile.description) tags.push(['description', profile.description]);
103
100
  if (profile.capabilities) {
104
101
  for (const cap of profile.capabilities) {
@@ -107,12 +104,9 @@ export function createProfileEvent(sk: Uint8Array, profile: AgentProfile) {
107
104
  }
108
105
  if (profile.framework) tags.push(['framework', profile.framework]);
109
106
  if (profile.model) tags.push(['model', profile.model]);
110
- if (profile.website) tags.push(['website', profile.website]);
111
- if (profile.avatar) tags.push(['avatar', profile.avatar]);
112
- // Lightning address belongs in kind 0 (lud16), not kind 31337
113
- // Use --lightning flag to set lud16 in kind 0 during claim
114
- if (profile.human) tags.push(['human', profile.human]);
107
+ if (profile.ownerType) tags.push(['owner_type', profile.ownerType]);
115
108
  if (profile.ownerX) tags.push(['owner_x', profile.ownerX]);
109
+ if (profile.parent) tags.push(['parent', profile.parent]);
116
110
  if (profile.status) tags.push(['status', profile.status || 'active']);
117
111
  if (profile.messagingPolicy) tags.push(['messaging_policy', profile.messagingPolicy]);
118
112
  if (profile.messagingMinTrust) tags.push(['messaging_min_trust', String(profile.messagingMinTrust)]);
@@ -137,7 +131,7 @@ export function createProfileEvent(sk: Uint8Array, profile: AgentProfile) {
137
131
  }
138
132
 
139
133
  const event = finalizeEvent({
140
- kind: 31337,
134
+ kind: 31339,
141
135
  created_at: Math.floor(Date.now() / 1000),
142
136
  tags,
143
137
  content: '',
@@ -219,11 +213,16 @@ export async function updateKind0(sk: Uint8Array, updates: { lud16?: string }, r
219
213
  * After claiming a NIP-05 name, publish this to relays so Nostr clients
220
214
  * (njump, Damus, Primal) can verify the identity.
221
215
  */
222
- export function createKind0Event(sk: Uint8Array, profile: { name: string; about?: string; nip05?: string; picture?: string; lud16?: string }) {
216
+ export function createKind0Event(sk: Uint8Array, profile: { name: string; about?: string; nip05?: string; picture?: string; lud16?: string; ownerPubkeyHex?: string; bot?: boolean }) {
217
+ const tags: string[][] = [];
218
+ if (profile.bot) tags.push(["bot"]);
219
+ if (profile.ownerPubkeyHex) {
220
+ tags.push(["p", profile.ownerPubkeyHex, "", "owner"]);
221
+ }
223
222
  return finalizeEvent({
224
223
  kind: 0,
225
224
  created_at: Math.floor(Date.now() / 1000),
226
- tags: [],
225
+ tags,
227
226
  content: JSON.stringify({
228
227
  name: profile.name,
229
228
  ...(profile.about && { about: profile.about }),