Add ccc-keygen for secrets file generation
--create <names> generates secrets.json with random 32-byte hex secrets --filter <names> extracts a subset of users into filtered-secrets.json --input/--output override default file paths Removes hardcoded user/agent assumptions from secrets.example.json. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
85
bin/ccc-keygen.mjs
Executable file
85
bin/ccc-keygen.mjs
Executable file
@@ -0,0 +1,85 @@
|
||||
#!/usr/bin/env node
|
||||
import { randomBytes } from 'crypto';
|
||||
import { readFileSync, writeFileSync, existsSync } from 'fs';
|
||||
|
||||
function get_arg(argv, flag) {
|
||||
const i = argv.indexOf(flag);
|
||||
return i !== -1 ? argv[i + 1] : null;
|
||||
}
|
||||
|
||||
function has_flag(argv, flag) {
|
||||
return argv.includes(flag);
|
||||
}
|
||||
|
||||
function parse_names(value, flag) {
|
||||
if (!value) {
|
||||
console.error(`${flag} requires a comma-separated list of usernames`);
|
||||
process.exit(1);
|
||||
}
|
||||
return value.split(',').map(s => s.trim()).filter(Boolean);
|
||||
}
|
||||
|
||||
function generate_secret() {
|
||||
return randomBytes(32).toString('hex');
|
||||
}
|
||||
|
||||
function read_secrets_file(input_path) {
|
||||
if (!existsSync(input_path)) {
|
||||
console.error(`Secrets file not found: ${input_path}`);
|
||||
process.exit(1);
|
||||
}
|
||||
try {
|
||||
return JSON.parse(readFileSync(input_path, 'utf8'));
|
||||
} catch (err) {
|
||||
console.error(`Cannot read secrets file: ${err.message}`);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
function write_secrets_file(output_path, data) {
|
||||
writeFileSync(output_path, JSON.stringify(data, null, '\t') + '\n', 'utf8');
|
||||
console.log(`Written: ${output_path}`);
|
||||
}
|
||||
|
||||
const argv = process.argv;
|
||||
const create_arg = get_arg(argv, '--create');
|
||||
const filter_arg = get_arg(argv, '--filter');
|
||||
const input_arg = get_arg(argv, '--input') || 'secrets.json';
|
||||
const output_arg = get_arg(argv, '--output');
|
||||
|
||||
if (!create_arg && !filter_arg) {
|
||||
console.error(
|
||||
'Usage:\n' +
|
||||
' ccc-keygen --create <names> [--output secrets.json]\n' +
|
||||
' ccc-keygen --filter <names> [--input secrets.json] [--output filtered-secrets.json]'
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
if (create_arg) {
|
||||
const names = parse_names(create_arg, '--create');
|
||||
const output_path = output_arg || 'secrets.json';
|
||||
const users = {};
|
||||
for (const name of names) {
|
||||
users[name] = { secret: generate_secret(), canApprove: [] };
|
||||
}
|
||||
write_secrets_file(output_path, { users });
|
||||
console.log(`Created users: ${names.join(', ')}`);
|
||||
console.log('Edit canApprove lists to configure approval permissions.');
|
||||
}
|
||||
|
||||
if (filter_arg) {
|
||||
const names = parse_names(filter_arg, '--filter');
|
||||
const output_path = output_arg || 'filtered-secrets.json';
|
||||
const source = read_secrets_file(input_arg);
|
||||
const users = {};
|
||||
for (const name of names) {
|
||||
if (!source.users?.[name]) {
|
||||
console.error(`User '${name}' not found in ${input_arg}`);
|
||||
process.exit(1);
|
||||
}
|
||||
users[name] = source.users[name];
|
||||
}
|
||||
write_secrets_file(output_path, { users });
|
||||
console.log(`Filtered users: ${names.join(', ')}`);
|
||||
}
|
||||
Reference in New Issue
Block a user