agentdex-cli 0.1.0 → 0.2.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 +129 -19
- package/dist/client.d.ts +4 -4
- package/dist/client.js +5 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/nostr.d.ts +11 -0
- package/dist/nostr.js +18 -0
- package/dist/nwc.d.ts +11 -0
- package/dist/nwc.js +17 -0
- package/package.json +16 -8
- package/src/cli.ts +132 -20
- package/src/client.ts +7 -2
- package/src/index.ts +1 -0
- package/src/nostr.ts +19 -0
- package/src/nwc.ts +24 -0
package/dist/cli.js
CHANGED
|
@@ -6,12 +6,13 @@ import inquirer from 'inquirer';
|
|
|
6
6
|
import qrcode from 'qrcode-terminal';
|
|
7
7
|
import { readFileSync } from 'fs';
|
|
8
8
|
import { AgentdexClient } from './client.js';
|
|
9
|
-
import { parseSecretKey, getNpub, getPubkeyHex, createProfileEvent, publishToRelays, createNote } from './nostr.js';
|
|
9
|
+
import { parseSecretKey, getNpub, getPubkeyHex, createProfileEvent, createKind0Event, publishToRelays, createNote } from './nostr.js';
|
|
10
|
+
import { payInvoice } from './nwc.js';
|
|
10
11
|
const program = new Command();
|
|
11
12
|
program
|
|
12
13
|
.name('agentdex')
|
|
13
14
|
.description('CLI for the agentdex AI agent directory')
|
|
14
|
-
.version('0.
|
|
15
|
+
.version('0.2.0');
|
|
15
16
|
/**
|
|
16
17
|
* Resolve secret key from flags, env, or key file
|
|
17
18
|
*/
|
|
@@ -43,6 +44,7 @@ program
|
|
|
43
44
|
.option('--website <url>', 'Website URL')
|
|
44
45
|
.option('--lightning <addr>', 'Lightning address')
|
|
45
46
|
.option('--owner-x <handle>', 'Owner X/Twitter handle (e.g., @username)')
|
|
47
|
+
.option('--nwc <uri>', 'Nostr Wallet Connect URI for auto-pay')
|
|
46
48
|
.option('--api-key <key>', 'Agentdex API key')
|
|
47
49
|
.option('--relay <url>', 'Additional relay (repeatable)', (val, acc) => [...acc, val], [])
|
|
48
50
|
.option('--json', 'Output JSON')
|
|
@@ -84,6 +86,65 @@ program
|
|
|
84
86
|
const client = new AgentdexClient({ apiKey: options.apiKey });
|
|
85
87
|
try {
|
|
86
88
|
const result = await client.register(event);
|
|
89
|
+
// Payment required (402)
|
|
90
|
+
if (result.status === 'awaiting_payment' && result.invoice) {
|
|
91
|
+
spinner.stop();
|
|
92
|
+
console.log('');
|
|
93
|
+
console.log(chalk.hex('#D4A574')(` 💰 Registration fee: ${result.amount_sats?.toLocaleString()} sats`));
|
|
94
|
+
console.log('');
|
|
95
|
+
const nwcUri = options.nwc || process.env.NWC_URL;
|
|
96
|
+
if (nwcUri) {
|
|
97
|
+
const paySpinner = ora('Paying invoice via NWC...').start();
|
|
98
|
+
try {
|
|
99
|
+
await payInvoice(nwcUri, result.invoice);
|
|
100
|
+
paySpinner.succeed('Invoice paid!');
|
|
101
|
+
}
|
|
102
|
+
catch (payErr) {
|
|
103
|
+
paySpinner.fail(`NWC payment failed: ${payErr.message}`);
|
|
104
|
+
console.log('');
|
|
105
|
+
console.log(chalk.gray(' Pay manually:'));
|
|
106
|
+
qrcode.generate(result.invoice, { small: true }, (qr) => { console.log(qr); });
|
|
107
|
+
console.log(chalk.gray(` bolt11: ${result.invoice}`));
|
|
108
|
+
console.log('');
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
else {
|
|
112
|
+
qrcode.generate(result.invoice, { small: true }, (qr) => { console.log(qr); });
|
|
113
|
+
console.log(chalk.gray(` bolt11: ${result.invoice}`));
|
|
114
|
+
console.log('');
|
|
115
|
+
}
|
|
116
|
+
// Poll for payment
|
|
117
|
+
const pollSpinner = ora('Waiting for payment...').start();
|
|
118
|
+
const startTime = Date.now();
|
|
119
|
+
const timeout = 15 * 60 * 1000;
|
|
120
|
+
while (Date.now() - startTime < timeout) {
|
|
121
|
+
await new Promise((r) => setTimeout(r, 3000));
|
|
122
|
+
const status = await client.registerStatus(result.payment_hash);
|
|
123
|
+
if (status.paid) {
|
|
124
|
+
pollSpinner.succeed('Registered!');
|
|
125
|
+
spinner.text = 'Publishing to Nostr relays...';
|
|
126
|
+
const relays = ['wss://nos.lol', 'wss://relay.damus.io', ...options.relay];
|
|
127
|
+
const published = await publishToRelays(event, relays);
|
|
128
|
+
if (options.json) {
|
|
129
|
+
console.log(JSON.stringify({ ...result, relays: published }, null, 2));
|
|
130
|
+
}
|
|
131
|
+
else {
|
|
132
|
+
console.log('');
|
|
133
|
+
console.log(chalk.hex('#D4A574')(' ✅ Registered on agentdex'));
|
|
134
|
+
console.log(chalk.gray(` npub: ${npub}`));
|
|
135
|
+
console.log(chalk.gray(` Name: ${name}`));
|
|
136
|
+
console.log(chalk.gray(` Published to: ${published.join(', ')}`));
|
|
137
|
+
console.log('');
|
|
138
|
+
console.log(chalk.gray(` Run ${chalk.white('agentdex claim <name>')} to get ${chalk.hex('#D4A574')('<name>@agentdex.id')}`));
|
|
139
|
+
}
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
pollSpinner.fail('Payment timeout (15 min). Invoice expired.');
|
|
144
|
+
process.exit(1);
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
// Free registration — no payment needed
|
|
87
148
|
spinner.text = 'Publishing to Nostr relays...';
|
|
88
149
|
const relays = ['wss://nos.lol', 'wss://relay.damus.io', ...options.relay];
|
|
89
150
|
const published = await publishToRelays(event, relays);
|
|
@@ -104,7 +165,13 @@ program
|
|
|
104
165
|
}
|
|
105
166
|
}
|
|
106
167
|
catch (err) {
|
|
107
|
-
|
|
168
|
+
const apiErr = err;
|
|
169
|
+
if (apiErr.status === 503) {
|
|
170
|
+
spinner.fail('Registration is currently disabled.');
|
|
171
|
+
}
|
|
172
|
+
else {
|
|
173
|
+
spinner.fail(`Registration failed: ${err.message}`);
|
|
174
|
+
}
|
|
108
175
|
process.exit(1);
|
|
109
176
|
}
|
|
110
177
|
}
|
|
@@ -121,6 +188,8 @@ program
|
|
|
121
188
|
.option('--key-file <path>', 'Path to JSON key file')
|
|
122
189
|
.option('--nwc <uri>', 'Nostr Wallet Connect URI for auto-pay')
|
|
123
190
|
.option('--api-key <key>', 'Agentdex API key')
|
|
191
|
+
.option('--skip-kind0', 'Skip publishing kind 0 profile to relays')
|
|
192
|
+
.option('--relay <url>', 'Additional relay', (val, acc) => [...acc, val], [])
|
|
124
193
|
.option('--json', 'Output JSON')
|
|
125
194
|
.action(async (name, options) => {
|
|
126
195
|
try {
|
|
@@ -136,16 +205,32 @@ program
|
|
|
136
205
|
// Free/successful claim
|
|
137
206
|
if (claim.claimed) {
|
|
138
207
|
spinner.succeed(`${chalk.hex('#D4A574')(`${claim.nip05}`)} is now active!`);
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
const
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
}
|
|
208
|
+
// Auto-publish kind 0 to relays so Nostr clients verify the NIP-05
|
|
209
|
+
if (!options.skipKind0) {
|
|
210
|
+
const k0Spinner = ora('Publishing kind 0 profile to Nostr relays...').start();
|
|
211
|
+
try {
|
|
212
|
+
const kind0 = createKind0Event(sk, {
|
|
213
|
+
name: claim.agent?.name || name,
|
|
214
|
+
nip05: `${name}@agentdex.id`,
|
|
215
|
+
});
|
|
216
|
+
const relays = ['wss://nos.lol', 'wss://relay.damus.io', ...(options.relay || [])];
|
|
217
|
+
const published = await publishToRelays(kind0, relays);
|
|
218
|
+
k0Spinner.succeed(`Kind 0 published to ${published.join(', ')}`);
|
|
219
|
+
console.log(chalk.gray(' NIP-05 will appear on njump/Damus/Primal once relays propagate (~30s)'));
|
|
220
|
+
}
|
|
221
|
+
catch {
|
|
222
|
+
k0Spinner.warn('Kind 0 publish failed. Publish manually:');
|
|
223
|
+
console.log(chalk.gray(` kind 0 content: {"name":"...","nip05":"${name}@agentdex.id"}`));
|
|
147
224
|
}
|
|
148
225
|
}
|
|
226
|
+
else {
|
|
227
|
+
console.log('');
|
|
228
|
+
console.log(chalk.yellow(' ⚠ Skipped kind 0 publish. For NIP-05 to show on Nostr clients:'));
|
|
229
|
+
console.log(chalk.gray(` Publish kind 0 with: "nip05": "${name}@agentdex.id"`));
|
|
230
|
+
}
|
|
231
|
+
if (options.json) {
|
|
232
|
+
console.log(JSON.stringify(claim, null, 2));
|
|
233
|
+
}
|
|
149
234
|
return;
|
|
150
235
|
}
|
|
151
236
|
// Payment required (402)
|
|
@@ -154,14 +239,26 @@ program
|
|
|
154
239
|
console.log('');
|
|
155
240
|
console.log(chalk.hex('#D4A574')(` 💰 Claim ${name}@agentdex.id for ${claim.amount_sats?.toLocaleString()} sats`));
|
|
156
241
|
console.log('');
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
242
|
+
const nwcUri = options.nwc || process.env.NWC_URL;
|
|
243
|
+
if (nwcUri) {
|
|
244
|
+
const paySpinner = ora('Paying invoice via NWC...').start();
|
|
245
|
+
try {
|
|
246
|
+
await payInvoice(nwcUri, claim.invoice);
|
|
247
|
+
paySpinner.succeed('Invoice paid!');
|
|
248
|
+
}
|
|
249
|
+
catch (payErr) {
|
|
250
|
+
paySpinner.fail(`NWC payment failed: ${payErr.message}`);
|
|
251
|
+
console.log('');
|
|
252
|
+
console.log(chalk.gray(' Pay manually:'));
|
|
253
|
+
qrcode.generate(claim.invoice, { small: true }, (qr) => { console.log(qr); });
|
|
254
|
+
console.log(chalk.gray(` bolt11: ${claim.invoice}`));
|
|
255
|
+
console.log('');
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
else {
|
|
259
|
+
qrcode.generate(claim.invoice, { small: true }, (qr) => { console.log(qr); });
|
|
260
|
+
console.log(chalk.gray(` bolt11: ${claim.invoice}`));
|
|
261
|
+
console.log('');
|
|
165
262
|
}
|
|
166
263
|
// Poll for payment
|
|
167
264
|
const pollSpinner = ora('Waiting for payment...').start();
|
|
@@ -172,6 +269,19 @@ program
|
|
|
172
269
|
const status = await client.claimStatus(claim.payment_hash);
|
|
173
270
|
if (status.paid) {
|
|
174
271
|
pollSpinner.succeed(`${chalk.hex('#D4A574')(`${name}@agentdex.id`)} is now active!`);
|
|
272
|
+
// Auto-publish kind 0 after payment
|
|
273
|
+
if (!options.skipKind0) {
|
|
274
|
+
const k0Spinner = ora('Publishing kind 0 profile to Nostr relays...').start();
|
|
275
|
+
try {
|
|
276
|
+
const kind0 = createKind0Event(sk, { name, nip05: `${name}@agentdex.id` });
|
|
277
|
+
const relays = ['wss://nos.lol', 'wss://relay.damus.io', ...(options.relay || [])];
|
|
278
|
+
await publishToRelays(kind0, relays);
|
|
279
|
+
k0Spinner.succeed('Kind 0 published — NIP-05 active on all Nostr clients');
|
|
280
|
+
}
|
|
281
|
+
catch {
|
|
282
|
+
k0Spinner.warn('Kind 0 publish failed — publish manually');
|
|
283
|
+
}
|
|
284
|
+
}
|
|
175
285
|
return;
|
|
176
286
|
}
|
|
177
287
|
}
|
package/dist/client.d.ts
CHANGED
|
@@ -66,10 +66,10 @@ export declare class AgentdexClient {
|
|
|
66
66
|
constructor(config?: AgentdexConfig);
|
|
67
67
|
private fetch;
|
|
68
68
|
verify(pubkeyOrNpub: string): Promise<VerifyResult>;
|
|
69
|
-
register(event: object): Promise<
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
69
|
+
register(event: object): Promise<any>;
|
|
70
|
+
registerStatus(paymentHash: string): Promise<{
|
|
71
|
+
paid: boolean;
|
|
72
|
+
agent?: object;
|
|
73
73
|
}>;
|
|
74
74
|
claim(name: string, event: object): Promise<ClaimResult>;
|
|
75
75
|
claimStatus(paymentHash: string): Promise<ClaimStatus>;
|
package/dist/client.js
CHANGED
|
@@ -28,12 +28,16 @@ export class AgentdexClient {
|
|
|
28
28
|
method: 'POST',
|
|
29
29
|
body: JSON.stringify({ event }),
|
|
30
30
|
});
|
|
31
|
-
if (!res.ok) {
|
|
31
|
+
if (!res.ok && res.status !== 402) {
|
|
32
32
|
const err = await res.json();
|
|
33
33
|
throw new Error(err.error || 'Registration failed');
|
|
34
34
|
}
|
|
35
35
|
return res.json();
|
|
36
36
|
}
|
|
37
|
+
async registerStatus(paymentHash) {
|
|
38
|
+
const res = await this.fetch(`/api/v1/agents/register/status?payment_hash=${encodeURIComponent(paymentHash)}`);
|
|
39
|
+
return res.json();
|
|
40
|
+
}
|
|
37
41
|
async claim(name, event) {
|
|
38
42
|
const res = await this.fetch('/api/v1/agents/claim', {
|
|
39
43
|
method: 'POST',
|
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, publishToRelays, createNote, } from './nostr.js';
|
|
14
|
+
export { parseSecretKey, getNpub, getPubkeyHex, createProfileEvent, createKind0Event, 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, publishToRelays, createNote, } from './nostr.js';
|
|
13
|
+
export { parseSecretKey, getNpub, getPubkeyHex, createProfileEvent, createKind0Event, publishToRelays, createNote, } from './nostr.js';
|
package/dist/nostr.d.ts
CHANGED
|
@@ -37,6 +37,17 @@ export declare function createProfileEvent(sk: Uint8Array, profile: AgentProfile
|
|
|
37
37
|
* Publish an event to Nostr relays
|
|
38
38
|
*/
|
|
39
39
|
export declare function publishToRelays(event: object, relays?: string[]): Promise<string[]>;
|
|
40
|
+
/**
|
|
41
|
+
* Build and sign a kind 0 profile metadata event (for NIP-05 verification).
|
|
42
|
+
* After claiming a NIP-05 name, publish this to relays so Nostr clients
|
|
43
|
+
* (njump, Damus, Primal) can verify the identity.
|
|
44
|
+
*/
|
|
45
|
+
export declare function createKind0Event(sk: Uint8Array, profile: {
|
|
46
|
+
name: string;
|
|
47
|
+
about?: string;
|
|
48
|
+
nip05?: string;
|
|
49
|
+
picture?: string;
|
|
50
|
+
}): import("nostr-tools").VerifiedEvent;
|
|
40
51
|
/**
|
|
41
52
|
* Create and sign a kind 1 note tagged #agentdex
|
|
42
53
|
*/
|
package/dist/nostr.js
CHANGED
|
@@ -102,6 +102,24 @@ export async function publishToRelays(event, relays = DEFAULT_RELAYS) {
|
|
|
102
102
|
}
|
|
103
103
|
return published;
|
|
104
104
|
}
|
|
105
|
+
/**
|
|
106
|
+
* Build and sign a kind 0 profile metadata event (for NIP-05 verification).
|
|
107
|
+
* After claiming a NIP-05 name, publish this to relays so Nostr clients
|
|
108
|
+
* (njump, Damus, Primal) can verify the identity.
|
|
109
|
+
*/
|
|
110
|
+
export function createKind0Event(sk, profile) {
|
|
111
|
+
return finalizeEvent({
|
|
112
|
+
kind: 0,
|
|
113
|
+
created_at: Math.floor(Date.now() / 1000),
|
|
114
|
+
tags: [],
|
|
115
|
+
content: JSON.stringify({
|
|
116
|
+
name: profile.name,
|
|
117
|
+
...(profile.about && { about: profile.about }),
|
|
118
|
+
...(profile.nip05 && { nip05: profile.nip05 }),
|
|
119
|
+
...(profile.picture && { picture: profile.picture }),
|
|
120
|
+
}),
|
|
121
|
+
}, sk);
|
|
122
|
+
}
|
|
105
123
|
/**
|
|
106
124
|
* Create and sign a kind 1 note tagged #agentdex
|
|
107
125
|
*/
|
package/dist/nwc.d.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* NWC (Nostr Wallet Connect) payment utility using Alby SDK
|
|
3
|
+
*/
|
|
4
|
+
export interface PaymentResult {
|
|
5
|
+
preimage: string;
|
|
6
|
+
paid: boolean;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Pay a bolt11 invoice via NWC
|
|
10
|
+
*/
|
|
11
|
+
export declare function payInvoice(nwcUri: string, bolt11: string): Promise<PaymentResult>;
|
package/dist/nwc.js
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* NWC (Nostr Wallet Connect) payment utility using Alby SDK
|
|
3
|
+
*/
|
|
4
|
+
import { NWCClient } from '@getalby/sdk';
|
|
5
|
+
/**
|
|
6
|
+
* Pay a bolt11 invoice via NWC
|
|
7
|
+
*/
|
|
8
|
+
export async function payInvoice(nwcUri, bolt11) {
|
|
9
|
+
const client = new NWCClient({ nostrWalletConnectUrl: nwcUri });
|
|
10
|
+
try {
|
|
11
|
+
const response = await client.payInvoice({ invoice: bolt11 });
|
|
12
|
+
return { preimage: response.preimage, paid: true };
|
|
13
|
+
}
|
|
14
|
+
finally {
|
|
15
|
+
client.close();
|
|
16
|
+
}
|
|
17
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agentdex-cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "CLI and SDK for the agentdex AI agent directory",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -19,7 +19,14 @@
|
|
|
19
19
|
"dev": "tsc --watch",
|
|
20
20
|
"prepublishOnly": "npm run build"
|
|
21
21
|
},
|
|
22
|
-
"keywords": [
|
|
22
|
+
"keywords": [
|
|
23
|
+
"agentdex",
|
|
24
|
+
"nostr",
|
|
25
|
+
"ai",
|
|
26
|
+
"agent",
|
|
27
|
+
"directory",
|
|
28
|
+
"nip-05"
|
|
29
|
+
],
|
|
23
30
|
"author": "Koda <kodabuilds@gmail.com>",
|
|
24
31
|
"license": "MIT",
|
|
25
32
|
"repository": {
|
|
@@ -27,17 +34,18 @@
|
|
|
27
34
|
"url": "https://github.com/Koda-Builds/agentdex-cli"
|
|
28
35
|
},
|
|
29
36
|
"dependencies": {
|
|
30
|
-
"
|
|
31
|
-
"commander": "^12.0.0",
|
|
37
|
+
"@getalby/sdk": "^7.0.0",
|
|
32
38
|
"chalk": "^5.3.0",
|
|
33
|
-
"
|
|
39
|
+
"commander": "^12.0.0",
|
|
34
40
|
"inquirer": "^9.2.0",
|
|
41
|
+
"nostr-tools": "^2.10.4",
|
|
42
|
+
"ora": "^8.0.0",
|
|
35
43
|
"qrcode-terminal": "^0.12.0"
|
|
36
44
|
},
|
|
37
45
|
"devDependencies": {
|
|
38
|
-
"typescript": "^5.4.0",
|
|
39
|
-
"@types/node": "^22.0.0",
|
|
40
46
|
"@types/inquirer": "^9.0.0",
|
|
41
|
-
"@types/
|
|
47
|
+
"@types/node": "^22.0.0",
|
|
48
|
+
"@types/qrcode-terminal": "^0.12.0",
|
|
49
|
+
"typescript": "^5.4.0"
|
|
42
50
|
}
|
|
43
51
|
}
|
package/src/cli.ts
CHANGED
|
@@ -7,14 +7,15 @@ import inquirer from 'inquirer';
|
|
|
7
7
|
import qrcode from 'qrcode-terminal';
|
|
8
8
|
import { readFileSync } from 'fs';
|
|
9
9
|
import { AgentdexClient } from './client.js';
|
|
10
|
-
import { parseSecretKey, getNpub, getPubkeyHex, createProfileEvent, publishToRelays, createNote } from './nostr.js';
|
|
10
|
+
import { parseSecretKey, getNpub, getPubkeyHex, createProfileEvent, createKind0Event, publishToRelays, createNote } from './nostr.js';
|
|
11
|
+
import { payInvoice } from './nwc.js';
|
|
11
12
|
|
|
12
13
|
const program = new Command();
|
|
13
14
|
|
|
14
15
|
program
|
|
15
16
|
.name('agentdex')
|
|
16
17
|
.description('CLI for the agentdex AI agent directory')
|
|
17
|
-
.version('0.
|
|
18
|
+
.version('0.2.0');
|
|
18
19
|
|
|
19
20
|
/**
|
|
20
21
|
* Resolve secret key from flags, env, or key file
|
|
@@ -48,6 +49,7 @@ program
|
|
|
48
49
|
.option('--website <url>', 'Website URL')
|
|
49
50
|
.option('--lightning <addr>', 'Lightning address')
|
|
50
51
|
.option('--owner-x <handle>', 'Owner X/Twitter handle (e.g., @username)')
|
|
52
|
+
.option('--nwc <uri>', 'Nostr Wallet Connect URI for auto-pay')
|
|
51
53
|
.option('--api-key <key>', 'Agentdex API key')
|
|
52
54
|
.option('--relay <url>', 'Additional relay (repeatable)', (val: string, acc: string[]) => [...acc, val], [])
|
|
53
55
|
.option('--json', 'Output JSON')
|
|
@@ -96,6 +98,70 @@ program
|
|
|
96
98
|
try {
|
|
97
99
|
const result = await client.register(event);
|
|
98
100
|
|
|
101
|
+
// Payment required (402)
|
|
102
|
+
if (result.status === 'awaiting_payment' && result.invoice) {
|
|
103
|
+
spinner.stop();
|
|
104
|
+
console.log('');
|
|
105
|
+
console.log(chalk.hex('#D4A574')(` 💰 Registration fee: ${result.amount_sats?.toLocaleString()} sats`));
|
|
106
|
+
console.log('');
|
|
107
|
+
|
|
108
|
+
const nwcUri = options.nwc || process.env.NWC_URL;
|
|
109
|
+
|
|
110
|
+
if (nwcUri) {
|
|
111
|
+
const paySpinner = ora('Paying invoice via NWC...').start();
|
|
112
|
+
try {
|
|
113
|
+
await payInvoice(nwcUri, result.invoice);
|
|
114
|
+
paySpinner.succeed('Invoice paid!');
|
|
115
|
+
} catch (payErr) {
|
|
116
|
+
paySpinner.fail(`NWC payment failed: ${(payErr as Error).message}`);
|
|
117
|
+
console.log('');
|
|
118
|
+
console.log(chalk.gray(' Pay manually:'));
|
|
119
|
+
qrcode.generate(result.invoice, { small: true }, (qr: string) => { console.log(qr); });
|
|
120
|
+
console.log(chalk.gray(` bolt11: ${result.invoice}`));
|
|
121
|
+
console.log('');
|
|
122
|
+
}
|
|
123
|
+
} else {
|
|
124
|
+
qrcode.generate(result.invoice, { small: true }, (qr: string) => { console.log(qr); });
|
|
125
|
+
console.log(chalk.gray(` bolt11: ${result.invoice}`));
|
|
126
|
+
console.log('');
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// Poll for payment
|
|
130
|
+
const pollSpinner = ora('Waiting for payment...').start();
|
|
131
|
+
const startTime = Date.now();
|
|
132
|
+
const timeout = 15 * 60 * 1000;
|
|
133
|
+
|
|
134
|
+
while (Date.now() - startTime < timeout) {
|
|
135
|
+
await new Promise((r) => setTimeout(r, 3000));
|
|
136
|
+
const status = await client.registerStatus(result.payment_hash!);
|
|
137
|
+
if (status.paid) {
|
|
138
|
+
pollSpinner.succeed('Registered!');
|
|
139
|
+
|
|
140
|
+
spinner.text = 'Publishing to Nostr relays...';
|
|
141
|
+
const relays = ['wss://nos.lol', 'wss://relay.damus.io', ...options.relay];
|
|
142
|
+
const published = await publishToRelays(event, relays);
|
|
143
|
+
|
|
144
|
+
if (options.json) {
|
|
145
|
+
console.log(JSON.stringify({ ...result, relays: published }, null, 2));
|
|
146
|
+
} else {
|
|
147
|
+
console.log('');
|
|
148
|
+
console.log(chalk.hex('#D4A574')(' ✅ Registered on agentdex'));
|
|
149
|
+
console.log(chalk.gray(` npub: ${npub}`));
|
|
150
|
+
console.log(chalk.gray(` Name: ${name}`));
|
|
151
|
+
console.log(chalk.gray(` Published to: ${published.join(', ')}`));
|
|
152
|
+
console.log('');
|
|
153
|
+
console.log(chalk.gray(` Run ${chalk.white('agentdex claim <name>')} to get ${chalk.hex('#D4A574')('<name>@agentdex.id')}`));
|
|
154
|
+
}
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
pollSpinner.fail('Payment timeout (15 min). Invoice expired.');
|
|
160
|
+
process.exit(1);
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// Free registration — no payment needed
|
|
99
165
|
spinner.text = 'Publishing to Nostr relays...';
|
|
100
166
|
const relays = ['wss://nos.lol', 'wss://relay.damus.io', ...options.relay];
|
|
101
167
|
const published = await publishToRelays(event, relays);
|
|
@@ -116,7 +182,12 @@ program
|
|
|
116
182
|
console.log(chalk.gray(' Next: Claim a NIP-05 name to get verified (first 100 free, then 5000 sats).'));
|
|
117
183
|
}
|
|
118
184
|
} catch (err) {
|
|
119
|
-
|
|
185
|
+
const apiErr = err as any;
|
|
186
|
+
if (apiErr.status === 503) {
|
|
187
|
+
spinner.fail('Registration is currently disabled.');
|
|
188
|
+
} else {
|
|
189
|
+
spinner.fail(`Registration failed: ${(err as Error).message}`);
|
|
190
|
+
}
|
|
120
191
|
process.exit(1);
|
|
121
192
|
}
|
|
122
193
|
} catch (err) {
|
|
@@ -134,6 +205,8 @@ program
|
|
|
134
205
|
.option('--key-file <path>', 'Path to JSON key file')
|
|
135
206
|
.option('--nwc <uri>', 'Nostr Wallet Connect URI for auto-pay')
|
|
136
207
|
.option('--api-key <key>', 'Agentdex API key')
|
|
208
|
+
.option('--skip-kind0', 'Skip publishing kind 0 profile to relays')
|
|
209
|
+
.option('--relay <url>', 'Additional relay', (val: string, acc: string[]) => [...acc, val], [])
|
|
137
210
|
.option('--json', 'Output JSON')
|
|
138
211
|
.action(async (name: string, options) => {
|
|
139
212
|
try {
|
|
@@ -153,15 +226,31 @@ program
|
|
|
153
226
|
// Free/successful claim
|
|
154
227
|
if (claim.claimed) {
|
|
155
228
|
spinner.succeed(`${chalk.hex('#D4A574')(`${claim.nip05}`)} is now active!`);
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
229
|
+
|
|
230
|
+
// Auto-publish kind 0 to relays so Nostr clients verify the NIP-05
|
|
231
|
+
if (!options.skipKind0) {
|
|
232
|
+
const k0Spinner = ora('Publishing kind 0 profile to Nostr relays...').start();
|
|
233
|
+
try {
|
|
234
|
+
const kind0 = createKind0Event(sk, {
|
|
235
|
+
name: claim.agent?.name || name,
|
|
236
|
+
nip05: `${name}@agentdex.id`,
|
|
237
|
+
});
|
|
238
|
+
const relays = ['wss://nos.lol', 'wss://relay.damus.io', ...(options.relay || [])];
|
|
239
|
+
const published = await publishToRelays(kind0, relays);
|
|
240
|
+
k0Spinner.succeed(`Kind 0 published to ${published.join(', ')}`);
|
|
241
|
+
console.log(chalk.gray(' NIP-05 will appear on njump/Damus/Primal once relays propagate (~30s)'));
|
|
242
|
+
} catch {
|
|
243
|
+
k0Spinner.warn('Kind 0 publish failed. Publish manually:');
|
|
244
|
+
console.log(chalk.gray(` kind 0 content: {"name":"...","nip05":"${name}@agentdex.id"}`));
|
|
164
245
|
}
|
|
246
|
+
} else {
|
|
247
|
+
console.log('');
|
|
248
|
+
console.log(chalk.yellow(' ⚠ Skipped kind 0 publish. For NIP-05 to show on Nostr clients:'));
|
|
249
|
+
console.log(chalk.gray(` Publish kind 0 with: "nip05": "${name}@agentdex.id"`));
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
if (options.json) {
|
|
253
|
+
console.log(JSON.stringify(claim, null, 2));
|
|
165
254
|
}
|
|
166
255
|
return;
|
|
167
256
|
}
|
|
@@ -173,15 +262,25 @@ program
|
|
|
173
262
|
console.log(chalk.hex('#D4A574')(` 💰 Claim ${name}@agentdex.id for ${claim.amount_sats?.toLocaleString()} sats`));
|
|
174
263
|
console.log('');
|
|
175
264
|
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
265
|
+
const nwcUri = options.nwc || process.env.NWC_URL;
|
|
266
|
+
|
|
267
|
+
if (nwcUri) {
|
|
268
|
+
const paySpinner = ora('Paying invoice via NWC...').start();
|
|
269
|
+
try {
|
|
270
|
+
await payInvoice(nwcUri, claim.invoice);
|
|
271
|
+
paySpinner.succeed('Invoice paid!');
|
|
272
|
+
} catch (payErr) {
|
|
273
|
+
paySpinner.fail(`NWC payment failed: ${(payErr as Error).message}`);
|
|
274
|
+
console.log('');
|
|
275
|
+
console.log(chalk.gray(' Pay manually:'));
|
|
276
|
+
qrcode.generate(claim.invoice, { small: true }, (qr: string) => { console.log(qr); });
|
|
277
|
+
console.log(chalk.gray(` bolt11: ${claim.invoice}`));
|
|
278
|
+
console.log('');
|
|
279
|
+
}
|
|
280
|
+
} else {
|
|
281
|
+
qrcode.generate(claim.invoice, { small: true }, (qr: string) => { console.log(qr); });
|
|
282
|
+
console.log(chalk.gray(` bolt11: ${claim.invoice}`));
|
|
283
|
+
console.log('');
|
|
185
284
|
}
|
|
186
285
|
|
|
187
286
|
// Poll for payment
|
|
@@ -194,6 +293,19 @@ program
|
|
|
194
293
|
const status = await client.claimStatus(claim.payment_hash!);
|
|
195
294
|
if (status.paid) {
|
|
196
295
|
pollSpinner.succeed(`${chalk.hex('#D4A574')(`${name}@agentdex.id`)} is now active!`);
|
|
296
|
+
|
|
297
|
+
// Auto-publish kind 0 after payment
|
|
298
|
+
if (!options.skipKind0) {
|
|
299
|
+
const k0Spinner = ora('Publishing kind 0 profile to Nostr relays...').start();
|
|
300
|
+
try {
|
|
301
|
+
const kind0 = createKind0Event(sk, { name, nip05: `${name}@agentdex.id` });
|
|
302
|
+
const relays = ['wss://nos.lol', 'wss://relay.damus.io', ...(options.relay || [])];
|
|
303
|
+
await publishToRelays(kind0, relays);
|
|
304
|
+
k0Spinner.succeed('Kind 0 published — NIP-05 active on all Nostr clients');
|
|
305
|
+
} catch {
|
|
306
|
+
k0Spinner.warn('Kind 0 publish failed — publish manually');
|
|
307
|
+
}
|
|
308
|
+
}
|
|
197
309
|
return;
|
|
198
310
|
}
|
|
199
311
|
}
|
package/src/client.ts
CHANGED
|
@@ -90,18 +90,23 @@ export class AgentdexClient {
|
|
|
90
90
|
return res.json();
|
|
91
91
|
}
|
|
92
92
|
|
|
93
|
-
async register(event: object): Promise<
|
|
93
|
+
async register(event: object): Promise<any> {
|
|
94
94
|
const res = await this.fetch('/api/v1/agents/register', {
|
|
95
95
|
method: 'POST',
|
|
96
96
|
body: JSON.stringify({ event }),
|
|
97
97
|
});
|
|
98
|
-
if (!res.ok) {
|
|
98
|
+
if (!res.ok && res.status !== 402) {
|
|
99
99
|
const err = await res.json();
|
|
100
100
|
throw new Error(err.error || 'Registration failed');
|
|
101
101
|
}
|
|
102
102
|
return res.json();
|
|
103
103
|
}
|
|
104
104
|
|
|
105
|
+
async registerStatus(paymentHash: string): Promise<{ paid: boolean; agent?: object }> {
|
|
106
|
+
const res = await this.fetch(`/api/v1/agents/register/status?payment_hash=${encodeURIComponent(paymentHash)}`);
|
|
107
|
+
return res.json();
|
|
108
|
+
}
|
|
109
|
+
|
|
105
110
|
async claim(name: string, event: object): Promise<ClaimResult> {
|
|
106
111
|
const res = await this.fetch('/api/v1/agents/claim', {
|
|
107
112
|
method: 'POST',
|
package/src/index.ts
CHANGED
package/src/nostr.ts
CHANGED
|
@@ -121,6 +121,25 @@ export async function publishToRelays(event: object, relays: string[] = DEFAULT_
|
|
|
121
121
|
return published;
|
|
122
122
|
}
|
|
123
123
|
|
|
124
|
+
/**
|
|
125
|
+
* Build and sign a kind 0 profile metadata event (for NIP-05 verification).
|
|
126
|
+
* After claiming a NIP-05 name, publish this to relays so Nostr clients
|
|
127
|
+
* (njump, Damus, Primal) can verify the identity.
|
|
128
|
+
*/
|
|
129
|
+
export function createKind0Event(sk: Uint8Array, profile: { name: string; about?: string; nip05?: string; picture?: string }) {
|
|
130
|
+
return finalizeEvent({
|
|
131
|
+
kind: 0,
|
|
132
|
+
created_at: Math.floor(Date.now() / 1000),
|
|
133
|
+
tags: [],
|
|
134
|
+
content: JSON.stringify({
|
|
135
|
+
name: profile.name,
|
|
136
|
+
...(profile.about && { about: profile.about }),
|
|
137
|
+
...(profile.nip05 && { nip05: profile.nip05 }),
|
|
138
|
+
...(profile.picture && { picture: profile.picture }),
|
|
139
|
+
}),
|
|
140
|
+
}, sk);
|
|
141
|
+
}
|
|
142
|
+
|
|
124
143
|
/**
|
|
125
144
|
* Create and sign a kind 1 note tagged #agentdex
|
|
126
145
|
*/
|
package/src/nwc.ts
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* NWC (Nostr Wallet Connect) payment utility using Alby SDK
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { NWCClient } from '@getalby/sdk';
|
|
6
|
+
|
|
7
|
+
export interface PaymentResult {
|
|
8
|
+
preimage: string;
|
|
9
|
+
paid: boolean;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Pay a bolt11 invoice via NWC
|
|
14
|
+
*/
|
|
15
|
+
export async function payInvoice(nwcUri: string, bolt11: string): Promise<PaymentResult> {
|
|
16
|
+
const client = new NWCClient({ nostrWalletConnectUrl: nwcUri });
|
|
17
|
+
|
|
18
|
+
try {
|
|
19
|
+
const response = await client.payInvoice({ invoice: bolt11 });
|
|
20
|
+
return { preimage: response.preimage, paid: true };
|
|
21
|
+
} finally {
|
|
22
|
+
client.close();
|
|
23
|
+
}
|
|
24
|
+
}
|