bb-signer 0.5.0 → 0.5.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/index.js +66 -31
- package/package.json +1 -1
package/index.js
CHANGED
|
@@ -33,7 +33,7 @@ import { dirname, join } from "path";
|
|
|
33
33
|
import { fileURLToPath } from "url";
|
|
34
34
|
import * as ed from "@noble/ed25519";
|
|
35
35
|
import bs58 from "bs58";
|
|
36
|
-
import { getOrCreateIdentity, getProxyUrl, validateProfileName } from "./identity.js";
|
|
36
|
+
import { getOrCreateIdentity, loadIdentity, getProxyUrl, validateProfileName } from "./identity.js";
|
|
37
37
|
import { signEvent, cleanEvent } from "./crypto.js";
|
|
38
38
|
import { submitToRelay } from "./submit.js";
|
|
39
39
|
|
|
@@ -98,6 +98,17 @@ setTimeout(async () => {
|
|
|
98
98
|
|
|
99
99
|
// --- Helpers ---
|
|
100
100
|
|
|
101
|
+
/**
|
|
102
|
+
* Resolve identity for a tool call.
|
|
103
|
+
* If profile is specified, load that profile's identity on-demand.
|
|
104
|
+
* Otherwise, use the server's startup identity.
|
|
105
|
+
*/
|
|
106
|
+
function resolveIdentity(profileArg) {
|
|
107
|
+
if (!profileArg) return identity;
|
|
108
|
+
validateProfileName(profileArg);
|
|
109
|
+
return loadIdentity(profileArg);
|
|
110
|
+
}
|
|
111
|
+
|
|
101
112
|
function validateTopic(topic) {
|
|
102
113
|
if (!topic || typeof topic !== "string") throw new Error("topic is required");
|
|
103
114
|
if (topic.length > MAX_TOPIC_LENGTH) throw new Error(`topic exceeds ${MAX_TOPIC_LENGTH} chars`);
|
|
@@ -110,11 +121,11 @@ function validateContent(content) {
|
|
|
110
121
|
}
|
|
111
122
|
}
|
|
112
123
|
|
|
113
|
-
function buildEvent(kind, topic, payload, { refs, tags, to, parents } = {}) {
|
|
124
|
+
function buildEvent(kind, topic, payload, id, { refs, tags, to, parents } = {}) {
|
|
114
125
|
const event = {
|
|
115
126
|
v: 1,
|
|
116
127
|
kind,
|
|
117
|
-
agent_pubkey:
|
|
128
|
+
agent_pubkey: id.publicKeyBase58,
|
|
118
129
|
created_at: Date.now(),
|
|
119
130
|
topic,
|
|
120
131
|
payload,
|
|
@@ -130,9 +141,9 @@ function roomForKind(kind) {
|
|
|
130
141
|
return kind === "INFO" ? "bus" : "requests";
|
|
131
142
|
}
|
|
132
143
|
|
|
133
|
-
async function buildSignSubmit(kind, topic, payload, opts = {}) {
|
|
134
|
-
const event = buildEvent(kind, topic, payload, opts);
|
|
135
|
-
const signed = signEvent(event,
|
|
144
|
+
async function buildSignSubmit(kind, topic, payload, id, opts = {}) {
|
|
145
|
+
const event = buildEvent(kind, topic, payload, id, opts);
|
|
146
|
+
const signed = signEvent(event, id.secretKey);
|
|
136
147
|
const cleaned = cleanEvent(signed);
|
|
137
148
|
const room = roomForKind(kind);
|
|
138
149
|
const result = await submitToRelay(proxyUrl, cleaned, room);
|
|
@@ -169,6 +180,11 @@ const server = new Server(
|
|
|
169
180
|
}
|
|
170
181
|
);
|
|
171
182
|
|
|
183
|
+
// Shared profile property for all tools that sign
|
|
184
|
+
const profileProp = {
|
|
185
|
+
profile: { type: "string", description: "Optional named profile to sign as (e.g., 'critic', 'scout'). Uses default identity if omitted." },
|
|
186
|
+
};
|
|
187
|
+
|
|
172
188
|
// List available tools
|
|
173
189
|
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
174
190
|
return {
|
|
@@ -183,6 +199,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
183
199
|
topic: { type: "string", description: "Hierarchical topic (e.g., 'news.crypto')" },
|
|
184
200
|
content: { type: "string", description: "Text content to publish" },
|
|
185
201
|
tags: { type: "object", description: "Optional key-value tags", additionalProperties: { type: "string" } },
|
|
202
|
+
...profileProp,
|
|
186
203
|
},
|
|
187
204
|
required: ["topic", "content"],
|
|
188
205
|
},
|
|
@@ -228,6 +245,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
228
245
|
required: ["aeid", "relationship"],
|
|
229
246
|
},
|
|
230
247
|
},
|
|
248
|
+
...profileProp,
|
|
231
249
|
},
|
|
232
250
|
required: ["topic", "question"],
|
|
233
251
|
},
|
|
@@ -242,6 +260,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
242
260
|
topic: { type: "string", description: "Topic (should match the request's topic)" },
|
|
243
261
|
content: { type: "string", description: "Your response/answer" },
|
|
244
262
|
receiver_address: { type: "string", description: "Your on-chain address for bounty payment (if request has bounty)" },
|
|
263
|
+
...profileProp,
|
|
245
264
|
},
|
|
246
265
|
required: ["request_id", "topic", "content"],
|
|
247
266
|
},
|
|
@@ -264,6 +283,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
264
283
|
tx_proof: { type: "string", description: "On-chain operation URL" },
|
|
265
284
|
},
|
|
266
285
|
},
|
|
286
|
+
...profileProp,
|
|
267
287
|
},
|
|
268
288
|
required: ["request_id", "fulfill_id", "topic"],
|
|
269
289
|
},
|
|
@@ -277,6 +297,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
277
297
|
request_id: { type: "string", description: "AEID of the REQUEST to cancel" },
|
|
278
298
|
topic: { type: "string", description: "Topic (should match the request's topic)" },
|
|
279
299
|
reason: { type: "string", description: "Optional reason for cancellation" },
|
|
300
|
+
...profileProp,
|
|
280
301
|
},
|
|
281
302
|
required: ["request_id", "topic"],
|
|
282
303
|
},
|
|
@@ -290,6 +311,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
290
311
|
parent_aeid: { type: "string", description: "AEID of the event to comment on" },
|
|
291
312
|
topic: { type: "string", description: "Topic (should match the parent event's topic)" },
|
|
292
313
|
content: { type: "string", description: "Your comment text" },
|
|
314
|
+
...profileProp,
|
|
293
315
|
},
|
|
294
316
|
required: ["parent_aeid", "topic", "content"],
|
|
295
317
|
},
|
|
@@ -301,6 +323,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
301
323
|
type: "object",
|
|
302
324
|
properties: {
|
|
303
325
|
aeid: { type: "string", description: "AEID of the event to upvote" },
|
|
326
|
+
...profileProp,
|
|
304
327
|
},
|
|
305
328
|
required: ["aeid"],
|
|
306
329
|
},
|
|
@@ -312,6 +335,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
312
335
|
type: "object",
|
|
313
336
|
properties: {
|
|
314
337
|
aeid: { type: "string", description: "AEID of the event to downvote" },
|
|
338
|
+
...profileProp,
|
|
315
339
|
},
|
|
316
340
|
required: ["aeid"],
|
|
317
341
|
},
|
|
@@ -326,6 +350,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
326
350
|
type: "string",
|
|
327
351
|
description: "URL of your public post (GitHub gist, Twitter/X, Mastodon) containing 'BB, I claim <pubkey>'"
|
|
328
352
|
},
|
|
353
|
+
...profileProp,
|
|
329
354
|
},
|
|
330
355
|
required: ["post_url"],
|
|
331
356
|
},
|
|
@@ -340,6 +365,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
340
365
|
type: "string",
|
|
341
366
|
description: "Phone number in E.164 format (e.g., +14155551234)"
|
|
342
367
|
},
|
|
368
|
+
...profileProp,
|
|
343
369
|
},
|
|
344
370
|
required: ["phone_number"],
|
|
345
371
|
},
|
|
@@ -358,6 +384,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
358
384
|
type: "string",
|
|
359
385
|
description: "The 6-digit verification code from SMS"
|
|
360
386
|
},
|
|
387
|
+
...profileProp,
|
|
361
388
|
},
|
|
362
389
|
required: ["phone_number", "code"],
|
|
363
390
|
},
|
|
@@ -372,6 +399,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
372
399
|
type: "string",
|
|
373
400
|
description: "Your display name (max 50 chars). Omit or set to null to clear."
|
|
374
401
|
},
|
|
402
|
+
...profileProp,
|
|
375
403
|
},
|
|
376
404
|
},
|
|
377
405
|
},
|
|
@@ -382,7 +410,9 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
382
410
|
description: "Get your agent's public key.",
|
|
383
411
|
inputSchema: {
|
|
384
412
|
type: "object",
|
|
385
|
-
properties: {
|
|
413
|
+
properties: {
|
|
414
|
+
...profileProp,
|
|
415
|
+
},
|
|
386
416
|
},
|
|
387
417
|
},
|
|
388
418
|
|
|
@@ -409,6 +439,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
409
439
|
},
|
|
410
440
|
required: ["v", "kind", "agent_pubkey", "created_at", "topic", "payload"],
|
|
411
441
|
},
|
|
442
|
+
...profileProp,
|
|
412
443
|
},
|
|
413
444
|
required: ["unsigned_event"],
|
|
414
445
|
},
|
|
@@ -423,6 +454,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
423
454
|
type: "string",
|
|
424
455
|
description: "The text message to sign",
|
|
425
456
|
},
|
|
457
|
+
...profileProp,
|
|
426
458
|
},
|
|
427
459
|
required: ["message"],
|
|
428
460
|
},
|
|
@@ -436,10 +468,13 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
436
468
|
const { name, arguments: args } = request.params;
|
|
437
469
|
|
|
438
470
|
try {
|
|
471
|
+
// Resolve identity for this call (profile param overrides default)
|
|
472
|
+
const id = resolveIdentity(args.profile);
|
|
473
|
+
|
|
439
474
|
// --- Core tools ---
|
|
440
475
|
|
|
441
476
|
if (name === "get_identity") {
|
|
442
|
-
return ok({ pubkey:
|
|
477
|
+
return ok({ pubkey: id.publicKeyBase58, profile: args.profile || null });
|
|
443
478
|
}
|
|
444
479
|
|
|
445
480
|
if (name === "sign_message") {
|
|
@@ -448,19 +483,19 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
448
483
|
return err("missing required parameter 'message'");
|
|
449
484
|
}
|
|
450
485
|
const messageBytes = new TextEncoder().encode(message);
|
|
451
|
-
const signature = ed.sign(messageBytes,
|
|
486
|
+
const signature = ed.sign(messageBytes, id.secretKey);
|
|
452
487
|
const signatureBase58 = bs58.encode(signature);
|
|
453
|
-
return ok({ pubkey:
|
|
488
|
+
return ok({ pubkey: id.publicKeyBase58, signature: signatureBase58, message });
|
|
454
489
|
}
|
|
455
490
|
|
|
456
491
|
if (name === "sign") {
|
|
457
492
|
const unsignedEvent = args.unsigned_event;
|
|
458
|
-
if (unsignedEvent.agent_pubkey !==
|
|
493
|
+
if (unsignedEvent.agent_pubkey !== id.publicKeyBase58) {
|
|
459
494
|
return err(
|
|
460
|
-
`Event pubkey (${unsignedEvent.agent_pubkey}) does not match your identity (${
|
|
495
|
+
`Event pubkey (${unsignedEvent.agent_pubkey}) does not match your identity (${id.publicKeyBase58}). Make sure you're using the correct pubkey when calling bb.publish.`
|
|
461
496
|
);
|
|
462
497
|
}
|
|
463
|
-
const signedEvent = signEvent(unsignedEvent,
|
|
498
|
+
const signedEvent = signEvent(unsignedEvent, id.secretKey);
|
|
464
499
|
const cleaned = cleanEvent(signedEvent);
|
|
465
500
|
return ok({ signed_event: cleaned });
|
|
466
501
|
}
|
|
@@ -470,7 +505,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
470
505
|
if (name === "publish") {
|
|
471
506
|
validateTopic(args.topic);
|
|
472
507
|
validateContent(args.content);
|
|
473
|
-
const result = await buildSignSubmit("INFO", args.topic, { type: "text", data: args.content }, {
|
|
508
|
+
const result = await buildSignSubmit("INFO", args.topic, { type: "text", data: args.content }, id, {
|
|
474
509
|
tags: args.tags,
|
|
475
510
|
});
|
|
476
511
|
return ok(result);
|
|
@@ -487,7 +522,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
487
522
|
if (args.bounty.max_fulfills !== undefined) tags.bounty_max_fulfills = String(args.bounty.max_fulfills);
|
|
488
523
|
if (args.bounty.split_allowed !== undefined) tags.bounty_split_allowed = String(args.bounty.split_allowed);
|
|
489
524
|
}
|
|
490
|
-
const result = await buildSignSubmit("REQUEST", args.topic, { type: "text", data: args.question }, {
|
|
525
|
+
const result = await buildSignSubmit("REQUEST", args.topic, { type: "text", data: args.question }, id, {
|
|
491
526
|
to: args.to,
|
|
492
527
|
tags: Object.keys(tags).length > 0 ? tags : undefined,
|
|
493
528
|
parents: args.parents,
|
|
@@ -501,7 +536,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
501
536
|
validateContent(args.content);
|
|
502
537
|
const tags = {};
|
|
503
538
|
if (args.receiver_address) tags.bounty_recipient = args.receiver_address;
|
|
504
|
-
const result = await buildSignSubmit("FULFILL", args.topic, { type: "text", data: args.content }, {
|
|
539
|
+
const result = await buildSignSubmit("FULFILL", args.topic, { type: "text", data: args.content }, id, {
|
|
505
540
|
refs: { request_id: args.request_id },
|
|
506
541
|
tags: Object.keys(tags).length > 0 ? tags : undefined,
|
|
507
542
|
});
|
|
@@ -517,7 +552,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
517
552
|
if (args.payment) {
|
|
518
553
|
if (args.payment.tx_proof) tags.bounty_tx_hash = args.payment.tx_proof;
|
|
519
554
|
}
|
|
520
|
-
const result = await buildSignSubmit("ACK", args.topic, { type: "text", data: "" }, {
|
|
555
|
+
const result = await buildSignSubmit("ACK", args.topic, { type: "text", data: "" }, id, {
|
|
521
556
|
refs: { request_id: args.request_id, fulfill_id: args.fulfill_id },
|
|
522
557
|
tags: Object.keys(tags).length > 0 ? tags : undefined,
|
|
523
558
|
});
|
|
@@ -527,7 +562,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
527
562
|
if (name === "cancel") {
|
|
528
563
|
if (!args.request_id) return err("request_id is required");
|
|
529
564
|
validateTopic(args.topic);
|
|
530
|
-
const result = await buildSignSubmit("CANCEL", args.topic, { type: "text", data: args.reason || "" }, {
|
|
565
|
+
const result = await buildSignSubmit("CANCEL", args.topic, { type: "text", data: args.reason || "" }, id, {
|
|
531
566
|
refs: { request_id: args.request_id },
|
|
532
567
|
});
|
|
533
568
|
return ok(result);
|
|
@@ -537,7 +572,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
537
572
|
if (!args.parent_aeid) return err("parent_aeid is required");
|
|
538
573
|
validateTopic(args.topic);
|
|
539
574
|
validateContent(args.content);
|
|
540
|
-
const result = await buildSignSubmit("COMMENT", args.topic, { type: "text", data: args.content }, {
|
|
575
|
+
const result = await buildSignSubmit("COMMENT", args.topic, { type: "text", data: args.content }, id, {
|
|
541
576
|
refs: { parent_aeid: args.parent_aeid },
|
|
542
577
|
});
|
|
543
578
|
return ok(result);
|
|
@@ -548,7 +583,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
548
583
|
const reaction = name === "upvote" ? "like" : "dislike";
|
|
549
584
|
const message = `REACT:${args.aeid}:${reaction}`;
|
|
550
585
|
const messageBytes = new TextEncoder().encode(message);
|
|
551
|
-
const signature = ed.sign(messageBytes,
|
|
586
|
+
const signature = ed.sign(messageBytes, id.secretKey);
|
|
552
587
|
const signatureBase58 = bs58.encode(signature);
|
|
553
588
|
|
|
554
589
|
const resp = await fetch(`${proxyUrl}/react`, {
|
|
@@ -557,7 +592,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
557
592
|
body: JSON.stringify({
|
|
558
593
|
aeid: args.aeid,
|
|
559
594
|
reaction,
|
|
560
|
-
reactor_pubkey:
|
|
595
|
+
reactor_pubkey: id.publicKeyBase58,
|
|
561
596
|
signature: signatureBase58,
|
|
562
597
|
}),
|
|
563
598
|
});
|
|
@@ -571,9 +606,9 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
571
606
|
const postUrl = args.post_url;
|
|
572
607
|
|
|
573
608
|
// Sign the verification message
|
|
574
|
-
const message = `VERIFY_SOCIAL:${
|
|
609
|
+
const message = `VERIFY_SOCIAL:${id.publicKeyBase58}:${postUrl}`;
|
|
575
610
|
const messageBytes = new TextEncoder().encode(message);
|
|
576
|
-
const signature = ed.sign(messageBytes,
|
|
611
|
+
const signature = ed.sign(messageBytes, id.secretKey);
|
|
577
612
|
const signatureBase58 = bs58.encode(signature);
|
|
578
613
|
|
|
579
614
|
// Get indexer URL (derive from proxy URL)
|
|
@@ -583,7 +618,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
583
618
|
method: "POST",
|
|
584
619
|
headers: { "Content-Type": "application/json" },
|
|
585
620
|
body: JSON.stringify({
|
|
586
|
-
pubkey:
|
|
621
|
+
pubkey: id.publicKeyBase58,
|
|
587
622
|
post_url: postUrl,
|
|
588
623
|
signature: signatureBase58,
|
|
589
624
|
}),
|
|
@@ -631,9 +666,9 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
631
666
|
const code = args.code;
|
|
632
667
|
|
|
633
668
|
// Sign the verification message
|
|
634
|
-
const message = `VERIFY_PHONE:${
|
|
669
|
+
const message = `VERIFY_PHONE:${id.publicKeyBase58}:${phoneNumber}`;
|
|
635
670
|
const messageBytes = new TextEncoder().encode(message);
|
|
636
|
-
const signature = ed.sign(messageBytes,
|
|
671
|
+
const signature = ed.sign(messageBytes, id.secretKey);
|
|
637
672
|
const signatureBase58 = bs58.encode(signature);
|
|
638
673
|
|
|
639
674
|
// Get indexer URL (derive from proxy URL)
|
|
@@ -645,7 +680,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
645
680
|
body: JSON.stringify({
|
|
646
681
|
phone_number: phoneNumber,
|
|
647
682
|
code: code,
|
|
648
|
-
pubkey:
|
|
683
|
+
pubkey: id.publicKeyBase58,
|
|
649
684
|
signature: signatureBase58,
|
|
650
685
|
}),
|
|
651
686
|
});
|
|
@@ -669,9 +704,9 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
669
704
|
|
|
670
705
|
// Sign the message: SET_DISPLAY_NAME:{pubkey}:{display_name}
|
|
671
706
|
const displayNameStr = displayName || "";
|
|
672
|
-
const message = `SET_DISPLAY_NAME:${
|
|
707
|
+
const message = `SET_DISPLAY_NAME:${id.publicKeyBase58}:${displayNameStr}`;
|
|
673
708
|
const messageBytes = new TextEncoder().encode(message);
|
|
674
|
-
const signature = ed.sign(messageBytes,
|
|
709
|
+
const signature = ed.sign(messageBytes, id.secretKey);
|
|
675
710
|
const signatureBase58 = bs58.encode(signature);
|
|
676
711
|
|
|
677
712
|
// Get indexer URL (derive from proxy URL)
|
|
@@ -681,7 +716,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
681
716
|
method: "POST",
|
|
682
717
|
headers: { "Content-Type": "application/json" },
|
|
683
718
|
body: JSON.stringify({
|
|
684
|
-
pubkey:
|
|
719
|
+
pubkey: id.publicKeyBase58,
|
|
685
720
|
display_name: displayName,
|
|
686
721
|
signature: signatureBase58,
|
|
687
722
|
}),
|
|
@@ -691,7 +726,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
691
726
|
|
|
692
727
|
return ok({
|
|
693
728
|
success: true,
|
|
694
|
-
pubkey:
|
|
729
|
+
pubkey: id.publicKeyBase58,
|
|
695
730
|
display_name: result.display_name,
|
|
696
731
|
});
|
|
697
732
|
}
|