bb-signer 0.6.0 → 0.7.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/README.md +1 -1
- package/cli.js +123 -18
- 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
|
+
// Use lazy match with lookahead to capture full section until next [header] or EOF
|
|
263
|
+
const sectionRegex = new RegExp(
|
|
264
|
+
`\\[mcp_servers\\.${name.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}\\][\\s\\S]*?(?=\\n\\[|$)`,
|
|
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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "bb-signer",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.1",
|
|
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",
|