UNPKG

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
/** * 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