@secondlayer/cli 6.0.0 → 7.0.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
@@ -2522,7 +2522,7 @@ function resolveApiUrl() {
2522
2522
  return process.env.SL_API_URL ?? process.env.SL_PLATFORM_API_URL ?? DEFAULT_API_URL;
2523
2523
  }
2524
2524
  function resolveEnvKey() {
2525
- return process.env.SL_API_KEY ?? process.env.SL_SERVICE_KEY ?? process.env.SL_STREAMS_API_KEY;
2525
+ return process.env.SL_API_KEY;
2526
2526
  }
2527
2527
  async function resolveAuth() {
2528
2528
  const apiUrl = resolveApiUrl();
@@ -4946,88 +4946,6 @@ var init_src = __esm(() => {
4946
4946
  src_default = Postgres;
4947
4947
  });
4948
4948
 
4949
- // src/lib/dev-state.ts
4950
- var exports_dev_state = {};
4951
- __export(exports_dev_state, {
4952
- saveDevState: () => saveDevState,
4953
- loadDevState: () => loadDevState,
4954
- isProcessRunning: () => isProcessRunning,
4955
- isDevRunning: () => isDevRunning,
4956
- getRunningServices: () => getRunningServices,
4957
- getLogsDir: () => getLogsDir,
4958
- getLogFile: () => getLogFile,
4959
- ensureDirs: () => ensureDirs,
4960
- clearLogs: () => clearLogs,
4961
- clearDevState: () => clearDevState
4962
- });
4963
- import { homedir as homedir4 } from "node:os";
4964
- import { join as join5 } from "node:path";
4965
- function getLogsDir() {
4966
- return LOGS_DIR;
4967
- }
4968
- function getLogFile(service) {
4969
- return join5(LOGS_DIR, `${service}.log`);
4970
- }
4971
- async function ensureDirs() {
4972
- await Bun.$`mkdir -p ${STATE_DIR}`.quiet();
4973
- await Bun.$`mkdir -p ${LOGS_DIR}`.quiet();
4974
- }
4975
- async function loadDevState() {
4976
- const file = Bun.file(DEV_STATE_PATH);
4977
- if (!await file.exists()) {
4978
- return null;
4979
- }
4980
- try {
4981
- return await file.json();
4982
- } catch {
4983
- return null;
4984
- }
4985
- }
4986
- async function saveDevState(state) {
4987
- await ensureDirs();
4988
- await Bun.write(DEV_STATE_PATH, `${JSON.stringify(state, null, 2)}
4989
- `);
4990
- }
4991
- async function clearDevState() {
4992
- const file = Bun.file(DEV_STATE_PATH);
4993
- if (await file.exists()) {
4994
- await Bun.$`rm ${DEV_STATE_PATH}`.quiet();
4995
- }
4996
- }
4997
- async function clearLogs() {
4998
- await Bun.$`rm -f ${LOGS_DIR}/*.log`.quiet().nothrow();
4999
- }
5000
- function isProcessRunning(pid) {
5001
- try {
5002
- process.kill(pid, 0);
5003
- return true;
5004
- } catch {
5005
- return false;
5006
- }
5007
- }
5008
- async function getRunningServices() {
5009
- const state = await loadDevState();
5010
- if (!state)
5011
- return {};
5012
- const running = {};
5013
- for (const [name, service] of Object.entries(state.services)) {
5014
- if (isProcessRunning(service.pid)) {
5015
- running[name] = service;
5016
- }
5017
- }
5018
- return running;
5019
- }
5020
- async function isDevRunning() {
5021
- const running = await getRunningServices();
5022
- return Object.keys(running).length > 0;
5023
- }
5024
- var STATE_DIR, DEV_STATE_PATH, LOGS_DIR;
5025
- var init_dev_state = __esm(() => {
5026
- STATE_DIR = join5(homedir4(), ".secondlayer");
5027
- DEV_STATE_PATH = join5(STATE_DIR, "dev.json");
5028
- LOGS_DIR = join5(STATE_DIR, "logs");
5029
- });
5030
-
5031
4949
  // src/utils/abi-compat.ts
5032
4950
  function normalizeAccess(access) {
5033
4951
  if (access === "read_only")
@@ -12990,6 +12908,88 @@ function inferNetwork(address) {
12990
12908
  return;
12991
12909
  }
12992
12910
 
12911
+ // src/lib/dev-state.ts
12912
+ var exports_dev_state = {};
12913
+ __export(exports_dev_state, {
12914
+ saveDevState: () => saveDevState,
12915
+ loadDevState: () => loadDevState,
12916
+ isProcessRunning: () => isProcessRunning,
12917
+ isDevRunning: () => isDevRunning,
12918
+ getRunningServices: () => getRunningServices,
12919
+ getLogsDir: () => getLogsDir,
12920
+ getLogFile: () => getLogFile,
12921
+ ensureDirs: () => ensureDirs,
12922
+ clearLogs: () => clearLogs,
12923
+ clearDevState: () => clearDevState
12924
+ });
12925
+ import { homedir as homedir4 } from "node:os";
12926
+ import { join as join8 } from "node:path";
12927
+ function getLogsDir() {
12928
+ return LOGS_DIR;
12929
+ }
12930
+ function getLogFile(service) {
12931
+ return join8(LOGS_DIR, `${service}.log`);
12932
+ }
12933
+ async function ensureDirs() {
12934
+ await Bun.$`mkdir -p ${STATE_DIR}`.quiet();
12935
+ await Bun.$`mkdir -p ${LOGS_DIR}`.quiet();
12936
+ }
12937
+ async function loadDevState() {
12938
+ const file = Bun.file(DEV_STATE_PATH);
12939
+ if (!await file.exists()) {
12940
+ return null;
12941
+ }
12942
+ try {
12943
+ return await file.json();
12944
+ } catch {
12945
+ return null;
12946
+ }
12947
+ }
12948
+ async function saveDevState(state) {
12949
+ await ensureDirs();
12950
+ await Bun.write(DEV_STATE_PATH, `${JSON.stringify(state, null, 2)}
12951
+ `);
12952
+ }
12953
+ async function clearDevState() {
12954
+ const file = Bun.file(DEV_STATE_PATH);
12955
+ if (await file.exists()) {
12956
+ await Bun.$`rm ${DEV_STATE_PATH}`.quiet();
12957
+ }
12958
+ }
12959
+ async function clearLogs() {
12960
+ await Bun.$`rm -f ${LOGS_DIR}/*.log`.quiet().nothrow();
12961
+ }
12962
+ function isProcessRunning(pid) {
12963
+ try {
12964
+ process.kill(pid, 0);
12965
+ return true;
12966
+ } catch {
12967
+ return false;
12968
+ }
12969
+ }
12970
+ async function getRunningServices() {
12971
+ const state = await loadDevState();
12972
+ if (!state)
12973
+ return {};
12974
+ const running = {};
12975
+ for (const [name, service] of Object.entries(state.services)) {
12976
+ if (isProcessRunning(service.pid)) {
12977
+ running[name] = service;
12978
+ }
12979
+ }
12980
+ return running;
12981
+ }
12982
+ async function isDevRunning() {
12983
+ const running = await getRunningServices();
12984
+ return Object.keys(running).length > 0;
12985
+ }
12986
+ var STATE_DIR, DEV_STATE_PATH, LOGS_DIR;
12987
+ var init_dev_state = __esm(() => {
12988
+ STATE_DIR = join8(homedir4(), ".secondlayer");
12989
+ DEV_STATE_PATH = join8(STATE_DIR, "dev.json");
12990
+ LOGS_DIR = join8(STATE_DIR, "logs");
12991
+ });
12992
+
12993
12993
  // src/lib/network.ts
12994
12994
  function getChainId(network) {
12995
12995
  return CHAIN_IDS[network].decimal.toString();
@@ -32354,7 +32354,7 @@ var {
32354
32354
  // package.json
32355
32355
  var package_default = {
32356
32356
  name: "@secondlayer/cli",
32357
- version: "6.0.0",
32357
+ version: "7.0.0",
32358
32358
  repository: {
32359
32359
  type: "git",
32360
32360
  url: "git+https://github.com/ryanwaits/secondlayer.git",
@@ -32605,10 +32605,6 @@ async function runBillingStatus(json) {
32605
32605
  function addBillingCommand(parent) {
32606
32606
  parent.command("billing").description("Show your plan, Stripe subscription, trial, and discounts").option("--json", "Output as JSON").action((options) => runBillingStatus(options.json));
32607
32607
  }
32608
- function registerBillingCommand(program2) {
32609
- const billing = program2.command("billing").description("Deprecated: use `account billing`");
32610
- billing.command("status").description("Show your current plan, Stripe subscription, trial, and discounts").option("--json", "Output as JSON").action((options) => runBillingStatus(options.json));
32611
- }
32612
32608
  function renderBillingStatus(res) {
32613
32609
  if (!res.subscription) {
32614
32610
  console.log(formatKeyValue([
@@ -32700,13 +32696,6 @@ function registerAccountCommand(program2) {
32700
32696
  account.command("update").description("Update your public profile").option("--name <name>", "Set display name").option("--bio <bio>", "Set bio").option("--slug <slug>", "Set public URL slug").option("--json", "Output as JSON").action(withErrorHandling(async (options) => applyProfileUpdate(options), {
32701
32697
  action: "update profile"
32702
32698
  }));
32703
- account.command("profile").description("Deprecated: use `account get` / `account update`").option("--name <name>", "Set display name").option("--bio <bio>", "Set bio").option("--slug <slug>", "Set public URL slug").option("--json", "Output as JSON").action(withErrorHandling(async (options) => {
32704
- if (options.name || options.bio || options.slug) {
32705
- await applyProfileUpdate(options);
32706
- } else {
32707
- await showProfile(options.json);
32708
- }
32709
- }, { action: "manage profile" }));
32710
32699
  }
32711
32700
  // src/commands/config.ts
32712
32701
  init_config();
@@ -32952,7 +32941,7 @@ async function detectStacksNodes() {
32952
32941
  init_output();
32953
32942
  function registerConfigCommand(program2) {
32954
32943
  const config = program2.command("config").description("Manage CLI configuration");
32955
- config.command("get").alias("show").description("Show current configuration").option("--json", "Output as JSON").action(async (options) => {
32944
+ config.command("get").description("Show current configuration").option("--json", "Output as JSON").action(async (options) => {
32956
32945
  try {
32957
32946
  const cfg = await loadConfig();
32958
32947
  if (options.json) {
@@ -33001,7 +32990,7 @@ Examples:
33001
32990
  process.exit(1);
33002
32991
  }
33003
32992
  });
33004
- config.command("delete").alias("clear").description("Delete all configuration (remove the config file)").option("-y, --yes", "Skip confirmation").action(async (opts) => {
32993
+ config.command("delete").description("Delete all configuration (remove the config file)").option("-y, --yes", "Skip confirmation").action(async (opts) => {
33005
32994
  try {
33006
32995
  if (!opts.yes && !await confirmDestructive("Delete the config file?")) {
33007
32996
  return;
@@ -33106,20 +33095,97 @@ async function validateDatabaseConnection(url) {
33106
33095
  console.log(dim("The URL was saved but connection failed. Check your database settings."));
33107
33096
  }
33108
33097
  }
33109
- // src/commands/create.ts
33110
- import {
33111
- copyFileSync,
33112
- existsSync,
33113
- mkdirSync,
33114
- readFileSync,
33115
- readdirSync,
33116
- statSync,
33117
- writeFileSync
33118
- } from "node:fs";
33119
- import { dirname as dirname2, join as join4, relative, resolve } from "node:path";
33120
- import { fileURLToPath } from "node:url";
33121
- import { input, select as select2 } from "@inquirer/prompts";
33122
- import { SecondLayer as SecondLayer2 } from "@secondlayer/sdk";
33098
+ // src/commands/status.ts
33099
+ init_config();
33100
+ init_output();
33101
+ function registerStatusCommand(program2) {
33102
+ program2.command("status").description("Show system status").option("--json", "Output as JSON").action(async (options) => {
33103
+ const config = await loadConfig();
33104
+ try {
33105
+ const status = await httpPlatform("/status");
33106
+ output({
33107
+ json: options.json,
33108
+ data: status,
33109
+ human: () => printStatus(status)
33110
+ });
33111
+ } catch (err) {
33112
+ if (err instanceof CliHttpError && err.code === "SESSION_EXPIRED") {
33113
+ console.error("Not logged in. Run: sl login");
33114
+ process.exit(1);
33115
+ }
33116
+ console.log("");
33117
+ console.log(blue("System Status"));
33118
+ console.log(` ${red("NOT RUNNING")}`);
33119
+ console.log("");
33120
+ if (config.network === "local") {
33121
+ console.log(dim(" API service is not running."));
33122
+ console.log(dim(" Start with: sl local start"));
33123
+ } else {
33124
+ console.log(dim(" Can't reach the platform API."));
33125
+ console.log(dim(" Check your connection or try again."));
33126
+ }
33127
+ console.log("");
33128
+ process.exit(1);
33129
+ }
33130
+ });
33131
+ }
33132
+ function printStatus(status) {
33133
+ console.log("");
33134
+ const statusColor = status.status === "healthy" ? green : red;
33135
+ console.log(blue("System Status"));
33136
+ console.log(` ${statusColor(status.status.toUpperCase())}${status.network ? dim(` (${status.network})`) : ""}`);
33137
+ console.log("");
33138
+ console.log(blue("Database"));
33139
+ const dbColor = status.database.status === "ok" ? green : red;
33140
+ console.log(` Status: ${dbColor(status.database.status)}`);
33141
+ console.log("");
33142
+ console.log(blue("Index Progress"));
33143
+ if (status.indexProgress.length === 0) {
33144
+ console.log(dim(" No data indexed yet"));
33145
+ } else {
33146
+ for (const p of status.indexProgress) {
33147
+ const behind = p.highestSeenBlock - p.lastIndexedBlock;
33148
+ const behindStr = behind > 0 ? yellow(` (${behind} behind)`) : green(" (synced)");
33149
+ console.log(` ${p.network}: block ${p.lastIndexedBlock}${behindStr}`);
33150
+ if (status.chainTip && status.chainTip > 0) {
33151
+ const pct = Math.min(p.lastIndexedBlock / status.chainTip * 100, 100);
33152
+ const barWidth = 30;
33153
+ const filled = Math.round(pct / 100 * barWidth);
33154
+ const empty = barWidth - filled;
33155
+ const bar = `${"█".repeat(filled)}${"░".repeat(empty)}`;
33156
+ const pctStr = `${pct.toFixed(1)}%`;
33157
+ const color = pct >= 99.9 ? green : pct >= 50 ? yellow : red;
33158
+ console.log(` ${dim("chain:")} ${color(bar)} ${color(pctStr)} ${dim(`(tip: ${status.chainTip.toLocaleString()})`)}`);
33159
+ }
33160
+ const contiguous = p.lastContiguousBlock ?? 0;
33161
+ if (status.integrity === "complete") {
33162
+ console.log(` ${dim("contiguous:")} ${contiguous} ${green("(complete)")}`);
33163
+ } else {
33164
+ const gapCount = status.gaps?.length ?? 0;
33165
+ const missing = status.totalMissingBlocks ?? 0;
33166
+ console.log(` ${dim("contiguous:")} ${contiguous} ${yellow(`(${gapCount} gaps, ${missing} missing blocks)`)}`);
33167
+ if (status.gaps) {
33168
+ for (const gap of status.gaps.slice(0, 5)) {
33169
+ const range = gap.gapStart === gap.gapEnd ? `${gap.gapStart}` : `${gap.gapStart}-${gap.gapEnd}`;
33170
+ console.log(` ${dim("gap:")} ${range} ${dim(`(${gap.size} block${gap.size > 1 ? "s" : ""})`)}`);
33171
+ }
33172
+ }
33173
+ }
33174
+ }
33175
+ }
33176
+ console.log("");
33177
+ if (status.activeSubgraphs !== undefined) {
33178
+ console.log(blue("Activity"));
33179
+ console.log(formatKeyValue([
33180
+ [" Active Subgraphs", status.activeSubgraphs.toString()]
33181
+ ]));
33182
+ console.log("");
33183
+ }
33184
+ console.log(dim(`Last updated: ${status.timestamp}`));
33185
+ }
33186
+ // src/commands/subscriptions.ts
33187
+ import { confirm } from "@inquirer/prompts";
33188
+ import { sign } from "@secondlayer/shared/crypto/standard-webhooks";
33123
33189
 
33124
33190
  // src/lib/filter-params.ts
33125
33191
  var FILTER_OPERATORS = ["eq", "neq", "gt", "gte", "lt", "lte"];
@@ -33187,9 +33253,8 @@ function parseSubscriptionFilter(args) {
33187
33253
  return Object.keys(filters).length > 0 ? filters : undefined;
33188
33254
  }
33189
33255
 
33190
- // src/commands/create.ts
33256
+ // src/commands/subscriptions.ts
33191
33257
  init_output();
33192
- init_resolve_auth();
33193
33258
 
33194
33259
  // src/lib/subscription-validation.ts
33195
33260
  import {
@@ -33209,6 +33274,23 @@ async function validateSubscriptionTargetFromApi(client, input) {
33209
33274
  }
33210
33275
  }
33211
33276
 
33277
+ // src/commands/create.ts
33278
+ import {
33279
+ copyFileSync,
33280
+ existsSync,
33281
+ mkdirSync,
33282
+ readFileSync,
33283
+ readdirSync,
33284
+ statSync,
33285
+ writeFileSync
33286
+ } from "node:fs";
33287
+ import { dirname as dirname2, join as join4, relative, resolve } from "node:path";
33288
+ import { fileURLToPath } from "node:url";
33289
+ import { input, select as select2 } from "@inquirer/prompts";
33290
+ import { SecondLayer as SecondLayer2 } from "@secondlayer/sdk";
33291
+ init_output();
33292
+ init_resolve_auth();
33293
+
33212
33294
  // src/utils/urls.ts
33213
33295
  function deriveBaseUrl(apiUrl) {
33214
33296
  const override = process.env.SL_DASHBOARD_URL?.trim();
@@ -33453,105 +33535,8 @@ function addSubscriptionsCreateCommand(subscriptions) {
33453
33535
  examplePrefix: "sl subscriptions create"
33454
33536
  });
33455
33537
  }
33456
- function registerCreateCommand(program2) {
33457
- const create = program2.command("create").description("Deprecated: use `sl subscriptions create`");
33458
- addSubscriptionScaffold(create, "subscription <name>", {
33459
- description: "Deprecated: use `sl subscriptions create`",
33460
- examplePrefix: "sl create subscription"
33461
- });
33462
- }
33463
- // src/commands/status.ts
33464
- init_config();
33465
- init_output();
33466
- function registerStatusCommand(program2) {
33467
- program2.command("status").description("Show system status").option("--json", "Output as JSON").action(async (options) => {
33468
- const config = await loadConfig();
33469
- try {
33470
- const status = await httpPlatform("/status");
33471
- output({
33472
- json: options.json,
33473
- data: status,
33474
- human: () => printStatus(status)
33475
- });
33476
- } catch (err) {
33477
- if (err instanceof CliHttpError && err.code === "SESSION_EXPIRED") {
33478
- console.error("Not logged in. Run: sl login");
33479
- process.exit(1);
33480
- }
33481
- console.log("");
33482
- console.log(blue("System Status"));
33483
- console.log(` ${red("NOT RUNNING")}`);
33484
- console.log("");
33485
- if (config.network === "local") {
33486
- console.log(dim(" API service is not running."));
33487
- console.log(dim(" Start with: sl local start"));
33488
- } else {
33489
- console.log(dim(" Can't reach the platform API."));
33490
- console.log(dim(" Check your connection or try again."));
33491
- }
33492
- console.log("");
33493
- process.exit(1);
33494
- }
33495
- });
33496
- }
33497
- function printStatus(status) {
33498
- console.log("");
33499
- const statusColor = status.status === "healthy" ? green : red;
33500
- console.log(blue("System Status"));
33501
- console.log(` ${statusColor(status.status.toUpperCase())}${status.network ? dim(` (${status.network})`) : ""}`);
33502
- console.log("");
33503
- console.log(blue("Database"));
33504
- const dbColor = status.database.status === "ok" ? green : red;
33505
- console.log(` Status: ${dbColor(status.database.status)}`);
33506
- console.log("");
33507
- console.log(blue("Index Progress"));
33508
- if (status.indexProgress.length === 0) {
33509
- console.log(dim(" No data indexed yet"));
33510
- } else {
33511
- for (const p of status.indexProgress) {
33512
- const behind = p.highestSeenBlock - p.lastIndexedBlock;
33513
- const behindStr = behind > 0 ? yellow(` (${behind} behind)`) : green(" (synced)");
33514
- console.log(` ${p.network}: block ${p.lastIndexedBlock}${behindStr}`);
33515
- if (status.chainTip && status.chainTip > 0) {
33516
- const pct = Math.min(p.lastIndexedBlock / status.chainTip * 100, 100);
33517
- const barWidth = 30;
33518
- const filled = Math.round(pct / 100 * barWidth);
33519
- const empty = barWidth - filled;
33520
- const bar = `${"█".repeat(filled)}${"░".repeat(empty)}`;
33521
- const pctStr = `${pct.toFixed(1)}%`;
33522
- const color = pct >= 99.9 ? green : pct >= 50 ? yellow : red;
33523
- console.log(` ${dim("chain:")} ${color(bar)} ${color(pctStr)} ${dim(`(tip: ${status.chainTip.toLocaleString()})`)}`);
33524
- }
33525
- const contiguous = p.lastContiguousBlock ?? 0;
33526
- if (status.integrity === "complete") {
33527
- console.log(` ${dim("contiguous:")} ${contiguous} ${green("(complete)")}`);
33528
- } else {
33529
- const gapCount = status.gaps?.length ?? 0;
33530
- const missing = status.totalMissingBlocks ?? 0;
33531
- console.log(` ${dim("contiguous:")} ${contiguous} ${yellow(`(${gapCount} gaps, ${missing} missing blocks)`)}`);
33532
- if (status.gaps) {
33533
- for (const gap of status.gaps.slice(0, 5)) {
33534
- const range = gap.gapStart === gap.gapEnd ? `${gap.gapStart}` : `${gap.gapStart}-${gap.gapEnd}`;
33535
- console.log(` ${dim("gap:")} ${range} ${dim(`(${gap.size} block${gap.size > 1 ? "s" : ""})`)}`);
33536
- }
33537
- }
33538
- }
33539
- }
33540
- }
33541
- console.log("");
33542
- if (status.activeSubgraphs !== undefined) {
33543
- console.log(blue("Activity"));
33544
- console.log(formatKeyValue([
33545
- [" Active Subgraphs", status.activeSubgraphs.toString()]
33546
- ]));
33547
- console.log("");
33548
- }
33549
- console.log(dim(`Last updated: ${status.timestamp}`));
33550
- }
33538
+
33551
33539
  // src/commands/subscriptions.ts
33552
- import { confirm } from "@inquirer/prompts";
33553
- import { sign } from "@secondlayer/shared/crypto/standard-webhooks";
33554
- init_output();
33555
33540
  function commonOptions(cmd) {
33556
33541
  return cmd.option("--service-key <key>", "SL_SERVICE_KEY override").option("--base-url <url>", "SL_API_URL override");
33557
33542
  }
@@ -34177,375 +34162,10 @@ POST result:`));
34177
34162
  }
34178
34163
  });
34179
34164
  }
34180
- // src/commands/db.ts
34181
- init_config();
34182
- init_dev_state();
34183
- init_output();
34184
- import { confirm as confirm2 } from "@inquirer/prompts";
34185
- import { getDb, sql } from "@secondlayer/shared/db";
34186
- import {
34187
- countMissingBlocks,
34188
- findGaps
34189
- } from "@secondlayer/shared/db/queries/integrity";
34190
- import { StacksNodeClient } from "@secondlayer/shared/node";
34191
- var DEV_DATABASE_URL = "postgres://postgres:postgres@localhost:5432/secondlayer_dev";
34192
- function registerDbCommand(program2) {
34193
- addDbCommand(program2, { deprecated: true });
34194
- }
34195
- function addDbCommand(parent, opts) {
34196
- const dbCmd = parent.command("db").description(opts?.deprecated ? "Deprecated: use `sl local db`" : "Inspect indexer database tables").addHelpText("after", `
34197
- Subcommands:
34198
- blocks Show recent blocks
34199
- txs Show recent transactions
34200
- events Show recent events
34201
- gaps Show gaps in indexed block data
34202
- truncate Truncate all indexed data
34203
- resync Reset database and restart indexer
34204
-
34205
- Run \`sl db\` with no subcommand to print an overview.`).hook("preAction", async () => {
34206
- await requireLocalNetwork();
34207
- }).action(async () => {
34208
- await showOverview(10);
34209
- });
34210
- dbCmd.command("blocks").description("Show recent blocks").option("--limit <n>", "Number of rows", "10").option("--json", "Output as JSON").action(async function() {
34211
- const opts2 = this.opts();
34212
- await showBlocks(Number.parseInt(opts2.limit), opts2.json);
34213
- });
34214
- dbCmd.command("txs").description("Show recent transactions").option("--limit <n>", "Number of rows", "10").option("--json", "Output as JSON").action(async function() {
34215
- const opts2 = this.opts();
34216
- await showTransactions(Number.parseInt(opts2.limit), opts2.json);
34217
- });
34218
- dbCmd.command("events").description("Show recent events").option("--limit <n>", "Number of rows", "10").option("--json", "Output as JSON").action(async function() {
34219
- const opts2 = this.opts();
34220
- await showEvents(Number.parseInt(opts2.limit), opts2.json);
34221
- });
34222
- dbCmd.command("gaps").description("Show gaps in indexed block data").option("--limit <n>", "Number of gaps to show", "50").option("--json", "Output as JSON").action(async function() {
34223
- const opts2 = this.opts();
34224
- await showGaps(Number.parseInt(opts2.limit), opts2.json);
34225
- });
34226
- dbCmd.command("truncate").alias("reset").description("Truncate all indexed data (blocks, txs, events)").option("-y, --yes", "Skip confirmation prompt").action(async function() {
34227
- const opts2 = this.opts();
34228
- await resetDatabase(opts2.yes);
34229
- });
34230
- dbCmd.command("resync").description("Reset database and restart indexer for fresh sync").option("-y, --yes", "Skip confirmation prompt").option("--backfill", "After reset, fetch all blocks from node").action(async function() {
34231
- const opts2 = this.opts();
34232
- await resyncDatabase(opts2.yes, opts2.backfill);
34233
- });
34234
- }
34235
- function ensureDb() {
34236
- if (!process.env.DATABASE_URL) {
34237
- process.env.DATABASE_URL = DEV_DATABASE_URL;
34238
- }
34239
- return getDb();
34240
- }
34241
- async function showOverview(_limit) {
34242
- try {
34243
- const db = ensureDb();
34244
- const blockCountResult = await db.selectFrom("blocks").select(sql`count(*)`.as("count")).executeTakeFirst();
34245
- const txCountResult = await db.selectFrom("transactions").select(sql`count(*)`.as("count")).executeTakeFirst();
34246
- const eventCountResult = await db.selectFrom("events").select(sql`count(*)`.as("count")).executeTakeFirst();
34247
- const blockCount = blockCountResult?.count ?? 0;
34248
- const txCount = txCountResult?.count ?? 0;
34249
- const eventCount = eventCountResult?.count ?? 0;
34250
- const latest = await db.selectFrom("blocks").select(["height", "hash", "timestamp"]).orderBy("height", "desc").limit(1).executeTakeFirst();
34251
- console.log("");
34252
- console.log(blue("Database Overview"));
34253
- console.log("");
34254
- console.log(` Blocks: ${green(blockCount.toString())}`);
34255
- console.log(` Transactions: ${green(txCount.toString())}`);
34256
- console.log(` Events: ${green(eventCount.toString())}`);
34257
- console.log("");
34258
- if (latest) {
34259
- const time = new Date(latest.timestamp * 1000).toISOString();
34260
- console.log(blue("Latest Block"));
34261
- console.log(` Height: ${latest.height}`);
34262
- console.log(` Hash: ${dim(latest.hash.slice(0, 20))}...`);
34263
- console.log(` Time: ${time}`);
34264
- console.log("");
34265
- }
34266
- console.log(dim(`Use 'sl db blocks|txs|events' to see records`));
34267
- console.log("");
34268
- process.exit(0);
34269
- } catch (err) {
34270
- error(`Failed to query database: ${err}`);
34271
- console.log(dim(`
34272
- Make sure PostgreSQL is running.`));
34273
- console.log(dim("Run 'sl local start' to start all services."));
34274
- process.exit(1);
34275
- }
34276
- }
34277
- async function showBlocks(limit, json) {
34278
- try {
34279
- const db = ensureDb();
34280
- const rows = await db.selectFrom("blocks").select(["height", "hash", "timestamp", "canonical"]).orderBy("height", "desc").limit(limit).execute();
34281
- if (json) {
34282
- console.log(JSON.stringify(rows, null, 2));
34283
- process.exit(0);
34284
- }
34285
- console.log("");
34286
- console.log(blue(`Recent Blocks (${rows.length})`));
34287
- console.log("");
34288
- console.log(dim(" HEIGHT".padEnd(10)) + dim("HASH".padEnd(24)) + dim("TIME"));
34289
- for (const row of rows) {
34290
- const time = new Date(row.timestamp * 1000).toLocaleTimeString();
34291
- const canonicalMark = row.canonical ? "" : yellow(" (reorg)");
34292
- console.log(` ${row.height.toString().padEnd(10)}${`${row.hash.slice(0, 20)}... `.padEnd(24)}${time}${canonicalMark}`);
34293
- }
34294
- console.log("");
34295
- process.exit(0);
34296
- } catch (err) {
34297
- error(`Failed to query blocks: ${err}`);
34298
- process.exit(1);
34299
- }
34300
- }
34301
- async function showTransactions(limit, json) {
34302
- try {
34303
- const db = ensureDb();
34304
- const rows = await db.selectFrom("transactions").select([
34305
- "tx_id",
34306
- "block_height",
34307
- "type",
34308
- "sender",
34309
- "status",
34310
- "contract_id",
34311
- "function_name"
34312
- ]).orderBy("block_height", "desc").limit(limit).execute();
34313
- if (json) {
34314
- console.log(JSON.stringify(rows, null, 2));
34315
- process.exit(0);
34316
- }
34317
- console.log("");
34318
- console.log(blue(`Recent Transactions (${rows.length})`));
34319
- console.log("");
34320
- console.log(dim(" BLOCK".padEnd(8)) + dim("TYPE".padEnd(18)) + dim("SENDER".padEnd(20)) + dim("CONTRACT/FUNCTION"));
34321
- for (const row of rows) {
34322
- const statusColor = row.status === "success" ? green : yellow;
34323
- const contractInfo = row.contract_id ? `${row.contract_id.split(".")[1] || row.contract_id}${row.function_name ? `::${row.function_name}` : ""}` : "-";
34324
- console.log(` ${statusColor(row.block_height.toString().padEnd(8))}${row.type.padEnd(18)}${`${row.sender.slice(0, 18)}... `.padEnd(20)}${contractInfo}`);
34325
- }
34326
- console.log("");
34327
- process.exit(0);
34328
- } catch (err) {
34329
- error(`Failed to query transactions: ${err}`);
34330
- process.exit(1);
34331
- }
34332
- }
34333
- async function showEvents(limit, json) {
34334
- try {
34335
- const db = ensureDb();
34336
- const rows = await db.selectFrom("events").select(["block_height", "tx_id", "event_index", "type", "data"]).orderBy("block_height", "desc").limit(limit).execute();
34337
- if (json) {
34338
- console.log(JSON.stringify(rows, null, 2));
34339
- process.exit(0);
34340
- }
34341
- console.log("");
34342
- console.log(blue(`Recent Events (${rows.length})`));
34343
- console.log("");
34344
- if (rows.length === 0) {
34345
- console.log(dim(" No events found"));
34346
- console.log("");
34347
- process.exit(0);
34348
- }
34349
- console.log(dim(" BLOCK".padEnd(8)) + dim("INDEX".padEnd(8)) + dim("TYPE".padEnd(24)) + dim("TX"));
34350
- for (const row of rows) {
34351
- console.log(` ${row.block_height.toString().padEnd(8)}` + `${row.event_index.toString().padEnd(8)}` + `${row.type.padEnd(24)}` + `${row.tx_id.slice(0, 16)}...`);
34352
- }
34353
- console.log("");
34354
- process.exit(0);
34355
- } catch (err) {
34356
- error(`Failed to query events: ${err}`);
34357
- process.exit(1);
34358
- }
34359
- }
34360
- async function showGaps(limit, json) {
34361
- try {
34362
- const db = ensureDb();
34363
- const gaps = await findGaps(db, limit);
34364
- const missing = await countMissingBlocks(db);
34365
- if (json) {
34366
- console.log(JSON.stringify({ gaps, totalMissingBlocks: missing }, null, 2));
34367
- process.exit(0);
34368
- }
34369
- console.log("");
34370
- if (gaps.length === 0) {
34371
- console.log(green("No gaps detected — block data is contiguous"));
34372
- console.log("");
34373
- process.exit(0);
34374
- }
34375
- console.log(blue(`Block Gaps (${gaps.length})`));
34376
- console.log("");
34377
- console.log(dim(" GAP_START".padEnd(14)) + dim("GAP_END".padEnd(14)) + dim("SIZE"));
34378
- for (const gap of gaps) {
34379
- console.log(` ${gap.gapStart.toString().padEnd(14)}` + `${gap.gapEnd.toString().padEnd(14)}` + `${gap.size}`);
34380
- }
34381
- console.log("");
34382
- console.log(`${yellow(gaps.length.toString())} gaps, ${red(missing.toString())} missing blocks`);
34383
- console.log("");
34384
- process.exit(0);
34385
- } catch (err) {
34386
- error(`Failed to query gaps: ${err}`);
34387
- process.exit(1);
34388
- }
34389
- }
34390
- async function resetDatabase(skipConfirm) {
34391
- try {
34392
- const db = ensureDb();
34393
- const blockCount = (await db.selectFrom("blocks").select(sql`count(*)`.as("count")).executeTakeFirst())?.count ?? 0;
34394
- const txCount = (await db.selectFrom("transactions").select(sql`count(*)`.as("count")).executeTakeFirst())?.count ?? 0;
34395
- const eventCount = (await db.selectFrom("events").select(sql`count(*)`.as("count")).executeTakeFirst())?.count ?? 0;
34396
- console.log("");
34397
- console.log(yellow("This will delete all indexed blockchain data:"));
34398
- console.log("");
34399
- console.log(` ${red(blockCount.toString())} blocks`);
34400
- console.log(` ${red(txCount.toString())} transactions`);
34401
- console.log(` ${red(eventCount.toString())} events`);
34402
- console.log(` ${dim("+ index_progress")}`);
34403
- console.log("");
34404
- console.log(dim("Note: Subgraph configurations will be preserved."));
34405
- console.log("");
34406
- if (!skipConfirm) {
34407
- const confirmed = await confirm2({
34408
- message: "Are you sure you want to reset the database?",
34409
- default: false
34410
- });
34411
- if (!confirmed) {
34412
- info("Cancelled");
34413
- process.exit(0);
34414
- }
34415
- }
34416
- info("Truncating tables...");
34417
- await sql`TRUNCATE TABLE events, transactions, blocks, index_progress RESTART IDENTITY CASCADE`.execute(db);
34418
- console.log("");
34419
- success("Database reset complete");
34420
- console.log("");
34421
- console.log(dim("Run 'sl local restart' to restart the indexer for fresh sync."));
34422
- console.log("");
34423
- process.exit(0);
34424
- } catch (err) {
34425
- error(`Failed to reset database: ${err}`);
34426
- process.exit(1);
34427
- }
34428
- }
34429
- async function resyncDatabase(skipConfirm, backfill) {
34430
- try {
34431
- const db = ensureDb();
34432
- const blockCount = (await db.selectFrom("blocks").select(sql`count(*)`.as("count")).executeTakeFirst())?.count ?? 0;
34433
- console.log("");
34434
- console.log(yellow("This will:"));
34435
- console.log(` 1. Delete all indexed data (${red(blockCount.toString())} blocks)`);
34436
- console.log(" 2. Restart the indexer for fresh sync");
34437
- if (backfill) {
34438
- console.log(" 3. Fetch all blocks from node (backfill)");
34439
- }
34440
- console.log("");
34441
- console.log(dim("Note: Subgraph configurations will be preserved."));
34442
- console.log("");
34443
- if (!skipConfirm) {
34444
- const confirmed = await confirm2({
34445
- message: "Are you sure you want to resync?",
34446
- default: false
34447
- });
34448
- if (!confirmed) {
34449
- info("Cancelled");
34450
- process.exit(0);
34451
- }
34452
- }
34453
- info("Truncating tables...");
34454
- await sql`TRUNCATE TABLE events, transactions, blocks, index_progress RESTART IDENTITY CASCADE`.execute(db);
34455
- console.log(green(" ✓ Database reset"));
34456
- const state = await loadDevState();
34457
- if (state?.services?.indexer && isProcessRunning(state.services.indexer.pid)) {
34458
- info("Restarting indexer...");
34459
- try {
34460
- process.kill(state.services.indexer.pid, "SIGTERM");
34461
- await Bun.sleep(1000);
34462
- } catch {}
34463
- if (isProcessRunning(state.services.indexer.pid)) {
34464
- try {
34465
- process.kill(state.services.indexer.pid, "SIGKILL");
34466
- } catch {}
34467
- }
34468
- console.log(green(" ✓ Indexer stopped"));
34469
- console.log("");
34470
- info("Run 'sl local restart' to start fresh sync");
34471
- } else {
34472
- console.log("");
34473
- success("Database reset complete");
34474
- console.log("");
34475
- if (!backfill) {
34476
- console.log(dim("Indexer not running. Start with 'sl local start' to begin sync."));
34477
- }
34478
- }
34479
- if (backfill) {
34480
- console.log("");
34481
- info("Starting backfill from node...");
34482
- const nodeClient = new StacksNodeClient;
34483
- const healthy = await nodeClient.isHealthy();
34484
- if (!healthy) {
34485
- warn(`Cannot reach Stacks node at ${nodeClient.getRpcUrl()}`);
34486
- console.log(dim("Run 'sl sync --from 1 --to <tip>' manually once the node is available."));
34487
- console.log("");
34488
- process.exit(0);
34489
- }
34490
- const nodeInfo = await nodeClient.getInfo();
34491
- const tip = nodeInfo.stacks_tip_height;
34492
- info(`Node tip: block ${tip}. Fetching blocks 1 to ${tip}...`);
34493
- console.log(dim(`This may take a while. Run 'sl sync --from 1 --to ${tip}' if interrupted.`));
34494
- console.log("");
34495
- const { loadConfig: loadConfig2 } = await Promise.resolve().then(() => (init_config(), exports_config));
34496
- const config = await loadConfig2();
34497
- const indexerUrl = process.env.INDEXER_URL || `http://localhost:${config.ports.indexer}`;
34498
- const concurrency = 5;
34499
- let fetched = 0;
34500
- let errors = 0;
34501
- for (let i = 1;i <= tip; i += concurrency) {
34502
- const batch = [];
34503
- for (let j = i;j < i + concurrency && j <= tip; j++) {
34504
- batch.push(j);
34505
- }
34506
- const results = await Promise.allSettled(batch.map(async (height) => {
34507
- const block = await nodeClient.getBlock(height);
34508
- if (!block)
34509
- throw new Error(`Block ${height} not found`);
34510
- const res = await fetch(`${indexerUrl}/new_block`, {
34511
- method: "POST",
34512
- headers: {
34513
- "Content-Type": "application/json",
34514
- "X-Source": "backfill"
34515
- },
34516
- body: JSON.stringify(block)
34517
- });
34518
- if (!res.ok)
34519
- throw new Error(`Indexer rejected block ${height}: ${res.status}`);
34520
- }));
34521
- for (const r of results) {
34522
- if (r.status === "fulfilled")
34523
- fetched++;
34524
- else
34525
- errors++;
34526
- }
34527
- const pct = Math.round((fetched + errors) / tip * 100);
34528
- process.stdout.write(`\r ${green(`${fetched}`)} fetched, ${errors > 0 ? red(`${errors} errors`) : "0 errors"} (${pct}%)`);
34529
- }
34530
- console.log("");
34531
- console.log("");
34532
- if (errors === 0) {
34533
- success(`Backfill complete: ${fetched} blocks indexed`);
34534
- } else {
34535
- warn(`Backfill complete: ${fetched} indexed, ${errors} errors`);
34536
- }
34537
- }
34538
- console.log("");
34539
- process.exit(0);
34540
- } catch (err) {
34541
- error(`Failed to resync: ${err}`);
34542
- process.exit(1);
34543
- }
34544
- }
34545
34165
  // src/commands/devnet.ts
34546
34166
  import { spawnSync } from "node:child_process";
34547
34167
  import { existsSync as existsSync3, mkdirSync as mkdirSync2, writeFileSync as writeFileSync3 } from "node:fs";
34548
- import { dirname as dirname4, join as join7 } from "node:path";
34168
+ import { dirname as dirname4, join as join6 } from "node:path";
34549
34169
 
34550
34170
  // src/lib/devnet-compose.ts
34551
34171
  var DEFAULT_IMAGE_OWNER = "ryanwaits";
@@ -34688,14 +34308,14 @@ volumes:
34688
34308
 
34689
34309
  // src/lib/devnet-config.ts
34690
34310
  import { existsSync as existsSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "node:fs";
34691
- import { dirname as dirname3, join as join6, parse as parsePath } from "node:path";
34311
+ import { dirname as dirname3, join as join5, parse as parsePath } from "node:path";
34692
34312
  import { parse as parseToml } from "smol-toml";
34693
34313
  var DEFAULT_INDEXER_OBSERVER = "host.docker.internal:3700";
34694
34314
  function findClarinetProject(startDir) {
34695
34315
  let dir = startDir;
34696
34316
  const { root } = parsePath(dir);
34697
34317
  while (true) {
34698
- if (existsSync2(join6(dir, "Clarinet.toml")))
34318
+ if (existsSync2(join5(dir, "Clarinet.toml")))
34699
34319
  return dir;
34700
34320
  if (dir === root)
34701
34321
  return null;
@@ -34742,7 +34362,7 @@ stacks_node_events_observers = [${entry}]
34742
34362
 
34743
34363
  // src/commands/devnet.ts
34744
34364
  init_output();
34745
- var COMPOSE_REL = join7(".secondlayer", "docker-compose.yml");
34365
+ var COMPOSE_REL = join6(".secondlayer", "docker-compose.yml");
34746
34366
  function ensureDocker() {
34747
34367
  const probe = spawnSync("docker", ["info"], { stdio: "ignore" });
34748
34368
  if (probe.error || probe.status !== 0) {
@@ -34780,13 +34400,13 @@ function registerDevnetCommand(program2) {
34780
34400
  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) => {
34781
34401
  await status(options);
34782
34402
  });
34783
- 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").option("--tail <n>", "Deprecated alias for --lines").action(async (service, options) => {
34403
+ 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) => {
34784
34404
  await logs(service, options);
34785
34405
  });
34786
34406
  }
34787
34407
  function resolveProject(explicit) {
34788
34408
  if (explicit) {
34789
- if (!existsSync3(join7(explicit, "Clarinet.toml"))) {
34409
+ if (!existsSync3(join6(explicit, "Clarinet.toml"))) {
34790
34410
  error(`No Clarinet.toml in ${explicit}`);
34791
34411
  process.exit(1);
34792
34412
  }
@@ -34802,7 +34422,7 @@ function resolveProject(explicit) {
34802
34422
  }
34803
34423
  async function connect(options) {
34804
34424
  const project = resolveProject(options.project);
34805
- const devnetToml = join7(project, "settings", "Devnet.toml");
34425
+ const devnetToml = join6(project, "settings", "Devnet.toml");
34806
34426
  mkdirSync2(dirname4(devnetToml), { recursive: true });
34807
34427
  const result = ensureEventObserver(devnetToml, DEFAULT_INDEXER_OBSERVER);
34808
34428
  console.log(`${green("✓")} found Clarinet.toml`);
@@ -34812,7 +34432,7 @@ async function connect(options) {
34812
34432
  console.log(`${green("✓")} ${result.status === "created" ? "created" : "patched"} settings/Devnet.toml`);
34813
34433
  console.log(dim(` + stacks_node_events_observers = ["${DEFAULT_INDEXER_OBSERVER}"]`));
34814
34434
  }
34815
- const composePath = join7(project, COMPOSE_REL);
34435
+ const composePath = join6(project, COMPOSE_REL);
34816
34436
  mkdirSync2(dirname4(composePath), { recursive: true });
34817
34437
  writeFileSync3(composePath, buildDevnetCompose({ owner: options.owner, imageTag: options.imageTag }));
34818
34438
  console.log(`${green("✓")} wrote ${COMPOSE_REL}`);
@@ -34836,13 +34456,13 @@ ${bold("Secondlayer stack up")}`);
34836
34456
  console.log(`
34837
34457
  ${bold("Next:")}`);
34838
34458
  console.log(` ${yellow("clarinet devnet start")} ${dim("# auto-deploys + streams to the indexer")}`);
34839
- console.log(` ${yellow("SL_API_URL=http://localhost:3800 SL_SERVICE_KEY=dummy sl subgraphs deploy ./subgraph.ts")}`);
34459
+ console.log(` ${yellow("SL_API_URL=http://localhost:3800 SL_API_KEY=dummy sl subgraphs deploy ./subgraph.ts")}`);
34840
34460
  console.log(dim(`
34841
34461
  Stop with: sl devnet down`));
34842
34462
  }
34843
34463
  async function down(options) {
34844
34464
  const project = resolveProject(options.project);
34845
- const composePath = join7(project, COMPOSE_REL);
34465
+ const composePath = join6(project, COMPOSE_REL);
34846
34466
  if (!existsSync3(composePath)) {
34847
34467
  error(`No ${COMPOSE_REL} in ${project} — nothing to stop. Run \`sl devnet connect\` first.`);
34848
34468
  process.exit(1);
@@ -34857,7 +34477,7 @@ async function down(options) {
34857
34477
  }
34858
34478
  async function logs(service, options) {
34859
34479
  const project = resolveProject(options.project);
34860
- const composePath = join7(project, COMPOSE_REL);
34480
+ const composePath = join6(project, COMPOSE_REL);
34861
34481
  if (!existsSync3(composePath)) {
34862
34482
  error(`No ${COMPOSE_REL} in ${project} — run \`sl devnet connect\` first.`);
34863
34483
  process.exit(1);
@@ -34867,7 +34487,7 @@ async function logs(service, options) {
34867
34487
  process.exit(1);
34868
34488
  }
34869
34489
  ensureDocker();
34870
- const args = ["logs", "--tail", options.lines ?? options.tail ?? "200"];
34490
+ const args = ["logs", "--tail", options.lines ?? "200"];
34871
34491
  if (options.follow)
34872
34492
  args.push("-f");
34873
34493
  if (service)
@@ -34984,9 +34604,9 @@ import {
34984
34604
  watch,
34985
34605
  writeFileSync as writeFileSync4
34986
34606
  } from "node:fs";
34987
- import { dirname as dirname5, join as join8, resolve as resolve2 } from "node:path";
34607
+ import { dirname as dirname5, join as join7, resolve as resolve2 } from "node:path";
34988
34608
  import { fileURLToPath as fileURLToPath2 } from "node:url";
34989
- import { confirm as confirm4 } from "@inquirer/prompts";
34609
+ import { confirm as confirm3 } from "@inquirer/prompts";
34990
34610
 
34991
34611
  // src/generators/subgraph-scaffold.ts
34992
34612
  init_format();
@@ -35249,7 +34869,7 @@ init_fs();
35249
34869
  init_output();
35250
34870
 
35251
34871
  // src/commands/login.ts
35252
- import { confirm as confirm3, input as input2 } from "@inquirer/prompts";
34872
+ import { confirm as confirm2, input as input2 } from "@inquirer/prompts";
35253
34873
  init_output();
35254
34874
  init_session();
35255
34875
  function sessionExpiry() {
@@ -35293,7 +34913,7 @@ async function runLoginFlow(options = {}) {
35293
34913
  return;
35294
34914
  }
35295
34915
  try {
35296
- const proceed = await confirm3({
34916
+ const proceed = await confirm2({
35297
34917
  message: "Log in as a different user?",
35298
34918
  default: false
35299
34919
  });
@@ -35764,7 +35384,7 @@ async function loadSubgraphWithDepCheck(absPath) {
35764
35384
  if (!missingSdk)
35765
35385
  throw err;
35766
35386
  warn("Missing dependency: @secondlayer/subgraphs");
35767
- const install = await confirm4({
35387
+ const install = await confirm3({
35768
35388
  message: "Install with `bun add @secondlayer/subgraphs`?",
35769
35389
  default: true
35770
35390
  });
@@ -35833,6 +35453,35 @@ async function writeOrPrintSubgraphSpec(spec, format, output2) {
35833
35453
  await writeTextFile(outPath, text);
35834
35454
  success(`Created ${outPath}`);
35835
35455
  }
35456
+ async function specFromLocalFile(absPath, format, specOptions) {
35457
+ const { readFile: readFile3 } = await import("node:fs/promises");
35458
+ const { bundleSubgraphCode } = await import("@secondlayer/bundler");
35459
+ const { generateSubgraphSQL } = await import("@secondlayer/subgraphs");
35460
+ const {
35461
+ generateSubgraphAgentSchema,
35462
+ generateSubgraphMarkdown,
35463
+ generateSubgraphOpenApi
35464
+ } = await import("@secondlayer/shared/subgraphs/spec");
35465
+ const source = await readFile3(absPath, "utf8");
35466
+ const bundled = await bundleSubgraphCode(source);
35467
+ const { hash } = generateSubgraphSQL({
35468
+ name: bundled.name,
35469
+ version: bundled.version,
35470
+ description: bundled.description,
35471
+ sources: bundled.sources,
35472
+ schema: bundled.schema,
35473
+ handlers: {}
35474
+ });
35475
+ const detail = createLocalSubgraphDetail({
35476
+ name: bundled.name,
35477
+ version: bundled.version,
35478
+ description: bundled.description,
35479
+ sources: bundled.sources,
35480
+ schema: bundled.schema,
35481
+ schemaHash: hash
35482
+ });
35483
+ return format === "openapi" ? generateSubgraphOpenApi(detail, specOptions) : format === "agent" ? generateSubgraphAgentSchema(detail, specOptions) : generateSubgraphMarkdown(detail, specOptions);
35484
+ }
35836
35485
  function createLocalSubgraphDetail(input3) {
35837
35486
  const tables = {};
35838
35487
  for (const [tableName, rawTable] of Object.entries(input3.schema)) {
@@ -35907,7 +35556,7 @@ function readCliSubgraphsDependency() {
35907
35556
  return "^1.3.2";
35908
35557
  }
35909
35558
  function ensureScaffoldPackageJson(dir) {
35910
- const packagePath = join8(dir, "package.json");
35559
+ const packagePath = join7(dir, "package.json");
35911
35560
  const subgraphsDep = readCliSubgraphsDependency();
35912
35561
  if (!existsSync4(packagePath)) {
35913
35562
  writeFileSync4(packagePath, `${JSON.stringify({
@@ -36025,7 +35674,7 @@ function formatSubgraphSync(sync) {
36025
35674
  }
36026
35675
  function registerSubgraphsCommand(program2) {
36027
35676
  const subgraphs = program2.command("subgraphs").description("Manage materialized subgraphs");
36028
- subgraphs.command("create <name>").alias("new").description("Create a new subgraph definition file").option("--template <slug>", `Foundation Dataset starter (one of: ${SUBGRAPH_TEMPLATE_SLUGS.join(", ")})`).addHelpText("after", `
35677
+ subgraphs.command("create <name>").description("Create a new subgraph definition file").option("--template <slug>", `Foundation Dataset starter (one of: ${SUBGRAPH_TEMPLATE_SLUGS.join(", ")})`).addHelpText("after", `
36029
35678
  Examples:
36030
35679
  $ sl subgraphs new my-graph
36031
35680
  $ sl subgraphs new token-balances --template sip-010-balances`).action(async (name, opts) => {
@@ -36070,9 +35719,9 @@ ${SUBGRAPH_TEMPLATE_SLUGS.map((s) => ` ${s.padEnd(20)} ${SUBGRAPH_TEMPLATE_DESC
36070
35719
  const def = mod.default ?? mod;
36071
35720
  const { validateSubgraphDefinition } = await import("@secondlayer/subgraphs/validate");
36072
35721
  const { deploySchema } = await import("@secondlayer/subgraphs");
36073
- const { getDb: getDb2 } = await import("@secondlayer/shared/db");
35722
+ const { getDb } = await import("@secondlayer/shared/db");
36074
35723
  validateSubgraphDefinition(def);
36075
- const db = getDb2();
35724
+ const db = getDb();
36076
35725
  const result = await deploySchema(db, def, absPath, {
36077
35726
  forceReindex: false
36078
35727
  });
@@ -36114,14 +35763,14 @@ Stopped watching.`);
36114
35763
  });
36115
35764
  await new Promise(() => {});
36116
35765
  });
36117
- subgraphs.command("deploy <file>").description("Deploy a subgraph definition file").option("--start-block <n>", "Override the subgraph definition startBlock for this deploy").option("--dry-run", "Validate and preview deploy without writing changes").option("--preview", "Deprecated alias for --dry-run").option("-y, --yes", "Skip the reindex confirmation prompt").option("--force", "Deprecated alias for --yes").option("--database-url <url>", "BYO data plane: write the subgraph's schema/rows to your own Postgres. With --dry-run, prints the DDL + grant script and verifies the connection.").option("--strict", "Run `tsc --noEmit` against the handler before deploy (slower; catches TS type errors)").action(async (file, options2) => {
35766
+ subgraphs.command("deploy <file>").description("Deploy a subgraph definition file").option("--start-block <n>", "Override the subgraph definition startBlock for this deploy").option("--dry-run", "Validate and preview deploy without writing changes").option("-y, --yes", "Skip the reindex confirmation prompt").option("--database-url <url>", "BYO data plane: write the subgraph's schema/rows to your own Postgres. With --dry-run, prints the DDL + grant script and verifies the connection.").option("--strict", "Run `tsc --noEmit` against the handler before deploy (slower; catches TS type errors)").action(async (file, options2) => {
36118
35767
  try {
36119
35768
  const absPath = resolve2(file);
36120
35769
  const config = await loadConfig();
36121
35770
  if (config.network !== "local") {
36122
35771
  await requireAuth();
36123
35772
  }
36124
- const dryRun = options2.dryRun || options2.preview;
35773
+ const dryRun = options2.dryRun;
36125
35774
  const startBlock = parseStartBlockOption(options2.startBlock);
36126
35775
  if (startBlock !== undefined) {
36127
35776
  warn(`--start-block ${startBlock} overrides the definition's startBlock for this deploy.`);
@@ -36204,7 +35853,7 @@ Stopped watching.`);
36204
35853
  info(` + columns: ${t}.${cols.join(", ")}`);
36205
35854
  }
36206
35855
  }
36207
- const confirmed = options2.yes || options2.force || await confirm4({
35856
+ const confirmed = options2.yes || await confirm3({
36208
35857
  message: "⚠ This will drop all data and reindex from scratch. Continue?"
36209
35858
  });
36210
35859
  if (!confirmed) {
@@ -36238,8 +35887,8 @@ Stopped watching.`);
36238
35887
  return;
36239
35888
  }
36240
35889
  const { deploySchema } = await import("@secondlayer/subgraphs");
36241
- const { getDb: getDb2, closeDb } = await import("@secondlayer/shared/db");
36242
- const db = getDb2();
35890
+ const { getDb, closeDb } = await import("@secondlayer/shared/db");
35891
+ const db = getDb();
36243
35892
  const result = await deploySchema(db, effectiveDef, absPath, {
36244
35893
  version: undefined,
36245
35894
  forceReindex: startBlock !== undefined
@@ -36355,64 +36004,24 @@ Synced — exiting watch.`));
36355
36004
  handleApiError(err, "get subgraph status");
36356
36005
  }
36357
36006
  });
36358
- subgraphs.command("spec <name>").description("Output API documentation for a deployed subgraph").option("--format <format>", "Output format: openapi, agent, markdown", "openapi").option("-o, --output <path>", "Write output to a file").option("--server <url>", "Override the server URL in generated docs").action(async (name, options2) => {
36007
+ subgraphs.command("spec <nameOrFile>").description("Output API documentation for a deployed subgraph or a local subgraph file").option("--format <format>", "Output format: openapi, agent, markdown", "openapi").option("-o, --output <path>", "Write output to a file").option("--server <url>", "Override the server URL in generated docs").action(async (nameOrFile, options2) => {
36359
36008
  try {
36360
36009
  const format = parseSubgraphSpecFormat(options2.format);
36361
36010
  const specOptions = options2.server ? { serverUrl: options2.server } : undefined;
36362
- const spec = format === "openapi" ? await getSubgraphOpenApi(name, specOptions) : format === "agent" ? await getSubgraphAgentSchema(name, specOptions) : await getSubgraphMarkdown(name, specOptions);
36363
- await writeOrPrintSubgraphSpec(spec, format, options2.output);
36364
- } catch (err) {
36365
- if (err instanceof Error && err.message.startsWith("--format")) {
36366
- error(err.message);
36367
- process.exit(1);
36368
- }
36369
- handleApiError(err, "generate subgraph spec");
36370
- }
36371
- });
36372
- subgraphs.command("inspect <file>").description("Output API documentation for a local subgraph file").option("--format <format>", "Output format: openapi, agent, markdown", "agent").option("-o, --output <path>", "Write output to a file").option("--server <url>", "Override the server URL in generated docs").action(async (file, options2) => {
36373
- try {
36374
- const format = parseSubgraphSpecFormat(options2.format);
36375
- const absPath = resolve2(file);
36376
- if (!existsSync4(absPath)) {
36011
+ const absPath = resolve2(nameOrFile);
36012
+ if (nameOrFile.endsWith(".ts") && !existsSync4(absPath)) {
36377
36013
  error(`File not found: ${absPath}`);
36378
36014
  process.exit(1);
36379
36015
  }
36380
- const { readFile: readFile3 } = await import("node:fs/promises");
36381
- const { bundleSubgraphCode } = await import("@secondlayer/bundler");
36382
- const { generateSubgraphSQL } = await import("@secondlayer/subgraphs");
36383
- const {
36384
- generateSubgraphAgentSchema,
36385
- generateSubgraphMarkdown,
36386
- generateSubgraphOpenApi
36387
- } = await import("@secondlayer/shared/subgraphs/spec");
36388
- const source = await readFile3(absPath, "utf8");
36389
- const bundled = await bundleSubgraphCode(source);
36390
- const { hash } = generateSubgraphSQL({
36391
- name: bundled.name,
36392
- version: bundled.version,
36393
- description: bundled.description,
36394
- sources: bundled.sources,
36395
- schema: bundled.schema,
36396
- handlers: {}
36397
- });
36398
- const detail = createLocalSubgraphDetail({
36399
- name: bundled.name,
36400
- version: bundled.version,
36401
- description: bundled.description,
36402
- sources: bundled.sources,
36403
- schema: bundled.schema,
36404
- schemaHash: hash
36405
- });
36406
- const specOptions = options2.server ? { serverUrl: options2.server } : undefined;
36407
- const spec = format === "openapi" ? generateSubgraphOpenApi(detail, specOptions) : format === "agent" ? generateSubgraphAgentSchema(detail, specOptions) : generateSubgraphMarkdown(detail, specOptions);
36016
+ const isLocalFile = nameOrFile.endsWith(".ts");
36017
+ const spec = isLocalFile ? await specFromLocalFile(absPath, format, specOptions) : format === "openapi" ? await getSubgraphOpenApi(nameOrFile, specOptions) : format === "agent" ? await getSubgraphAgentSchema(nameOrFile, specOptions) : await getSubgraphMarkdown(nameOrFile, specOptions);
36408
36018
  await writeOrPrintSubgraphSpec(spec, format, options2.output);
36409
36019
  } catch (err) {
36410
36020
  if (err instanceof Error && err.message.startsWith("--format")) {
36411
36021
  error(err.message);
36412
36022
  process.exit(1);
36413
36023
  }
36414
- error(`Failed to inspect subgraph: ${err}`);
36415
- process.exit(1);
36024
+ handleApiError(err, "generate subgraph spec");
36416
36025
  }
36417
36026
  });
36418
36027
  subgraphs.command("codegen <file>").description("Generate an ORM schema (Prisma or Drizzle) for a subgraph's tables — point it at your BYO database").option("--target <orm>", "ORM target: prisma | drizzle", "prisma").option("--schema <name>", "Postgres schema name (defaults to subgraph_<name>)").option("--env <var>", "datasource url env var", "DATABASE_URL").option("--models-only", "Emit only Prisma models (compose via prismaSchemaFolder)").option("-o, --output <path>", "Write to a file (defaults to stdout)").action(async (file, options2) => {
@@ -36457,13 +36066,13 @@ Synced — exiting watch.`));
36457
36066
  process.exit(1);
36458
36067
  }
36459
36068
  });
36460
- subgraphs.command("reindex <name>").description("Reindex a subgraph from historical blocks (drops + reprocesses)").option("--from-block <n>", "Start block height").option("--to-block <n>", "End block height").option("--from <n>", "Deprecated alias for --from-block").option("--to <n>", "Deprecated alias for --to-block").option("-y, --yes", "Skip confirmation").addHelpText("after", `
36069
+ subgraphs.command("reindex <name>").description("Reindex a subgraph from historical blocks (drops + reprocesses)").option("--from-block <n>", "Start block height").option("--to-block <n>", "End block height").option("-y, --yes", "Skip confirmation").addHelpText("after", `
36461
36070
  Examples:
36462
36071
  $ sl subgraphs reindex my-graph -y
36463
36072
  $ sl subgraphs reindex my-graph --from-block 150000 --to-block 160000 -y`).action(async (name, options2) => {
36464
36073
  try {
36465
- const fromRaw = options2.fromBlock ?? options2.from;
36466
- const toRaw = options2.toBlock ?? options2.to;
36074
+ const fromRaw = options2.fromBlock;
36075
+ const toRaw = options2.toBlock;
36467
36076
  const fromBlock = fromRaw ? Number.parseInt(fromRaw, 10) : undefined;
36468
36077
  const toBlock = toRaw ? Number.parseInt(toRaw, 10) : undefined;
36469
36078
  if (!options2.yes) {
@@ -36471,11 +36080,11 @@ Examples:
36471
36080
  error("Interactive prompt unavailable (stdin is not a TTY). Re-run with -y to skip confirmation.");
36472
36081
  process.exit(1);
36473
36082
  }
36474
- const { confirm: confirm5 } = await import("@inquirer/prompts");
36083
+ const { confirm: confirm4 } = await import("@inquirer/prompts");
36475
36084
  const range = fromBlock !== undefined && toBlock !== undefined ? ` for blocks [${fromBlock}, ${toBlock}]` : fromBlock !== undefined ? ` from block ${fromBlock}` : toBlock !== undefined ? ` up to block ${toBlock}` : "";
36476
36085
  let ok = false;
36477
36086
  try {
36478
- ok = await confirm5({
36087
+ ok = await confirm4({
36479
36088
  message: `Reindex subgraph "${name}"${range}? Existing rows in this range will be dropped and reprocessed.`,
36480
36089
  default: false
36481
36090
  });
@@ -36503,12 +36112,12 @@ Examples:
36503
36112
  handleApiError(err, "reindex subgraph");
36504
36113
  }
36505
36114
  });
36506
- subgraphs.command("backfill <name>").description("Backfill a block range without dropping existing data").option("--from-block <n>", "Start block height").option("--to-block <n>", "End block height").option("--from <n>", "Deprecated alias for --from-block").option("--to <n>", "Deprecated alias for --to-block").addHelpText("after", `
36115
+ subgraphs.command("backfill <name>").description("Backfill a block range without dropping existing data").option("--from-block <n>", "Start block height").option("--to-block <n>", "End block height").addHelpText("after", `
36507
36116
  Examples:
36508
36117
  $ sl subgraphs backfill my-graph --from-block 150000 --to-block 160000`).action(async (name, options2) => {
36509
36118
  try {
36510
- const fromRaw = options2.fromBlock ?? options2.from;
36511
- const toRaw = options2.toBlock ?? options2.to;
36119
+ const fromRaw = options2.fromBlock;
36120
+ const toRaw = options2.toBlock;
36512
36121
  if (!fromRaw || !toRaw) {
36513
36122
  error("--from-block and --to-block are required");
36514
36123
  process.exit(1);
@@ -36530,7 +36139,7 @@ Examples:
36530
36139
  handleApiError(err, "backfill subgraph");
36531
36140
  }
36532
36141
  });
36533
- subgraphs.command("cancel <name>").alias("stop").description("Cancel a running reindex or backfill operation").action(async (name) => {
36142
+ subgraphs.command("cancel <name>").description("Cancel a running reindex or backfill operation").action(async (name) => {
36534
36143
  try {
36535
36144
  info(`Stopping operation for subgraph "${name}"...`);
36536
36145
  const result = await stopSubgraphApi(name);
@@ -36633,10 +36242,10 @@ ${rows.length} row(s)`));
36633
36242
  error("Interactive prompt unavailable (stdin is not a TTY). Re-run with -y to skip confirmation.");
36634
36243
  process.exit(1);
36635
36244
  }
36636
- const { confirm: confirm5 } = await import("@inquirer/prompts");
36245
+ const { confirm: confirm4 } = await import("@inquirer/prompts");
36637
36246
  let ok = false;
36638
36247
  try {
36639
- ok = await confirm5({
36248
+ ok = await confirm4({
36640
36249
  message: `Delete subgraph "${name}" and all its data? This cannot be undone.`
36641
36250
  });
36642
36251
  } catch (promptErr) {
@@ -36754,185 +36363,6 @@ Examples:
36754
36363
  }
36755
36364
  });
36756
36365
  }
36757
- // src/commands/stack.ts
36758
- init_config();
36759
- init_dev_state();
36760
- init_docker();
36761
- init_network();
36762
- init_node_manager();
36763
- init_output();
36764
- function registerStackCommand(program2) {
36765
- const stack = program2.command("stack").description("Manage the full stack");
36766
- stack.command("start").description("Start the full stack (node + dev services)").option("--no-node", "Skip starting the Stacks node").option("--no-dev", "Skip starting dev services").hook("preAction", async () => {
36767
- await requireLocalNetwork();
36768
- }).action(async (options2) => {
36769
- await stackStart(options2);
36770
- });
36771
- stack.command("stop").description("Stop the full stack").option("--no-node", "Skip stopping the Stacks node").option("--no-dev", "Skip stopping dev services").option("--wait", "Wait for in-flight work to drain before stopping").hook("preAction", async () => {
36772
- await requireLocalNetwork();
36773
- }).action(async (options2) => {
36774
- await stackStop(options2);
36775
- });
36776
- stack.command("restart").description("Restart the full stack").option("--no-node", "Skip restarting the Stacks node").option("--no-dev", "Skip restarting dev services").hook("preAction", async () => {
36777
- await requireLocalNetwork();
36778
- }).action(async (options2) => {
36779
- await stackStop({ ...options2, wait: false });
36780
- await stackStart(options2);
36781
- });
36782
- }
36783
- async function stackStart(options2) {
36784
- const config = await loadConfig();
36785
- try {
36786
- await requireDocker();
36787
- } catch (err) {
36788
- if (err instanceof DockerNotAvailableError) {
36789
- error(err.message);
36790
- process.exit(1);
36791
- }
36792
- throw err;
36793
- }
36794
- const network = process.env.STACKS_NETWORK ?? config.node?.network ?? "mainnet";
36795
- const validation = await validateNetworkConsistency(config);
36796
- if (!validation.valid) {
36797
- error("Network mismatch detected:");
36798
- for (const issue of validation.issues) {
36799
- console.log(red(` - ${issue}`));
36800
- }
36801
- console.log("");
36802
- console.log(dim("Fix your config or stop mismatched containers first."));
36803
- process.exit(1);
36804
- }
36805
- console.log("");
36806
- console.log(blue("Starting Secondlayer Stack"));
36807
- console.log(dim(` Network: ${network}`));
36808
- console.log("");
36809
- if (options2.node) {
36810
- const nodePath = config.node?.installPath;
36811
- if (!nodePath) {
36812
- warn("No node configured, skipping. Run 'sl local node setup' to configure.");
36813
- } else if (await isNodeRunning()) {
36814
- info("Node already running");
36815
- } else {
36816
- info("Starting Stacks node...");
36817
- const started = await startNodeContainers(nodePath, network);
36818
- if (!started) {
36819
- error("Failed to start node containers");
36820
- process.exit(1);
36821
- }
36822
- success("Node containers started");
36823
- info("Waiting for node RPC...");
36824
- let rpcReady = false;
36825
- for (let i = 0;i < 60; i++) {
36826
- try {
36827
- const res = await fetch("http://localhost:20443/v2/info", {
36828
- signal: AbortSignal.timeout(2000)
36829
- });
36830
- if (res.ok) {
36831
- rpcReady = true;
36832
- break;
36833
- }
36834
- } catch {}
36835
- await Bun.sleep(1000);
36836
- }
36837
- if (rpcReady) {
36838
- success("Node RPC responsive");
36839
- const postValidation = await validateNetworkConsistency(config);
36840
- if (!postValidation.valid) {
36841
- warn("Post-start chain ID mismatch:");
36842
- for (const issue of postValidation.issues) {
36843
- console.log(yellow(` - ${issue}`));
36844
- }
36845
- }
36846
- } else {
36847
- warn("Node RPC not responsive after 60s (may still be starting)");
36848
- }
36849
- }
36850
- console.log("");
36851
- }
36852
- if (options2.dev) {
36853
- if (await isDevRunning()) {
36854
- info("Dev services already running");
36855
- } else {
36856
- const args = [
36857
- "bun",
36858
- "run",
36859
- `${import.meta.dir}/../../bin/secondlayer.ts`,
36860
- "dev",
36861
- "start"
36862
- ];
36863
- if (config.node?.installPath) {
36864
- args.push("--stacks-node");
36865
- }
36866
- info("Starting dev services...");
36867
- const proc = Bun.spawn(args, {
36868
- stdin: "inherit",
36869
- stdout: "inherit",
36870
- stderr: "inherit"
36871
- });
36872
- await proc.exited;
36873
- }
36874
- }
36875
- console.log("");
36876
- success("Stack started");
36877
- console.log(dim("Run 'sl status' to check health"));
36878
- console.log("");
36879
- }
36880
- async function stackStop(options2) {
36881
- console.log("");
36882
- info("Stopping Secondlayer Stack...");
36883
- if (options2.dev) {
36884
- if (await isDevRunning()) {
36885
- info("Stopping dev services...");
36886
- const proc = Bun.spawn([
36887
- "bun",
36888
- "run",
36889
- `${import.meta.dir}/../../bin/secondlayer.ts`,
36890
- "dev",
36891
- "stop"
36892
- ], { stdin: "inherit", stdout: "inherit", stderr: "inherit" });
36893
- await proc.exited;
36894
- } else {
36895
- info("Dev services not running");
36896
- }
36897
- }
36898
- if (options2.node) {
36899
- if (await isNodeRunning()) {
36900
- info("Stopping node containers...");
36901
- const stopped = await stopNodeContainers();
36902
- if (stopped.length > 0) {
36903
- success(`Stopped: ${stopped.join(", ")}`);
36904
- }
36905
- } else {
36906
- info("Node not running");
36907
- }
36908
- }
36909
- try {
36910
- const result = await Bun.$`docker ps -a --format json --filter "name=secondlayer-dev" --filter "name=stacks"`.quiet().nothrow();
36911
- if (result.exitCode === 0) {
36912
- const lines = result.stdout.toString().trim().split(`
36913
- `).filter(Boolean);
36914
- const orphans = [];
36915
- for (const line of lines) {
36916
- try {
36917
- const c = JSON.parse(line);
36918
- const name = c.Names || c.Name;
36919
- if (name && c.Status?.includes("Exited")) {
36920
- orphans.push(name);
36921
- }
36922
- } catch {}
36923
- }
36924
- if (orphans.length > 0) {
36925
- info(`Cleaning up ${orphans.length} orphaned container(s)...`);
36926
- for (const name of orphans) {
36927
- await Bun.$`docker rm ${name}`.quiet().nothrow();
36928
- }
36929
- }
36930
- }
36931
- } catch {}
36932
- console.log("");
36933
- success("Stack stopped");
36934
- console.log("");
36935
- }
36936
36366
  // src/commands/streams.ts
36937
36367
  init_output();
36938
36368
  init_resolve_auth();
@@ -37005,7 +36435,7 @@ function registerStreamsCommand(program2) {
37005
36435
  process.exit(1);
37006
36436
  }
37007
36437
  });
37008
- streams.command("events").description("List events (cursor-paginated; one page per call)").option("--types <types>", `comma-separated event types (${VALID_TYPES.join(", ")})`).option("--contract-id <id>", "filter to a single contract identifier").option("--cursor <cursor>", "start cursor (block_height:event_index)").option("--from-block <n>", "filter to blocks >= n").option("--to-block <n>", "filter to blocks <= n").option("--from-height <n>", "Deprecated alias for --from-block").option("--to-height <n>", "Deprecated alias for --to-block").option("--limit <n>", "page size (1-1000, default 100)", "100").addHelpText("after", `
36438
+ streams.command("events").description("List events (cursor-paginated; one page per call)").option("--types <types>", `comma-separated event types (${VALID_TYPES.join(", ")})`).option("--contract-id <id>", "filter to a single contract identifier").option("--cursor <cursor>", "start cursor (block_height:event_index)").option("--from-block <n>", "filter to blocks >= n").option("--to-block <n>", "filter to blocks <= n").option("--limit <n>", "page size (1-1000, default 100)", "100").addHelpText("after", `
37009
36439
  Examples:
37010
36440
  $ sl streams events --types stx_transfer,print --limit 50
37011
36441
  $ sl streams events --contract-id SP00....token --from-block 150000 --to-block 160000
@@ -37015,8 +36445,8 @@ Examples:
37015
36445
  types: parseTypes(options2.types),
37016
36446
  contractId: options2.contractId,
37017
36447
  cursor: options2.cursor,
37018
- fromHeight: parseHeight(options2.fromBlock ?? options2.fromHeight, "--from-block"),
37019
- toHeight: parseHeight(options2.toBlock ?? options2.toHeight, "--to-block"),
36448
+ fromHeight: parseHeight(options2.fromBlock, "--from-block"),
36449
+ toHeight: parseHeight(options2.toBlock, "--to-block"),
37020
36450
  limit: parseLimit(options2.limit)
37021
36451
  });
37022
36452
  writeData(JSON.stringify(envelope, null, 2));
@@ -37490,6 +36920,532 @@ async function runLocalDoctor(jsonOutput) {
37490
36920
  }
37491
36921
  // src/commands/local.ts
37492
36922
  init_config();
36923
+
36924
+ // src/commands/db.ts
36925
+ init_config();
36926
+ init_dev_state();
36927
+ init_output();
36928
+ import { confirm as confirm4 } from "@inquirer/prompts";
36929
+ import { getDb, sql } from "@secondlayer/shared/db";
36930
+ import {
36931
+ countMissingBlocks,
36932
+ findGaps
36933
+ } from "@secondlayer/shared/db/queries/integrity";
36934
+ import { StacksNodeClient } from "@secondlayer/shared/node";
36935
+ var DEV_DATABASE_URL = "postgres://postgres:postgres@localhost:5432/secondlayer_dev";
36936
+ function addDbCommand(parent) {
36937
+ const dbCmd = parent.command("db").description("Inspect indexer database tables").addHelpText("after", `
36938
+ Subcommands:
36939
+ blocks Show recent blocks
36940
+ txs Show recent transactions
36941
+ events Show recent events
36942
+ gaps Show gaps in indexed block data
36943
+ truncate Truncate all indexed data
36944
+ resync Reset database and restart indexer
36945
+
36946
+ Run \`sl db\` with no subcommand to print an overview.`).hook("preAction", async () => {
36947
+ await requireLocalNetwork();
36948
+ }).action(async () => {
36949
+ await showOverview(10);
36950
+ });
36951
+ dbCmd.command("blocks").description("Show recent blocks").option("--limit <n>", "Number of rows", "10").option("--json", "Output as JSON").action(async function() {
36952
+ const opts = this.opts();
36953
+ await showBlocks(Number.parseInt(opts.limit), opts.json);
36954
+ });
36955
+ dbCmd.command("txs").description("Show recent transactions").option("--limit <n>", "Number of rows", "10").option("--json", "Output as JSON").action(async function() {
36956
+ const opts = this.opts();
36957
+ await showTransactions(Number.parseInt(opts.limit), opts.json);
36958
+ });
36959
+ dbCmd.command("events").description("Show recent events").option("--limit <n>", "Number of rows", "10").option("--json", "Output as JSON").action(async function() {
36960
+ const opts = this.opts();
36961
+ await showEvents(Number.parseInt(opts.limit), opts.json);
36962
+ });
36963
+ dbCmd.command("gaps").description("Show gaps in indexed block data").option("--limit <n>", "Number of gaps to show", "50").option("--json", "Output as JSON").action(async function() {
36964
+ const opts = this.opts();
36965
+ await showGaps(Number.parseInt(opts.limit), opts.json);
36966
+ });
36967
+ dbCmd.command("truncate").description("Truncate all indexed data (blocks, txs, events)").option("-y, --yes", "Skip confirmation prompt").action(async function() {
36968
+ const opts = this.opts();
36969
+ await resetDatabase(opts.yes);
36970
+ });
36971
+ dbCmd.command("resync").description("Reset database and restart indexer for fresh sync").option("-y, --yes", "Skip confirmation prompt").option("--backfill", "After reset, fetch all blocks from node").action(async function() {
36972
+ const opts = this.opts();
36973
+ await resyncDatabase(opts.yes, opts.backfill);
36974
+ });
36975
+ }
36976
+ function ensureDb() {
36977
+ if (!process.env.DATABASE_URL) {
36978
+ process.env.DATABASE_URL = DEV_DATABASE_URL;
36979
+ }
36980
+ return getDb();
36981
+ }
36982
+ async function showOverview(_limit) {
36983
+ try {
36984
+ const db = ensureDb();
36985
+ const blockCountResult = await db.selectFrom("blocks").select(sql`count(*)`.as("count")).executeTakeFirst();
36986
+ const txCountResult = await db.selectFrom("transactions").select(sql`count(*)`.as("count")).executeTakeFirst();
36987
+ const eventCountResult = await db.selectFrom("events").select(sql`count(*)`.as("count")).executeTakeFirst();
36988
+ const blockCount = blockCountResult?.count ?? 0;
36989
+ const txCount = txCountResult?.count ?? 0;
36990
+ const eventCount = eventCountResult?.count ?? 0;
36991
+ const latest = await db.selectFrom("blocks").select(["height", "hash", "timestamp"]).orderBy("height", "desc").limit(1).executeTakeFirst();
36992
+ console.log("");
36993
+ console.log(blue("Database Overview"));
36994
+ console.log("");
36995
+ console.log(` Blocks: ${green(blockCount.toString())}`);
36996
+ console.log(` Transactions: ${green(txCount.toString())}`);
36997
+ console.log(` Events: ${green(eventCount.toString())}`);
36998
+ console.log("");
36999
+ if (latest) {
37000
+ const time = new Date(latest.timestamp * 1000).toISOString();
37001
+ console.log(blue("Latest Block"));
37002
+ console.log(` Height: ${latest.height}`);
37003
+ console.log(` Hash: ${dim(latest.hash.slice(0, 20))}...`);
37004
+ console.log(` Time: ${time}`);
37005
+ console.log("");
37006
+ }
37007
+ console.log(dim(`Use 'sl db blocks|txs|events' to see records`));
37008
+ console.log("");
37009
+ process.exit(0);
37010
+ } catch (err) {
37011
+ error(`Failed to query database: ${err}`);
37012
+ console.log(dim(`
37013
+ Make sure PostgreSQL is running.`));
37014
+ console.log(dim("Run 'sl local start' to start all services."));
37015
+ process.exit(1);
37016
+ }
37017
+ }
37018
+ async function showBlocks(limit, json) {
37019
+ try {
37020
+ const db = ensureDb();
37021
+ const rows = await db.selectFrom("blocks").select(["height", "hash", "timestamp", "canonical"]).orderBy("height", "desc").limit(limit).execute();
37022
+ if (json) {
37023
+ console.log(JSON.stringify(rows, null, 2));
37024
+ process.exit(0);
37025
+ }
37026
+ console.log("");
37027
+ console.log(blue(`Recent Blocks (${rows.length})`));
37028
+ console.log("");
37029
+ console.log(dim(" HEIGHT".padEnd(10)) + dim("HASH".padEnd(24)) + dim("TIME"));
37030
+ for (const row of rows) {
37031
+ const time = new Date(row.timestamp * 1000).toLocaleTimeString();
37032
+ const canonicalMark = row.canonical ? "" : yellow(" (reorg)");
37033
+ console.log(` ${row.height.toString().padEnd(10)}${`${row.hash.slice(0, 20)}... `.padEnd(24)}${time}${canonicalMark}`);
37034
+ }
37035
+ console.log("");
37036
+ process.exit(0);
37037
+ } catch (err) {
37038
+ error(`Failed to query blocks: ${err}`);
37039
+ process.exit(1);
37040
+ }
37041
+ }
37042
+ async function showTransactions(limit, json) {
37043
+ try {
37044
+ const db = ensureDb();
37045
+ const rows = await db.selectFrom("transactions").select([
37046
+ "tx_id",
37047
+ "block_height",
37048
+ "type",
37049
+ "sender",
37050
+ "status",
37051
+ "contract_id",
37052
+ "function_name"
37053
+ ]).orderBy("block_height", "desc").limit(limit).execute();
37054
+ if (json) {
37055
+ console.log(JSON.stringify(rows, null, 2));
37056
+ process.exit(0);
37057
+ }
37058
+ console.log("");
37059
+ console.log(blue(`Recent Transactions (${rows.length})`));
37060
+ console.log("");
37061
+ console.log(dim(" BLOCK".padEnd(8)) + dim("TYPE".padEnd(18)) + dim("SENDER".padEnd(20)) + dim("CONTRACT/FUNCTION"));
37062
+ for (const row of rows) {
37063
+ const statusColor = row.status === "success" ? green : yellow;
37064
+ const contractInfo = row.contract_id ? `${row.contract_id.split(".")[1] || row.contract_id}${row.function_name ? `::${row.function_name}` : ""}` : "-";
37065
+ console.log(` ${statusColor(row.block_height.toString().padEnd(8))}${row.type.padEnd(18)}${`${row.sender.slice(0, 18)}... `.padEnd(20)}${contractInfo}`);
37066
+ }
37067
+ console.log("");
37068
+ process.exit(0);
37069
+ } catch (err) {
37070
+ error(`Failed to query transactions: ${err}`);
37071
+ process.exit(1);
37072
+ }
37073
+ }
37074
+ async function showEvents(limit, json) {
37075
+ try {
37076
+ const db = ensureDb();
37077
+ const rows = await db.selectFrom("events").select(["block_height", "tx_id", "event_index", "type", "data"]).orderBy("block_height", "desc").limit(limit).execute();
37078
+ if (json) {
37079
+ console.log(JSON.stringify(rows, null, 2));
37080
+ process.exit(0);
37081
+ }
37082
+ console.log("");
37083
+ console.log(blue(`Recent Events (${rows.length})`));
37084
+ console.log("");
37085
+ if (rows.length === 0) {
37086
+ console.log(dim(" No events found"));
37087
+ console.log("");
37088
+ process.exit(0);
37089
+ }
37090
+ console.log(dim(" BLOCK".padEnd(8)) + dim("INDEX".padEnd(8)) + dim("TYPE".padEnd(24)) + dim("TX"));
37091
+ for (const row of rows) {
37092
+ console.log(` ${row.block_height.toString().padEnd(8)}` + `${row.event_index.toString().padEnd(8)}` + `${row.type.padEnd(24)}` + `${row.tx_id.slice(0, 16)}...`);
37093
+ }
37094
+ console.log("");
37095
+ process.exit(0);
37096
+ } catch (err) {
37097
+ error(`Failed to query events: ${err}`);
37098
+ process.exit(1);
37099
+ }
37100
+ }
37101
+ async function showGaps(limit, json) {
37102
+ try {
37103
+ const db = ensureDb();
37104
+ const gaps = await findGaps(db, limit);
37105
+ const missing = await countMissingBlocks(db);
37106
+ if (json) {
37107
+ console.log(JSON.stringify({ gaps, totalMissingBlocks: missing }, null, 2));
37108
+ process.exit(0);
37109
+ }
37110
+ console.log("");
37111
+ if (gaps.length === 0) {
37112
+ console.log(green("No gaps detected — block data is contiguous"));
37113
+ console.log("");
37114
+ process.exit(0);
37115
+ }
37116
+ console.log(blue(`Block Gaps (${gaps.length})`));
37117
+ console.log("");
37118
+ console.log(dim(" GAP_START".padEnd(14)) + dim("GAP_END".padEnd(14)) + dim("SIZE"));
37119
+ for (const gap of gaps) {
37120
+ console.log(` ${gap.gapStart.toString().padEnd(14)}` + `${gap.gapEnd.toString().padEnd(14)}` + `${gap.size}`);
37121
+ }
37122
+ console.log("");
37123
+ console.log(`${yellow(gaps.length.toString())} gaps, ${red(missing.toString())} missing blocks`);
37124
+ console.log("");
37125
+ process.exit(0);
37126
+ } catch (err) {
37127
+ error(`Failed to query gaps: ${err}`);
37128
+ process.exit(1);
37129
+ }
37130
+ }
37131
+ async function resetDatabase(skipConfirm) {
37132
+ try {
37133
+ const db = ensureDb();
37134
+ const blockCount = (await db.selectFrom("blocks").select(sql`count(*)`.as("count")).executeTakeFirst())?.count ?? 0;
37135
+ const txCount = (await db.selectFrom("transactions").select(sql`count(*)`.as("count")).executeTakeFirst())?.count ?? 0;
37136
+ const eventCount = (await db.selectFrom("events").select(sql`count(*)`.as("count")).executeTakeFirst())?.count ?? 0;
37137
+ console.log("");
37138
+ console.log(yellow("This will delete all indexed blockchain data:"));
37139
+ console.log("");
37140
+ console.log(` ${red(blockCount.toString())} blocks`);
37141
+ console.log(` ${red(txCount.toString())} transactions`);
37142
+ console.log(` ${red(eventCount.toString())} events`);
37143
+ console.log(` ${dim("+ index_progress")}`);
37144
+ console.log("");
37145
+ console.log(dim("Note: Subgraph configurations will be preserved."));
37146
+ console.log("");
37147
+ if (!skipConfirm) {
37148
+ const confirmed = await confirm4({
37149
+ message: "Are you sure you want to reset the database?",
37150
+ default: false
37151
+ });
37152
+ if (!confirmed) {
37153
+ info("Cancelled");
37154
+ process.exit(0);
37155
+ }
37156
+ }
37157
+ info("Truncating tables...");
37158
+ await sql`TRUNCATE TABLE events, transactions, blocks, index_progress RESTART IDENTITY CASCADE`.execute(db);
37159
+ console.log("");
37160
+ success("Database reset complete");
37161
+ console.log("");
37162
+ console.log(dim("Run 'sl local restart' to restart the indexer for fresh sync."));
37163
+ console.log("");
37164
+ process.exit(0);
37165
+ } catch (err) {
37166
+ error(`Failed to reset database: ${err}`);
37167
+ process.exit(1);
37168
+ }
37169
+ }
37170
+ async function resyncDatabase(skipConfirm, backfill) {
37171
+ try {
37172
+ const db = ensureDb();
37173
+ const blockCount = (await db.selectFrom("blocks").select(sql`count(*)`.as("count")).executeTakeFirst())?.count ?? 0;
37174
+ console.log("");
37175
+ console.log(yellow("This will:"));
37176
+ console.log(` 1. Delete all indexed data (${red(blockCount.toString())} blocks)`);
37177
+ console.log(" 2. Restart the indexer for fresh sync");
37178
+ if (backfill) {
37179
+ console.log(" 3. Fetch all blocks from node (backfill)");
37180
+ }
37181
+ console.log("");
37182
+ console.log(dim("Note: Subgraph configurations will be preserved."));
37183
+ console.log("");
37184
+ if (!skipConfirm) {
37185
+ const confirmed = await confirm4({
37186
+ message: "Are you sure you want to resync?",
37187
+ default: false
37188
+ });
37189
+ if (!confirmed) {
37190
+ info("Cancelled");
37191
+ process.exit(0);
37192
+ }
37193
+ }
37194
+ info("Truncating tables...");
37195
+ await sql`TRUNCATE TABLE events, transactions, blocks, index_progress RESTART IDENTITY CASCADE`.execute(db);
37196
+ console.log(green(" ✓ Database reset"));
37197
+ const state = await loadDevState();
37198
+ if (state?.services?.indexer && isProcessRunning(state.services.indexer.pid)) {
37199
+ info("Restarting indexer...");
37200
+ try {
37201
+ process.kill(state.services.indexer.pid, "SIGTERM");
37202
+ await Bun.sleep(1000);
37203
+ } catch {}
37204
+ if (isProcessRunning(state.services.indexer.pid)) {
37205
+ try {
37206
+ process.kill(state.services.indexer.pid, "SIGKILL");
37207
+ } catch {}
37208
+ }
37209
+ console.log(green(" ✓ Indexer stopped"));
37210
+ console.log("");
37211
+ info("Run 'sl local restart' to start fresh sync");
37212
+ } else {
37213
+ console.log("");
37214
+ success("Database reset complete");
37215
+ console.log("");
37216
+ if (!backfill) {
37217
+ console.log(dim("Indexer not running. Start with 'sl local start' to begin sync."));
37218
+ }
37219
+ }
37220
+ if (backfill) {
37221
+ console.log("");
37222
+ info("Starting backfill from node...");
37223
+ const nodeClient = new StacksNodeClient;
37224
+ const healthy = await nodeClient.isHealthy();
37225
+ if (!healthy) {
37226
+ warn(`Cannot reach Stacks node at ${nodeClient.getRpcUrl()}`);
37227
+ console.log(dim("Run 'sl sync --from 1 --to <tip>' manually once the node is available."));
37228
+ console.log("");
37229
+ process.exit(0);
37230
+ }
37231
+ const nodeInfo = await nodeClient.getInfo();
37232
+ const tip = nodeInfo.stacks_tip_height;
37233
+ info(`Node tip: block ${tip}. Fetching blocks 1 to ${tip}...`);
37234
+ console.log(dim(`This may take a while. Run 'sl sync --from 1 --to ${tip}' if interrupted.`));
37235
+ console.log("");
37236
+ const { loadConfig: loadConfig2 } = await Promise.resolve().then(() => (init_config(), exports_config));
37237
+ const config = await loadConfig2();
37238
+ const indexerUrl = process.env.INDEXER_URL || `http://localhost:${config.ports.indexer}`;
37239
+ const concurrency = 5;
37240
+ let fetched = 0;
37241
+ let errors2 = 0;
37242
+ for (let i = 1;i <= tip; i += concurrency) {
37243
+ const batch = [];
37244
+ for (let j = i;j < i + concurrency && j <= tip; j++) {
37245
+ batch.push(j);
37246
+ }
37247
+ const results = await Promise.allSettled(batch.map(async (height) => {
37248
+ const block = await nodeClient.getBlock(height);
37249
+ if (!block)
37250
+ throw new Error(`Block ${height} not found`);
37251
+ const res = await fetch(`${indexerUrl}/new_block`, {
37252
+ method: "POST",
37253
+ headers: {
37254
+ "Content-Type": "application/json",
37255
+ "X-Source": "backfill"
37256
+ },
37257
+ body: JSON.stringify(block)
37258
+ });
37259
+ if (!res.ok)
37260
+ throw new Error(`Indexer rejected block ${height}: ${res.status}`);
37261
+ }));
37262
+ for (const r of results) {
37263
+ if (r.status === "fulfilled")
37264
+ fetched++;
37265
+ else
37266
+ errors2++;
37267
+ }
37268
+ const pct = Math.round((fetched + errors2) / tip * 100);
37269
+ process.stdout.write(`\r ${green(`${fetched}`)} fetched, ${errors2 > 0 ? red(`${errors2} errors`) : "0 errors"} (${pct}%)`);
37270
+ }
37271
+ console.log("");
37272
+ console.log("");
37273
+ if (errors2 === 0) {
37274
+ success(`Backfill complete: ${fetched} blocks indexed`);
37275
+ } else {
37276
+ warn(`Backfill complete: ${fetched} indexed, ${errors2} errors`);
37277
+ }
37278
+ }
37279
+ console.log("");
37280
+ process.exit(0);
37281
+ } catch (err) {
37282
+ error(`Failed to resync: ${err}`);
37283
+ process.exit(1);
37284
+ }
37285
+ }
37286
+
37287
+ // src/commands/stack.ts
37288
+ init_config();
37289
+ init_dev_state();
37290
+ init_docker();
37291
+ init_network();
37292
+ init_node_manager();
37293
+ init_output();
37294
+ async function stackStart(options2) {
37295
+ const config = await loadConfig();
37296
+ try {
37297
+ await requireDocker();
37298
+ } catch (err) {
37299
+ if (err instanceof DockerNotAvailableError) {
37300
+ error(err.message);
37301
+ process.exit(1);
37302
+ }
37303
+ throw err;
37304
+ }
37305
+ const network = process.env.STACKS_NETWORK ?? config.node?.network ?? "mainnet";
37306
+ const validation = await validateNetworkConsistency(config);
37307
+ if (!validation.valid) {
37308
+ error("Network mismatch detected:");
37309
+ for (const issue of validation.issues) {
37310
+ console.log(red(` - ${issue}`));
37311
+ }
37312
+ console.log("");
37313
+ console.log(dim("Fix your config or stop mismatched containers first."));
37314
+ process.exit(1);
37315
+ }
37316
+ console.log("");
37317
+ console.log(blue("Starting Secondlayer Stack"));
37318
+ console.log(dim(` Network: ${network}`));
37319
+ console.log("");
37320
+ if (options2.node) {
37321
+ const nodePath = config.node?.installPath;
37322
+ if (!nodePath) {
37323
+ warn("No node configured, skipping. Run 'sl local node setup' to configure.");
37324
+ } else if (await isNodeRunning()) {
37325
+ info("Node already running");
37326
+ } else {
37327
+ info("Starting Stacks node...");
37328
+ const started = await startNodeContainers(nodePath, network);
37329
+ if (!started) {
37330
+ error("Failed to start node containers");
37331
+ process.exit(1);
37332
+ }
37333
+ success("Node containers started");
37334
+ info("Waiting for node RPC...");
37335
+ let rpcReady = false;
37336
+ for (let i = 0;i < 60; i++) {
37337
+ try {
37338
+ const res = await fetch("http://localhost:20443/v2/info", {
37339
+ signal: AbortSignal.timeout(2000)
37340
+ });
37341
+ if (res.ok) {
37342
+ rpcReady = true;
37343
+ break;
37344
+ }
37345
+ } catch {}
37346
+ await Bun.sleep(1000);
37347
+ }
37348
+ if (rpcReady) {
37349
+ success("Node RPC responsive");
37350
+ const postValidation = await validateNetworkConsistency(config);
37351
+ if (!postValidation.valid) {
37352
+ warn("Post-start chain ID mismatch:");
37353
+ for (const issue of postValidation.issues) {
37354
+ console.log(yellow(` - ${issue}`));
37355
+ }
37356
+ }
37357
+ } else {
37358
+ warn("Node RPC not responsive after 60s (may still be starting)");
37359
+ }
37360
+ }
37361
+ console.log("");
37362
+ }
37363
+ if (options2.dev) {
37364
+ if (await isDevRunning()) {
37365
+ info("Dev services already running");
37366
+ } else {
37367
+ const args = [
37368
+ "bun",
37369
+ "run",
37370
+ `${import.meta.dir}/../../bin/secondlayer.ts`,
37371
+ "dev",
37372
+ "start"
37373
+ ];
37374
+ if (config.node?.installPath) {
37375
+ args.push("--stacks-node");
37376
+ }
37377
+ info("Starting dev services...");
37378
+ const proc = Bun.spawn(args, {
37379
+ stdin: "inherit",
37380
+ stdout: "inherit",
37381
+ stderr: "inherit"
37382
+ });
37383
+ await proc.exited;
37384
+ }
37385
+ }
37386
+ console.log("");
37387
+ success("Stack started");
37388
+ console.log(dim("Run 'sl status' to check health"));
37389
+ console.log("");
37390
+ }
37391
+ async function stackStop(options2) {
37392
+ console.log("");
37393
+ info("Stopping Secondlayer Stack...");
37394
+ if (options2.dev) {
37395
+ if (await isDevRunning()) {
37396
+ info("Stopping dev services...");
37397
+ const proc = Bun.spawn([
37398
+ "bun",
37399
+ "run",
37400
+ `${import.meta.dir}/../../bin/secondlayer.ts`,
37401
+ "dev",
37402
+ "stop"
37403
+ ], { stdin: "inherit", stdout: "inherit", stderr: "inherit" });
37404
+ await proc.exited;
37405
+ } else {
37406
+ info("Dev services not running");
37407
+ }
37408
+ }
37409
+ if (options2.node) {
37410
+ if (await isNodeRunning()) {
37411
+ info("Stopping node containers...");
37412
+ const stopped = await stopNodeContainers();
37413
+ if (stopped.length > 0) {
37414
+ success(`Stopped: ${stopped.join(", ")}`);
37415
+ }
37416
+ } else {
37417
+ info("Node not running");
37418
+ }
37419
+ }
37420
+ try {
37421
+ const result = await Bun.$`docker ps -a --format json --filter "name=secondlayer-dev" --filter "name=stacks"`.quiet().nothrow();
37422
+ if (result.exitCode === 0) {
37423
+ const lines = result.stdout.toString().trim().split(`
37424
+ `).filter(Boolean);
37425
+ const orphans = [];
37426
+ for (const line of lines) {
37427
+ try {
37428
+ const c = JSON.parse(line);
37429
+ const name = c.Names || c.Name;
37430
+ if (name && c.Status?.includes("Exited")) {
37431
+ orphans.push(name);
37432
+ }
37433
+ } catch {}
37434
+ }
37435
+ if (orphans.length > 0) {
37436
+ info(`Cleaning up ${orphans.length} orphaned container(s)...`);
37437
+ for (const name of orphans) {
37438
+ await Bun.$`docker rm ${name}`.quiet().nothrow();
37439
+ }
37440
+ }
37441
+ }
37442
+ } catch {}
37443
+ console.log("");
37444
+ success("Stack stopped");
37445
+ console.log("");
37446
+ }
37447
+
37448
+ // src/commands/local.ts
37493
37449
  init_dev_state();
37494
37450
  init_node_manager();
37495
37451
  init_output();
@@ -37519,6 +37475,29 @@ function registerLocalCommand(program2) {
37519
37475
  const { restartDev: restartDev2 } = await Promise.resolve().then(() => (init_dev_impl(), exports_dev_impl));
37520
37476
  await restartDev2();
37521
37477
  });
37478
+ local.command("up").description("Start the full local stack (Stacks node + dev services)").option("--devnet", "Run against a local Clarinet devnet instead").option("--no-node", "Skip starting the Stacks node").option("--no-dev", "Skip starting dev services").option("--project <dir>", "[--devnet] Clarinet project directory").option("--image-tag <tag>", "[--devnet] Published image tag to run").option("--owner <owner>", "[--devnet] ghcr image owner to pull from").option("--no-up", "[--devnet] Patch config + write compose without starting").action(async (options2) => {
37479
+ if (options2.devnet) {
37480
+ await connect({
37481
+ project: options2.project,
37482
+ imageTag: options2.imageTag ?? DEFAULT_IMAGE_TAG,
37483
+ owner: options2.owner,
37484
+ up: options2.up
37485
+ });
37486
+ } else {
37487
+ await stackStart({ node: options2.node, dev: options2.dev });
37488
+ }
37489
+ });
37490
+ local.command("down").description("Stop the full local stack").option("--devnet", "Stop a local Clarinet devnet instead").option("--no-node", "Skip stopping the Stacks node").option("--no-dev", "Skip stopping dev services").option("--wait", "Wait for in-flight work to drain before stopping").option("--project <dir>", "[--devnet] Clarinet project directory").option("--purge", "[--devnet] Also remove volumes (wipes the local index)").action(async (options2) => {
37491
+ if (options2.devnet) {
37492
+ await down({ project: options2.project, purge: options2.purge });
37493
+ } else {
37494
+ await stackStop({
37495
+ node: options2.node,
37496
+ dev: options2.dev,
37497
+ wait: options2.wait
37498
+ });
37499
+ }
37500
+ });
37522
37501
  local.command("status").description("Show local environment status").action(async () => {
37523
37502
  await showLocalStatus();
37524
37503
  });
@@ -37535,13 +37514,13 @@ function registerLocalCommand(program2) {
37535
37514
  const { startNode: startNode2 } = await Promise.resolve().then(() => (init_node_impl(), exports_node_impl));
37536
37515
  await startNode2(options2.path, options2.withIndexer);
37537
37516
  });
37538
- node.command("stop").description("Stop the Stacks node").option("-p, --path <path>", "Path to stacks-blockchain-docker (overrides config)").option("-y, --yes", "Skip confirmation").option("--force", "Deprecated alias for --yes").option("--wait", "Wait for in-flight work to drain first").action(async (options2) => {
37517
+ node.command("stop").description("Stop the Stacks node").option("-p, --path <path>", "Path to stacks-blockchain-docker (overrides config)").option("-y, --yes", "Skip confirmation").option("--wait", "Wait for in-flight work to drain first").action(async (options2) => {
37539
37518
  const { stopNode: stopNode2 } = await Promise.resolve().then(() => (init_node_impl(), exports_node_impl));
37540
- await stopNode2(options2.path, options2.yes ?? options2.force, options2.wait);
37519
+ await stopNode2(options2.path, options2.yes, options2.wait);
37541
37520
  });
37542
- node.command("restart").description("Restart the Stacks node (stop then start)").option("-p, --path <path>", "Path to stacks-blockchain-docker (overrides config)").option("-y, --yes", "Skip confirmation").option("--force", "Deprecated alias for --yes").option("--wait", "Wait for in-flight work to drain before stopping").action(async (options2) => {
37521
+ node.command("restart").description("Restart the Stacks node (stop then start)").option("-p, --path <path>", "Path to stacks-blockchain-docker (overrides config)").option("-y, --yes", "Skip confirmation").option("--wait", "Wait for in-flight work to drain before stopping").action(async (options2) => {
37543
37522
  const { restartNode: restartNode2 } = await Promise.resolve().then(() => (init_node_impl(), exports_node_impl));
37544
- await restartNode2(options2.path, options2.yes ?? options2.force, options2.wait);
37523
+ await restartNode2(options2.path, options2.yes, options2.wait);
37545
37524
  });
37546
37525
  node.command("status").description("Show Stacks node status").option("-p, --path <path>", "Path to stacks-blockchain-docker (overrides config)").option("--json", "Output as JSON").action(async (options2) => {
37547
37526
  const { showStatus: showStatus2 } = await Promise.resolve().then(() => (init_node_impl(), exports_node_impl));
@@ -37966,7 +37945,7 @@ function registerProjectCommand(program2) {
37966
37945
  info(dim(`Written to ${path2}`));
37967
37946
  info(dim("Tip: add `.secondlayer/` to .gitignore — it's account-personal."));
37968
37947
  });
37969
- project.command("get").alias("current").description("Show the active project for this directory").option("--json", "Output as JSON").action(async (options2) => {
37948
+ project.command("get").description("Show the active project for this directory").option("--json", "Output as JSON").action(async (options2) => {
37970
37949
  const config = await loadConfig();
37971
37950
  const active = await readActiveProject(process.cwd(), config.defaultProject);
37972
37951
  output({
@@ -38023,7 +38002,7 @@ program.hook("preAction", (thisCommand) => {
38023
38002
  program.addHelpText("after", `
38024
38003
  Quickstart:
38025
38004
  $ sl login # Authenticate (magic-link email)
38026
- $ sl subgraphs new my-watcher --template sip-010-balances
38005
+ $ sl subgraphs create my-watcher --template sip-010-balances
38027
38006
  $ sl subgraphs deploy subgraphs/my-watcher.ts
38028
38007
  $ sl subgraphs status my-watcher
38029
38008
  `);
@@ -38037,14 +38016,13 @@ registerLogoutCommand(program);
38037
38016
  registerWhoamiCommand(program);
38038
38017
  program.commandsGroup("Data products:");
38039
38018
  registerSubgraphsCommand(program);
38040
- registerCreateCommand(program);
38041
38019
  registerSubscriptionsCommand(program);
38042
38020
  registerStreamsCommand(program);
38043
38021
  registerDatasetsCommand(program);
38044
38022
  program.commandsGroup("Project & codegen:");
38045
38023
  registerProjectCommand(program);
38046
- var configureGenerate = (cmd) => cmd.option("-c, --config <path>", "Path to config file").option("-o, --output <path>", "Output file path (required when using direct files)").option("--out <path>", "Deprecated alias for --output").option("-k, --api-key <key>", "Stacks node API key for direct RPC URLs").option("-w, --watch", "Watch for changes").action(async (files, options3) => {
38047
- options3.out = options3.output ?? options3.out;
38024
+ var configureGenerate = (cmd) => cmd.option("-c, --config <path>", "Path to config file").option("-o, --output <path>", "Output file path (required when using direct files)").option("-k, --api-key <key>", "Stacks node API key for direct RPC URLs").option("-w, --watch", "Watch for changes").action(async (files, options3) => {
38025
+ options3.out = options3.output;
38048
38026
  const { generate: generate2 } = await Promise.resolve().then(() => (init_generate(), exports_generate));
38049
38027
  await generate2(files, options3);
38050
38028
  });
@@ -38054,20 +38032,16 @@ Examples:
38054
38032
  $ sl contracts generate
38055
38033
  $ sl contracts generate ./contracts/pool.clar -o ./src/generated.ts
38056
38034
  $ sl contracts generate --config secondlayer.config.ts --watch`);
38057
- configureGenerate(program.command("generate [files...]").aliases(["gen"]).description("Deprecated: use `sl contracts generate`"));
38058
38035
  program.commandsGroup("Local development:");
38059
38036
  registerLocalCommand(program);
38060
38037
  registerDevnetCommand(program);
38061
- registerStackCommand(program);
38062
- registerDbCommand(program);
38063
38038
  program.commandsGroup("Diagnostics:");
38064
38039
  registerStatusCommand(program);
38065
38040
  registerDoctorCommand(program);
38066
38041
  registerConfigCommand(program);
38067
38042
  program.commandsGroup("Account:");
38068
38043
  registerAccountCommand(program);
38069
- registerBillingCommand(program);
38070
38044
  program.parse();
38071
38045
 
38072
- //# debugId=14BF0C548ABB243864756E2164756E21
38046
+ //# debugId=E63D3732E427AF4164756E2164756E21
38073
38047
  //# sourceMappingURL=cli.js.map