@uvrn/cli 1.0.1 → 1.0.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/README.md +7 -3
- package/package.json +11 -3
- package/docs/CLI_GUIDE.md +0 -618
- package/jest.config.js +0 -12
- package/src/cli.ts +0 -294
- package/src/index.ts +0 -6
- package/test-bundle.json +0 -25
- package/test-receipt.json +0 -1
- package/tests/cli.test.ts +0 -393
- package/tsconfig.json +0 -24
package/src/cli.ts
DELETED
|
@@ -1,294 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Loosechain Delta Engine CLI
|
|
5
|
-
* Command-line interface for running delta engine operations
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import { Command } from 'commander';
|
|
9
|
-
import * as fs from 'fs';
|
|
10
|
-
import * as path from 'path';
|
|
11
|
-
import { runDeltaEngine, validateBundle, verifyReceipt } from '@uvrn/core';
|
|
12
|
-
import type { DeltaBundle, DeltaReceipt } from '@uvrn/core';
|
|
13
|
-
|
|
14
|
-
const packageJson = require('../package.json');
|
|
15
|
-
|
|
16
|
-
// Exit codes
|
|
17
|
-
const EXIT_SUCCESS = 0;
|
|
18
|
-
const EXIT_INVALID_BUNDLE = 1;
|
|
19
|
-
const EXIT_ENGINE_ERROR = 2;
|
|
20
|
-
const EXIT_IO_ERROR = 3;
|
|
21
|
-
|
|
22
|
-
interface CliOptions {
|
|
23
|
-
output?: string;
|
|
24
|
-
quiet?: boolean;
|
|
25
|
-
pretty?: boolean;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
/**
|
|
29
|
-
* Read input from file, stdin, or URL
|
|
30
|
-
*/
|
|
31
|
-
async function readInput(input?: string): Promise<string> {
|
|
32
|
-
try {
|
|
33
|
-
// If no input specified, read from stdin
|
|
34
|
-
if (!input || input === '-') {
|
|
35
|
-
return await readStdin();
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
// Check if it's a URL
|
|
39
|
-
if (input.startsWith('http://') || input.startsWith('https://')) {
|
|
40
|
-
return await fetchUrl(input);
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
// Otherwise, treat as file path
|
|
44
|
-
const resolvedPath = path.resolve(process.cwd(), input);
|
|
45
|
-
return fs.readFileSync(resolvedPath, 'utf-8');
|
|
46
|
-
} catch (error) {
|
|
47
|
-
throw new Error(`Failed to read input: ${(error as Error).message}`);
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
/**
|
|
52
|
-
* Read from stdin
|
|
53
|
-
*/
|
|
54
|
-
async function readStdin(): Promise<string> {
|
|
55
|
-
return new Promise((resolve, reject) => {
|
|
56
|
-
let data = '';
|
|
57
|
-
process.stdin.setEncoding('utf-8');
|
|
58
|
-
|
|
59
|
-
process.stdin.on('data', chunk => {
|
|
60
|
-
data += chunk;
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
process.stdin.on('end', () => {
|
|
64
|
-
resolve(data);
|
|
65
|
-
});
|
|
66
|
-
|
|
67
|
-
process.stdin.on('error', error => {
|
|
68
|
-
reject(error);
|
|
69
|
-
});
|
|
70
|
-
});
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
/**
|
|
74
|
-
* Fetch from URL
|
|
75
|
-
*/
|
|
76
|
-
async function fetchUrl(url: string): Promise<string> {
|
|
77
|
-
const https = url.startsWith('https://') ? require('https') : require('http');
|
|
78
|
-
|
|
79
|
-
return new Promise((resolve, reject) => {
|
|
80
|
-
https.get(url, (res: any) => {
|
|
81
|
-
let data = '';
|
|
82
|
-
|
|
83
|
-
res.on('data', (chunk: string) => {
|
|
84
|
-
data += chunk;
|
|
85
|
-
});
|
|
86
|
-
|
|
87
|
-
res.on('end', () => {
|
|
88
|
-
if (res.statusCode >= 200 && res.statusCode < 300) {
|
|
89
|
-
resolve(data);
|
|
90
|
-
} else {
|
|
91
|
-
reject(new Error(`HTTP ${res.statusCode}: ${res.statusMessage}`));
|
|
92
|
-
}
|
|
93
|
-
});
|
|
94
|
-
}).on('error', (error: Error) => {
|
|
95
|
-
reject(error);
|
|
96
|
-
});
|
|
97
|
-
});
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
/**
|
|
101
|
-
* Parse JSON safely
|
|
102
|
-
*/
|
|
103
|
-
function parseJson<T>(jsonString: string, type: string): T {
|
|
104
|
-
try {
|
|
105
|
-
return JSON.parse(jsonString);
|
|
106
|
-
} catch (error) {
|
|
107
|
-
throw new Error(`Invalid JSON for ${type}: ${(error as Error).message}`);
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
/**
|
|
112
|
-
* Write output to file or stdout
|
|
113
|
-
*/
|
|
114
|
-
function writeOutput(data: any, options: CliOptions): void {
|
|
115
|
-
const output = options.pretty
|
|
116
|
-
? JSON.stringify(data, null, 2)
|
|
117
|
-
: JSON.stringify(data);
|
|
118
|
-
|
|
119
|
-
if (options.output) {
|
|
120
|
-
try {
|
|
121
|
-
const resolvedPath = path.resolve(process.cwd(), options.output);
|
|
122
|
-
fs.writeFileSync(resolvedPath, output, 'utf-8');
|
|
123
|
-
if (!options.quiet) {
|
|
124
|
-
console.error(`Output written to: ${options.output}`);
|
|
125
|
-
}
|
|
126
|
-
} catch (error) {
|
|
127
|
-
console.error(`Failed to write output file: ${(error as Error).message}`);
|
|
128
|
-
process.exit(EXIT_IO_ERROR);
|
|
129
|
-
}
|
|
130
|
-
} else {
|
|
131
|
-
console.log(output);
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
/**
|
|
136
|
-
* Command: run
|
|
137
|
-
* Execute the delta engine on a bundle
|
|
138
|
-
*/
|
|
139
|
-
async function runCommand(input: string | undefined, options: CliOptions): Promise<void> {
|
|
140
|
-
try {
|
|
141
|
-
// Read and parse bundle
|
|
142
|
-
const bundleJson = await readInput(input);
|
|
143
|
-
const bundle = parseJson<DeltaBundle>(bundleJson, 'bundle');
|
|
144
|
-
|
|
145
|
-
// Run engine
|
|
146
|
-
const receipt = runDeltaEngine(bundle);
|
|
147
|
-
|
|
148
|
-
// Output receipt
|
|
149
|
-
writeOutput(receipt, options);
|
|
150
|
-
process.exit(EXIT_SUCCESS);
|
|
151
|
-
} catch (error) {
|
|
152
|
-
const errorMessage = (error as Error).message;
|
|
153
|
-
|
|
154
|
-
if (!options.quiet) {
|
|
155
|
-
console.error('Error:', errorMessage);
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
if (errorMessage.includes('Invalid DeltaBundle')) {
|
|
159
|
-
process.exit(EXIT_INVALID_BUNDLE);
|
|
160
|
-
} else if (errorMessage.includes('Failed to read input')) {
|
|
161
|
-
process.exit(EXIT_IO_ERROR);
|
|
162
|
-
} else {
|
|
163
|
-
process.exit(EXIT_ENGINE_ERROR);
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
/**
|
|
169
|
-
* Command: validate
|
|
170
|
-
* Validate bundle structure without running engine
|
|
171
|
-
*/
|
|
172
|
-
async function validateCommand(input: string | undefined, options: CliOptions): Promise<void> {
|
|
173
|
-
try {
|
|
174
|
-
// Read and parse bundle
|
|
175
|
-
const bundleJson = await readInput(input);
|
|
176
|
-
const bundle = parseJson<DeltaBundle>(bundleJson, 'bundle');
|
|
177
|
-
|
|
178
|
-
// Validate
|
|
179
|
-
const result = validateBundle(bundle);
|
|
180
|
-
|
|
181
|
-
if (result.valid) {
|
|
182
|
-
if (!options.quiet) {
|
|
183
|
-
console.log('✓ Bundle is valid');
|
|
184
|
-
}
|
|
185
|
-
writeOutput({ valid: true }, options);
|
|
186
|
-
process.exit(EXIT_SUCCESS);
|
|
187
|
-
} else {
|
|
188
|
-
if (!options.quiet) {
|
|
189
|
-
console.error('✗ Bundle is invalid:', result.error);
|
|
190
|
-
}
|
|
191
|
-
writeOutput({ valid: false, error: result.error }, options);
|
|
192
|
-
process.exit(EXIT_INVALID_BUNDLE);
|
|
193
|
-
}
|
|
194
|
-
} catch (error) {
|
|
195
|
-
if (!options.quiet) {
|
|
196
|
-
console.error('Error:', (error as Error).message);
|
|
197
|
-
}
|
|
198
|
-
process.exit(EXIT_IO_ERROR);
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
/**
|
|
203
|
-
* Command: verify
|
|
204
|
-
* Verify receipt integrity by replaying hash computation
|
|
205
|
-
*/
|
|
206
|
-
async function verifyCommand(input: string | undefined, options: CliOptions): Promise<void> {
|
|
207
|
-
try {
|
|
208
|
-
// Read and parse receipt
|
|
209
|
-
const receiptJson = await readInput(input);
|
|
210
|
-
const receipt = parseJson<DeltaReceipt>(receiptJson, 'receipt');
|
|
211
|
-
|
|
212
|
-
// Verify
|
|
213
|
-
const result = verifyReceipt(receipt);
|
|
214
|
-
|
|
215
|
-
if (result.verified) {
|
|
216
|
-
if (!options.quiet) {
|
|
217
|
-
console.log('✓ Receipt is valid');
|
|
218
|
-
console.log(' Hash:', receipt.hash);
|
|
219
|
-
}
|
|
220
|
-
writeOutput({ verified: true, hash: receipt.hash }, options);
|
|
221
|
-
process.exit(EXIT_SUCCESS);
|
|
222
|
-
} else {
|
|
223
|
-
if (!options.quiet) {
|
|
224
|
-
console.error('✗ Receipt verification failed:', result.error);
|
|
225
|
-
if (result.recomputedHash) {
|
|
226
|
-
console.error(' Expected:', receipt.hash);
|
|
227
|
-
console.error(' Computed:', result.recomputedHash);
|
|
228
|
-
}
|
|
229
|
-
}
|
|
230
|
-
writeOutput({
|
|
231
|
-
verified: false,
|
|
232
|
-
error: result.error,
|
|
233
|
-
providedHash: receipt.hash,
|
|
234
|
-
recomputedHash: result.recomputedHash
|
|
235
|
-
}, options);
|
|
236
|
-
process.exit(EXIT_ENGINE_ERROR);
|
|
237
|
-
}
|
|
238
|
-
} catch (error) {
|
|
239
|
-
if (!options.quiet) {
|
|
240
|
-
console.error('Error:', (error as Error).message);
|
|
241
|
-
}
|
|
242
|
-
process.exit(EXIT_IO_ERROR);
|
|
243
|
-
}
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
/**
|
|
247
|
-
* Main CLI setup
|
|
248
|
-
*/
|
|
249
|
-
function main(): void {
|
|
250
|
-
const program = new Command();
|
|
251
|
-
|
|
252
|
-
program
|
|
253
|
-
.name('delta-engine')
|
|
254
|
-
.description('CLI for Loosechain Delta Engine - Bundle → Receipt')
|
|
255
|
-
.version(packageJson.version);
|
|
256
|
-
|
|
257
|
-
program
|
|
258
|
-
.command('run [bundle]')
|
|
259
|
-
.description('Execute delta engine on a bundle (file path, URL, or stdin)')
|
|
260
|
-
.option('-o, --output <file>', 'Write output to file instead of stdout')
|
|
261
|
-
.option('-q, --quiet', 'Suppress informational messages')
|
|
262
|
-
.option('-p, --pretty', 'Pretty-print JSON output')
|
|
263
|
-
.action(runCommand);
|
|
264
|
-
|
|
265
|
-
program
|
|
266
|
-
.command('validate [bundle]')
|
|
267
|
-
.description('Validate bundle structure without running engine')
|
|
268
|
-
.option('-o, --output <file>', 'Write output to file instead of stdout')
|
|
269
|
-
.option('-q, --quiet', 'Suppress informational messages')
|
|
270
|
-
.option('-p, --pretty', 'Pretty-print JSON output')
|
|
271
|
-
.action(validateCommand);
|
|
272
|
-
|
|
273
|
-
program
|
|
274
|
-
.command('verify [receipt]')
|
|
275
|
-
.description('Verify receipt integrity by replaying hash computation')
|
|
276
|
-
.option('-o, --output <file>', 'Write output to file instead of stdout')
|
|
277
|
-
.option('-q, --quiet', 'Suppress informational messages')
|
|
278
|
-
.option('-p, --pretty', 'Pretty-print JSON output')
|
|
279
|
-
.action(verifyCommand);
|
|
280
|
-
|
|
281
|
-
program.parse(process.argv);
|
|
282
|
-
|
|
283
|
-
// Show help if no command provided
|
|
284
|
-
if (!process.argv.slice(2).length) {
|
|
285
|
-
program.outputHelp();
|
|
286
|
-
}
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
// Run CLI
|
|
290
|
-
if (require.main === module) {
|
|
291
|
-
main();
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
export { main };
|
package/src/index.ts
DELETED
package/test-bundle.json
DELETED
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"bundleId": "cli-test-001",
|
|
3
|
-
"claim": "Test bundle for CLI verification",
|
|
4
|
-
"thresholdPct": 0.05,
|
|
5
|
-
"dataSpecs": [
|
|
6
|
-
{
|
|
7
|
-
"id": "source-a",
|
|
8
|
-
"label": "Source A",
|
|
9
|
-
"sourceKind": "metric",
|
|
10
|
-
"originDocIds": ["doc-a"],
|
|
11
|
-
"metrics": [
|
|
12
|
-
{ "key": "value", "value": 100, "unit": "count" }
|
|
13
|
-
]
|
|
14
|
-
},
|
|
15
|
-
{
|
|
16
|
-
"id": "source-b",
|
|
17
|
-
"label": "Source B",
|
|
18
|
-
"sourceKind": "metric",
|
|
19
|
-
"originDocIds": ["doc-b"],
|
|
20
|
-
"metrics": [
|
|
21
|
-
{ "key": "value", "value": 102, "unit": "count" }
|
|
22
|
-
]
|
|
23
|
-
}
|
|
24
|
-
]
|
|
25
|
-
}
|
package/test-receipt.json
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"bundleId":"cli-test-001","deltaFinal":0.01980198,"sources":["Source A","Source B"],"rounds":[{"round":1,"deltasByMetric":{"value":0.01980198},"withinThreshold":true,"witnessRequired":false}],"suggestedFixes":[],"outcome":"consensus","hash":"36247244c63f58e0b2908d2fad115f60677f29b59b67665579b9b6e8db727791"}
|