@secondlayer/cli 12.1.0 → 12.2.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/README.md +38 -1
- package/dist/cli.js +189 -20
- package/dist/cli.js.map +7 -6
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -51,7 +51,44 @@ No account. Writes `.env.local`, restores history, prints the Stacks observer st
|
|
|
51
51
|
| `secondlayer bootstrap --against <manifest> [--to-block <n>] [--public-key <pem>] [-y] [--json]` | Restore chain history from a verified archive into an empty database. Exit `0` restored, `1` diverged, `2` refused |
|
|
52
52
|
| `secondlayer observer [--mode indexer\|signer-shared] [--endpoint host:port] [--recovery journal\|archive] [--network …]` | Print the `[[events_observer]]` stanza. Signer-shared requires `--recovery` |
|
|
53
53
|
| `secondlayer verify [all\|raw\|decode:<name>\|subgraph:<name>] --against <manifest> [--quick\|--deep\|--anchor]` | Compare local data to a signed archive. Default target `raw`. Exit `0` clean, `1` diverged, `2` unanchored |
|
|
54
|
-
| `secondlayer repair --against <archive> [--apply]` | Plan (default) or apply an archive repair |
|
|
54
|
+
| `secondlayer repair --against <archive> [--apply] [-y]` | Plan (default) or apply an archive repair |
|
|
55
|
+
|
|
56
|
+
Bootstrap and repair against the official hosted archive (`archive.secondlayer.tools`)
|
|
57
|
+
are metered per partition; against any other manifest (a mirror, a teammate's
|
|
58
|
+
box, a local file) they are free. See [Metered fetches](#metered-fetches).
|
|
59
|
+
|
|
60
|
+
### Metered fetches
|
|
61
|
+
|
|
62
|
+
`secondlayer bootstrap` and `secondlayer repair` pull partitions from the
|
|
63
|
+
signed archive instead of replaying the chain, and that pull costs money only
|
|
64
|
+
when it targets the official hosted archive. Point `--against` at a mirror or
|
|
65
|
+
a local manifest and nothing is charged, nothing is even contacted beyond
|
|
66
|
+
that manifest, because self-hosting the archive is a supported way to use
|
|
67
|
+
these commands, not a workaround.
|
|
68
|
+
|
|
69
|
+
Against the official host, both commands quote before they charge:
|
|
70
|
+
|
|
71
|
+
1. The manifest's partition list is priced with a free, no-write call
|
|
72
|
+
(`POST /api/archive/quote`).
|
|
73
|
+
2. The quote prints into the existing plan output, for example
|
|
74
|
+
`metered: 528 partitions ≈ $44.00 · balance $50.00`, or for `repair`
|
|
75
|
+
inside its monthly allowance, `metered: free (4 of 6 monthly repair
|
|
76
|
+
fetches remaining)`.
|
|
77
|
+
3. You confirm, or pass `-y` to skip the prompt. `-y` never skips the quote
|
|
78
|
+
or the balance check: if the balance is short, the command exits before
|
|
79
|
+
any partition is fetched and prints the shortfall and
|
|
80
|
+
`secondlayer credits buy`.
|
|
81
|
+
4. Only then does the command fetch, and only the partitions it actually
|
|
82
|
+
reads are charged.
|
|
83
|
+
|
|
84
|
+
A partition already charged in the last 24 hours re-presigns for free, so a
|
|
85
|
+
resumed or retried bootstrap never pays twice for the same bytes. `repair`
|
|
86
|
+
gets 6 free range-bundles a month; `bootstrap` does not, since it is the
|
|
87
|
+
whole-chain operation the free tier exists to not subsidize.
|
|
88
|
+
|
|
89
|
+
`secondlayer verify` is unaffected: it reads manifests and digests, never
|
|
90
|
+
partition bytes, and stays free and anonymous no matter which archive it
|
|
91
|
+
points at.
|
|
55
92
|
|
|
56
93
|
### Subgraphs
|
|
57
94
|
|
package/dist/cli.js
CHANGED
|
@@ -34320,7 +34320,7 @@ init_esm();
|
|
|
34320
34320
|
// package.json
|
|
34321
34321
|
var package_default = {
|
|
34322
34322
|
name: "@secondlayer/cli",
|
|
34323
|
-
version: "12.
|
|
34323
|
+
version: "12.2.0",
|
|
34324
34324
|
description: "Run a Stacks index on your own hardware — set it up, restore verified history, and keep it current.",
|
|
34325
34325
|
repository: {
|
|
34326
34326
|
type: "git",
|
|
@@ -34364,7 +34364,7 @@ var package_default = {
|
|
|
34364
34364
|
"@secondlayer/bundler": "^0.5.1",
|
|
34365
34365
|
"@secondlayer/scaffold": "^1.5.4",
|
|
34366
34366
|
"@secondlayer/sdk": "^8.0.1",
|
|
34367
|
-
"@secondlayer/shared": "^10.0.
|
|
34367
|
+
"@secondlayer/shared": "^10.0.1",
|
|
34368
34368
|
"@secondlayer/stacks": "^4.0.0",
|
|
34369
34369
|
"@secondlayer/subgraphs": "^4.0.2",
|
|
34370
34370
|
esbuild: "^0.27.4",
|
|
@@ -38217,9 +38217,22 @@ async function resolveArchivePublicKey(input4) {
|
|
|
38217
38217
|
}
|
|
38218
38218
|
return;
|
|
38219
38219
|
}
|
|
38220
|
-
async function
|
|
38220
|
+
async function fetchViaGate(gate, partition, retried) {
|
|
38221
|
+
const url = await gate.getUrl(partition.path, retried ? { forceRefresh: true } : undefined);
|
|
38222
|
+
const response2 = await fetch(url, { signal: AbortSignal.timeout(300000) });
|
|
38223
|
+
if (response2.status === 403 && !retried) {
|
|
38224
|
+
return fetchViaGate(gate, partition, true);
|
|
38225
|
+
}
|
|
38226
|
+
if (!response2.ok) {
|
|
38227
|
+
throw new Error(`could not fetch ${partition.path} (${response2.status} ${response2.statusText})`);
|
|
38228
|
+
}
|
|
38229
|
+
return Buffer.from(await response2.arrayBuffer());
|
|
38230
|
+
}
|
|
38231
|
+
async function fetchVerifiedPartition(reference, partition, gate) {
|
|
38221
38232
|
let bytes;
|
|
38222
|
-
if (
|
|
38233
|
+
if (gate) {
|
|
38234
|
+
bytes = await fetchViaGate(gate, partition, false);
|
|
38235
|
+
} else if (reference.isRemote) {
|
|
38223
38236
|
const url = `${reference.root.replace(/\/$/, "")}/${partition.path}`;
|
|
38224
38237
|
const response2 = await fetch(url, { signal: AbortSignal.timeout(300000) });
|
|
38225
38238
|
if (!response2.ok) {
|
|
@@ -38448,11 +38461,123 @@ import { unlink as unlink2, writeFile as writeFile5 } from "node:fs/promises";
|
|
|
38448
38461
|
import { tmpdir as tmpdir2 } from "node:os";
|
|
38449
38462
|
import { join as join15 } from "node:path";
|
|
38450
38463
|
import { ParquetReader } from "@dsnp/parquetjs";
|
|
38464
|
+
import { confirm as confirm7 } from "@inquirer/prompts";
|
|
38451
38465
|
import {
|
|
38452
38466
|
compareRangeDigests as compareRangeDigests2,
|
|
38453
38467
|
computeRangeDigest as computeRangeDigest2
|
|
38454
38468
|
} from "@secondlayer/shared/archive/range-digest";
|
|
38455
38469
|
import { getDb as getDb3, sql as sql2 } from "@secondlayer/shared/db";
|
|
38470
|
+
|
|
38471
|
+
// src/lib/archive-gate.ts
|
|
38472
|
+
init_http();
|
|
38473
|
+
var OFFICIAL_ARCHIVE_HOST = "archive.secondlayer.tools";
|
|
38474
|
+
function isOfficialArchive(reference) {
|
|
38475
|
+
if (!reference.isRemote)
|
|
38476
|
+
return false;
|
|
38477
|
+
try {
|
|
38478
|
+
return new URL(reference.origin).hostname === OFFICIAL_ARCHIVE_HOST;
|
|
38479
|
+
} catch {
|
|
38480
|
+
return false;
|
|
38481
|
+
}
|
|
38482
|
+
}
|
|
38483
|
+
var defaultDeps = { httpArchiveOps };
|
|
38484
|
+
function quoteFromBody(body) {
|
|
38485
|
+
return {
|
|
38486
|
+
partitions: body.partitions,
|
|
38487
|
+
bundles: body.bundles,
|
|
38488
|
+
usdMicros: body.usd_micros,
|
|
38489
|
+
usd: body.usd,
|
|
38490
|
+
freeAllowanceAppliedMicros: body.free_allowance_applied_micros,
|
|
38491
|
+
allowanceRemainingBundles: body.allowance_remaining_bundles,
|
|
38492
|
+
balanceUsdMicros: body.balance_usd_micros,
|
|
38493
|
+
sufficient: body.sufficient
|
|
38494
|
+
};
|
|
38495
|
+
}
|
|
38496
|
+
async function quoteArchiveFetch(paths, flow, deps = defaultDeps) {
|
|
38497
|
+
try {
|
|
38498
|
+
const body = await deps.httpArchiveOps("/api/archive/quote", { method: "POST", body: { paths, flow } });
|
|
38499
|
+
return { ok: true, quote: quoteFromBody(body) };
|
|
38500
|
+
} catch (err) {
|
|
38501
|
+
if (err instanceof CliHttpError) {
|
|
38502
|
+
if (err.status === 503)
|
|
38503
|
+
return { ok: false, kind: "not_configured" };
|
|
38504
|
+
if (err.status === 401) {
|
|
38505
|
+
return { ok: false, kind: "not_authed", message: err.message };
|
|
38506
|
+
}
|
|
38507
|
+
return { ok: false, kind: "error", message: err.message };
|
|
38508
|
+
}
|
|
38509
|
+
throw err;
|
|
38510
|
+
}
|
|
38511
|
+
}
|
|
38512
|
+
var BUY_CREDITS_COMMAND = "secondlayer credits buy";
|
|
38513
|
+
function formatInsufficientMessage(quote) {
|
|
38514
|
+
const shortfallUsd = ((quote.usdMicros - quote.balanceUsdMicros) / 1e6).toFixed(2);
|
|
38515
|
+
const balanceUsd = (quote.balanceUsdMicros / 1e6).toFixed(2);
|
|
38516
|
+
return `Insufficient archive credits: quote $${quote.usd}, balance $${balanceUsd}, short $${shortfallUsd}. Buy more with \`${BUY_CREDITS_COMMAND}\`.`;
|
|
38517
|
+
}
|
|
38518
|
+
var ARCHIVE_GATE_NOT_CONFIGURED_MESSAGE = "the archive gate is not configured on the server; contact the operator";
|
|
38519
|
+
var ALLOWANCE_MONTHLY_BUNDLES = 6;
|
|
38520
|
+
function formatQuoteValue(quote, flow) {
|
|
38521
|
+
if (flow === "repair" && quote.usdMicros === 0 && quote.freeAllowanceAppliedMicros > 0) {
|
|
38522
|
+
return `free (${quote.allowanceRemainingBundles} of ${ALLOWANCE_MONTHLY_BUNDLES} monthly repair fetches remaining)`;
|
|
38523
|
+
}
|
|
38524
|
+
const balance = `$${(quote.balanceUsdMicros / 1e6).toFixed(2)}`;
|
|
38525
|
+
return `${quote.partitions} partitions ≈ $${quote.usd} · balance ${balance}`;
|
|
38526
|
+
}
|
|
38527
|
+
function shouldPromptForGatedFetch(opts) {
|
|
38528
|
+
return !opts.yes && !opts.json;
|
|
38529
|
+
}
|
|
38530
|
+
var FETCH_BATCH_SIZE = 16;
|
|
38531
|
+
function createGatedFetcher(paths, flow, deps = defaultDeps) {
|
|
38532
|
+
const cache = new Map;
|
|
38533
|
+
const batchOf = new Map;
|
|
38534
|
+
for (const [index, path11] of paths.entries()) {
|
|
38535
|
+
batchOf.set(path11, Math.floor(index / FETCH_BATCH_SIZE));
|
|
38536
|
+
}
|
|
38537
|
+
const fetchedBatches = new Set;
|
|
38538
|
+
const inFlight = new Map;
|
|
38539
|
+
async function fetchAndCache(batchPaths) {
|
|
38540
|
+
const body = await deps.httpArchiveOps("/api/archive/fetch", { method: "POST", body: { paths: batchPaths, flow } });
|
|
38541
|
+
for (const u3 of body.urls)
|
|
38542
|
+
cache.set(u3.path, u3.url);
|
|
38543
|
+
}
|
|
38544
|
+
return {
|
|
38545
|
+
async getUrl(path11, opts) {
|
|
38546
|
+
if (opts?.forceRefresh) {
|
|
38547
|
+
if (process.env.DEBUG) {
|
|
38548
|
+
console.error(`[archive-gate] re-issuing expired URL for ${path11}`);
|
|
38549
|
+
}
|
|
38550
|
+
await fetchAndCache([path11]);
|
|
38551
|
+
const fresh = cache.get(path11);
|
|
38552
|
+
if (!fresh) {
|
|
38553
|
+
throw new Error(`archive gate: no URL returned for ${path11}`);
|
|
38554
|
+
}
|
|
38555
|
+
return fresh;
|
|
38556
|
+
}
|
|
38557
|
+
const batchIndex = batchOf.get(path11);
|
|
38558
|
+
if (batchIndex === undefined) {
|
|
38559
|
+
throw new Error(`archive gate: ${path11} was not in the quoted batch`);
|
|
38560
|
+
}
|
|
38561
|
+
if (!fetchedBatches.has(batchIndex)) {
|
|
38562
|
+
let inflight = inFlight.get(batchIndex);
|
|
38563
|
+
if (!inflight) {
|
|
38564
|
+
const batchPaths = paths.filter((p2) => batchOf.get(p2) === batchIndex);
|
|
38565
|
+
inflight = fetchAndCache(batchPaths).then(() => {
|
|
38566
|
+
fetchedBatches.add(batchIndex);
|
|
38567
|
+
});
|
|
38568
|
+
inFlight.set(batchIndex, inflight);
|
|
38569
|
+
}
|
|
38570
|
+
await inflight;
|
|
38571
|
+
}
|
|
38572
|
+
const url = cache.get(path11);
|
|
38573
|
+
if (!url)
|
|
38574
|
+
throw new Error(`archive gate: no URL returned for ${path11}`);
|
|
38575
|
+
return url;
|
|
38576
|
+
}
|
|
38577
|
+
};
|
|
38578
|
+
}
|
|
38579
|
+
|
|
38580
|
+
// src/commands/repair.ts
|
|
38456
38581
|
init_output();
|
|
38457
38582
|
var REPAIR_EXIT = {
|
|
38458
38583
|
OK: 0,
|
|
@@ -38502,8 +38627,8 @@ function parseHeight3(value, flag) {
|
|
|
38502
38627
|
}
|
|
38503
38628
|
return parsed;
|
|
38504
38629
|
}
|
|
38505
|
-
async function planRange(db, reference, partition) {
|
|
38506
|
-
const bytes = await fetchVerifiedPartition(reference, partition);
|
|
38630
|
+
async function planRange(db, reference, partition, gate) {
|
|
38631
|
+
const bytes = await fetchVerifiedPartition(reference, partition, gate);
|
|
38507
38632
|
const archiveBlocks = await readBlocksPartition(bytes, `${partition.from_block}-${partition.to_block}`);
|
|
38508
38633
|
const localRows = await db.selectFrom("blocks").select(["height", "hash"]).where("canonical", "=", true).where("height", ">=", partition.from_block).where("height", "<=", partition.to_block).execute();
|
|
38509
38634
|
const localByHeight = new Map(localRows.map((r2) => [Number(r2.height), r2.hash]));
|
|
@@ -38530,10 +38655,10 @@ async function planRange(db, reference, partition) {
|
|
|
38530
38655
|
const extraHeights = [...localByHeight.keys()].sort((a4, b3) => a4 - b3);
|
|
38531
38656
|
return { fixes, extraHeights };
|
|
38532
38657
|
}
|
|
38533
|
-
async function applyFixes(db, reference, partition, fixes) {
|
|
38658
|
+
async function applyFixes(db, reference, partition, fixes, gate) {
|
|
38534
38659
|
if (fixes.length === 0)
|
|
38535
38660
|
return 0;
|
|
38536
|
-
const bytes = await fetchVerifiedPartition(reference, partition);
|
|
38661
|
+
const bytes = await fetchVerifiedPartition(reference, partition, gate);
|
|
38537
38662
|
const archiveBlocks = await readBlocksPartition(bytes, `apply-${partition.from_block}`);
|
|
38538
38663
|
const byHeight = new Map(archiveBlocks.map((b3) => [b3.height, b3]));
|
|
38539
38664
|
const targets = fixes.map((fix) => byHeight.get(fix.height)).filter((b3) => b3 !== undefined);
|
|
@@ -38562,7 +38687,7 @@ async function applyFixes(db, reference, partition, fixes) {
|
|
|
38562
38687
|
return targets.length;
|
|
38563
38688
|
}
|
|
38564
38689
|
function registerRepairCommand(program2) {
|
|
38565
|
-
program2.command("repair").description("Replace local chain data that diverges from a signed archive (dry-run by default)").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 the signing key instead of fetching it").option("--json", "Output as JSON").addHelpText("after", `
|
|
38690
|
+
program2.command("repair").description("Replace local chain data that diverges from a signed archive (dry-run by default)").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 the signing key instead of fetching it").option("-y, --yes", "skip the confirmation prompt for a metered fetch").option("--json", "Output as JSON").addHelpText("after", `
|
|
38566
38691
|
Examples:
|
|
38567
38692
|
$ secondlayer repair --against ./snapshot.json # plan only
|
|
38568
38693
|
$ secondlayer repair --against ./snapshot.json --apply # write the fix
|
|
@@ -38608,12 +38733,37 @@ Exit codes:
|
|
|
38608
38733
|
process.exit(REPAIR_EXIT.OK);
|
|
38609
38734
|
}
|
|
38610
38735
|
const partitions = (reference.manifest.partitions ?? []).filter((p2) => p2.dataset === "blocks" && divergent.some((d2) => d2.from_block === p2.from_block && d2.to_block === p2.to_block));
|
|
38736
|
+
let gate;
|
|
38737
|
+
if (isOfficialArchive(reference)) {
|
|
38738
|
+
const paths = partitions.map((p2) => p2.path);
|
|
38739
|
+
const result = await quoteArchiveFetch(paths, "repair");
|
|
38740
|
+
if (!result.ok) {
|
|
38741
|
+
printError(result.kind === "not_configured" ? ARCHIVE_GATE_NOT_CONFIGURED_MESSAGE : result.message);
|
|
38742
|
+
process.exit(REPAIR_EXIT.UNANCHORED);
|
|
38743
|
+
}
|
|
38744
|
+
if (!result.quote.sufficient) {
|
|
38745
|
+
printError(formatInsufficientMessage(result.quote));
|
|
38746
|
+
process.exit(REPAIR_EXIT.UNANCHORED);
|
|
38747
|
+
}
|
|
38748
|
+
note(` metered: ${formatQuoteValue(result.quote, "repair")}`);
|
|
38749
|
+
if (shouldPromptForGatedFetch(opts)) {
|
|
38750
|
+
const proceed = await confirm7({
|
|
38751
|
+
message: `Fetch ${partitions.length} partition(s) from the archive?`,
|
|
38752
|
+
default: true
|
|
38753
|
+
});
|
|
38754
|
+
if (!proceed) {
|
|
38755
|
+
note("Nothing was fetched.");
|
|
38756
|
+
process.exit(REPAIR_EXIT.UNANCHORED);
|
|
38757
|
+
}
|
|
38758
|
+
}
|
|
38759
|
+
gate = createGatedFetcher(paths, "repair");
|
|
38760
|
+
}
|
|
38611
38761
|
const allFixes = [];
|
|
38612
38762
|
const allExtra = [];
|
|
38613
38763
|
const planned = [];
|
|
38614
38764
|
for (const partition of partitions) {
|
|
38615
38765
|
note(` reading ${partition.dataset} ${partition.from_block}-${partition.to_block}`);
|
|
38616
|
-
const { fixes, extraHeights } = await planRange(db, reference, partition);
|
|
38766
|
+
const { fixes, extraHeights } = await planRange(db, reference, partition, gate);
|
|
38617
38767
|
planned.push({ partition, fixes });
|
|
38618
38768
|
allFixes.push(...fixes);
|
|
38619
38769
|
allExtra.push(...extraHeights);
|
|
@@ -38621,7 +38771,7 @@ Exit codes:
|
|
|
38621
38771
|
let applied = 0;
|
|
38622
38772
|
if (opts.apply) {
|
|
38623
38773
|
for (const { partition, fixes } of planned) {
|
|
38624
|
-
applied += await applyFixes(db, reference, partition, fixes);
|
|
38774
|
+
applied += await applyFixes(db, reference, partition, fixes, gate);
|
|
38625
38775
|
}
|
|
38626
38776
|
}
|
|
38627
38777
|
let remaining = divergent.length;
|
|
@@ -38698,7 +38848,7 @@ import { unlink as unlink3, writeFile as writeFile6 } from "node:fs/promises";
|
|
|
38698
38848
|
import { tmpdir as tmpdir3 } from "node:os";
|
|
38699
38849
|
import { join as join16 } from "node:path";
|
|
38700
38850
|
import { ParquetReader as ParquetReader2 } from "@dsnp/parquetjs";
|
|
38701
|
-
import { confirm as
|
|
38851
|
+
import { confirm as confirm8 } from "@inquirer/prompts";
|
|
38702
38852
|
import {
|
|
38703
38853
|
copyStatement,
|
|
38704
38854
|
writeRowsToCopyStream
|
|
@@ -38794,8 +38944,8 @@ async function* readPartitionRows(bytes, label) {
|
|
|
38794
38944
|
await unlink3(path11).catch(() => {});
|
|
38795
38945
|
}
|
|
38796
38946
|
}
|
|
38797
|
-
async function loadPartition(reference, partition, rawClient) {
|
|
38798
|
-
const bytes = await fetchVerifiedPartition(reference, partition);
|
|
38947
|
+
async function loadPartition(reference, partition, rawClient, gate) {
|
|
38948
|
+
const bytes = await fetchVerifiedPartition(reference, partition, gate);
|
|
38799
38949
|
const writable2 = await rawClient.unsafe(copyStatement(partition.dataset)).writable();
|
|
38800
38950
|
const written = await writeRowsToCopyStream({
|
|
38801
38951
|
writable: writable2,
|
|
@@ -38872,6 +39022,23 @@ Exit codes:
|
|
|
38872
39022
|
const tipHeight = Math.max(...partitions.map((p2) => p2.to_block));
|
|
38873
39023
|
const restoreSeconds = totalRows / RESTORE_ROWS_PER_SECOND;
|
|
38874
39024
|
const genesisSeconds = tipHeight / BLOCKS_PER_DAY_ESTIMATE * 86400;
|
|
39025
|
+
const gated = isOfficialArchive(reference);
|
|
39026
|
+
let quoteLine;
|
|
39027
|
+
let gate;
|
|
39028
|
+
if (gated) {
|
|
39029
|
+
const paths = partitions.map((p2) => p2.path);
|
|
39030
|
+
const result = await quoteArchiveFetch(paths, "bootstrap");
|
|
39031
|
+
if (!result.ok) {
|
|
39032
|
+
printError(result.kind === "not_configured" ? ARCHIVE_GATE_NOT_CONFIGURED_MESSAGE : result.message);
|
|
39033
|
+
process.exit(BOOTSTRAP_EXIT.REFUSED);
|
|
39034
|
+
}
|
|
39035
|
+
if (!result.quote.sufficient) {
|
|
39036
|
+
printError(formatInsufficientMessage(result.quote));
|
|
39037
|
+
process.exit(BOOTSTRAP_EXIT.REFUSED);
|
|
39038
|
+
}
|
|
39039
|
+
quoteLine = formatQuoteValue(result.quote, "bootstrap");
|
|
39040
|
+
gate = createGatedFetcher(paths, "bootstrap");
|
|
39041
|
+
}
|
|
38875
39042
|
if (!opts.json) {
|
|
38876
39043
|
console.error("");
|
|
38877
39044
|
console.error(formatKeyValue([
|
|
@@ -38882,15 +39049,16 @@ Exit codes:
|
|
|
38882
39049
|
],
|
|
38883
39050
|
["rows", totalRows.toLocaleString()],
|
|
38884
39051
|
["download", `${(totalBytes / 1e9).toFixed(1)} GB`],
|
|
38885
|
-
["signature", "verified"]
|
|
39052
|
+
["signature", "verified"],
|
|
39053
|
+
...quoteLine ? [["metered", quoteLine]] : []
|
|
38886
39054
|
]));
|
|
38887
39055
|
console.error("");
|
|
38888
39056
|
console.error(` sync from genesis ${bold(`~${formatDuration2(genesisSeconds)}`)} ${dim("(estimate)")}`);
|
|
38889
39057
|
console.error(` restore from archive ${bold(`~${formatDuration2(restoreSeconds)}`)}`);
|
|
38890
39058
|
console.error("");
|
|
38891
39059
|
}
|
|
38892
|
-
if (
|
|
38893
|
-
const proceed = await
|
|
39060
|
+
if (shouldPromptForGatedFetch(opts)) {
|
|
39061
|
+
const proceed = await confirm8({
|
|
38894
39062
|
message: "Restore from the verified archive?",
|
|
38895
39063
|
default: true
|
|
38896
39064
|
});
|
|
@@ -38907,7 +39075,7 @@ Exit codes:
|
|
|
38907
39075
|
for (const dataset of order) {
|
|
38908
39076
|
const datasetPartitions = partitions.filter((p2) => p2.dataset === dataset).sort((a4, b3) => a4.from_block - b3.from_block);
|
|
38909
39077
|
for (const [index, partition] of datasetPartitions.entries()) {
|
|
38910
|
-
loaded[dataset] += await loadPartition(reference, partition, rawClient);
|
|
39078
|
+
loaded[dataset] += await loadPartition(reference, partition, rawClient, gate);
|
|
38911
39079
|
const done = index + 1;
|
|
38912
39080
|
if (done % 10 === 0 || done === datasetPartitions.length) {
|
|
38913
39081
|
const elapsed2 = (Date.now() - startedAt) / 1000;
|
|
@@ -38964,7 +39132,8 @@ Exit codes:
|
|
|
38964
39132
|
elapsed_seconds: Math.round(elapsed),
|
|
38965
39133
|
resume_from: tipHeight + 1,
|
|
38966
39134
|
node_tip_at_start: nodeTipAtStart,
|
|
38967
|
-
catch_up_blocks: seam?.gap ?? null
|
|
39135
|
+
catch_up_blocks: seam?.gap ?? null,
|
|
39136
|
+
metered: quoteLine ?? null
|
|
38968
39137
|
};
|
|
38969
39138
|
output({
|
|
38970
39139
|
json: opts.json,
|
|
@@ -39639,5 +39808,5 @@ program.commandsGroup("Archive:");
|
|
|
39639
39808
|
registerCreditsCommand(program);
|
|
39640
39809
|
program.parse();
|
|
39641
39810
|
|
|
39642
|
-
//# debugId=
|
|
39811
|
+
//# debugId=5BF91F30BB4466C964756E2164756E21
|
|
39643
39812
|
//# sourceMappingURL=cli.js.map
|