@secondlayer/cli 5.9.0 → 5.10.1

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
@@ -13977,8 +13977,8 @@ function printRunningServices(services) {
13977
13977
  console.log(blue("Services"));
13978
13978
  for (const [name, service] of Object.entries(services)) {
13979
13979
  const portInfo = service.port ? dim(`http://localhost:${service.port}`) : dim("background");
13980
- const status = isProcessRunning(service.pid) ? green("✓") : red("✗");
13981
- console.log(` ${status} ${name.padEnd(10)} ${portInfo} ${dim(`(pid ${service.pid})`)}`);
13980
+ const status2 = isProcessRunning(service.pid) ? green("✓") : red("✗");
13981
+ console.log(` ${status2} ${name.padEnd(10)} ${portInfo} ${dim(`(pid ${service.pid})`)}`);
13982
13982
  }
13983
13983
  }
13984
13984
  async function isContainerRunning(name) {
@@ -26266,14 +26266,14 @@ var require_enoent = __commonJS((exports, module) => {
26266
26266
  return originalEmit.apply(cp, arguments);
26267
26267
  };
26268
26268
  }
26269
- function verifyENOENT(status, parsed) {
26270
- if (isWin && status === 1 && !parsed.file) {
26269
+ function verifyENOENT(status2, parsed) {
26270
+ if (isWin && status2 === 1 && !parsed.file) {
26271
26271
  return notFoundError(parsed.original, "spawn");
26272
26272
  }
26273
26273
  return null;
26274
26274
  }
26275
- function verifyENOENTSync(status, parsed) {
26276
- if (isWin && status === 1 && !parsed.file) {
26275
+ function verifyENOENTSync(status2, parsed) {
26276
+ if (isWin && status2 === 1 && !parsed.file) {
26277
26277
  return notFoundError(parsed.original, "spawnSync");
26278
26278
  }
26279
26279
  return null;
@@ -32324,7 +32324,7 @@ var {
32324
32324
  // package.json
32325
32325
  var package_default = {
32326
32326
  name: "@secondlayer/cli",
32327
- version: "5.9.0",
32327
+ version: "5.10.1",
32328
32328
  repository: {
32329
32329
  type: "git",
32330
32330
  url: "git+https://github.com/ryanwaits/secondlayer.git",
@@ -32373,10 +32373,10 @@ var package_default = {
32373
32373
  dependencies: {
32374
32374
  "@inquirer/prompts": "^8.2.0",
32375
32375
  "@secondlayer/bundler": "^0.3.7",
32376
- "@secondlayer/sdk": "^5.6.0",
32377
- "@secondlayer/shared": "^6.9.0",
32376
+ "@secondlayer/sdk": "^5.8.0",
32377
+ "@secondlayer/shared": "^6.10.0",
32378
32378
  "@secondlayer/stacks": "^2.3.0",
32379
- "@secondlayer/subgraphs": "^3.4.0",
32379
+ "@secondlayer/subgraphs": "^3.5.0",
32380
32380
  "@biomejs/js-api": "^0.7.0",
32381
32381
  "@biomejs/wasm-nodejs": "^1.9.0",
32382
32382
  esbuild: "^0.19.0",
@@ -34449,6 +34449,7 @@ function buildDevnetCompose(opts = {}) {
34449
34449
  const apiImage = `ghcr.io/${owner}/secondlayer-api:${tag}`;
34450
34450
  const indexerImage = `ghcr.io/${owner}/secondlayer-indexer:${tag}`;
34451
34451
  const dbUrl = "postgres://secondlayer:secondlayer@postgres:5432/secondlayer";
34452
+ const DEV_SECRETS_KEY = "deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef";
34452
34453
  return `# Generated by \`sl devnet connect\` — do not edit by hand.
34453
34454
  # Secondlayer app services for local clarinet devnet development.
34454
34455
  name: secondlayer-devnet
@@ -34487,6 +34488,11 @@ services:
34487
34488
  PORT: 3800
34488
34489
  NODE_ENV: production
34489
34490
  DATA_DIR: /data
34491
+ # Shared across api + subgraph-processor so the secret the api encrypts
34492
+ # (e.g. a subscription's signing secret) can be decrypted by the emitter.
34493
+ # OSS auto-generates a per-process key when unset — that splits them.
34494
+ # Local-dev fixed value; not a production secret.
34495
+ SECONDLAYER_SECRETS_KEY: ${DEV_SECRETS_KEY}
34490
34496
  ports:
34491
34497
  - "${apiPort}:3800"
34492
34498
  volumes:
@@ -34549,6 +34555,11 @@ services:
34549
34555
  # index_progress under NETWORK.
34550
34556
  NETWORK: testnet
34551
34557
  STACKS_NETWORK: testnet
34558
+ # The emitter blocks webhook delivery to private/loopback IPs by default.
34559
+ # On a local devnet you deliver to your own localhost receiver, so allow it.
34560
+ SECONDLAYER_ALLOW_PRIVATE_EGRESS: "true"
34561
+ # Must match the api's key so the emitter can decrypt signing secrets.
34562
+ SECONDLAYER_SECRETS_KEY: ${DEV_SECRETS_KEY}
34552
34563
  volumes:
34553
34564
  - subgraphs_data:/data/subgraphs
34554
34565
  depends_on:
@@ -34640,6 +34651,15 @@ function dockerCompose(composePath, args) {
34640
34651
  });
34641
34652
  return res.status ?? 1;
34642
34653
  }
34654
+ var API_URL = process.env.SL_API_URL ?? "http://localhost:3800";
34655
+ var INDEXER_URL = process.env.INDEXER_URL ?? "http://localhost:3700";
34656
+ var SERVICES = [
34657
+ "indexer",
34658
+ "api",
34659
+ "subgraph-processor",
34660
+ "postgres",
34661
+ "migrate"
34662
+ ];
34643
34663
  function registerDevnetCommand(program2) {
34644
34664
  const devnet = program2.command("devnet").description("Run Secondlayer services against a local Clarinet devnet");
34645
34665
  devnet.command("connect").description("Point your clarinet project's devnet at a local Secondlayer stack and start it").option("--project <dir>", "Clarinet project directory (defaults to the nearest Clarinet.toml)").option("--image-tag <tag>", "Published image tag to run", DEFAULT_IMAGE_TAG).option("--owner <owner>", "ghcr image owner (namespace) to pull from").option("--no-up", "Patch config + write compose without starting docker").action(async (options) => {
@@ -34648,6 +34668,12 @@ function registerDevnetCommand(program2) {
34648
34668
  devnet.command("down").description("Stop the local Secondlayer stack started by `sl devnet connect`").option("--project <dir>", "Clarinet project directory").option("--purge", "Also remove volumes (wipes the local index)").action(async (options) => {
34649
34669
  await down(options);
34650
34670
  });
34671
+ devnet.command("status").description("Snapshot of the local stack: ingest, subgraphs, and recent activity").option("-w, --watch", "Refresh every 2s until Ctrl-C").option("-n, --limit <n>", "Recent activity rows to show", "12").action(async (options) => {
34672
+ await status(options);
34673
+ });
34674
+ 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, --tail <n>", "Lines to show from the end of each log", "200").action(async (service, options) => {
34675
+ await logs(service, options);
34676
+ });
34651
34677
  }
34652
34678
  function resolveProject(explicit) {
34653
34679
  if (explicit) {
@@ -34720,6 +34746,126 @@ async function down(options) {
34720
34746
  }
34721
34747
  console.log(`${green("✓")}${options.purge ? " stack stopped, volumes removed" : " stack stopped"}`);
34722
34748
  }
34749
+ async function logs(service, options) {
34750
+ const project = resolveProject(options.project);
34751
+ const composePath = join7(project, COMPOSE_REL);
34752
+ if (!existsSync3(composePath)) {
34753
+ error(`No ${COMPOSE_REL} in ${project} — run \`sl devnet connect\` first.`);
34754
+ process.exit(1);
34755
+ }
34756
+ if (service && !SERVICES.includes(service)) {
34757
+ error(`Unknown service "${service}". Choose one of: ${SERVICES.join(", ")}`);
34758
+ process.exit(1);
34759
+ }
34760
+ ensureDocker();
34761
+ const args = ["logs", "--tail", options.tail];
34762
+ if (options.follow)
34763
+ args.push("-f");
34764
+ if (service)
34765
+ args.push(service);
34766
+ process.exit(dockerCompose(composePath, args));
34767
+ }
34768
+ async function jget(url) {
34769
+ try {
34770
+ const r = await fetch(url, { signal: AbortSignal.timeout(1500) });
34771
+ return r.ok ? await r.json() : null;
34772
+ } catch {
34773
+ return null;
34774
+ }
34775
+ }
34776
+ var SYS_COLS = new Set([
34777
+ "_id",
34778
+ "_block_height",
34779
+ "_tx_id",
34780
+ "_created_at",
34781
+ "block_height",
34782
+ "tx_id"
34783
+ ]);
34784
+ function shortVal(v) {
34785
+ const s = String(v);
34786
+ return s.length > 18 ? `${s.slice(0, 6)}…${s.slice(-4)}` : s;
34787
+ }
34788
+ async function renderStatus(limit) {
34789
+ const [health, integrity, subsRes, apiHealth] = await Promise.all([
34790
+ jget(`${INDEXER_URL}/health`),
34791
+ jget(`${INDEXER_URL}/health/integrity`),
34792
+ jget(`${API_URL}/api/subgraphs`),
34793
+ jget(`${API_URL}/health`)
34794
+ ]);
34795
+ const out = [];
34796
+ out.push(bold("STACK"));
34797
+ out.push(` ${health ? green("●") : red("●")} indexer ${health ? "healthy" : "down"} ${dim(":3700")}`);
34798
+ out.push(` ${apiHealth ? green("●") : red("●")} api ${apiHealth ? "healthy" : "down"} ${dim(":3800")}`);
34799
+ if (!health) {
34800
+ out.push("");
34801
+ out.push(dim(" indexer unreachable — is `sl devnet connect` running?"));
34802
+ return out.join(`
34803
+ `);
34804
+ }
34805
+ const tip = Number(health.lastSeenHeight ?? integrity?.lastIndexedBlock ?? 0);
34806
+ const indexed = Number(integrity?.lastIndexedBlock ?? tip);
34807
+ const lag = Math.max(0, tip - indexed);
34808
+ const ago = health.lastBlockReceivedSecondsAgo;
34809
+ out.push("");
34810
+ out.push(bold("INGEST"));
34811
+ out.push(` chain tip ${cyan(String(tip))} indexed ${indexed} lag ${lag === 0 ? green("caught up") : yellow(`${lag} blk`)} ${dim(`last block ${ago != null ? `${ago}s ago` : "?"}`)}`);
34812
+ const subs = subsRes?.data ?? [];
34813
+ out.push("");
34814
+ out.push(bold("SUBGRAPHS"));
34815
+ if (subs.length === 0) {
34816
+ out.push(dim(" none deployed — sl subgraphs deploy ./subgraph.ts"));
34817
+ } else {
34818
+ for (const sg of subs) {
34819
+ const st = sg.status === "active" ? green(sg.status) : yellow(String(sg.status));
34820
+ const tables = (sg.tables ?? []).join(", ");
34821
+ out.push(` ${sg.name} ${st} ${dim(`block ${sg.lastProcessedBlock}`)} ${tables} ${dim(`· ${sg.totalRows ?? 0} rows`)}`);
34822
+ }
34823
+ }
34824
+ out.push("");
34825
+ out.push(bold("ACTIVITY") + dim(" (recent)"));
34826
+ const rows = [];
34827
+ for (const sg of subs) {
34828
+ for (const table of sg.tables ?? []) {
34829
+ const res = await jget(`${API_URL}/api/subgraphs/${sg.name}/${table}?_limit=${limit}&_sort=_block_height&_order=desc`);
34830
+ for (const row of res?.data ?? []) {
34831
+ const summary = Object.entries(row).filter(([k]) => !SYS_COLS.has(k)).map(([, v]) => shortVal(v)).join(" ");
34832
+ rows.push({ block: Number(row._block_height ?? 0), table, summary });
34833
+ }
34834
+ }
34835
+ }
34836
+ rows.sort((a, b2) => b2.block - a.block);
34837
+ if (rows.length === 0) {
34838
+ out.push(dim(" no indexed rows yet — fire a contract call"));
34839
+ } else {
34840
+ for (const r of rows.slice(0, limit)) {
34841
+ out.push(` ${dim(String(r.block).padStart(5))} ${r.table.padEnd(16)} ${r.summary}`);
34842
+ }
34843
+ }
34844
+ return out.join(`
34845
+ `);
34846
+ }
34847
+ async function status(options) {
34848
+ const limit = Math.max(1, Number(options.limit) || 12);
34849
+ if (!options.watch) {
34850
+ console.log(await renderStatus(limit));
34851
+ return;
34852
+ }
34853
+ const tick = async () => {
34854
+ const body = await renderStatus(limit);
34855
+ process.stdout.write("\x1B[2J\x1B[H");
34856
+ console.log(`${dim("sl devnet status")} ${dim("· ctrl-c to stop")}
34857
+ `);
34858
+ console.log(body);
34859
+ };
34860
+ await tick();
34861
+ const id = setInterval(() => void tick(), 2000);
34862
+ process.on("SIGINT", () => {
34863
+ clearInterval(id);
34864
+ process.stdout.write(`
34865
+ `);
34866
+ process.exit(0);
34867
+ });
34868
+ }
34723
34869
  // src/commands/subgraphs.ts
34724
34870
  import { spawn } from "node:child_process";
34725
34871
  import {
@@ -36348,8 +36494,8 @@ ${rows.length} row(s)`));
36348
36494
  success(result.message);
36349
36495
  } catch (delErr) {
36350
36496
  const msg = delErr instanceof Error ? delErr.message : String(delErr);
36351
- const status = delErr?.status;
36352
- if (status === 404 || /not found/i.test(msg)) {
36497
+ const status2 = delErr?.status;
36498
+ if (status2 === 404 || /not found/i.test(msg)) {
36353
36499
  info(`Subgraph "${name}" not found (already deleted?)`);
36354
36500
  return;
36355
36501
  }
@@ -36879,15 +37025,15 @@ async function checkHealth() {
36879
37025
  try {
36880
37026
  const c = JSON.parse(line);
36881
37027
  const name = c.Names || c.Name;
36882
- const status = c.Status || "unknown";
37028
+ const status2 = c.Status || "unknown";
36883
37029
  let health = "unknown";
36884
- if (status.includes("healthy"))
37030
+ if (status2.includes("healthy"))
36885
37031
  health = "healthy";
36886
- else if (status.includes("unhealthy"))
37032
+ else if (status2.includes("unhealthy"))
36887
37033
  health = "unhealthy";
36888
- else if (status.includes("Up"))
37034
+ else if (status2.includes("Up"))
36889
37035
  health = "running";
36890
- else if (status.includes("Exited"))
37036
+ else if (status2.includes("Exited"))
36891
37037
  health = "exited";
36892
37038
  let restartCount = 0;
36893
37039
  try {
@@ -36896,7 +37042,7 @@ async function checkHealth() {
36896
37042
  restartCount = Number.parseInt(inspect2.stdout.toString().trim()) || 0;
36897
37043
  }
36898
37044
  } catch {}
36899
- containers.push({ name, status, health, restartCount });
37045
+ containers.push({ name, status: status2, health, restartCount });
36900
37046
  if (health === "unhealthy") {
36901
37047
  issues.push(`Container ${name} is unhealthy`);
36902
37048
  }
@@ -37310,13 +37456,13 @@ async function showNodeLogs(options2) {
37310
37456
  console.log(dim("Run 'sl local node start' first"));
37311
37457
  process.exit(1);
37312
37458
  }
37313
- const logs = await getNodeLogs({
37459
+ const logs2 = await getNodeLogs({
37314
37460
  follow: options2.follow,
37315
37461
  lines: Number.parseInt(options2.lines),
37316
37462
  quiet: options2.quiet,
37317
37463
  format: true
37318
37464
  });
37319
- for await (const line of logs) {
37465
+ for await (const line of logs2) {
37320
37466
  console.log(line);
37321
37467
  }
37322
37468
  }
@@ -37342,8 +37488,8 @@ async function showStaticCombinedLogs(showDev, showNode, lines, quiet) {
37342
37488
  }
37343
37489
  }
37344
37490
  if (showNode && await isNodeRunning()) {
37345
- const logs = await getNodeLogs({ lines, quiet, format: true });
37346
- for await (const line of logs) {
37491
+ const logs2 = await getNodeLogs({ lines, quiet, format: true });
37492
+ for await (const line of logs2) {
37347
37493
  const tsMatch = line.match(/\[(\d{4}-\d{2}-\d{2}T[\d:.]+Z)\]/);
37348
37494
  const timestamp = tsMatch ? new Date(tsMatch[1]) : new Date(0);
37349
37495
  allLines.push({ service: "node", line, timestamp });
@@ -37696,5 +37842,5 @@ registerAccountCommand(program);
37696
37842
  registerBillingCommand(program);
37697
37843
  program.parse();
37698
37844
 
37699
- //# debugId=FDDBCAB122A19D1564756E2164756E21
37845
+ //# debugId=7F4D9295EECF14D064756E2164756E21
37700
37846
  //# sourceMappingURL=cli.js.map