@secondlayer/cli 5.9.0 → 5.10.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
@@ -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.0",
32328
32328
  repository: {
32329
32329
  type: "git",
32330
32330
  url: "git+https://github.com/ryanwaits/secondlayer.git",
@@ -32373,8 +32373,8 @@ 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.7.0",
32377
+ "@secondlayer/shared": "^6.10.0",
32378
32378
  "@secondlayer/stacks": "^2.3.0",
32379
32379
  "@secondlayer/subgraphs": "^3.4.0",
32380
32380
  "@biomejs/js-api": "^0.7.0",
@@ -34640,6 +34640,15 @@ function dockerCompose(composePath, args) {
34640
34640
  });
34641
34641
  return res.status ?? 1;
34642
34642
  }
34643
+ var API_URL = process.env.SL_API_URL ?? "http://localhost:3800";
34644
+ var INDEXER_URL = process.env.INDEXER_URL ?? "http://localhost:3700";
34645
+ var SERVICES = [
34646
+ "indexer",
34647
+ "api",
34648
+ "subgraph-processor",
34649
+ "postgres",
34650
+ "migrate"
34651
+ ];
34643
34652
  function registerDevnetCommand(program2) {
34644
34653
  const devnet = program2.command("devnet").description("Run Secondlayer services against a local Clarinet devnet");
34645
34654
  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 +34657,12 @@ function registerDevnetCommand(program2) {
34648
34657
  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
34658
  await down(options);
34650
34659
  });
34660
+ 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) => {
34661
+ await status(options);
34662
+ });
34663
+ 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) => {
34664
+ await logs(service, options);
34665
+ });
34651
34666
  }
