moflo 4.10.1 → 4.10.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/skills/healer/SKILL.md +3 -1
- package/bin/session-start-launcher.mjs +112 -5
- package/dist/src/cli/commands/doctor-checks-config.js +4 -4
- package/dist/src/cli/commands/doctor-checks-memory-access.js +27 -1
- package/dist/src/cli/commands/doctor-embedding-hygiene.js +48 -12
- package/dist/src/cli/commands/doctor-render.js +118 -74
- package/dist/src/cli/commands/doctor-version.js +1 -1
- package/dist/src/cli/commands/doctor.js +70 -25
- package/dist/src/cli/commands/index.js +0 -6
- package/dist/src/cli/init/executor.js +2 -2
- package/dist/src/cli/mcp-tools/swarm-tools.js +3 -4
- package/dist/src/cli/memory/bridge-core.js +36 -0
- package/dist/src/cli/services/moflo-paths.js +6 -5
- package/dist/src/cli/services/moflo-require.js +2 -2
- package/dist/src/cli/shared/core/config/loader.js +2 -2
- package/dist/src/cli/version.js +1 -1
- package/package.json +2 -2
- package/dist/src/cli/appliance/gguf-engine.js +0 -425
- package/dist/src/cli/appliance/ruvllm-bridge.js +0 -231
- package/dist/src/cli/appliance/rvfa-builder.js +0 -325
- package/dist/src/cli/appliance/rvfa-distribution.js +0 -370
- package/dist/src/cli/appliance/rvfa-format.js +0 -393
- package/dist/src/cli/appliance/rvfa-runner.js +0 -238
- package/dist/src/cli/appliance/rvfa-signing.js +0 -351
- package/dist/src/cli/commands/appliance-advanced.js +0 -213
- package/dist/src/cli/commands/appliance.js +0 -404
|
@@ -1,404 +0,0 @@
|
|
|
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
|
-
import { errorDetail } from '../shared/utils/error-detail.js';
|
|
8
|
-
function fmtSize(bytes) {
|
|
9
|
-
if (bytes < 1024)
|
|
10
|
-
return `${bytes} B`;
|
|
11
|
-
if (bytes < 1024 * 1024)
|
|
12
|
-
return `${(bytes / 1024).toFixed(1)} KB`;
|
|
13
|
-
if (bytes < 1024 * 1024 * 1024)
|
|
14
|
-
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
15
|
-
return `${(bytes / (1024 * 1024 * 1024)).toFixed(2)} GB`;
|
|
16
|
-
}
|
|
17
|
-
const fail = (msg, detail) => {
|
|
18
|
-
output.printError(msg, detail);
|
|
19
|
-
return { success: false, exitCode: 1 };
|
|
20
|
-
};
|
|
21
|
-
async function loadModule(path, exportName, label) {
|
|
22
|
-
try {
|
|
23
|
-
const mod = await import(path);
|
|
24
|
-
return mod[exportName];
|
|
25
|
-
}
|
|
26
|
-
catch {
|
|
27
|
-
output.printError(`RVFA ${label} module not found`, 'Rebuild with: npm run build (RVFA ships inline with moflo)');
|
|
28
|
-
return null;
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
async function requireFile(file) {
|
|
32
|
-
const fs = await import('fs');
|
|
33
|
-
if (!fs.existsSync(file)) {
|
|
34
|
-
output.printError(`File not found: ${file}`);
|
|
35
|
-
return false;
|
|
36
|
-
}
|
|
37
|
-
return true;
|
|
38
|
-
}
|
|
39
|
-
function header(title) {
|
|
40
|
-
output.writeln();
|
|
41
|
-
output.writeln(output.bold(title));
|
|
42
|
-
output.writeln(output.dim('─'.repeat(50)));
|
|
43
|
-
output.writeln();
|
|
44
|
-
}
|
|
45
|
-
async function runSteps(steps, delay = 300) {
|
|
46
|
-
for (const step of steps) {
|
|
47
|
-
const spinner = output.createSpinner({ text: step + '...', spinner: 'dots' });
|
|
48
|
-
spinner.start();
|
|
49
|
-
await new Promise(r => setTimeout(r, delay));
|
|
50
|
-
spinner.succeed(step);
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
// BUILD
|
|
54
|
-
const buildCommand = {
|
|
55
|
-
name: 'build',
|
|
56
|
-
description: 'Build a self-contained ruflo.rvf appliance',
|
|
57
|
-
options: [
|
|
58
|
-
{ name: 'profile', short: 'p', type: 'string', description: 'Build profile: cloud, hybrid, offline', default: 'cloud' },
|
|
59
|
-
{ name: 'output', short: 'o', type: 'string', description: 'Output file path', default: 'ruflo.rvf' },
|
|
60
|
-
{ name: 'arch', type: 'string', description: 'Target architecture', default: 'x86_64' },
|
|
61
|
-
{ name: 'models', short: 'm', type: 'array', description: 'Models to include (offline/hybrid)' },
|
|
62
|
-
{ name: 'api-keys', type: 'string', description: 'Path to .env file for API key vault' },
|
|
63
|
-
{ name: 'verbose', short: 'v', type: 'boolean', description: 'Verbose output' },
|
|
64
|
-
],
|
|
65
|
-
action: async (ctx) => {
|
|
66
|
-
const profile = ctx.flags.profile || 'cloud';
|
|
67
|
-
const outputPath = ctx.flags.output || 'ruflo.rvf';
|
|
68
|
-
const arch = ctx.flags.arch || 'x86_64';
|
|
69
|
-
const models = ctx.flags.models || [];
|
|
70
|
-
const apiKeysPath = ctx.flags.apiKeys;
|
|
71
|
-
header('RVFA Appliance Builder');
|
|
72
|
-
output.printInfo(`Profile: ${output.highlight(profile)}`);
|
|
73
|
-
output.printInfo(`Arch: ${arch}`);
|
|
74
|
-
output.printInfo(`Output: ${outputPath}`);
|
|
75
|
-
if (models.length > 0)
|
|
76
|
-
output.printInfo(`Models: ${models.join(', ')}`);
|
|
77
|
-
output.writeln();
|
|
78
|
-
const startTime = Date.now();
|
|
79
|
-
const RvfaBuilder = await loadModule('../appliance/rvfa-builder.js', 'RvfaBuilder', 'builder');
|
|
80
|
-
if (!RvfaBuilder)
|
|
81
|
-
return { success: false, exitCode: 1 };
|
|
82
|
-
const steps = [
|
|
83
|
-
'Collecting kernel artifacts', 'Bundling runtime environment',
|
|
84
|
-
'Packaging flo CLI + MCP tools', 'Compressing sections',
|
|
85
|
-
'Computing SHA-256 checksums', 'Writing RVFA container',
|
|
86
|
-
];
|
|
87
|
-
if (profile !== 'cloud' && models.length > 0)
|
|
88
|
-
steps.splice(3, 0, 'Embedding model weights');
|
|
89
|
-
if (apiKeysPath)
|
|
90
|
-
steps.splice(steps.length - 1, 0, 'Sealing API key vault');
|
|
91
|
-
try {
|
|
92
|
-
const builder = new RvfaBuilder({ profile, outputPath, arch, models, apiKeysPath });
|
|
93
|
-
await runSteps(steps);
|
|
94
|
-
const result = await builder.build();
|
|
95
|
-
const duration = ((Date.now() - startTime) / 1000).toFixed(1);
|
|
96
|
-
if (result.sections?.length) {
|
|
97
|
-
output.writeln();
|
|
98
|
-
output.printTable({
|
|
99
|
-
columns: [
|
|
100
|
-
{ key: 'id', header: 'Section', width: 16 },
|
|
101
|
-
{ key: 'size', header: 'Size', width: 12, align: 'right' },
|
|
102
|
-
],
|
|
103
|
-
data: result.sections.map(s => ({ id: s.id, size: fmtSize(s.size) })),
|
|
104
|
-
});
|
|
105
|
-
}
|
|
106
|
-
output.writeln();
|
|
107
|
-
output.printSuccess(`Appliance written to ${output.bold(outputPath)}`);
|
|
108
|
-
output.printInfo(`Total size: ${output.bold(fmtSize(result.totalSize))} Duration: ${duration}s`);
|
|
109
|
-
return { success: true, data: result };
|
|
110
|
-
}
|
|
111
|
-
catch (err) {
|
|
112
|
-
return fail('Build failed', errorDetail(err));
|
|
113
|
-
}
|
|
114
|
-
},
|
|
115
|
-
};
|
|
116
|
-
// INSPECT
|
|
117
|
-
const inspectCommand = {
|
|
118
|
-
name: 'inspect',
|
|
119
|
-
description: 'Show RVFA appliance header and section manifest',
|
|
120
|
-
options: [
|
|
121
|
-
{ name: 'file', short: 'f', type: 'string', description: 'Path to .rvf file', required: true },
|
|
122
|
-
{ name: 'json', type: 'boolean', description: 'Output as JSON' },
|
|
123
|
-
],
|
|
124
|
-
action: async (ctx) => {
|
|
125
|
-
const file = ctx.flags.file;
|
|
126
|
-
if (!file)
|
|
127
|
-
return fail('--file is required');
|
|
128
|
-
const RvfaReader = await loadModule('../appliance/rvfa-format.js', 'RvfaReader', 'format');
|
|
129
|
-
if (!RvfaReader)
|
|
130
|
-
return { success: false, exitCode: 1 };
|
|
131
|
-
if (!(await requireFile(file)))
|
|
132
|
-
return { success: false, exitCode: 1 };
|
|
133
|
-
try {
|
|
134
|
-
const reader = new RvfaReader(file);
|
|
135
|
-
const hdr = await reader.parse();
|
|
136
|
-
if (ctx.flags.json) {
|
|
137
|
-
output.printJson(hdr);
|
|
138
|
-
return { success: true, data: hdr };
|
|
139
|
-
}
|
|
140
|
-
header('RVFA Appliance');
|
|
141
|
-
for (const [label, value] of [
|
|
142
|
-
['Name', hdr.name || 'ruflo'], ['Version', hdr.version || 'unknown'],
|
|
143
|
-
['Architecture', hdr.arch || 'x86_64'], ['Profile', hdr.profile || 'cloud'],
|
|
144
|
-
['Created', hdr.created || 'unknown'],
|
|
145
|
-
]) {
|
|
146
|
-
output.writeln(` ${output.bold(label.padEnd(16))}${value}`);
|
|
147
|
-
}
|
|
148
|
-
output.writeln();
|
|
149
|
-
output.writeln(output.bold('Sections'));
|
|
150
|
-
output.writeln(output.dim('─'.repeat(60)));
|
|
151
|
-
if (hdr.sections?.length) {
|
|
152
|
-
output.printTable({
|
|
153
|
-
columns: [
|
|
154
|
-
{ key: 'id', header: 'Section', width: 14 },
|
|
155
|
-
{ key: 'size', header: 'Packed', width: 12, align: 'right' },
|
|
156
|
-
{ key: 'original', header: 'Original', width: 12, align: 'right' },
|
|
157
|
-
{ key: 'compression', header: 'Compression', width: 12 },
|
|
158
|
-
{ key: 'sha256', header: 'SHA-256', width: 18 },
|
|
159
|
-
],
|
|
160
|
-
data: hdr.sections.map((s) => ({
|
|
161
|
-
id: s.id,
|
|
162
|
-
size: fmtSize(s.size),
|
|
163
|
-
original: fmtSize(s.originalSize ?? s.size),
|
|
164
|
-
compression: s.compression || 'none',
|
|
165
|
-
sha256: s.sha256 ? s.sha256.slice(0, 16) + '..' : output.dim('n/a'),
|
|
166
|
-
})),
|
|
167
|
-
});
|
|
168
|
-
}
|
|
169
|
-
else {
|
|
170
|
-
output.writeln(output.dim(' No sections found'));
|
|
171
|
-
}
|
|
172
|
-
const fs = await import('fs');
|
|
173
|
-
const stat = fs.statSync(file);
|
|
174
|
-
output.writeln();
|
|
175
|
-
output.printInfo(`Total file size: ${output.bold(fmtSize(stat.size))}`);
|
|
176
|
-
if (hdr.footerHash) {
|
|
177
|
-
output.printInfo(`Footer hash: ${output.dim(hdr.footerHash.slice(0, 32) + '..')}`);
|
|
178
|
-
}
|
|
179
|
-
return { success: true, data: hdr };
|
|
180
|
-
}
|
|
181
|
-
catch (err) {
|
|
182
|
-
return fail('Failed to inspect appliance', errorDetail(err));
|
|
183
|
-
}
|
|
184
|
-
},
|
|
185
|
-
};
|
|
186
|
-
// VERIFY
|
|
187
|
-
const verifyCommand = {
|
|
188
|
-
name: 'verify',
|
|
189
|
-
description: 'Verify appliance integrity and run capability tests',
|
|
190
|
-
options: [
|
|
191
|
-
{ name: 'file', short: 'f', type: 'string', description: 'Path to .rvf file', required: true },
|
|
192
|
-
{ name: 'quick', short: 'q', type: 'boolean', description: 'Quick check (integrity only, skip capability tests)' },
|
|
193
|
-
],
|
|
194
|
-
action: async (ctx) => {
|
|
195
|
-
const file = ctx.flags.file;
|
|
196
|
-
const quick = ctx.flags.quick;
|
|
197
|
-
if (!file)
|
|
198
|
-
return fail('--file is required');
|
|
199
|
-
const RvfaReader = await loadModule('../appliance/rvfa-format.js', 'RvfaReader', 'format');
|
|
200
|
-
if (!RvfaReader)
|
|
201
|
-
return { success: false, exitCode: 1 };
|
|
202
|
-
if (!(await requireFile(file)))
|
|
203
|
-
return { success: false, exitCode: 1 };
|
|
204
|
-
try {
|
|
205
|
-
header('RVFA Verification');
|
|
206
|
-
const reader = new RvfaReader(file);
|
|
207
|
-
const hdr = await reader.parse();
|
|
208
|
-
// Section checksums
|
|
209
|
-
const s1 = output.createSpinner({ text: 'Verifying section checksums...', spinner: 'dots' });
|
|
210
|
-
s1.start();
|
|
211
|
-
const checksums = await reader.verifyChecksums();
|
|
212
|
-
const allValid = checksums.every(r => r.valid);
|
|
213
|
-
if (allValid) {
|
|
214
|
-
s1.succeed(`Section checksums: ${output.success('PASS')} (${checksums.length} sections)`);
|
|
215
|
-
}
|
|
216
|
-
else {
|
|
217
|
-
const bad = checksums.filter(r => !r.valid);
|
|
218
|
-
s1.fail(`Section checksums: ${output.error('FAIL')} (${bad.length} corrupted)`);
|
|
219
|
-
bad.forEach(f => output.writeln(` ${output.error('X')} ${f.section}`));
|
|
220
|
-
}
|
|
221
|
-
// Footer hash
|
|
222
|
-
const s2 = output.createSpinner({ text: 'Verifying footer hash...', spinner: 'dots' });
|
|
223
|
-
s2.start();
|
|
224
|
-
const footerOk = await reader.verifyFooter();
|
|
225
|
-
footerOk ? s2.succeed(`Footer hash: ${output.success('PASS')}`)
|
|
226
|
-
: s2.fail(`Footer hash: ${output.error('FAIL')}`);
|
|
227
|
-
// Capability tests
|
|
228
|
-
let capOk = true;
|
|
229
|
-
if (!quick && hdr.sections?.find((s) => s.id === 'verify')) {
|
|
230
|
-
const s3 = output.createSpinner({ text: 'Running capability tests...', spinner: 'dots' });
|
|
231
|
-
s3.start();
|
|
232
|
-
await new Promise(r => setTimeout(r, 500));
|
|
233
|
-
s3.succeed(`Capability tests: ${output.success('PASS')}`);
|
|
234
|
-
}
|
|
235
|
-
else if (quick) {
|
|
236
|
-
output.writeln(output.dim(' Skipped capability tests (--quick)'));
|
|
237
|
-
}
|
|
238
|
-
output.writeln();
|
|
239
|
-
const pass = allValid && footerOk && capOk;
|
|
240
|
-
pass ? output.printSuccess('Appliance verification passed')
|
|
241
|
-
: output.printError('Appliance verification failed');
|
|
242
|
-
return { success: pass, exitCode: pass ? 0 : 1 };
|
|
243
|
-
}
|
|
244
|
-
catch (err) {
|
|
245
|
-
return fail('Verification failed', errorDetail(err));
|
|
246
|
-
}
|
|
247
|
-
},
|
|
248
|
-
};
|
|
249
|
-
// EXTRACT
|
|
250
|
-
const extractCommand = {
|
|
251
|
-
name: 'extract',
|
|
252
|
-
description: 'Extract all sections from an RVFA appliance',
|
|
253
|
-
options: [
|
|
254
|
-
{ name: 'file', short: 'f', type: 'string', description: 'Path to .rvf file', required: true },
|
|
255
|
-
{ name: 'output', short: 'o', type: 'string', description: 'Output directory', default: './rvfa-extracted' },
|
|
256
|
-
{ name: 'section', short: 's', type: 'string', description: 'Extract specific section only' },
|
|
257
|
-
],
|
|
258
|
-
action: async (ctx) => {
|
|
259
|
-
const file = ctx.flags.file;
|
|
260
|
-
const outputDir = ctx.flags.output || './rvfa-extracted';
|
|
261
|
-
const sectionFilter = ctx.flags.section;
|
|
262
|
-
if (!file)
|
|
263
|
-
return fail('--file is required');
|
|
264
|
-
const RvfaReader = await loadModule('../appliance/rvfa-format.js', 'RvfaReader', 'format');
|
|
265
|
-
if (!RvfaReader)
|
|
266
|
-
return { success: false, exitCode: 1 };
|
|
267
|
-
if (!(await requireFile(file)))
|
|
268
|
-
return { success: false, exitCode: 1 };
|
|
269
|
-
try {
|
|
270
|
-
const fs = await import('fs');
|
|
271
|
-
const path = await import('path');
|
|
272
|
-
header('RVFA Extraction');
|
|
273
|
-
const reader = new RvfaReader(file);
|
|
274
|
-
const hdr = await reader.parse();
|
|
275
|
-
const dest = path.resolve(outputDir);
|
|
276
|
-
if (!fs.existsSync(dest))
|
|
277
|
-
fs.mkdirSync(dest, { recursive: true });
|
|
278
|
-
output.printInfo(`Destination: ${dest}`);
|
|
279
|
-
output.writeln();
|
|
280
|
-
if (sectionFilter) {
|
|
281
|
-
if (!hdr.sections?.find((s) => s.id === sectionFilter)) {
|
|
282
|
-
output.printError(`Section not found: ${sectionFilter}`);
|
|
283
|
-
output.printInfo(`Available: ${(hdr.sections || []).map((s) => s.id).join(', ')}`);
|
|
284
|
-
return { success: false, exitCode: 1 };
|
|
285
|
-
}
|
|
286
|
-
const sp = output.createSpinner({ text: `Extracting ${sectionFilter}...`, spinner: 'dots' });
|
|
287
|
-
sp.start();
|
|
288
|
-
const r = await reader.extractSection(sectionFilter, dest);
|
|
289
|
-
sp.succeed(`${sectionFilter}: ${fmtSize(r.size)}`);
|
|
290
|
-
}
|
|
291
|
-
else {
|
|
292
|
-
const results = await reader.extractAll(dest);
|
|
293
|
-
for (const r of results) {
|
|
294
|
-
output.printSuccess(`${r.id.padEnd(14)} ${fmtSize(r.size).padStart(10)} -> ${r.path}`);
|
|
295
|
-
}
|
|
296
|
-
}
|
|
297
|
-
output.writeln();
|
|
298
|
-
output.printSuccess(`Extraction complete: ${dest}`);
|
|
299
|
-
output.writeln(output.dim(' Directory structure:'));
|
|
300
|
-
for (const d of ['kernel', 'runtime', 'ruflo', 'models', 'data', 'verify']) {
|
|
301
|
-
const exists = fs.existsSync(path.join(dest, d));
|
|
302
|
-
output.writeln(` ${exists ? output.success('+') : output.dim('-')} ${d}/`);
|
|
303
|
-
}
|
|
304
|
-
return { success: true };
|
|
305
|
-
}
|
|
306
|
-
catch (err) {
|
|
307
|
-
return fail('Extraction failed', errorDetail(err));
|
|
308
|
-
}
|
|
309
|
-
},
|
|
310
|
-
};
|
|
311
|
-
// RUN
|
|
312
|
-
const runCommand = {
|
|
313
|
-
name: 'run',
|
|
314
|
-
description: 'Boot and run an RVFA appliance',
|
|
315
|
-
options: [
|
|
316
|
-
{ name: 'file', short: 'f', type: 'string', description: 'Path to .rvf file', required: true },
|
|
317
|
-
{ name: 'mode', type: 'string', description: 'Run mode: cli, mcp, verify', default: 'cli' },
|
|
318
|
-
{ name: 'isolation', type: 'string', description: 'Isolation: container, native', default: 'native' },
|
|
319
|
-
],
|
|
320
|
-
action: async (ctx) => {
|
|
321
|
-
const file = ctx.flags.file;
|
|
322
|
-
const mode = ctx.flags.mode || 'cli';
|
|
323
|
-
const isolation = ctx.flags.isolation || 'native';
|
|
324
|
-
if (!file)
|
|
325
|
-
return fail('--file is required');
|
|
326
|
-
const RvfaRunner = await loadModule('../appliance/rvfa-runner.js', 'RvfaRunner', 'runner');
|
|
327
|
-
if (!RvfaRunner)
|
|
328
|
-
return { success: false, exitCode: 1 };
|
|
329
|
-
if (!(await requireFile(file)))
|
|
330
|
-
return { success: false, exitCode: 1 };
|
|
331
|
-
try {
|
|
332
|
-
header('RVFA Appliance Boot');
|
|
333
|
-
output.printInfo(`File: ${file}`);
|
|
334
|
-
output.printInfo(`Mode: ${mode}`);
|
|
335
|
-
output.printInfo(`Isolation: ${isolation}`);
|
|
336
|
-
output.writeln();
|
|
337
|
-
await runSteps([
|
|
338
|
-
'Loading RVFA container', 'Verifying integrity', 'Extracting kernel',
|
|
339
|
-
'Initializing runtime', `Starting ${mode} interface`,
|
|
340
|
-
], 250);
|
|
341
|
-
output.writeln();
|
|
342
|
-
const runner = new RvfaRunner({ file, mode, isolation });
|
|
343
|
-
const result = await runner.boot();
|
|
344
|
-
if (mode === 'mcp' && result.port)
|
|
345
|
-
output.printSuccess(`MCP server listening on port ${result.port}`);
|
|
346
|
-
else if (mode === 'verify')
|
|
347
|
-
output.printSuccess('Verification complete');
|
|
348
|
-
else
|
|
349
|
-
output.printSuccess('Appliance is running');
|
|
350
|
-
if (result.pid)
|
|
351
|
-
output.printInfo(`PID: ${result.pid}`);
|
|
352
|
-
return { success: true, data: result };
|
|
353
|
-
}
|
|
354
|
-
catch (err) {
|
|
355
|
-
return fail('Boot failed', errorDetail(err));
|
|
356
|
-
}
|
|
357
|
-
},
|
|
358
|
-
};
|
|
359
|
-
// Main command
|
|
360
|
-
export const applianceCommand = {
|
|
361
|
-
name: 'appliance',
|
|
362
|
-
description: 'Self-contained RVFA appliance management (build, inspect, verify, extract, run)',
|
|
363
|
-
aliases: ['rvfa'],
|
|
364
|
-
subcommands: [buildCommand, inspectCommand, verifyCommand, extractCommand, runCommand, signCommand, publishCommand, updateAppCommand],
|
|
365
|
-
examples: [
|
|
366
|
-
{ command: 'flo appliance build -p cloud', description: 'Build a cloud appliance' },
|
|
367
|
-
{ command: 'flo appliance inspect -f ruflo.rvf', description: 'Inspect appliance contents' },
|
|
368
|
-
{ command: 'flo appliance verify -f ruflo.rvf', description: 'Verify integrity' },
|
|
369
|
-
{ command: 'flo appliance extract -f ruflo.rvf', description: 'Extract sections' },
|
|
370
|
-
{ command: 'flo appliance run -f ruflo.rvf', description: 'Boot and run appliance' },
|
|
371
|
-
{ command: 'flo appliance sign -f ruflo.rvf --generate-keys', description: 'Generate keys and sign' },
|
|
372
|
-
{ command: 'flo appliance publish -f ruflo.rvf', description: 'Publish to IPFS via Pinata' },
|
|
373
|
-
{ command: 'flo appliance update -f ruflo.rvf -s ruflo -d ./new-ruflo.bin', description: 'Hot-patch a section' },
|
|
374
|
-
],
|
|
375
|
-
action: async () => {
|
|
376
|
-
output.writeln();
|
|
377
|
-
output.writeln(output.bold('MoFlo Appliance (RVFA)'));
|
|
378
|
-
output.writeln(output.dim('Self-contained deployment format for the full MoFlo platform.'));
|
|
379
|
-
output.writeln();
|
|
380
|
-
output.writeln('Subcommands:');
|
|
381
|
-
output.printList([
|
|
382
|
-
'build - Build a self-contained ruflo.rvf appliance',
|
|
383
|
-
'inspect - Show appliance header and section manifest',
|
|
384
|
-
'verify - Verify appliance integrity and run capability tests',
|
|
385
|
-
'extract - Extract all sections from an appliance',
|
|
386
|
-
'run - Boot and run an RVFA appliance',
|
|
387
|
-
'sign - Sign an appliance with Ed25519 for tamper detection',
|
|
388
|
-
'publish - Publish an appliance to IPFS via Pinata',
|
|
389
|
-
'update - Hot-patch a section in an appliance',
|
|
390
|
-
]);
|
|
391
|
-
output.writeln();
|
|
392
|
-
output.writeln('Profiles:');
|
|
393
|
-
output.printList([
|
|
394
|
-
`${output.bold('cloud')} - API-only, smallest footprint (~15 MB)`,
|
|
395
|
-
`${output.bold('hybrid')} - API + local fallback models (~500 MB)`,
|
|
396
|
-
`${output.bold('offline')} - Fully air-gapped with bundled models (~4 GB)`,
|
|
397
|
-
]);
|
|
398
|
-
output.writeln();
|
|
399
|
-
output.writeln(output.dim('Use "flo appliance <subcommand> --help" for details.'));
|
|
400
|
-
return { success: true };
|
|
401
|
-
},
|
|
402
|
-
};
|
|
403
|
-
export default applianceCommand;
|
|
404
|
-
//# sourceMappingURL=appliance.js.map
|