@secondlayer/cli 22.0.0 → 22.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 +211 -16
- package/dist/cli.js.map +9 -8
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -2567,12 +2567,27 @@ function nextStepHint(status) {
|
|
|
2567
2567
|
return "Server error — retry shortly, or check `secondlayer status`.";
|
|
2568
2568
|
return;
|
|
2569
2569
|
}
|
|
2570
|
+
function authFailureMessage(code, status, ossMode) {
|
|
2571
|
+
if (code === "KEY_REVOKED") {
|
|
2572
|
+
return {
|
|
2573
|
+
message: "Your saved login was revoked.",
|
|
2574
|
+
hint: "Run `secondlayer login` to sign in again. If you set SECONDLAYER_API_KEY, replace it with a key from /account/keys."
|
|
2575
|
+
};
|
|
2576
|
+
}
|
|
2577
|
+
if (code === "SESSION_EXPIRED" || status === 401) {
|
|
2578
|
+
return {
|
|
2579
|
+
message: "Authentication required.",
|
|
2580
|
+
hint: ossMode ? "Set INSTANCE_TOKEN from `secondlayer init`." : "Run `secondlayer login` to re-authenticate."
|
|
2581
|
+
};
|
|
2582
|
+
}
|
|
2583
|
+
return null;
|
|
2584
|
+
}
|
|
2570
2585
|
function handleApiError(err, action) {
|
|
2571
2586
|
const status = err instanceof ApiError || err instanceof CliHttpError ? err.status : undefined;
|
|
2572
|
-
|
|
2573
|
-
|
|
2574
|
-
|
|
2575
|
-
});
|
|
2587
|
+
const code = err instanceof ApiError || err instanceof CliHttpError ? err.code : undefined;
|
|
2588
|
+
const auth = authFailureMessage(code, status, isOssMode());
|
|
2589
|
+
if (auth) {
|
|
2590
|
+
printError(auth.message, { hint: auth.hint });
|
|
2576
2591
|
process.exit(1);
|
|
2577
2592
|
}
|
|
2578
2593
|
const detail = err instanceof Error ? err.message : String(err);
|
|
@@ -16181,7 +16196,11 @@ class StacksApiClient {
|
|
|
16181
16196
|
return response.body;
|
|
16182
16197
|
} catch (error) {
|
|
16183
16198
|
const statusCode = typeof error === "object" && error !== null && "response" in error && typeof error.response === "object" && error.response !== null && "statusCode" in error.response ? error.response.statusCode : undefined;
|
|
16199
|
+
const responseBody = typeof error === "object" && error !== null && "response" in error && typeof error.response === "object" && error.response !== null && "body" in error.response ? error.response.body : undefined;
|
|
16184
16200
|
const message = error instanceof Error ? error.message : String(error);
|
|
16201
|
+
if (statusCode === 401 && typeof responseBody === "object" && responseBody !== null && "code" in responseBody && responseBody.code === "KEY_REVOKED") {
|
|
16202
|
+
throw new Error("Your saved login was revoked. Run `secondlayer login` to sign in again.");
|
|
16203
|
+
}
|
|
16185
16204
|
if (statusCode === 401) {
|
|
16186
16205
|
throw new Error(isOssMode() ? "Authentication required. Set INSTANCE_TOKEN from `secondlayer init`." : "Authentication required. Run: secondlayer login");
|
|
16187
16206
|
}
|
|
@@ -29469,7 +29488,7 @@ var init_git_status = () => {};
|
|
|
29469
29488
|
|
|
29470
29489
|
// ../../node_modules/typescript/lib/typescript.js
|
|
29471
29490
|
var require_typescript = __commonJS(function(exports, module) {
|
|
29472
|
-
var __dirname = "/Users/ryan/projects/secondlayer/node_modules/typescript/lib", __filename = "/Users/ryan/projects/secondlayer/node_modules/typescript/lib/typescript.js";
|
|
29491
|
+
var __dirname = "/Users/ryan/projects/secondlayer/.claude/worktrees/rel313/node_modules/typescript/lib", __filename = "/Users/ryan/projects/secondlayer/.claude/worktrees/rel313/node_modules/typescript/lib/typescript.js";
|
|
29473
29492
|
/*! *****************************************************************************
|
|
29474
29493
|
Copyright (c) Microsoft Corporation. All rights reserved.
|
|
29475
29494
|
Licensed under the Apache License, Version 2.0 (the "License"); you may not use
|
|
@@ -202692,7 +202711,7 @@ var {
|
|
|
202692
202711
|
// package.json
|
|
202693
202712
|
var package_default = {
|
|
202694
202713
|
name: "@secondlayer/cli",
|
|
202695
|
-
version: "22.
|
|
202714
|
+
version: "22.1.0",
|
|
202696
202715
|
description: "Run a Stacks index on your own hardware — set it up, restore verified history, and keep it current.",
|
|
202697
202716
|
repository: {
|
|
202698
202717
|
type: "git",
|
|
@@ -204165,6 +204184,126 @@ import {
|
|
|
204165
204184
|
reportVerify
|
|
204166
204185
|
} from "@secondlayer/shared/coverage";
|
|
204167
204186
|
import { getDb as getDb3 } from "@secondlayer/shared/db";
|
|
204187
|
+
|
|
204188
|
+
// src/lib/archive-heights.ts
|
|
204189
|
+
import {
|
|
204190
|
+
PerHeightDigestAccumulator
|
|
204191
|
+
} from "@secondlayer/shared/archive/per-height-digest";
|
|
204192
|
+
import {
|
|
204193
|
+
streamHeightDigests
|
|
204194
|
+
} from "@secondlayer/shared/archive/semantic-digest-builder";
|
|
204195
|
+
var PART_FOR_DATASET = {
|
|
204196
|
+
blocks: "block",
|
|
204197
|
+
transactions: "transactions",
|
|
204198
|
+
events: "events"
|
|
204199
|
+
};
|
|
204200
|
+
async function localPerHeightDigests(db, fromBlock, toBlock, datasets = ["blocks", "transactions", "events"]) {
|
|
204201
|
+
const accumulator = new PerHeightDigestAccumulator;
|
|
204202
|
+
for await (const { height, digest } of streamHeightDigests(db, "blocks", fromBlock, toBlock)) {
|
|
204203
|
+
accumulator.setBlockDigest(height, digest);
|
|
204204
|
+
}
|
|
204205
|
+
if (datasets.includes("transactions")) {
|
|
204206
|
+
for await (const { height, digest } of streamHeightDigests(db, "transactions", fromBlock, toBlock)) {
|
|
204207
|
+
accumulator.appendTransactionDigest(height, digest);
|
|
204208
|
+
}
|
|
204209
|
+
}
|
|
204210
|
+
if (datasets.includes("events")) {
|
|
204211
|
+
for await (const { height, digest } of streamHeightDigests(db, "events", fromBlock, toBlock)) {
|
|
204212
|
+
accumulator.appendEventDigest(height, digest);
|
|
204213
|
+
}
|
|
204214
|
+
}
|
|
204215
|
+
const rows = new Map;
|
|
204216
|
+
for (const row of accumulator.drain(fromBlock, toBlock)) {
|
|
204217
|
+
rows.set(row.height, row);
|
|
204218
|
+
}
|
|
204219
|
+
return rows;
|
|
204220
|
+
}
|
|
204221
|
+
function sidecarsCovering(digestIndex, fromBlock, toBlock) {
|
|
204222
|
+
return (digestIndex ?? []).filter((p) => p.from_block <= toBlock && p.to_block >= fromBlock);
|
|
204223
|
+
}
|
|
204224
|
+
async function fetchSidecarRows(reference, partition) {
|
|
204225
|
+
const bytes = await fetchVerifiedPartition(reference, partition);
|
|
204226
|
+
const rows = [];
|
|
204227
|
+
for await (const raw of readPartitionRows(bytes, `digests-${partition.from_block}-${partition.to_block}`)) {
|
|
204228
|
+
const row = raw;
|
|
204229
|
+
rows.push({
|
|
204230
|
+
height: Number(row.height),
|
|
204231
|
+
block_digest: String(row.block_digest),
|
|
204232
|
+
transactions_rollup: row.transactions_rollup == null ? null : String(row.transactions_rollup),
|
|
204233
|
+
events_rollup: row.events_rollup == null ? null : String(row.events_rollup)
|
|
204234
|
+
});
|
|
204235
|
+
}
|
|
204236
|
+
return rows;
|
|
204237
|
+
}
|
|
204238
|
+
function compareHeights(params) {
|
|
204239
|
+
const { dataset, fromBlock, toBlock, local, archive, covered } = params;
|
|
204240
|
+
const inCoverage = (h) => covered.some((c) => h >= c.from_block && h <= c.to_block);
|
|
204241
|
+
const heights = new Set;
|
|
204242
|
+
for (const h of archive.keys())
|
|
204243
|
+
heights.add(h);
|
|
204244
|
+
for (const h of local.keys())
|
|
204245
|
+
if (inCoverage(h))
|
|
204246
|
+
heights.add(h);
|
|
204247
|
+
const out = [];
|
|
204248
|
+
for (const height of [...heights].sort((a, b) => a - b)) {
|
|
204249
|
+
if (height < fromBlock || height > toBlock)
|
|
204250
|
+
continue;
|
|
204251
|
+
const mine = local.get(height);
|
|
204252
|
+
const theirs = archive.get(height);
|
|
204253
|
+
if (!mine && theirs) {
|
|
204254
|
+
out.push({ height, parts: ["missing-local"] });
|
|
204255
|
+
} else if (mine && !theirs) {
|
|
204256
|
+
out.push({ height, parts: ["missing-archive"] });
|
|
204257
|
+
} else if (mine && theirs && !partsMatch(dataset, mine, theirs)) {
|
|
204258
|
+
out.push({ height, parts: [PART_FOR_DATASET[dataset]] });
|
|
204259
|
+
}
|
|
204260
|
+
}
|
|
204261
|
+
return out;
|
|
204262
|
+
}
|
|
204263
|
+
function partsMatch(dataset, a, b) {
|
|
204264
|
+
if (dataset === "blocks")
|
|
204265
|
+
return a.block_digest === b.block_digest;
|
|
204266
|
+
if (dataset === "transactions") {
|
|
204267
|
+
return a.transactions_rollup === b.transactions_rollup;
|
|
204268
|
+
}
|
|
204269
|
+
return a.events_rollup === b.events_rollup;
|
|
204270
|
+
}
|
|
204271
|
+
async function divergentHeightsForRange(params) {
|
|
204272
|
+
const { db, reference, dataset, fromBlock, toBlock } = params;
|
|
204273
|
+
const sidecars = sidecarsCovering(params.digestIndex, fromBlock, toBlock);
|
|
204274
|
+
if (sidecars.length === 0)
|
|
204275
|
+
return null;
|
|
204276
|
+
const archive = new Map;
|
|
204277
|
+
for (const sidecar of sidecars) {
|
|
204278
|
+
for (const row of await fetchSidecarRows(reference, sidecar)) {
|
|
204279
|
+
archive.set(row.height, row);
|
|
204280
|
+
}
|
|
204281
|
+
}
|
|
204282
|
+
const local = await localPerHeightDigests(db, fromBlock, toBlock, [dataset]);
|
|
204283
|
+
return compareHeights({
|
|
204284
|
+
dataset,
|
|
204285
|
+
fromBlock,
|
|
204286
|
+
toBlock,
|
|
204287
|
+
local,
|
|
204288
|
+
archive,
|
|
204289
|
+
covered: sidecars
|
|
204290
|
+
});
|
|
204291
|
+
}
|
|
204292
|
+
function mergeRepairRanges(ranges) {
|
|
204293
|
+
const sorted = [...ranges].sort((a, b) => a.from_block - b.from_block || a.to_block - b.to_block);
|
|
204294
|
+
const merged = [];
|
|
204295
|
+
for (const range of sorted) {
|
|
204296
|
+
const last = merged[merged.length - 1];
|
|
204297
|
+
if (last && range.from_block <= last.to_block + 1) {
|
|
204298
|
+
last.to_block = Math.max(last.to_block, range.to_block);
|
|
204299
|
+
} else {
|
|
204300
|
+
merged.push({ from_block: range.from_block, to_block: range.to_block });
|
|
204301
|
+
}
|
|
204302
|
+
}
|
|
204303
|
+
return merged;
|
|
204304
|
+
}
|
|
204305
|
+
|
|
204306
|
+
// src/commands/verify.ts
|
|
204168
204307
|
init_output();
|
|
204169
204308
|
init_resolve_auth();
|
|
204170
204309
|
|
|
@@ -204559,6 +204698,7 @@ function parseHeight3(value, flag) {
|
|
|
204559
204698
|
}
|
|
204560
204699
|
return parsed;
|
|
204561
204700
|
}
|
|
204701
|
+
var MAX_HEIGHTS_SHOWN = 20;
|
|
204562
204702
|
function statusLabel(status) {
|
|
204563
204703
|
if (status === "match")
|
|
204564
204704
|
return green("ok");
|
|
@@ -204597,9 +204737,10 @@ Exit codes:
|
|
|
204597
204737
|
envPem: process.env.ARCHIVE_SIGNING_PUBLIC_KEY ?? process.env.STREAMS_SIGNING_PUBLIC_KEY,
|
|
204598
204738
|
allowHostedApi: !isOssMode()
|
|
204599
204739
|
});
|
|
204600
|
-
const
|
|
204740
|
+
const loaded = await loadReference(opts.against, {
|
|
204601
204741
|
publicKeyPem: publicKey
|
|
204602
204742
|
});
|
|
204743
|
+
const { manifest, origin } = loaded;
|
|
204603
204744
|
const signature = checkSignature(manifest, publicKey, !!opts.insecure);
|
|
204604
204745
|
if (!signature.verified && !opts.insecure) {
|
|
204605
204746
|
printError(`Cannot trust the reference: ${signature.reason}.`, {
|
|
@@ -204732,6 +204873,30 @@ Exit codes:
|
|
|
204732
204873
|
}))
|
|
204733
204874
|
];
|
|
204734
204875
|
const diverged = all.filter((c) => c.status !== "match");
|
|
204876
|
+
const digestIndex = manifest.digest_index;
|
|
204877
|
+
const heightNotes = [];
|
|
204878
|
+
if (diverged.length > 0 && !digestIndex?.length) {
|
|
204879
|
+
heightNotes.push("This snapshot has no per-height digests; exact diverging heights need a newer snapshot.");
|
|
204880
|
+
} else {
|
|
204881
|
+
for (const range of diverged) {
|
|
204882
|
+
try {
|
|
204883
|
+
const heights = await divergentHeightsForRange({
|
|
204884
|
+
db,
|
|
204885
|
+
reference: loaded,
|
|
204886
|
+
digestIndex,
|
|
204887
|
+
dataset: range.dataset,
|
|
204888
|
+
fromBlock: range.from_block,
|
|
204889
|
+
toBlock: range.to_block
|
|
204890
|
+
});
|
|
204891
|
+
if (heights)
|
|
204892
|
+
range.divergent_heights = heights;
|
|
204893
|
+
else
|
|
204894
|
+
heightNotes.push(`No per-height digests cover ${range.from_block}-${range.to_block}.`);
|
|
204895
|
+
} catch (err) {
|
|
204896
|
+
heightNotes.push(`Could not narrow ${range.from_block}-${range.to_block} to heights: ${err instanceof Error ? err.message : String(err)}`);
|
|
204897
|
+
}
|
|
204898
|
+
}
|
|
204899
|
+
}
|
|
204735
204900
|
const verdict = reportVerify({
|
|
204736
204901
|
target,
|
|
204737
204902
|
mode,
|
|
@@ -204744,7 +204909,8 @@ Exit codes:
|
|
|
204744
204909
|
signature_verified: signature.verified,
|
|
204745
204910
|
ranges_checked: all.length,
|
|
204746
204911
|
semantic_checks: semanticChecks.length,
|
|
204747
|
-
divergent_ranges: diverged
|
|
204912
|
+
divergent_ranges: diverged,
|
|
204913
|
+
...heightNotes.length > 0 ? { note: heightNotes.join(" ") } : {}
|
|
204748
204914
|
};
|
|
204749
204915
|
output({
|
|
204750
204916
|
json: opts.json,
|
|
@@ -204764,11 +204930,28 @@ Exit codes:
|
|
|
204764
204930
|
String(d.expected_rows),
|
|
204765
204931
|
String(d.actual_rows)
|
|
204766
204932
|
])));
|
|
204933
|
+
for (const range of diverged) {
|
|
204934
|
+
const heights = range.divergent_heights;
|
|
204935
|
+
if (!heights)
|
|
204936
|
+
continue;
|
|
204937
|
+
console.error("");
|
|
204938
|
+
console.error(`${range.from_block}-${range.to_block} ${range.dataset}: ${heights.length} diverging height${heights.length === 1 ? "" : "s"}`);
|
|
204939
|
+
for (const h of heights.slice(0, MAX_HEIGHTS_SHOWN)) {
|
|
204940
|
+
console.error(` ${h.height} ${h.parts.join(", ")}`);
|
|
204941
|
+
}
|
|
204942
|
+
if (heights.length > MAX_HEIGHTS_SHOWN) {
|
|
204943
|
+
console.error(dim(` and ${heights.length - MAX_HEIGHTS_SHOWN} more (see --json)`));
|
|
204944
|
+
}
|
|
204945
|
+
}
|
|
204946
|
+
if (heightNotes.length > 0) {
|
|
204947
|
+
console.error("");
|
|
204948
|
+
for (const line of heightNotes)
|
|
204949
|
+
console.error(dim(` ${line}`));
|
|
204950
|
+
}
|
|
204767
204951
|
console.error("");
|
|
204768
204952
|
console.error(dim("These ranges hold different data than the signed archive. Repair with:"));
|
|
204769
|
-
const
|
|
204770
|
-
|
|
204771
|
-
console.error(dim(` secondlayer repair --from-block ${first.from_block} --to-block ${first.to_block}`));
|
|
204953
|
+
for (const range of mergeRepairRanges(diverged)) {
|
|
204954
|
+
console.error(dim(` secondlayer repair --from-block ${range.from_block} --to-block ${range.to_block}`));
|
|
204772
204955
|
}
|
|
204773
204956
|
}
|
|
204774
204957
|
});
|
|
@@ -207861,12 +208044,24 @@ async function runTokenLogin() {
|
|
|
207861
208044
|
}, target);
|
|
207862
208045
|
success(`Logged in as ${account.email} (${target})`);
|
|
207863
208046
|
}
|
|
207864
|
-
|
|
208047
|
+
var defaultLoginCheckDeps = {
|
|
208048
|
+
readSession,
|
|
208049
|
+
verify: (target, token) => httpAt(target, "/api/accounts/me", { bearer: token })
|
|
208050
|
+
};
|
|
208051
|
+
async function shouldContinueLogin(target, options, deps = defaultLoginCheckDeps) {
|
|
207865
208052
|
if (options.force)
|
|
207866
208053
|
return true;
|
|
207867
|
-
const existing = await readSession(target);
|
|
208054
|
+
const existing = await deps.readSession(target);
|
|
207868
208055
|
if (!existing)
|
|
207869
208056
|
return true;
|
|
208057
|
+
try {
|
|
208058
|
+
await deps.verify(target, existing.token);
|
|
208059
|
+
} catch (err) {
|
|
208060
|
+
if (err instanceof CliHttpError && (err.status === 401 || err.status === 403)) {
|
|
208061
|
+
info(`Your saved login for ${existing.email} stopped working (revoked or expired). Signing in again.`);
|
|
208062
|
+
return true;
|
|
208063
|
+
}
|
|
208064
|
+
}
|
|
207870
208065
|
info(`Already logged in as ${existing.email} (${target}).`);
|
|
207871
208066
|
const hint = "Run 'secondlayer logout' first, or re-run with --force to switch accounts.";
|
|
207872
208067
|
if (!process.stdin.isTTY) {
|
|
@@ -207936,10 +208131,10 @@ async function runDeviceLogin(options = {}) {
|
|
|
207936
208131
|
error(err instanceof Error ? err.message : String(err));
|
|
207937
208132
|
process.exit(1);
|
|
207938
208133
|
}
|
|
207939
|
-
info(`
|
|
208134
|
+
info(`Your one-time code: ${start.userCode}`);
|
|
207940
208135
|
const readline = process.stdin.isTTY ? createInterface({ input: process.stdin }) : null;
|
|
207941
208136
|
if (readline) {
|
|
207942
|
-
info("Press Enter to open
|
|
208137
|
+
info("Press Enter to open the sign-in page, then check the code matches.");
|
|
207943
208138
|
readline.once("line", () => openBrowser(start.verificationUrl));
|
|
207944
208139
|
}
|
|
207945
208140
|
info(dim(`Or open ${start.verificationUrl}`));
|
|
@@ -208659,5 +208854,5 @@ registerCreditsCommand(program);
|
|
|
208659
208854
|
registerArchiveCommand(program);
|
|
208660
208855
|
program.parse();
|
|
208661
208856
|
|
|
208662
|
-
//# debugId=
|
|
208857
|
+
//# debugId=32F86A2D9F7E084964756E2164756E21
|
|
208663
208858
|
//# sourceMappingURL=cli.js.map
|