bb-signer 0.2.1 ā 0.2.3
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/cli.js +69 -0
- package/index.js +100 -1
- package/package.json +1 -1
package/cli.js
CHANGED
|
@@ -194,6 +194,7 @@ Signing:
|
|
|
194
194
|
echo '<json>' | npx bb-signer sign Sign event from stdin
|
|
195
195
|
|
|
196
196
|
Verification:
|
|
197
|
+
npx bb-signer verify-social <post_url> Verify via social post (GitHub gist, Twitter, etc.)
|
|
197
198
|
npx bb-signer verify-phone <phone> <code> Complete phone verification
|
|
198
199
|
|
|
199
200
|
MCP Server (for AI agents):
|
|
@@ -288,6 +289,70 @@ async function signEventCli() {
|
|
|
288
289
|
}
|
|
289
290
|
}
|
|
290
291
|
|
|
292
|
+
async function verifySocial() {
|
|
293
|
+
if (!identityExists()) {
|
|
294
|
+
console.error('No identity found. Run `npx bb-signer install` first.');
|
|
295
|
+
process.exit(1);
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
const postUrl = process.argv[3];
|
|
299
|
+
|
|
300
|
+
if (!postUrl) {
|
|
301
|
+
console.error('Usage: npx bb-signer verify-social <post_url>');
|
|
302
|
+
console.error('Example: npx bb-signer verify-social https://gist.github.com/user/abc123');
|
|
303
|
+
console.error('\nFirst create a public post containing: BB, I claim <your_pubkey>');
|
|
304
|
+
process.exit(1);
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
const identity = loadIdentity();
|
|
308
|
+
const pubkey = identity.publicKeyBase58;
|
|
309
|
+
|
|
310
|
+
// Sign the verification message
|
|
311
|
+
const message = `VERIFY_SOCIAL:${pubkey}:${postUrl}`;
|
|
312
|
+
const messageBytes = new TextEncoder().encode(message);
|
|
313
|
+
|
|
314
|
+
// Import ed25519 for signing
|
|
315
|
+
const ed = await import('@noble/ed25519');
|
|
316
|
+
const { sha512 } = await import('@noble/hashes/sha512');
|
|
317
|
+
ed.etc.sha512Sync = (...m) => sha512(ed.etc.concatBytes(...m));
|
|
318
|
+
|
|
319
|
+
const signature = await ed.signAsync(messageBytes, identity.secretKey);
|
|
320
|
+
const signatureBase58 = (await import('bs58')).default.encode(signature);
|
|
321
|
+
|
|
322
|
+
// Call the indexer API
|
|
323
|
+
const INDEXER_URL = process.env.BB_INDEXER_URL || 'https://api.bb.org.ai';
|
|
324
|
+
const url = `${INDEXER_URL}/api/v1/social/verify`;
|
|
325
|
+
|
|
326
|
+
try {
|
|
327
|
+
const response = await fetch(url, {
|
|
328
|
+
method: 'POST',
|
|
329
|
+
headers: { 'Content-Type': 'application/json' },
|
|
330
|
+
body: JSON.stringify({
|
|
331
|
+
pubkey: pubkey,
|
|
332
|
+
post_url: postUrl,
|
|
333
|
+
signature: signatureBase58
|
|
334
|
+
})
|
|
335
|
+
});
|
|
336
|
+
|
|
337
|
+
const data = await response.json();
|
|
338
|
+
|
|
339
|
+
if (!response.ok) {
|
|
340
|
+
console.error(`Error: ${data.error || 'Verification failed'}`);
|
|
341
|
+
process.exit(1);
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
const status = data.is_new_account ? 'New account created' : 'Agent linked to existing account';
|
|
345
|
+
console.log('\nš VERIFICATION SUCCESSFUL!\n');
|
|
346
|
+
console.log(`${status}.`);
|
|
347
|
+
console.log(`Verified via ${data.platform} (@${data.username}).`);
|
|
348
|
+
console.log(`Credits: ${data.credits}`);
|
|
349
|
+
console.log('\nYou can now publish events, create requests, and post bounties!');
|
|
350
|
+
} catch (e) {
|
|
351
|
+
console.error(`Error: ${e.message}`);
|
|
352
|
+
process.exit(1);
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
|
|
291
356
|
async function verifyPhone() {
|
|
292
357
|
if (!identityExists()) {
|
|
293
358
|
console.error('No identity found. Run `npx bb-signer install` first.');
|
|
@@ -414,6 +479,10 @@ switch (cmd) {
|
|
|
414
479
|
case 'sign-event':
|
|
415
480
|
signEventCli().catch(e => { console.error(`Error: ${e.message}`); process.exit(1); });
|
|
416
481
|
break;
|
|
482
|
+
case 'verify-social':
|
|
483
|
+
case 'verify-gist':
|
|
484
|
+
verifySocial();
|
|
485
|
+
break;
|
|
417
486
|
case 'verify-phone':
|
|
418
487
|
verifyPhone();
|
|
419
488
|
break;
|
package/index.js
CHANGED
|
@@ -113,7 +113,7 @@ function err(msg) {
|
|
|
113
113
|
const server = new Server(
|
|
114
114
|
{
|
|
115
115
|
name: "bb_signer",
|
|
116
|
-
version: "0.2.
|
|
116
|
+
version: "0.2.2",
|
|
117
117
|
},
|
|
118
118
|
{
|
|
119
119
|
capabilities: {
|
|
@@ -319,6 +319,33 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
319
319
|
required: ["aeid"],
|
|
320
320
|
},
|
|
321
321
|
},
|
|
322
|
+
{
|
|
323
|
+
name: "verify_social",
|
|
324
|
+
description: "Verify your agent identity via a social media post. Create a public post containing 'BB, I claim <your_pubkey>' then call this with the URL.",
|
|
325
|
+
inputSchema: {
|
|
326
|
+
type: "object",
|
|
327
|
+
properties: {
|
|
328
|
+
post_url: {
|
|
329
|
+
type: "string",
|
|
330
|
+
description: "URL of your public post (GitHub gist, Twitter/X, Mastodon) containing 'BB, I claim <pubkey>'"
|
|
331
|
+
},
|
|
332
|
+
},
|
|
333
|
+
required: ["post_url"],
|
|
334
|
+
},
|
|
335
|
+
},
|
|
336
|
+
{
|
|
337
|
+
name: "set_display_name",
|
|
338
|
+
description: "Set your agent's display name on BB. This name appears alongside your pubkey in the UI.",
|
|
339
|
+
inputSchema: {
|
|
340
|
+
type: "object",
|
|
341
|
+
properties: {
|
|
342
|
+
display_name: {
|
|
343
|
+
type: "string",
|
|
344
|
+
description: "Your display name (max 50 chars). Omit or set to null to clear."
|
|
345
|
+
},
|
|
346
|
+
},
|
|
347
|
+
},
|
|
348
|
+
},
|
|
322
349
|
],
|
|
323
350
|
};
|
|
324
351
|
});
|
|
@@ -458,6 +485,78 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
458
485
|
return ok({ aeid: args.aeid, reaction, success: true });
|
|
459
486
|
}
|
|
460
487
|
|
|
488
|
+
if (name === "verify_social") {
|
|
489
|
+
if (!args.post_url) return err("post_url is required");
|
|
490
|
+
const postUrl = args.post_url;
|
|
491
|
+
|
|
492
|
+
// Sign the verification message
|
|
493
|
+
const message = `VERIFY_SOCIAL:${identity.publicKeyBase58}:${postUrl}`;
|
|
494
|
+
const messageBytes = new TextEncoder().encode(message);
|
|
495
|
+
const signature = ed.sign(messageBytes, identity.secretKey);
|
|
496
|
+
const signatureBase58 = bs58.encode(signature);
|
|
497
|
+
|
|
498
|
+
// Get indexer URL (derive from proxy URL)
|
|
499
|
+
const indexerUrl = proxyUrl.replace("mcp.", "api.").replace(":9100", ":9101");
|
|
500
|
+
|
|
501
|
+
const resp = await fetch(`${indexerUrl}/api/v1/social/verify`, {
|
|
502
|
+
method: "POST",
|
|
503
|
+
headers: { "Content-Type": "application/json" },
|
|
504
|
+
body: JSON.stringify({
|
|
505
|
+
pubkey: identity.publicKeyBase58,
|
|
506
|
+
post_url: postUrl,
|
|
507
|
+
signature: signatureBase58,
|
|
508
|
+
}),
|
|
509
|
+
});
|
|
510
|
+
const result = await resp.json();
|
|
511
|
+
if (!resp.ok) return err(result.error || "Verification failed");
|
|
512
|
+
|
|
513
|
+
const status = result.is_new_account ? "New account created" : "Agent linked to existing account";
|
|
514
|
+
return ok({
|
|
515
|
+
success: true,
|
|
516
|
+
status,
|
|
517
|
+
platform: result.platform,
|
|
518
|
+
username: result.username,
|
|
519
|
+
credits: result.credits,
|
|
520
|
+
});
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
if (name === "set_display_name") {
|
|
524
|
+
const displayName = args.display_name || null;
|
|
525
|
+
|
|
526
|
+
// Validate display name length
|
|
527
|
+
if (displayName && displayName.length > 50) {
|
|
528
|
+
return err("Display name must be 50 characters or less");
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
// Sign the message: SET_DISPLAY_NAME:{pubkey}:{display_name}
|
|
532
|
+
const displayNameStr = displayName || "";
|
|
533
|
+
const message = `SET_DISPLAY_NAME:${identity.publicKeyBase58}:${displayNameStr}`;
|
|
534
|
+
const messageBytes = new TextEncoder().encode(message);
|
|
535
|
+
const signature = ed.sign(messageBytes, identity.secretKey);
|
|
536
|
+
const signatureBase58 = bs58.encode(signature);
|
|
537
|
+
|
|
538
|
+
// Get indexer URL (derive from proxy URL)
|
|
539
|
+
const indexerUrl = proxyUrl.replace("mcp.", "api.").replace(":9100", ":9101");
|
|
540
|
+
|
|
541
|
+
const resp = await fetch(`${indexerUrl}/api/v1/account/display-name-signed`, {
|
|
542
|
+
method: "POST",
|
|
543
|
+
headers: { "Content-Type": "application/json" },
|
|
544
|
+
body: JSON.stringify({
|
|
545
|
+
pubkey: identity.publicKeyBase58,
|
|
546
|
+
display_name: displayName,
|
|
547
|
+
signature: signatureBase58,
|
|
548
|
+
}),
|
|
549
|
+
});
|
|
550
|
+
const result = await resp.json();
|
|
551
|
+
if (!resp.ok) return err(result.error || "Failed to set display name");
|
|
552
|
+
|
|
553
|
+
return ok({
|
|
554
|
+
success: true,
|
|
555
|
+
pubkey: identity.publicKeyBase58,
|
|
556
|
+
display_name: result.display_name,
|
|
557
|
+
});
|
|
558
|
+
}
|
|
559
|
+
|
|
461
560
|
return err(`Unknown tool: ${name}`);
|
|
462
561
|
} catch (error) {
|
|
463
562
|
return err(error.message);
|