34652
34667
  function resolveProject(explicit) {
34653
34668
  if (explicit) {
@@ -34720,6 +34735,126 @@ async function down(options) {
34720
34735
  }
34721
34736
  console.log(`${green("✓")}${options.purge ? " stack stopped, volumes removed" : " stack stopped"}`);
34722
34737
  }
34738
+ async function logs(service, options) {
34739
+ const project = resolveProject(options.project);
34740
+ const composePath = join7(project, COMPOSE_REL);
34741
+ if (!existsSync3(composePath)) {
34742
+ error(`No ${COMPOSE_REL} in ${project} — run \`sl devnet connect\` first.`);
34743
+ process.exit(1);
34744
+ }
34745
+ if (service && !SERVICES.includes(service)) {
34746
+ error(`Unknown service "${service}". Choose one of: ${SERVICES.join(", ")}`);
34747
+ process.exit(1);
34748
+ }
34749
+ ensureDocker();
34750
+ const args = ["logs", "--tail", options.tail];
34751
+ if (options.follow)
34752
+ args.push("-f");
34753
+ if (service)
34754
+ args.push(service);
34755
+ process.exit(dockerCompose(composePath, args));
34756
+ }
34757
+ async function jget(url) {
34758
+ try {
34759
+ const r = await fetch(url, { signal: AbortSignal.timeout(1500) });
34760
+ return r.ok ? await r.json() : null;
34761
+ } catch {
34762
+ return null;
34763
+ }
34764
+ }
34765
+ var SYS_COLS = new Set([
34766
+ "_id",
34767
+ "_block_height",
34768
+ "_tx_id",
34769
+ "_created_at",
34770
+ "block_height",
34771
+ "tx_id"
34772
+ ]);
34773
+ function shortVal(v) {
34774
+ const s = String(v);
34775
+ return s.length > 18 ? `${s.slice(0, 6)}…${s.slice(-4)}` : s;
34776
+ }
34777
+ async function renderStatus(limit) {
34778
+ const [health, integrity, subsRes, apiHealth] = await Promise.all([
34779
+ jget(`${INDEXER_URL}/health`),
34780
+ jget(`${INDEXER_URL}/health/integrity`),
34781
+ jget(`${API_URL}/api/subgraphs`),
34782
+ jget(`${API_URL}/health`)
34783
+ ]);
34784
+ const out = [];
34785
+ out.push(bold("STACK"));
34786
+ out.push(` ${health ? green("●") : red("●")} indexer ${health ? "healthy" : "down"} ${dim(":3700")}`);
34787
+ out.push(` ${apiHealth ? green("●") : red("●")} api ${apiHealth ? "healthy" : "down"} ${dim(":3800")}`);
34788
+ if (!health) {
34789
+ out.push("");
34790
+ out.push(dim(" indexer unreachable — is `sl devnet connect` running?"));
34791
+ return out.join(`
34792
+ `);
34793
+ }
34794
+ const tip = Number(health.lastSeenHeight ?? integrity?.lastIndexedBlock ?? 0);
34795
+ const indexed = Number(integrity?.lastIndexedBlock ?? tip);
34796
+ const lag = Math.max(0, tip - indexed);
34797
+ const ago = health.lastBlockReceivedSecondsAgo;
34798
+ out.push("");
34799
+ out.push(bold("INGEST"));
34800
+ 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` : "?"}`)}`);
34801
+ const subs = subsRes?.data ?? [];
34802
+ out.push("");
34803
+ out.push(bold("SUBGRAPHS"));
34804
+ if (subs.length === 0) {
34805
+ out.push(dim(" none deployed — sl subgraphs deploy ./subgraph.ts"));
34806
+ } else {
34807
+ for (const sg of subs) {
34808
+ const st = sg.status === "active" ? green(sg.status) : yellow(String(sg.status));
34809
+ const tables = (sg.tables ?? []).join(", ");
34810
+ out.push(` ${sg.name} ${st} ${dim(`block ${sg.lastProcessedBlock}`)} ${tables} ${dim(`· ${sg.totalRows ?? 0} rows`)}`);
34811
+ }
34812
+ }
34813
+ out.push("");
34814
+ out.push(bold("ACTIVITY") + dim(" (recent)"));
34815
+ const rows = [];
34816
+ for (const sg of subs) {
34817
+ for (const table of sg.tables ?? []) {
34818
+ const res = await jget(`${API_URL}/api/subgraphs/${sg.name}/${table}?_limit=${limit}&_sort=_block_height&_order=desc`);
34819
+ for (const row of res?.data ?? []) {
34820
+ const summary = Object.entries(row).filter(([k]) => !SYS_COLS.has(k)).map(([, v]) => shortVal(v)).join(" ");
34821
+ rows.push({ block: Number(row._block_height ?? 0), table, summary });
34822
+ }
34823
+ }
34824
+ }
34825
+ rows.sort((a, b2) => b2.block - a.block);
34826
+ if (rows.length === 0) {
34827
+ out.push(dim(" no indexed rows yet — fire a contract call"));
34828
+ } else {
34829
+ for (const r of rows.slice(0, limit)) {
34830
+ out.push(` ${dim(String(r.block).padStart(5))} ${r.table.padEnd(16)} ${r.summary}`);
34831
+ }
34832
+ }
34833
+ return out.join(`
34834
+ `);
34835
+ }
34836
+ async function status(options) {
34837
+ const limit = Math.max(1, Number(options.limit) || 12);
34838
+ if (!options.watch) {
34839
+ console.log(await renderStatus(limit));
34840
+ return;
34841
+ }
34842
+ const tick = async () => {
34843
+ const body = await renderStatus(limit);
34844
+ process.stdout.write("\x1B[2J\x1B[H");
34845
+ console.log(`${dim("sl devnet status")} ${dim("· ctrl-c to stop")}
34846
+ `);
34847
+ console.log(body);
34848
+ };
34849
+ await tick();
34850
+ const id = setInterval(() => void tick(), 2000);
34851
+ process.on("SIGINT", () => {
34852
+ clearInterval(id);
34853
+ process.stdout.write(`
34854
+ `);
34855
+ process.exit(0);
34856
+ });
34857
+ }
34723
34858
  // src/commands/subgraphs.ts
34724
34859
  import { spawn } from "node:child_process";
34725
34860
  import {
@@ -36348,8 +36483,8 @@ ${rows.length} row(s)`));
36348
36483
  success(result.message);
