agentdex-cli 0.1.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/nostr.js ADDED
@@ -0,0 +1,115 @@
1
+ /**
2
+ * Nostr utilities — event creation, signing, publishing
3
+ */
4
+ import { finalizeEvent, getPublicKey } from 'nostr-tools/pure';
5
+ import { nip19 } from 'nostr-tools';
6
+ import { SimplePool } from 'nostr-tools/pool';
7
+ const DEFAULT_RELAYS = ['wss://nos.lol', 'wss://relay.damus.io'];
8
+ /**
9
+ * Parse a secret key from nsec, hex, or key file
10
+ */
11
+ export function parseSecretKey(input) {
12
+ // If it's an nsec
13
+ if (input.startsWith('nsec')) {
14
+ const decoded = nip19.decode(input);
15
+ if (decoded.type !== 'nsec')
16
+ throw new Error('Invalid nsec');
17
+ return decoded.data;
18
+ }
19
+ // Hex string
20
+ if (/^[0-9a-f]{64}$/i.test(input)) {
21
+ return Uint8Array.from(Buffer.from(input, 'hex'));
22
+ }
23
+ throw new Error('Invalid key format. Provide nsec or 64-char hex.');
24
+ }
25
+ /**
26
+ * Get npub from secret key
27
+ */
28
+ export function getNpub(sk) {
29
+ const pubHex = getPublicKey(sk);
30
+ return nip19.npubEncode(pubHex);
31
+ }
32
+ /**
33
+ * Get hex pubkey from secret key
34
+ */
35
+ export function getPubkeyHex(sk) {
36
+ return getPublicKey(sk);
37
+ }
38
+ /**
39
+ * Build and sign a kind 31337 agent profile event
40
+ */
41
+ export function createProfileEvent(sk, profile) {
42
+ const tags = [
43
+ ['d', 'agentdex-profile'],
44
+ ['name', profile.name],
45
+ ];
46
+ if (profile.description)
47
+ tags.push(['description', profile.description]);
48
+ if (profile.capabilities) {
49
+ for (const cap of profile.capabilities) {
50
+ tags.push(['capability', cap]);
51
+ }
52
+ }
53
+ if (profile.framework)
54
+ tags.push(['framework', profile.framework]);
55
+ if (profile.model)
56
+ tags.push(['model', profile.model]);
57
+ if (profile.website)
58
+ tags.push(['website', profile.website]);
59
+ if (profile.avatar)
60
+ tags.push(['avatar', profile.avatar]);
61
+ if (profile.lightning)
62
+ tags.push(['lightning', profile.lightning]);
63
+ if (profile.human)
64
+ tags.push(['human', profile.human]);
65
+ if (profile.ownerX)
66
+ tags.push(['owner_x', profile.ownerX]);
67
+ if (profile.status)
68
+ tags.push(['status', profile.status || 'active']);
69
+ if (profile.messagingPolicy)
70
+ tags.push(['messaging_policy', profile.messagingPolicy]);
71
+ if (profile.messagingMinTrust)
72
+ tags.push(['messaging_min_trust', String(profile.messagingMinTrust)]);
73
+ if (profile.messagingFee)
74
+ tags.push(['messaging_fee', String(profile.messagingFee)]);
75
+ const event = finalizeEvent({
76
+ kind: 31337,
77
+ created_at: Math.floor(Date.now() / 1000),
78
+ tags,
79
+ content: '',
80
+ }, sk);
81
+ return event;
82
+ }
83
+ /**
84
+ * Publish an event to Nostr relays
85
+ */
86
+ export async function publishToRelays(event, relays = DEFAULT_RELAYS) {
87
+ const pool = new SimplePool();
88
+ const published = [];
89
+ try {
90
+ const results = await Promise.allSettled(relays.map(async (relay) => {
91
+ await pool.publish([relay], event);
92
+ return relay;
93
+ }));
94
+ for (const result of results) {
95
+ if (result.status === 'fulfilled') {
96
+ published.push(result.value);
97
+ }
98
+ }
99
+ }
100
+ finally {
101
+ pool.close(relays);
102
+ }
103
+ return published;
104
+ }
105
+ /**
106
+ * Create and sign a kind 1 note tagged #agentdex
107
+ */
108
+ export function createNote(sk, content) {
109
+ return finalizeEvent({
110
+ kind: 1,
111
+ created_at: Math.floor(Date.now() / 1000),
112
+ tags: [['t', 'agentdex']],
113
+ content,
114
+ }, sk);
115
+ }
package/package.json ADDED
@@ -0,0 +1,43 @@
1
+ {
2
+ "name": "agentdex-cli",
3
+ "version": "0.1.0",
4
+ "description": "CLI and SDK for the agentdex AI agent directory",
5
+ "type": "module",
6
+ "bin": {
7
+ "agentdex": "./dist/cli.js"
8
+ },
9
+ "main": "./dist/index.js",
10
+ "types": "./dist/index.d.ts",
11
+ "exports": {
12
+ ".": {
13
+ "import": "./dist/index.js",
14
+ "types": "./dist/index.d.ts"
15
+ }
16
+ },
17
+ "scripts": {
18
+ "build": "tsc",
19
+ "dev": "tsc --watch",
20
+ "prepublishOnly": "npm run build"
21
+ },
22
+ "keywords": ["agentdex", "nostr", "ai", "agent", "directory", "nip-05"],
23
+ "author": "Koda <kodabuilds@gmail.com>",
24
+ "license": "MIT",
25
+ "repository": {
26
+ "type": "git",
27
+ "url": "https://github.com/Koda-Builds/agentdex-cli"
28
+ },
29
+ "dependencies": {
30
+ "nostr-tools": "^2.10.4",
31
+ "commander": "^12.0.0",
32
+ "chalk": "^5.3.0",
33
+ "ora": "^8.0.0",
34
+ "inquirer": "^9.2.0",
35
+ "qrcode-terminal": "^0.12.0"
36
+ },
37
+ "devDependencies": {
38
+ "typescript": "^5.4.0",
39
+ "@types/node": "^22.0.0",
40
+ "@types/inquirer": "^9.0.0",
41
+ "@types/qrcode-terminal": "^0.12.0"
42
+ }
43
+ }
package/src/cli.ts ADDED
@@ -0,0 +1,353 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { Command } from 'commander';
4
+ import chalk from 'chalk';
5
+ import ora from 'ora';
6
+ import inquirer from 'inquirer';
7
+ import qrcode from 'qrcode-terminal';
8
+ import { readFileSync } from 'fs';
9
+ import { AgentdexClient } from './client.js';
10
+ import { parseSecretKey, getNpub, getPubkeyHex, createProfileEvent, publishToRelays, createNote } from './nostr.js';
11
+
12
+ const program = new Command();
13
+
14
+ program
15
+ .name('agentdex')
16
+ .description('CLI for the agentdex AI agent directory')
17
+ .version('0.1.0');
18
+
19
+ /**
20
+ * Resolve secret key from flags, env, or key file
21
+ */
22
+ function resolveKey(options: { nsec?: string; keyFile?: string }): Uint8Array {
23
+ const raw = options.nsec || process.env.NOSTR_NSEC;
24
+ if (raw) return parseSecretKey(raw);
25
+
26
+ if (options.keyFile) {
27
+ const data = JSON.parse(readFileSync(options.keyFile, 'utf-8'));
28
+ if (data.sk_hex) return parseSecretKey(data.sk_hex);
29
+ if (data.nsec) return parseSecretKey(data.nsec);
30
+ throw new Error('Key file must contain sk_hex or nsec');
31
+ }
32
+
33
+ throw new Error('No key provided. Use --nsec, --key-file, or set NOSTR_NSEC env var.');
34
+ }
35
+
36
+ // ==================== REGISTER ====================
37
+
38
+ program
39
+ .command('register')
40
+ .description('Register your agent on agentdex')
41
+ .option('--nsec <nsec>', 'Nostr secret key (nsec or hex)')
42
+ .option('--key-file <path>', 'Path to JSON key file')
43
+ .option('--name <name>', 'Agent name')
44
+ .option('--description <desc>', 'Agent description')
45
+ .option('--capabilities <caps>', 'Comma-separated capabilities')
46
+ .option('--framework <fw>', 'Framework (e.g., langchain, openclaw)')
47
+ .option('--model <model>', 'Model (e.g., claude-3.5-sonnet)')
48
+ .option('--website <url>', 'Website URL')
49
+ .option('--lightning <addr>', 'Lightning address')
50
+ .option('--owner-x <handle>', 'Owner X/Twitter handle (e.g., @username)')
51
+ .option('--api-key <key>', 'Agentdex API key')
52
+ .option('--relay <url>', 'Additional relay (repeatable)', (val: string, acc: string[]) => [...acc, val], [])
53
+ .option('--json', 'Output JSON')
54
+ .action(async (options) => {
55
+ try {
56
+ const sk = resolveKey(options);
57
+ const npub = getNpub(sk);
58
+ const pubHex = getPubkeyHex(sk);
59
+
60
+ let name = options.name;
61
+ let description = options.description;
62
+ let capabilities = options.capabilities?.split(',').map((s: string) => s.trim());
63
+ let framework = options.framework;
64
+
65
+ // Interactive mode if name not provided
66
+ if (!name) {
67
+ const answers = await inquirer.prompt([
68
+ { type: 'input', name: 'name', message: 'Agent name:', validate: (v: string) => v.length > 0 || 'Required' },
69
+ { type: 'input', name: 'description', message: 'Description (optional):' },
70
+ { type: 'input', name: 'capabilities', message: 'Capabilities (comma-separated):' },
71
+ { type: 'input', name: 'framework', message: 'Framework (optional):' },
72
+ ]);
73
+ name = answers.name;
74
+ description = answers.description || description;
75
+ capabilities = answers.capabilities ? answers.capabilities.split(',').map((s: string) => s.trim()) : capabilities;
76
+ framework = answers.framework || framework;
77
+ }
78
+
79
+ const spinner = ora('Signing event...').start();
80
+
81
+ const event = createProfileEvent(sk, {
82
+ name,
83
+ description,
84
+ capabilities,
85
+ framework,
86
+ model: options.model,
87
+ website: options.website,
88
+ lightning: options.lightning,
89
+ ownerX: options.ownerX,
90
+ status: 'active',
91
+ });
92
+
93
+ spinner.text = 'Registering on agentdex...';
94
+ const client = new AgentdexClient({ apiKey: options.apiKey });
95
+
96
+ try {
97
+ const result = await client.register(event);
98
+
99
+ spinner.text = 'Publishing to Nostr relays...';
100
+ const relays = ['wss://nos.lol', 'wss://relay.damus.io', ...options.relay];
101
+ const published = await publishToRelays(event, relays);
102
+
103
+ spinner.succeed('Registered!');
104
+
105
+ if (options.json) {
106
+ console.log(JSON.stringify({ ...result, relays: published }, null, 2));
107
+ } else {
108
+ console.log('');
109
+ console.log(chalk.hex('#D4A574')(' ✅ Registered on agentdex (free tier)'));
110
+ console.log(chalk.gray(` npub: ${npub}`));
111
+ console.log(chalk.gray(` Name: ${name}`));
112
+ console.log(chalk.gray(` Published to: ${published.join(', ')}`));
113
+ console.log('');
114
+ console.log(chalk.gray(` Run ${chalk.white('agentdex claim <name>')} to get ${chalk.hex('#D4A574')('<name>@agentdex.id')}`));
115
+ console.log('');
116
+ console.log(chalk.gray(' Next: Claim a NIP-05 name to get verified (first 100 free, then 5000 sats).'));
117
+ }
118
+ } catch (err) {
119
+ spinner.fail(`Registration failed: ${(err as Error).message}`);
120
+ process.exit(1);
121
+ }
122
+ } catch (err) {
123
+ console.error(chalk.red(`Error: ${(err as Error).message}`));
124
+ process.exit(1);
125
+ }
126
+ });
127
+
128
+ // ==================== CLAIM ====================
129
+
130
+ program
131
+ .command('claim <name>')
132
+ .description('Claim a NIP-05 name (name@agentdex.id)')
133
+ .option('--nsec <nsec>', 'Nostr secret key')
134
+ .option('--key-file <path>', 'Path to JSON key file')
135
+ .option('--nwc <uri>', 'Nostr Wallet Connect URI for auto-pay')
136
+ .option('--api-key <key>', 'Agentdex API key')
137
+ .option('--json', 'Output JSON')
138
+ .action(async (name: string, options) => {
139
+ try {
140
+ const sk = resolveKey(options);
141
+ const client = new AgentdexClient({ apiKey: options.apiKey });
142
+
143
+ const spinner = ora(`Claiming ${name}@agentdex.id...`).start();
144
+
145
+ // Sign a kind 31337 event for claim authentication
146
+ const event = createProfileEvent(sk, {
147
+ name,
148
+ status: 'active',
149
+ });
150
+
151
+ const claim = await client.claim(name, event);
152
+
153
+ // Free/successful claim
154
+ if (claim.claimed) {
155
+ spinner.succeed(`${chalk.hex('#D4A574')(`${claim.nip05}`)} is now active!`);
156
+ if (claim.next_steps) {
157
+ console.log('');
158
+ const ns = claim.next_steps as Record<string, { description?: string; relays?: string[] }>;
159
+ if (ns.publish_kind0) {
160
+ console.log(chalk.gray(` Next: ${ns.publish_kind0.description}`));
161
+ if (ns.publish_kind0.relays) {
162
+ console.log(chalk.gray(` Relays: ${ns.publish_kind0.relays.join(', ')}`));
163
+ }
164
+ }
165
+ }
166
+ return;
167
+ }
168
+
169
+ // Payment required (402)
170
+ if (claim.status === 'awaiting_payment' && claim.invoice) {
171
+ spinner.stop();
172
+ console.log('');
173
+ console.log(chalk.hex('#D4A574')(` 💰 Claim ${name}@agentdex.id for ${claim.amount_sats?.toLocaleString()} sats`));
174
+ console.log('');
175
+
176
+ qrcode.generate(claim.invoice, { small: true }, (qr: string) => {
177
+ console.log(qr);
178
+ });
179
+ console.log(chalk.gray(` bolt11: ${claim.invoice}`));
180
+ console.log('');
181
+
182
+ if (options.nwc) {
183
+ console.log(chalk.gray(' Auto-paying via NWC...'));
184
+ console.log(chalk.yellow(' NWC auto-pay not yet implemented. Pay the invoice manually.'));
185
+ }
186
+
187
+ // Poll for payment
188
+ const pollSpinner = ora('Waiting for payment...').start();
189
+ const startTime = Date.now();
190
+ const timeout = 15 * 60 * 1000;
191
+
192
+ while (Date.now() - startTime < timeout) {
193
+ await new Promise((r) => setTimeout(r, 3000));
194
+ const status = await client.claimStatus(claim.payment_hash!);
195
+ if (status.paid) {
196
+ pollSpinner.succeed(`${chalk.hex('#D4A574')(`${name}@agentdex.id`)} is now active!`);
197
+ return;
198
+ }
199
+ }
200
+
201
+ pollSpinner.fail('Payment timeout (15 min). Invoice expired.');
202
+ process.exit(1);
203
+ }
204
+ } catch (err) {
205
+ console.error(chalk.red(`Error: ${(err as Error).message}`));
206
+ process.exit(1);
207
+ }
208
+ });
209
+
210
+ // ==================== VERIFY ====================
211
+
212
+ program
213
+ .command('verify <npub>')
214
+ .description('Check if an agent is registered on agentdex')
215
+ .option('--json', 'Output JSON')
216
+ .action(async (npub: string, options) => {
217
+ try {
218
+ const client = new AgentdexClient();
219
+ const spinner = ora('Verifying...').start();
220
+
221
+ const result = await client.verify(npub);
222
+
223
+ if (options.json) {
224
+ spinner.stop();
225
+ console.log(JSON.stringify(result, null, 2));
226
+ return;
227
+ }
228
+
229
+ if (result.registered) {
230
+ spinner.succeed('Registered on agentdex');
231
+ console.log(chalk.gray(` Name: ${result.name}`));
232
+ console.log(chalk.gray(` Trust Score: ${result.trustScore}`));
233
+ console.log(chalk.gray(` Capabilities: ${result.capabilities.join(', ') || 'none'}`));
234
+ console.log(chalk.gray(` Nostr: ${result.hasNostr ? '✅' : '❌'} Agentdex: ${result.hasAgentdex ? '✅' : '❌'}`));
235
+ } else {
236
+ spinner.warn('Not registered on agentdex');
237
+ }
238
+ } catch (err) {
239
+ console.error(chalk.red(`Error: ${(err as Error).message}`));
240
+ process.exit(1);
241
+ }
242
+ });
243
+
244
+ // ==================== SEARCH ====================
245
+
246
+ program
247
+ .command('search [query]')
248
+ .description('Search the agentdex directory')
249
+ .option('--capability <cap>', 'Filter by capability')
250
+ .option('--framework <fw>', 'Filter by framework')
251
+ .option('--min-trust <score>', 'Minimum trust score')
252
+ .option('--limit <n>', 'Max results', '10')
253
+ .option('--json', 'Output JSON')
254
+ .action(async (query: string | undefined, options) => {
255
+ try {
256
+ const client = new AgentdexClient();
257
+ const spinner = ora('Searching...').start();
258
+
259
+ const agents = await client.search({
260
+ q: query,
261
+ capability: options.capability,
262
+ framework: options.framework,
263
+ limit: parseInt(options.limit),
264
+ }) as any[];
265
+
266
+ spinner.stop();
267
+
268
+ if (options.json) {
269
+ console.log(JSON.stringify(agents, null, 2));
270
+ return;
271
+ }
272
+
273
+ if (agents.length === 0) {
274
+ console.log(chalk.gray('No agents found.'));
275
+ return;
276
+ }
277
+
278
+ for (const agent of agents) {
279
+ const trust = agent.trustScore ? chalk.hex('#D4A574')(`[${agent.trustScore}]`) : '';
280
+ console.log(`${chalk.white(agent.name)} ${trust} ${chalk.gray(agent.npub?.substring(0, 20) + '...')}`);
281
+ if (agent.description) console.log(chalk.gray(` ${agent.description.substring(0, 80)}`));
282
+ if (agent.capabilities?.length) console.log(chalk.gray(` ${agent.capabilities.join(', ')}`));
283
+ console.log('');
284
+ }
285
+
286
+ console.log(chalk.gray(`${agents.length} agents found`));
287
+ } catch (err) {
288
+ console.error(chalk.red(`Error: ${(err as Error).message}`));
289
+ process.exit(1);
290
+ }
291
+ });
292
+
293
+ // ==================== WHOAMI ====================
294
+
295
+ program
296
+ .command('whoami')
297
+ .description('Show your agent profile')
298
+ .option('--nsec <nsec>', 'Nostr secret key')
299
+ .option('--key-file <path>', 'Path to JSON key file')
300
+ .action(async (options) => {
301
+ try {
302
+ const sk = resolveKey(options);
303
+ const npub = getNpub(sk);
304
+ const client = new AgentdexClient();
305
+
306
+ const spinner = ora('Looking up...').start();
307
+ const result = await client.verify(npub);
308
+ spinner.stop();
309
+
310
+ if (result.registered) {
311
+ console.log(chalk.hex('#D4A574')(` ${result.name}`));
312
+ console.log(chalk.gray(` ${npub}`));
313
+ console.log(chalk.gray(` Trust: ${result.trustScore}`));
314
+ console.log(chalk.gray(` Nostr: ${result.hasNostr ? '✅' : '❌'} Agentdex: ${result.hasAgentdex ? '✅' : '❌'}`));
315
+ console.log(chalk.gray(` Capabilities: ${result.capabilities.join(', ') || 'none'}`));
316
+ } else {
317
+ console.log(chalk.yellow(' Not registered on agentdex yet.'));
318
+ console.log(chalk.gray(` npub: ${npub}`));
319
+ console.log(chalk.gray(` Run: agentdex register`));
320
+ }
321
+ } catch (err) {
322
+ console.error(chalk.red(`Error: ${(err as Error).message}`));
323
+ process.exit(1);
324
+ }
325
+ });
326
+
327
+ // ==================== PUBLISH ====================
328
+
329
+ program
330
+ .command('publish <message>')
331
+ .description('Publish a note tagged #agentdex')
332
+ .option('--nsec <nsec>', 'Nostr secret key')
333
+ .option('--key-file <path>', 'Path to JSON key file')
334
+ .option('--relay <url>', 'Additional relay', (val: string, acc: string[]) => [...acc, val], [])
335
+ .action(async (message: string, options) => {
336
+ try {
337
+ const sk = resolveKey(options);
338
+ const spinner = ora('Publishing...').start();
339
+
340
+ const event = createNote(sk, message);
341
+ const relays = ['wss://nos.lol', 'wss://relay.damus.io', ...options.relay];
342
+ const published = await publishToRelays(event, relays);
343
+
344
+ spinner.succeed('Published!');
345
+ console.log(chalk.gray(` Published to: ${published.join(', ')}`));
346
+ console.log(chalk.gray(` Event ID: ${(event as any).id}`));
347
+ } catch (err) {
348
+ console.error(chalk.red(`Error: ${(err as Error).message}`));
349
+ process.exit(1);
350
+ }
351
+ });
352
+
353
+ program.parse();
package/src/client.ts ADDED
@@ -0,0 +1,134 @@
1
+ /**
2
+ * AgentdexClient — SDK for interacting with the agentdex API
3
+ */
4
+
5
+ export interface AgentdexConfig {
6
+ baseUrl?: string;
7
+ apiKey?: string;
8
+ }
9
+
10
+ export interface RegisterOptions {
11
+ name: string;
12
+ description?: string;
13
+ capabilities?: string[];
14
+ framework?: string;
15
+ model?: string;
16
+ website?: string;
17
+ avatar?: string;
18
+ lightning?: string;
19
+ human?: string;
20
+ status?: string;
21
+ messagingPolicy?: string;
22
+ messagingMinTrust?: number;
23
+ messagingFee?: number;
24
+ }
25
+
26
+ export interface VerifyResult {
27
+ registered: boolean;
28
+ hasNostr: boolean;
29
+ hasAgentdex: boolean;
30
+ trustScore: number;
31
+ name: string | null;
32
+ npub: string | null;
33
+ capabilities: string[];
34
+ messagingPolicy: string | null;
35
+ }
36
+
37
+ export interface ClaimResult {
38
+ // Free/successful claim
39
+ claimed?: boolean;
40
+ nip05?: string;
41
+ agent?: { id: string; name: string; nip05Name: string; tier: string };
42
+ next_steps?: Record<string, unknown>;
43
+ // Paid claim (402)
44
+ status?: string;
45
+ invoice?: string;
46
+ payment_hash?: string;
47
+ amount_sats?: number;
48
+ expires_at?: string;
49
+ }
50
+
51
+ export interface ClaimStatus {
52
+ paid: boolean;
53
+ name?: string;
54
+ nip05?: string;
55
+ }
56
+
57
+ export interface SearchOptions {
58
+ q?: string;
59
+ capability?: string;
60
+ framework?: string;
61
+ status?: string;
62
+ sourceFilter?: string;
63
+ sort?: string;
64
+ limit?: number;
65
+ }
66
+
67
+ export class AgentdexClient {
68
+ private baseUrl: string;
69
+ private apiKey?: string;
70
+
71
+ constructor(config: AgentdexConfig = {}) {
72
+ this.baseUrl = (config.baseUrl || process.env.AGENTDEX_URL || 'https://agentdex.id').replace(/\/$/, '');
73
+ this.apiKey = config.apiKey || process.env.AGENTDEX_API_KEY;
74
+ }
75
+
76
+ private async fetch(path: string, options: RequestInit = {}): Promise<Response> {
77
+ const headers: Record<string, string> = {
78
+ 'Content-Type': 'application/json',
79
+ ...(options.headers as Record<string, string> || {}),
80
+ };
81
+ if (this.apiKey) {
82
+ headers['Authorization'] = `Bearer ${this.apiKey}`;
83
+ }
84
+ return fetch(`${this.baseUrl}${path}`, { ...options, headers });
85
+ }
86
+
87
+ async verify(pubkeyOrNpub: string): Promise<VerifyResult> {
88
+ const param = pubkeyOrNpub.startsWith('npub') ? 'npub' : 'pubkey';
89
+ const res = await this.fetch(`/api/v1/agents/verify?${param}=${encodeURIComponent(pubkeyOrNpub)}`);
90
+ return res.json();
91
+ }
92
+
93
+ async register(event: object): Promise<{ agent: object; registered: boolean; tier: string }> {
94
+ const res = await this.fetch('/api/v1/agents/register', {
95
+ method: 'POST',
96
+ body: JSON.stringify({ event }),
97
+ });
98
+ if (!res.ok) {
99
+ const err = await res.json();
100
+ throw new Error(err.error || 'Registration failed');
101
+ }
102
+ return res.json();
103
+ }
104
+
105
+ async claim(name: string, event: object): Promise<ClaimResult> {
106
+ const res = await this.fetch('/api/v1/agents/claim', {
107
+ method: 'POST',
108
+ body: JSON.stringify({ name, event }),
109
+ });
110
+ if (!res.ok && res.status !== 402) {
111
+ const err = await res.json();
112
+ throw new Error(err.error || 'Claim failed');
113
+ }
114
+ return res.json();
115
+ }
116
+
117
+ async claimStatus(paymentHash: string): Promise<ClaimStatus> {
118
+ const res = await this.fetch(`/api/v1/agents/claim/status?payment_hash=${encodeURIComponent(paymentHash)}`);
119
+ return res.json();
120
+ }
121
+
122
+ async search(options: SearchOptions = {}): Promise<object[]> {
123
+ const params = new URLSearchParams();
124
+ if (options.q) params.set('q', options.q);
125
+ if (options.capability) params.set('capability', options.capability);
126
+ if (options.framework) params.set('framework', options.framework);
127
+ if (options.status) params.set('status', options.status);
128
+ if (options.sourceFilter) params.set('source_filter', options.sourceFilter);
129
+ if (options.sort) params.set('sort', options.sort);
130
+ if (options.limit) params.set('limit', String(options.limit));
131
+ const res = await this.fetch(`/api/v1/agents?${params}`);
132
+ return res.json();
133
+ }
134
+ }
package/src/index.ts ADDED
@@ -0,0 +1,31 @@
1
+ /**
2
+ * agentdex — SDK and CLI for the agentdex AI agent directory
3
+ *
4
+ * @example
5
+ * ```typescript
6
+ * import { AgentdexClient } from 'agentdex';
7
+ *
8
+ * const client = new AgentdexClient({ apiKey: 'adx_...' });
9
+ * const result = await client.verify('npub1...');
10
+ * ```
11
+ */
12
+
13
+ export { AgentdexClient } from './client.js';
14
+ export type {
15
+ AgentdexConfig,
16
+ RegisterOptions,
17
+ VerifyResult,
18
+ ClaimResult,
19
+ ClaimStatus,
20
+ SearchOptions,
21
+ } from './client.js';
22
+
23
+ export {
24
+ parseSecretKey,
25
+ getNpub,
26
+ getPubkeyHex,
27
+ createProfileEvent,
28
+ publishToRelays,
29
+ createNote,
30
+ } from './nostr.js';
31
+ export type { AgentProfile } from './nostr.js';