meridian-core 0.1.0 → 0.1.2

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.
Files changed (67) hide show
  1. package/README.md +120 -0
  2. package/dist/index.js +1217 -4
  3. package/package.json +10 -11
  4. package/dist/cli.d.ts +0 -14
  5. package/dist/cli.d.ts.map +0 -1
  6. package/dist/cli.js +0 -46
  7. package/dist/cli.js.map +0 -1
  8. package/dist/commands/analyze.d.ts +0 -8
  9. package/dist/commands/analyze.d.ts.map +0 -1
  10. package/dist/commands/analyze.js +0 -78
  11. package/dist/commands/analyze.js.map +0 -1
  12. package/dist/commands/field.d.ts +0 -8
  13. package/dist/commands/field.d.ts.map +0 -1
  14. package/dist/commands/field.js +0 -46
  15. package/dist/commands/field.js.map +0 -1
  16. package/dist/commands/gravity.d.ts +0 -8
  17. package/dist/commands/gravity.d.ts.map +0 -1
  18. package/dist/commands/gravity.js +0 -47
  19. package/dist/commands/gravity.js.map +0 -1
  20. package/dist/commands/trace.d.ts +0 -8
  21. package/dist/commands/trace.d.ts.map +0 -1
  22. package/dist/commands/trace.js +0 -37
  23. package/dist/commands/trace.js.map +0 -1
  24. package/dist/commands/version.d.ts +0 -8
  25. package/dist/commands/version.d.ts.map +0 -1
  26. package/dist/commands/version.js +0 -21
  27. package/dist/commands/version.js.map +0 -1
  28. package/dist/index.d.ts +0 -3
  29. package/dist/index.d.ts.map +0 -1
  30. package/dist/index.js.map +0 -1
  31. package/dist/lib/errors.d.ts +0 -21
  32. package/dist/lib/errors.d.ts.map +0 -1
  33. package/dist/lib/errors.js +0 -37
  34. package/dist/lib/errors.js.map +0 -1
  35. package/dist/lib/input.d.ts +0 -11
  36. package/dist/lib/input.d.ts.map +0 -1
  37. package/dist/lib/input.js +0 -38
  38. package/dist/lib/input.js.map +0 -1
  39. package/dist/lib/manifest.d.ts +0 -10
  40. package/dist/lib/manifest.d.ts.map +0 -1
  41. package/dist/lib/manifest.js +0 -35
  42. package/dist/lib/manifest.js.map +0 -1
  43. package/dist/lib/options.d.ts +0 -26
  44. package/dist/lib/options.d.ts.map +0 -1
  45. package/dist/lib/options.js +0 -44
  46. package/dist/lib/options.js.map +0 -1
  47. package/dist/lib/output.d.ts +0 -32
  48. package/dist/lib/output.d.ts.map +0 -1
  49. package/dist/lib/output.js +0 -156
  50. package/dist/lib/output.js.map +0 -1
  51. package/src/cli.ts +0 -54
  52. package/src/commands/analyze.ts +0 -103
  53. package/src/commands/field.ts +0 -66
  54. package/src/commands/gravity.ts +0 -70
  55. package/src/commands/trace.ts +0 -51
  56. package/src/commands/version.ts +0 -21
  57. package/src/index.ts +0 -7
  58. package/src/lib/errors.ts +0 -42
  59. package/src/lib/input.test.ts +0 -38
  60. package/src/lib/input.ts +0 -43
  61. package/src/lib/manifest.test.ts +0 -60
  62. package/src/lib/manifest.ts +0 -41
  63. package/src/lib/options.test.ts +0 -27
  64. package/src/lib/options.ts +0 -47
  65. package/src/lib/output.ts +0 -180
  66. package/tsconfig.json +0 -10
  67. package/vitest.config.ts +0 -8
package/dist/index.js CHANGED
@@ -1,7 +1,1220 @@
1
1
  #!/usr/bin/env node
