@secondlayer/cli 23.2.0 → 23.3.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 CHANGED
@@ -2360,7 +2360,7 @@ function ignoredEnvKeyNote(ignored) {
2360
2360
  return ignored ? " INSTANCE_TOKEN was ignored: it unlocks this instance, not your account." : "";
2361
2361
  }
2362
2362
  async function httpArchiveOps(path, opts = {}) {
2363
- const { bearer, ignoredEnvKey } = await resolveArchiveOpsBearer();
2363
+ const { bearer, source, ignoredEnvKey } = await resolveArchiveOpsBearer();
2364
2364
  if (!bearer) {
2365
2365
  throw new CliHttpError(401, "SESSION_EXPIRED", { error: "Not logged in" }, `This needs your Secondlayer account. ${ARCHIVE_AUTH_HINT}${ignoredEnvKeyNote(ignoredEnvKey)}`);
2366
2366
  }
@@ -2371,7 +2371,8 @@ async function httpArchiveOps(path, opts = {}) {
2371
2371
  });
2372
2372
  } catch (err) {
2373
2373
  if (err instanceof CliHttpError && err.status === 401) {
2374
- throw new CliHttpError(401, err.code, err.body, `Your account rejected the credential (${err.message}). ${ARCHIVE_AUTH_HINT}${ignoredEnvKeyNote(ignoredEnvKey)}`);
2374
+ const remedy = source === "session" ? `Your saved login was rejected (${err.message}). Run \`secondlayer login\`, or \`secondlayer logout\` to use SECONDLAYER_API_KEY.` : `Your account rejected the credential (${err.message}). ${ARCHIVE_AUTH_HINT}${ignoredEnvKeyNote(ignoredEnvKey)}`;
2375
+ throw new CliHttpError(401, err.code, err.body, remedy);
2375
2376
  }
2376
2377
  throw err;
2377
2378
  }
