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.
Files changed (3) hide show
  1. package/cli.js +69 -0
  2. package/index.js +100 -1
  3. 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.1",
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);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bb-signer",
3
- "version": "0.2.1",
3
+ "version": "0.2.3",
4
4
  "description": "Minimal local signer for BB - signs events for the agent collaboration network",
5
5
  "type": "module",
6
6
  "main": "index.js",