2
- import { runCli } from './cli.js';
2
+
3
+ // src/cli.ts
4
+ import { Command as Command7 } from "commander";
5
+
6
+ // src/version.ts
7
+ import { createRequire } from "node:module";
8
+ var require2 = createRequire(import.meta.url);
9
+ var { version } = require2("../package.json");
10
+ var CLI_VERSION = version;
11
+
12
+ // src/commands/analyze.ts
13
+ import { Command as Command2 } from "commander";
14
+
15
+ // ../core/src/analyze.ts
16
+ import { createRequire as createRequire2 } from "node:module";
17
+
18
+ // ../core/src/logger.ts
19
+ var Logger = class {
20
+ level;
21
+ levels = {
22
+ debug: 0,
23
+ info: 1,
24
+ warn: 2,
25
+ error: 3
26
+ };
27
+ constructor(level = "info") {
28
+ this.level = level;
29
+ }
30
+ /**
31
+ * @param message - Log message
32
+ * @param context - Optional structured context
33
+ */
34
+ debug(message, context) {
35
+ this.log("debug", message, context);
36
+ }
37
+ /**
38
+ * @param message - Log message
39
+ * @param context - Optional structured context
40
+ */
41
+ info(message, context) {
42
+ this.log("info", message, context);
43
+ }
44
+ /**
45
+ * @param message - Log message
46
+ * @param context - Optional structured context
47
+ */
48
+ warn(message, context) {
49
+ this.log("warn", message, context);
50
+ }
51
+ /**
52
+ * @param message - Log message
53
+ * @param context - Optional structured context
54
+ */
55
+ error(message, context) {
56
+ this.log("error", message, context);
57
+ }
58
+ log(level, message, context) {
59
+ if (this.levels[level] < this.levels[this.level]) return;
60
+ const entry = {
61
+ level,
62
+ message,
63
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
64
+ ...context && { context }
65
+ };
66
+ const output = JSON.stringify(entry);
67
+ if (level === "error") {
68
+ console.error(output);
69
+ } else if (level === "warn") {
70
+ console.warn(output);
71
+ } else {
72
+ console.log(output);
73
+ }
74
+ }
75
+ };
76
+ var logger = new Logger(
77
+ process.env.LOG_LEVEL ?? "info"
78
+ );
79
+
80
+ // ../core/src/field/index.ts
81
+ function buildFieldGraph(trace2, context, options) {
82
+ logger.info("field:start", { contracts: context.footprintContracts.length });
83
+ const manifest = options?.manifest;
84
+ const contracts = new Set(context.footprintContracts);
85
+ for (const step of trace2.execution_path) {
86
+ if (step.contract_id) contracts.add(step.contract_id);
87
+ }
88
+ const dependencyGraph = [];
89
+ const manifestLookup = buildManifestLookup(manifest);
90
+ for (const address of contracts) {
91
+ const manifestEntry = manifestLookup.get(address);
92
+ const dependencies = manifestEntry?.dependencies ?? [];
93
+ dependencyGraph.push({
94
+ address,
95
+ name: manifestEntry?.name,
96
+ dependencies,
97
+ depth: dependencies.length > 0 ? 1 : 0
98
+ });
99
+ }
100
+ const manifestCoverage = computeManifestCoverage(contracts, manifest);
101
+ return {
102
+ contracts_mapped: dependencyGraph.length,
103
+ dependency_graph: dependencyGraph,
104
+ ttl_warnings: [],
105
+ manifest_coverage: manifestCoverage
106
+ };
107
+ }
108
+ function buildManifestLookup(manifest) {
109
+ const lookup = /* @__PURE__ */ new Map();
110
+ if (!manifest) return lookup;
111
+ for (const contract of manifest.contracts) {
112
+ lookup.set(contract.address, {
113
+ name: contract.name,
114
+ dependencies: contract.dependencies
115
+ });
116
+ }
117
+ return lookup;
118
+ }
119
+ function computeManifestCoverage(contracts, manifest) {
120
+ if (!manifest || contracts.size === 0) return 0;
121
+ const manifestAddresses = new Set(manifest.contracts.map((c) => c.address));
122
+ let covered = 0;
123
+ for (const addr of contracts) {
124
+ if (manifestAddresses.has(addr)) covered++;
125
+ }
126
+ return covered / contracts.size;
127
+ }
128
+
129
+ // ../core/src/gravity/index.ts
130
+ function scoreGravity(trace2, field2, options) {
131
+ logger.info("gravity:start", { contracts: field2.contracts_mapped });
132
+ const manifest = options?.manifest;
133
+ const manifestLookup = buildManifestLookup2(manifest);
134
+ const affectedContracts = [];
135
+ const critical = [];
136
+ const warning = [];
137
+ const safe = [];
138
+ const monitor = [];
139
+ for (const node of field2.dependency_graph) {
140
+ const manifestEntry = manifestLookup.get(node.address);
141
+ let impact = "SAFE";
142
+ let reason = "Read-only dependency";
143
+ if (!trace2.success) {
144
+ const failedContract = trace2.failure_point?.contract_id;
145
+ if (failedContract === node.address) {
146
+ impact = "CRITICAL";
147
+ reason = trace2.failure_point?.root_cause ?? "Direct failure point";
148
+ critical.push(node.address);
149
+ } else if (node.dependencies.length > 0) {
150
+ impact = "MONITOR";
151
+ reason = "Indirect dependency of failed contract";
152
+ monitor.push(node.address);
153
+ } else {
154
+ safe.push(node.address);
155
+ }
156
+ } else {
157
+ const hasWrite = trace2.execution_path.some(
158
+ (s) => s.contract_id === node.address && s.type === "invoke"
159
+ );
160
+ if (hasWrite) {
161
+ impact = "WARNING";
162
+ reason = "Contract receives state writes";
163
+ warning.push(node.address);
164
+ } else {
165
+ safe.push(node.address);
166
+ }
167
+ }
168
+ affectedContracts.push({
169
+ address: node.address,
170
+ name: manifestEntry?.name ?? node.name,
171
+ impact,
172
+ active_users: manifestEntry?.active_users,
173
+ reason
174
+ });
175
+ }
176
+ const totalAffectedUsers = affectedContracts.reduce(
177
+ (sum, c) => sum + (c.active_users ?? 0),
178
+ 0
179
+ );
180
+ const blastRadius = computeBlastRadius(critical.length, warning.length, field2.contracts_mapped);
181
+ const recovery = critical.length > 0 ? "PARTIAL" : "FULL";
182
+ return {
183
+ blast_radius: blastRadius,
184
+ affected_contracts: affectedContracts,
185
+ critical,
186
+ warning,
187
+ safe,
188
+ monitor,
189
+ total_affected_users: totalAffectedUsers,
190
+ recovery
191
+ };
192
+ }
193
+ function buildManifestLookup2(manifest) {
194
+ const lookup = /* @__PURE__ */ new Map();
195
+ if (!manifest) return lookup;
196
+ for (const c of manifest.contracts) {
197
+ lookup.set(c.address, { name: c.name, active_users: c.active_users });
198
+ }
199
+ return lookup;
200
+ }
201
+ function computeBlastRadius(criticalCount, warningCount, totalContracts) {
202
+ if (totalContracts === 0) return 0;
203
+ const score = (criticalCount * 40 + warningCount * 15) / totalContracts;
204
+ return Math.min(100, Math.round(score * 100) / 100);
205
+ }
206
+
207
+ // ../core/src/errors.ts
208
+ var STELLAR_ERROR_TAXONOMY = {
209
+ "transaction failed": {
210
+ code: "TX_FAILED",
211
+ hint: "Review simulation failure_point for the exact contract and function that failed."
212
+ },
213
+ "bad sequence": {
214
+ code: "BAD_SEQUENCE",
215
+ hint: "Fetch the current account sequence number and rebuild the transaction."
216
+ },
217
+ "tx_bad_auth": {
218
+ code: "BAD_AUTH",
219
+ hint: "Ensure all required signers have signed and auth entries are valid."
220
+ },
221
+ "tx_insufficient_fee": {
222
+ code: "INSUFFICIENT_FEE",
223
+ hint: "Increase the fee to cover minResourceFee from simulation."
224
+ },
225
+ "tx_too_early": {
226
+ code: "TX_TOO_EARLY",
227
+ hint: "Wait until the minTime precondition is satisfied."
228
+ },
229
+ "tx_too_late": {
230
+ code: "TX_TOO_LATE",
231
+ hint: "Rebuild the transaction \u2014 the maxTime precondition has expired."
232
+ },
233
+ "entry_archived": {
234
+ code: "ENTRY_ARCHIVED",
235
+ hint: "Restore the archived ledger entry before submitting."
236
+ },
237
+ "contract_not_found": {
238
+ code: "CONTRACT_NOT_FOUND",
239
+ hint: "Verify the contract address exists on the target network."
240
+ },
241
+ "timeout": {
242
+ code: "RPC_TIMEOUT",
243
+ hint: "Retry the request or check RPC endpoint availability."
244
+ },
245
+ "network": {
246
+ code: "RPC_NETWORK_ERROR",
247
+ hint: "Check network connectivity and RPC endpoint configuration."
248
+ }
249
+ };
250
+ function classifyStellarError(raw, layer) {
251
+ const message = typeof raw === "string" ? raw : raw.message;
252
+ const lower = message.toLowerCase();
253
+ for (const [pattern, classification] of Object.entries(STELLAR_ERROR_TAXONOMY)) {
254
+ if (lower.includes(pattern)) {
255
+ return {
256
+ error: message,
257
+ code: classification.code,
258
+ hint: classification.hint,
259
+ layer
260
+ };
261
+ }
262
+ }
263
+ return {
264
+ error: message,
265
+ code: "STELLAR_UNKNOWN",
266
+ hint: "Check transaction XDR validity and network configuration.",
267
+ layer
268
+ };
269
+ }
270
+ function createMeridianError(error, code, hint, layer) {
271
+ return { error, code, hint, layer };
272
+ }
273
+
274
+ // ../core/src/trace/parser.ts
275
+ import {
276
+ Address,
277
+ FeeBumpTransaction,
278
+ TransactionBuilder,
279
+ humanizeEvents,
280
+ xdr
281
+ } from "@stellar/stellar-sdk";
282
+ var STALENESS_THRESHOLD = 5;
283
+ function parseExecutionPath(txXdr) {
284
+ const steps = [];
285
+ let index = 0;
286
+ try {
287
+ let tx;
288
+ try {
289
+ tx = TransactionBuilder.fromXDR(txXdr, "Public Global Stellar Network ; September 2015");
290
+ } catch {
291
+ tx = TransactionBuilder.fromXDR(txXdr, "Test SDF Network ; September 2015");
292
+ }
293
+ const envelope = tx instanceof FeeBumpTransaction ? tx.innerTransaction : tx;
294
+ for (const op of envelope.operations) {
295
+ steps.push(parseOperation(op, index));
296
+ index++;
297
+ }
298
+ } catch {
299
+ steps.push({
300
+ index: 0,
301
+ type: "classic",
302
+ description: "Unable to parse transaction XDR \u2014 raw simulation only"
303
+ });
304
+ }
305
+ return steps;
306
+ }
307
+ function parseOperation(op, index) {
308
+ if (op.type === "invokeHostFunction") {
309
+ const hostFn = op;
310
+ const fn = hostFn.func;
311
+ let contractId;
312
+ let functionName;
313
+ if (fn && typeof fn === "object" && "invokeContract" in fn) {
314
+ const invoke = fn;
315
+ contractId = invoke.invokeContract?.contractAddress;
316
+ functionName = invoke.invokeContract?.functionName;
317
+ }
318
+ return {
319
+ index,
320
+ type: "invoke",
321
+ contract_id: contractId,
322
+ function_name: functionName,
323
+ description: `Invoke ${functionName ?? "host function"} on ${contractId ?? "unknown contract"}`
324
+ };
325
+ }
326
+ return {
327
+ index,
328
+ type: "classic",
329
+ description: `Classic operation: ${op.type}`
330
+ };
331
+ }
332
+ function parseAuthEntries(events) {
333
+ const entries = [];
334
+ const humanized = humanizeEvents(events);
335
+ for (const event of humanized) {
336
+ const topicStrs = event.topics.map((t) => String(t));
337
+ const topicStr = topicStrs.join(":");
338
+ if (topicStr.includes("require_auth") || topicStr.includes("auth")) {
339
+ entries.push({
340
+ address: event.contractId ?? "unknown",
341
+ contract_id: event.contractId,
342
+ credentials: topicStrs
343
+ });
344
+ }
345
+ }
346
+ return entries;
347
+ }
348
+ function computeFeeEstimate(txXdr, minResourceFee) {
349
+ let classicBaseFee = 100;
350
+ try {
351
+ let tx;
352
+ try {
353
+ tx = TransactionBuilder.fromXDR(txXdr, "Public Global Stellar Network ; September 2015");
354
+ } catch {
355
+ tx = TransactionBuilder.fromXDR(txXdr, "Test SDF Network ; September 2015");
356
+ }
357
+ const envelope = tx instanceof FeeBumpTransaction ? tx.innerTransaction : tx;
358
+ classicBaseFee = Number(envelope.fee) || 100;
359
+ } catch {
360
+ }
361
+ const resourceFee = parseInt(minResourceFee, 10) || 0;
362
+ return {
363
+ classic_base_fee: classicBaseFee,
364
+ min_resource_fee: resourceFee,
365
+ total_fee: classicBaseFee + resourceFee
366
+ };
367
+ }
368
+ function extractResourceUsage(sorobanData) {
369
+ if (!sorobanData) {
370
+ return { cpu_instructions: 0, memory_bytes: 0, read_bytes: 0, write_bytes: 0 };
371
+ }
372
+ const resources = sorobanData.build().resources();
373
+ return {
374
+ cpu_instructions: resources.instructions(),
375
+ memory_bytes: 0,
376
+ read_bytes: resources.readBytes(),
377
+ write_bytes: resources.writeBytes()
378
+ };
379
+ }
380
+ function parseFailurePoint(error, executionPath) {
381
+ const lastInvoke = [...executionPath].reverse().find((s) => s.type === "invoke");
382
+ const errorLower = error.toLowerCase();
383
+ let errorCode = "SIMULATION_FAILED";
384
+ let rootCause = "Transaction simulation failed";
385
+ if (errorLower.includes("require_auth") || errorLower.includes("auth")) {
386
+ errorCode = "AUTH_REQUIRED";
387
+ rootCause = "Missing or invalid authorization credentials";
388
+ } else if (errorLower.includes("archived") || errorLower.includes("ttl")) {
389
+ errorCode = "ENTRY_ARCHIVED";
390
+ rootCause = "Ledger entry is archived or TTL expired";
391
+ } else if (errorLower.includes("insufficient")) {
392
+ errorCode = "INSUFFICIENT_BALANCE";
393
+ rootCause = "Insufficient balance for operation";
394
+ }
395
+ return {
396
+ step_index: lastInvoke?.index ?? 0,
397
+ contract_id: lastInvoke?.contract_id,
398
+ function_name: lastInvoke?.function_name,
399
+ error_code: errorCode,
400
+ error_message: error,
401
+ root_cause: rootCause
402
+ };
403
+ }
404
+ function parseSimulationResult(raw, txXdr) {
405
+ const executionPath = parseExecutionPath(txXdr);
406
+ const stalenessDelta = raw.latestLedger - raw.simulationLedger;
407
+ const isStale = stalenessDelta > STALENESS_THRESHOLD;
408
+ if (!raw.success && raw.error) {
409
+ return {
410
+ success: false,
411
+ failure_point: parseFailurePoint(raw.error, executionPath),
412
+ execution_path: executionPath,
413
+ auth_entries: parseAuthEntries(raw.events),
414
+ fee_estimate: computeFeeEstimate(txXdr, raw.minResourceFee),
415
+ resource_usage: extractResourceUsage(raw.sorobanData),
416
+ staleness_warning: isStale
417
+ };
418
+ }
419
+ return {
420
+ success: true,
421
+ execution_path: executionPath,
422
+ auth_entries: parseAuthEntries(raw.events),
423
+ fee_estimate: computeFeeEstimate(txXdr, raw.minResourceFee),
424
+ resource_usage: extractResourceUsage(raw.sorobanData),
425
+ staleness_warning: isStale
426
+ };
427
+ }
428
+
429
+ // ../core/src/trace/rpc.ts
430
+ import { rpc, TransactionBuilder as TransactionBuilder2, Networks } from "@stellar/stellar-sdk";
431
+ var DEFAULT_TIMEOUT_MS = 3e4;
432
+ function resolveRpcUrl(network) {
433
+ const envKey = network === "mainnet" ? "STELLAR_RPC_MAINNET" : "STELLAR_RPC_TESTNET";
434
+ const url = process.env[envKey];
435
+ if (!url) {
436
+ throw new Error(`Missing environment variable: ${envKey}`);
437
+ }
438
+ return url;
439
+ }
440
+ async function withTimeout(promise, timeoutMs, label) {
441
+ let timer;
442
+ const timeout = new Promise((_, reject) => {
443
+ timer = setTimeout(() => reject(new Error(`timeout: ${label} exceeded ${timeoutMs}ms`)), timeoutMs);
444
+ });
445
+ try {
446
+ return await Promise.race([promise, timeout]);
447
+ } finally {
448
+ clearTimeout(timer);
449
+ }
450
+ }
451
+ async function simulateTransaction(txXdr, rpcUrl, timeoutMs = DEFAULT_TIMEOUT_MS) {
452
+ const server = new rpc.Server(rpcUrl, { allowHttp: rpcUrl.startsWith("http://") });
453
+ try {
454
+ logger.debug("simulateTransaction:start", { rpcUrl });
455
+ const latestLedgerResponse = await withTimeout(
456
+ server.getLatestLedger(),
457
+ timeoutMs,
458
+ "getLatestLedger"
459
+ );
460
+ const transaction = TransactionBuilder2.fromXDR(
461
+ txXdr,
462
+ Networks.TESTNET
463
+ // network passphrase resolved during parse; RPC validates
464
+ );
465
+ const simResponse = await withTimeout(
466
+ server.simulateTransaction(transaction),
467
+ timeoutMs,
468
+ "simulateTransaction"
469
+ );
470
+ if (rpc.Api.isSimulationError(simResponse)) {
471
+ return {
472
+ success: false,
473
+ latestLedger: latestLedgerResponse.sequence,
474
+ simulationLedger: latestLedgerResponse.sequence,
475
+ minResourceFee: "0",
476
+ events: simResponse.events,
477
+ error: simResponse.error
478
+ };
479
+ }
480
+ return {
481
+ success: true,
482
+ latestLedger: latestLedgerResponse.sequence,
483
+ simulationLedger: latestLedgerResponse.sequence,
484
+ minResourceFee: simResponse.minResourceFee,
485
+ events: simResponse.events ?? [],
486
+ sorobanData: simResponse.transactionData
487
+ };
488
+ } catch (err) {
489
+ logger.error("simulateTransaction:failed", {
490
+ error: err instanceof Error ? err.message : String(err)
491
+ });
492
+ return classifyStellarError(err instanceof Error ? err : String(err), "TRACE");
493
+ }
494
+ }
495
+
496
+ // ../core/src/trace/index.ts
497
+ async function trace(txXdr, options) {
498
+ const network = options?.network ?? "testnet";
499
+ const rpcUrl = options?.rpcUrl ?? resolveRpcUrl(network);
500
+ const timeoutMs = options?.timeoutMs ?? 3e4;
501
+ logger.info("trace:start", { network });
502
+ if (!txXdr || txXdr.trim().length === 0) {
503
+ return classifyStellarError("Invalid transaction XDR: empty input", "TRACE");
504
+ }
505
+ const raw = await simulateTransaction(txXdr, rpcUrl, timeoutMs);
506
+ if ("layer" in raw) {
507
+ return raw;
508
+ }
509
+ const result = parseSimulationResult(raw, txXdr);
510
+ logger.info("trace:complete", { success: result.success });
511
+ return result;
512
+ }
513
+
514
+ // ../core/src/analyze.ts
515
+ function resolveEngineVersion() {
516
+ if (true) {
517
+ return "0.1.1";
518
+ }
519
+ const require3 = createRequire2(import.meta.url);
520
+ const { version: version2 } = require3("../package.json");
521
+ return version2;
522
+ }
523
+ var MERIDIAN_VERSION = resolveEngineVersion();
524
+ var DEFAULT_CONFIDENCE_THRESHOLD = 0.75;
525
+ function computeVerdict(traceSuccess, blastRadius, confidence, threshold, isStale) {
526
+ if (!traceSuccess) {
527
+ return { verdict: "ABORT", confidence: Math.min(confidence, 0.95) };
528
+ }
529
+ if (isStale || confidence < threshold) {
530
+ return { verdict: "WARN", confidence };
531
+ }
532
+ if (blastRadius >= 50) {
533
+ return { verdict: "WARN", confidence };
534
+ }
535
+ return { verdict: "CLEAR", confidence };
536
+ }
537
+ function computeConfidence(traceSuccess, manifestCoverage, isStale) {
538
+ let confidence = traceSuccess ? 0.85 : 0.3;
539
+ confidence += manifestCoverage * 0.1;
540
+ if (isStale) confidence -= 0.2;
541
+ return Math.max(0, Math.min(1, Math.round(confidence * 100) / 100));
542
+ }
543
+ function generateFixSequence(traceSuccess, failureRootCause) {
544
+ if (traceSuccess) return void 0;
545
+ return [
546
+ {
547
+ order: 1,
548
+ operation: "diagnose",
549
+ description: failureRootCause ?? "Review simulation failure point",
550
+ estimated_cost_stroops: 0,
551
+ estimated_time_minutes: 5
552
+ },
553
+ {
554
+ order: 2,
555
+ operation: "fix_auth",
556
+ description: "Ensure all require_auth credentials are signed and valid",
557
+ estimated_cost_stroops: 100,
558
+ estimated_time_minutes: 10
559
+ },
560
+ {
561
+ order: 3,
562
+ operation: "resimulate",
563
+ description: "Re-run MERIDIAN analysis after applying fixes",
564
+ estimated_cost_stroops: 0,
565
+ estimated_time_minutes: 2
566
+ }
567
+ ];
568
+ }
569
+ async function analyze(request) {
570
+ const startMs = Date.now();
571
+ const threshold = request.options?.confidence_threshold ?? DEFAULT_CONFIDENCE_THRESHOLD;
572
+ logger.info("analyze:start", { network: request.network });
573
+ const traceResult = await trace(request.tx, {
574
+ network: request.network,
575
+ rpcUrl: request.options?.rpc_url
576
+ });
577
+ if ("layer" in traceResult) {
578
+ return traceResult;
579
+ }
580
+ const context = {
581
+ ledgerSequence: 0,
582
+ latestLedger: 0,
583
+ footprintContracts: extractFootprintContracts(traceResult),
584
+ readOnly: [],
585
+ readWrite: []
586
+ };
587
+ const fieldResult = request.options?.skip_field ? emptyFieldResult() : buildFieldGraph(traceResult, context, {
588
+ network: request.network,
589
+ manifest: request.ecosystem
590
+ });
591
+ const gravityResult = request.options?.skip_gravity ? emptyGravityResult() : scoreGravity(traceResult, fieldResult, { manifest: request.ecosystem });
592
+ const isStale = traceResult.staleness_warning ?? false;
593
+ const confidence = computeConfidence(
594
+ traceResult.success,
595
+ fieldResult.manifest_coverage,
596
+ isStale
597
+ );
598
+ const { verdict } = computeVerdict(
599
+ traceResult.success,
600
+ gravityResult.blast_radius,
601
+ confidence,
602
+ threshold,
603
+ isStale
604
+ );
605
+ const warnings = [];
606
+ if (isStale) warnings.push("Simulation ledger is stale (>5 ledgers behind latest)");
607
+ if (confidence < threshold) {
608
+ warnings.push(`Confidence ${confidence} is below threshold ${threshold}`);
609
+ }
610
+ const fixSequence = generateFixSequence(
611
+ traceResult.success,
612
+ traceResult.failure_point?.root_cause
613
+ );
614
+ return {
615
+ product: "MERIDIAN",
616
+ version: MERIDIAN_VERSION,
617
+ verdict,
618
+ confidence,
619
+ trace: traceResult,
620
+ field: fieldResult,
621
+ gravity: gravityResult,
622
+ fix_sequence: fixSequence,
623
+ warnings: warnings.length > 0 ? warnings : void 0,
624
+ meta: {
625
+ analyzed_at: (/* @__PURE__ */ new Date()).toISOString(),
626
+ ledger_sequence: context.ledgerSequence,
627
+ simulation_stale: isStale,
628
+ network: request.network,
629
+ processing_ms: Date.now() - startMs
630
+ }
631
+ };
632
+ }
633
+ function extractFootprintContracts(trace2) {
634
+ const contracts = /* @__PURE__ */ new Set();
635
+ for (const step of trace2.execution_path) {
636
+ if (step.contract_id) contracts.add(step.contract_id);
637
+ }
638
+ return [...contracts];
639
+ }
640
+ function emptyFieldResult() {
641
+ return {
642
+ contracts_mapped: 0,
643
+ dependency_graph: [],
644
+ ttl_warnings: [],
645
+ manifest_coverage: 0
646
+ };
647
+ }
648
+ function emptyGravityResult() {
649
+ return {
650
+ blast_radius: 0,
651
+ affected_contracts: [],
652
+ critical: [],
653
+ warning: [],
654
+ safe: [],
655
+ monitor: [],
656
+ total_affected_users: 0,
657
+ recovery: "FULL"
658
+ };
659
+ }
660
+
661
+ // ../ai/src/brief.ts
662
+ import Anthropic from "@anthropic-ai/sdk";
663
+ var BRIEF_MODEL = "claude-sonnet-4-6";
664
+ var MAX_BRIEF_WORDS = 300;
665
+ var BRIEF_SYSTEM_PROMPT = `You are MERIDIAN BRIEF \u2014 a Stellar pre-execution risk synthesis engine.
666
+
667
+ Your job: produce a structured mission briefing from TRACE and GRAVITY JSON inputs.
668
+ You must NEVER hallucinate contract names, addresses, ledger values, or user counts.
669
+ Every specific fact must come from the provided structured data.
670
+
671
+ ## Stellar Error Taxonomy
672
+ - AUTH_REQUIRED: Missing or invalid require_auth credentials
673
+ - ENTRY_ARCHIVED: Ledger entry TTL expired or archived
674
+ - INSUFFICIENT_BALANCE: Account lacks required balance
675
+ - BAD_SEQUENCE: Transaction sequence number mismatch
676
+ - TX_FAILED: General simulation failure
677
+
678
+ ## Output Format (strict, max 300 words)
679
+ 1. **Verdict Reason** (1-2 sentences): Why CLEAR, WARN, or ABORT
680
+ 2. **Affected Parties**: Who is impacted and how many users (from GRAVITY data only)
681
+ 3. **Fix Sequence**: Numbered steps with estimated stroop costs (from fix_sequence data)
682
+ 4. **Confidence**: Explain confidence score; if < 0.75, explicitly recommend re-running after fixes
683
+
684
+ Never use conversational prose. Be direct and actionable.`;
685
+ function truncateToWordLimit(text, maxWords) {
686
+ const words = text.trim().split(/\s+/);
687
+ if (words.length <= maxWords) return text;
688
+ return words.slice(0, maxWords).join(" ") + "\u2026";
689
+ }
690
+ function generateFallbackBrief(input) {
691
+ const lines = [];
692
+ lines.push(`**Verdict: ${input.verdict}** (confidence: ${input.confidence})`);
693
+ if (!input.trace.success && input.trace.failure_point) {
694
+ const fp = input.trace.failure_point;
695
+ lines.push(
696
+ `Simulation failed at step ${fp.step_index}: ${fp.root_cause} (${fp.error_code}).`
697
+ );
698
+ } else {
699
+ lines.push("Transaction simulation succeeded with no critical failures detected.");
700
+ }
701
+ if (input.gravity.total_affected_users > 0) {
702
+ lines.push(
703
+ `**Affected Parties**: ${input.gravity.total_affected_users} users across ${input.gravity.affected_contracts.length} contracts.`
704
+ );
705
+ }
706
+ if (input.gravity.critical.length > 0) {
707
+ lines.push(`**Critical contracts**: ${input.gravity.critical.join(", ")}`);
708
+ }
709
+ if (input.fix_sequence && input.fix_sequence.length > 0) {
710
+ lines.push("**Fix Sequence**:");
711
+ for (const step of input.fix_sequence) {
712
+ lines.push(
713
+ `${step.order}. ${step.operation}: ${step.description} (~${step.estimated_cost_stroops} stroops, ~${step.estimated_time_minutes}min)`
714
+ );
715
+ }
716
+ }
717
+ if (input.confidence < 0.75) {
718
+ lines.push(
719
+ `**Confidence**: ${input.confidence} is below the 0.75 threshold. Re-run analysis after applying fixes.`
720
+ );
721
+ }
722
+ if (input.warnings && input.warnings.length > 0) {
723
+ lines.push(`**Warnings**: ${input.warnings.join("; ")}`);
724
+ }
725
+ return truncateToWordLimit(lines.join("\n"), MAX_BRIEF_WORDS);
726
+ }
727
+ async function synthesizeBrief(input, options) {
728
+ const apiKey = options?.apiKey ?? process.env.ANTHROPIC_API_KEY;
729
+ const maxWords = options?.maxWords ?? MAX_BRIEF_WORDS;
730
+ if (!apiKey) {
731
+ return generateFallbackBrief(input);
732
+ }
733
+ const client = new Anthropic({ apiKey });
734
+ const contextPayload = {
735
+ verdict: input.verdict,
736
+ confidence: input.confidence,
737
+ trace: {
738
+ success: input.trace.success,
739
+ failure_point: input.trace.failure_point,
740
+ execution_path: input.trace.execution_path,
741
+ auth_entries: input.trace.auth_entries,
742
+ fee_estimate: input.trace.fee_estimate,
743
+ staleness_warning: input.trace.staleness_warning
744
+ },
745
+ gravity: {
746
+ blast_radius: input.gravity.blast_radius,
747
+ affected_contracts: input.gravity.affected_contracts,
748
+ critical: input.gravity.critical,
749
+ warning: input.gravity.warning,
750
+ total_affected_users: input.gravity.total_affected_users,
751
+ recovery: input.gravity.recovery
752
+ },
753
+ fix_sequence: input.fix_sequence,
754
+ warnings: input.warnings
755
+ };
756
+ try {
757
+ const response = await client.messages.create({
758
+ model: BRIEF_MODEL,
759
+ max_tokens: 1024,
760
+ system: BRIEF_SYSTEM_PROMPT,
761
+ messages: [
762
+ {
763
+ role: "user",
764
+ content: `Synthesize a MERIDIAN risk brief from this structured analysis data:
765
+
766
+ ${JSON.stringify(contextPayload, null, 2)}`
767
+ }
768
+ ]
769
+ });
770
+ const textBlock = response.content.find((block) => block.type === "text");
771
+ if (!textBlock || textBlock.type !== "text") {
772
+ return createMeridianError(
773
+ "BRIEF synthesis returned no text content",
774
+ "BRIEF_EMPTY",
775
+ "Retry the analysis or check Anthropic API status",
776
+ "BRIEF"
777
+ );
778
+ }
779
+ return truncateToWordLimit(textBlock.text, maxWords);
780
+ } catch (err) {
781
+ const message = err instanceof Error ? err.message : String(err);
782
+ return createMeridianError(
783
+ `BRIEF synthesis failed: ${message}`,
784
+ "BRIEF_API_ERROR",
785
+ "Check ANTHROPIC_API_KEY and retry. Fallback brief will be used.",
786
+ "BRIEF"
787
+ );
788
+ }
789
+ }
790
+
791
+ // src/lib/input.ts
792
+ import { readFile } from "node:fs/promises";
793
+ async function readStdin() {
794
+ const chunks = [];
795
+ for await (const chunk of process.stdin) {
796
+ chunks.push(Buffer.from(chunk));
797
+ }
798
+ return Buffer.concat(chunks).toString("utf-8").trim();
799
+ }
800
+ async function resolveTxInput(txArg, filePath) {
801
+ if (filePath) {
802
+ const contents = await readFile(filePath, "utf-8");
803
+ return contents.trim();
804
+ }
805
+ if (txArg && txArg.trim().length > 0) {
806
+ return txArg.trim();
807
+ }
808
+ if (!process.stdin.isTTY) {
809
+ const fromStdin = await readStdin();
810
+ if (fromStdin.length > 0) return fromStdin;
811
+ }
812
+ throw new Error(
813
+ "No transaction XDR provided. Pass it as an argument, via --file <path>, or pipe it over stdin."
814
+ );
815
+ }
816
+
817
+ // src/lib/manifest.ts
818
+ import { readFile as readFile2 } from "node:fs/promises";
819
+ async function loadManifest(filePath) {
820
+ if (!filePath) return void 0;
821
+ let raw;
822
+ try {
823
+ raw = await readFile2(filePath, "utf-8");
824
+ } catch (err) {
825
+ const message = err instanceof Error ? err.message : String(err);
826
+ throw new Error(`Failed to read ecosystem manifest at ${filePath}: ${message}`);
827
+ }
828
+ let parsed;
829
+ try {
830
+ parsed = JSON.parse(raw);
831
+ } catch (err) {
832
+ const message = err instanceof Error ? err.message : String(err);
833
+ throw new Error(`Ecosystem manifest at ${filePath} is not valid JSON: ${message}`);
834
+ }
835
+ if (typeof parsed !== "object" || parsed === null || !Array.isArray(parsed.contracts)) {
836
+ throw new Error(
837
+ `Ecosystem manifest at ${filePath} must be an object with a "contracts" array.`
838
+ );
839
+ }
840
+ return parsed;
841
+ }
842
+
843
+ // src/lib/errors.ts
844
+ import pc from "picocolors";
845
+ function isMeridianError(value) {
846
+ return typeof value === "object" && value !== null && "layer" in value && "code" in value && "hint" in value;
847
+ }
848
+ function failWithMeridianError(error) {
849
+ console.error(pc.red(pc.bold(`\u2716 [${error.layer}] ${error.code}`)));
850
+ console.error(pc.red(error.error));
851
+ console.error(pc.dim(`hint: ${error.hint}`));
852
+ process.exit(1);
853
+ }
854
+ function failWithError(err) {
855
+ const message = err instanceof Error ? err.message : String(err);
856
+ console.error(pc.red(pc.bold("\u2716 Error")));
857
+ console.error(pc.red(message));
858
+ process.exit(1);
859
+ }
860
+
861
+ // src/lib/output.ts
862
+ import pc2 from "picocolors";
863
+ function printJson(value) {
864
+ console.log(JSON.stringify(value, null, 2));
865
+ }
866
+ function verdictBadge(verdict) {
867
+ switch (verdict) {
868
+ case "CLEAR":
869
+ return pc2.bgGreen(pc2.black(" CLEAR "));
870
+ case "WARN":
871
+ return pc2.bgYellow(pc2.black(" WARN "));
872
+ case "ABORT":
873
+ return pc2.bgRed(pc2.white(pc2.bold(" ABORT ")));
874
+ default:
875
+ return verdict;
876
+ }
877
+ }
878
+ function section(title) {
879
+ console.log("");
880
+ console.log(pc2.bold(pc2.cyan(`\u2500\u2500 ${title} `.padEnd(48, "\u2500"))));
881
+ }
882
+ function field(label, value) {
883
+ console.log(` ${pc2.dim(label + ":")} ${value}`);
884
+ }
885
+ function printTrace(trace2) {
886
+ section("TRACE");
887
+ field("success", trace2.success ? pc2.green("true") : pc2.red("false"));
888
+ if (trace2.staleness_warning) {
889
+ field("staleness_warning", pc2.yellow("true"));
890
+ }
891
+ if (trace2.failure_point) {
892
+ const fp = trace2.failure_point;
893
+ console.log(` ${pc2.red("failure_point")}:`);
894
+ field(" step_index", fp.step_index);
895
+ if (fp.contract_id) field(" contract_id", fp.contract_id);
896
+ if (fp.function_name) field(" function_name", fp.function_name);
897
+ field(" error_code", fp.error_code);
898
+ field(" root_cause", fp.root_cause);
899
+ }
900
+ field("execution_path", `${trace2.execution_path.length} step(s)`);
901
+ field("auth_entries", `${trace2.auth_entries.length} entrie(s)`);
902
+ field(
903
+ "fee_estimate",
904
+ `total=${trace2.fee_estimate.total_fee} base=${trace2.fee_estimate.classic_base_fee} min_resource=${trace2.fee_estimate.min_resource_fee}`
905
+ );
906
+ field(
907
+ "resource_usage",
908
+ `cpu=${trace2.resource_usage.cpu_instructions} mem=${trace2.resource_usage.memory_bytes}b read=${trace2.resource_usage.read_bytes}b write=${trace2.resource_usage.write_bytes}b`
909
+ );
910
+ }
911
+ function printField(result) {
912
+ section("FIELD");
913
+ field("contracts_mapped", result.contracts_mapped);
914
+ field("manifest_coverage", `${Math.round(result.manifest_coverage * 100)}%`);
915
+ if (result.ttl_warnings.length > 0) {
916
+ console.log(` ${pc2.yellow("ttl_warnings")}:`);
917
+ for (const warning of result.ttl_warnings) {
918
+ console.log(
919
+ ` - ${warning.contract_id} (${warning.ledger_key}) ttl_remaining=${warning.ttl_remaining} [${warning.severity}]`
920
+ );
921
+ }
922
+ }
923
+ if (result.dependency_graph.length > 0) {
924
+ console.log(` ${pc2.dim("dependency_graph")}:`);
925
+ for (const node of result.dependency_graph) {
926
+ const label = node.name ? `${node.name} (${node.address})` : node.address;
927
+ const deps = node.dependencies.length > 0 ? ` \u2192 ${node.dependencies.join(", ")}` : "";
928
+ console.log(` - ${label}${deps}`);
929
+ }
930
+ }
931
+ }
932
+ function printGravity(result) {
933
+ section("GRAVITY");
934
+ field("blast_radius", result.blast_radius);
935
+ field("total_affected_users", result.total_affected_users);
936
+ field("recovery", result.recovery);
937
+ if (result.critical.length > 0) field("critical", pc2.red(result.critical.join(", ")));
938
+ if (result.warning.length > 0) field("warning", pc2.yellow(result.warning.join(", ")));
939
+ if (result.monitor.length > 0) field("monitor", pc2.blue(result.monitor.join(", ")));
940
+ if (result.safe.length > 0) field("safe", pc2.green(result.safe.join(", ")));
941
+ if (result.affected_contracts.length > 0) {
942
+ console.log(` ${pc2.dim("affected_contracts")}:`);
943
+ for (const contract of result.affected_contracts) {
944
+ const label = contract.name ? `${contract.name} (${contract.address})` : contract.address;
945
+ console.log(` - [${contract.impact}] ${label} \u2014 ${contract.reason}`);
946
+ }
947
+ }
948
+ }
949
+ function printAnalysis(response) {
950
+ console.log("");
951
+ console.log(`${pc2.bold("MERIDIAN")} v${response.version} ${verdictBadge(response.verdict)} confidence=${response.confidence}`);
952
+ printTrace(response.trace);
953
+ printField(response.field);
954
+ printGravity(response.gravity);
955
+ if (response.fix_sequence && response.fix_sequence.length > 0) {
956
+ section("FIX SEQUENCE");
957
+ for (const step of response.fix_sequence) {
958
+ console.log(
959
+ ` ${pc2.bold(String(step.order) + ".")} ${step.operation} \u2014 ${step.description} ${pc2.dim(`(~${step.estimated_cost_stroops} stroops, ~${step.estimated_time_minutes}min)`)}`
960
+ );
961
+ }
962
+ }
963
+ if (response.warnings && response.warnings.length > 0) {
964
+ section("WARNINGS");
965
+ for (const warning of response.warnings) {
966
+ console.log(` ${pc2.yellow("\u26A0")} ${warning}`);
967
+ }
968
+ }
969
+ section("BRIEF");
970
+ console.log(response.brief);
971
+ section("META");
972
+ field("analyzed_at", response.meta.analyzed_at);
973
+ field("network", response.meta.network);
974
+ field("ledger_sequence", response.meta.ledger_sequence);
975
+ field("simulation_stale", response.meta.simulation_stale);
976
+ field("processing_ms", response.meta.processing_ms);
977
+ console.log("");
978
+ }
979
+
980
+ // src/lib/options.ts
981
+ import { InvalidArgumentError } from "commander";
982
+ function parseNetwork(value) {
983
+ if (value !== "mainnet" && value !== "testnet") {
984
+ throw new InvalidArgumentError('Network must be "mainnet" or "testnet".');
985
+ }
986
+ return value;
987
+ }
988
+ function parseThreshold(value) {
989
+ const parsed = Number.parseFloat(value);
990
+ if (Number.isNaN(parsed) || parsed < 0 || parsed > 1) {
991
+ throw new InvalidArgumentError("Confidence threshold must be a number between 0 and 1.");
992
+ }
993
+ return parsed;
994
+ }
995
+ function withCommonOptions(command) {
996
+ return command.argument("[tx]", "Base64-encoded transaction XDR").option("-n, --network <network>", "Stellar network (mainnet | testnet)", parseNetwork, "testnet").option("--rpc-url <url>", "Override the Soroban RPC endpoint (else read from env)").option("-f, --file <path>", "Read the transaction XDR from a file instead of an argument").option("-e, --ecosystem <path>", "Path to an ecosystem manifest JSON file").option("--json", "Print raw JSON instead of a formatted report");
997
+ }
998
+
999
+ // src/commands/analyze.ts
1000
+ function analyzeCommand() {
1001
+ const command = new Command2("analyze").description(
1002
+ "Run the full MERIDIAN pipeline (TRACE + FIELD + GRAVITY + BRIEF) on a transaction"
1003
+ );
1004
+ withCommonOptions(command).option("--skip-field", "Skip the FIELD dependency-mapping layer").option("--skip-gravity", "Skip the GRAVITY blast-radius layer").option("--confidence-threshold <n>", "Minimum confidence required for a CLEAR verdict", parseThreshold).option("--no-brief", "Skip GenAI BRIEF synthesis (structured layers only)").option("--api-key <key>", "Anthropic API key for BRIEF synthesis (else read from env)").action(async (tx, options) => {
1005
+ try {
1006
+ const txXdr = await resolveTxInput(tx, options.file);
1007
+ const ecosystem = await loadManifest(options.ecosystem);
1008
+ const result = await analyze({
1009
+ tx: txXdr,
1010
+ network: options.network,
1011
+ ecosystem,
1012
+ options: {
1013
+ skip_field: options.skipField,
1014
+ skip_gravity: options.skipGravity,
1015
+ confidence_threshold: options.confidenceThreshold,
1016
+ rpc_url: options.rpcUrl
1017
+ }
1018
+ });
1019
+ if (isMeridianError(result)) {
1020
+ if (options.json) {
1021
+ printJson(result);
1022
+ process.exit(1);
1023
+ }
1024
+ failWithMeridianError(result);
1025
+ }
1026
+ let brief = "BRIEF synthesis skipped (--no-brief).";
1027
+ let warnings = result.warnings;
1028
+ if (options.brief) {
1029
+ const briefInput = {
1030
+ verdict: result.verdict,
1031
+ confidence: result.confidence,
1032
+ trace: result.trace,
1033
+ field: result.field,
1034
+ gravity: result.gravity,
1035
+ fix_sequence: result.fix_sequence,
1036
+ warnings: result.warnings
1037
+ };
1038
+ const briefResult = await synthesizeBrief(briefInput, { apiKey: options.apiKey });
1039
+ if (isMeridianError(briefResult)) {
1040
+ brief = generateFallbackBrief(briefInput);
1041
+ warnings = [...result.warnings ?? [], briefResult.error];
1042
+ } else {
1043
+ brief = briefResult;
1044
+ }
1045
+ }
1046
+ const response = { ...result, brief, warnings };
1047
+ if (options.json) {
1048
+ printJson(response);
1049
+ return;
1050
+ }
1051
+ printAnalysis(response);
1052
+ } catch (err) {
1053
+ failWithError(err);
1054
+ }
1055
+ });
1056
+ return command;
1057
+ }
1058
+
1059
+ // src/commands/trace.ts
1060
+ import { Command as Command3 } from "commander";
1061
+ function traceCommand() {
1062
+ const command = new Command3("trace").description(
1063
+ "Run the TRACE engine only \u2014 simulate a transaction and report the execution path"
1064
+ );
1065
+ withCommonOptions(command).action(async (tx, options) => {
1066
+ try {
1067
+ const txXdr = await resolveTxInput(tx, options.file);
1068
+ const result = await trace(txXdr, { network: options.network, rpcUrl: options.rpcUrl });
1069
+ if (isMeridianError(result)) {
1070
+ if (options.json) {
1071
+ printJson(result);
1072
+ process.exit(1);
1073
+ }
1074
+ failWithMeridianError(result);
1075
+ }
1076
+ if (options.json) {
1077
+ printJson(result);
1078
+ return;
1079
+ }
1080
+ printTrace(result);
1081
+ } catch (err) {
1082
+ failWithError(err);
1083
+ }
1084
+ });
1085
+ return command;
1086
+ }
1087
+
1088
+ // src/commands/field.ts
1089
+ import { Command as Command4 } from "commander";
1090
+ function fieldCommand() {
1091
+ const command = new Command4("field").description(
1092
+ "Run TRACE + FIELD \u2014 map the dependency graph touched by a transaction"
1093
+ );
1094
+ withCommonOptions(command).action(async (tx, options) => {
1095
+ try {
1096
+ const txXdr = await resolveTxInput(tx, options.file);
1097
+ const manifest = await loadManifest(options.ecosystem);
1098
+ const traceResult = await trace(txXdr, { network: options.network, rpcUrl: options.rpcUrl });
1099
+ if (isMeridianError(traceResult)) {
1100
+ if (options.json) {
1101
+ printJson(traceResult);
1102
+ process.exit(1);
1103
+ }
1104
+ failWithMeridianError(traceResult);
1105
+ }
1106
+ const fieldResult = buildFieldGraph(
1107
+ traceResult,
1108
+ {
1109
+ ledgerSequence: 0,
1110
+ latestLedger: 0,
1111
+ footprintContracts: [],
1112
+ readOnly: [],
1113
+ readWrite: []
1114
+ },
1115
+ { network: options.network, manifest }
1116
+ );
1117
+ if (options.json) {
1118
+ printJson(fieldResult);
1119
+ return;
1120
+ }
1121
+ printField(fieldResult);
1122
+ } catch (err) {
1123
+ failWithError(err);
1124
+ }
1125
+ });
1126
+ return command;
1127
+ }
1128
+
1129
+ // src/commands/gravity.ts
1130
+ import { Command as Command5 } from "commander";
1131
+ function gravityCommand() {
1132
+ const command = new Command5("gravity").description(
1133
+ "Run TRACE + FIELD + GRAVITY \u2014 score the blast radius of a transaction"
1134
+ );
1135
+ withCommonOptions(command).action(
1136
+ async (tx, options) => {
1137
+ try {
1138
+ const txXdr = await resolveTxInput(tx, options.file);
1139
+ const manifest = await loadManifest(options.ecosystem);
1140
+ const traceResult = await trace(txXdr, { network: options.network, rpcUrl: options.rpcUrl });
1141
+ if (isMeridianError(traceResult)) {
1142
+ if (options.json) {
1143
+ printJson(traceResult);
1144
+ process.exit(1);
1145
+ }
1146
+ failWithMeridianError(traceResult);
1147
+ }
1148
+ const fieldResult = buildFieldGraph(
1149
+ traceResult,
1150
+ {
1151
+ ledgerSequence: 0,
1152
+ latestLedger: 0,
1153
+ footprintContracts: [],
1154
+ readOnly: [],
1155
+ readWrite: []
1156
+ },
1157
+ { network: options.network, manifest }
1158
+ );
1159
+ const gravityResult = scoreGravity(traceResult, fieldResult, { manifest });
1160
+ if (options.json) {
1161
+ printJson(gravityResult);
1162
+ return;
1163
+ }
1164
+ printGravity(gravityResult);
1165
+ } catch (err) {
1166
+ failWithError(err);
1167
+ }
1168
+ }
1169
+ );
1170
+ return command;
1171
+ }
1172
+
1173
+ // src/commands/version.ts
1174
+ import { Command as Command6 } from "commander";
1175
+ function versionCommand() {
1176
+ return new Command6("version").description("Print MERIDIAN CLI and core engine version").option("--json", "Print raw JSON instead of a formatted report").action((options) => {
1177
+ if (options.json) {
1178
+ printJson({ product: "MERIDIAN", cli_version: CLI_VERSION, engine_version: MERIDIAN_VERSION });
1179
+ return;
1180
+ }
1181
+ console.log(`meridian-core v${CLI_VERSION} (engine v${MERIDIAN_VERSION})`);
1182
+ });
1183
+ }
1184
+
1185
+ // src/cli.ts
1186
+ function buildProgram() {
1187
+ const program = new Command7("meridian").description(
1188
+ "MERIDIAN \u2014 pre-execution intelligence for Stellar developers.\nKnow what crosses before it does."
1189
+ ).version(CLI_VERSION, "-v, --version", "Print the meridian-core CLI version").addHelpText(
1190
+ "after",
1191
+ `
1192
+ Examples:
1193
+ $ meridian analyze <base64-xdr> --network testnet
1194
+ $ cat tx.xdr | meridian analyze --network mainnet --json
1195
+ $ meridian trace --file tx.xdr --network testnet
1196
+ $ meridian gravity <base64-xdr> --ecosystem manifest.json
1197
+
1198
+ Environment:
1199
+ STELLAR_RPC_TESTNET Soroban RPC endpoint for testnet (or use --rpc-url)
1200
+ STELLAR_RPC_MAINNET Soroban RPC endpoint for mainnet (or use --rpc-url)
1201
+ ANTHROPIC_API_KEY Claude API key for BRIEF synthesis (optional, falls back to a deterministic brief)
1202
+ `
1203
+ );
1204
+ program.addCommand(analyzeCommand(), { isDefault: true });
1205
+ program.addCommand(traceCommand());
1206
+ program.addCommand(fieldCommand());
1207
+ program.addCommand(gravityCommand());
1208
+ program.addCommand(versionCommand());
1209
+ return program;
1210
+ }
1211
+ async function runCli(argv = process.argv) {
1212
+ const program = buildProgram();
1213
+ await program.parseAsync(argv);
1214
+ }
1215
+
1216
+ // src/index.ts
3
1217
  runCli().catch((err) => {
4
- console.error(err instanceof Error ? err.message : err);
5
- process.exit(1);
1218
+ console.error(err instanceof Error ? err.message : err);
1219
+ process.exit(1);
6
1220
  });
7
- //# sourceMappingURL=index.js.map