meridian-core 0.1.0
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/dist/cli.d.ts +14 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +46 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands/analyze.d.ts +8 -0
- package/dist/commands/analyze.d.ts.map +1 -0
- package/dist/commands/analyze.js +78 -0
- package/dist/commands/analyze.js.map +1 -0
- package/dist/commands/field.d.ts +8 -0
- package/dist/commands/field.d.ts.map +1 -0
- package/dist/commands/field.js +46 -0
- package/dist/commands/field.js.map +1 -0
- package/dist/commands/gravity.d.ts +8 -0
- package/dist/commands/gravity.d.ts.map +1 -0
- package/dist/commands/gravity.js +47 -0
- package/dist/commands/gravity.js.map +1 -0
- package/dist/commands/trace.d.ts +8 -0
- package/dist/commands/trace.d.ts.map +1 -0
- package/dist/commands/trace.js +37 -0
- package/dist/commands/trace.js.map +1 -0
- package/dist/commands/version.d.ts +8 -0
- package/dist/commands/version.d.ts.map +1 -0
- package/dist/commands/version.js +21 -0
- package/dist/commands/version.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +7 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/errors.d.ts +21 -0
- package/dist/lib/errors.d.ts.map +1 -0
- package/dist/lib/errors.js +37 -0
- package/dist/lib/errors.js.map +1 -0
- package/dist/lib/input.d.ts +11 -0
- package/dist/lib/input.d.ts.map +1 -0
- package/dist/lib/input.js +38 -0
- package/dist/lib/input.js.map +1 -0
- package/dist/lib/manifest.d.ts +10 -0
- package/dist/lib/manifest.d.ts.map +1 -0
- package/dist/lib/manifest.js +35 -0
- package/dist/lib/manifest.js.map +1 -0
- package/dist/lib/options.d.ts +26 -0
- package/dist/lib/options.d.ts.map +1 -0
- package/dist/lib/options.js +44 -0
- package/dist/lib/options.js.map +1 -0
- package/dist/lib/output.d.ts +32 -0
- package/dist/lib/output.d.ts.map +1 -0
- package/dist/lib/output.js +156 -0
- package/dist/lib/output.js.map +1 -0
- package/package.json +39 -0
- package/src/cli.ts +54 -0
- package/src/commands/analyze.ts +103 -0
- package/src/commands/field.ts +66 -0
- package/src/commands/gravity.ts +70 -0
- package/src/commands/trace.ts +51 -0
- package/src/commands/version.ts +21 -0
- package/src/index.ts +7 -0
- package/src/lib/errors.ts +42 -0
- package/src/lib/input.test.ts +38 -0
- package/src/lib/input.ts +43 -0
- package/src/lib/manifest.test.ts +60 -0
- package/src/lib/manifest.ts +41 -0
- package/src/lib/options.test.ts +27 -0
- package/src/lib/options.ts +47 -0
- package/src/lib/output.ts +180 -0
- package/tsconfig.json +10 -0
- package/vitest.config.ts +8 -0
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { InvalidArgumentError } from 'commander';
|
|
2
|
+
/**
|
|
3
|
+
* Parse and validate a --network option value.
|
|
4
|
+
*
|
|
5
|
+
* @param value - Raw CLI argument
|
|
6
|
+
* @returns Validated Network
|
|
7
|
+
* @throws InvalidArgumentError if the value is not "mainnet" or "testnet"
|
|
8
|
+
*/
|
|
9
|
+
export function parseNetwork(value) {
|
|
10
|
+
if (value !== 'mainnet' && value !== 'testnet') {
|
|
11
|
+
throw new InvalidArgumentError('Network must be "mainnet" or "testnet".');
|
|
12
|
+
}
|
|
13
|
+
return value;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Parse and validate a --confidence-threshold option value.
|
|
17
|
+
*
|
|
18
|
+
* @param value - Raw CLI argument
|
|
19
|
+
* @returns Parsed float between 0 and 1
|
|
20
|
+
* @throws InvalidArgumentError if the value is not a number in range
|
|
21
|
+
*/
|
|
22
|
+
export function parseThreshold(value) {
|
|
23
|
+
const parsed = Number.parseFloat(value);
|
|
24
|
+
if (Number.isNaN(parsed) || parsed < 0 || parsed > 1) {
|
|
25
|
+
throw new InvalidArgumentError('Confidence threshold must be a number between 0 and 1.');
|
|
26
|
+
}
|
|
27
|
+
return parsed;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Attach the options shared by every layer command (network, RPC, input, output).
|
|
31
|
+
*
|
|
32
|
+
* @param command - Commander command to extend
|
|
33
|
+
* @returns The same command, for chaining
|
|
34
|
+
*/
|
|
35
|
+
export function withCommonOptions(command) {
|
|
36
|
+
return command
|
|
37
|
+
.argument('[tx]', 'Base64-encoded transaction XDR')
|
|
38
|
+
.option('-n, --network <network>', 'Stellar network (mainnet | testnet)', parseNetwork, 'testnet')
|
|
39
|
+
.option('--rpc-url <url>', 'Override the Soroban RPC endpoint (else read from env)')
|
|
40
|
+
.option('-f, --file <path>', 'Read the transaction XDR from a file instead of an argument')
|
|
41
|
+
.option('-e, --ecosystem <path>', 'Path to an ecosystem manifest JSON file')
|
|
42
|
+
.option('--json', 'Print raw JSON instead of a formatted report');
|
|
43
|
+
}
|
|
44
|
+
//# sourceMappingURL=options.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"options.js","sourceRoot":"","sources":["../../src/lib/options.ts"],"names":[],"mappings":"AAAA,OAAO,EAAW,oBAAoB,EAAE,MAAM,WAAW,CAAC;AAG1D;;;;;;GAMG;AACH,MAAM,UAAU,YAAY,CAAC,KAAa;IACxC,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;QAC/C,MAAM,IAAI,oBAAoB,CAAC,yCAAyC,CAAC,CAAC;IAC5E,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,cAAc,CAAC,KAAa;IAC1C,MAAM,MAAM,GAAG,MAAM,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC;IACxC,IAAI,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,MAAM,GAAG,CAAC,IAAI,MAAM,GAAG,CAAC,EAAE,CAAC;QACrD,MAAM,IAAI,oBAAoB,CAAC,wDAAwD,CAAC,CAAC;IAC3F,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,iBAAiB,CAAC,OAAgB;IAChD,OAAO,OAAO;SACX,QAAQ,CAAC,MAAM,EAAE,gCAAgC,CAAC;SAClD,MAAM,CAAC,yBAAyB,EAAE,qCAAqC,EAAE,YAAY,EAAE,SAAS,CAAC;SACjG,MAAM,CAAC,iBAAiB,EAAE,wDAAwD,CAAC;SACnF,MAAM,CAAC,mBAAmB,EAAE,6DAA6D,CAAC;SAC1F,MAAM,CAAC,wBAAwB,EAAE,yCAAyC,CAAC;SAC3E,MAAM,CAAC,QAAQ,EAAE,8CAA8C,CAAC,CAAC;AACtE,CAAC"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { AnalyzeResponse, FieldResult, GravityResult, TraceResult } from '@meridian/core';
|
|
2
|
+
/**
|
|
3
|
+
* Print any value as pretty-printed JSON.
|
|
4
|
+
*
|
|
5
|
+
* @param value - Value to serialize
|
|
6
|
+
*/
|
|
7
|
+
export declare function printJson(value: unknown): void;
|
|
8
|
+
/**
|
|
9
|
+
* Print a TraceResult in human-readable form.
|
|
10
|
+
*
|
|
11
|
+
* @param trace - TRACE layer result
|
|
12
|
+
*/
|
|
13
|
+
export declare function printTrace(trace: TraceResult): void;
|
|
14
|
+
/**
|
|
15
|
+
* Print a FieldResult in human-readable form.
|
|
16
|
+
*
|
|
17
|
+
* @param result - FIELD layer result
|
|
18
|
+
*/
|
|
19
|
+
export declare function printField(result: FieldResult): void;
|
|
20
|
+
/**
|
|
21
|
+
* Print a GravityResult in human-readable form.
|
|
22
|
+
*
|
|
23
|
+
* @param result - GRAVITY layer result
|
|
24
|
+
*/
|
|
25
|
+
export declare function printGravity(result: GravityResult): void;
|
|
26
|
+
/**
|
|
27
|
+
* Print a full AnalyzeResponse in human-readable form.
|
|
28
|
+
*
|
|
29
|
+
* @param response - Full analysis response including brief
|
|
30
|
+
*/
|
|
31
|
+
export declare function printAnalysis(response: AnalyzeResponse): void;
|
|
32
|
+
//# sourceMappingURL=output.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"output.d.ts","sourceRoot":"","sources":["../../src/lib/output.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,eAAe,EACf,WAAW,EACX,aAAa,EACb,WAAW,EAEZ,MAAM,gBAAgB,CAAC;AAExB;;;;GAIG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI,CAE9C;AAyCD;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,WAAW,GAAG,IAAI,CAyBnD;AAED;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,MAAM,EAAE,WAAW,GAAG,IAAI,CAoBpD;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,aAAa,GAAG,IAAI,CAiBxD;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,QAAQ,EAAE,eAAe,GAAG,IAAI,CAkC7D"}
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import pc from 'picocolors';
|
|
2
|
+
/**
|
|
3
|
+
* Print any value as pretty-printed JSON.
|
|
4
|
+
*
|
|
5
|
+
* @param value - Value to serialize
|
|
6
|
+
*/
|
|
7
|
+
export function printJson(value) {
|
|
8
|
+
console.log(JSON.stringify(value, null, 2));
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Colorize a verdict badge.
|
|
12
|
+
*
|
|
13
|
+
* @param verdict - MERIDIAN verdict
|
|
14
|
+
* @returns Colorized verdict string
|
|
15
|
+
*/
|
|
16
|
+
function verdictBadge(verdict) {
|
|
17
|
+
switch (verdict) {
|
|
18
|
+
case 'CLEAR':
|
|
19
|
+
return pc.bgGreen(pc.black(' CLEAR '));
|
|
20
|
+
case 'WARN':
|
|
21
|
+
return pc.bgYellow(pc.black(' WARN '));
|
|
22
|
+
case 'ABORT':
|
|
23
|
+
return pc.bgRed(pc.white(pc.bold(' ABORT ')));
|
|
24
|
+
default:
|
|
25
|
+
return verdict;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Print a section header.
|
|
30
|
+
*
|
|
31
|
+
* @param title - Section title
|
|
32
|
+
*/
|
|
33
|
+
function section(title) {
|
|
34
|
+
console.log('');
|
|
35
|
+
console.log(pc.bold(pc.cyan(`── ${title} `.padEnd(48, '─'))));
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Print a labeled key/value line.
|
|
39
|
+
*
|
|
40
|
+
* @param label - Field label
|
|
41
|
+
* @param value - Field value
|
|
42
|
+
*/
|
|
43
|
+
function field(label, value) {
|
|
44
|
+
console.log(` ${pc.dim(label + ':')} ${value}`);
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Print a TraceResult in human-readable form.
|
|
48
|
+
*
|
|
49
|
+
* @param trace - TRACE layer result
|
|
50
|
+
*/
|
|
51
|
+
export function printTrace(trace) {
|
|
52
|
+
section('TRACE');
|
|
53
|
+
field('success', trace.success ? pc.green('true') : pc.red('false'));
|
|
54
|
+
if (trace.staleness_warning) {
|
|
55
|
+
field('staleness_warning', pc.yellow('true'));
|
|
56
|
+
}
|
|
57
|
+
if (trace.failure_point) {
|
|
58
|
+
const fp = trace.failure_point;
|
|
59
|
+
console.log(` ${pc.red('failure_point')}:`);
|
|
60
|
+
field(' step_index', fp.step_index);
|
|
61
|
+
if (fp.contract_id)
|
|
62
|
+
field(' contract_id', fp.contract_id);
|
|
63
|
+
if (fp.function_name)
|
|
64
|
+
field(' function_name', fp.function_name);
|
|
65
|
+
field(' error_code', fp.error_code);
|
|
66
|
+
field(' root_cause', fp.root_cause);
|
|
67
|
+
}
|
|
68
|
+
field('execution_path', `${trace.execution_path.length} step(s)`);
|
|
69
|
+
field('auth_entries', `${trace.auth_entries.length} entrie(s)`);
|
|
70
|
+
field('fee_estimate', `total=${trace.fee_estimate.total_fee} base=${trace.fee_estimate.classic_base_fee} min_resource=${trace.fee_estimate.min_resource_fee}`);
|
|
71
|
+
field('resource_usage', `cpu=${trace.resource_usage.cpu_instructions} mem=${trace.resource_usage.memory_bytes}b read=${trace.resource_usage.read_bytes}b write=${trace.resource_usage.write_bytes}b`);
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Print a FieldResult in human-readable form.
|
|
75
|
+
*
|
|
76
|
+
* @param result - FIELD layer result
|
|
77
|
+
*/
|
|
78
|
+
export function printField(result) {
|
|
79
|
+
section('FIELD');
|
|
80
|
+
field('contracts_mapped', result.contracts_mapped);
|
|
81
|
+
field('manifest_coverage', `${Math.round(result.manifest_coverage * 100)}%`);
|
|
82
|
+
if (result.ttl_warnings.length > 0) {
|
|
83
|
+
console.log(` ${pc.yellow('ttl_warnings')}:`);
|
|
84
|
+
for (const warning of result.ttl_warnings) {
|
|
85
|
+
console.log(` - ${warning.contract_id} (${warning.ledger_key}) ttl_remaining=${warning.ttl_remaining} [${warning.severity}]`);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
if (result.dependency_graph.length > 0) {
|
|
89
|
+
console.log(` ${pc.dim('dependency_graph')}:`);
|
|
90
|
+
for (const node of result.dependency_graph) {
|
|
91
|
+
const label = node.name ? `${node.name} (${node.address})` : node.address;
|
|
92
|
+
const deps = node.dependencies.length > 0 ? ` → ${node.dependencies.join(', ')}` : '';
|
|
93
|
+
console.log(` - ${label}${deps}`);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Print a GravityResult in human-readable form.
|
|
99
|
+
*
|
|
100
|
+
* @param result - GRAVITY layer result
|
|
101
|
+
*/
|
|
102
|
+
export function printGravity(result) {
|
|
103
|
+
section('GRAVITY');
|
|
104
|
+
field('blast_radius', result.blast_radius);
|
|
105
|
+
field('total_affected_users', result.total_affected_users);
|
|
106
|
+
field('recovery', result.recovery);
|
|
107
|
+
if (result.critical.length > 0)
|
|
108
|
+
field('critical', pc.red(result.critical.join(', ')));
|
|
109
|
+
if (result.warning.length > 0)
|
|
110
|
+
field('warning', pc.yellow(result.warning.join(', ')));
|
|
111
|
+
if (result.monitor.length > 0)
|
|
112
|
+
field('monitor', pc.blue(result.monitor.join(', ')));
|
|
113
|
+
if (result.safe.length > 0)
|
|
114
|
+
field('safe', pc.green(result.safe.join(', ')));
|
|
115
|
+
if (result.affected_contracts.length > 0) {
|
|
116
|
+
console.log(` ${pc.dim('affected_contracts')}:`);
|
|
117
|
+
for (const contract of result.affected_contracts) {
|
|
118
|
+
const label = contract.name ? `${contract.name} (${contract.address})` : contract.address;
|
|
119
|
+
console.log(` - [${contract.impact}] ${label} — ${contract.reason}`);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Print a full AnalyzeResponse in human-readable form.
|
|
125
|
+
*
|
|
126
|
+
* @param response - Full analysis response including brief
|
|
127
|
+
*/
|
|
128
|
+
export function printAnalysis(response) {
|
|
129
|
+
console.log('');
|
|
130
|
+
console.log(`${pc.bold('MERIDIAN')} v${response.version} ${verdictBadge(response.verdict)} confidence=${response.confidence}`);
|
|
131
|
+
printTrace(response.trace);
|
|
132
|
+
printField(response.field);
|
|
133
|
+
printGravity(response.gravity);
|
|
134
|
+
if (response.fix_sequence && response.fix_sequence.length > 0) {
|
|
135
|
+
section('FIX SEQUENCE');
|
|
136
|
+
for (const step of response.fix_sequence) {
|
|
137
|
+
console.log(` ${pc.bold(String(step.order) + '.')} ${step.operation} — ${step.description} ${pc.dim(`(~${step.estimated_cost_stroops} stroops, ~${step.estimated_time_minutes}min)`)}`);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
if (response.warnings && response.warnings.length > 0) {
|
|
141
|
+
section('WARNINGS');
|
|
142
|
+
for (const warning of response.warnings) {
|
|
143
|
+
console.log(` ${pc.yellow('⚠')} ${warning}`);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
section('BRIEF');
|
|
147
|
+
console.log(response.brief);
|
|
148
|
+
section('META');
|
|
149
|
+
field('analyzed_at', response.meta.analyzed_at);
|
|
150
|
+
field('network', response.meta.network);
|
|
151
|
+
field('ledger_sequence', response.meta.ledger_sequence);
|
|
152
|
+
field('simulation_stale', response.meta.simulation_stale);
|
|
153
|
+
field('processing_ms', response.meta.processing_ms);
|
|
154
|
+
console.log('');
|
|
155
|
+
}
|
|
156
|
+
//# sourceMappingURL=output.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"output.js","sourceRoot":"","sources":["../../src/lib/output.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "meridian-core",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "MERIDIAN command-line interface — pre-execution intelligence from your terminal",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"bin": {
|
|
9
|
+
"meridian": "dist/index.js",
|
|
10
|
+
"meridian-core": "dist/index.js"
|
|
11
|
+
},
|
|
12
|
+
"exports": {
|
|
13
|
+
".": {
|
|
14
|
+
"types": "./dist/index.d.ts",
|
|
15
|
+
"import": "./dist/index.js"
|
|
16
|
+
}
|
|
17
|
+
},
|
|
18
|
+
"scripts": {
|
|
19
|
+
"build": "tsc && chmod +x dist/index.js",
|
|
20
|
+
"dev": "tsx watch src/index.ts",
|
|
21
|
+
"start": "node dist/index.js",
|
|
22
|
+
"test": "vitest run",
|
|
23
|
+
"typecheck": "tsc --noEmit"
|
|
24
|
+
},
|
|
25
|
+
"dependencies": {
|
|
26
|
+
"@meridian/ai": "0.1.0",
|
|
27
|
+
"@meridian/core": "0.1.0",
|
|
28
|
+
"commander": "^13.0.0",
|
|
29
|
+
"picocolors": "^1.1.1"
|
|
30
|
+
},
|
|
31
|
+
"devDependencies": {
|
|
32
|
+
"tsx": "^4.19.2",
|
|
33
|
+
"typescript": "^5.7.2",
|
|
34
|
+
"vitest": "^4.1.9"
|
|
35
|
+
},
|
|
36
|
+
"engines": {
|
|
37
|
+
"node": ">=20"
|
|
38
|
+
}
|
|
39
|
+
}
|
package/src/cli.ts
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import { MERIDIAN_VERSION } from '@meridian/core';
|
|
3
|
+
import { analyzeCommand } from './commands/analyze.js';
|
|
4
|
+
import { traceCommand } from './commands/trace.js';
|
|
5
|
+
import { fieldCommand } from './commands/field.js';
|
|
6
|
+
import { gravityCommand } from './commands/gravity.js';
|
|
7
|
+
import { versionCommand } from './commands/version.js';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Build the root `meridian` commander program with all subcommands attached.
|
|
11
|
+
*
|
|
12
|
+
* @returns Configured commander Command ready to parse argv
|
|
13
|
+
*/
|
|
14
|
+
export function buildProgram(): Command {
|
|
15
|
+
const program = new Command('meridian')
|
|
16
|
+
.description(
|
|
17
|
+
'MERIDIAN — pre-execution intelligence for Stellar developers.\n' +
|
|
18
|
+
'Know what crosses before it does.',
|
|
19
|
+
)
|
|
20
|
+
.version(MERIDIAN_VERSION, '-v, --version', 'Print the MERIDIAN version')
|
|
21
|
+
.addHelpText(
|
|
22
|
+
'after',
|
|
23
|
+
`
|
|
24
|
+
Examples:
|
|
25
|
+
$ meridian analyze <base64-xdr> --network testnet
|
|
26
|
+
$ cat tx.xdr | meridian analyze --network mainnet --json
|
|
27
|
+
$ meridian trace --file tx.xdr --network testnet
|
|
28
|
+
$ meridian gravity <base64-xdr> --ecosystem manifest.json
|
|
29
|
+
|
|
30
|
+
Environment:
|
|
31
|
+
STELLAR_RPC_TESTNET Soroban RPC endpoint for testnet (or use --rpc-url)
|
|
32
|
+
STELLAR_RPC_MAINNET Soroban RPC endpoint for mainnet (or use --rpc-url)
|
|
33
|
+
ANTHROPIC_API_KEY Claude API key for BRIEF synthesis (optional, falls back to a deterministic brief)
|
|
34
|
+
`,
|
|
35
|
+
);
|
|
36
|
+
|
|
37
|
+
program.addCommand(analyzeCommand(), { isDefault: true });
|
|
38
|
+
program.addCommand(traceCommand());
|
|
39
|
+
program.addCommand(fieldCommand());
|
|
40
|
+
program.addCommand(gravityCommand());
|
|
41
|
+
program.addCommand(versionCommand());
|
|
42
|
+
|
|
43
|
+
return program;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Parse argv and run the MERIDIAN CLI.
|
|
48
|
+
*
|
|
49
|
+
* @param argv - Process argv (defaults to process.argv)
|
|
50
|
+
*/
|
|
51
|
+
export async function runCli(argv: string[] = process.argv): Promise<void> {
|
|
52
|
+
const program = buildProgram();
|
|
53
|
+
await program.parseAsync(argv);
|
|
54
|
+
}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import { analyze } from '@meridian/core';
|
|
3
|
+
import type { AnalyzeResponse, Network } from '@meridian/core';
|
|
4
|
+
import { synthesizeBrief, generateFallbackBrief } from '@meridian/ai';
|
|
5
|
+
import { resolveTxInput } from '../lib/input.js';
|
|
6
|
+
import { loadManifest } from '../lib/manifest.js';
|
|
7
|
+
import { failWithError, failWithMeridianError, isMeridianError } from '../lib/errors.js';
|
|
8
|
+
import { printAnalysis, printJson } from '../lib/output.js';
|
|
9
|
+
import { parseThreshold, withCommonOptions } from '../lib/options.js';
|
|
10
|
+
|
|
11
|
+
interface AnalyzeCommandOptions {
|
|
12
|
+
network: Network;
|
|
13
|
+
rpcUrl?: string;
|
|
14
|
+
file?: string;
|
|
15
|
+
ecosystem?: string;
|
|
16
|
+
json?: boolean;
|
|
17
|
+
skipField?: boolean;
|
|
18
|
+
skipGravity?: boolean;
|
|
19
|
+
confidenceThreshold?: number;
|
|
20
|
+
brief: boolean;
|
|
21
|
+
apiKey?: string;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Build the `meridian analyze` subcommand (default command).
|
|
26
|
+
*
|
|
27
|
+
* @returns Configured commander Command
|
|
28
|
+
*/
|
|
29
|
+
export function analyzeCommand(): Command {
|
|
30
|
+
const command = new Command('analyze').description(
|
|
31
|
+
'Run the full MERIDIAN pipeline (TRACE + FIELD + GRAVITY + BRIEF) on a transaction',
|
|
32
|
+
);
|
|
33
|
+
|
|
34
|
+
withCommonOptions(command)
|
|
35
|
+
.option('--skip-field', 'Skip the FIELD dependency-mapping layer')
|
|
36
|
+
.option('--skip-gravity', 'Skip the GRAVITY blast-radius layer')
|
|
37
|
+
.option('--confidence-threshold <n>', 'Minimum confidence required for a CLEAR verdict', parseThreshold)
|
|
38
|
+
.option('--no-brief', 'Skip GenAI BRIEF synthesis (structured layers only)')
|
|
39
|
+
.option('--api-key <key>', 'Anthropic API key for BRIEF synthesis (else read from env)')
|
|
40
|
+
.action(async (tx: string | undefined, options: AnalyzeCommandOptions) => {
|
|
41
|
+
try {
|
|
42
|
+
const txXdr = await resolveTxInput(tx, options.file);
|
|
43
|
+
const ecosystem = await loadManifest(options.ecosystem);
|
|
44
|
+
|
|
45
|
+
const result = await analyze({
|
|
46
|
+
tx: txXdr,
|
|
47
|
+
network: options.network,
|
|
48
|
+
ecosystem,
|
|
49
|
+
options: {
|
|
50
|
+
skip_field: options.skipField,
|
|
51
|
+
skip_gravity: options.skipGravity,
|
|
52
|
+
confidence_threshold: options.confidenceThreshold,
|
|
53
|
+
rpc_url: options.rpcUrl,
|
|
54
|
+
},
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
if (isMeridianError(result)) {
|
|
58
|
+
if (options.json) {
|
|
59
|
+
printJson(result);
|
|
60
|
+
process.exit(1);
|
|
61
|
+
}
|
|
62
|
+
failWithMeridianError(result);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
let brief = 'BRIEF synthesis skipped (--no-brief).';
|
|
66
|
+
let warnings = result.warnings;
|
|
67
|
+
|
|
68
|
+
if (options.brief) {
|
|
69
|
+
const briefInput = {
|
|
70
|
+
verdict: result.verdict,
|
|
71
|
+
confidence: result.confidence,
|
|
72
|
+
trace: result.trace,
|
|
73
|
+
field: result.field,
|
|
74
|
+
gravity: result.gravity,
|
|
75
|
+
fix_sequence: result.fix_sequence,
|
|
76
|
+
warnings: result.warnings,
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
const briefResult = await synthesizeBrief(briefInput, { apiKey: options.apiKey });
|
|
80
|
+
|
|
81
|
+
if (isMeridianError(briefResult)) {
|
|
82
|
+
brief = generateFallbackBrief(briefInput);
|
|
83
|
+
warnings = [...(result.warnings ?? []), briefResult.error];
|
|
84
|
+
} else {
|
|
85
|
+
brief = briefResult;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const response: AnalyzeResponse = { ...result, brief, warnings };
|
|
90
|
+
|
|
91
|
+
if (options.json) {
|
|
92
|
+
printJson(response);
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
printAnalysis(response);
|
|
97
|
+
} catch (err) {
|
|
98
|
+
failWithError(err);
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
return command;
|
|
103
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import { buildFieldGraph, trace } from '@meridian/core';
|
|
3
|
+
import type { Network } from '@meridian/core';
|
|
4
|
+
import { resolveTxInput } from '../lib/input.js';
|
|
5
|
+
import { loadManifest } from '../lib/manifest.js';
|
|
6
|
+
import { failWithError, failWithMeridianError, isMeridianError } from '../lib/errors.js';
|
|
7
|
+
import { printField, printJson } from '../lib/output.js';
|
|
8
|
+
import { withCommonOptions } from '../lib/options.js';
|
|
9
|
+
|
|
10
|
+
interface FieldCommandOptions {
|
|
11
|
+
network: Network;
|
|
12
|
+
rpcUrl?: string;
|
|
13
|
+
file?: string;
|
|
14
|
+
ecosystem?: string;
|
|
15
|
+
json?: boolean;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Build the `meridian field` subcommand.
|
|
20
|
+
*
|
|
21
|
+
* @returns Configured commander Command
|
|
22
|
+
*/
|
|
23
|
+
export function fieldCommand(): Command {
|
|
24
|
+
const command = new Command('field').description(
|
|
25
|
+
'Run TRACE + FIELD — map the dependency graph touched by a transaction',
|
|
26
|
+
);
|
|
27
|
+
|
|
28
|
+
withCommonOptions(command).action(async (tx: string | undefined, options: FieldCommandOptions) => {
|
|
29
|
+
try {
|
|
30
|
+
const txXdr = await resolveTxInput(tx, options.file);
|
|
31
|
+
const manifest = await loadManifest(options.ecosystem);
|
|
32
|
+
|
|
33
|
+
const traceResult = await trace(txXdr, { network: options.network, rpcUrl: options.rpcUrl });
|
|
34
|
+
if (isMeridianError(traceResult)) {
|
|
35
|
+
if (options.json) {
|
|
36
|
+
printJson(traceResult);
|
|
37
|
+
process.exit(1);
|
|
38
|
+
}
|
|
39
|
+
failWithMeridianError(traceResult);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const fieldResult = buildFieldGraph(
|
|
43
|
+
traceResult,
|
|
44
|
+
{
|
|
45
|
+
ledgerSequence: 0,
|
|
46
|
+
latestLedger: 0,
|
|
47
|
+
footprintContracts: [],
|
|
48
|
+
readOnly: [],
|
|
49
|
+
readWrite: [],
|
|
50
|
+
},
|
|
51
|
+
{ network: options.network, manifest },
|
|
52
|
+
);
|
|
53
|
+
|
|
54
|
+
if (options.json) {
|
|
55
|
+
printJson(fieldResult);
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
printField(fieldResult);
|
|
60
|
+
} catch (err) {
|
|
61
|
+
failWithError(err);
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
return command;
|
|
66
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import { buildFieldGraph, scoreGravity, trace } from '@meridian/core';
|
|
3
|
+
import type { Network } from '@meridian/core';
|
|
4
|
+
import { resolveTxInput } from '../lib/input.js';
|
|
5
|
+
import { loadManifest } from '../lib/manifest.js';
|
|
6
|
+
import { failWithError, failWithMeridianError, isMeridianError } from '../lib/errors.js';
|
|
7
|
+
import { printGravity, printJson } from '../lib/output.js';
|
|
8
|
+
import { withCommonOptions } from '../lib/options.js';
|
|
9
|
+
|
|
10
|
+
interface GravityCommandOptions {
|
|
11
|
+
network: Network;
|
|
12
|
+
rpcUrl?: string;
|
|
13
|
+
file?: string;
|
|
14
|
+
ecosystem?: string;
|
|
15
|
+
json?: boolean;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Build the `meridian gravity` subcommand.
|
|
20
|
+
*
|
|
21
|
+
* @returns Configured commander Command
|
|
22
|
+
*/
|
|
23
|
+
export function gravityCommand(): Command {
|
|
24
|
+
const command = new Command('gravity').description(
|
|
25
|
+
'Run TRACE + FIELD + GRAVITY — score the blast radius of a transaction',
|
|
26
|
+
);
|
|
27
|
+
|
|
28
|
+
withCommonOptions(command).action(
|
|
29
|
+
async (tx: string | undefined, options: GravityCommandOptions) => {
|
|
30
|
+
try {
|
|
31
|
+
const txXdr = await resolveTxInput(tx, options.file);
|
|
32
|
+
const manifest = await loadManifest(options.ecosystem);
|
|
33
|
+
|
|
34
|
+
const traceResult = await trace(txXdr, { network: options.network, rpcUrl: options.rpcUrl });
|
|
35
|
+
if (isMeridianError(traceResult)) {
|
|
36
|
+
if (options.json) {
|
|
37
|
+
printJson(traceResult);
|
|
38
|
+
process.exit(1);
|
|
39
|
+
}
|
|
40
|
+
failWithMeridianError(traceResult);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const fieldResult = buildFieldGraph(
|
|
44
|
+
traceResult,
|
|
45
|
+
{
|
|
46
|
+
ledgerSequence: 0,
|
|
47
|
+
latestLedger: 0,
|
|
48
|
+
footprintContracts: [],
|
|
49
|
+
readOnly: [],
|
|
50
|
+
readWrite: [],
|
|
51
|
+
},
|
|
52
|
+
{ network: options.network, manifest },
|
|
53
|
+
);
|
|
54
|
+
|
|
55
|
+
const gravityResult = scoreGravity(traceResult, fieldResult, { manifest });
|
|
56
|
+
|
|
57
|
+
if (options.json) {
|
|
58
|
+
printJson(gravityResult);
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
printGravity(gravityResult);
|
|
63
|
+
} catch (err) {
|
|
64
|
+
failWithError(err);
|
|
65
|
+
}
|
|
66
|
+
},
|
|
67
|
+
);
|
|
68
|
+
|
|
69
|
+
return command;
|
|
70
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import { trace } from '@meridian/core';
|
|
3
|
+
import { resolveTxInput } from '../lib/input.js';
|
|
4
|
+
import { failWithError, failWithMeridianError, isMeridianError } from '../lib/errors.js';
|
|
5
|
+
import { printJson, printTrace } from '../lib/output.js';
|
|
6
|
+
import { withCommonOptions } from '../lib/options.js';
|
|
7
|
+
import type { Network } from '@meridian/core';
|
|
8
|
+
|
|
9
|
+
interface TraceCommandOptions {
|
|
10
|
+
network: Network;
|
|
11
|
+
rpcUrl?: string;
|
|
12
|
+
file?: string;
|
|
13
|
+
json?: boolean;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Build the `meridian trace` subcommand.
|
|
18
|
+
*
|
|
19
|
+
* @returns Configured commander Command
|
|
20
|
+
*/
|
|
21
|
+
export function traceCommand(): Command {
|
|
22
|
+
const command = new Command('trace').description(
|
|
23
|
+
'Run the TRACE engine only — simulate a transaction and report the execution path',
|
|
24
|
+
);
|
|
25
|
+
|
|
26
|
+
withCommonOptions(command).action(async (tx: string | undefined, options: TraceCommandOptions) => {
|
|
27
|
+
try {
|
|
28
|
+
const txXdr = await resolveTxInput(tx, options.file);
|
|
29
|
+
const result = await trace(txXdr, { network: options.network, rpcUrl: options.rpcUrl });
|
|
30
|
+
|
|
31
|
+
if (isMeridianError(result)) {
|
|
32
|
+
if (options.json) {
|
|
33
|
+
printJson(result);
|
|
34
|
+
process.exit(1);
|
|
35
|
+
}
|
|
36
|
+
failWithMeridianError(result);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
if (options.json) {
|
|
40
|
+
printJson(result);
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
printTrace(result);
|
|
45
|
+
} catch (err) {
|
|
46
|
+
failWithError(err);
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
return command;
|
|
51
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import { MERIDIAN_VERSION } from '@meridian/core';
|
|
3
|
+
import { printJson } from '../lib/output.js';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Build the `meridian version` subcommand.
|
|
7
|
+
*
|
|
8
|
+
* @returns Configured commander Command
|
|
9
|
+
*/
|
|
10
|
+
export function versionCommand(): Command {
|
|
11
|
+
return new Command('version')
|
|
12
|
+
.description('Print MERIDIAN product and core engine version')
|
|
13
|
+
.option('--json', 'Print raw JSON instead of a formatted report')
|
|
14
|
+
.action((options: { json?: boolean }) => {
|
|
15
|
+
if (options.json) {
|
|
16
|
+
printJson({ product: 'MERIDIAN', version: MERIDIAN_VERSION });
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
console.log(`MERIDIAN v${MERIDIAN_VERSION}`);
|
|
20
|
+
});
|
|
21
|
+
}
|