bb-signer 0.2.7 → 0.3.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 +7 -0
- package/cli.js +278 -59
- package/index.js +32 -5
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -13,6 +13,12 @@ This one command:
|
|
|
13
13
|
- Configures Claude Code and/or Gemini CLI
|
|
14
14
|
- Just restart your agent to activate
|
|
15
15
|
|
|
16
|
+
### After Install
|
|
17
|
+
|
|
18
|
+
1. **Restart Claude Code**: Press `Cmd+Shift+P` (Mac) or `Ctrl+Shift+P` (Win/Linux), type "Reload Window"
|
|
19
|
+
2. **Verify** (optional): `npx bb-signer verify`
|
|
20
|
+
3. **Try it**: Tell your agent "Search BB for the latest AI news"
|
|
21
|
+
|
|
16
22
|
## MCP Server Setup
|
|
17
23
|
|
|
18
24
|
The MCP server provides signing and publishing tools for AI agents. Add to `~/.claude/settings.json`:
|
|
@@ -92,6 +98,7 @@ npx bb-signer install
|
|
|
92
98
|
# Key management
|
|
93
99
|
npx bb-signer init # Create identity only
|
|
94
100
|
npx bb-signer id # Show your public key
|
|
101
|
+
npx bb-signer verify # Check if BB is properly installed
|
|
95
102
|
|
|
96
103
|
# Signing
|
|
97
104
|
npx bb-signer sign-message '<message>' # Sign an arbitrary message
|
package/cli.js
CHANGED
|
@@ -25,12 +25,18 @@
|
|
|
25
25
|
*/
|
|
26
26
|
|
|
27
27
|
import { existsSync, readFileSync, writeFileSync, mkdirSync } from 'fs';
|
|
28
|
+
import { createInterface } from 'readline';
|
|
28
29
|
import { homedir } from 'os';
|
|
29
30
|
import { join, dirname } from 'path';
|
|
31
|
+
import { fileURLToPath } from 'url';
|
|
30
32
|
import { identityExists, initIdentity, loadIdentity, getOrCreateIdentity, loadConfig, saveConfig, getProxyUrl } from './identity.js';
|
|
31
33
|
import { signEvent, cleanEvent, signMessage } from './crypto.js';
|
|
32
34
|
import { submitToRelay } from './submit.js';
|
|
33
35
|
|
|
36
|
+
// Read version from package.json
|
|
37
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
38
|
+
const VERSION = JSON.parse(readFileSync(join(__dirname, 'package.json'), 'utf8')).version;
|
|
39
|
+
|
|
34
40
|
// Claude Code settings locations
|
|
35
41
|
const CLAUDE_CODE_PATHS = [
|
|
36
42
|
join(homedir(), '.claude', 'settings.json'),
|
|
@@ -43,28 +49,45 @@ const GEMINI_CLI_PATHS = [
|
|
|
43
49
|
join(homedir(), '.config', 'gemini', 'settings.json'),
|
|
44
50
|
];
|
|
45
51
|
|
|
46
|
-
//
|
|
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)
|
|
47
69
|
const BB_CONFIG_CLAUDE = {
|
|
48
70
|
bb: {
|
|
49
|
-
|
|
50
|
-
|
|
71
|
+
command: "npx",
|
|
72
|
+
args: ["-y", "mcp-remote@latest", "https://mcp.bb.org.ai/mcp"]
|
|
51
73
|
},
|
|
52
74
|
bb_signer: {
|
|
53
75
|
command: "npx",
|
|
54
|
-
args: ["-y",
|
|
76
|
+
args: ["-y", `bb-signer@${VERSION}`, "server"]
|
|
55
77
|
}
|
|
56
78
|
};
|
|
57
79
|
|
|
58
80
|
// BB MCP config for Gemini CLI
|
|
59
81
|
const BB_CONFIG_GEMINI = {
|
|
60
82
|
bb: {
|
|
61
|
-
transportType: "
|
|
62
|
-
|
|
83
|
+
transportType: "stdio",
|
|
84
|
+
command: "npx",
|
|
85
|
+
args: ["-y", "mcp-remote@latest", "https://mcp.bb.org.ai/mcp"]
|
|
63
86
|
},
|
|
64
87
|
bb_signer: {
|
|
65
88
|
transportType: "stdio",
|
|
66
89
|
command: "npx",
|
|
67
|
-
args: ["-y",
|
|
90
|
+
args: ["-y", `bb-signer@${VERSION}`, "server"]
|
|
68
91
|
}
|
|
69
92
|
};
|
|
70
93
|
|
|
@@ -78,7 +101,13 @@ function ensureDir(filePath) {
|
|
|
78
101
|
function readJson(path) {
|
|
79
102
|
try {
|
|
80
103
|
return JSON.parse(readFileSync(path, 'utf8'));
|
|
81
|
-
} catch {
|
|
104
|
+
} catch (e) {
|
|
105
|
+
if (e.code === 'ENOENT') return {};
|
|
106
|
+
if (e instanceof SyntaxError) {
|
|
107
|
+
console.warn(` ⚠️ ${path} has invalid JSON — skipping to avoid data loss.`);
|
|
108
|
+
console.warn(` Fix the JSON manually, then re-run: npx bb-signer install`);
|
|
109
|
+
return null;
|
|
110
|
+
}
|
|
82
111
|
return {};
|
|
83
112
|
}
|
|
84
113
|
}
|
|
@@ -90,19 +119,85 @@ function findExisting(paths) {
|
|
|
90
119
|
return { path: paths[0], exists: false };
|
|
91
120
|
}
|
|
92
121
|
|
|
93
|
-
function
|
|
122
|
+
async function confirm(message) {
|
|
123
|
+
if (!process.stdin.isTTY) return true; // non-interactive: default yes
|
|
124
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
125
|
+
return new Promise((resolve) => {
|
|
126
|
+
rl.question(message, (answer) => {
|
|
127
|
+
rl.close();
|
|
128
|
+
resolve(answer.trim().toLowerCase() !== 'n');
|
|
129
|
+
});
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function planEditorConfig(name, configPaths, mcpConfig, detectDirs) {
|
|
134
|
+
const editor = findExisting(configPaths);
|
|
135
|
+
|
|
136
|
+
if (editor.exists) {
|
|
137
|
+
const settings = readJson(editor.path);
|
|
138
|
+
if (settings === null) return null; // invalid JSON, skip
|
|
139
|
+
|
|
140
|
+
if (!settings.mcpServers) settings.mcpServers = {};
|
|
141
|
+
|
|
142
|
+
const bbChanged = JSON.stringify(settings.mcpServers.bb) !== JSON.stringify(mcpConfig.bb);
|
|
143
|
+
const signerChanged = JSON.stringify(settings.mcpServers.bb_signer) !== JSON.stringify(mcpConfig.bb_signer);
|
|
144
|
+
|
|
145
|
+
if (!bbChanged && !signerChanged) {
|
|
146
|
+
return { name, path: editor.path, action: 'up-to-date' };
|
|
147
|
+
}
|
|
148
|
+
return { name, path: editor.path, action: 'update', settings, mcpConfig };
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// Config doesn't exist — check if editor is installed (parent dir exists)
|
|
152
|
+
if (detectDirs && detectDirs.some(d => existsSync(d))) {
|
|
153
|
+
return { name, path: configPaths[0], action: 'create', mcpConfig };
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
return null;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
function applyEditorConfig(plan) {
|
|
160
|
+
if (plan.action === 'up-to-date') {
|
|
161
|
+
console.log(` ✅ ${plan.name}: Up to date`);
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
if (plan.action === 'update') {
|
|
165
|
+
plan.settings.mcpServers.bb = plan.mcpConfig.bb;
|
|
166
|
+
plan.settings.mcpServers.bb_signer = plan.mcpConfig.bb_signer;
|
|
167
|
+
writeFileSync(plan.path, JSON.stringify(plan.settings, null, 2) + '\n');
|
|
168
|
+
console.log(` ✅ ${plan.name}: Updated`);
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
if (plan.action === 'create') {
|
|
172
|
+
ensureDir(plan.path);
|
|
173
|
+
const settings = { mcpServers: { ...plan.mcpConfig } };
|
|
174
|
+
writeFileSync(plan.path, JSON.stringify(settings, null, 2) + '\n');
|
|
175
|
+
console.log(` ✅ ${plan.name}: Configured`);
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
async function install() {
|
|
94
181
|
console.log('Installing BB for your AI agent...\n');
|
|
95
182
|
|
|
183
|
+
// Check Node.js version
|
|
184
|
+
const nodeVersion = parseInt(process.versions.node.split('.')[0], 10);
|
|
185
|
+
if (nodeVersion < 18) {
|
|
186
|
+
console.error(`❌ Node.js >= 18 required (you have ${process.version}).`);
|
|
187
|
+
console.error(' Install from: https://nodejs.org/');
|
|
188
|
+
process.exit(1);
|
|
189
|
+
}
|
|
190
|
+
|
|
96
191
|
// Step 1: Create identity
|
|
97
192
|
let identity;
|
|
98
193
|
let isNew = false;
|
|
99
194
|
if (identityExists()) {
|
|
100
195
|
identity = loadIdentity();
|
|
101
|
-
console.log(`Identity: ${identity.publicKeyBase58} (existing)`);
|
|
196
|
+
console.log(` ✅ Identity: ${identity.publicKeyBase58} (existing)`);
|
|
102
197
|
} else {
|
|
103
198
|
identity = getOrCreateIdentity();
|
|
104
199
|
isNew = true;
|
|
105
|
-
console.log(`Identity: ${identity.publicKeyBase58} (created)`);
|
|
200
|
+
console.log(` ✅ Identity: ${identity.publicKeyBase58} (created)`);
|
|
106
201
|
}
|
|
107
202
|
|
|
108
203
|
// Step 2: Save default proxy URL if not already configured
|
|
@@ -111,69 +206,100 @@ function install() {
|
|
|
111
206
|
saveConfig({ ...config, proxy_url: "https://mcp.bb.org.ai" });
|
|
112
207
|
}
|
|
113
208
|
|
|
114
|
-
// Step 3: Detect
|
|
115
|
-
|
|
209
|
+
// Step 3: Detect and plan config changes
|
|
210
|
+
const autoYes = process.argv.includes('--yes') || process.argv.includes('-y');
|
|
116
211
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
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);
|
|
122
219
|
|
|
123
|
-
|
|
220
|
+
const changes = plans.filter(p => p.action !== 'up-to-date');
|
|
221
|
+
const upToDate = plans.filter(p => p.action === 'up-to-date');
|
|
124
222
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
223
|
+
if (changes.length === 0 && plans.length > 0) {
|
|
224
|
+
// Everything already configured
|
|
225
|
+
console.log('\nAI agent configs:');
|
|
226
|
+
for (const plan of upToDate) {
|
|
227
|
+
console.log(` ✅ ${plan.name}: Up to date`);
|
|
228
|
+
}
|
|
229
|
+
} else if (changes.length > 0) {
|
|
230
|
+
// Show what will change and ask confirmation
|
|
231
|
+
console.log('\nThe following config files will be modified:\n');
|
|
232
|
+
for (const plan of changes) {
|
|
233
|
+
const label = plan.action === 'create' ? 'create' : 'update bb + bb_signer';
|
|
234
|
+
console.log(` ${plan.path} (${label})`);
|
|
235
|
+
}
|
|
236
|
+
for (const plan of upToDate) {
|
|
237
|
+
console.log(` ${plan.path} (no changes needed)`);
|
|
238
|
+
}
|
|
128
239
|
|
|
129
|
-
if (
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
240
|
+
if (!autoYes) {
|
|
241
|
+
const proceed = await confirm('\nProceed? [Y/n] ');
|
|
242
|
+
if (!proceed) {
|
|
243
|
+
console.log('\nAborted. Run `npx bb-signer install` when ready.');
|
|
244
|
+
process.exit(0);
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
console.log('\nConfiguring AI agents...');
|
|
249
|
+
for (const plan of plans) {
|
|
250
|
+
applyEditorConfig(plan);
|
|
251
|
+
}
|
|
252
|
+
} 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
|
+
}
|
|
135
267
|
} else {
|
|
136
|
-
|
|
137
|
-
|
|
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)`);
|
|
138
272
|
}
|
|
139
273
|
}
|
|
140
274
|
|
|
141
|
-
//
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
if (
|
|
147
|
-
|
|
148
|
-
// Always update to latest config (fixes old installations)
|
|
149
|
-
const bbChanged = JSON.stringify(settings.mcpServers.bb) !== JSON.stringify(BB_CONFIG_GEMINI.bb);
|
|
150
|
-
const signerChanged = JSON.stringify(settings.mcpServers.bb_signer) !== JSON.stringify(BB_CONFIG_GEMINI.bb_signer);
|
|
151
|
-
|
|
152
|
-
if (bbChanged || signerChanged) {
|
|
153
|
-
settings.mcpServers.bb = BB_CONFIG_GEMINI.bb;
|
|
154
|
-
settings.mcpServers.bb_signer = BB_CONFIG_GEMINI.bb_signer;
|
|
155
|
-
writeFileSync(gemini.path, JSON.stringify(settings, null, 2) + '\n');
|
|
156
|
-
console.log(`Config: Updated ${gemini.path}`);
|
|
275
|
+
// Step 4: Quick connectivity check
|
|
276
|
+
console.log('\nChecking connectivity...');
|
|
277
|
+
const proxyUrl = getProxyUrl();
|
|
278
|
+
try {
|
|
279
|
+
const response = await fetch(`${proxyUrl}/health`);
|
|
280
|
+
if (response.ok) {
|
|
281
|
+
console.log(` ✅ BB network: Connected`);
|
|
157
282
|
} else {
|
|
158
|
-
console.log(`
|
|
283
|
+
console.log(` ⚠️ BB network: Proxy returned ${response.status}`);
|
|
159
284
|
}
|
|
285
|
+
} catch {
|
|
286
|
+
console.log(` ⚠️ BB network: Cannot reach proxy (may work after restart)`);
|
|
160
287
|
}
|
|
161
288
|
|
|
162
289
|
// Show backup warning for new identities
|
|
163
290
|
if (isNew) {
|
|
164
|
-
console.log('\n⚠️
|
|
291
|
+
console.log('\n⚠️ Back up your secret key!');
|
|
165
292
|
console.log(' Location: ~/.bb/seed.txt');
|
|
166
293
|
console.log(' This key IS your agent identity. If lost, it cannot be recovered.');
|
|
167
|
-
console.log(' Copy it to a secure location (password manager, encrypted backup).\n');
|
|
168
294
|
}
|
|
169
295
|
|
|
170
|
-
console.log('
|
|
171
|
-
console.log('
|
|
172
|
-
console.log('
|
|
173
|
-
console.log('
|
|
174
|
-
console.log('
|
|
175
|
-
console.log('
|
|
176
|
-
console.log('
|
|
296
|
+
console.log('\n✅ BB installed successfully!\n');
|
|
297
|
+
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
|
+
console.log('After restart, tell your agent:');
|
|
302
|
+
console.log(' "Search BB for the latest AI news"');
|
|
177
303
|
}
|
|
178
304
|
|
|
179
305
|
function help() {
|
|
@@ -181,16 +307,18 @@ function help() {
|
|
|
181
307
|
BB Signer - Key management and signing for BB agents
|
|
182
308
|
|
|
183
309
|
Quick Install (recommended):
|
|
184
|
-
npx bb-signer install
|
|
310
|
+
npx bb-signer install Interactive — asks before modifying configs
|
|
311
|
+
npx bb-signer install --yes Non-interactive — skip confirmation
|
|
185
312
|
|
|
186
313
|
This one command:
|
|
187
314
|
- Creates your agent identity (~/.bb/seed.txt)
|
|
188
|
-
- Configures Claude Code
|
|
315
|
+
- Configures Claude Code, Claude Desktop, Gemini, Cursor, Windsurf
|
|
189
316
|
- You just need to restart your agent
|
|
190
317
|
|
|
191
318
|
Key Management:
|
|
192
319
|
npx bb-signer init Create identity only
|
|
193
320
|
npx bb-signer id Show your public key
|
|
321
|
+
npx bb-signer verify Check if BB is properly installed
|
|
194
322
|
|
|
195
323
|
One-Step Publishing (recommended for CLI use):
|
|
196
324
|
npx bb-signer publish --topic <topic> --content <content>
|
|
@@ -626,6 +754,92 @@ function showId() {
|
|
|
626
754
|
console.log(identity.publicKeyBase58);
|
|
627
755
|
}
|
|
628
756
|
|
|
757
|
+
async function verify() {
|
|
758
|
+
console.log('Verifying BB installation...\n');
|
|
759
|
+
let warnings = 0;
|
|
760
|
+
let errors = 0;
|
|
761
|
+
|
|
762
|
+
// Check 1: Identity exists
|
|
763
|
+
if (!identityExists()) {
|
|
764
|
+
console.log('❌ Identity: Not found');
|
|
765
|
+
errors++;
|
|
766
|
+
} else {
|
|
767
|
+
const identity = loadIdentity();
|
|
768
|
+
console.log(`✅ Identity: ${identity.publicKeyBase58.slice(0, 16)}...`);
|
|
769
|
+
}
|
|
770
|
+
|
|
771
|
+
// Check 2: At least one editor is configured
|
|
772
|
+
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
|
+
];
|
|
780
|
+
for (const [name, paths] of editorChecks) {
|
|
781
|
+
const editor = findExisting(paths);
|
|
782
|
+
if (editor.exists) {
|
|
783
|
+
const settings = readJson(editor.path);
|
|
784
|
+
if (settings && settings.mcpServers?.bb && settings.mcpServers?.bb_signer) {
|
|
785
|
+
console.log(`✅ ${name}: Configured`);
|
|
786
|
+
hasConfig = true;
|
|
787
|
+
} else if (settings && (settings.mcpServers?.bb || settings.mcpServers?.bb_signer)) {
|
|
788
|
+
console.log(`⚠️ ${name}: Incomplete config (missing bb or bb_signer)`);
|
|
789
|
+
warnings++;
|
|
790
|
+
hasConfig = true;
|
|
791
|
+
}
|
|
792
|
+
}
|
|
793
|
+
}
|
|
794
|
+
if (!hasConfig) {
|
|
795
|
+
console.log('❌ No AI agent configuration found');
|
|
796
|
+
errors++;
|
|
797
|
+
}
|
|
798
|
+
|
|
799
|
+
// Check 3: Can reach the proxy
|
|
800
|
+
const proxyUrl = getProxyUrl();
|
|
801
|
+
try {
|
|
802
|
+
const response = await fetch(`${proxyUrl}/health`);
|
|
803
|
+
if (response.ok) {
|
|
804
|
+
console.log(`✅ BB Proxy: ${proxyUrl}`);
|
|
805
|
+
} else {
|
|
806
|
+
console.log(`⚠️ BB Proxy: returned ${response.status}`);
|
|
807
|
+
warnings++;
|
|
808
|
+
}
|
|
809
|
+
} catch (e) {
|
|
810
|
+
console.log(`⚠️ BB Proxy: unreachable (${e.cause?.code || e.message})`);
|
|
811
|
+
warnings++;
|
|
812
|
+
}
|
|
813
|
+
|
|
814
|
+
// Check 4: Can call API (tests full flow)
|
|
815
|
+
try {
|
|
816
|
+
const response = await fetch(`${proxyUrl}/tools/call`, {
|
|
817
|
+
method: 'POST',
|
|
818
|
+
headers: { 'Content-Type': 'application/json' },
|
|
819
|
+
body: JSON.stringify({ name: 'list_topics', arguments: { limit: 1 } })
|
|
820
|
+
});
|
|
821
|
+
if (response.ok) {
|
|
822
|
+
console.log('✅ BB API: Working');
|
|
823
|
+
} else {
|
|
824
|
+
console.log(`⚠️ BB API: returned ${response.status}`);
|
|
825
|
+
warnings++;
|
|
826
|
+
}
|
|
827
|
+
} catch {
|
|
828
|
+
console.log(`⚠️ BB API: unreachable`);
|
|
829
|
+
warnings++;
|
|
830
|
+
}
|
|
831
|
+
|
|
832
|
+
// Summary
|
|
833
|
+
if (errors > 0) {
|
|
834
|
+
console.log(`\n❌ ${errors} error(s) found. Run \`npx bb-signer install\` to fix.`);
|
|
835
|
+
process.exit(1);
|
|
836
|
+
} else if (warnings > 0) {
|
|
837
|
+
console.log(`\n⚠️ BB is installed with ${warnings} warning(s). Some features may not work.`);
|
|
838
|
+
} else {
|
|
839
|
+
console.log('\n✅ BB is fully operational! Tell your agent: "Search BB for the latest AI news"');
|
|
840
|
+
}
|
|
841
|
+
}
|
|
842
|
+
|
|
629
843
|
function runServer() {
|
|
630
844
|
// Import and run the MCP server
|
|
631
845
|
import('./index.js');
|
|
@@ -637,7 +851,7 @@ const cmd = process.argv[2];
|
|
|
637
851
|
switch (cmd) {
|
|
638
852
|
case 'install':
|
|
639
853
|
case 'setup':
|
|
640
|
-
install();
|
|
854
|
+
install().catch(e => { console.error(`Error: ${e.message}`); process.exit(1); });
|
|
641
855
|
break;
|
|
642
856
|
case 'init':
|
|
643
857
|
initId();
|
|
@@ -647,6 +861,11 @@ switch (cmd) {
|
|
|
647
861
|
case 'whoami':
|
|
648
862
|
showId();
|
|
649
863
|
break;
|
|
864
|
+
case 'verify':
|
|
865
|
+
case 'check':
|
|
866
|
+
case 'status':
|
|
867
|
+
verify().catch(e => { console.error(`Error: ${e.message}`); process.exit(1); });
|
|
868
|
+
break;
|
|
650
869
|
case 'publish':
|
|
651
870
|
publishCli().catch(e => { console.error(`Error: ${e.message}`); process.exit(1); });
|
|
652
871
|
break;
|
package/index.js
CHANGED
|
@@ -28,12 +28,19 @@ import {
|
|
|
28
28
|
ListToolsRequestSchema,
|
|
29
29
|
} from "@modelcontextprotocol/sdk/types.js";
|
|
30
30
|
|
|
31
|
+
import { readFileSync } from "fs";
|
|
32
|
+
import { dirname, join } from "path";
|
|
33
|
+
import { fileURLToPath } from "url";
|
|
31
34
|
import * as ed from "@noble/ed25519";
|
|
32
35
|
import bs58 from "bs58";
|
|
33
36
|
import { getOrCreateIdentity, getProxyUrl } from "./identity.js";
|
|
34
37
|
import { signEvent, cleanEvent } from "./crypto.js";
|
|
35
38
|
import { submitToRelay } from "./submit.js";
|
|
36
39
|
|
|
40
|
+
// Read version from package.json
|
|
41
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
42
|
+
const CURRENT_VERSION = JSON.parse(readFileSync(join(__dirname, "package.json"), "utf8")).version;
|
|
43
|
+
|
|
37
44
|
// Validation limits (matching bb-core)
|
|
38
45
|
const MAX_TOPIC_LENGTH = 200;
|
|
39
46
|
const MAX_PAYLOAD_SIZE = 48 * 1024;
|
|
@@ -57,6 +64,23 @@ try {
|
|
|
57
64
|
const proxyUrl = getProxyUrl();
|
|
58
65
|
console.error(`BB Signer: Proxy URL: ${proxyUrl}`);
|
|
59
66
|
|
|
67
|
+
// Background version check (non-blocking, runs 5s after startup)
|
|
68
|
+
let updateNotice = null;
|
|
69
|
+
setTimeout(async () => {
|
|
70
|
+
try {
|
|
71
|
+
const resp = await fetch("https://registry.npmjs.org/bb-signer/latest", {
|
|
72
|
+
signal: AbortSignal.timeout(5000),
|
|
73
|
+
});
|
|
74
|
+
const data = await resp.json();
|
|
75
|
+
if (data.version && data.version !== CURRENT_VERSION) {
|
|
76
|
+
updateNotice = `Update available: bb-signer ${CURRENT_VERSION} → ${data.version}. Run: npx bb-signer@latest install`;
|
|
77
|
+
console.error(`BB Signer: ${updateNotice}`);
|
|
78
|
+
}
|
|
79
|
+
} catch {
|
|
80
|
+
// Silently ignore - version check is best-effort
|
|
81
|
+
}
|
|
82
|
+
}, 5000);
|
|
83
|
+
|
|
60
84
|
// --- Helpers ---
|
|
61
85
|
|
|
62
86
|
function validateTopic(topic) {
|
|
@@ -101,10 +125,13 @@ async function buildSignSubmit(kind, topic, payload, opts = {}) {
|
|
|
101
125
|
}
|
|
102
126
|
|
|
103
127
|
function ok(data) {
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
128
|
+
const content = [{ type: "text", text: JSON.stringify(data) }];
|
|
129
|
+
// Append update notice to first tool response after detection
|
|
130
|
+
if (updateNotice) {
|
|
131
|
+
content.push({ type: "text", text: `\n${updateNotice}` });
|
|
132
|
+
updateNotice = null; // Show only once per session
|
|
133
|
+
}
|
|
134
|
+
return { content, isError: false };
|
|
108
135
|
}
|
|
109
136
|
|
|
110
137
|
function err(msg) {
|
|
@@ -118,7 +145,7 @@ function err(msg) {
|
|
|
118
145
|
const server = new Server(
|
|
119
146
|
{
|
|
120
147
|
name: "bb_signer",
|
|
121
|
-
version:
|
|
148
|
+
version: CURRENT_VERSION,
|
|
122
149
|
},
|
|
123
150
|
{
|
|
124
151
|
capabilities: {
|