--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>
86 lines
2.4 KiB
JavaScript
Executable File
86 lines
2.4 KiB
JavaScript
Executable File
#!/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(', ')}`);
|
|
}
|