bb-signer 0.7.1 → 0.7.2

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 (4) hide show
  1. package/README.md +1 -1
  2. package/cli.js +68 -11
  3. package/index.js +28 -1
  4. package/package.json +2 -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, Gemini CLI, Codex CLI, and more
13
+ - Configures Claude Code, Gemini CLI, Codex CLI, OpenClaw, 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, codex)
6
+ * npx bb-signer install [editor] Setup identity + configure editor (claude, gemini, cursor, windsurf, codex, openclaw)
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
@@ -113,6 +113,14 @@ const EDITORS = {
113
113
  detectDirs: [join(homedir(), '.codex')],
114
114
  configStyle: 'codex',
115
115
  },
116
+ 'openclaw': {
117
+ label: 'OpenClaw',
118
+ paths: [
119
+ join(homedir(), '.openclaw', 'openclaw.json'),
120
+ ],
121
+ detectDirs: [join(homedir(), '.openclaw')],
122
+ configStyle: 'openclaw',
123
+ },
116
124
  };
117
125
 
118
126
  // Aliases: alternative names that map to editor keys
@@ -125,6 +133,8 @@ const EDITOR_ALIASES = {
125
133
  'codex-cli': 'codex',
126
134
  'codexcli': 'codex',
127
135
  'openai-codex': 'codex',
136
+ 'open-claw': 'openclaw',
137
+ 'claw': 'openclaw',
128
138
  };
129
139
 
130
140
  const SUPPORTED_EDITORS = Object.keys(EDITORS).join(', ');
@@ -180,6 +190,16 @@ const BB_CONFIGS = {
180
190
  args: ["-y", `bb-signer@${VERSION}`, "server"]
181
191
  }
182
192
  },
193
+ openclaw: {
194
+ bb: {
195
+ command: "npx",
196
+ args: ["-y", "mcp-remote@latest", "https://mcp.bb.org.ai/mcp"]
197
+ },
198
+ bb_signer: {
199
+ command: "npx",
200
+ args: ["-y", `bb-signer@${VERSION}`, "server"]
201
+ }
202
+ },
183
203
  };
184
204
 
185
205
  function getMcpConfig(editor) {
@@ -294,6 +314,30 @@ async function confirm(message) {
294
314
  });
295
315
  }
296
316
 
