@secondlayer/cli 14.1.1 → 14.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.
package/dist/cli.js CHANGED
@@ -34963,14 +34963,13 @@ var init_node_impl = __esm(() => {
34963
34963
  // src/lib/archive-reference.ts
34964
34964
  import { createHash as createHash4 } from "node:crypto";
34965
34965
  import { readFile as readFile5 } from "node:fs/promises";
34966
- import { dirname as dirname16, join as join13, resolve as resolve13, sep as sep2 } from "node:path";
34966
+ import { join as join13, resolve as resolve13, sep as sep2 } from "node:path";
34967
34967
  import { parseRetryAfter } from "@secondlayer/sdk";
34968
- import { ARCHIVE_ROOT_PUBLIC_KEY_PEM } from "@secondlayer/shared/archive/root-key";
34969
- import { verifyStreamsBulkManifestSignature } from "@secondlayer/shared/streams-bulk-manifest";
34970
- function isLatestPointer(value) {
34971
- const pointer = value;
34972
- return typeof pointer.snapshot_path === "string" && (value.partitions === undefined || value.partitions.length === 0);
34973
- }
34968
+ import {
34969
+ checkSignature,
34970
+ loadReference,
34971
+ resolveArchivePublicKey
34972
+ } from "@secondlayer/shared/archive/reference";
34974
34973
  function assertPathWithinRoot(path11, root) {
34975
34974
  const segments = path11.split("/");
34976
34975
  const leaves = path11.startsWith("/") || path11.includes("\\") || /^[a-z][a-z0-9+.-]*:/i.test(path11) || /[%?#]/.test(path11) || segments.some((part) => part === "" || part === "." || part === "..");
@@ -34985,109 +34984,6 @@ function assertPathWithinRoot(path11, root) {
34985
34984
  }
34986
34985
  }
34987
34986
  }
34988
- async function fetchJson(url) {
34989
- const response2 = await fetch(url, { signal: AbortSignal.timeout(30000) });
34990
- if (!response2.ok) {
34991
- throw new Error(`could not fetch manifest (${response2.status} ${response2.statusText})`);
34992
- }
34993
- return response2.json();
34994
- }
34995
- async function loadReference(source, options3 = {}) {
34996
- const isRemote = /^https?:\/\//.test(source);
34997
- let manifest;
34998
- let root;
34999
- if (isRemote) {
35000
- manifest = await fetchJson(source);
35001
- const url = new URL(source);
35002
- url.pathname = url.pathname.replace(/\/snapshots\/[^/]+$/, "").replace(/\/[^/]+\.json$/, "");
35003
- root = url.toString();
35004
- } else {
35005
- const path11 = resolve13(source);
35006
- manifest = JSON.parse(await readFile5(path11, "utf8"));
35007
- root = isLatestPointer(manifest) ? dirname16(path11) : dirname16(dirname16(path11));
35008
- }
35009
- if (!isLatestPointer(manifest)) {
35010
- return { manifest, origin: source, root, isRemote };
35011
- }
35012
- const pointer = manifest;
35013
- const snapshotPath = pointer.snapshot_path;
35014
- if (!SNAPSHOT_PATH_PATTERN.test(snapshotPath)) {
35015
- throw new Error(`pointer names an invalid snapshot path: ${snapshotPath} (expected snapshots/<sha256>.json)`);
35016
- }
35017
- if (typeof pointer.snapshot_digest !== "string" || !/^[0-9a-f]{64}$/.test(pointer.snapshot_digest)) {
35018
- throw new Error("pointer carries no snapshot_digest, so it cannot prove which snapshot is current");
35019
- }
35020
- if (pointer.signature !== undefined) {
35021
- if (!options3.publicKeyPem) {
35022
- throw new Error("pointer is signed but no public key is available to verify it");
35023
- }
35024
- let verified = false;
35025
- try {
35026
- verified = verifyStreamsBulkManifestSignature(pointer, options3.publicKeyPem);
35027
- } catch {
35028
- verified = false;
35029
- }
35030
- if (!verified) {
35031
- throw new Error("pointer signature did not verify against the archive key");
35032
- }
35033
- }
35034
- const resolved = isRemote ? await fetchJson(`${root.replace(/\/$/, "")}/${snapshotPath}`) : JSON.parse(await readFile5(join13(root, snapshotPath), "utf8"));
35035
- const digest = createHash4("sha256").update(JSON.stringify((({ signature: _s, key_id: _k, ...rest }) => rest)(resolved))).digest("hex");
35036
- if (digest !== pointer.snapshot_digest) {
35037
- throw new Error(`pointer/snapshot mismatch: latest.json names ${pointer.snapshot_digest}, resolved manifest hashes to ${digest}`);
35038
- }
35039
- return { manifest: resolved, origin: source, root, isRemote };
35040
- }
35041
- function checkSignature(manifest, publicKeyPem, insecure) {
35042
- if (insecure)
35043
- return { verified: false, reason: "signature check skipped" };
35044
- if (!manifest.signature) {
35045
- return { verified: false, reason: "manifest carries no signature" };
35046
- }
35047
- if (!publicKeyPem) {
35048
- return {
35049
- verified: false,
35050
- reason: "no public key available to verify with"
35051
- };
35052
- }
35053
- try {
35054
- const verified = verifyStreamsBulkManifestSignature(manifest, publicKeyPem);
35055
- return {
35056
- verified,
35057
- reason: verified ? undefined : "signature did not verify"
35058
- };
35059
- } catch (err) {
35060
- return {
35061
- verified: false,
35062
- reason: err instanceof Error ? err.message : "signature check failed"
35063
- };
35064
- }
35065
- }
35066
- async function fetchHostedKey(url) {
35067
- if (!url.startsWith("https://"))
35068
- return;
35069
- try {
35070
- const response2 = await fetch(url, { signal: AbortSignal.timeout(1e4) });
35071
- if (!response2.ok)
35072
- return;
35073
- const body = await response2.json();
35074
- return typeof body.public_key_pem === "string" ? body.public_key_pem : undefined;
35075
- } catch {
35076
- return;
35077
- }
35078
- }
35079
- async function resolveArchivePublicKey(input4) {
35080
- if (input4.explicitPem)
35081
- return input4.explicitPem;
35082
- if (input4.envPem)
35083
- return input4.envPem;
35084
- if (input4.allowHostedApi) {
35085
- const hosted = await fetchHostedKey(input4.hostedKeyUrl ?? HOSTED_SIGNING_KEY_URL);
35086
- if (hosted)
35087
- return hosted;
35088
- }
35089
- return ARCHIVE_ROOT_PUBLIC_KEY_PEM;
35090
- }
35091
34987
  function isTransientCode(code) {
35092
34988
  return typeof code === "string" && (TRANSIENT_ERROR_CODES.has(code) || code.startsWith("UND_ERR_"));
35093
34989
  }
@@ -35178,9 +35074,8 @@ async function fetchVerifiedPartition(reference, partition, gate, fetchOpts) {
35178
35074
  }
35179
35075
  return bytes;
35180
35076
  }
35181
- var SNAPSHOT_PATH_PATTERN, HOSTED_SIGNING_KEY_URL = "https://api.secondlayer.tools/public/streams/signing-key", ArchiveFetchError, TRANSIENT_ERROR_CODES, PARTITION_FETCH_RETRIES = 3, PARTITION_FETCH_TIMEOUT_MS = 300000, RETRY_BASE_MS = 1000, RETRY_AFTER_CAP_MS = 60000;
35077
+ var ArchiveFetchError, TRANSIENT_ERROR_CODES, PARTITION_FETCH_RETRIES = 3, PARTITION_FETCH_TIMEOUT_MS = 300000, RETRY_BASE_MS = 1000, RETRY_AFTER_CAP_MS = 60000;
35182
35078
  var init_archive_reference = __esm(() => {
35183
- SNAPSHOT_PATH_PATTERN = /^snapshots\/[0-9a-f]{64}\.json$/;
35184
35079
  ArchiveFetchError = class ArchiveFetchError extends Error {
35185
35080
  transient;
35186
35081
  attempts;
@@ -35207,7 +35102,7 @@ var init_archive_reference = __esm(() => {
35207
35102
  import { randomBytes as randomBytes3 } from "node:crypto";
35208
35103
  import { existsSync as existsSync8, readFileSync as readFileSync7, writeFileSync as writeFileSync7 } from "node:fs";
35209
35104
  import { resolve as resolve14 } from "node:path";
35210
- import { ARCHIVE_ROOT_PUBLIC_KEY_PEM as ARCHIVE_ROOT_PUBLIC_KEY_PEM2 } from "@secondlayer/shared/archive/root-key";
35105
+ import { ARCHIVE_ROOT_PUBLIC_KEY_PEM } from "@secondlayer/shared/archive/root-key";
35211
35106
  import { generateEd25519KeyPair } from "@secondlayer/shared/crypto/ed25519";
35212
35107
  import { INSTANCE_NETWORKS } from "@secondlayer/shared/db/queries/instance";
35213
35108
  function parseInstanceNetwork(value) {
@@ -35261,7 +35156,7 @@ function buildInstanceEnv(input4) {
35261
35156
  const secrets = input4.existing?.SECONDLAYER_SECRETS_KEY || generateSecretsKey();
35262
35157
  const signing = input4.existing?.STREAMS_SIGNING_PRIVATE_KEY || input4.existing?.SECONDLAYER_WEBHOOK_SIGNING_PRIVATE_KEY || generateSigningPrivateKey();
35263
35158
  const apiUrl = input4.apiUrl ?? "http://127.0.0.1:3800";
35264
- const archiveKey = input4.existing?.ARCHIVE_SIGNING_PUBLIC_KEY || input4.archivePublicKeyPem || ARCHIVE_ROOT_PUBLIC_KEY_PEM2;
35159
+ const archiveKey = input4.existing?.ARCHIVE_SIGNING_PUBLIC_KEY || input4.archivePublicKeyPem || ARCHIVE_ROOT_PUBLIC_KEY_PEM;
35265
35160
  return {
35266
35161
  INSTANCE_MODE: "oss",
35267
35162
  STACKS_NETWORK: input4.network,
@@ -36792,7 +36687,7 @@ var {
36792
36687
  // package.json
36793
36688
  var package_default = {
36794
36689
  name: "@secondlayer/cli",
36795
- version: "14.1.1",
36690
+ version: "14.1.2",
36796
36691
  description: "Run a Stacks index on your own hardware — set it up, restore verified history, and keep it current.",
36797
36692
  repository: {
36798
36693
  type: "git",
@@ -36837,8 +36732,8 @@ var package_default = {
36837
36732
  "@opentui/react": "^0.5.3",
36838
36733
  "@secondlayer/bundler": "^0.5.1",
36839
36734
  "@secondlayer/scaffold": "^1.5.5",
36840
- "@secondlayer/sdk": "^10.1.1",
36841
- "@secondlayer/shared": "^11.2.1",
36735
+ "@secondlayer/sdk": "^10.2.0",
36736
+ "@secondlayer/shared": "^11.3.0",
36842
36737
  "@secondlayer/stacks": "^5.1.0",
36843
36738
  "@secondlayer/subgraphs": "^4.1.5",
36844
36739
  esbuild: "^0.27.4",
@@ -37456,112 +37351,14 @@ import {
37456
37351
  } from "@secondlayer/sdk";
37457
37352
 
37458
37353
  // src/lib/instance-diagnosis.ts
37459
- var BOOTSTRAP_STEP = "Restore verified history: secondlayer bootstrap --against <manifest>";
37460
- var OBSERVER_STEP = "Confirm your node streams into this instance — secondlayer observer --mode indexer --endpoint secondlayer:3700 prints the stanza its config needs";
37461
- var SELF_HOST_DOCS_STEP = "Docs: https://www.secondlayer.tools/docs/self-host";
37462
- function hasNoChainData(status) {
37463
- const integrity = status.chainIntegrity;
37464
- if (!integrity || integrity.reason === "check_failed")
37465
- return false;
37466
- if (integrity.maxHeight !== 0)
37467
- return false;
37468
- return (status.chainTip ?? null) === null;
37469
- }
37470
- function emptyIndexIssue(status) {
37471
- const decoders = status.index?.decoders ?? [];
37472
- const stalled = decoders.filter((d2) => d2.status !== "ok").length;
37473
- const decoderClause = stalled > 0 ? ` and ${stalled} decoder${stalled === 1 ? "" : "s"} report unavailable because there is nothing to decode` : "";
37474
- return {
37475
- title: "No blocks indexed yet — this instance holds 0 blocks.",
37476
- detail: `Streams has no tip${decoderClause}, which is what makes the overall status DEGRADED. This is the expected state for a fresh instance: nothing is broken, but no read returns data until history lands.`,
37477
- nextSteps: [BOOTSTRAP_STEP, OBSERVER_STEP, SELF_HOST_DOCS_STEP]
37478
- };
37479
- }
37480
- var SERVICE_STEPS = [
37481
- "See the raw per-service report: secondlayer status --json",
37482
- "Check the containers from docker/oss: docker compose ps, then docker compose logs --tail=50 secondlayer"
37483
- ];
37484
- function diagnoseInstanceStatus(status) {
37485
- const overall = String(status.status ?? "unknown");
37486
- if (hasNoChainData(status)) {
37487
- return { state: "empty-index", overall, issues: [emptyIndexIssue(status)] };
37488
- }
37489
- const issues = [];
37490
- for (const service of status.services ?? []) {
37491
- if (service.status === "ok")
37492
- continue;
37493
- issues.push({
37494
- title: `Service ${service.name} reports ${service.status}.`,
37495
- nextSteps: SERVICE_STEPS
37496
- });
37497
- }
37498
- const integrity = status.chainIntegrity;
37499
- if (integrity && integrity.ok === false) {
37500
- issues.push({
37501
- title: "Chain-data integrity check failed.",
37502
- detail: integrity.reason ?? "The tip the instance serves is not backed by the history it implies.",
37503
- nextSteps: [
37504
- "Verify against the archive: secondlayer verify all --against <manifest>",
37505
- "Repair what diverged: secondlayer repair --against <manifest> --apply"
37506
- ]
37507
- });
37508
- }
37509
- if (issues.length === 0 && overall !== "healthy") {
37510
- issues.push({
37511
- title: `The API reports status=${overall} but names no failing service.`,
37512
- detail: "Every service in the report is ok, so the degradation is in a surface this command does not summarize.",
37513
- nextSteps: SERVICE_STEPS
37514
- });
37515
- }
37516
- return {
37517
- state: issues.length > 0 ? "degraded" : "healthy",
37518
- overall,
37519
- issues
37520
- };
37521
- }
37522
- var CONTEXT_FIELDS = [
37523
- "account",
37524
- "streamsTip",
37525
- "indexTip",
37526
- "subgraphs",
37527
- "subscriptions",
37528
- "activeOperations"
37529
- ];
37530
- function explainContextNulls(snapshot, probe) {
37531
- const nullFields = CONTEXT_FIELDS.filter((field) => (snapshot[field] ?? null) === null);
37532
- if (nullFields.length === 0) {
37533
- return { summary: "Every field resolved.", nulls: {} };
37534
- }
37535
- if (!probe.status) {
37536
- const reason = `No instance answered at ${probe.apiUrl} (${probe.statusError ?? "unreachable"}), so this read never reached an API.`;
37537
- return {
37538
- summary: `${reason} Start it from docker/oss with: docker compose up -d — or point SL_API_URL at the instance you meant.`,
37539
- nulls: Object.fromEntries(nullFields.map((field) => [field, reason]))
37540
- };
37541
- }
37542
- const emptyIndex = hasNoChainData(probe.status);
37543
- const blocks = probe.status.chainIntegrity?.maxHeight ?? null;
37544
- const accountReason = probe.selfHosted ? "Expected: a self-hosted instance has no account system. Accounts exist only on the metered archive deployment at api.secondlayer.tools, so null here is normal and not a failure." : "Not authenticated — run `secondlayer login`, or export INSTANCE_TOKEN.";
37545
- const listReason = unresolvedReadReason(probe, blocks);
37546
- const chainReason = emptyIndex ? `No blocks indexed yet — this instance holds 0 blocks, so there is no tip to report. ${BOOTSTRAP_STEP}` : listReason;
37547
- const nulls = {};
37548
- for (const field of nullFields) {
37549
- if (field === "account")
37550
- nulls[field] = accountReason;
37551
- else if (field === "streamsTip" || field === "indexTip")
37552
- nulls[field] = chainReason;
37553
- else if (field === "activeOperations")
37554
- nulls[field] = (snapshot.subgraphs ?? null) === null ? "Not probed: in-flight operations are read per subgraph, and the subgraph list did not resolve." : listReason;
37555
- else
37556
- nulls[field] = listReason;
37557
- }
37558
- const summary = emptyIndex ? `Instance at ${probe.apiUrl} is reachable but holds 0 blocks — the chain fields are null because nothing has been indexed yet. ${BOOTSTRAP_STEP}` : nullFields.length === 1 && nullFields[0] === "account" ? `Instance at ${probe.apiUrl} is reachable and indexed to block ${blocks ?? "unknown"}; only \`account\` is null, which is normal when self-hosting.` : `Instance at ${probe.apiUrl} is reachable, but ${nullFields.length} field${nullFields.length === 1 ? "" : "s"} did not resolve — see \`nulls\` below for each one.`;
37559
- return { summary, nulls };
37560
- }
37561
- function unresolvedReadReason(probe, blocks) {
37562
- const where = `The instance at ${probe.apiUrl} is reachable${blocks !== null ? ` and indexed to block ${blocks}` : ""}, but this read returned nothing.`;
37563
- return probe.hasCredential ? `${where} Check the surface directly: secondlayer status --json` : `${where} No credential resolved: export INSTANCE_TOKEN — an instance published past loopback requires it on every /v1 read.`;
37564
- }
37354
+ import {
37355
+ BOOTSTRAP_STEP,
37356
+ OBSERVER_STEP,
37357
+ SELF_HOST_DOCS_STEP,
37358
+ diagnoseInstanceStatus,
37359
+ explainContextNulls,
37360
+ hasNoChainData
37361
+ } from "@secondlayer/shared/archive/instance-diagnosis";
37565
37362
 
37566
37363
  // src/commands/context.ts
37567
37364
  init_output();
@@ -42769,5 +42566,5 @@ program.commandsGroup("Archive:");
42769
42566
  registerCreditsCommand(program);
42770
42567
  program.parse();
42771
42568
 
42772
- //# debugId=A5FE446DEB77888264756E2164756E21
42569
+ //# debugId=54476D3169B18AA964756E2164756E21
42773
42570
  //# sourceMappingURL=cli.js.map