@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 +169 -23
- package/dist/cli.js.map +4 -4
- package/package.json +4 -4
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
|
|
13981
|
-
console.log(` ${
|
|
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(
|
|
26270
|
-
if (isWin &&
|
|
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(
|
|
26276
|
-
if (isWin &&
|
|
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.
|
|
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.
|
|
32377
|
-
"@secondlayer/shared": "^6.
|
|
32376
|
+
"@secondlayer/sdk": "^5.8.0",
|
|
32377
|
+
"@secondlayer/shared": "^6.10.0",
|
|
32378
32378
|
"@secondlayer/stacks": "^2.3.0",
|
|
32379
|
-
"@secondlayer/subgraphs": "^3.
|
|
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
|
|
36352
|
-
if (
|
|
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
|
|
37028
|
+
const status2 = c.Status || "unknown";
|
|
36883
37029
|
let health = "unknown";
|
|
36884
|
-
if (
|
|
37030
|
+
if (status2.includes("healthy"))
|
|
36885
37031
|
health = "healthy";
|
|
36886
|
-
else if (
|
|
37032
|
+
else if (status2.includes("unhealthy"))
|
|
36887
37033
|
health = "unhealthy";
|
|
36888
|
-
else if (
|
|
37034
|
+
else if (status2.includes("Up"))
|
|
36889
37035
|
health = "running";
|
|
36890
|
-
else if (
|
|
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
|
|
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
|
|
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
|
|
37346
|
-
for await (const line of
|
|
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=
|
|
37845
|
+
//# debugId=7F4D9295EECF14D064756E2164756E21
|
|
37700
37846
|
//# sourceMappingURL=cli.js.map
|