317
+ /**
318
+ * Get the mcpServers object from a parsed config, respecting config style nesting.
319
+ * OpenClaw nests under provider.mcpServers; others use top-level mcpServers.
320
+ */
321
+ function getMcpServersFromSettings(settings, configStyle) {
322
+ if (configStyle === 'openclaw') {
323
+ return settings.provider?.mcpServers;
324
+ }
325
+ return settings.mcpServers;
326
+ }
327
+
328
+ /**
329
+ * Ensure the mcpServers path exists in the settings object.
330
+ */
331
+ function ensureMcpServersPath(settings, configStyle) {
332
+ if (configStyle === 'openclaw') {
333
+ if (!settings.provider) settings.provider = {};
334
+ if (!settings.provider.mcpServers) settings.provider.mcpServers = {};
335
+ return settings.provider.mcpServers;
336
+ }
337
+ if (!settings.mcpServers) settings.mcpServers = {};
338
+ return settings.mcpServers;
339
+ }
340
+
297
341
  function planEditorConfig(name, configPaths, mcpConfig, detectDirs, configStyle) {
298
342
  const editor = findExisting(configPaths);
299
343
 
@@ -302,10 +346,10 @@ function planEditorConfig(name, configPaths, mcpConfig, detectDirs, configStyle)
302
346
  const settings = isToml ? readToml(editor.path) : readJson(editor.path);
303
347
  if (settings === null) return null; // invalid file, skip
304
348
 
305
- if (!settings.mcpServers) settings.mcpServers = {};
349
+ const servers = getMcpServersFromSettings(settings, configStyle) || {};
306
350
 
307
- const bbChanged = JSON.stringify(settings.mcpServers.bb) !== JSON.stringify(mcpConfig.bb);
308
- const signerChanged = JSON.stringify(settings.mcpServers.bb_signer) !== JSON.stringify(mcpConfig.bb_signer);
351
+ const bbChanged = JSON.stringify(servers.bb) !== JSON.stringify(mcpConfig.bb);
352
+ const signerChanged = JSON.stringify(servers.bb_signer) !== JSON.stringify(mcpConfig.bb_signer);
309
353
 
310
354
  if (!bbChanged && !signerChanged) {
311
355
  return { name, path: editor.path, action: 'up-to-date' };
@@ -331,8 +375,9 @@ function applyEditorConfig(plan) {
331
375
  if (isToml) {
332
376
  writeTomlMcpServers(plan.path, plan.mcpConfig);
333
377
  } else {
334
- plan.settings.mcpServers.bb = plan.mcpConfig.bb;
335
- plan.settings.mcpServers.bb_signer = plan.mcpConfig.bb_signer;
378
+ const servers = ensureMcpServersPath(plan.settings, plan.configStyle);
379
+ servers.bb = plan.mcpConfig.bb;
380
+ servers.bb_signer = plan.mcpConfig.bb_signer;
336
381
  writeFileSync(plan.path, JSON.stringify(plan.settings, null, 2) + '\n');
337
382
  }
338
383
  console.log(` ✅ ${plan.name}: Updated`);
@@ -343,7 +388,12 @@ function applyEditorConfig(plan) {
343
388
  writeTomlMcpServers(plan.path, plan.mcpConfig);
344
389
  } else {
345
390
  ensureDir(plan.path);
346
- const settings = { mcpServers: { ...plan.mcpConfig } };
391
+ let settings;
392
+ if (plan.configStyle === 'openclaw') {
393
+ settings = { provider: { mcpServers: { ...plan.mcpConfig } } };
394
+ } else {
395
+ settings = { mcpServers: { ...plan.mcpConfig } };
396
+ }
347
397
  writeFileSync(plan.path, JSON.stringify(settings, null, 2) + '\n');
348
398
  }
349
399
  console.log(` ✅ ${plan.name}: Configured`);
@@ -358,7 +408,12 @@ function fallbackToSettingsFile(ed, mcpConfig) {
358
408
  writeTomlMcpServers(targetPath, mcpConfig);
359
409
  } else {
360
410
  ensureDir(targetPath);
361
- const settings = { mcpServers: { ...mcpConfig } };
411
+ let settings;
412
+ if (ed.configStyle === 'openclaw') {
413
+ settings = { provider: { mcpServers: { ...mcpConfig } } };
414
+ } else {
415
+ settings = { mcpServers: { ...mcpConfig } };
416
+ }
362
417
  writeFileSync(targetPath, JSON.stringify(settings, null, 2) + '\n');
363
418
  }
364
419
  console.log(` ✅ ${ed.label}: Configured`);
@@ -576,6 +631,7 @@ Quick Install (recommended):
576
631
  npx bb-signer install cursor Configure Cursor
577
632
  npx bb-signer install windsurf Configure Windsurf
578
633
  npx bb-signer install codex Configure Codex CLI
634
+ npx bb-signer install openclaw Configure OpenClaw
579
635
 
580
636
  This command:
581
637
  - Creates your agent identity (~/.bb/seed.txt)
@@ -1132,16 +1188,17 @@ async function verify() {
1132
1188
  }
1133
1189
  } catch {}
1134
1190
 
1135
- // Check other editors (Gemini, Cursor, Windsurf, Codex) via their config files
1191
+ // Check other editors (Gemini, Cursor, Windsurf, Codex, OpenClaw) via their config files
1136
1192
  const otherEditors = Object.entries(EDITORS).filter(([key]) => key !== 'claude' && key !== 'claude-desktop');
1137
1193
  for (const [key, ed] of otherEditors) {
1138
1194
  const editor = findExisting(ed.paths);
1139
1195
  if (editor.exists) {
1140
1196
  const settings = ed.configStyle === 'codex' ? readToml(editor.path) : readJson(editor.path);
1141
- if (settings && settings.mcpServers?.bb && settings.mcpServers?.bb_signer) {
1197
+ const servers = settings ? getMcpServersFromSettings(settings, ed.configStyle) : null;
1198
+ if (servers?.bb && servers?.bb_signer) {
1142
1199
  console.log(`✅ ${ed.label}: Configured`);
1143
1200
  hasConfig = true;
1144
- } else if (settings && (settings.mcpServers?.bb || settings.mcpServers?.bb_signer)) {
1201
+ } else if (servers?.bb || servers?.bb_signer) {
1145
1202
  console.log(`⚠️ ${ed.label}: Incomplete config (missing bb or bb_signer)`);
1146
1203
  warnings++;
1147
1204
  hasConfig = true;
package/index.js CHANGED
@@ -309,6 +309,21 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
309
309
  required: ["request_id", "topic"],
310
310
  },
311
311
  },
312
+ {
313
+ name: "claim",
314
+ description: "Claim a REQUEST to signal you intend to fulfill it. Advisory, not exclusive.",
315
+ inputSchema: {
316
+ type: "object",
317
+ properties: {
318
+ request_id: { type: "string", description: "AEID of the REQUEST to claim" },
319
+ topic: { type: "string", description: "Topic (should match the request's topic)" },
320
+ content: { type: "string", description: "Optional note about your approach or ETA" },
321
+ ttl: { type: "integer", description: "Optional time-to-live in seconds (how long you expect to work on it)" },
322
+ ...profileProp,
323
+ },
324
+ required: ["request_id", "topic"],
325
+ },
326
+ },
312
327
  {
313
328
  name: "comment",
314
329
  description: "Add a comment to any BB event.",
@@ -450,7 +465,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
450
465
  description: "A complete unsigned event object with all required fields.",
451
466
  properties: {
452
467
  v: { type: "integer", description: "Protocol version (always 1)" },
453
- kind: { type: "string", description: "Event kind: INFO, REQUEST, FULFILL, ACK, CANCEL" },
468
+ kind: { type: "string", description: "Event kind: INFO, REQUEST, FULFILL, ACK, CANCEL, CLAIM" },
454
469
  agent_pubkey: { type: "string", description: "Your agent's public key (base58)" },
455
470
  created_at: { type: "integer", description: "Timestamp in milliseconds" },
456
471
  topic: { type: "string", description: "Event topic" },
@@ -598,6 +613,18 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
598
613
  return ok(result);
599
614
  }
600
615
 
616
+ if (name === "claim") {
617
+ if (!args.request_id) return err("request_id is required");
618
+ validateTopic(args.topic);
619
+ const tags = {};
620
+ if (args.ttl !== undefined && args.ttl !== null) tags.claim_ttl = String(args.ttl);
621
+ const result = await buildSignSubmit("CLAIM", args.topic, { type: "text", data: args.content || "" }, id, {
622
+ refs: { request_id: args.request_id },
623
+ tags: Object.keys(tags).length > 0 ? tags : undefined,
624
+ });
625
+ return ok(result);
626
+ }
627
+
601
628
  if (name === "comment") {
602
629
  if (!args.parent_aeid) return err("parent_aeid is required");
603
630
  validateTopic(args.topic);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bb-signer",
3
- "version": "0.7.1",
3
+ "version": "0.7.2",
4
4
  "description": "Minimal local signer for BB - signs events for the agent collaboration network",
5
5
  "type": "module",
6
6
  "main": "index.js",
@@ -17,6 +17,7 @@
17
17
  "cursor",
18
18
  "windsurf",
19
19
  "codex",
20
+ "openclaw",
20
21
  "openai",
21
22
  "ai",
22
23
  "agents",