@@ -31891,7 +31892,7 @@ var {
31891
31892
  // package.json
31892
31893
  var package_default = {
31893
31894
  name: "@secondlayer/cli",
31894
- version: "23.2.0",
31895
+ version: "23.3.0",
31895
31896
  description: "Run a Stacks index on your own hardware — set it up, restore verified history, and keep it current.",
31896
31897
  repository: {
31897
31898
  type: "git",
@@ -31917,7 +31918,7 @@ var package_default = {
31917
31918
  build: "bunup",
31918
31919
  dev: "bunup --watch",
31919
31920
  test: "bun test",
31920
- typecheck: "tsc --noEmit",
31921
+ typecheck: "bun check",
31921
31922
  prepublishOnly: "bun run build"
31922
31923
  },
31923
31924
  keywords: [
@@ -31941,7 +31942,7 @@ var package_default = {
31941
31942
  "@secondlayer/bundler": "^0.7.3",
31942
31943
  "@secondlayer/scaffold": "^1.6.3",
31943
31944
  "@secondlayer/sdk": "^16.2.0",
31944
- "@secondlayer/shared": "^15.0.0",
31945
+ "@secondlayer/shared": "^15.0.1",
31945
31946
  "@secondlayer/stacks": "^6.5.3",
31946
31947
  "@secondlayer/subgraphs": "^8.0.1",
31947
31948
  "@secondlayer/verify": "^0.6.1",
@@ -35610,14 +35611,14 @@ import { dirname as dirname7, join as join10 } from "node:path";
35610
35611
  var DEFAULT_IMAGE_OWNER = "ryanwaits";
35611
35612
  var DEFAULT_IMAGE_TAG = "latest";
35612
35613
  var DEV_INSTANCE_TOKEN = "dev-instance-token";
35614
+ var DEV_WEBHOOK_SIGNING_KEY = "-----BEGIN PRIVATE KEY-----\\nMC4CAQAwBQYDK2VwBCIEIIczaKV9CBblFKXjqGwrA3A1FOJxY2HiST7++XGea/jX\\n-----END PRIVATE KEY-----";
35613
35615
  function buildDevnetCompose(opts = {}) {
35614
35616
  const owner = opts.owner ?? DEFAULT_IMAGE_OWNER;
35615
35617
  const tag = opts.imageTag ?? DEFAULT_IMAGE_TAG;
35616
35618
  const apiPort = opts.apiPort ?? 3800;
35617
35619
  const indexerPort = opts.indexerPort ?? 3700;
35618
35620
  const apiPublish = `127.0.0.1:${apiPort}`;
35619
- const apiImage = `ghcr.io/${owner}/secondlayer-api:${tag}`;
35620
- const indexerImage = `ghcr.io/${owner}/secondlayer-indexer:${tag}`;
35621
+ const runtimeImage = `ghcr.io/${owner}/secondlayer-runtime:${tag}`;
35621
35622
  const dbUrl = "postgres://secondlayer:secondlayer@postgres:5432/secondlayer";
35622
35623
  const DEV_SECRETS_KEY = "deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef";
35623
35624
  return `# Generated by \`secondlayer devnet connect\` — do not edit by hand.
@@ -35640,22 +35641,24 @@ services:
35640
35641
  retries: 10
35641
35642
  restart: unless-stopped
35642
35643
 
35643
- migrate:
35644
- image: ${apiImage}
35645
- command: ["bun", "run", "packages/shared/src/db/migrate.ts"]
35646
- environment:
35647
- DATABASE_URL: ${dbUrl}
35648
- depends_on:
35649
- postgres:
35650
- condition: service_healthy
35651
- restart: "no"
35652
-
35653
- api:
35654
- image: ${apiImage}
35644
+ secondlayer:
35645
+ image: ${runtimeImage}
35655
35646
  environment:
35656
35647
  INSTANCE_MODE: oss
35648
+ # Keep every network key aligned: the indexer writes index_progress under
35649
+ # STACKS_NETWORK, the subgraph processor reads it under NETWORK. If they
35650
+ # differ, catch-up finds no progress row and subgraphs never advance.
35651
+ NETWORKS: testnet
35652
+ STACKS_NETWORK: testnet
35653
+ NETWORK: testnet
35654
+ NODE_MODE: external
35655
+ # A devnet index is tiny; the runtime's RAM/disk floors target real
35656
+ # networks and a laptop's Docker VM often has less than 80 GB free.
35657
+ SECONDLAYER_ALLOW_UNDERSIZED: "true"
35657
35658
  DATABASE_URL: ${dbUrl}
35658
- PORT: 3800
35659
+ DATA_DIR: /data
35660
+ API_PORT: "3800"
35661
+ INDEXER_PORT: "3700"
35659
35662
  # 0.0.0.0 is forced: a container process that binds loopback is
35660
35663
  # unreachable through a published port. The real reach is the publish
35661
35664
  # spec below, handed over verbatim so /v1 reads stay keyless on devnet.
@@ -35664,93 +35667,48 @@ services:
35664
35667
  # Boot guard reads LISTEN_HOST, so a token is required to start at all.
35665
35668
  # It gates writes only — reads are open on this loopback publish.
35666
35669
  INSTANCE_TOKEN: ${DEV_INSTANCE_TOKEN}
35667
- NODE_ENV: production
35668
- DATA_DIR: /data
35669
- # Shared across api + subgraph-processor so the secret the api encrypts
35670
- # (e.g. a webhook's signing secret) can be decrypted by the emitter.
35671
- # OSS auto-generates a per-process key when unset — that splits them.
35672
- # Local-dev fixed value; not a production secret.
35670
+ # Fixed so the secret the api encrypts (e.g. a webhook's signing secret)
35671
+ # can be decrypted by the emitter across restarts. Not a production secret.
35673
35672
  SECONDLAYER_SECRETS_KEY: ${DEV_SECRETS_KEY}
35674
- ports:
35675
- - "${apiPublish}:3800"
35676
- volumes:
35677
- - subgraphs_data:/data/subgraphs
35678
- depends_on:
35679
- migrate:
35680
- condition: service_completed_successfully
35681
- postgres:
35682
- condition: service_healthy
35683
- healthcheck:
35684
- test: ["CMD", "curl", "-f", "http://localhost:3800/health"]
35685
- interval: 10s
35686
- timeout: 3s
35687
- start_period: 10s
35688
- retries: 5
35689
- restart: unless-stopped
35690
-
35691
- indexer:
35692
- image: ${indexerImage}
35693
- environment:
35694
- DATABASE_URL: ${dbUrl}
35695
- PORT: 3700
35696
- NODE_ENV: production
35697
- # Keep every network key aligned: the indexer writes index_progress under
35698
- # STACKS_NETWORK, the subgraph processor reads it under NETWORK. If they
35699
- # differ, catch-up finds no progress row and subgraphs never advance.
35700
- NETWORKS: testnet
35701
- STACKS_NETWORK: testnet
35702
- NETWORK: testnet
35673
+ # Signed deliveries, so receivers can test verification. Publicly known.
35674
+ SECONDLAYER_WEBHOOK_SIGNING_PRIVATE_KEY: "${DEV_WEBHOOK_SIGNING_KEY}"
35675
+ # The emitter blocks webhook delivery to private/loopback IPs by default.
35676
+ # On a local devnet you deliver to your own localhost receiver, so allow it.
35677
+ SECONDLAYER_ALLOW_PRIVATE_EGRESS: "true"
35678
+ # An OSS indexer with no index_progress spools observer blocks until an
35679
+ # archive import finishes. A devnet has no archive, so ingest live.
35680
+ INGEST_MODE: live
35703
35681
  # Fed by the devnet node's event observer — never reach out to Hiro mainnet.
35704
35682
  AUTO_BACKFILL_ENABLED: "false"
35705
35683
  TIP_FOLLOWER_ENABLED: "false"
35706
- # Deliberately published on every interface, unlike the api: the devnet's
35707
- # stacks-node runs in its own container and POSTs events to
35708
- # host.docker.internal:3700, which resolves to the host gateway address,
35709
- # not 127.0.0.1. A loopback publish here would drop every block.
35684
+ # clarinet publishes the devnet node RPC on the host's 20443.
35685
+ STACKS_NODE_RPC_URL: http://host.docker.internal:20443
35686
+ STREAMS_API_URL: http://127.0.0.1:3800
35687
+ SUBGRAPH_SOURCE: streams-index
35688
+ SUBGRAPH_INDEX_API_URL: http://127.0.0.1:3800
35689
+ NODE_ENV: production
35690
+ # The api is loopback-only; the indexer is deliberately published on every
35691
+ # interface: the devnet's stacks-node runs in its own container and POSTs
35692
+ # events to host.docker.internal:3700, which resolves to the host gateway
35693
+ # address, not 127.0.0.1. A loopback publish would drop every block.
35710
35694
  ports:
35695
+ - "${apiPublish}:3800"
35711
35696
  - "${indexerPort}:3700"
35712
35697
  extra_hosts:
35713
35698
  - "host.docker.internal:host-gateway"
35699
+ volumes:
35700
+ - subgraphs_data:/data/subgraphs
35714
35701
  depends_on:
35715
- migrate:
35716
- condition: service_completed_successfully
35717
35702
  postgres:
35718
35703
  condition: service_healthy
35719
35704
  healthcheck:
35720
- test: ["CMD", "curl", "-f", "http://localhost:3700/health"]
35705
+ test: ["CMD", "curl", "-f", "http://localhost:3800/health"]
35721
35706
  interval: 10s
35722
35707
  timeout: 3s
35723
- start_period: 10s
35708
+ start_period: 40s
35724
35709
  retries: 5
35725
35710
  restart: unless-stopped
35726
35711
 
35727
- subgraph-processor:
35728
- image: ${apiImage}
35729
- command: ["bun", "run", "packages/subgraphs/src/service.ts"]
35730
- environment:
35731
- DATABASE_URL: ${dbUrl}
35732
- NODE_ENV: production
35733
- DATA_DIR: /data
35734
- # Must match the indexer's network key (see indexer note) — catch-up reads
35735
- # index_progress under NETWORK.
35736
- NETWORK: testnet
35737
- STACKS_NETWORK: testnet
35738
- # The emitter blocks webhook delivery to private/loopback IPs by default.
35739
- # On a local devnet you deliver to your own localhost receiver, so allow it.
35740
- SECONDLAYER_ALLOW_PRIVATE_EGRESS: "true"
35741
- # Must match the api's key so the emitter can decrypt signing secrets.
35742
- SECONDLAYER_SECRETS_KEY: ${DEV_SECRETS_KEY}
35743
- volumes:
35744
- - subgraphs_data:/data/subgraphs
35745
- depends_on:
35746
- migrate:
35747
- condition: service_completed_successfully
35748
- postgres:
35749
- condition: service_healthy
35750
- healthcheck:
35751
- disable: true
35752
- restart: unless-stopped
35753
-
35754
35712
  volumes:
35755
35713
  postgres_data:
35756
35714
  subgraphs_data:
@@ -35965,13 +35923,7 @@ function dockerCompose(composePath, args) {
35965
35923
  }
35966
35924
  var API_URL = (process.env.SECONDLAYER_API_URL || LOCAL_API_URL).replace(/\/+$/, "");
35967
35925
  var INDEXER_URL = process.env.INDEXER_URL ?? "http://localhost:3700";
35968
- var SERVICES = [
35969
- "indexer",
35970
- "api",
35971
- "subgraph-processor",
35972
- "postgres",
35973
- "migrate"
35974
- ];
35926
+ var SERVICES = ["secondlayer", "postgres"];
35975
35927
  function registerDevnetCommand(program) {
35976
35928
  const devnet = program.command("devnet").description("Run Secondlayer services against a local Clarinet devnet");
35977
35929
  registerFaucetCommand(devnet);
@@ -35984,7 +35936,7 @@ function registerDevnetCommand(program) {
35984
35936
  devnet.command("status").description("Snapshot of the local stack: ingest, subgraphs, and recent activity").option("-w, --watch", "Refresh every 2s until Ctrl-C").option("--limit <n>", "Recent activity rows to show", "12").action(async (options) => {
35985
35937
  await status(options);
35986
35938
  });
35987
- devnet.command("logs [service]").description("Tail stack logs — all services, or one of: indexer, api, subgraph-processor, postgres").option("--project <dir>", "Clarinet project directory").option("-f, --follow", "Follow log output").option("-n, --lines <n>", "Lines to show from the end of each log").action(async (service, options) => {
35939
+ devnet.command("logs [service]").description("Tail stack logs — all services, or one of: secondlayer, postgres").option("--project <dir>", "Clarinet project directory").option("-f, --follow", "Follow log output").option("-n, --lines <n>", "Lines to show from the end of each log").action(async (service, options) => {
35988
35940
  await logs(service, options);
35989
35941
  });
35990
35942
  }
@@ -36028,6 +35980,11 @@ Skipped docker. Start it with:
36028
35980
  }
36029
35981
  ensureDocker();
36030
35982
  console.log(dim(`
35983
+ Pulling latest images…`));
35984
+ if (dockerCompose(composePath, ["pull"]) !== 0) {
35985
+ console.log(yellow("couldn't pull the latest images — starting with what's cached"));
35986
+ }
35987
+ console.log(dim(`
36031
35988
  Starting Secondlayer stack (docker compose up -d)…`));
36032
35989
  if (dockerCompose(composePath, ["up", "-d"]) !== 0) {
36033
35990
  error("docker compose up failed — see output above.");
@@ -36104,7 +36061,7 @@ var SYS_COLS = new Set([
36104
36061
  "tx_id"
36105
36062
  ]);
36106
36063
  function shortVal(v) {
36107
- const s = String(v);
36064
+ const s = typeof v === "object" && v !== null ? JSON.stringify(v) : String(v);
36108
36065
  return s.length > 18 ? `${s.slice(0, 6)}…${s.slice(-4)}` : s;
36109
36066
  }
36110
36067
  async function renderStatus(limit) {
@@ -37780,5 +37737,5 @@ registerCreditsCommand(program);
37780
37737
  registerArchiveCommand(program);
37781
37738
  program.parse();
37782
37739
 
37783
- //# debugId=E3E81976D75D13F864756E2164756E21
37740
+ //# debugId=599D8A8ADF6556BF64756E2164756E21
37784
37741
  //# sourceMappingURL=cli.js.map