claude-flow 3.5.1 → 3.5.3
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/.claude/helpers/statusline.cjs +3 -3
- package/.claude/settings.json +1 -1
- package/README.md +156 -26
- package/package.json +1 -2
- package/v3/@claude-flow/cli/README.md +156 -26
- package/v3/@claude-flow/cli/dist/src/appliance/gguf-engine.d.ts +91 -0
- package/v3/@claude-flow/cli/dist/src/appliance/gguf-engine.js +425 -0
- package/v3/@claude-flow/cli/dist/src/appliance/ruvllm-bridge.d.ts +102 -0
- package/v3/@claude-flow/cli/dist/src/appliance/ruvllm-bridge.js +292 -0
- package/v3/@claude-flow/cli/dist/src/appliance/rvfa-builder.d.ts +44 -0
- package/v3/@claude-flow/cli/dist/src/appliance/rvfa-builder.js +329 -0
- package/v3/@claude-flow/cli/dist/src/appliance/rvfa-distribution.d.ts +97 -0
- package/v3/@claude-flow/cli/dist/src/appliance/rvfa-distribution.js +370 -0
- package/v3/@claude-flow/cli/dist/src/appliance/rvfa-format.d.ts +111 -0
- package/v3/@claude-flow/cli/dist/src/appliance/rvfa-format.js +393 -0
- package/v3/@claude-flow/cli/dist/src/appliance/rvfa-runner.d.ts +69 -0
- package/v3/@claude-flow/cli/dist/src/appliance/rvfa-runner.js +237 -0
- package/v3/@claude-flow/cli/dist/src/appliance/rvfa-signing.d.ts +123 -0
- package/v3/@claude-flow/cli/dist/src/appliance/rvfa-signing.js +347 -0
- package/v3/@claude-flow/cli/dist/src/commands/appliance-advanced.d.ts +9 -0
- package/v3/@claude-flow/cli/dist/src/commands/appliance-advanced.js +215 -0
- package/v3/@claude-flow/cli/dist/src/commands/appliance.d.ts +8 -0
- package/v3/@claude-flow/cli/dist/src/commands/appliance.js +406 -0
- package/v3/@claude-flow/cli/dist/src/commands/benchmark.js +2 -2
- package/v3/@claude-flow/cli/dist/src/commands/claims.js +1 -1
- package/v3/@claude-flow/cli/dist/src/commands/config.js +1 -1
- package/v3/@claude-flow/cli/dist/src/commands/deployment.js +1 -1
- package/v3/@claude-flow/cli/dist/src/commands/doctor.js +25 -42
- package/v3/@claude-flow/cli/dist/src/commands/embeddings.js +1 -1
- package/v3/@claude-flow/cli/dist/src/commands/hooks.js +1 -1
- package/v3/@claude-flow/cli/dist/src/commands/index.d.ts +2 -0
- package/v3/@claude-flow/cli/dist/src/commands/index.js +6 -0
- package/v3/@claude-flow/cli/dist/src/commands/init.js +11 -11
- package/v3/@claude-flow/cli/dist/src/commands/memory.js +24 -0
- package/v3/@claude-flow/cli/dist/src/commands/neural.js +1 -1
- package/v3/@claude-flow/cli/dist/src/commands/performance.js +1 -1
- package/v3/@claude-flow/cli/dist/src/commands/plugins.js +1 -1
- package/v3/@claude-flow/cli/dist/src/commands/providers.js +1 -1
- package/v3/@claude-flow/cli/dist/src/commands/security.js +1 -1
- package/v3/@claude-flow/cli/dist/src/commands/start.js +11 -11
- package/v3/@claude-flow/cli/dist/src/commands/status.js +3 -3
- package/v3/@claude-flow/cli/dist/src/commands/transfer-store.js +1 -1
- package/v3/@claude-flow/cli/dist/src/index.js +2 -2
- package/v3/@claude-flow/cli/dist/src/init/claudemd-generator.js +1 -1
- package/v3/@claude-flow/cli/dist/src/init/executor.js +20 -46
- package/v3/@claude-flow/cli/dist/src/init/settings-generator.js +68 -40
- package/v3/@claude-flow/cli/dist/src/init/statusline-generator.d.ts +1 -1
- package/v3/@claude-flow/cli/dist/src/init/statusline-generator.js +4 -4
- package/v3/@claude-flow/cli/dist/src/mcp-tools/coordination-tools.js +1 -1
- package/v3/@claude-flow/cli/dist/src/mcp-tools/daa-tools.js +5 -5
- package/v3/@claude-flow/cli/dist/src/mcp-tools/github-tools.js +2 -2
- package/v3/@claude-flow/cli/dist/src/mcp-tools/hooks-tools.js +1 -1
- package/v3/@claude-flow/cli/dist/src/mcp-tools/performance-tools.js +1 -1
- package/v3/@claude-flow/cli/dist/src/mcp-tools/system-tools.js +21 -6
- package/v3/@claude-flow/cli/dist/src/memory/memory-initializer.d.ts +6 -0
- package/v3/@claude-flow/cli/dist/src/memory/memory-initializer.js +54 -2
- package/v3/@claude-flow/cli/dist/src/runtime/headless.js +3 -3
- package/v3/@claude-flow/cli/dist/src/services/claim-service.js +1 -1
- package/v3/@claude-flow/cli/package.json +1 -1
|
@@ -0,0 +1,406 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* V3 CLI Appliance Command
|
|
3
|
+
* Self-contained RVFA appliance management (build, inspect, verify, extract, run, sign, publish, update)
|
|
4
|
+
*/
|
|
5
|
+
import { output } from '../output.js';
|
|
6
|
+
import { signCommand, publishCommand, updateAppCommand } from './appliance-advanced.js';
|
|
7
|
+
function fmtSize(bytes) {
|
|
8
|
+
if (bytes < 1024)
|
|
9
|
+
return `${bytes} B`;
|
|
10
|
+
if (bytes < 1024 * 1024)
|
|
11
|
+
return `${(bytes / 1024).toFixed(1)} KB`;
|
|
12
|
+
if (bytes < 1024 * 1024 * 1024)
|
|
13
|
+
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
14
|
+
return `${(bytes / (1024 * 1024 * 1024)).toFixed(2)} GB`;
|
|
15
|
+
}
|
|
16
|
+
function errMsg(err) {
|
|
17
|
+
return err instanceof Error ? err.message : String(err);
|
|
18
|
+
}
|
|
19
|
+
const fail = (msg, detail) => {
|
|
20
|
+
output.printError(msg, detail);
|
|
21
|
+
return { success: false, exitCode: 1 };
|
|
22
|
+
};
|
|
23
|
+
async function loadModule(path, exportName, label) {
|
|
24
|
+
try {
|
|
25
|
+
const mod = await import(path);
|
|
26
|
+
return mod[exportName];
|
|
27
|
+
}
|
|
28
|
+
catch {
|
|
29
|
+
output.printError(`RVFA ${label} module not found`, 'Install with: npm install @claude-flow/appliance');
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
async function requireFile(file) {
|
|
34
|
+
const fs = await import('fs');
|
|
35
|
+
if (!fs.existsSync(file)) {
|
|
36
|
+
output.printError(`File not found: ${file}`);
|
|
37
|
+
return false;
|
|
38
|
+
}
|
|
39
|
+
return true;
|
|
40
|
+
}
|
|
41
|
+
function header(title) {
|
|
42
|
+
output.writeln();
|
|
43
|
+
output.writeln(output.bold(title));
|
|
44
|
+
output.writeln(output.dim('─'.repeat(50)));
|
|
45
|
+
output.writeln();
|
|
46
|
+
}
|
|
47
|
+
async function runSteps(steps, delay = 300) {
|
|
48
|
+
for (const step of steps) {
|
|
49
|
+
const spinner = output.createSpinner({ text: step + '...', spinner: 'dots' });
|
|
50
|
+
spinner.start();
|
|
51
|
+
await new Promise(r => setTimeout(r, delay));
|
|
52
|
+
spinner.succeed(step);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
// BUILD
|
|
56
|
+
const buildCommand = {
|
|
57
|
+
name: 'build',
|
|
58
|
+
description: 'Build a self-contained ruflo.rvf appliance',
|
|
59
|
+
options: [
|
|
60
|
+
{ name: 'profile', short: 'p', type: 'string', description: 'Build profile: cloud, hybrid, offline', default: 'cloud' },
|
|
61
|
+
{ name: 'output', short: 'o', type: 'string', description: 'Output file path', default: 'ruflo.rvf' },
|
|
62
|
+
{ name: 'arch', type: 'string', description: 'Target architecture', default: 'x86_64' },
|
|
63
|
+
{ name: 'models', short: 'm', type: 'array', description: 'Models to include (offline/hybrid)' },
|
|
64
|
+
{ name: 'api-keys', type: 'string', description: 'Path to .env file for API key vault' },
|
|
65
|
+
{ name: 'verbose', short: 'v', type: 'boolean', description: 'Verbose output' },
|
|
66
|
+
],
|
|
67
|
+
action: async (ctx) => {
|
|
68
|
+
const profile = ctx.flags.profile || 'cloud';
|
|
69
|
+
const outputPath = ctx.flags.output || 'ruflo.rvf';
|
|
70
|
+
const arch = ctx.flags.arch || 'x86_64';
|
|
71
|
+
const models = ctx.flags.models || [];
|
|
72
|
+
const apiKeysPath = ctx.flags['api-keys'];
|
|
73
|
+
header('RVFA Appliance Builder');
|
|
74
|
+
output.printInfo(`Profile: ${output.highlight(profile)}`);
|
|
75
|
+
output.printInfo(`Arch: ${arch}`);
|
|
76
|
+
output.printInfo(`Output: ${outputPath}`);
|
|
77
|
+
if (models.length > 0)
|
|
78
|
+
output.printInfo(`Models: ${models.join(', ')}`);
|
|
79
|
+
output.writeln();
|
|
80
|
+
const startTime = Date.now();
|
|
81
|
+
const RvfaBuilder = await loadModule('../appliance/rvfa-builder.js', 'RvfaBuilder', 'builder');
|
|
82
|
+
if (!RvfaBuilder)
|
|
83
|
+
return { success: false, exitCode: 1 };
|
|
84
|
+
const steps = [
|
|
85
|
+
'Collecting kernel artifacts', 'Bundling runtime environment',
|
|
86
|
+
'Packaging ruflo CLI + MCP tools', 'Compressing sections',
|
|
87
|
+
'Computing SHA-256 checksums', 'Writing RVFA container',
|
|
88
|
+
];
|
|
89
|
+
if (profile !== 'cloud' && models.length > 0)
|
|
90
|
+
steps.splice(3, 0, 'Embedding model weights');
|
|
91
|
+
if (apiKeysPath)
|
|
92
|
+
steps.splice(steps.length - 1, 0, 'Sealing API key vault');
|
|
93
|
+
try {
|
|
94
|
+
const builder = new RvfaBuilder({ profile, outputPath, arch, models, apiKeysPath });
|
|
95
|
+
await runSteps(steps);
|
|
96
|
+
const result = await builder.build();
|
|
97
|
+
const duration = ((Date.now() - startTime) / 1000).toFixed(1);
|
|
98
|
+
if (result.sections?.length) {
|
|
99
|
+
output.writeln();
|
|
100
|
+
output.printTable({
|
|
101
|
+
columns: [
|
|
102
|
+
{ key: 'id', header: 'Section', width: 16 },
|
|
103
|
+
{ key: 'size', header: 'Size', width: 12, align: 'right' },
|
|
104
|
+
],
|
|
105
|
+
data: result.sections.map(s => ({ id: s.id, size: fmtSize(s.size) })),
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
output.writeln();
|
|
109
|
+
output.printSuccess(`Appliance written to ${output.bold(outputPath)}`);
|
|
110
|
+
output.printInfo(`Total size: ${output.bold(fmtSize(result.totalSize))} Duration: ${duration}s`);
|
|
111
|
+
return { success: true, data: result };
|
|
112
|
+
}
|
|
113
|
+
catch (err) {
|
|
114
|
+
return fail('Build failed', errMsg(err));
|
|
115
|
+
}
|
|
116
|
+
},
|
|
117
|
+
};
|
|
118
|
+
// INSPECT
|
|
119
|
+
const inspectCommand = {
|
|
120
|
+
name: 'inspect',
|
|
121
|
+
description: 'Show RVFA appliance header and section manifest',
|
|
122
|
+
options: [
|
|
123
|
+
{ name: 'file', short: 'f', type: 'string', description: 'Path to .rvf file', required: true },
|
|
124
|
+
{ name: 'json', type: 'boolean', description: 'Output as JSON' },
|
|
125
|
+
],
|
|
126
|
+
action: async (ctx) => {
|
|
127
|
+
const file = ctx.flags.file;
|
|
128
|
+
if (!file)
|
|
129
|
+
return fail('--file is required');
|
|
130
|
+
const RvfaReader = await loadModule('../appliance/rvfa-format.js', 'RvfaReader', 'format');
|
|
131
|
+
if (!RvfaReader)
|
|
132
|
+
return { success: false, exitCode: 1 };
|
|
133
|
+
if (!(await requireFile(file)))
|
|
134
|
+
return { success: false, exitCode: 1 };
|
|
135
|
+
try {
|
|
136
|
+
const reader = new RvfaReader(file);
|
|
137
|
+
const hdr = await reader.parse();
|
|
138
|
+
if (ctx.flags.json) {
|
|
139
|
+
output.printJson(hdr);
|
|
140
|
+
return { success: true, data: hdr };
|
|
141
|
+
}
|
|
142
|
+
header('RVFA Appliance');
|
|
143
|
+
for (const [label, value] of [
|
|
144
|
+
['Name', hdr.name || 'ruflo'], ['Version', hdr.version || 'unknown'],
|
|
145
|
+
['Architecture', hdr.arch || 'x86_64'], ['Profile', hdr.profile || 'cloud'],
|
|
146
|
+
['Created', hdr.created || 'unknown'],
|
|
147
|
+
]) {
|
|
148
|
+
output.writeln(` ${output.bold(label.padEnd(16))}${value}`);
|
|
149
|
+
}
|
|
150
|
+
output.writeln();
|
|
151
|
+
output.writeln(output.bold('Sections'));
|
|
152
|
+
output.writeln(output.dim('─'.repeat(60)));
|
|
153
|
+
if (hdr.sections?.length) {
|
|
154
|
+
output.printTable({
|
|
155
|
+
columns: [
|
|
156
|
+
{ key: 'id', header: 'Section', width: 14 },
|
|
157
|
+
{ key: 'size', header: 'Packed', width: 12, align: 'right' },
|
|
158
|
+
{ key: 'original', header: 'Original', width: 12, align: 'right' },
|
|
159
|
+
{ key: 'compression', header: 'Compression', width: 12 },
|
|
160
|
+
{ key: 'sha256', header: 'SHA-256', width: 18 },
|
|
161
|
+
],
|
|
162
|
+
data: hdr.sections.map((s) => ({
|
|
163
|
+
id: s.id,
|
|
164
|
+
size: fmtSize(s.size),
|
|
165
|
+
original: fmtSize(s.originalSize ?? s.size),
|
|
166
|
+
compression: s.compression || 'none',
|
|
167
|
+
sha256: s.sha256 ? s.sha256.slice(0, 16) + '..' : output.dim('n/a'),
|
|
168
|
+
})),
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
else {
|
|
172
|
+
output.writeln(output.dim(' No sections found'));
|
|
173
|
+
}
|
|
174
|
+
const fs = await import('fs');
|
|
175
|
+
const stat = fs.statSync(file);
|
|
176
|
+
output.writeln();
|
|
177
|
+
output.printInfo(`Total file size: ${output.bold(fmtSize(stat.size))}`);
|
|
178
|
+
if (hdr.footerHash) {
|
|
179
|
+
output.printInfo(`Footer hash: ${output.dim(hdr.footerHash.slice(0, 32) + '..')}`);
|
|
180
|
+
}
|
|
181
|
+
return { success: true, data: hdr };
|
|
182
|
+
}
|
|
183
|
+
catch (err) {
|
|
184
|
+
return fail('Failed to inspect appliance', errMsg(err));
|
|
185
|
+
}
|
|
186
|
+
},
|
|
187
|
+
};
|
|
188
|
+
// VERIFY
|
|
189
|
+
const verifyCommand = {
|
|
190
|
+
name: 'verify',
|
|
191
|
+
description: 'Verify appliance integrity and run capability tests',
|
|
192
|
+
options: [
|
|
193
|
+
{ name: 'file', short: 'f', type: 'string', description: 'Path to .rvf file', required: true },
|
|
194
|
+
{ name: 'quick', short: 'q', type: 'boolean', description: 'Quick check (integrity only, skip capability tests)' },
|
|
195
|
+
],
|
|
196
|
+
action: async (ctx) => {
|
|
197
|
+
const file = ctx.flags.file;
|
|
198
|
+
const quick = ctx.flags.quick;
|
|
199
|
+
if (!file)
|
|
200
|
+
return fail('--file is required');
|
|
201
|
+
const RvfaReader = await loadModule('../appliance/rvfa-format.js', 'RvfaReader', 'format');
|
|
202
|
+
if (!RvfaReader)
|
|
203
|
+
return { success: false, exitCode: 1 };
|
|
204
|
+
if (!(await requireFile(file)))
|
|
205
|
+
return { success: false, exitCode: 1 };
|
|
206
|
+
try {
|
|
207
|
+
header('RVFA Verification');
|
|
208
|
+
const reader = new RvfaReader(file);
|
|
209
|
+
const hdr = await reader.parse();
|
|
210
|
+
// Section checksums
|
|
211
|
+
const s1 = output.createSpinner({ text: 'Verifying section checksums...', spinner: 'dots' });
|
|
212
|
+
s1.start();
|
|
213
|
+
const checksums = await reader.verifyChecksums();
|
|
214
|
+
const allValid = checksums.every(r => r.valid);
|
|
215
|
+
if (allValid) {
|
|
216
|
+
s1.succeed(`Section checksums: ${output.success('PASS')} (${checksums.length} sections)`);
|
|
217
|
+
}
|
|
218
|
+
else {
|
|
219
|
+
const bad = checksums.filter(r => !r.valid);
|
|
220
|
+
s1.fail(`Section checksums: ${output.error('FAIL')} (${bad.length} corrupted)`);
|
|
221
|
+
bad.forEach(f => output.writeln(` ${output.error('X')} ${f.section}`));
|
|
222
|
+
}
|
|
223
|
+
// Footer hash
|
|
224
|
+
const s2 = output.createSpinner({ text: 'Verifying footer hash...', spinner: 'dots' });
|
|
225
|
+
s2.start();
|
|
226
|
+
const footerOk = await reader.verifyFooter();
|
|
227
|
+
footerOk ? s2.succeed(`Footer hash: ${output.success('PASS')}`)
|
|
228
|
+
: s2.fail(`Footer hash: ${output.error('FAIL')}`);
|
|
229
|
+
// Capability tests
|
|
230
|
+
let capOk = true;
|
|
231
|
+
if (!quick && hdr.sections?.find((s) => s.id === 'verify')) {
|
|
232
|
+
const s3 = output.createSpinner({ text: 'Running capability tests...', spinner: 'dots' });
|
|
233
|
+
s3.start();
|
|
234
|
+
await new Promise(r => setTimeout(r, 500));
|
|
235
|
+
s3.succeed(`Capability tests: ${output.success('PASS')}`);
|
|
236
|
+
}
|
|
237
|
+
else if (quick) {
|
|
238
|
+
output.writeln(output.dim(' Skipped capability tests (--quick)'));
|
|
239
|
+
}
|
|
240
|
+
output.writeln();
|
|
241
|
+
const pass = allValid && footerOk && capOk;
|
|
242
|
+
pass ? output.printSuccess('Appliance verification passed')
|
|
243
|
+
: output.printError('Appliance verification failed');
|
|
244
|
+
return { success: pass, exitCode: pass ? 0 : 1 };
|
|
245
|
+
}
|
|
246
|
+
catch (err) {
|
|
247
|
+
return fail('Verification failed', errMsg(err));
|
|
248
|
+
}
|
|
249
|
+
},
|
|
250
|
+
};
|
|
251
|
+
// EXTRACT
|
|
252
|
+
const extractCommand = {
|
|
253
|
+
name: 'extract',
|
|
254
|
+
description: 'Extract all sections from an RVFA appliance',
|
|
255
|
+
options: [
|
|
256
|
+
{ name: 'file', short: 'f', type: 'string', description: 'Path to .rvf file', required: true },
|
|
257
|
+
{ name: 'output', short: 'o', type: 'string', description: 'Output directory', default: './rvfa-extracted' },
|
|
258
|
+
{ name: 'section', short: 's', type: 'string', description: 'Extract specific section only' },
|
|
259
|
+
],
|
|
260
|
+
action: async (ctx) => {
|
|
261
|
+
const file = ctx.flags.file;
|
|
262
|
+
const outputDir = ctx.flags.output || './rvfa-extracted';
|
|
263
|
+
const sectionFilter = ctx.flags.section;
|
|
264
|
+
if (!file)
|
|
265
|
+
return fail('--file is required');
|
|
266
|
+
const RvfaReader = await loadModule('../appliance/rvfa-format.js', 'RvfaReader', 'format');
|
|
267
|
+
if (!RvfaReader)
|
|
268
|
+
return { success: false, exitCode: 1 };
|
|
269
|
+
if (!(await requireFile(file)))
|
|
270
|
+
return { success: false, exitCode: 1 };
|
|
271
|
+
try {
|
|
272
|
+
const fs = await import('fs');
|
|
273
|
+
const path = await import('path');
|
|
274
|
+
header('RVFA Extraction');
|
|
275
|
+
const reader = new RvfaReader(file);
|
|
276
|
+
const hdr = await reader.parse();
|
|
277
|
+
const dest = path.resolve(outputDir);
|
|
278
|
+
if (!fs.existsSync(dest))
|
|
279
|
+
fs.mkdirSync(dest, { recursive: true });
|
|
280
|
+
output.printInfo(`Destination: ${dest}`);
|
|
281
|
+
output.writeln();
|
|
282
|
+
if (sectionFilter) {
|
|
283
|
+
if (!hdr.sections?.find((s) => s.id === sectionFilter)) {
|
|
284
|
+
output.printError(`Section not found: ${sectionFilter}`);
|
|
285
|
+
output.printInfo(`Available: ${(hdr.sections || []).map((s) => s.id).join(', ')}`);
|
|
286
|
+
return { success: false, exitCode: 1 };
|
|
287
|
+
}
|
|
288
|
+
const sp = output.createSpinner({ text: `Extracting ${sectionFilter}...`, spinner: 'dots' });
|
|
289
|
+
sp.start();
|
|
290
|
+
const r = await reader.extractSection(sectionFilter, dest);
|
|
291
|
+
sp.succeed(`${sectionFilter}: ${fmtSize(r.size)}`);
|
|
292
|
+
}
|
|
293
|
+
else {
|
|
294
|
+
const results = await reader.extractAll(dest);
|
|
295
|
+
for (const r of results) {
|
|
296
|
+
output.printSuccess(`${r.id.padEnd(14)} ${fmtSize(r.size).padStart(10)} -> ${r.path}`);
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
output.writeln();
|
|
300
|
+
output.printSuccess(`Extraction complete: ${dest}`);
|
|
301
|
+
output.writeln(output.dim(' Directory structure:'));
|
|
302
|
+
for (const d of ['kernel', 'runtime', 'ruflo', 'models', 'data', 'verify']) {
|
|
303
|
+
const exists = fs.existsSync(path.join(dest, d));
|
|
304
|
+
output.writeln(` ${exists ? output.success('+') : output.dim('-')} ${d}/`);
|
|
305
|
+
}
|
|
306
|
+
return { success: true };
|
|
307
|
+
}
|
|
308
|
+
catch (err) {
|
|
309
|
+
return fail('Extraction failed', errMsg(err));
|
|
310
|
+
}
|
|
311
|
+
},
|
|
312
|
+
};
|
|
313
|
+
// RUN
|
|
314
|
+
const runCommand = {
|
|
315
|
+
name: 'run',
|
|
316
|
+
description: 'Boot and run an RVFA appliance',
|
|
317
|
+
options: [
|
|
318
|
+
{ name: 'file', short: 'f', type: 'string', description: 'Path to .rvf file', required: true },
|
|
319
|
+
{ name: 'mode', type: 'string', description: 'Run mode: cli, mcp, verify', default: 'cli' },
|
|
320
|
+
{ name: 'isolation', type: 'string', description: 'Isolation: container, native', default: 'native' },
|
|
321
|
+
],
|
|
322
|
+
action: async (ctx) => {
|
|
323
|
+
const file = ctx.flags.file;
|
|
324
|
+
const mode = ctx.flags.mode || 'cli';
|
|
325
|
+
const isolation = ctx.flags.isolation || 'native';
|
|
326
|
+
if (!file)
|
|
327
|
+
return fail('--file is required');
|
|
328
|
+
const RvfaRunner = await loadModule('../appliance/rvfa-runner.js', 'RvfaRunner', 'runner');
|
|
329
|
+
if (!RvfaRunner)
|
|
330
|
+
return { success: false, exitCode: 1 };
|
|
331
|
+
if (!(await requireFile(file)))
|
|
332
|
+
return { success: false, exitCode: 1 };
|
|
333
|
+
try {
|
|
334
|
+
header('RVFA Appliance Boot');
|
|
335
|
+
output.printInfo(`File: ${file}`);
|
|
336
|
+
output.printInfo(`Mode: ${mode}`);
|
|
337
|
+
output.printInfo(`Isolation: ${isolation}`);
|
|
338
|
+
output.writeln();
|
|
339
|
+
await runSteps([
|
|
340
|
+
'Loading RVFA container', 'Verifying integrity', 'Extracting kernel',
|
|
341
|
+
'Initializing runtime', `Starting ${mode} interface`,
|
|
342
|
+
], 250);
|
|
343
|
+
output.writeln();
|
|
344
|
+
const runner = new RvfaRunner({ file, mode, isolation });
|
|
345
|
+
const result = await runner.boot();
|
|
346
|
+
if (mode === 'mcp' && result.port)
|
|
347
|
+
output.printSuccess(`MCP server listening on port ${result.port}`);
|
|
348
|
+
else if (mode === 'verify')
|
|
349
|
+
output.printSuccess('Verification complete');
|
|
350
|
+
else
|
|
351
|
+
output.printSuccess('Appliance is running');
|
|
352
|
+
if (result.pid)
|
|
353
|
+
output.printInfo(`PID: ${result.pid}`);
|
|
354
|
+
return { success: true, data: result };
|
|
355
|
+
}
|
|
356
|
+
catch (err) {
|
|
357
|
+
return fail('Boot failed', errMsg(err));
|
|
358
|
+
}
|
|
359
|
+
},
|
|
360
|
+
};
|
|
361
|
+
// Main command
|
|
362
|
+
export const applianceCommand = {
|
|
363
|
+
name: 'appliance',
|
|
364
|
+
description: 'Self-contained RVFA appliance management (build, inspect, verify, extract, run)',
|
|
365
|
+
aliases: ['rvfa'],
|
|
366
|
+
subcommands: [buildCommand, inspectCommand, verifyCommand, extractCommand, runCommand, signCommand, publishCommand, updateAppCommand],
|
|
367
|
+
examples: [
|
|
368
|
+
{ command: 'ruflo appliance build -p cloud', description: 'Build a cloud appliance' },
|
|
369
|
+
{ command: 'ruflo appliance inspect -f ruflo.rvf', description: 'Inspect appliance contents' },
|
|
370
|
+
{ command: 'ruflo appliance verify -f ruflo.rvf', description: 'Verify integrity' },
|
|
371
|
+
{ command: 'ruflo appliance extract -f ruflo.rvf', description: 'Extract sections' },
|
|
372
|
+
{ command: 'ruflo appliance run -f ruflo.rvf', description: 'Boot and run appliance' },
|
|
373
|
+
{ command: 'ruflo appliance sign -f ruflo.rvf --generate-keys', description: 'Generate keys and sign' },
|
|
374
|
+
{ command: 'ruflo appliance publish -f ruflo.rvf', description: 'Publish to IPFS via Pinata' },
|
|
375
|
+
{ command: 'ruflo appliance update -f ruflo.rvf -s ruflo -d ./new-ruflo.bin', description: 'Hot-patch a section' },
|
|
376
|
+
],
|
|
377
|
+
action: async () => {
|
|
378
|
+
output.writeln();
|
|
379
|
+
output.writeln(output.bold('Ruflo Appliance (RVFA)'));
|
|
380
|
+
output.writeln(output.dim('Self-contained deployment format for the full Ruflo platform.'));
|
|
381
|
+
output.writeln();
|
|
382
|
+
output.writeln('Subcommands:');
|
|
383
|
+
output.printList([
|
|
384
|
+
'build - Build a self-contained ruflo.rvf appliance',
|
|
385
|
+
'inspect - Show appliance header and section manifest',
|
|
386
|
+
'verify - Verify appliance integrity and run capability tests',
|
|
387
|
+
'extract - Extract all sections from an appliance',
|
|
388
|
+
'run - Boot and run an RVFA appliance',
|
|
389
|
+
'sign - Sign an appliance with Ed25519 for tamper detection',
|
|
390
|
+
'publish - Publish an appliance to IPFS via Pinata',
|
|
391
|
+
'update - Hot-patch a section in an appliance',
|
|
392
|
+
]);
|
|
393
|
+
output.writeln();
|
|
394
|
+
output.writeln('Profiles:');
|
|
395
|
+
output.printList([
|
|
396
|
+
`${output.bold('cloud')} - API-only, smallest footprint (~15 MB)`,
|
|
397
|
+
`${output.bold('hybrid')} - API + local fallback models (~500 MB)`,
|
|
398
|
+
`${output.bold('offline')} - Fully air-gapped with bundled models (~4 GB)`,
|
|
399
|
+
]);
|
|
400
|
+
output.writeln();
|
|
401
|
+
output.writeln(output.dim('Use "ruflo appliance <subcommand> --help" for details.'));
|
|
402
|
+
return { success: true };
|
|
403
|
+
},
|
|
404
|
+
};
|
|
405
|
+
export default applianceCommand;
|
|
406
|
+
//# sourceMappingURL=appliance.js.map
|
|
@@ -371,7 +371,7 @@ const allCommand = {
|
|
|
371
371
|
action: async (ctx) => {
|
|
372
372
|
output.writeln();
|
|
373
373
|
output.writeln(output.bold(output.highlight('═'.repeat(65))));
|
|
374
|
-
output.writeln(output.bold('
|
|
374
|
+
output.writeln(output.bold(' RuFlo V3 - Full Benchmark Suite'));
|
|
375
375
|
output.writeln(output.bold(output.highlight('═'.repeat(65))));
|
|
376
376
|
const startTime = Date.now();
|
|
377
377
|
const allResults = {};
|
|
@@ -439,7 +439,7 @@ export const benchmarkCommand = {
|
|
|
439
439
|
],
|
|
440
440
|
action: async (_ctx) => {
|
|
441
441
|
output.writeln();
|
|
442
|
-
output.writeln(output.bold('
|
|
442
|
+
output.writeln(output.bold('RuFlo V3 Benchmark Suite'));
|
|
443
443
|
output.writeln(output.dim('─'.repeat(50)));
|
|
444
444
|
output.writeln();
|
|
445
445
|
output.writeln('Available subcommands:');
|
|
@@ -344,7 +344,7 @@ export const claimsCommand = {
|
|
|
344
344
|
],
|
|
345
345
|
action: async () => {
|
|
346
346
|
output.writeln();
|
|
347
|
-
output.writeln(output.bold('
|
|
347
|
+
output.writeln(output.bold('RuFlo Claims System'));
|
|
348
348
|
output.writeln(output.dim('Fine-grained authorization and access control'));
|
|
349
349
|
output.writeln();
|
|
350
350
|
output.writeln('Subcommands:');
|
|
@@ -33,7 +33,7 @@ const initCommand = {
|
|
|
33
33
|
const sparc = ctx.flags.sparc;
|
|
34
34
|
const v3 = ctx.flags.v3;
|
|
35
35
|
output.writeln();
|
|
36
|
-
output.printInfo('Initializing
|
|
36
|
+
output.printInfo('Initializing RuFlo configuration...');
|
|
37
37
|
output.writeln();
|
|
38
38
|
// Create default configuration
|
|
39
39
|
const config = {
|
|
@@ -260,7 +260,7 @@ export const deploymentCommand = {
|
|
|
260
260
|
],
|
|
261
261
|
action: async () => {
|
|
262
262
|
output.writeln();
|
|
263
|
-
output.writeln(output.bold('
|
|
263
|
+
output.writeln(output.bold('RuFlo Deployment'));
|
|
264
264
|
output.writeln(output.dim('Multi-environment deployment management'));
|
|
265
265
|
output.writeln();
|
|
266
266
|
output.writeln('Subcommands:');
|
|
@@ -5,8 +5,9 @@
|
|
|
5
5
|
* Created with ruv.io
|
|
6
6
|
*/
|
|
7
7
|
import { output } from '../output.js';
|
|
8
|
-
import { existsSync, readFileSync,
|
|
9
|
-
import { join } from 'path';
|
|
8
|
+
import { existsSync, readFileSync, statSync } from 'fs';
|
|
9
|
+
import { join, dirname } from 'path';
|
|
10
|
+
import { fileURLToPath } from 'url';
|
|
10
11
|
import { execSync, exec } from 'child_process';
|
|
11
12
|
import { promisify } from 'util';
|
|
12
13
|
// Promisified exec with proper shell and env inheritance for cross-platform support
|
|
@@ -224,53 +225,35 @@ async function checkBuildTools() {
|
|
|
224
225
|
async function checkVersionFreshness() {
|
|
225
226
|
try {
|
|
226
227
|
// Get current CLI version from package.json
|
|
228
|
+
// Use import.meta.url to reliably locate our own package.json,
|
|
229
|
+
// regardless of how deep the compiled file sits (e.g. dist/src/commands/).
|
|
227
230
|
let currentVersion = '0.0.0';
|
|
228
231
|
try {
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
//
|
|
234
|
-
|
|
235
|
-
join(
|
|
236
|
-
join(moduleDir, '..', '..', 'package.json'), // ../../package.json from dist/commands
|
|
237
|
-
join(process.cwd(), 'package.json'), // Current working directory
|
|
238
|
-
join(process.cwd(), 'node_modules/@claude-flow/cli/package.json')
|
|
239
|
-
];
|
|
240
|
-
// Also search in npx cache directories (Linux/macOS)
|
|
241
|
-
const homeDir = process.env.HOME || process.env.USERPROFILE || '';
|
|
242
|
-
if (homeDir) {
|
|
243
|
-
// Check common npx cache locations
|
|
232
|
+
const thisFile = fileURLToPath(import.meta.url);
|
|
233
|
+
let dir = dirname(thisFile);
|
|
234
|
+
// Walk up from the current file's directory until we find the
|
|
235
|
+
// package.json that belongs to @claude-flow/cli (or claude-flow/cli).
|
|
236
|
+
// Walk until dirname(dir) === dir (filesystem root on any platform).
|
|
237
|
+
for (;;) {
|
|
238
|
+
const candidate = join(dir, 'package.json');
|
|
244
239
|
try {
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
possiblePaths.push(npxPkgPath);
|
|
252
|
-
}
|
|
253
|
-
}
|
|
254
|
-
}
|
|
255
|
-
}
|
|
256
|
-
catch {
|
|
257
|
-
// Ignore errors scanning npx cache
|
|
258
|
-
}
|
|
259
|
-
}
|
|
260
|
-
for (const pkgPath of possiblePaths) {
|
|
261
|
-
try {
|
|
262
|
-
if (existsSync(pkgPath)) {
|
|
263
|
-
const packageJson = JSON.parse(readFileSync(pkgPath, 'utf8'));
|
|
264
|
-
// Make sure it's the right package
|
|
265
|
-
if (packageJson.name === '@claude-flow/cli' && packageJson.version) {
|
|
266
|
-
currentVersion = packageJson.version;
|
|
240
|
+
if (existsSync(candidate)) {
|
|
241
|
+
const pkg = JSON.parse(readFileSync(candidate, 'utf8'));
|
|
242
|
+
if (pkg.version &&
|
|
243
|
+
typeof pkg.name === 'string' &&
|
|
244
|
+
(pkg.name === '@claude-flow/cli' || pkg.name === 'claude-flow' || pkg.name === 'ruflo')) {
|
|
245
|
+
currentVersion = pkg.version;
|
|
267
246
|
break;
|
|
268
247
|
}
|
|
269
248
|
}
|
|
270
249
|
}
|
|
271
250
|
catch {
|
|
272
|
-
|
|
251
|
+
// Unreadable/invalid JSON -- skip and keep walking up
|
|
273
252
|
}
|
|
253
|
+
const parent = dirname(dir);
|
|
254
|
+
if (parent === dir)
|
|
255
|
+
break; // reached root
|
|
256
|
+
dir = parent;
|
|
274
257
|
}
|
|
275
258
|
}
|
|
276
259
|
catch {
|
|
@@ -284,7 +267,7 @@ async function checkVersionFreshness() {
|
|
|
284
267
|
// Query npm for latest version (using alpha tag since that's what we publish to)
|
|
285
268
|
let latestVersion = currentVersion;
|
|
286
269
|
try {
|
|
287
|
-
const npmInfo = await runCommand('npm view @claude-flow/cli@alpha version
|
|
270
|
+
const npmInfo = await runCommand('npm view @claude-flow/cli@alpha version', 5000);
|
|
288
271
|
latestVersion = npmInfo.trim();
|
|
289
272
|
}
|
|
290
273
|
catch {
|
|
@@ -471,7 +454,7 @@ export const doctorCommand = {
|
|
|
471
454
|
const component = ctx.flags.component;
|
|
472
455
|
const verbose = ctx.flags.verbose;
|
|
473
456
|
output.writeln();
|
|
474
|
-
output.writeln(output.bold('
|
|
457
|
+
output.writeln(output.bold('RuFlo Doctor'));
|
|
475
458
|
output.writeln(output.dim('System diagnostics and health check'));
|
|
476
459
|
output.writeln(output.dim('─'.repeat(50)));
|
|
477
460
|
output.writeln();
|
|
@@ -1536,7 +1536,7 @@ export const embeddingsCommand = {
|
|
|
1536
1536
|
],
|
|
1537
1537
|
action: async () => {
|
|
1538
1538
|
output.writeln();
|
|
1539
|
-
output.writeln(output.bold('
|
|
1539
|
+
output.writeln(output.bold('RuFlo Embeddings'));
|
|
1540
1540
|
output.writeln(output.dim('Vector embeddings and semantic search'));
|
|
1541
1541
|
output.writeln();
|
|
1542
1542
|
output.writeln('Core Commands:');
|
|
@@ -2869,7 +2869,7 @@ const statuslineCommand = {
|
|
|
2869
2869
|
return '[' + '●'.repeat(filled) + '○'.repeat(empty) + ']';
|
|
2870
2870
|
};
|
|
2871
2871
|
// Generate lines
|
|
2872
|
-
let header = `${c.bold}${c.brightPurple}▊
|
|
2872
|
+
let header = `${c.bold}${c.brightPurple}▊ RuFlo V3 ${c.reset}`;
|
|
2873
2873
|
header += `${swarm.coordinationActive ? c.brightCyan : c.dim}● ${c.brightCyan}${user.name}${c.reset}`;
|
|
2874
2874
|
if (user.gitBranch) {
|
|
2875
2875
|
header += ` ${c.dim}│${c.reset} ${c.brightBlue}⎇ ${user.gitBranch}${c.reset}`;
|
|
@@ -25,6 +25,7 @@ export { securityCommand } from './security.js';
|
|
|
25
25
|
export { ruvectorCommand } from './ruvector/index.js';
|
|
26
26
|
export { hiveMindCommand } from './hive-mind.js';
|
|
27
27
|
export { guidanceCommand } from './guidance.js';
|
|
28
|
+
export { applianceCommand } from './appliance.js';
|
|
28
29
|
export declare function getConfigCommand(): Promise<Command | undefined>;
|
|
29
30
|
export declare function getMigrateCommand(): Promise<Command | undefined>;
|
|
30
31
|
export declare function getWorkflowCommand(): Promise<Command | undefined>;
|
|
@@ -47,6 +48,7 @@ export declare function getProgressCommand(): Promise<Command | undefined>;
|
|
|
47
48
|
export declare function getIssuesCommand(): Promise<Command | undefined>;
|
|
48
49
|
export declare function getRuvectorCommand(): Promise<Command | undefined>;
|
|
49
50
|
export declare function getGuidanceCommand(): Promise<Command | undefined>;
|
|
51
|
+
export declare function getApplianceCommand(): Promise<Command | undefined>;
|
|
50
52
|
/**
|
|
51
53
|
* Core commands loaded synchronously (available immediately)
|
|
52
54
|
* Advanced commands loaded on-demand for faster startup
|
|
@@ -56,6 +56,8 @@ const commandLoaders = {
|
|
|
56
56
|
benchmark: () => import('./benchmark.js'),
|
|
57
57
|
// Guidance Control Plane
|
|
58
58
|
guidance: () => import('./guidance.js'),
|
|
59
|
+
// RVFA Appliance Management
|
|
60
|
+
appliance: () => import('./appliance.js'),
|
|
59
61
|
};
|
|
60
62
|
// Cache for loaded commands
|
|
61
63
|
const loadedCommands = new Map();
|
|
@@ -124,6 +126,7 @@ import { issuesCommand } from './issues.js';
|
|
|
124
126
|
import updateCommand from './update.js';
|
|
125
127
|
import { processCommand } from './process.js';
|
|
126
128
|
import { guidanceCommand } from './guidance.js';
|
|
129
|
+
import { applianceCommand } from './appliance.js';
|
|
127
130
|
// Pre-populate cache with core commands
|
|
128
131
|
loadedCommands.set('init', initCommand);
|
|
129
132
|
loadedCommands.set('start', startCommand);
|
|
@@ -167,6 +170,7 @@ export { securityCommand } from './security.js';
|
|
|
167
170
|
export { ruvectorCommand } from './ruvector/index.js';
|
|
168
171
|
export { hiveMindCommand } from './hive-mind.js';
|
|
169
172
|
export { guidanceCommand } from './guidance.js';
|
|
173
|
+
export { applianceCommand } from './appliance.js';
|
|
170
174
|
// Lazy-loaded command re-exports (for backwards compatibility, but async-only)
|
|
171
175
|
export async function getConfigCommand() { return loadCommand('config'); }
|
|
172
176
|
export async function getMigrateCommand() { return loadCommand('migrate'); }
|
|
@@ -190,6 +194,7 @@ export async function getProgressCommand() { return loadCommand('progress'); }
|
|
|
190
194
|
export async function getIssuesCommand() { return loadCommand('issues'); }
|
|
191
195
|
export async function getRuvectorCommand() { return loadCommand('ruvector'); }
|
|
192
196
|
export async function getGuidanceCommand() { return loadCommand('guidance'); }
|
|
197
|
+
export async function getApplianceCommand() { return loadCommand('appliance'); }
|
|
193
198
|
/**
|
|
194
199
|
* Core commands loaded synchronously (available immediately)
|
|
195
200
|
* Advanced commands loaded on-demand for faster startup
|
|
@@ -262,6 +267,7 @@ export const commandsByCategory = {
|
|
|
262
267
|
issuesCommand,
|
|
263
268
|
updateCommand,
|
|
264
269
|
processCommand,
|
|
270
|
+
applianceCommand,
|
|
265
271
|
],
|
|
266
272
|
};
|
|
267
273
|
/**
|