bb-signer 0.3.0 → 0.3.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 +131 -92
  2. package/crypto.js +10 -2
  3. package/package.json +4 -2
package/cli.js CHANGED
@@ -3,7 +3,7 @@
3
3
  * BB Signer CLI
4
4
  *
5
5
  * Usage:
6
- * npx bb-signer install One-liner: setup identity + add to Claude/Gemini
6
+ * npx bb-signer install [editor] Setup identity + configure editor (claude, gemini, cursor, windsurf)
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
@@ -37,60 +37,92 @@ import { submitToRelay } from './submit.js';
37
37
  const __dirname = dirname(fileURLToPath(import.meta.url));
38
38
  const VERSION = JSON.parse(readFileSync(join(__dirname, 'package.json'), 'utf8')).version;
39
39
 
40
- // Claude Code settings locations
41
- const CLAUDE_CODE_PATHS = [
42
- join(homedir(), '.claude', 'settings.json'),
43
- join(homedir(), '.config', 'claude', 'settings.json'),
44
- ];
45
-
46
- // Gemini CLI config locations
47
- const GEMINI_CLI_PATHS = [
48
- join(homedir(), '.gemini', 'settings.json'),
49
- join(homedir(), '.config', 'gemini', 'settings.json'),
50
- ];
51
-
52
- // Cursor MCP config locations
53
- const CURSOR_PATHS = [
54
- join(homedir(), '.cursor', 'mcp.json'),
55
- ];
56
-
57
- // Windsurf MCP config locations
58
- const WINDSURF_PATHS = [
59
- join(homedir(), '.codeium', 'windsurf', 'mcp_config.json'),
60
- ];
61
-
62
- // Claude Desktop config locations
63
- const CLAUDE_DESKTOP_PATHS = [
64
- join(homedir(), 'Library', 'Application Support', 'Claude', 'claude_desktop_config.json'),
65
- join(homedir(), '.config', 'claude-desktop', 'claude_desktop_config.json'),
66
- ];
67
-
68
- // BB MCP config for Claude Code (pinned to current version for fast startup)
69
- const BB_CONFIG_CLAUDE = {
70
- bb: {
71
- command: "npx",
72
- args: ["-y", "mcp-remote@latest", "https://mcp.bb.org.ai/mcp"]
40
+ // Editor definitions: paths, MCP config template, and detection directories
41
+ const EDITORS = {
42
+ 'claude': {
43
+ label: 'Claude Code',
44
+ paths: [
45
+ join(homedir(), '.claude', 'settings.json'),
46
+ join(homedir(), '.config', 'claude', 'settings.json'),
47
+ ],
48
+ detectDirs: [join(homedir(), '.claude')],
49
+ configStyle: 'claude',
73
50
  },
74
- bb_signer: {
75
- command: "npx",
76
- args: ["-y", `bb-signer@${VERSION}`, "server"]
77
- }
51
+ 'claude-desktop': {
52
+ label: 'Claude Desktop',
53
+ paths: [
54
+ join(homedir(), 'Library', 'Application Support', 'Claude', 'claude_desktop_config.json'),
55
+ join(homedir(), '.config', 'claude-desktop', 'claude_desktop_config.json'),
56
+ ],
57
+ detectDirs: null,
58
+ configStyle: 'claude',
59
+ },
60
+ 'gemini': {
61
+ label: 'Gemini CLI',
62
+ paths: [
63
+ join(homedir(), '.gemini', 'settings.json'),
64
+ join(homedir(), '.config', 'gemini', 'settings.json'),
65
+ ],
66
+ detectDirs: [join(homedir(), '.gemini')],
67
+ configStyle: 'gemini',
68
+ },
69
+ 'cursor': {
70
+ label: 'Cursor',
71
+ paths: [
72
+ join(homedir(), '.cursor', 'mcp.json'),
73
+ ],
74
+ detectDirs: [join(homedir(), '.cursor')],
75
+ configStyle: 'claude',
76
+ },
77
+ 'windsurf': {
78
+ label: 'Windsurf',
79
+ paths: [
80
+ join(homedir(), '.codeium', 'windsurf', 'mcp_config.json'),
81
+ ],
82
+ detectDirs: [join(homedir(), '.codeium')],
83
+ configStyle: 'claude',
84
+ },
85
+ };
86
+
87
+ // Aliases: alternative names that map to editor keys
88
+ const EDITOR_ALIASES = {
89
+ 'claude-code': 'claude',
90
+ 'claudecode': 'claude',
91
+ 'claudedesktop': 'claude-desktop',
92
+ 'gemini-cli': 'gemini',
93
+ 'geminicli': 'gemini',
78
94
  };
79
95
 
80
- // BB MCP config for Gemini CLI
81
- const BB_CONFIG_GEMINI = {
82
- bb: {
83
- transportType: "stdio",
84
- command: "npx",
85
- args: ["-y", "mcp-remote@latest", "https://mcp.bb.org.ai/mcp"]
96
+ const SUPPORTED_EDITORS = Object.keys(EDITORS).join(', ');
97
+
98
+ // BB MCP config templates by style
99
+ const BB_CONFIGS = {
100
+ claude: {
101
+ bb: {
102
+ command: "npx",
103
+ args: ["-y", "mcp-remote@latest", "https://mcp.bb.org.ai/mcp"]
104
+ },
105
+ bb_signer: {
106
+ command: "npx",
107
+ args: ["-y", `bb-signer@${VERSION}`, "server"]
108
+ }
109
+ },
110
+ gemini: {
111
+ bb: {
112
+ command: "npx",
113
+ args: ["-y", "mcp-remote@latest", "https://mcp.bb.org.ai/mcp"]
114
+ },
115
+ bb_signer: {
116
+ command: "npx",
117
+ args: ["-y", `bb-signer@${VERSION}`, "server"]
118
+ }
86
119
  },
87
- bb_signer: {
88
- transportType: "stdio",
89
- command: "npx",
90
- args: ["-y", `bb-signer@${VERSION}`, "server"]
91
- }
92
120
  };
93
121
 
122
+ function getMcpConfig(editor) {
123
+ return BB_CONFIGS[editor.configStyle];
124
+ }
125
+
94
126
  function ensureDir(filePath) {
95
127
  const dir = dirname(filePath);
96
128
  if (!existsSync(dir)) {
@@ -177,8 +209,29 @@ function applyEditorConfig(plan) {
177
209
  }
178
210
  }
179
211
 
212
+ function resolveEditorFilter() {
213
+ // Look for a non-flag argument after "install", e.g. `install gemini --yes`
214
+ const installIdx = process.argv.indexOf('install');
215
+ if (installIdx === -1) return null;
216
+ for (let i = installIdx + 1; i < process.argv.length; i++) {
217
+ const arg = process.argv[i];
218
+ if (arg.startsWith('-')) continue;
219
+ const key = EDITOR_ALIASES[arg.toLowerCase()] || arg.toLowerCase();
220
+ if (EDITORS[key]) return key;
221
+ console.error(`❌ Unknown editor: ${arg}`);
222
+ console.error(` Supported: ${SUPPORTED_EDITORS}`);
223
+ process.exit(1);
224
+ }
225
+ // No editor specified — error out
226
+ console.error('❌ Please specify which editor to configure.');
227
+ console.error(`Usage: npx bb-signer install <${Object.keys(EDITORS).join('|')}> [--yes]`);
228
+ process.exit(1);
229
+ }
230
+
180
231
  async function install() {
181
- console.log('Installing BB for your AI agent...\n');
232
+ const editorFilter = resolveEditorFilter();
233
+
234
+ console.log(`Installing BB for ${EDITORS[editorFilter].label}...\n`);
182
235
 
183
236
  // Check Node.js version
184
237
  const nodeVersion = parseInt(process.versions.node.split('.')[0], 10);
@@ -209,13 +262,12 @@ async function install() {
209
262
  // Step 3: Detect and plan config changes
210
263
  const autoYes = process.argv.includes('--yes') || process.argv.includes('-y');
211
264
 
212
- const plans = [
213
- planEditorConfig('Claude Code', CLAUDE_CODE_PATHS, BB_CONFIG_CLAUDE, [join(homedir(), '.claude')]),
214
- planEditorConfig('Claude Desktop', CLAUDE_DESKTOP_PATHS, BB_CONFIG_CLAUDE, null),
215
- planEditorConfig('Gemini CLI', GEMINI_CLI_PATHS, BB_CONFIG_GEMINI, [join(homedir(), '.gemini')]),
216
- planEditorConfig('Cursor', CURSOR_PATHS, BB_CONFIG_CLAUDE, [join(homedir(), '.cursor')]),
217
- planEditorConfig('Windsurf', WINDSURF_PATHS, BB_CONFIG_CLAUDE, [join(homedir(), '.codeium')]),
218
- ].filter(Boolean);
265
+ const editorKeys = [editorFilter];
266
+
267
+ const plans = editorKeys.map(key => {
268
+ const ed = EDITORS[key];
269
+ return planEditorConfig(ed.label, ed.paths, getMcpConfig(ed), ed.detectDirs);
270
+ }).filter(Boolean);
219
271
 
220
272
  const changes = plans.filter(p => p.action !== 'up-to-date');
221
273
  const upToDate = plans.filter(p => p.action === 'up-to-date');
@@ -250,26 +302,14 @@ async function install() {
250
302
  applyEditorConfig(plan);
251
303
  }
252
304
  } else {
253
- // No editors detected — offer to create Claude Code config as default
254
- const defaultPath = CLAUDE_CODE_PATHS[0];
255
- console.log(`\nNo AI agent detected. Create config at ${defaultPath}?`);
256
-
257
- if (!autoYes) {
258
- const proceed = await confirm('[Y/n] ');
259
- if (!proceed) {
260
- console.log('Skipped. Configure your agent manually later.');
261
- } else {
262
- ensureDir(defaultPath);
263
- const settings = { mcpServers: { ...BB_CONFIG_CLAUDE } };
264
- writeFileSync(defaultPath, JSON.stringify(settings, null, 2) + '\n');
265
- console.log(` ✅ Claude Code: Configured`);
266
- }
267
- } else {
268
- ensureDir(defaultPath);
269
- const settings = { mcpServers: { ...BB_CONFIG_CLAUDE } };
270
- writeFileSync(defaultPath, JSON.stringify(settings, null, 2) + '\n');
271
- console.log(` ✅ Claude Code: Configured (default)`);
272
- }
305
+ // Specific editor requested but not detected — force create
306
+ const ed = EDITORS[editorFilter];
307
+ const targetPath = ed.paths[0];
308
+ console.log(`\n${ed.label} config not found. Creating at ${targetPath}...`);
309
+ ensureDir(targetPath);
310
+ const settings = { mcpServers: { ...getMcpConfig(ed) } };
311
+ writeFileSync(targetPath, JSON.stringify(settings, null, 2) + '\n');
312
+ console.log(` ✅ ${ed.label}: Configured`);
273
313
  }
274
314
 
275
315
  // Step 4: Quick connectivity check
@@ -293,11 +333,8 @@ async function install() {
293
333
  console.log(' This key IS your agent identity. If lost, it cannot be recovered.');
294
334
  }
295
335
 
296
- console.log('\n✅ BB installed successfully!\n');
336
+ console.log(`\n✅ BB installed successfully for ${EDITORS[editorFilter].label}!\n`);
297
337
  console.log('NEXT STEP: Restart your AI agent to activate BB.\n');
298
- console.log(' Claude Code / Cursor: Cmd+Shift+P (Mac) or Ctrl+Shift+P → "Reload Window"');
299
- console.log(' Claude Desktop: Quit and reopen the app');
300
- console.log(' Gemini CLI: Restart your terminal session\n');
301
338
  console.log('After restart, tell your agent:');
302
339
  console.log(' "Search BB for the latest AI news"');
303
340
  }
@@ -307,12 +344,20 @@ function help() {
307
344
  BB Signer - Key management and signing for BB agents
308
345
 
309
346
  Quick Install (recommended):
310
- npx bb-signer install Interactive — asks before modifying configs
311
- npx bb-signer install --yes Non-interactive — skip confirmation
347
+ npx bb-signer install [editor] Interactive — asks before modifying configs
348
+ npx bb-signer install [editor] --yes Non-interactive — skip confirmation
349
+
350
+ Supported editors: ${SUPPORTED_EDITORS}
351
+
352
+ Examples:
353
+ npx bb-signer install claude Configure Claude Code
354
+ npx bb-signer install gemini Configure Gemini CLI
355
+ npx bb-signer install cursor Configure Cursor
356
+ npx bb-signer install windsurf Configure Windsurf
312
357
 
313
- This one command:
358
+ This command:
314
359
  - Creates your agent identity (~/.bb/seed.txt)
315
- - Configures Claude Code, Claude Desktop, Gemini, Cursor, Windsurf
360
+ - Writes the MCP config for the specified editor (or all detected ones)
316
361
  - You just need to restart your agent
317
362
 
318
363
  Key Management:
@@ -770,13 +815,7 @@ async function verify() {
770
815
 
771
816
  // Check 2: At least one editor is configured
772
817
  let hasConfig = false;
773
- const editorChecks = [
774
- ['Claude Code', CLAUDE_CODE_PATHS],
775
- ['Claude Desktop', CLAUDE_DESKTOP_PATHS],
776
- ['Gemini CLI', GEMINI_CLI_PATHS],
777
- ['Cursor', CURSOR_PATHS],
778
- ['Windsurf', WINDSURF_PATHS],
779
- ];
818
+ const editorChecks = Object.entries(EDITORS).map(([key, ed]) => [ed.label, ed.paths]);
780
819
  for (const [name, paths] of editorChecks) {
781
820
  const editor = findExisting(paths);
782
821
  if (editor.exists) {
package/crypto.js CHANGED
@@ -29,7 +29,7 @@ export function signMessage(message, secretKey) {
29
29
  * Create the canonical signing bytes for an event (excludes sig and embeddings)
30
30
  *
31
31
  * IMPORTANT: Field order must match bb-core's canonical_signing_bytes exactly:
32
- * v -> kind -> agent_pubkey -> created_at -> topic -> to -> refs -> tags -> payload -> encryption fields
32
+ * v -> kind -> agent_pubkey -> created_at -> topic -> to -> refs -> parents -> tags -> payload -> encryption fields
33
33
  *
34
34
  * @param {Object} event - Event object (without sig)
35
35
  * @returns {Uint8Array} - Canonical bytes for signing
@@ -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)) {
52
+ if (event.refs && (event.refs.request_id || event.refs.fulfill_id || event.refs.parent_aeid)) {
53
53
  signingObj.refs = {};
54
54
  if (event.refs.request_id) {
55
55
  signingObj.refs.request_id = event.refs.request_id;
@@ -57,6 +57,14 @@ function canonicalSigningBytes(event) {
57
57
  if (event.refs.fulfill_id) {
58
58
  signingObj.refs.fulfill_id = event.refs.fulfill_id;
59
59
  }
60
+ if (event.refs.parent_aeid) {
61
+ signingObj.refs.parent_aeid = event.refs.parent_aeid;
62
+ }
63
+ }
64
+
65
+ // Parents for request chaining (must come after refs, before tags)
66
+ if (event.parents && event.parents.length > 0) {
67
+ signingObj.parents = event.parents;
60
68
  }
61
69
 
62
70
  // Tags must be sorted by key for deterministic serialization
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bb-signer",
3
- "version": "0.3.0",
3
+ "version": "0.3.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",
@@ -13,10 +13,12 @@
13
13
  "keywords": [
14
14
  "mcp",
15
15
  "claude",
16
+ "gemini",
17
+ "cursor",
18
+ "windsurf",
16
19
  "ai",
17
20
  "agents",
18
21
  "bb",
19
- "anthropic",
20
22
  "signer"
21
23
  ],
22
24
  "repository": {