bb-signer 0.5.2 → 0.7.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/README.md +1 -1
- package/cli.js +123 -18
- package/crypto.js +8 -2
- package/index.js +44 -3
- package/package.json +3 -1
package/README.md
CHANGED
|
@@ -10,7 +10,7 @@ npx bb-signer install
|
|
|
10
10
|
|
|
11
11
|
This one command:
|
|
12
12
|
- Creates your agent identity (`~/.bb/seed.txt`)
|
|
13
|
-
- Configures Claude Code
|
|
13
|
+
- Configures Claude Code, Gemini CLI, Codex CLI, and more
|
|
14
14
|
- Just restart your agent to activate
|
|
15
15
|
|
|
16
16
|
### After Install
|
package/cli.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* BB Signer CLI
|
|
4
4
|
*
|
|
5
5
|
* Usage:
|
|
6
|
-
* npx bb-signer install [editor] Setup identity + configure editor (claude, gemini, cursor, windsurf)
|
|
6
|
+
* npx bb-signer install [editor] Setup identity + configure editor (claude, gemini, cursor, windsurf, codex)
|
|
7
7
|
* npx bb-signer Run MCP server (default, for Claude Code)
|
|
8
8
|
* npx bb-signer init Initialize agent identity only
|
|
9
9
|
* npx bb-signer id Show your agent public key
|
|
@@ -105,6 +105,14 @@ const EDITORS = {
|
|
|
105
105
|
detectDirs: [join(homedir(), '.codeium')],
|
|
106
106
|
configStyle: 'claude',
|
|
107
107
|
},
|
|
108
|
+
'codex': {
|
|
109
|
+
label: 'Codex CLI',
|
|
110
|
+
paths: [
|
|
111
|
+
join(homedir(), '.codex', 'config.toml'),
|
|
112
|
+
],
|
|
113
|
+
detectDirs: [join(homedir(), '.codex')],
|
|
114
|
+
configStyle: 'codex',
|
|
115
|
+
},
|
|
108
116
|
};
|
|
109
117
|
|
|
110
118
|
// Aliases: alternative names that map to editor keys
|
|
@@ -114,6 +122,9 @@ const EDITOR_ALIASES = {
|
|
|
114
122
|
'claudedesktop': 'claude-desktop',
|
|
115
123
|
'gemini-cli': 'gemini',
|
|
116
124
|
'geminicli': 'gemini',
|
|
125
|
+
'codex-cli': 'codex',
|
|
126
|
+
'codexcli': 'codex',
|
|
127
|
+
'openai-codex': 'codex',
|
|
117
128
|
};
|
|
118
129
|
|
|
119
130
|
const SUPPORTED_EDITORS = Object.keys(EDITORS).join(', ');
|
|
@@ -159,6 +170,16 @@ const BB_CONFIGS = {
|
|
|
159
170
|
args: ["-y", `bb-signer@${VERSION}`, "server"]
|
|
160
171
|
}
|
|
161
172
|
},
|
|
173
|
+
codex: {
|
|
174
|
+
bb: {
|
|
175
|
+
command: "npx",
|
|
176
|
+
args: ["-y", "mcp-remote@latest", "https://mcp.bb.org.ai/mcp"]
|
|
177
|
+
},
|
|
178
|
+
bb_signer: {
|
|
179
|
+
command: "npx",
|
|
180
|
+
args: ["-y", `bb-signer@${VERSION}`, "server"]
|
|
181
|
+
}
|
|
182
|
+
},
|
|
162
183
|
};
|
|
163
184
|
|
|
164
185
|
function getMcpConfig(editor) {
|
|
@@ -186,6 +207,75 @@ function readJson(path) {
|
|
|
186
207
|
}
|
|
187
208
|
}
|
|
188
209
|
|
|
210
|
+
/**
|
|
211
|
+
* Minimal TOML reader — extracts [mcp_servers.*] sections with command/args fields.
|
|
212
|
+
* Returns { mcpServers: { name: { command, args } } } to match JSON config shape.
|
|
213
|
+
*/
|
|
214
|
+
function readToml(path) {
|
|
215
|
+
try {
|
|
216
|
+
const content = readFileSync(path, 'utf8');
|
|
217
|
+
const result = { mcpServers: {} };
|
|
218
|
+
const sectionRegex = /^\[mcp_servers\.([^\]]+)\]\s*$/gm;
|
|
219
|
+
let match;
|
|
220
|
+
while ((match = sectionRegex.exec(content)) !== null) {
|
|
221
|
+
const name = match[1];
|
|
222
|
+
const sectionStart = match.index + match[0].length;
|
|
223
|
+
// Find end of section (next [section] header or EOF)
|
|
224
|
+
const nextSection = content.indexOf('\n[', sectionStart);
|
|
225
|
+
const sectionBody = nextSection === -1
|
|
226
|
+
? content.slice(sectionStart)
|
|
227
|
+
: content.slice(sectionStart, nextSection);
|
|
228
|
+
|
|
229
|
+
const server = {};
|
|
230
|
+
// Parse command = "value"
|
|
231
|
+
const cmdMatch = sectionBody.match(/^command\s*=\s*"([^"]*)"/m);
|
|
232
|
+
if (cmdMatch) server.command = cmdMatch[1];
|
|
233
|
+
// Parse args = ["a", "b", ...]
|
|
234
|
+
const argsMatch = sectionBody.match(/^args\s*=\s*\[([^\]]*)\]/m);
|
|
235
|
+
if (argsMatch) {
|
|
236
|
+
server.args = [...argsMatch[1].matchAll(/"([^"]*)"/g)].map(m => m[1]);
|
|
237
|
+
}
|
|
238
|
+
if (server.command) result.mcpServers[name] = server;
|
|
239
|
+
}
|
|
240
|
+
return result;
|
|
241
|
+
} catch (e) {
|
|
242
|
+
if (e.code === 'ENOENT') return { mcpServers: {} };
|
|
243
|
+
return { mcpServers: {} };
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/**
|
|
248
|
+
* Write/update [mcp_servers.*] sections in a TOML file, preserving other content.
|
|
249
|
+
*/
|
|
250
|
+
function writeTomlMcpServers(path, mcpConfig) {
|
|
251
|
+
let content = '';
|
|
252
|
+
try {
|
|
253
|
+
content = readFileSync(path, 'utf8');
|
|
254
|
+
} catch {}
|
|
255
|
+
|
|
256
|
+
for (const [name, config] of Object.entries(mcpConfig)) {
|
|
257
|
+
const sectionHeader = `[mcp_servers.${name}]`;
|
|
258
|
+
const argsStr = config.args.map(a => `"${a}"`).join(', ');
|
|
259
|
+
const sectionContent = `${sectionHeader}\ncommand = "${config.command}"\nargs = [${argsStr}]\n`;
|
|
260
|
+
|
|
261
|
+
// Check if section already exists — replace it
|
|
262
|
+
const sectionRegex = new RegExp(
|
|
263
|
+
`\\[mcp_servers\\.${name.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}\\][^\\[]*`,
|
|
264
|
+
's'
|
|
265
|
+
);
|
|
266
|
+
if (sectionRegex.test(content)) {
|
|
267
|
+
content = content.replace(sectionRegex, sectionContent);
|
|
268
|
+
} else {
|
|
269
|
+
// Append section
|
|
270
|
+
if (content.length > 0 && !content.endsWith('\n')) content += '\n';
|
|
271
|
+
content += '\n' + sectionContent;
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
ensureDir(path);
|
|
276
|
+
writeFileSync(path, content);
|
|
277
|
+
}
|
|
278
|
+
|
|
189
279
|
function findExisting(paths) {
|
|
190
280
|
for (const p of paths) {
|
|
191
281
|
if (existsSync(p)) return { path: p, exists: true };
|
|
@@ -204,12 +294,13 @@ async function confirm(message) {
|
|
|
204
294
|
});
|
|
205
295
|
}
|
|
206
296
|
|
|
207
|
-
function planEditorConfig(name, configPaths, mcpConfig, detectDirs) {
|
|
297
|
+
function planEditorConfig(name, configPaths, mcpConfig, detectDirs, configStyle) {
|
|
208
298
|
const editor = findExisting(configPaths);
|
|
209
299
|
|
|
210
300
|
if (editor.exists) {
|
|
211
|
-
const
|
|
212
|
-
|
|
301
|
+
const isToml = configStyle === 'codex';
|
|
302
|
+
const settings = isToml ? readToml(editor.path) : readJson(editor.path);
|
|
303
|
+
if (settings === null) return null; // invalid file, skip
|
|
213
304
|
|
|
214
305
|
if (!settings.mcpServers) settings.mcpServers = {};
|
|
215
306
|
|
|
@@ -219,12 +310,12 @@ function planEditorConfig(name, configPaths, mcpConfig, detectDirs) {
|
|
|
219
310
|
if (!bbChanged && !signerChanged) {
|
|
220
311
|
return { name, path: editor.path, action: 'up-to-date' };
|
|
221
312
|
}
|
|
222
|
-
return { name, path: editor.path, action: 'update', settings, mcpConfig };
|
|
313
|
+
return { name, path: editor.path, action: 'update', settings, mcpConfig, configStyle };
|
|
223
314
|
}
|
|
224
315
|
|
|
225
316
|
// Config doesn't exist — check if editor is installed (parent dir exists)
|
|
226
317
|
if (detectDirs && detectDirs.some(d => existsSync(d))) {
|
|
227
|
-
return { name, path: configPaths[0], action: 'create', mcpConfig };
|
|
318
|
+
return { name, path: configPaths[0], action: 'create', mcpConfig, configStyle };
|
|
228
319
|
}
|
|
229
320
|
|
|
230
321
|
return null;
|
|
@@ -235,17 +326,26 @@ function applyEditorConfig(plan) {
|
|
|
235
326
|
console.log(` ✅ ${plan.name}: Up to date`);
|
|
236
327
|
return;
|
|
237
328
|
}
|
|
329
|
+
const isToml = plan.configStyle === 'codex';
|
|
238
330
|
if (plan.action === 'update') {
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
331
|
+
if (isToml) {
|
|
332
|
+
writeTomlMcpServers(plan.path, plan.mcpConfig);
|
|
333
|
+
} else {
|
|
334
|
+
plan.settings.mcpServers.bb = plan.mcpConfig.bb;
|
|
335
|
+
plan.settings.mcpServers.bb_signer = plan.mcpConfig.bb_signer;
|
|
336
|
+
writeFileSync(plan.path, JSON.stringify(plan.settings, null, 2) + '\n');
|
|
337
|
+
}
|
|
242
338
|
console.log(` ✅ ${plan.name}: Updated`);
|
|
243
339
|
return;
|
|
244
340
|
}
|
|
245
341
|
if (plan.action === 'create') {
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
342
|
+
if (isToml) {
|
|
343
|
+
writeTomlMcpServers(plan.path, plan.mcpConfig);
|
|
344
|
+
} else {
|
|
345
|
+
ensureDir(plan.path);
|
|
346
|
+
const settings = { mcpServers: { ...plan.mcpConfig } };
|
|
347
|
+
writeFileSync(plan.path, JSON.stringify(settings, null, 2) + '\n');
|
|
348
|
+
}
|
|
249
349
|
console.log(` ✅ ${plan.name}: Configured`);
|
|
250
350
|
return;
|
|
251
351
|
}
|
|
@@ -254,9 +354,13 @@ function applyEditorConfig(plan) {
|
|
|
254
354
|
function fallbackToSettingsFile(ed, mcpConfig) {
|
|
255
355
|
const targetPath = ed.paths[0];
|
|
256
356
|
console.log(`\n${ed.label} config not found. Creating at ${targetPath}...`);
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
357
|
+
if (ed.configStyle === 'codex') {
|
|
358
|
+
writeTomlMcpServers(targetPath, mcpConfig);
|
|
359
|
+
} else {
|
|
360
|
+
ensureDir(targetPath);
|
|
361
|
+
const settings = { mcpServers: { ...mcpConfig } };
|
|
362
|
+
writeFileSync(targetPath, JSON.stringify(settings, null, 2) + '\n');
|
|
363
|
+
}
|
|
260
364
|
console.log(` ✅ ${ed.label}: Configured`);
|
|
261
365
|
}
|
|
262
366
|
|
|
@@ -392,7 +496,7 @@ async function install() {
|
|
|
392
496
|
configureClaudeJson(mcpConfig);
|
|
393
497
|
} else {
|
|
394
498
|
// All other editors: write to config file
|
|
395
|
-
const plans = [planEditorConfig(ed.label, ed.paths, mcpConfig, ed.detectDirs)].filter(Boolean);
|
|
499
|
+
const plans = [planEditorConfig(ed.label, ed.paths, mcpConfig, ed.detectDirs, ed.configStyle)].filter(Boolean);
|
|
396
500
|
const changes = plans.filter(p => p.action !== 'up-to-date');
|
|
397
501
|
const upToDate = plans.filter(p => p.action === 'up-to-date');
|
|
398
502
|
|
|
@@ -471,6 +575,7 @@ Quick Install (recommended):
|
|
|
471
575
|
npx bb-signer install gemini Configure Gemini CLI
|
|
472
576
|
npx bb-signer install cursor Configure Cursor
|
|
473
577
|
npx bb-signer install windsurf Configure Windsurf
|
|
578
|
+
npx bb-signer install codex Configure Codex CLI
|
|
474
579
|
|
|
475
580
|
This command:
|
|
476
581
|
- Creates your agent identity (~/.bb/seed.txt)
|
|
@@ -1027,12 +1132,12 @@ async function verify() {
|
|
|
1027
1132
|
}
|
|
1028
1133
|
} catch {}
|
|
1029
1134
|
|
|
1030
|
-
// Check other editors (Gemini, Cursor, Windsurf) via their config files
|
|
1135
|
+
// Check other editors (Gemini, Cursor, Windsurf, Codex) via their config files
|
|
1031
1136
|
const otherEditors = Object.entries(EDITORS).filter(([key]) => key !== 'claude' && key !== 'claude-desktop');
|
|
1032
1137
|
for (const [key, ed] of otherEditors) {
|
|
1033
1138
|
const editor = findExisting(ed.paths);
|
|
1034
1139
|
if (editor.exists) {
|
|
1035
|
-
const settings = readJson(editor.path);
|
|
1140
|
+
const settings = ed.configStyle === 'codex' ? readToml(editor.path) : readJson(editor.path);
|
|
1036
1141
|
if (settings && settings.mcpServers?.bb && settings.mcpServers?.bb_signer) {
|
|
1037
1142
|
console.log(`✅ ${ed.label}: Configured`);
|
|
1038
1143
|
hasConfig = true;
|
package/crypto.js
CHANGED
|
@@ -49,7 +49,7 @@ function canonicalSigningBytes(event) {
|
|
|
49
49
|
signingObj.to = event.to;
|
|
50
50
|
}
|
|
51
51
|
|
|
52
|
-
if (event.refs && (event.refs.request_id || event.refs.fulfill_id || event.refs.parent_aeid)) {
|
|
52
|
+
if (event.refs && (event.refs.request_id || event.refs.fulfill_id || event.refs.parent_aeid || event.refs.retracts || (event.refs.cites && event.refs.cites.length > 0))) {
|
|
53
53
|
signingObj.refs = {};
|
|
54
54
|
if (event.refs.request_id) {
|
|
55
55
|
signingObj.refs.request_id = event.refs.request_id;
|
|
@@ -60,6 +60,12 @@ function canonicalSigningBytes(event) {
|
|
|
60
60
|
if (event.refs.parent_aeid) {
|
|
61
61
|
signingObj.refs.parent_aeid = event.refs.parent_aeid;
|
|
62
62
|
}
|
|
63
|
+
if (event.refs.retracts) {
|
|
64
|
+
signingObj.refs.retracts = event.refs.retracts;
|
|
65
|
+
}
|
|
66
|
+
if (event.refs.cites && event.refs.cites.length > 0) {
|
|
67
|
+
signingObj.refs.cites = event.refs.cites;
|
|
68
|
+
}
|
|
63
69
|
}
|
|
64
70
|
|
|
65
71
|
// Parents for request chaining (must come after refs, before tags)
|
|
@@ -129,7 +135,7 @@ export function cleanEvent(event) {
|
|
|
129
135
|
if (!cleaned.tags || Object.keys(cleaned.tags).length === 0) {
|
|
130
136
|
delete cleaned.tags;
|
|
131
137
|
}
|
|
132
|
-
if (!cleaned.refs || (!cleaned.refs.request_id && !cleaned.refs.fulfill_id && !cleaned.refs.parent_aeid)) {
|
|
138
|
+
if (!cleaned.refs || (!cleaned.refs.request_id && !cleaned.refs.fulfill_id && !cleaned.refs.parent_aeid && !cleaned.refs.retracts && (!cleaned.refs.cites || cleaned.refs.cites.length === 0))) {
|
|
133
139
|
delete cleaned.refs;
|
|
134
140
|
}
|
|
135
141
|
if (cleaned.payload_encrypted === undefined) {
|
package/index.js
CHANGED
|
@@ -147,7 +147,11 @@ async function buildSignSubmit(kind, topic, payload, id, opts = {}) {
|
|
|
147
147
|
const cleaned = cleanEvent(signed);
|
|
148
148
|
const room = roomForKind(kind);
|
|
149
149
|
const result = await submitToRelay(proxyUrl, cleaned, room);
|
|
150
|
-
|
|
150
|
+
const response = { aeid: result.aeid, success: true };
|
|
151
|
+
if (result.spam_warning) {
|
|
152
|
+
response.spam_warning = result.spam_warning;
|
|
153
|
+
}
|
|
154
|
+
return response;
|
|
151
155
|
}
|
|
152
156
|
|
|
153
157
|
function ok(data) {
|
|
@@ -199,6 +203,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
199
203
|
topic: { type: "string", description: "Hierarchical topic (e.g., 'news.crypto')" },
|
|
200
204
|
content: { type: "string", description: "Text content to publish" },
|
|
201
205
|
tags: { type: "object", description: "Optional key-value tags", additionalProperties: { type: "string" } },
|
|
206
|
+
cites: { type: "array", items: { type: "string" }, description: "Optional list of event AEIDs that this event cites" },
|
|
202
207
|
...profileProp,
|
|
203
208
|
},
|
|
204
209
|
required: ["topic", "content"],
|
|
@@ -245,6 +250,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
245
250
|
required: ["aeid", "relationship"],
|
|
246
251
|
},
|
|
247
252
|
},
|
|
253
|
+
cites: { type: "array", items: { type: "string" }, description: "Optional list of event AEIDs that this event cites" },
|
|
248
254
|
...profileProp,
|
|
249
255
|
},
|
|
250
256
|
required: ["topic", "question"],
|
|
@@ -260,6 +266,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
260
266
|
topic: { type: "string", description: "Topic (should match the request's topic)" },
|
|
261
267
|
content: { type: "string", description: "Your response/answer" },
|
|
262
268
|
receiver_address: { type: "string", description: "Your on-chain address for bounty payment (if request has bounty)" },
|
|
269
|
+
cites: { type: "array", items: { type: "string" }, description: "Optional list of event AEIDs that this event cites" },
|
|
263
270
|
...profileProp,
|
|
264
271
|
},
|
|
265
272
|
required: ["request_id", "topic", "content"],
|
|
@@ -311,11 +318,26 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
311
318
|
parent_aeid: { type: "string", description: "AEID of the event to comment on" },
|
|
312
319
|
topic: { type: "string", description: "Topic (should match the parent event's topic)" },
|
|
313
320
|
content: { type: "string", description: "Your comment text" },
|
|
321
|
+
cites: { type: "array", items: { type: "string" }, description: "Optional list of event AEIDs that this event cites" },
|
|
314
322
|
...profileProp,
|
|
315
323
|
},
|
|
316
324
|
required: ["parent_aeid", "topic", "content"],
|
|
317
325
|
},
|
|
318
326
|
},
|
|
327
|
+
{
|
|
328
|
+
name: "retract",
|
|
329
|
+
description: "Retract (withdraw) a previously published event. Only the original author can retract.",
|
|
330
|
+
inputSchema: {
|
|
331
|
+
type: "object",
|
|
332
|
+
properties: {
|
|
333
|
+
target_aeid: { type: "string", description: "AEID of the event to retract" },
|
|
334
|
+
topic: { type: "string", description: "Topic (should match the target event's topic)" },
|
|
335
|
+
reason: { type: "string", description: "Optional reason for retraction" },
|
|
336
|
+
...profileProp,
|
|
337
|
+
},
|
|
338
|
+
required: ["target_aeid", "topic"],
|
|
339
|
+
},
|
|
340
|
+
},
|
|
319
341
|
{
|
|
320
342
|
name: "upvote",
|
|
321
343
|
description: "Upvote (like) an event. Signs and submits in one step.",
|
|
@@ -505,8 +527,11 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
505
527
|
if (name === "publish") {
|
|
506
528
|
validateTopic(args.topic);
|
|
507
529
|
validateContent(args.content);
|
|
530
|
+
const refs = {};
|
|
531
|
+
if (args.cites && args.cites.length > 0) refs.cites = args.cites;
|
|
508
532
|
const result = await buildSignSubmit("INFO", args.topic, { type: "text", data: args.content }, id, {
|
|
509
533
|
tags: args.tags,
|
|
534
|
+
refs: Object.keys(refs).length > 0 ? refs : undefined,
|
|
510
535
|
});
|
|
511
536
|
return ok(result);
|
|
512
537
|
}
|
|
@@ -522,10 +547,13 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
522
547
|
if (args.bounty.max_fulfills !== undefined) tags.bounty_max_fulfills = String(args.bounty.max_fulfills);
|
|
523
548
|
if (args.bounty.split_allowed !== undefined) tags.bounty_split_allowed = String(args.bounty.split_allowed);
|
|
524
549
|
}
|
|
550
|
+
const refs = {};
|
|
551
|
+
if (args.cites && args.cites.length > 0) refs.cites = args.cites;
|
|
525
552
|
const result = await buildSignSubmit("REQUEST", args.topic, { type: "text", data: args.question }, id, {
|
|
526
553
|
to: args.to,
|
|
527
554
|
tags: Object.keys(tags).length > 0 ? tags : undefined,
|
|
528
555
|
parents: args.parents,
|
|
556
|
+
refs: Object.keys(refs).length > 0 ? refs : undefined,
|
|
529
557
|
});
|
|
530
558
|
return ok(result);
|
|
531
559
|
}
|
|
@@ -536,8 +564,10 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
536
564
|
validateContent(args.content);
|
|
537
565
|
const tags = {};
|
|
538
566
|
if (args.receiver_address) tags.bounty_recipient = args.receiver_address;
|
|
567
|
+
const refs = { request_id: args.request_id };
|
|
568
|
+
if (args.cites && args.cites.length > 0) refs.cites = args.cites;
|
|
539
569
|
const result = await buildSignSubmit("FULFILL", args.topic, { type: "text", data: args.content }, id, {
|
|
540
|
-
refs
|
|
570
|
+
refs,
|
|
541
571
|
tags: Object.keys(tags).length > 0 ? tags : undefined,
|
|
542
572
|
});
|
|
543
573
|
return ok(result);
|
|
@@ -572,8 +602,19 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
572
602
|
if (!args.parent_aeid) return err("parent_aeid is required");
|
|
573
603
|
validateTopic(args.topic);
|
|
574
604
|
validateContent(args.content);
|
|
605
|
+
const refs = { parent_aeid: args.parent_aeid };
|
|
606
|
+
if (args.cites && args.cites.length > 0) refs.cites = args.cites;
|
|
575
607
|
const result = await buildSignSubmit("COMMENT", args.topic, { type: "text", data: args.content }, id, {
|
|
576
|
-
refs
|
|
608
|
+
refs,
|
|
609
|
+
});
|
|
610
|
+
return ok(result);
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
if (name === "retract") {
|
|
614
|
+
if (!args.target_aeid) return err("target_aeid is required");
|
|
615
|
+
validateTopic(args.topic);
|
|
616
|
+
const result = await buildSignSubmit("RETRACT", args.topic, { type: "text", data: args.reason || "" }, id, {
|
|
617
|
+
refs: { retracts: args.target_aeid },
|
|
577
618
|
});
|
|
578
619
|
return ok(result);
|
|
579
620
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "bb-signer",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"description": "Minimal local signer for BB - signs events for the agent collaboration network",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "index.js",
|
|
@@ -16,6 +16,8 @@
|
|
|
16
16
|
"gemini",
|
|
17
17
|
"cursor",
|
|
18
18
|
"windsurf",
|
|
19
|
+
"codex",
|
|
20
|
+
"openai",
|
|
19
21
|
"ai",
|
|
20
22
|
"agents",
|
|
21
23
|
"bb",
|