claude-flow
Version:
Ruflo - Enterprise AI agent orchestration for Claude Code. Deploy 60+ specialized agents in coordinated swarms with self-learning, fault-tolerant consensus, vector memory, and MCP integration
215 lines • 9.51 kB
JavaScript
/**
* V3 CLI Appliance Advanced Commands (Phase 3-4)
* Sign, publish, and hot-patch RVFA appliances.
*/
import { output } from '../output.js';
function fmtSize(bytes) {
if (bytes < 1024)
return `${bytes} B`;
if (bytes < 1024 * 1024)
return `${(bytes / 1024).toFixed(1)} KB`;
if (bytes < 1024 * 1024 * 1024)
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
return `${(bytes / (1024 * 1024 * 1024)).toFixed(2)} GB`;
}
function errMsg(err) {
return err instanceof Error ? err.message : String(err);
}
const fail = (msg, detail) => {
output.printError(msg, detail);
return { success: false, exitCode: 1 };
};
function hdr(title) {
output.writeln();
output.writeln(output.bold(title));
output.writeln(output.dim('─'.repeat(50)));
output.writeln();
}
async function requireFile(file) {
const fs = await import('fs');
if (!fs.existsSync(file)) {
output.printError(`File not found: ${file}`);
return false;
}
return true;
}
// SIGN
export const signCommand = {
name: 'sign',
description: 'Sign an RVFA appliance with Ed25519 for tamper detection',
options: [
{ name: 'file', short: 'f', type: 'string', description: 'Path to .rvf file', required: true },
{ name: 'key', short: 'k', type: 'string', description: 'Path to Ed25519 private key (PEM)' },
{ name: 'generate-keys', type: 'boolean', description: 'Generate a new key pair' },
{ name: 'key-dir', type: 'string', description: 'Directory for key storage', default: '.rvfa-keys' },
{ name: 'signer', type: 'string', description: 'Publisher name for signature metadata' },
],
action: async (ctx) => {
const file = ctx.flags.file;
const keyPath = ctx.flags.key;
const genKeys = ctx.flags['generate-keys'];
const keyDir = ctx.flags['key-dir'] || '.rvfa-keys';
const signer = ctx.flags.signer;
if (!file)
return fail('--file is required');
try {
const signing = await import('../appliance/rvfa-signing.js');
if (genKeys) {
hdr('Generating Ed25519 Key Pair');
const kp = await signing.generateKeyPair();
const paths = await signing.saveKeyPair(kp, keyDir);
output.printSuccess(`Public key: ${paths.publicKeyPath}`);
output.printSuccess(`Private key: ${paths.privateKeyPath}`);
output.printInfo(`Fingerprint: ${kp.fingerprint}`);
output.writeln(output.dim(' Keep the private key secure. Share only the public key.'));
output.writeln();
}
if (!(await requireFile(file)))
return { success: false, exitCode: 1 };
hdr('Signing RVFA Appliance');
let privateKey;
if (keyPath) {
const fs = await import('fs');
privateKey = fs.readFileSync(keyPath);
}
else {
const kp = await signing.loadKeyPair(keyDir);
privateKey = kp.privateKey;
}
const s = new signing.RvfaSigner(privateKey);
const meta = await s.signAppliance(file, signer);
output.printSuccess('Appliance signed successfully');
output.printInfo(`Algorithm: ${meta.algorithm}`);
output.printInfo(`Fingerprint: ${meta.publicKeyFingerprint}`);
output.printInfo(`Signed at: ${meta.signedAt}`);
if (signer)
output.printInfo(`Signed by: ${signer}`);
output.printInfo(`Signature: ${meta.signature.slice(0, 32)}...`);
return { success: true, data: meta };
}
catch (err) {
return fail('Signing failed', errMsg(err));
}
},
};
// PUBLISH
export const publishCommand = {
name: 'publish',
description: 'Publish an RVFA appliance to IPFS via Pinata',
options: [
{ name: 'file', short: 'f', type: 'string', description: 'Path to .rvf file', required: true },
{ name: 'name', short: 'n', type: 'string', description: 'Publication name' },
{ name: 'description', type: 'string', description: 'Description' },
],
action: async (ctx) => {
const file = ctx.flags.file;
if (!file)
return fail('--file is required');
if (!(await requireFile(file)))
return { success: false, exitCode: 1 };
try {
const dist = await import('../appliance/rvfa-distribution.js');
hdr('Publishing RVFA to IPFS');
output.printInfo(`File: ${file}`);
output.writeln();
const publisher = dist.createPublisher();
const result = await publisher.publish(file, {
name: ctx.flags.name,
description: ctx.flags.description,
});
output.printSuccess('Published successfully');
output.printInfo(`CID: ${output.bold(result.cid)}`);
output.printInfo(`Size: ${fmtSize(result.size)}`);
output.printInfo(`Gateway: ${result.gatewayUrl}`);
return { success: true, data: result };
}
catch (err) {
return fail('Publishing failed', errMsg(err));
}
},
};
// UPDATE (hot-patch)
export const updateAppCommand = {
name: 'update',
description: 'Hot-patch a section in an RVFA appliance',
options: [
{ name: 'file', short: 'f', type: 'string', description: 'Path to .rvf file', required: true },
{ name: 'section', short: 's', type: 'string', description: 'Section to patch (e.g. ruflo, models)', required: true },
{ name: 'patch', short: 'p', type: 'string', description: 'Path to .rvfp patch file' },
{ name: 'data', short: 'd', type: 'string', description: 'Path to new section data (creates patch automatically)' },
{ name: 'version', type: 'string', description: 'Patch version', default: '0.0.1' },
{ name: 'no-backup', type: 'boolean', description: 'Skip backup creation' },
{ name: 'public-key', type: 'string', description: 'Path to public key for patch verification' },
],
action: async (ctx) => {
const file = ctx.flags.file;
const section = ctx.flags.section;
const patchPath = ctx.flags.patch;
const dataPath = ctx.flags.data;
if (!file || !section)
return fail('--file and --section are required');
if (!patchPath && !dataPath)
return fail('Provide --patch (RVFP file) or --data (raw section data)');
if (!(await requireFile(file)))
return { success: false, exitCode: 1 };
try {
const dist = await import('../appliance/rvfa-distribution.js');
const { RvfaReader } = await import('../appliance/rvfa-format.js');
const fs = await import('fs');
hdr('RVFA Hot-Patch Update');
output.printInfo(`Appliance: ${file}`);
output.printInfo(`Section: ${section}`);
output.writeln();
let patchBuf;
if (patchPath) {
if (!(await requireFile(patchPath)))
return { success: false, exitCode: 1 };
patchBuf = fs.readFileSync(patchPath);
output.printInfo(`Patch file: ${patchPath} (${fmtSize(patchBuf.length)})`);
}
else {
if (!(await requireFile(dataPath)))
return { success: false, exitCode: 1 };
const newData = fs.readFileSync(dataPath);
const reader = await RvfaReader.fromFile(file);
const appHdr = reader.getHeader();
output.printInfo(`Creating patch for section "${section}" (${fmtSize(newData.length)} new data)`);
patchBuf = await dist.RvfaPatcher.createPatch({
targetName: appHdr.name,
targetVersion: appHdr.appVersion,
sectionId: section,
sectionData: newData,
patchVersion: ctx.flags.version || '0.0.1',
compression: 'gzip',
});
}
let pubKey;
if (ctx.flags['public-key']) {
const pkPath = ctx.flags['public-key'];
if (!(await requireFile(pkPath)))
return { success: false, exitCode: 1 };
pubKey = fs.readFileSync(pkPath);
}
const result = await dist.RvfaPatcher.applyPatch(file, patchBuf, {
backup: !ctx.flags['no-backup'],
verify: true,
publicKey: pubKey,
});
if (result.success) {
output.printSuccess(`Section "${result.patchedSection}" updated successfully`);
output.printInfo(`New size: ${fmtSize(result.newSize)}`);
if (result.backupPath)
output.printInfo(`Backup: ${result.backupPath}`);
}
else {
output.printError('Patch failed');
result.errors.forEach(e => output.writeln(` ${output.error('X')} ${e}`));
}
return { success: result.success, exitCode: result.success ? 0 : 1, data: result };
}
catch (err) {
return fail('Update failed', errMsg(err));
}
},
};
//# sourceMappingURL=appliance-advanced.js.map