@secondlayer/cli 18.0.0 → 18.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.js +205 -11
- package/dist/cli.js.map +3 -3
- package/package.json +2 -2
package/dist/cli.js
CHANGED
|
@@ -29430,7 +29430,7 @@ var init_git_status = () => {};
|
|
|
29430
29430
|
|
|
29431
29431
|
// ../../node_modules/typescript/lib/typescript.js
|
|
29432
29432
|
var require_typescript = __commonJS(function(exports, module) {
|
|
29433
|
-
var __dirname = "/Users/ryan/projects/secondlayer/node_modules/typescript/lib", __filename = "/Users/ryan/projects/secondlayer/node_modules/typescript/lib/typescript.js";
|
|
29433
|
+
var __dirname = "/Users/ryan/projects/secondlayer/.claude/worktrees/agent-a6a189c1bc1696e94/node_modules/typescript/lib", __filename = "/Users/ryan/projects/secondlayer/.claude/worktrees/agent-a6a189c1bc1696e94/node_modules/typescript/lib/typescript.js";
|
|
29434
29434
|
/*! *****************************************************************************
|
|
29435
29435
|
Copyright (c) Microsoft Corporation. All rights reserved.
|
|
29436
29436
|
Licensed under the Apache License, Version 2.0 (the "License"); you may not use
|
|
@@ -202495,7 +202495,7 @@ var {
|
|
|
202495
202495
|
// package.json
|
|
202496
202496
|
var package_default = {
|
|
202497
202497
|
name: "@secondlayer/cli",
|
|
202498
|
-
version: "18.
|
|
202498
|
+
version: "18.1.0",
|
|
202499
202499
|
description: "Run a Stacks index on your own hardware — set it up, restore verified history, and keep it current.",
|
|
202500
202500
|
repository: {
|
|
202501
202501
|
type: "git",
|
|
@@ -202535,7 +202535,7 @@ var package_default = {
|
|
|
202535
202535
|
"@secondlayer/bundler": "^0.6.1",
|
|
202536
202536
|
"@secondlayer/scaffold": "^1.6.1",
|
|
202537
202537
|
"@secondlayer/sdk": "^13.0.0",
|
|
202538
|
-
"@secondlayer/shared": "^11.
|
|
202538
|
+
"@secondlayer/shared": "^11.15.1",
|
|
202539
202539
|
"@secondlayer/stacks": "^6.1.0",
|
|
202540
202540
|
"@secondlayer/subgraphs": "^6.2.5",
|
|
202541
202541
|
esbuild: "^0.28.2",
|
|
@@ -203516,6 +203516,193 @@ async function reverify(db, reference, ranges) {
|
|
|
203516
203516
|
}
|
|
203517
203517
|
return remaining;
|
|
203518
203518
|
}
|
|
203519
|
+
function findChildPartition(partitions, dataset, fromBlock, toBlock) {
|
|
203520
|
+
return partitions.find((p) => p.dataset === dataset && p.from_block === fromBlock && p.to_block === toBlock);
|
|
203521
|
+
}
|
|
203522
|
+
function expectedZeroRows(zeroRecordRanges, dataset, fromBlock, toBlock) {
|
|
203523
|
+
return zeroRecordRanges.some((r) => r.dataset === dataset && r.from_block === fromBlock && r.to_block === toBlock);
|
|
203524
|
+
}
|
|
203525
|
+
async function planChildPartitionDivergence(db, partitions, zeroRecordRanges, inRange) {
|
|
203526
|
+
const ranges = new Map;
|
|
203527
|
+
for (const p of partitions) {
|
|
203528
|
+
if (p.dataset !== "transactions" && p.dataset !== "events")
|
|
203529
|
+
continue;
|
|
203530
|
+
if (!inRange(p.from_block, p.to_block))
|
|
203531
|
+
continue;
|
|
203532
|
+
ranges.set(`${p.from_block}-${p.to_block}`, {
|
|
203533
|
+
from: p.from_block,
|
|
203534
|
+
to: p.to_block
|
|
203535
|
+
});
|
|
203536
|
+
}
|
|
203537
|
+
const mismatches = [];
|
|
203538
|
+
for (const { from, to } of ranges.values()) {
|
|
203539
|
+
const txPartition = findChildPartition(partitions, "transactions", from, to);
|
|
203540
|
+
const evPartition = findChildPartition(partitions, "events", from, to);
|
|
203541
|
+
const expectedTx = txPartition?.row_count ?? (expectedZeroRows(zeroRecordRanges, "transactions", from, to) ? 0 : null);
|
|
203542
|
+
const expectedEv = evPartition?.row_count ?? (expectedZeroRows(zeroRecordRanges, "events", from, to) ? 0 : null);
|
|
203543
|
+
if (expectedTx === null && expectedEv === null)
|
|
203544
|
+
continue;
|
|
203545
|
+
const actualTx = (await computeRangeDigest2(db, "transactions", from, to)).row_count;
|
|
203546
|
+
const actualEv = (await computeRangeDigest2(db, "events", from, to)).row_count;
|
|
203547
|
+
if (expectedTx !== null && actualTx !== expectedTx || expectedEv !== null && actualEv !== expectedEv) {
|
|
203548
|
+
mismatches.push({
|
|
203549
|
+
from_block: from,
|
|
203550
|
+
to_block: to,
|
|
203551
|
+
transactions: { expected: expectedTx ?? actualTx, actual: actualTx },
|
|
203552
|
+
events: { expected: expectedEv ?? actualEv, actual: actualEv }
|
|
203553
|
+
});
|
|
203554
|
+
}
|
|
203555
|
+
}
|
|
203556
|
+
return mismatches;
|
|
203557
|
+
}
|
|
203558
|
+
async function applyChildRangeFix(rawClient, reference, mismatch, gate) {
|
|
203559
|
+
const partitions = reference.manifest.partitions ?? [];
|
|
203560
|
+
const txPartition = findChildPartition(partitions, "transactions", mismatch.from_block, mismatch.to_block);
|
|
203561
|
+
const evPartition = findChildPartition(partitions, "events", mismatch.from_block, mismatch.to_block);
|
|
203562
|
+
const txBytes = txPartition ? await fetchVerifiedPartition(reference, txPartition, gate) : null;
|
|
203563
|
+
const evBytes = evPartition ? await fetchVerifiedPartition(reference, evPartition, gate) : null;
|
|
203564
|
+
const written = { transactions: 0, events: 0 };
|
|
203565
|
+
await rawClient.begin(async (tx) => {
|
|
203566
|
+
await tx.unsafe("DELETE FROM events WHERE block_height BETWEEN $1 AND $2", [
|
|
203567
|
+
mismatch.from_block,
|
|
203568
|
+
mismatch.to_block
|
|
203569
|
+
]);
|
|
203570
|
+
await tx.unsafe("DELETE FROM transactions WHERE block_height BETWEEN $1 AND $2", [mismatch.from_block, mismatch.to_block]);
|
|
203571
|
+
if (txBytes) {
|
|
203572
|
+
const writable = await tx.unsafe(copyStatement2("transactions")).writable();
|
|
203573
|
+
written.transactions = await writeRowsToCopyStream2({
|
|
203574
|
+
writable,
|
|
203575
|
+
dataset: "transactions",
|
|
203576
|
+
rows: readPartitionRows(txBytes, `repair-transactions-${mismatch.from_block}`)
|
|
203577
|
+
});
|
|
203578
|
+
}
|
|
203579
|
+
if (evBytes) {
|
|
203580
|
+
const writable = await tx.unsafe(copyStatement2("events")).writable();
|
|
203581
|
+
written.events = await writeRowsToCopyStream2({
|
|
203582
|
+
writable,
|
|
203583
|
+
dataset: "events",
|
|
203584
|
+
rows: readPartitionRows(evBytes, `repair-events-${mismatch.from_block}`)
|
|
203585
|
+
});
|
|
203586
|
+
}
|
|
203587
|
+
});
|
|
203588
|
+
return written;
|
|
203589
|
+
}
|
|
203590
|
+
async function runChildOnlyRepair(params) {
|
|
203591
|
+
const { reference, childMismatches, apply, json, yes } = params;
|
|
203592
|
+
if (childMismatches.length === 0) {
|
|
203593
|
+
success("Nothing to repair — local data matches the archive.");
|
|
203594
|
+
output({ json, data: { status: "clean", fixes: [] }, human: () => {} });
|
|
203595
|
+
process.exit(REPAIR_EXIT.OK);
|
|
203596
|
+
}
|
|
203597
|
+
const partitions = reference.manifest.partitions ?? [];
|
|
203598
|
+
const fetchPaths = childMismatches.flatMap((m) => {
|
|
203599
|
+
const tx = findChildPartition(partitions, "transactions", m.from_block, m.to_block);
|
|
203600
|
+
const ev = findChildPartition(partitions, "events", m.from_block, m.to_block);
|
|
203601
|
+
return [tx?.path, ev?.path].filter((p) => p !== undefined);
|
|
203602
|
+
});
|
|
203603
|
+
let gate;
|
|
203604
|
+
let quoteLine;
|
|
203605
|
+
if (apply && isOfficialArchive(reference)) {
|
|
203606
|
+
const result = await quoteArchiveFetch(fetchPaths, "repair");
|
|
203607
|
+
if (!result.ok) {
|
|
203608
|
+
printError(result.kind === "not_configured" ? ARCHIVE_GATE_NOT_CONFIGURED_MESSAGE : result.message);
|
|
203609
|
+
process.exit(REPAIR_EXIT.UNANCHORED);
|
|
203610
|
+
}
|
|
203611
|
+
if (!result.quote.sufficient) {
|
|
203612
|
+
printError(formatInsufficientMessage(result.quote));
|
|
203613
|
+
process.exit(REPAIR_EXIT.UNANCHORED);
|
|
203614
|
+
}
|
|
203615
|
+
quoteLine = formatQuoteValue(result.quote, "repair");
|
|
203616
|
+
note(` metered: ${quoteLine}`);
|
|
203617
|
+
if (shouldPromptForGatedFetch({ yes })) {
|
|
203618
|
+
if (json) {
|
|
203619
|
+
writeData(JSON.stringify(confirmationRequiredPayload(result.quote)));
|
|
203620
|
+
process.exit(REPAIR_EXIT.UNANCHORED);
|
|
203621
|
+
}
|
|
203622
|
+
const proceed = await confirmDestructive({
|
|
203623
|
+
message: `Fetch ${fetchPaths.length} partition(s) from the archive?`,
|
|
203624
|
+
yes
|
|
203625
|
+
});
|
|
203626
|
+
if (!proceed) {
|
|
203627
|
+
note("Nothing was fetched.");
|
|
203628
|
+
process.exit(REPAIR_EXIT.UNANCHORED);
|
|
203629
|
+
}
|
|
203630
|
+
}
|
|
203631
|
+
gate = createGatedFetcher(fetchPaths, "repair");
|
|
203632
|
+
}
|
|
203633
|
+
const applied = { transactions: 0, events: 0 };
|
|
203634
|
+
if (apply) {
|
|
203635
|
+
const rawClient = getRawClient2("source");
|
|
203636
|
+
for (const mismatch of childMismatches) {
|
|
203637
|
+
const result = await applyChildRangeFix(rawClient, reference, mismatch, gate);
|
|
203638
|
+
applied.transactions += result.transactions;
|
|
203639
|
+
applied.events += result.events;
|
|
203640
|
+
}
|
|
203641
|
+
}
|
|
203642
|
+
const db = getDb2();
|
|
203643
|
+
let remaining = childMismatches.length;
|
|
203644
|
+
if (apply) {
|
|
203645
|
+
remaining = 0;
|
|
203646
|
+
for (const mismatch of childMismatches) {
|
|
203647
|
+
const [actualTx, actualEv] = await Promise.all([
|
|
203648
|
+
computeRangeDigest2(db, "transactions", mismatch.from_block, mismatch.to_block),
|
|
203649
|
+
computeRangeDigest2(db, "events", mismatch.from_block, mismatch.to_block)
|
|
203650
|
+
]);
|
|
203651
|
+
if (actualTx.row_count !== mismatch.transactions.expected || actualEv.row_count !== mismatch.events.expected) {
|
|
203652
|
+
remaining++;
|
|
203653
|
+
}
|
|
203654
|
+
}
|
|
203655
|
+
}
|
|
203656
|
+
const complete = apply && remaining === 0;
|
|
203657
|
+
const report = {
|
|
203658
|
+
status: apply ? complete ? "repaired" : "incomplete" : "plan",
|
|
203659
|
+
reference: reference.origin,
|
|
203660
|
+
divergent_ranges: 0,
|
|
203661
|
+
blocks_to_replace: 0,
|
|
203662
|
+
blocks_to_insert: 0,
|
|
203663
|
+
local_only_heights: [],
|
|
203664
|
+
applied: 0,
|
|
203665
|
+
rows_written: { blocks: 0, ...applied },
|
|
203666
|
+
datasets_rewritten: apply ? ["transactions", "events"] : [],
|
|
203667
|
+
heights_missing_child_partitions: [],
|
|
203668
|
+
child_partition_mismatches: childMismatches,
|
|
203669
|
+
fixes: [],
|
|
203670
|
+
metered: quoteLine ?? null
|
|
203671
|
+
};
|
|
203672
|
+
output({
|
|
203673
|
+
json,
|
|
203674
|
+
data: report,
|
|
203675
|
+
human: () => {
|
|
203676
|
+
if (apply) {
|
|
203677
|
+
const written = `${applied.transactions} transactions, ${applied.events} events`;
|
|
203678
|
+
if (complete) {
|
|
203679
|
+
success(`Repaired ${childMismatches.length} range(s) (rewrote ${written}); blocks were already correct.`);
|
|
203680
|
+
} else {
|
|
203681
|
+
warn(`Rewrote ${written} but ${remaining} range(s) still diverge from the archive.`);
|
|
203682
|
+
}
|
|
203683
|
+
} else {
|
|
203684
|
+
warn(`${childMismatches.length} range(s) have transactions/events that diverge from the archive, while their blocks match.`);
|
|
203685
|
+
console.error("");
|
|
203686
|
+
console.error(formatTable([
|
|
203687
|
+
"RANGE",
|
|
203688
|
+
"TX EXPECTED",
|
|
203689
|
+
"TX LOCAL",
|
|
203690
|
+
"EVENTS EXPECTED",
|
|
203691
|
+
"EVENTS LOCAL"
|
|
203692
|
+
], childMismatches.slice(0, 20).map((m) => [
|
|
203693
|
+
`${m.from_block}-${m.to_block}`,
|
|
203694
|
+
String(m.transactions.expected),
|
|
203695
|
+
String(m.transactions.actual),
|
|
203696
|
+
String(m.events.expected),
|
|
203697
|
+
String(m.events.actual)
|
|
203698
|
+
])));
|
|
203699
|
+
console.error("");
|
|
203700
|
+
console.error(dim("(dry-run — pass --apply to write the repair)"));
|
|
203701
|
+
}
|
|
203702
|
+
}
|
|
203703
|
+
});
|
|
203704
|
+
process.exit(complete ? REPAIR_EXIT.OK : REPAIR_EXIT.DIVERGENCE_REMAINS);
|
|
203705
|
+
}
|
|
203519
203706
|
function attachRepairCommand(cmd) {
|
|
203520
203707
|
return cmd.requiredOption("--against <manifest>", "archive manifest: an https URL or a local file path").option("--from-block <n>", "first height to consider").option("--to-block <n>", "last height to consider").option("--apply", "write the repair (default is a dry-run plan)").option("--public-key <pem>", "pin a signing key; default is the archive key built into this release").option("-y, --yes", "skip the confirmation prompt for a metered fetch").option("--json", "Output as JSON").addHelpText("after", `
|
|
203521
203708
|
Examples:
|
|
@@ -203528,6 +203715,11 @@ archive's partitions for that height. When the reference carries no
|
|
|
203528
203715
|
transactions or events partition for a height, the block is rewritten alone,
|
|
203529
203716
|
the run names the height, and it exits 1.
|
|
203530
203717
|
|
|
203718
|
+
When blocks already match the archive but a transactions or events partition
|
|
203719
|
+
does not, that range's transactions and events are replaced outright — there
|
|
203720
|
+
is no block or fork boundary to preserve, only stale rows underneath one that
|
|
203721
|
+
was never wrong.
|
|
203722
|
+
|
|
203531
203723
|
Exit codes:
|
|
203532
203724
|
0 nothing to repair, or the repair completed and re-verified clean
|
|
203533
203725
|
1 divergence remains (dry-run, or repair did not fully resolve it)
|
|
@@ -203565,13 +203757,15 @@ Exit codes:
|
|
|
203565
203757
|
}
|
|
203566
203758
|
const divergent = compareRangeDigests(local, referenceDigests).filter((c) => c.status !== "match");
|
|
203567
203759
|
if (divergent.length === 0) {
|
|
203568
|
-
|
|
203569
|
-
|
|
203570
|
-
|
|
203571
|
-
|
|
203572
|
-
|
|
203760
|
+
const childMismatches = await planChildPartitionDivergence(db, reference.manifest.partitions ?? [], reference.manifest.zero_record_ranges ?? [], inRange);
|
|
203761
|
+
await runChildOnlyRepair({
|
|
203762
|
+
reference,
|
|
203763
|
+
childMismatches,
|
|
203764
|
+
apply: !!opts.apply,
|
|
203765
|
+
json: !!opts.json,
|
|
203766
|
+
yes: !!opts.yes
|
|
203573
203767
|
});
|
|
203574
|
-
|
|
203768
|
+
return;
|
|
203575
203769
|
}
|
|
203576
203770
|
const partitions = (reference.manifest.partitions ?? []).filter((p) => p.dataset === "blocks" && divergent.some((d) => d.from_block === p.from_block && d.to_block === p.to_block));
|
|
203577
203771
|
const childPartitions = opts.apply ? (reference.manifest.partitions ?? []).filter((p) => (p.dataset === "transactions" || p.dataset === "events") && divergent.some((d) => p.from_block <= d.to_block && p.to_block >= d.from_block)) : [];
|
|
@@ -203672,7 +203866,7 @@ Exit codes:
|
|
|
203672
203866
|
warn(` the reference has no ${dataset} partition for ${heights.length} height(s): ${heights.slice(0, 5).join(", ")}${heights.length > 5 ? ", …" : ""}`);
|
|
203673
203867
|
}
|
|
203674
203868
|
for (const height of missingHeights.slice(0, 5)) {
|
|
203675
|
-
console.error(dim(` transactions/events at ${height}:
|
|
203869
|
+
console.error(dim(` the archive has no transactions/events at ${height} either: it cannot be repaired in place; re-ingest ${height} from your own node, or bootstrap a fresh database from the archive`));
|
|
203676
203870
|
}
|
|
203677
203871
|
}
|
|
203678
203872
|
output({
|
|
@@ -209059,5 +209253,5 @@ registerCreditsCommand(program);
|
|
|
209059
209253
|
registerArchiveCommand(program);
|
|
209060
209254
|
program.parse();
|
|
209061
209255
|
|
|
209062
|
-
//# debugId=
|
|
209256
|
+
//# debugId=24E0496FE3D50C6164756E2164756E21
|
|
209063
209257
|
//# sourceMappingURL=cli.js.map
|