36349
36484
  } catch (delErr) {
36350
36485
  const msg = delErr instanceof Error ? delErr.message : String(delErr);
36351
- const status = delErr?.status;
36352
- if (status === 404 || /not found/i.test(msg)) {
36486
+ const status2 = delErr?.status;
36487
+ if (status2 === 404 || /not found/i.test(msg)) {
36353
36488
  info(`Subgraph "${name}" not found (already deleted?)`);
36354
36489
  return;
36355
36490
  }
@@ -36879,15 +37014,15 @@ async function checkHealth() {
36879
37014
  try {
36880
37015
  const c = JSON.parse(line);
36881
37016
  const name = c.Names || c.Name;
36882
- const status = c.Status || "unknown";
37017
+ const status2 = c.Status || "unknown";
36883
37018
  let health = "unknown";
36884
- if (status.includes("healthy"))
37019
+ if (status2.includes("healthy"))
36885
37020
  health = "healthy";
36886
- else if (status.includes("unhealthy"))
37021
+ else if (status2.includes("unhealthy"))
36887
37022
  health = "unhealthy";
36888
- else if (status.includes("Up"))
37023
+ else if (status2.includes("Up"))
36889
37024
  health = "running";
36890
- else if (status.includes("Exited"))
37025
+ else if (status2.includes("Exited"))
36891
37026
  health = "exited";
36892
37027
  let restartCount = 0;
36893
37028
  try {
@@ -36896,7 +37031,7 @@ async function checkHealth() {
36896
37031
  restartCount = Number.parseInt(inspect2.stdout.toString().trim()) || 0;
36897
37032
  }
36898
37033
  } catch {}
36899
- containers.push({ name, status, health, restartCount });
37034
+ containers.push({ name, status: status2, health, restartCount });
36900
37035
  if (health === "unhealthy") {
36901
37036
  issues.push(`Container ${name} is unhealthy`);
36902
37037
  }
@@ -37310,13 +37445,13 @@ async function showNodeLogs(options2) {
37310
37445
  console.log(dim("Run 'sl local node start' first"));
37311
37446
  process.exit(1);
37312
37447
  }
37313
- const logs = await getNodeLogs({
37448
+ const logs2 = await getNodeLogs({
37314
37449
  follow: options2.follow,
37315
37450
  lines: Number.parseInt(options2.lines),
37316
37451
  quiet: options2.quiet,
37317
37452
  format: true
37318
37453
  });
37319
- for await (const line of logs) {
37454
+ for await (const line of logs2) {
37320
37455
  console.log(line);
37321
37456
  }
37322
37457
  }
@@ -37342,8 +37477,8 @@ async function showStaticCombinedLogs(showDev, showNode, lines, quiet) {
37342
37477
  }
37343
37478
  }
37344
37479
  if (showNode && await isNodeRunning()) {
37345
- const logs = await getNodeLogs({ lines, quiet, format: true });
37346
- for await (const line of logs) {
37480
+ const logs2 = await getNodeLogs({ lines, quiet, format: true });
37481
+ for await (const line of logs2) {
37347
37482
  const tsMatch = line.match(/\[(\d{4}-\d{2}-\d{2}T[\d:.]+Z)\]/);
37348
37483
  const timestamp = tsMatch ? new Date(tsMatch[1]) : new Date(0);
37349
37484
  allLines.push({ service: "node", line, timestamp });
@@ -37696,5 +37831,5 @@ registerAccountCommand(program);
37696
37831
  registerBillingCommand(program);
37697
37832
  program.parse();
37698
37833
 
37699
- //# debugId=FDDBCAB122A19D1564756E2164756E21
37834
+ //# debugId=98906583902E571F64756E2164756E21
37700
37835
  //# sourceMappingURL=cli.js.map