@secondlayer/cli 1.7.0 → 1.9.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
@@ -2141,7 +2141,7 @@ var require_commander = __commonJS((exports) => {
2141
2141
  });
2142
2142
 
2143
2143
  // src/lib/fs.ts
2144
- import { readFile, writeFile, mkdir, unlink, stat } from "node:fs/promises";
2144
+ import { mkdir, readFile, stat, unlink, writeFile } from "node:fs/promises";
2145
2145
  async function readJsonFile(path) {
2146
2146
  const text = await readFile(path, "utf-8");
2147
2147
  return JSON.parse(text);
@@ -2248,7 +2248,10 @@ function migrateConfig(raw) {
2248
2248
  migrated.ports = { ...DEFAULT_CONFIG.ports, ...old.ports };
2249
2249
  }
2250
2250
  if (old.database && typeof old.database === "object") {
2251
- migrated.database = { ...DEFAULT_CONFIG.database, ...old.database };
2251
+ migrated.database = {
2252
+ ...DEFAULT_CONFIG.database,
2253
+ ...old.database
2254
+ };
2252
2255
  }
2253
2256
  if (typeof old.indexerPort === "number") {
2254
2257
  migrated.ports.indexer = old.indexerPort;
@@ -2290,19 +2293,19 @@ function applyEnvOverrides(config) {
2290
2293
  result.dataDir = process.env.SL_DATA_DIR;
2291
2294
  }
2292
2295
  if (process.env.SL_API_PORT) {
2293
- const port = parseInt(process.env.SL_API_PORT, 10);
2296
+ const port = Number.parseInt(process.env.SL_API_PORT, 10);
2294
2297
  if (!isNaN(port) && port > 0 && port <= 65535) {
2295
2298
  result.ports = { ...result.ports, api: port };
2296
2299
  }
2297
2300
  }
2298
2301
  if (process.env.SL_INDEXER_PORT) {
2299
- const port = parseInt(process.env.SL_INDEXER_PORT, 10);
2302
+ const port = Number.parseInt(process.env.SL_INDEXER_PORT, 10);
2300
2303
  if (!isNaN(port) && port > 0 && port <= 65535) {
2301
2304
  result.ports = { ...result.ports, indexer: port };
2302
2305
  }
2303
2306
  }
2304
2307
  if (process.env.SL_RECEIVER_PORT) {
2305
- const port = parseInt(process.env.SL_RECEIVER_PORT, 10);
2308
+ const port = Number.parseInt(process.env.SL_RECEIVER_PORT, 10);
2306
2309
  if (!isNaN(port) && port > 0 && port <= 65535) {
2307
2310
  result.ports = { ...result.ports, receiver: port };
2308
2311
  }
@@ -4558,6 +4561,7 @@ __export(exports_api_client, {
4558
4561
  listSubgraphsApi: () => listSubgraphsApi,
4559
4562
  listStreams: () => listStreams,
4560
4563
  handleApiError: () => handleApiError,
4564
+ getSubgraphGaps: () => getSubgraphGaps,
4561
4565
  getSubgraphApi: () => getSubgraphApi,
4562
4566
  getStream: () => getStream,
4563
4567
  getQueueStats: () => getQueueStats,
@@ -4667,6 +4671,9 @@ async function querySubgraphTable(name, table, params = {}) {
4667
4671
  async function querySubgraphTableCount(name, table, params = {}) {
4668
4672
  return (await getClient()).subgraphs.queryTableCount(name, table, params);
4669
4673
  }
4674
+ async function getSubgraphGaps(name, opts) {
4675
+ return (await getClient()).subgraphs.gaps(name, opts);
4676
+ }
4670
4677
  var init_api_client = __esm(() => {
4671
4678
  init_config();
4672
4679
  });
@@ -4804,6 +4811,261 @@ async function formatCode(code) {
4804
4811
  var biome = null;
4805
4812
  var init_format = () => {};
4806
4813
 
4814
+ // src/utils/abi-compat.ts
4815
+ function normalizeAccess(access) {
4816
+ if (access === "read_only")
4817
+ return "read-only";
4818
+ return access;
4819
+ }
4820
+ function normalizeType(type) {
4821
+ if (typeof type === "string") {
4822
+ switch (type) {
4823
+ case "uint128":
4824
+ case "int128":
4825
+ case "bool":
4826
+ case "principal":
4827
+ case "trait_reference":
4828
+ return type;
4829
+ default:
4830
+ return type;
4831
+ }
4832
+ }
4833
+ if (typeof type !== "object" || type === null) {
4834
+ throw new Error(`Invalid ABI type: expected object, got ${typeof type}`);
4835
+ }
4836
+ const typeObj = type;
4837
+ if ("buffer" in typeObj) {
4838
+ const buffer2 = typeObj.buffer;
4839
+ return {
4840
+ buff: {
4841
+ length: buffer2?.length ?? 32
4842
+ }
4843
+ };
4844
+ }
4845
+ if ("buff" in typeObj) {
4846
+ const buff = typeObj.buff;
4847
+ return {
4848
+ buff: {
4849
+ length: buff?.length ?? 32
4850
+ }
4851
+ };
4852
+ }
4853
+ if ("string-ascii" in typeObj) {
4854
+ const strAscii = typeObj["string-ascii"];
4855
+ return {
4856
+ "string-ascii": {
4857
+ length: strAscii?.length ?? 256
4858
+ }
4859
+ };
4860
+ }
4861
+ if ("string-utf8" in typeObj) {
4862
+ const strUtf8 = typeObj["string-utf8"];
4863
+ return {
4864
+ "string-utf8": {
4865
+ length: strUtf8?.length ?? 256
4866
+ }
4867
+ };
4868
+ }
4869
+ if ("response" in typeObj) {
4870
+ const response = typeObj.response;
4871
+ return {
4872
+ response: {
4873
+ ok: normalizeType(response?.ok ?? "bool"),
4874
+ error: normalizeType(response?.error ?? "uint128")
4875
+ }
4876
+ };
4877
+ }
4878
+ if ("optional" in typeObj) {
4879
+ return {
4880
+ optional: normalizeType(typeObj.optional)
4881
+ };
4882
+ }
4883
+ if ("list" in typeObj) {
4884
+ const list = typeObj.list;
4885
+ return {
4886
+ list: {
4887
+ type: normalizeType(list?.type ?? "uint128"),
4888
+ length: list?.length ?? 100
4889
+ }
4890
+ };
4891
+ }
4892
+ if ("tuple" in typeObj) {
4893
+ const tuple = typeObj.tuple;
4894
+ return {
4895
+ tuple: tuple.map((field) => ({
4896
+ name: field.name,
4897
+ type: normalizeType(field.type)
4898
+ }))
4899
+ };
4900
+ }
4901
+ throw new Error(`Unknown ABI type structure: ${JSON.stringify(type)}`);
4902
+ }
4903
+ function normalizeFunction(func) {
4904
+ const access = normalizeAccess(func.access);
4905
+ const args = func.args ?? [];
4906
+ const outputs = func.outputs;
4907
+ return {
4908
+ name: func.name,
4909
+ access,
4910
+ args: args.map((arg) => ({
4911
+ name: arg.name,
4912
+ type: normalizeType(arg.type)
4913
+ })),
4914
+ outputs: normalizeType(typeof outputs === "object" && outputs !== null && "type" in outputs ? outputs.type : outputs)
4915
+ };
4916
+ }
4917
+ function normalizeMap(map) {
4918
+ return {
4919
+ name: map.name,
4920
+ key: normalizeType(map.key),
4921
+ value: normalizeType(map.value)
4922
+ };
4923
+ }
4924
+ function normalizeVariable(variable) {
4925
+ return {
4926
+ name: variable.name,
4927
+ type: normalizeType(variable.type),
4928
+ access: variable.access
4929
+ };
4930
+ }
4931
+ function normalizeAbi(abi) {
4932
+ if (typeof abi !== "object" || abi === null) {
4933
+ return { functions: [] };
4934
+ }
4935
+ const abiObj = abi;
4936
+ const functions = [];
4937
+ const maps = [];
4938
+ const variables = [];
4939
+ if (Array.isArray(abiObj.functions)) {
4940
+ for (const func of abiObj.functions) {
4941
+ if (typeof func === "object" && func !== null) {
4942
+ functions.push(normalizeFunction(func));
4943
+ }
4944
+ }
4945
+ }
4946
+ if (Array.isArray(abiObj.maps)) {
4947
+ for (const map of abiObj.maps) {
4948
+ if (typeof map === "object" && map !== null) {
4949
+ maps.push(normalizeMap(map));
4950
+ }
4951
+ }
4952
+ }
4953
+ if (Array.isArray(abiObj.variables)) {
4954
+ for (const variable of abiObj.variables) {
4955
+ if (typeof variable === "object" && variable !== null) {
4956
+ variables.push(normalizeVariable(variable));
4957
+ }
4958
+ }
4959
+ }
4960
+ return {
4961
+ functions,
4962
+ maps: maps.length > 0 ? maps : undefined,
4963
+ variables: variables.length > 0 ? variables : undefined
4964
+ };
4965
+ }
4966
+
4967
+ // src/parsers/clarity.ts
4968
+ import { promises as fs2 } from "fs";
4969
+ async function parseClarityFile(filePath) {
4970
+ try {
4971
+ const content = await fs2.readFile(filePath, "utf-8");
4972
+ const result = parseClarityContent(content);
4973
+ if (result.functions.length === 0) {
4974
+ console.warn(`⚠️ No functions found in ${filePath}. ` + `For complex contracts, deploy first and use the contract address instead.`);
4975
+ }
4976
+ return result;
4977
+ } catch (error2) {
4978
+ throw new Error(`Unable to parse ${filePath}. ` + `For complex contracts, deploy first and use the contract address instead.
4979
+ ` + `Original error: ${error2}`);
4980
+ }
4981
+ }
4982
+ function parseClarityContent(content) {
4983
+ const functions = [];
4984
+ const functionRegex = /\(define-(public|read-only|private)\s+\(([^)]+)\)([\s\S]*?)\)\s*$/gm;
4985
+ let match;
4986
+ while ((match = functionRegex.exec(content)) !== null) {
4987
+ const [, access, signature, body] = match;
4988
+ const func = parseFunctionSignature(signature, access, body);
4989
+ if (func) {
4990
+ functions.push(func);
4991
+ }
4992
+ }
4993
+ return { functions };
4994
+ }
4995
+ function parseFunctionSignature(signature, access, body) {
4996
+ const parts = signature.trim().split(/\s+/);
4997
+ const name = parts[0];
4998
+ const args = [];
4999
+ for (let i = 1;i < parts.length; i += 2) {
5000
+ if (parts[i] && parts[i + 1]) {
5001
+ const argName = parts[i].replace(/[()]/g, "");
5002
+ const argType = parseType(parts[i + 1]);
5003
+ if (argType) {
5004
+ args.push({ name: argName, type: argType });
5005
+ }
5006
+ }
5007
+ }
5008
+ const outputs = inferReturnType(body);
5009
+ return {
5010
+ name,
5011
+ access,
5012
+ args,
5013
+ outputs
5014
+ };
5015
+ }
5016
+ function parseType(typeStr) {
5017
+ typeStr = typeStr.replace(/[()]/g, "").trim();
5018
+ switch (typeStr) {
5019
+ case "uint":
5020
+ case "uint128":
5021
+ return "uint128";
5022
+ case "int":
5023
+ case "int128":
5024
+ return "int128";
5025
+ case "bool":
5026
+ return "bool";
5027
+ case "principal":
5028
+ return "principal";
5029
+ case "trait_reference":
5030
+ return "principal";
5031
+ default:
5032
+ if (typeStr.startsWith("string-ascii")) {
5033
+ return { "string-ascii": { length: 256 } };
5034
+ }
5035
+ if (typeStr.startsWith("string-utf8")) {
5036
+ return { "string-utf8": { length: 256 } };
5037
+ }
5038
+ if (typeStr.startsWith("buff")) {
5039
+ return { buff: { length: 32 } };
5040
+ }
5041
+ return "uint128";
5042
+ }
5043
+ }
5044
+ function inferReturnType(body) {
5045
+ if (body.includes("(ok")) {
5046
+ if (body.includes("(err")) {
5047
+ return {
5048
+ response: {
5049
+ ok: "bool",
5050
+ error: "uint128"
5051
+ }
5052
+ };
5053
+ }
5054
+ }
5055
+ if (body.includes("true") || body.includes("false")) {
5056
+ return "bool";
5057
+ }
5058
+ return "bool";
5059
+ }
5060
+ function parseApiResponse(apiResponse) {
5061
+ try {
5062
+ return normalizeAbi(apiResponse);
5063
+ } catch (error2) {
5064
+ throw new Error(`Failed to parse API response: ${error2}`);
5065
+ }
5066
+ }
5067
+ var init_clarity = () => {};
5068
+
4807
5069
  // ../../node_modules/@sindresorhus/is/dist/index.js
4808
5070
  function isTypedArrayName(name) {
4809
5071
  return typedArrayTypeNames.includes(name);
@@ -12495,8 +12757,8 @@ class StacksApiClient {
12495
12757
  var gotWithRetry;
12496
12758
  var init_api = __esm(() => {
12497
12759
  init_source3();
12498
- init_config();
12499
12760
  init_api_client();
12761
+ init_config();
12500
12762
  gotWithRetry = source_default2.extend({
12501
12763
  timeout: { request: 30000 },
12502
12764
  retry: {
@@ -12517,261 +12779,6 @@ function inferNetwork(address) {
12517
12779
  return;
12518
12780
  }
12519
12781
 
12520
- // src/utils/abi-compat.ts
12521
- function normalizeAccess(access) {
12522
- if (access === "read_only")
12523
- return "read-only";
12524
- return access;
12525
- }
12526
- function normalizeType(type) {
12527
- if (typeof type === "string") {
12528
- switch (type) {
12529
- case "uint128":
12530
- case "int128":
12531
- case "bool":
12532
- case "principal":
12533
- case "trait_reference":
12534
- return type;
12535
- default:
12536
- return type;
12537
- }
12538
- }
12539
- if (typeof type !== "object" || type === null) {
12540
- throw new Error(`Invalid ABI type: expected object, got ${typeof type}`);
12541
- }
12542
- const typeObj = type;
12543
- if ("buffer" in typeObj) {
12544
- const buffer2 = typeObj.buffer;
12545
- return {
12546
- buff: {
12547
- length: buffer2?.length ?? 32
12548
- }
12549
- };
12550
- }
12551
- if ("buff" in typeObj) {
12552
- const buff = typeObj.buff;
12553
- return {
12554
- buff: {
12555
- length: buff?.length ?? 32
12556
- }
12557
- };
12558
- }
12559
- if ("string-ascii" in typeObj) {
12560
- const strAscii = typeObj["string-ascii"];
12561
- return {
12562
- "string-ascii": {
12563
- length: strAscii?.length ?? 256
12564
- }
12565
- };
12566
- }
12567
- if ("string-utf8" in typeObj) {
12568
- const strUtf8 = typeObj["string-utf8"];
12569
- return {
12570
- "string-utf8": {
12571
- length: strUtf8?.length ?? 256
12572
- }
12573
- };
12574
- }
12575
- if ("response" in typeObj) {
12576
- const response2 = typeObj.response;
12577
- return {
12578
- response: {
12579
- ok: normalizeType(response2?.ok ?? "bool"),
12580
- error: normalizeType(response2?.error ?? "uint128")
12581
- }
12582
- };
12583
- }
12584
- if ("optional" in typeObj) {
12585
- return {
12586
- optional: normalizeType(typeObj.optional)
12587
- };
12588
- }
12589
- if ("list" in typeObj) {
12590
- const list = typeObj.list;
12591
- return {
12592
- list: {
12593
- type: normalizeType(list?.type ?? "uint128"),
12594
- length: list?.length ?? 100
12595
- }
12596
- };
12597
- }
12598
- if ("tuple" in typeObj) {
12599
- const tuple = typeObj.tuple;
12600
- return {
12601
- tuple: tuple.map((field) => ({
12602
- name: field.name,
12603
- type: normalizeType(field.type)
12604
- }))
12605
- };
12606
- }
12607
- throw new Error(`Unknown ABI type structure: ${JSON.stringify(type)}`);
12608
- }
12609
- function normalizeFunction(func) {
12610
- const access = normalizeAccess(func.access);
12611
- const args = func.args ?? [];
12612
- const outputs = func.outputs;
12613
- return {
12614
- name: func.name,
12615
- access,
12616
- args: args.map((arg) => ({
12617
- name: arg.name,
12618
- type: normalizeType(arg.type)
12619
- })),
12620
- outputs: normalizeType(typeof outputs === "object" && outputs !== null && "type" in outputs ? outputs.type : outputs)
12621
- };
12622
- }
12623
- function normalizeMap(map) {
12624
- return {
12625
- name: map.name,
12626
- key: normalizeType(map.key),
12627
- value: normalizeType(map.value)
12628
- };
12629
- }
12630
- function normalizeVariable(variable) {
12631
- return {
12632
- name: variable.name,
12633
- type: normalizeType(variable.type),
12634
- access: variable.access
12635
- };
12636
- }
12637
- function normalizeAbi(abi) {
12638
- if (typeof abi !== "object" || abi === null) {
12639
- return { functions: [] };
12640
- }
12641
- const abiObj = abi;
12642
- const functions = [];
12643
- const maps = [];
12644
- const variables = [];
12645
- if (Array.isArray(abiObj.functions)) {
12646
- for (const func of abiObj.functions) {
12647
- if (typeof func === "object" && func !== null) {
12648
- functions.push(normalizeFunction(func));
12649
- }
12650
- }
12651
- }
12652
- if (Array.isArray(abiObj.maps)) {
12653
- for (const map of abiObj.maps) {
12654
- if (typeof map === "object" && map !== null) {
12655
- maps.push(normalizeMap(map));
12656
- }
12657
- }
12658
- }
12659
- if (Array.isArray(abiObj.variables)) {
12660
- for (const variable of abiObj.variables) {
12661
- if (typeof variable === "object" && variable !== null) {
12662
- variables.push(normalizeVariable(variable));
12663
- }
12664
- }
12665
- }
12666
- return {
12667
- functions,
12668
- maps: maps.length > 0 ? maps : undefined,
12669
- variables: variables.length > 0 ? variables : undefined
12670
- };
12671
- }
12672
-
12673
- // src/parsers/clarity.ts
12674
- import { promises as fs2 } from "fs";
12675
- async function parseClarityFile(filePath) {
12676
- try {
12677
- const content = await fs2.readFile(filePath, "utf-8");
12678
- const result = parseClarityContent(content);
12679
- if (result.functions.length === 0) {
12680
- console.warn(`⚠️ No functions found in ${filePath}. ` + `For complex contracts, deploy first and use the contract address instead.`);
12681
- }
12682
- return result;
12683
- } catch (error2) {
12684
- throw new Error(`Unable to parse ${filePath}. ` + `For complex contracts, deploy first and use the contract address instead.
12685
- ` + `Original error: ${error2}`);
12686
- }
12687
- }
12688
- function parseClarityContent(content) {
12689
- const functions = [];
12690
- const functionRegex = /\(define-(public|read-only|private)\s+\(([^)]+)\)([\s\S]*?)\)\s*$/gm;
12691
- let match;
12692
- while ((match = functionRegex.exec(content)) !== null) {
12693
- const [, access, signature, body] = match;
12694
- const func = parseFunctionSignature(signature, access, body);
12695
- if (func) {
12696
- functions.push(func);
12697
- }
12698
- }
12699
- return { functions };
12700
- }
12701
- function parseFunctionSignature(signature, access, body) {
12702
- const parts = signature.trim().split(/\s+/);
12703
- const name = parts[0];
12704
- const args = [];
12705
- for (let i = 1;i < parts.length; i += 2) {
12706
- if (parts[i] && parts[i + 1]) {
12707
- const argName = parts[i].replace(/[()]/g, "");
12708
- const argType = parseType(parts[i + 1]);
12709
- if (argType) {
12710
- args.push({ name: argName, type: argType });
12711
- }
12712
- }
12713
- }
12714
- const outputs = inferReturnType(body);
12715
- return {
12716
- name,
12717
- access,
12718
- args,
12719
- outputs
12720
- };
12721
- }
12722
- function parseType(typeStr) {
12723
- typeStr = typeStr.replace(/[()]/g, "").trim();
12724
- switch (typeStr) {
12725
- case "uint":
12726
- case "uint128":
12727
- return "uint128";
12728
- case "int":
12729
- case "int128":
12730
- return "int128";
12731
- case "bool":
12732
- return "bool";
12733
- case "principal":
12734
- return "principal";
12735
- case "trait_reference":
12736
- return "principal";
12737
- default:
12738
- if (typeStr.startsWith("string-ascii")) {
12739
- return { "string-ascii": { length: 256 } };
12740
- }
12741
- if (typeStr.startsWith("string-utf8")) {
12742
- return { "string-utf8": { length: 256 } };
12743
- }
12744
- if (typeStr.startsWith("buff")) {
12745
- return { buff: { length: 32 } };
12746
- }
12747
- return "uint128";
12748
- }
12749
- }
12750
- function inferReturnType(body) {
12751
- if (body.includes("(ok")) {
12752
- if (body.includes("(err")) {
12753
- return {
12754
- response: {
12755
- ok: "bool",
12756
- error: "uint128"
12757
- }
12758
- };
12759
- }
12760
- }
12761
- if (body.includes("true") || body.includes("false")) {
12762
- return "bool";
12763
- }
12764
- return "bool";
12765
- }
12766
- function parseApiResponse(apiResponse) {
12767
- try {
12768
- return normalizeAbi(apiResponse);
12769
- } catch (error2) {
12770
- throw new Error(`Failed to parse API response: ${error2}`);
12771
- }
12772
- }
12773
- var init_clarity = () => {};
12774
-
12775
12782
  // src/lib/network.ts
12776
12783
  function getChainId(network) {
12777
12784
  return CHAIN_IDS[network].decimal.toString();
@@ -12811,7 +12818,7 @@ async function validateNetworkConsistency(config) {
12811
12818
  const envStr = result.stdout.toString();
12812
12819
  const match = envStr.match(/STACKS_CHAIN_ID=(0x[0-9a-fA-F]+|\d+)/);
12813
12820
  if (match) {
12814
- const containerId = parseInt(match[1], match[1].startsWith("0x") ? 16 : 10);
12821
+ const containerId = Number.parseInt(match[1], match[1].startsWith("0x") ? 16 : 10);
12815
12822
  if (containerId !== expected) {
12816
12823
  issues.push(`Container STACKS_CHAIN_ID=${containerId} but config expects ${expected} (${network})`);
12817
12824
  }
@@ -12825,7 +12832,7 @@ async function validateNetworkConsistency(config) {
12825
12832
  const content = await envFile.text();
12826
12833
  const match = content.match(/STACKS_CHAIN_ID=(0x[0-9a-fA-F]+|\d+)/);
12827
12834
  if (match) {
12828
- const fileId = parseInt(match[1], match[1].startsWith("0x") ? 16 : 10);
12835
+ const fileId = Number.parseInt(match[1], match[1].startsWith("0x") ? 16 : 10);
12829
12836
  if (fileId !== expected) {
12830
12837
  issues.push(`Node .env STACKS_CHAIN_ID=${fileId} but config expects ${expected} (${network})`);
12831
12838
  }
@@ -12864,7 +12871,15 @@ async function validateNodePath(path) {
12864
12871
  return { valid: true };
12865
12872
  }
12866
12873
  async function runManageScript(installPath, network, action, extraArgs = []) {
12867
- const args = ["bash", "./manage.sh", "-n", network, "-a", action, ...extraArgs];
12874
+ const args = [
12875
+ "bash",
12876
+ "./manage.sh",
12877
+ "-n",
12878
+ network,
12879
+ "-a",
12880
+ action,
12881
+ ...extraArgs
12882
+ ];
12868
12883
  const proc = Bun.spawn(args, {
12869
12884
  cwd: installPath,
12870
12885
  stdin: "inherit",
@@ -12875,9 +12890,7 @@ async function runManageScript(installPath, network, action, extraArgs = []) {
12875
12890
  return { stdout: "", stderr: "", exitCode };
12876
12891
  }
12877
12892
  async function startNodeContainers(installPath, network) {
12878
- const dirs = [
12879
- `${installPath}/persistent-data/${network}/stacks-blockchain`
12880
- ];
12893
+ const dirs = [`${installPath}/persistent-data/${network}/stacks-blockchain`];
12881
12894
  for (const dir of dirs) {
12882
12895
  await Bun.$`mkdir -p ${dir}`.quiet().nothrow();
12883
12896
  }
@@ -12888,7 +12901,11 @@ async function startNodeContainers(installPath, network) {
12888
12901
  const envStr = inspect2.stdout.toString();
12889
12902
  const match = envStr.match(/STACKS_CHAIN_ID=(\d+)/);
12890
12903
  if (match && match[1] !== chainId) {
12891
- await Bun.$`docker compose -f compose-files/common.yaml -f compose-files/networks/${network}.yaml --env-file .env down`.cwd(installPath).env({ ...process.env, SCRIPTPATH: installPath, STACKS_CHAIN_ID: chainId }).quiet().nothrow();
12904
+ await Bun.$`docker compose -f compose-files/common.yaml -f compose-files/networks/${network}.yaml --env-file .env down`.cwd(installPath).env({
12905
+ ...process.env,
12906
+ SCRIPTPATH: installPath,
12907
+ STACKS_CHAIN_ID: chainId
12908
+ }).quiet().nothrow();
12892
12909
  }
12893
12910
  }
12894
12911
  } catch {}
@@ -13003,7 +13020,7 @@ function formatLogLine(line) {
13003
13020
  return line;
13004
13021
  }
13005
13022
  const [, level, timestamp, _source, _thread, message] = match;
13006
- const date = new Date(parseFloat(timestamp) * 1000);
13023
+ const date = new Date(Number.parseFloat(timestamp) * 1000);
13007
13024
  const isoTime = date.toISOString();
13008
13025
  const shortMessage = extractKeyInfo(message);
13009
13026
  return `[stacks-node] [${isoTime}] ${level}: ${shortMessage}`;
@@ -13150,7 +13167,7 @@ var init_manager = __esm(() => {
13150
13167
  });
13151
13168
 
13152
13169
  // src/services/indexer.ts
13153
- import { resolve as resolve2, dirname } from "node:path";
13170
+ import { dirname, resolve as resolve2 } from "node:path";
13154
13171
  async function startIndexer(options2) {
13155
13172
  const port = options2.port ?? 3700;
13156
13173
  const rootDir = dirname(dirname(dirname(dirname(import.meta.dir))));
@@ -13170,7 +13187,7 @@ var init_indexer = __esm(() => {
13170
13187
  });
13171
13188
 
13172
13189
  // src/services/worker.ts
13173
- import { resolve as resolve3, dirname as dirname2 } from "node:path";
13190
+ import { dirname as dirname2, resolve as resolve3 } from "node:path";
13174
13191
  async function startWorker(options2) {
13175
13192
  const rootDir = dirname2(dirname2(dirname2(dirname2(import.meta.dir))));
13176
13193
  const workerPath = resolve3(rootDir, "packages/worker/src/index.ts");
@@ -13188,7 +13205,7 @@ var init_worker = __esm(() => {
13188
13205
  });
13189
13206
 
13190
13207
  // src/services/api.ts
13191
- import { resolve as resolve4, dirname as dirname3 } from "node:path";
13208
+ import { dirname as dirname3, resolve as resolve4 } from "node:path";
13192
13209
  async function startApi(options2) {
13193
13210
  const port = options2.port ?? 3800;
13194
13211
  const rootDir = dirname3(dirname3(dirname3(dirname3(import.meta.dir))));
@@ -13295,7 +13312,7 @@ var init_receiver_server = __esm(() => {
13295
13312
  });
13296
13313
 
13297
13314
  // src/services/subgraph-processor.ts
13298
- import { resolve as resolve5, dirname as dirname4 } from "node:path";
13315
+ import { dirname as dirname4, resolve as resolve5 } from "node:path";
13299
13316
  async function startSubgraphProcessor(options2) {
13300
13317
  const rootDir = dirname4(dirname4(dirname4(dirname4(import.meta.dir))));
13301
13318
  const servicePath = resolve5(rootDir, "packages/subgraphs/src/service.ts");
@@ -13333,8 +13350,8 @@ __export(exports_dev_impl, {
13333
13350
  restartDev: () => restartDev,
13334
13351
  isDevAlreadyRunning: () => isDevAlreadyRunning
13335
13352
  });
13336
- import { resolve as resolve6, dirname as dirname5, join as join6 } from "node:path";
13337
13353
  import { mkdirSync as mkdirSync2 } from "node:fs";
13354
+ import { dirname as dirname5, join as join6, resolve as resolve6 } from "node:path";
13338
13355
  async function isDevAlreadyRunning() {
13339
13356
  if (await isDevRunning()) {
13340
13357
  const running = await getRunningServices();
@@ -13360,9 +13377,9 @@ async function runBackground(options2) {
13360
13377
  }
13361
13378
  const config = await loadConfig();
13362
13379
  const dataDir = getDataDir(config);
13363
- const indexerPort = options2.stacksNode ? 3701 : parseInt(options2.indexerPort) || config.ports.indexer;
13364
- const apiPort = parseInt(options2.apiPort) || config.ports.api;
13365
- const receiverPort = parseInt(options2.receiverPort) || config.ports.receiver;
13380
+ const indexerPort = options2.stacksNode ? 3701 : Number.parseInt(options2.indexerPort) || config.ports.indexer;
13381
+ const apiPort = Number.parseInt(options2.apiPort) || config.ports.api;
13382
+ const receiverPort = Number.parseInt(options2.receiverPort) || config.ports.receiver;
13366
13383
  if (options2.stacksNode && config.node) {
13367
13384
  const validation = await validateNetworkConsistency(config);
13368
13385
  if (!validation.valid) {
@@ -13459,7 +13476,11 @@ async function runBackground(options2) {
13459
13476
  }
13460
13477
  {
13461
13478
  const subgraphsLogFile = getLogFile("subgraphs");
13462
- const subgraphsProc = Bun.spawn(["bun", "run", resolve6(packagesDir, "packages/subgraphs/src/service.ts")], {
13479
+ const subgraphsProc = Bun.spawn([
13480
+ "bun",
13481
+ "run",
13482
+ resolve6(packagesDir, "packages/subgraphs/src/service.ts")
13483
+ ], {
13463
13484
  env: { ...process.env, ...env },
13464
13485
  stdout: Bun.file(subgraphsLogFile),
13465
13486
  stderr: Bun.file(subgraphsLogFile)
@@ -13474,8 +13495,15 @@ async function runBackground(options2) {
13474
13495
  }
13475
13496
  if (options2.receiver) {
13476
13497
  const receiverLogFile = getLogFile("receiver");
13477
- const receiverArgs = ["bun", "run", resolve6(packagesDir, "packages/cli/src/services/receiver-standalone.ts")];
13478
- const receiverEnv = { ...process.env, PORT: String(receiverPort) };
13498
+ const receiverArgs = [
13499
+ "bun",
13500
+ "run",
13501
+ resolve6(packagesDir, "packages/cli/src/services/receiver-standalone.ts")
13502
+ ];
13503
+ const receiverEnv = {
13504
+ ...process.env,
13505
+ PORT: String(receiverPort)
13506
+ };
13479
13507
  if (options2.secret)
13480
13508
  receiverEnv.SIGNING_SECRET = options2.secret;
13481
13509
  const receiverProc = Bun.spawn(receiverArgs, {
@@ -13535,9 +13563,9 @@ async function runForeground(options2) {
13535
13563
  }
13536
13564
  const config = await loadConfig();
13537
13565
  const dataDir = getDataDir(config);
13538
- const indexerPort = options2.stacksNode ? 3701 : parseInt(options2.indexerPort) || config.ports.indexer;
13539
- const apiPort = parseInt(options2.apiPort) || config.ports.api;
13540
- const receiverPort = parseInt(options2.receiverPort) || config.ports.receiver;
13566
+ const indexerPort = options2.stacksNode ? 3701 : Number.parseInt(options2.indexerPort) || config.ports.indexer;
13567
+ const apiPort = Number.parseInt(options2.apiPort) || config.ports.api;
13568
+ const receiverPort = Number.parseInt(options2.receiverPort) || config.ports.receiver;
13541
13569
  let devPostgresStarted = false;
13542
13570
  const shutdown = async () => {
13543
13571
  console.log(`
@@ -13597,13 +13625,18 @@ async function runForeground(options2) {
13597
13625
  }
13598
13626
  await startApi({ port: apiPort, onLog: (line) => logService("api", line) });
13599
13627
  console.log(green(" ✓ API"), dim(`http://localhost:${apiPort}`));
13600
- await startIndexer({ port: indexerPort, onLog: (line) => logService("indexer", line) });
13628
+ await startIndexer({
13629
+ port: indexerPort,
13630
+ onLog: (line) => logService("indexer", line)
13631
+ });
13601
13632
  console.log(green(" ✓ Indexer"), dim(`http://localhost:${indexerPort}`));
13602
13633
  if (options2.worker) {
13603
13634
  await startWorker({ onLog: (line) => logService("worker", line) });
13604
13635
  console.log(green(" ✓ Worker"), dim("processing jobs"));
13605
13636
  }
13606
- await startSubgraphProcessor({ onLog: (line) => logService("subgraphs", line) });
13637
+ await startSubgraphProcessor({
13638
+ onLog: (line) => logService("subgraphs", line)
13639
+ });
13607
13640
  console.log(green(" ✓ Subgraph processor"), dim("processing subgraphs"));
13608
13641
  console.log("");
13609
13642
  printUrls(indexerPort, apiPort, receiverPort, options2.receiver);
@@ -13638,7 +13671,7 @@ async function showLogs(options2) {
13638
13671
  info("No log files found");
13639
13672
  return;
13640
13673
  }
13641
- const lines = parseInt(options2.lines);
13674
+ const lines = Number.parseInt(options2.lines);
13642
13675
  const verbose = options2.verbose ?? false;
13643
13676
  if (options2.follow) {
13644
13677
  await followLogs2(serviceEntries, lines, verbose);
@@ -13859,8 +13892,15 @@ async function restartDev() {
13859
13892
  if (state.services.receiver) {
13860
13893
  const receiverPort = config.ports.receiver;
13861
13894
  const receiverLogFile = getLogFile("receiver");
13862
- const receiverProc = Bun.spawn(["bun", "run", resolve6(packagesDir, "packages/cli/src/services/receiver-standalone.ts")], {
13863
- env: { ...process.env, PORT: String(receiverPort) },
13895
+ const receiverProc = Bun.spawn([
13896
+ "bun",
13897
+ "run",
13898
+ resolve6(packagesDir, "packages/cli/src/services/receiver-standalone.ts")
13899
+ ], {
13900
+ env: {
13901
+ ...process.env,
13902
+ PORT: String(receiverPort)
13903
+ },
13864
13904
  stdout: Bun.file(receiverLogFile),
13865
13905
  stderr: Bun.file(receiverLogFile)
13866
13906
  });
@@ -13983,13 +14023,21 @@ async function isDatabaseReachable(url) {
13983
14023
  try {
13984
14024
  const parsed = new URL(url);
13985
14025
  const host = parsed.hostname;
13986
- const port = parseInt(parsed.port || "5432");
14026
+ const port = Number.parseInt(parsed.port || "5432");
13987
14027
  const socket = await Bun.$`pg_isready -h ${host} -p ${port}`.quiet().nothrow();
13988
14028
  if (socket.exitCode === 0)
13989
14029
  return true;
13990
- const conn = await Bun.connect({ hostname: host, port, socket: { data() {}, open(s) {
13991
- s.end();
13992
- }, error() {} } }).catch(() => null);
14030
+ const conn = await Bun.connect({
14031
+ hostname: host,
14032
+ port,
14033
+ socket: {
14034
+ data() {},
14035
+ open(s) {
14036
+ s.end();
14037
+ },
14038
+ error() {}
14039
+ }
14040
+ }).catch(() => null);
13993
14041
  return conn !== null;
13994
14042
  } catch {
13995
14043
  return false;
@@ -14025,12 +14073,12 @@ async function runMigrations(databaseUrl) {
14025
14073
  }
14026
14074
  var DEV_DATABASE_URL3 = "postgres://postgres:postgres@localhost:5432/streams_dev", serviceColors;
14027
14075
  var init_dev_impl = __esm(() => {
14028
- init_services();
14029
- init_output();
14030
- init_dev_state();
14031
14076
  init_config();
14077
+ init_dev_state();
14032
14078
  init_docker();
14033
14079
  init_network();
14080
+ init_output();
14081
+ init_services();
14034
14082
  serviceColors = {
14035
14083
  api: blue,
14036
14084
  indexer: cyan,
@@ -14051,7 +14099,7 @@ __export(exports_node_impl, {
14051
14099
  runSetupWizard: () => runSetupWizard,
14052
14100
  restartNode: () => restartNode
14053
14101
  });
14054
- import { input as input3, select as select3, confirm as confirm6 } from "@inquirer/prompts";
14102
+ import { confirm as confirm6, input as input3, select as select3 } from "@inquirer/prompts";
14055
14103
  async function runSetupWizard() {
14056
14104
  console.log("");
14057
14105
  console.log(blue("Stacks Node Setup Wizard"));
@@ -14103,14 +14151,14 @@ async function runSetupWizard() {
14103
14151
  message: "Enter custom indexer port:",
14104
14152
  default: "3700",
14105
14153
  validate: (value) => {
14106
- const port = parseInt(value);
14154
+ const port = Number.parseInt(value);
14107
14155
  if (isNaN(port) || port < 1 || port > 65535) {
14108
14156
  return "Invalid port number";
14109
14157
  }
14110
14158
  return true;
14111
14159
  }
14112
14160
  });
14113
- indexerPort = parseInt(portInput);
14161
+ indexerPort = Number.parseInt(portInput);
14114
14162
  }
14115
14163
  }
14116
14164
  const config = await loadConfig();
@@ -14130,7 +14178,10 @@ async function runSetupWizard() {
14130
14178
  console.log(formatKeyValue([
14131
14179
  [" Install path", installPath],
14132
14180
  [" Network", network],
14133
- [" Auto-start indexer", autoStartIndexer ? `Yes (port ${indexerPort})` : "No"]
14181
+ [
14182
+ " Auto-start indexer",
14183
+ autoStartIndexer ? `Yes (port ${indexerPort})` : "No"
14184
+ ]
14134
14185
  ]));
14135
14186
  console.log("");
14136
14187
  console.log(dim("Next steps:"));
@@ -14361,8 +14412,14 @@ async function showStatus(pathOverride, jsonOutput) {
14361
14412
  if (nodeInfo) {
14362
14413
  console.log(blue("Chain Info"));
14363
14414
  console.log(formatKeyValue([
14364
- [" Stacks Height", nodeInfo.stacks_tip_height?.toString() || "syncing..."],
14365
- [" Burn Height", nodeInfo.burn_block_height?.toString() || "syncing..."],
14415
+ [
14416
+ " Stacks Height",
14417
+ nodeInfo.stacks_tip_height?.toString() || "syncing..."
14418
+ ],
14419
+ [
14420
+ " Burn Height",
14421
+ nodeInfo.burn_block_height?.toString() || "syncing..."
14422
+ ],
14366
14423
  [" Peers", peerCount.toString()],
14367
14424
  [" Version", nodeInfo.server_version || "unknown"]
14368
14425
  ]));
@@ -14451,11 +14508,11 @@ async function showConfigCheck(indexerPort) {
14451
14508
  }
14452
14509
  var DEFAULT_RPC_PORT = 20443;
14453
14510
  var init_node_impl = __esm(() => {
14511
+ init_api_client();
14454
14512
  init_config();
14513
+ init_network();
14455
14514
  init_node_manager();
14456
14515
  init_output();
14457
- init_api_client();
14458
- init_network();
14459
14516
  });
14460
14517
 
14461
14518
  // ../../node_modules/chalk/source/vendor/ansi-styles/index.js
@@ -20471,197 +20528,16 @@ var init_plugin_manager = __esm(() => {
20471
20528
  validateStacksAddress = _validateStacksAddress;
20472
20529
  });
20473
20530
 
20474
- // src/utils/config.ts
20475
- import { promises as fs4 } from "fs";
20476
- import path2 from "path";
20477
- import { tmpdir } from "os";
20478
- import { randomBytes } from "crypto";
20479
- import { pathToFileURL } from "url";
20480
- import { createRequire as createRequire2 } from "module";
20481
- async function findConfigFile(cwd) {
20482
- for (const fileName of CONFIG_FILE_NAMES) {
20483
- const filePath = path2.join(cwd, fileName);
20484
- try {
20485
- await fs4.access(filePath);
20486
- return filePath;
20487
- } catch {}
20488
- }
20489
- return null;
20490
- }
20491
- async function loadConfig2(configPath) {
20492
- const cwd = process.cwd();
20493
- const resolvedPath = configPath ? path2.resolve(cwd, configPath) : await findConfigFile(cwd);
20494
- if (!resolvedPath) {
20495
- throw new Error("No config file found. Create a secondlayer.config.ts file or specify a path with --config");
20496
- }
20497
- let config;
20498
- if (resolvedPath.endsWith(".ts")) {
20499
- const code = await fs4.readFile(resolvedPath, "utf-8");
20500
- let replacementPath;
20501
- try {
20502
- const require2 = createRequire2(import.meta.url);
20503
- const packagePath = require2.resolve("@secondlayer/cli");
20504
- replacementPath = pathToFileURL(packagePath).href;
20505
- } catch {
20506
- const currentModuleDir = path2.dirname(new URL(import.meta.url).pathname);
20507
- const indexPath = path2.resolve(currentModuleDir, "../index");
20508
- replacementPath = pathToFileURL(indexPath).href;
20509
- }
20510
- const transformedCode = code.replace(/from\s+["']@secondlayer\/cli["']/g, `from '${replacementPath}'`);
20511
- const { transformSync } = await import("esbuild");
20512
- const result = transformSync(transformedCode, {
20513
- format: "esm",
20514
- target: "node18",
20515
- loader: "ts"
20516
- });
20517
- const tempPath = path2.join(tmpdir(), `secondlayer-config-${randomBytes(4).toString("hex")}.mjs`);
20518
- await fs4.writeFile(tempPath, result.code);
20519
- try {
20520
- const fileUrl = pathToFileURL(tempPath).href;
20521
- const module = await import(fileUrl);
20522
- config = module.default;
20523
- } finally {
20524
- await fs4.unlink(tempPath).catch(() => {});
20525
- }
20526
- } else {
20527
- const fileUrl = pathToFileURL(resolvedPath).href;
20528
- const module = await import(fileUrl);
20529
- config = module.default;
20530
- }
20531
- if (!config) {
20532
- throw new Error("Config file must export a default configuration");
20533
- }
20534
- if (typeof config === "function") {
20535
- config = config({});
20536
- }
20537
- validateConfig(config);
20538
- const pluginManager = new PluginManager;
20539
- if (config.plugins && Array.isArray(config.plugins)) {
20540
- for (const plugin of config.plugins) {
20541
- pluginManager.register(plugin);
20542
- }
20543
- }
20544
- const resolvedConfig = await pluginManager.transformConfig(config);
20545
- return resolvedConfig;
20546
- }
20547
- function validateConfig(config) {
20548
- if (!config || typeof config !== "object") {
20549
- throw new Error("Config must be an object");
20550
- }
20551
- const c = config;
20552
- if (c.contracts && !Array.isArray(c.contracts)) {
20553
- throw new Error("Config contracts must be an array");
20554
- }
20555
- if (!c.out || typeof c.out !== "string") {
20556
- throw new Error("Config out must be a string path");
20557
- }
20558
- if (c.contracts) {
20559
- for (const contract of c.contracts) {
20560
- if (!contract.address && !contract.source) {
20561
- throw new Error("Each contract must have either an address or source");
20562
- }
20563
- }
20564
- }
20565
- if (c.plugins && !Array.isArray(c.plugins)) {
20566
- throw new Error("Config plugins must be an array");
20567
- }
20568
- }
20569
- var CONFIG_FILE_NAMES;
20570
- var init_config2 = __esm(() => {
20571
- init_plugin_manager();
20572
- CONFIG_FILE_NAMES = [
20573
- "secondlayer.config.ts",
20574
- "secondlayer.config",
20575
- "secondlayer.config.mjs"
20576
- ];
20577
- });
20578
-
20579
- // src/utils/type-mapping.ts
20531
+ // src/utils/clarity-conversion.ts
20580
20532
  import {
20581
- toCamelCase as toCamelCase3,
20533
+ isAbiBuffer as isAbiBuffer2,
20582
20534
  isAbiList,
20583
- isAbiTuple,
20584
20535
  isAbiOptional as isAbiOptional2,
20585
20536
  isAbiResponse,
20586
- isAbiBuffer as isAbiBuffer2,
20587
20537
  isAbiStringAscii as isAbiStringAscii2,
20588
- isAbiStringUtf8 as isAbiStringUtf82
20589
- } from "@secondlayer/stacks/clarity";
20590
- function clarityTypeToTS(type) {
20591
- if (typeof type === "string") {
20592
- switch (type) {
20593
- case "uint128":
20594
- case "int128":
20595
- return "bigint";
20596
- case "bool":
20597
- return "boolean";
20598
- case "principal":
20599
- case "trait_reference":
20600
- return "string";
20601
- default: {
20602
- const typeStr = type;
20603
- if (typeStr === "none") {
20604
- return "null";
20605
- }
20606
- if (typeStr.includes("string") || typeStr.includes("ascii") || typeStr.includes("utf8")) {
20607
- return "string";
20608
- }
20609
- if (typeStr.includes("buff")) {
20610
- return "Uint8Array | string | { type: 'ascii' | 'utf8' | 'hex'; value: string }";
20611
- }
20612
- if (typeStr.includes("uint") || typeStr.includes("int")) {
20613
- return "bigint";
20614
- }
20615
- return "any";
20616
- }
20617
- }
20618
- }
20619
- if (isAbiBuffer2(type)) {
20620
- return "Uint8Array | string | { type: 'ascii' | 'utf8' | 'hex'; value: string }";
20621
- }
20622
- if (isAbiStringAscii2(type) || isAbiStringUtf82(type)) {
20623
- return "string";
20624
- }
20625
- if (isAbiOptional2(type)) {
20626
- const innerType = clarityTypeToTS(type.optional);
20627
- if (innerType.includes(" | ") && !innerType.startsWith("(")) {
20628
- return `(${innerType}) | null`;
20629
- }
20630
- return `${innerType} | null`;
20631
- }
20632
- if (isAbiList(type)) {
20633
- const innerType = clarityTypeToTS(type.list.type);
20634
- if (innerType.includes(" | ") && !innerType.startsWith("(")) {
20635
- return `(${innerType})[]`;
20636
- }
20637
- return `${innerType}[]`;
20638
- }
20639
- if (isAbiTuple(type)) {
20640
- const fields = type.tuple.map((field) => `${toCamelCase3(field.name)}: ${clarityTypeToTS(field.type)}`).join("; ");
20641
- return `{ ${fields} }`;
20642
- }
20643
- if (isAbiResponse(type)) {
20644
- const okType = clarityTypeToTS(type.response.ok);
20645
- const errType = clarityTypeToTS(type.response.error);
20646
- return `{ ok: ${okType} } | { err: ${errType} }`;
20647
- }
20648
- return "any";
20649
- }
20650
- function getTypeForArg(arg) {
20651
- return clarityTypeToTS(arg.type);
20652
- }
20653
- var init_type_mapping = () => {};
20654
-
20655
- // src/utils/clarity-conversion.ts
20656
- import {
20657
- toCamelCase as toCamelCase4,
20658
- isAbiStringAscii as isAbiStringAscii3,
20659
- isAbiStringUtf8 as isAbiStringUtf83,
20660
- isAbiBuffer as isAbiBuffer3,
20661
- isAbiOptional as isAbiOptional3,
20662
- isAbiList as isAbiList2,
20663
- isAbiTuple as isAbiTuple2,
20664
- isAbiResponse as isAbiResponse2
20538
+ isAbiStringUtf8 as isAbiStringUtf82,
20539
+ isAbiTuple,
20540
+ toCamelCase as toCamelCase3
20665
20541
  } from "@secondlayer/stacks/clarity";
20666
20542
  function generateClarityConversion(argName, argType) {
20667
20543
  const type = argType.type;
@@ -20692,13 +20568,13 @@ function generateClarityConversion(argName, argType) {
20692
20568
  return `${argName}`;
20693
20569
  }
20694
20570
  }
20695
- if (isAbiStringAscii3(type)) {
20571
+ if (isAbiStringAscii2(type)) {
20696
20572
  return `Cl.stringAscii(${argName})`;
20697
20573
  }
20698
- if (isAbiStringUtf83(type)) {
20574
+ if (isAbiStringUtf82(type)) {
20699
20575
  return `Cl.stringUtf8(${argName})`;
20700
20576
  }
20701
- if (isAbiBuffer3(type)) {
20577
+ if (isAbiBuffer2(type)) {
20702
20578
  return `(() => {
20703
20579
  const value = ${argName};
20704
20580
  if (value instanceof Uint8Array) {
@@ -20729,13 +20605,13 @@ function generateClarityConversion(argName, argType) {
20729
20605
  throw new Error(\`Invalid buffer value: \${value}\`);
20730
20606
  })()`;
20731
20607
  }
20732
- if (isAbiOptional3(type)) {
20608
+ if (isAbiOptional2(type)) {
20733
20609
  const innerConversion = generateClarityConversion(argName, {
20734
20610
  type: type.optional
20735
20611
  });
20736
20612
  return `${argName} !== null ? Cl.some(${innerConversion.replace(argName, `${argName}`)}) : Cl.none()`;
20737
20613
  }
20738
- if (isAbiList2(type)) {
20614
+ if (isAbiList(type)) {
20739
20615
  const innerConversion = generateClarityConversion("item", {
20740
20616
  type: type.list.type
20741
20617
  });
@@ -20748,11 +20624,11 @@ function generateClarityConversion(argName, argType) {
20748
20624
  return Cl.list(listValue.map(item => ${innerConversion}));
20749
20625
  })()`;
20750
20626
  }
20751
- if (isAbiTuple2(type)) {
20627
+ if (isAbiTuple(type)) {
20752
20628
  const requiredFields = type.tuple.map((f) => f.name);
20753
20629
  const fieldNames = JSON.stringify(requiredFields);
20754
20630
  const fields = type.tuple.map((field) => {
20755
- const camelFieldName = toCamelCase4(field.name);
20631
+ const camelFieldName = toCamelCase3(field.name);
20756
20632
  const fieldConversion = generateClarityConversion(`tupleValue.${camelFieldName}`, { type: field.type });
20757
20633
  return `"${field.name}": ${fieldConversion}`;
20758
20634
  }).join(", ");
@@ -20768,7 +20644,7 @@ function generateClarityConversion(argName, argType) {
20768
20644
  return Cl.tuple({ ${fields} });
20769
20645
  })()`;
20770
20646
  }
20771
- if (isAbiResponse2(type)) {
20647
+ if (isAbiResponse(type)) {
20772
20648
  const okConversion = generateClarityConversion(`responseValue.ok`, {
20773
20649
  type: type.response.ok
20774
20650
  });
@@ -20792,8 +20668,87 @@ function generateClarityConversion(argName, argType) {
20792
20668
  }
20793
20669
  var init_clarity_conversion = () => {};
20794
20670
 
20671
+ // src/utils/type-mapping.ts
20672
+ import {
20673
+ isAbiBuffer as isAbiBuffer3,
20674
+ isAbiList as isAbiList2,
20675
+ isAbiOptional as isAbiOptional3,
20676
+ isAbiResponse as isAbiResponse2,
20677
+ isAbiStringAscii as isAbiStringAscii3,
20678
+ isAbiStringUtf8 as isAbiStringUtf83,
20679
+ isAbiTuple as isAbiTuple2,
20680
+ toCamelCase as toCamelCase4
20681
+ } from "@secondlayer/stacks/clarity";
20682
+ function clarityTypeToTS(type) {
20683
+ if (typeof type === "string") {
20684
+ switch (type) {
20685
+ case "uint128":
20686
+ case "int128":
20687
+ return "bigint";
20688
+ case "bool":
20689
+ return "boolean";
20690
+ case "principal":
20691
+ case "trait_reference":
20692
+ return "string";
20693
+ default: {
20694
+ const typeStr = type;
20695
+ if (typeStr === "none") {
20696
+ return "null";
20697
+ }
20698
+ if (typeStr.includes("string") || typeStr.includes("ascii") || typeStr.includes("utf8")) {
20699
+ return "string";
20700
+ }
20701
+ if (typeStr.includes("buff")) {
20702
+ return "Uint8Array | string | { type: 'ascii' | 'utf8' | 'hex'; value: string }";
20703
+ }
20704
+ if (typeStr.includes("uint") || typeStr.includes("int")) {
20705
+ return "bigint";
20706
+ }
20707
+ return "any";
20708
+ }
20709
+ }
20710
+ }
20711
+ if (isAbiBuffer3(type)) {
20712
+ return "Uint8Array | string | { type: 'ascii' | 'utf8' | 'hex'; value: string }";
20713
+ }
20714
+ if (isAbiStringAscii3(type) || isAbiStringUtf83(type)) {
20715
+ return "string";
20716
+ }
20717
+ if (isAbiOptional3(type)) {
20718
+ const innerType = clarityTypeToTS(type.optional);
20719
+ if (innerType.includes(" | ") && !innerType.startsWith("(")) {
20720
+ return `(${innerType}) | null`;
20721
+ }
20722
+ return `${innerType} | null`;
20723
+ }
20724
+ if (isAbiList2(type)) {
20725
+ const innerType = clarityTypeToTS(type.list.type);
20726
+ if (innerType.includes(" | ") && !innerType.startsWith("(")) {
20727
+ return `(${innerType})[]`;
20728
+ }
20729
+ return `${innerType}[]`;
20730
+ }
20731
+ if (isAbiTuple2(type)) {
20732
+ const fields = type.tuple.map((field) => `${toCamelCase4(field.name)}: ${clarityTypeToTS(field.type)}`).join("; ");
20733
+ return `{ ${fields} }`;
20734
+ }
20735
+ if (isAbiResponse2(type)) {
20736
+ const okType = clarityTypeToTS(type.response.ok);
20737
+ const errType = clarityTypeToTS(type.response.error);
20738
+ return `{ ok: ${okType} } | { err: ${errType} }`;
20739
+ }
20740
+ return "any";
20741
+ }
20742
+ function getTypeForArg(arg) {
20743
+ return clarityTypeToTS(arg.type);
20744
+ }
20745
+ var init_type_mapping = () => {};
20746
+
20795
20747
  // src/utils/generator-helpers.ts
20796
- import { toCamelCase as toCamelCase5, isAbiTuple as isAbiTuple3 } from "@secondlayer/stacks/clarity";
20748
+ import {
20749
+ isAbiTuple as isAbiTuple3,
20750
+ toCamelCase as toCamelCase5
20751
+ } from "@secondlayer/stacks/clarity";
20797
20752
  function generateMapKeyConversion(keyType) {
20798
20753
  if (isAbiTuple3(keyType)) {
20799
20754
  const fields = keyType.tuple.map((field) => {
@@ -20806,8 +20761,8 @@ function generateMapKeyConversion(keyType) {
20806
20761
  return generateClarityConversion("key", { type: keyType });
20807
20762
  }
20808
20763
  var init_generator_helpers = __esm(() => {
20809
- init_type_mapping();
20810
20764
  init_clarity_conversion();
20765
+ init_type_mapping();
20811
20766
  });
20812
20767
 
20813
20768
  // src/generators/contract.ts
@@ -21108,10 +21063,115 @@ function generateConstantsObject(variables, address, contractName) {
21108
21063
  }`;
21109
21064
  }
21110
21065
  var init_contract = __esm(() => {
21111
- init_format();
21112
- init_type_mapping();
21113
21066
  init_clarity_conversion();
21067
+ init_format();
21114
21068
  init_generator_helpers();
21069
+ init_type_mapping();
21070
+ });
21071
+
21072
+ // src/utils/config.ts
21073
+ import { randomBytes } from "crypto";
21074
+ import { promises as fs4 } from "fs";
21075
+ import { createRequire as createRequire2 } from "module";
21076
+ import { tmpdir } from "os";
21077
+ import path2 from "path";
21078
+ import { pathToFileURL } from "url";
21079
+ async function findConfigFile(cwd) {
21080
+ for (const fileName of CONFIG_FILE_NAMES) {
21081
+ const filePath = path2.join(cwd, fileName);
21082
+ try {
21083
+ await fs4.access(filePath);
21084
+ return filePath;
21085
+ } catch {}
21086
+ }
21087
+ return null;
21088
+ }
21089
+ async function loadConfig2(configPath) {
21090
+ const cwd = process.cwd();
21091
+ const resolvedPath = configPath ? path2.resolve(cwd, configPath) : await findConfigFile(cwd);
21092
+ if (!resolvedPath) {
21093
+ throw new Error("No config file found. Create a secondlayer.config.ts file or specify a path with --config");
21094
+ }
21095
+ let config;
21096
+ if (resolvedPath.endsWith(".ts")) {
21097
+ const code = await fs4.readFile(resolvedPath, "utf-8");
21098
+ let replacementPath;
21099
+ try {
21100
+ const require2 = createRequire2(import.meta.url);
21101
+ const packagePath = require2.resolve("@secondlayer/cli");
21102
+ replacementPath = pathToFileURL(packagePath).href;
21103
+ } catch {
21104
+ const currentModuleDir = path2.dirname(new URL(import.meta.url).pathname);
21105
+ const indexPath = path2.resolve(currentModuleDir, "../index");
21106
+ replacementPath = pathToFileURL(indexPath).href;
21107
+ }
21108
+ const transformedCode = code.replace(/from\s+["']@secondlayer\/cli["']/g, `from '${replacementPath}'`);
21109
+ const { transformSync } = await import("esbuild");
21110
+ const result = transformSync(transformedCode, {
21111
+ format: "esm",
21112
+ target: "node18",
21113
+ loader: "ts"
21114
+ });
21115
+ const tempPath = path2.join(tmpdir(), `secondlayer-config-${randomBytes(4).toString("hex")}.mjs`);
21116
+ await fs4.writeFile(tempPath, result.code);
21117
+ try {
21118
+ const fileUrl = pathToFileURL(tempPath).href;
21119
+ const module = await import(fileUrl);
21120
+ config = module.default;
21121
+ } finally {
21122
+ await fs4.unlink(tempPath).catch(() => {});
21123
+ }
21124
+ } else {
21125
+ const fileUrl = pathToFileURL(resolvedPath).href;
21126
+ const module = await import(fileUrl);
21127
+ config = module.default;
21128
+ }
21129
+ if (!config) {
21130
+ throw new Error("Config file must export a default configuration");
21131
+ }
21132
+ if (typeof config === "function") {
21133
+ config = config({});
21134
+ }
21135
+ validateConfig(config);
21136
+ const pluginManager = new PluginManager;
21137
+ if (config.plugins && Array.isArray(config.plugins)) {
21138
+ for (const plugin of config.plugins) {
21139
+ pluginManager.register(plugin);
21140
+ }
21141
+ }
21142
+ const resolvedConfig = await pluginManager.transformConfig(config);
21143
+ return resolvedConfig;
21144
+ }
21145
+ function validateConfig(config) {
21146
+ if (!config || typeof config !== "object") {
21147
+ throw new Error("Config must be an object");
21148
+ }
21149
+ const c = config;
21150
+ if (c.contracts && !Array.isArray(c.contracts)) {
21151
+ throw new Error("Config contracts must be an array");
21152
+ }
21153
+ if (!c.out || typeof c.out !== "string") {
21154
+ throw new Error("Config out must be a string path");
21155
+ }
21156
+ if (c.contracts) {
21157
+ for (const contract of c.contracts) {
21158
+ if (!contract.address && !contract.source) {
21159
+ throw new Error("Each contract must have either an address or source");
21160
+ }
21161
+ }
21162
+ }
21163
+ if (c.plugins && !Array.isArray(c.plugins)) {
21164
+ throw new Error("Config plugins must be an array");
21165
+ }
21166
+ }
21167
+ var CONFIG_FILE_NAMES;
21168
+ var init_config2 = __esm(() => {
21169
+ init_plugin_manager();
21170
+ CONFIG_FILE_NAMES = [
21171
+ "secondlayer.config.ts",
21172
+ "secondlayer.config",
21173
+ "secondlayer.config.mjs"
21174
+ ];
21115
21175
  });
21116
21176
 
21117
21177
  // ../../node_modules/ansis/index.cjs
@@ -32233,8 +32293,8 @@ async function checkBaseDependencies(targetDir) {
32233
32293
  var BASE_DEPENDENCIES;
32234
32294
  var init_dependencies = __esm(() => {
32235
32295
  init_dist4();
32236
- init_execa();
32237
32296
  init_source4();
32297
+ init_execa();
32238
32298
  BASE_DEPENDENCIES = {
32239
32299
  dependencies: ["@secondlayer/stacks"],
32240
32300
  devDependencies: []
@@ -32249,8 +32309,8 @@ __export(exports_generate, {
32249
32309
  generate: () => generate
32250
32310
  });
32251
32311
  import path10 from "path";
32252
- import { toCamelCase as toCamelCase7 } from "@secondlayer/stacks/clarity";
32253
32312
  import { getErrorMessage as getErrorMessage2 } from "@secondlayer/shared";
32313
+ import { toCamelCase as toCamelCase7 } from "@secondlayer/stacks/clarity";
32254
32314
  function isContractAddress(input4) {
32255
32315
  const contractIdPattern = /^(SP|ST|SM|SN)[A-Z0-9]{38,}\.[a-zA-Z][a-zA-Z0-9-]*$/;
32256
32316
  return contractIdPattern.test(input4);
@@ -32479,11 +32539,11 @@ async function resolveContracts(source, defaultNetwork, apiKey, apiUrl) {
32479
32539
  var import_fast_glob, DEFAULT_DEVNET_ADDRESS = "ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM";
32480
32540
  var init_generate = __esm(() => {
32481
32541
  init_source4();
32482
- init_config2();
32483
- init_api();
32484
- init_clarity();
32485
- init_contract();
32486
32542
  init_plugin_manager();
32543
+ init_contract();
32544
+ init_clarity();
32545
+ init_api();
32546
+ init_config2();
32487
32547
  init_dependencies();
32488
32548
  import_fast_glob = __toESM(require_out4(), 1);
32489
32549
  });
@@ -32556,7 +32616,7 @@ var {
32556
32616
  // package.json
32557
32617
  var package_default = {
32558
32618
  name: "@secondlayer/cli",
32559
- version: "1.7.0",
32619
+ version: "1.9.0",
32560
32620
  description: "CLI for streams, subgraphs, and real-time blockchain indexing on Stacks",
32561
32621
  type: "module",
32562
32622
  bin: {
@@ -32597,10 +32657,10 @@ var package_default = {
32597
32657
  license: "MIT",
32598
32658
  dependencies: {
32599
32659
  "@inquirer/prompts": "^8.2.0",
32600
- "@secondlayer/sdk": "^0.7.0",
32601
- "@secondlayer/shared": "^0.7.1",
32660
+ "@secondlayer/sdk": "^0.8.1",
32661
+ "@secondlayer/shared": "^0.9.0",
32602
32662
  "@secondlayer/stacks": "^0.2.2",
32603
- "@secondlayer/subgraphs": "^0.6.0",
32663
+ "@secondlayer/subgraphs": "^0.7.2",
32604
32664
  "@biomejs/js-api": "^0.7.0",
32605
32665
  "@biomejs/wasm-nodejs": "^1.9.0",
32606
32666
  esbuild: "^0.19.0",
@@ -32988,10 +33048,10 @@ async function validateDatabaseConnection(url) {
32988
33048
  }
32989
33049
  }
32990
33050
  // src/commands/setup.ts
32991
- init_output();
32992
33051
  init_api_client();
32993
- import { select as select2, input, confirm } from "@inquirer/prompts";
32994
33052
  init_config();
33053
+ import { confirm, input, select as select2 } from "@inquirer/prompts";
33054
+ init_output();
32995
33055
  var STREAMS_DIR = "streams";
32996
33056
  function registerSetupCommand(program2) {
32997
33057
  program2.command("setup").description("Set up a streams project and configure settings").option("--detect-only", "Only detect existing Stacks nodes, don't initialize").option("-y, --yes", "Use defaults without prompts").option("--data-dir <path>", "Data directory path").option("--node-path <path>", "Path to Stacks node").option("--network <network>", "Network (local, testnet, or mainnet)").option("--endpoint-url <url>", "Default endpoint URL for new streams").action(async (options) => {
@@ -33054,9 +33114,15 @@ async function runWizard() {
33054
33114
  const network = await select2({
33055
33115
  message: "How do you want to use Stacks Streams?",
33056
33116
  choices: [
33057
- { name: "Hosted mainnet (recommended — zero setup)", value: "mainnet" },
33117
+ {
33118
+ name: "Hosted mainnet (recommended — zero setup)",
33119
+ value: "mainnet"
33120
+ },
33058
33121
  { name: "Hosted testnet", value: "testnet" },
33059
- { name: "Local development (run your own node + services)", value: "local" }
33122
+ {
33123
+ name: "Local development (run your own node + services)",
33124
+ value: "local"
33125
+ }
33060
33126
  ]
33061
33127
  });
33062
33128
  config.network = network;
@@ -33266,14 +33332,22 @@ async function hostedLogin(config) {
33266
33332
  }
33267
33333
  const result = await verifyRes.json();
33268
33334
  const { hostname } = await import("node:os");
33269
- const sessionHeaders = { Authorization: `Bearer ${result.sessionToken}`, "Content-Type": "application/json" };
33335
+ const sessionHeaders = {
33336
+ Authorization: `Bearer ${result.sessionToken}`,
33337
+ "Content-Type": "application/json"
33338
+ };
33270
33339
  const keyName = `cli-${hostname().toLowerCase()}`;
33271
- const listRes = await fetch(`${apiUrl}/api/keys`, { headers: sessionHeaders });
33340
+ const listRes = await fetch(`${apiUrl}/api/keys`, {
33341
+ headers: sessionHeaders
33342
+ });
33272
33343
  if (listRes.ok) {
33273
33344
  const { keys } = await listRes.json();
33274
33345
  const existing = keys.find((k) => k.name === keyName && k.status === "active");
33275
33346
  if (existing) {
33276
- await fetch(`${apiUrl}/api/keys/${existing.id}`, { method: "DELETE", headers: sessionHeaders });
33347
+ await fetch(`${apiUrl}/api/keys/${existing.id}`, {
33348
+ method: "DELETE",
33349
+ headers: sessionHeaders
33350
+ });
33277
33351
  }
33278
33352
  }
33279
33353
  const createRes = await fetch(`${apiUrl}/api/keys`, {
@@ -33291,7 +33365,10 @@ async function hostedLogin(config) {
33291
33365
  config.apiKey = key;
33292
33366
  await saveConfig(config);
33293
33367
  try {
33294
- await fetch(`${apiUrl}/api/auth/logout`, { method: "POST", headers: sessionHeaders });
33368
+ await fetch(`${apiUrl}/api/auth/logout`, {
33369
+ method: "POST",
33370
+ headers: sessionHeaders
33371
+ });
33295
33372
  } catch {}
33296
33373
  const account = result.account;
33297
33374
  console.log();
@@ -33359,97 +33436,28 @@ function printNodeInfo(node) {
33359
33436
  console.log(` network: ${node.network}, source: ${source}
33360
33437
  `);
33361
33438
  }
33362
- // src/commands/new.ts
33363
- import { join as join3 } from "node:path";
33364
-
33365
- // src/templates/stream.ts
33366
- function generateStreamTemplate(name, endpointUrl) {
33367
- return {
33368
- name,
33369
- endpointUrl: endpointUrl || "https://example.com/endpoint",
33370
- filters: [
33371
- {
33372
- type: "contract_call",
33373
- contractId: "SP000000000000000000002Q6VF78.pox-4"
33374
- }
33375
- ],
33376
- options: {
33377
- decodeClarityValues: true,
33378
- includeRawTx: false,
33379
- includeBlockMetadata: true,
33380
- rateLimit: 10,
33381
- timeoutMs: 1e4,
33382
- maxRetries: 3
33383
- }
33384
- };
33385
- }
33386
-
33387
- // src/commands/new.ts
33388
- init_output();
33389
- init_config();
33390
- init_fs();
33391
- var STREAMS_DIR2 = "streams";
33392
- function registerNewCommand(program2) {
33393
- program2.command("new <name>").description("Generate a new stream configuration file").option("-o, --output <path>", "Output path (default: streams/<name>.json)").action(async (name, options) => {
33394
- try {
33395
- const config = await loadConfig();
33396
- const outputPath = options.output || join3(STREAMS_DIR2, `${name}.json`);
33397
- if (await fileExists(outputPath)) {
33398
- warn(`File already exists: ${outputPath}`);
33399
- process.exit(1);
33400
- }
33401
- const dir = outputPath.substring(0, outputPath.lastIndexOf("/"));
33402
- if (dir) {
33403
- await ensureDir(dir);
33404
- }
33405
- const template = generateStreamTemplate(name, config.defaultEndpointUrl);
33406
- await writeTextFile(outputPath, JSON.stringify(template, null, 2) + `
33407
- `);
33408
- success(`Created ${outputPath}`);
33409
- if (!config.defaultEndpointUrl) {
33410
- warn("Edit the endpointUrl before registering — it must be a reachable HTTPS endpoint");
33411
- }
33412
- console.log(`
33413
- Edit the file to configure your stream, then run:`);
33414
- console.log(` sl streams register ${outputPath}`);
33415
- } catch (err) {
33416
- error(`Failed to create stream: ${err}`);
33417
- process.exit(1);
33418
- }
33419
- });
33420
- }
33421
-
33422
- // src/commands/list.ts
33439
+ // src/commands/delete.ts
33423
33440
  init_api_client();
33424
33441
  init_output();
33425
- function registerListCommand(program2) {
33426
- program2.command("list").alias("ls").description("List all streams").option("-s, --status <status>", "Filter by status (inactive/active/paused/failed)").option("--json", "Output as JSON").action(async (options) => {
33442
+ import { confirm as confirm2 } from "@inquirer/prompts";
33443
+ function registerDeleteCommand(program2) {
33444
+ program2.command("delete <id>").alias("rm").description("Delete a stream").option("-f, --force", "Skip confirmation prompt").action(async (id, options) => {
33427
33445
  try {
33428
- const { streams, total } = await listStreams({
33429
- status: options.status
33430
- });
33431
- if (options.json) {
33432
- console.log(JSON.stringify(streams, null, 2));
33433
- return;
33434
- }
33435
- if (streams.length === 0) {
33436
- console.log("No streams found");
33437
- return;
33446
+ const stream = await getStream(id);
33447
+ if (!options.force) {
33448
+ const confirmed = await confirm2({
33449
+ message: `Delete stream "${stream.name}" (${stream.id})?`,
33450
+ default: false
33451
+ });
33452
+ if (!confirmed) {
33453
+ warn("Aborted");
33454
+ return;
33455
+ }
33438
33456
  }
33439
- const rows = streams.map((s) => {
33440
- const statusColor = s.status === "active" ? green : s.status === "failed" ? red : s.status === "paused" ? yellow : dim;
33441
- return [
33442
- s.id.slice(0, 8),
33443
- s.name,
33444
- statusColor(s.status),
33445
- s.totalDeliveries.toString()
33446
- ];
33447
- });
33448
- console.log(formatTable(["ID", "Name", "Status", "Deliveries"], rows));
33449
- console.log(dim(`
33450
- ${total} stream(s) total`));
33457
+ await deleteStream(id);
33458
+ success(`Deleted stream: ${stream.name}`);
33451
33459
  } catch (err) {
33452
- handleApiError(err, "list streams");
33460
+ handleApiError(err, "delete stream");
33453
33461
  }
33454
33462
  });
33455
33463
  }
@@ -33494,280 +33502,44 @@ ${dim("Options:")}`);
33494
33502
  });
33495
33503
  }
33496
33504
 
33497
- // src/commands/register.ts
33498
- init_api_client();
33499
- init_output();
33500
- init_fs();
33501
- import { CreateStreamSchema } from "@secondlayer/shared/schemas";
33502
- function registerRegisterCommand(program2) {
33503
- program2.command("register <file>").description("Register a stream from a JSON configuration file").option("-u, --update", "Update existing stream if name matches").action(async (filePath, options) => {
33504
- try {
33505
- if (!await fileExists(filePath)) {
33506
- error(`File not found: ${filePath}`);
33507
- process.exit(1);
33508
- }
33509
- const content = await readJsonFile(filePath);
33510
- const parsed = CreateStreamSchema.safeParse(content);
33511
- if (!parsed.success) {
33512
- error("Invalid stream configuration:");
33513
- for (const issue of parsed.error.issues) {
33514
- console.log(` - ${issue.path.join(".")}: ${issue.message}`);
33515
- }
33516
- process.exit(1);
33517
- }
33518
- const streamData = parsed.data;
33519
- if (options.update) {
33520
- try {
33521
- const updated = await updateStreamByName(streamData.name, streamData);
33522
- success(`Updated stream: ${updated.name}`);
33523
- console.log(formatKeyValue([
33524
- ["ID", updated.id],
33525
- ["Name", updated.name]
33526
- ]));
33527
- return;
33528
- } catch (err) {
33529
- if (err instanceof ApiError && err.status === 404) {
33530
- warn("Stream not found, creating new...");
33531
- } else {
33532
- throw err;
33533
- }
33534
- }
33535
- }
33536
- const result = await createStream(streamData);
33537
- success(`Registered stream: ${result.stream.name}`);
33538
- console.log(formatKeyValue([
33539
- ["ID", result.stream.id],
33540
- ["Name", result.stream.name],
33541
- ["Signing Secret", result.signingSecret]
33542
- ]));
33543
- console.log(dim(`
33544
- Save the signing secret - it won't be shown again!`));
33545
- } catch (err) {
33546
- handleApiError(err, "register stream");
33547
- }
33548
- });
33549
- }
33550
-
33551
- // src/commands/delete.ts
33552
- init_api_client();
33553
- init_output();
33554
- import { confirm as confirm2 } from "@inquirer/prompts";
33555
- function registerDeleteCommand(program2) {
33556
- program2.command("delete <id>").alias("rm").description("Delete a stream").option("-f, --force", "Skip confirmation prompt").action(async (id, options) => {
33557
- try {
33558
- const stream = await getStream(id);
33559
- if (!options.force) {
33560
- const confirmed = await confirm2({
33561
- message: `Delete stream "${stream.name}" (${stream.id})?`,
33562
- default: false
33563
- });
33564
- if (!confirmed) {
33565
- warn("Aborted");
33566
- return;
33567
- }
33568
- }
33569
- await deleteStream(id);
33570
- success(`Deleted stream: ${stream.name}`);
33571
- } catch (err) {
33572
- handleApiError(err, "delete stream");
33573
- }
33574
- });
33575
- }
33576
-
33577
- // src/commands/set.ts
33578
- init_config();
33505
+ // src/commands/list.ts
33579
33506
  init_api_client();
33580
33507
  init_output();
33581
- function registerSetCommand(program2) {
33582
- program2.command("set [id] [state]").description("Set stream state (active, disabled, paused)").option("--all", "Apply to all streams").option("--wait", "Wait for pending jobs to complete (with paused)").option("--retry", "Re-enable an errored stream").option("--replay-failed", "Also replay failed deliveries (with --retry)").option("-o, --option <kv...>", "Set stream option (key=value, e.g. maxRetries=5)").action(async (id, state, options) => {
33508
+ function registerListCommand(program2) {
33509
+ program2.command("list").alias("ls").description("List all streams").option("-s, --status <status>", "Filter by status (inactive/active/paused/failed)").option("--json", "Output as JSON").action(async (options) => {
33583
33510
  try {
33584
- if (options.retry) {
33585
- if (!id) {
33586
- error("Stream ID is required with --retry");
33587
- process.exit(1);
33588
- }
33589
- await retryStream(id, options.replayFailed);
33511
+ const { streams, total } = await listStreams({
33512
+ status: options.status
33513
+ });
33514
+ if (options.json) {
33515
+ console.log(JSON.stringify(streams, null, 2));
33590
33516
  return;
33591
33517
  }
33592
- if (options.all) {
33593
- if (!state) {
33594
- error("State is required: sl streams set --all <active|paused>");
33595
- process.exit(1);
33596
- }
33597
- await setAllStreams(state, options.wait);
33518
+ if (streams.length === 0) {
33519
+ console.log("No streams found");
33598
33520
  return;
33599
33521
  }
33600
- if (options.option?.length) {
33601
- if (!id) {
33602
- error("Stream ID is required with --option");
33603
- process.exit(1);
33604
- }
33605
- const parsedOptions = parseOptions2(options.option);
33606
- await updateStream(id, { options: parsedOptions });
33607
- success(`Updated stream options: ${Object.entries(parsedOptions).map(([k, v]) => `${k}=${v}`).join(", ")}`);
33608
- if (!state)
33609
- return;
33610
- }
33611
- if (!id || !state) {
33612
- error("Usage: sl streams set <id> <active|disabled>");
33613
- console.log(dim(`
33614
- Examples:`));
33615
- console.log(dim(" sl streams set <id> active"));
33616
- console.log(dim(" sl streams set <id> disabled"));
33617
- console.log(dim(" sl streams set --all paused --wait"));
33618
- console.log(dim(" sl streams set <id> --retry --replay-failed"));
33619
- console.log(dim(" sl streams set <id> --option maxRetries=5"));
33620
- process.exit(1);
33621
- }
33622
- await setSingleStream(id, state);
33522
+ const rows = streams.map((s) => {
33523
+ const statusColor = s.status === "active" ? green : s.status === "failed" ? red : s.status === "paused" ? yellow : dim;
33524
+ return [
33525
+ s.id.slice(0, 8),
33526
+ s.name,
33527
+ statusColor(s.status),
33528
+ s.totalDeliveries.toString()
33529
+ ];
33530
+ });
33531
+ console.log(formatTable(["ID", "Name", "Status", "Deliveries"], rows));
33532
+ console.log(dim(`
33533
+ ${total} stream(s) total`));
33623
33534
  } catch (err) {
33624
- handleApiError(err, "set stream state");
33625
- }
33626
- });
33627
- }
33628
- async function setSingleStream(id, state) {
33629
- switch (state) {
33630
- case "active": {
33631
- const stream = await enableStream(id);
33632
- success(`Enabled stream: ${stream.name} (status: active)`);
33633
- break;
33634
- }
33635
- case "disabled": {
33636
- const stream = await disableStream(id);
33637
- success(`Disabled stream: ${stream.name} (status: inactive)`);
33638
- break;
33639
- }
33640
- default:
33641
- error(`Unknown state: ${state}. Use active, disabled, or paused (with --all)`);
33642
- process.exit(1);
33643
- }
33644
- }
33645
- async function setAllStreams(state, wait) {
33646
- switch (state) {
33647
- case "paused": {
33648
- const result = await pauseAllStreams();
33649
- if (result.paused === 0) {
33650
- info("No active streams to pause");
33651
- return;
33652
- }
33653
- success(`Paused ${result.paused} stream${result.paused === 1 ? "" : "s"}`);
33654
- if (wait) {
33655
- await waitForQueueDrain();
33656
- }
33657
- break;
33658
- }
33659
- case "active": {
33660
- const result = await resumeAllStreams();
33661
- if (result.resumed === 0) {
33662
- info("No paused streams to resume");
33663
- return;
33664
- }
33665
- success(`Resumed ${result.resumed} stream${result.resumed === 1 ? "" : "s"}`);
33666
- break;
33535
+ handleApiError(err, "list streams");
33667
33536
  }
33668
- default:
33669
- error(`Unknown state for --all: ${state}. Use active or paused`);
33670
- process.exit(1);
33671
- }
33672
- }
33673
- async function retryStream(id, replayFailed) {
33674
- const config = await loadConfig();
33675
- const apiUrl = resolveApiUrl(config);
33676
- const getRes = await fetch(`${apiUrl}/api/streams/${id}`, {
33677
- headers: authHeaders(config)
33678
33537
  });
33679
- if (!getRes.ok) {
33680
- const body = await getRes.text();
33681
- throw new Error(parseError2(getRes.status, body));
33682
- }
33683
- const stream = await getRes.json();
33684
- if (stream.status !== "failed") {
33685
- warn(`Stream is not in failed status (current status: ${stream.status})`);
33686
- console.log(dim(`
33687
- Use 'sl streams set <id> active' to enable an inactive stream.`));
33688
- process.exit(1);
33689
- }
33690
- if (stream.errorMessage) {
33691
- console.log(red(`Previous error: ${stream.errorMessage}`));
33692
- console.log("");
33693
- }
33694
- const enableRes = await fetch(`${apiUrl}/streams/${id}/enable`, {
33695
- method: "POST",
33696
- headers: authHeaders(config)
33697
- });
33698
- if (!enableRes.ok) {
33699
- const body = await enableRes.text();
33700
- throw new Error(parseError2(enableRes.status, body));
33701
- }
33702
- success(`Re-enabled stream: ${stream.name}`);
33703
- if (replayFailed) {
33704
- info("Replaying failed deliveries...");
33705
- const replayRes = await fetch(`${apiUrl}/streams/${id}/replay-failed`, {
33706
- method: "POST",
33707
- headers: authHeaders(config)
33708
- });
33709
- if (!replayRes.ok) {
33710
- const body = await replayRes.text();
33711
- warn(`Failed to replay: ${parseError2(replayRes.status, body)}`);
33712
- } else {
33713
- const result = await replayRes.json();
33714
- success(`Enqueued ${result.jobCount} replay jobs`);
33715
- }
33716
- }
33717
- console.log(dim(`
33718
- Monitor with: sl streams logs ` + id + " -f"));
33719
- }
33720
- async function waitForQueueDrain() {
33721
- const POLL_INTERVAL_MS = 1000;
33722
- process.stdout.write(dim("Waiting for jobs to complete..."));
33723
- while (true) {
33724
- const stats = await getQueueStats();
33725
- const active = stats.pending + stats.processing;
33726
- if (active === 0) {
33727
- process.stdout.write(`
33728
- `);
33729
- success("All jobs completed");
33730
- return;
33731
- }
33732
- process.stdout.write(`\r${dim(`Waiting for jobs to complete... ${active} remaining`)}`);
33733
- await Bun.sleep(POLL_INTERVAL_MS);
33734
- }
33735
- }
33736
- function parseOptions2(kvPairs) {
33737
- const result = {};
33738
- for (const kv of kvPairs) {
33739
- const eqIndex = kv.indexOf("=");
33740
- if (eqIndex === -1) {
33741
- throw new Error(`Invalid option format: "${kv}". Use key=value.`);
33742
- }
33743
- const key = kv.slice(0, eqIndex);
33744
- const raw = kv.slice(eqIndex + 1);
33745
- if (raw === "true")
33746
- result[key] = true;
33747
- else if (raw === "false")
33748
- result[key] = false;
33749
- else if (raw !== "" && !isNaN(Number(raw)))
33750
- result[key] = Number(raw);
33751
- else
33752
- result[key] = raw;
33753
- }
33754
- return result;
33755
- }
33756
- function parseError2(status, body) {
33757
- let message = `HTTP ${status}`;
33758
- try {
33759
- const json = JSON.parse(body);
33760
- message = json.error || json.message || message;
33761
- } catch {
33762
- if (body)
33763
- message = body;
33764
- }
33765
- return message;
33766
33538
  }
33767
33539
 
33768
33540
  // src/commands/logs.ts
33769
- init_config();
33770
33541
  init_api_client();
33542
+ init_config();
33771
33543
  init_output();
33772
33544
  function registerLogsCommand(program2) {
33773
33545
  program2.command("logs <stream-id>").description("View delivery logs for a stream").option("-f, --follow", "Follow logs in real-time").option("-n, --limit <count>", "Number of logs to show", "20").option("-s, --status <status>", "Filter by status (success|failed)").action(async (streamId, options) => {
@@ -33777,7 +33549,7 @@ function registerLogsCommand(program2) {
33777
33549
  if (options.follow) {
33778
33550
  await followLogs(resolveApiUrl(config), fullId, config, options.status);
33779
33551
  } else {
33780
- await showRecentLogs(resolveApiUrl(config), fullId, parseInt(options.limit), config, options.status);
33552
+ await showRecentLogs(resolveApiUrl(config), fullId, Number.parseInt(options.limit), config, options.status);
33781
33553
  }
33782
33554
  } catch (err) {
33783
33555
  handleApiError(err, "get logs");
@@ -33888,9 +33660,123 @@ function printDelivery(d) {
33888
33660
  }
33889
33661
  }
33890
33662
 
33891
- // src/commands/replay.ts
33663
+ // src/commands/new.ts
33892
33664
  init_config();
33665
+ init_fs();
33666
+ init_output();
33667
+ import { join as join3 } from "node:path";
33668
+
33669
+ // src/templates/stream.ts
33670
+ function generateStreamTemplate(name, endpointUrl) {
33671
+ return {
33672
+ name,
33673
+ endpointUrl: endpointUrl || "https://example.com/endpoint",
33674
+ filters: [
33675
+ {
33676
+ type: "contract_call",
33677
+ contractId: "SP000000000000000000002Q6VF78.pox-4"
33678
+ }
33679
+ ],
33680
+ options: {
33681
+ decodeClarityValues: true,
33682
+ includeRawTx: false,
33683
+ includeBlockMetadata: true,
33684
+ rateLimit: 10,
33685
+ timeoutMs: 1e4,
33686
+ maxRetries: 3
33687
+ }
33688
+ };
33689
+ }
33690
+
33691
+ // src/commands/new.ts
33692
+ var STREAMS_DIR2 = "streams";
33693
+ function registerNewCommand(program2) {
33694
+ program2.command("new <name>").description("Generate a new stream configuration file").option("-o, --output <path>", "Output path (default: streams/<name>.json)").action(async (name, options) => {
33695
+ try {
33696
+ const config = await loadConfig();
33697
+ const outputPath = options.output || join3(STREAMS_DIR2, `${name}.json`);
33698
+ if (await fileExists(outputPath)) {
33699
+ warn(`File already exists: ${outputPath}`);
33700
+ process.exit(1);
33701
+ }
33702
+ const dir = outputPath.substring(0, outputPath.lastIndexOf("/"));
33703
+ if (dir) {
33704
+ await ensureDir(dir);
33705
+ }
33706
+ const template = generateStreamTemplate(name, config.defaultEndpointUrl);
33707
+ await writeTextFile(outputPath, JSON.stringify(template, null, 2) + `
33708
+ `);
33709
+ success(`Created ${outputPath}`);
33710
+ if (!config.defaultEndpointUrl) {
33711
+ warn("Edit the endpointUrl before registering — it must be a reachable HTTPS endpoint");
33712
+ }
33713
+ console.log(`
33714
+ Edit the file to configure your stream, then run:`);
33715
+ console.log(` sl streams register ${outputPath}`);
33716
+ } catch (err) {
33717
+ error(`Failed to create stream: ${err}`);
33718
+ process.exit(1);
33719
+ }
33720
+ });
33721
+ }
33722
+
33723
+ // src/commands/register.ts
33893
33724
  init_api_client();
33725
+ init_fs();
33726
+ init_output();
33727
+ import { CreateStreamSchema } from "@secondlayer/shared/schemas";
33728
+ function registerRegisterCommand(program2) {
33729
+ program2.command("register <file>").description("Register a stream from a JSON configuration file").option("-u, --update", "Update existing stream if name matches").action(async (filePath, options) => {
33730
+ try {
33731
+ if (!await fileExists(filePath)) {
33732
+ error(`File not found: ${filePath}`);
33733
+ process.exit(1);
33734
+ }
33735
+ const content = await readJsonFile(filePath);
33736
+ const parsed = CreateStreamSchema.safeParse(content);
33737
+ if (!parsed.success) {
33738
+ error("Invalid stream configuration:");
33739
+ for (const issue of parsed.error.issues) {
33740
+ console.log(` - ${issue.path.join(".")}: ${issue.message}`);
33741
+ }
33742
+ process.exit(1);
33743
+ }
33744
+ const streamData = parsed.data;
33745
+ if (options.update) {
33746
+ try {
33747
+ const updated = await updateStreamByName(streamData.name, streamData);
33748
+ success(`Updated stream: ${updated.name}`);
33749
+ console.log(formatKeyValue([
33750
+ ["ID", updated.id],
33751
+ ["Name", updated.name]
33752
+ ]));
33753
+ return;
33754
+ } catch (err) {
33755
+ if (err instanceof ApiError && err.status === 404) {
33756
+ warn("Stream not found, creating new...");
33757
+ } else {
33758
+ throw err;
33759
+ }
33760
+ }
33761
+ }
33762
+ const result = await createStream(streamData);
33763
+ success(`Registered stream: ${result.stream.name}`);
33764
+ console.log(formatKeyValue([
33765
+ ["ID", result.stream.id],
33766
+ ["Name", result.stream.name],
33767
+ ["Signing Secret", result.signingSecret]
33768
+ ]));
33769
+ console.log(dim(`
33770
+ Save the signing secret - it won't be shown again!`));
33771
+ } catch (err) {
33772
+ handleApiError(err, "register stream");
33773
+ }
33774
+ });
33775
+ }
33776
+
33777
+ // src/commands/replay.ts
33778
+ init_api_client();
33779
+ init_config();
33894
33780
  init_output();
33895
33781
  function registerReplayCommand(program2) {
33896
33782
  program2.command("replay <stream-id>").description("Replay blocks through a stream (re-evaluate and re-deliver)").option("--from <block>", "Start block height").option("--to <block>", "End block height").option("--last <count>", "Replay last N blocks").option("--block <height>", "Trigger evaluation for a single block").option("--fixture <path>", "Load block from fixture file (only with --block)").action(async (rawStreamId, options) => {
@@ -33908,7 +33794,7 @@ function registerReplayCommand(program2) {
33908
33794
  let fromBlock;
33909
33795
  let toBlock;
33910
33796
  if (options.last) {
33911
- const lastCount = parseInt(options.last);
33797
+ const lastCount = Number.parseInt(options.last);
33912
33798
  if (isNaN(lastCount) || lastCount <= 0) {
33913
33799
  error("--last must be a positive number");
33914
33800
  process.exit(1);
@@ -33929,8 +33815,8 @@ function registerReplayCommand(program2) {
33929
33815
  fromBlock = Math.max(0, toBlock - lastCount + 1);
33930
33816
  info(`Replaying last ${lastCount} blocks (${fromBlock} to ${toBlock})`);
33931
33817
  } else if (options.from && options.to) {
33932
- fromBlock = parseInt(options.from);
33933
- toBlock = parseInt(options.to);
33818
+ fromBlock = Number.parseInt(options.from);
33819
+ toBlock = Number.parseInt(options.to);
33934
33820
  if (isNaN(fromBlock) || fromBlock < 0) {
33935
33821
  error("--from must be a non-negative number");
33936
33822
  process.exit(1);
@@ -33960,7 +33846,7 @@ Examples:`));
33960
33846
  });
33961
33847
  if (!response.ok) {
33962
33848
  const body = await response.text();
33963
- throw new Error(parseError3(response.status, body));
33849
+ throw new Error(parseError2(response.status, body));
33964
33850
  }
33965
33851
  const result = await response.json();
33966
33852
  success("Replay started");
@@ -33979,7 +33865,7 @@ Jobs will be processed by the worker in block order.`));
33979
33865
  });
33980
33866
  }
33981
33867
  async function triggerBlock(config, streamId, blockStr, fixturePath) {
33982
- const blockHeight = parseInt(blockStr);
33868
+ const blockHeight = Number.parseInt(blockStr);
33983
33869
  if (isNaN(blockHeight) || blockHeight < 0) {
33984
33870
  error("Block height must be a non-negative number");
33985
33871
  process.exit(1);
@@ -34010,7 +33896,7 @@ async function triggerBlock(config, streamId, blockStr, fixturePath) {
34010
33896
  });
34011
33897
  if (!response.ok) {
34012
33898
  const body = await response.text();
34013
- throw new Error(parseError3(response.status, body));
33899
+ throw new Error(parseError2(response.status, body));
34014
33900
  }
34015
33901
  const result = await response.json();
34016
33902
  success(`Triggered evaluation for block ${blockHeight}`);
@@ -34020,7 +33906,7 @@ async function triggerBlock(config, streamId, blockStr, fixturePath) {
34020
33906
  ["Block", result.blockHeight.toString()]
34021
33907
  ]));
34022
33908
  }
34023
- function parseError3(status, body) {
33909
+ function parseError2(status, body) {
34024
33910
  let message = `HTTP ${status}`;
34025
33911
  try {
34026
33912
  const json = JSON.parse(body);
@@ -34064,6 +33950,197 @@ Save the signing secret - it won't be shown again!`));
34064
33950
  });
34065
33951
  }
34066
33952
 
33953
+ // src/commands/set.ts
33954
+ init_api_client();
33955
+ init_config();
33956
+ init_output();
33957
+ function registerSetCommand(program2) {
33958
+ program2.command("set [id] [state]").description("Set stream state (active, disabled, paused)").option("--all", "Apply to all streams").option("--wait", "Wait for pending jobs to complete (with paused)").option("--retry", "Re-enable an errored stream").option("--replay-failed", "Also replay failed deliveries (with --retry)").option("-o, --option <kv...>", "Set stream option (key=value, e.g. maxRetries=5)").action(async (id, state, options) => {
33959
+ try {
33960
+ if (options.retry) {
33961
+ if (!id) {
33962
+ error("Stream ID is required with --retry");
33963
+ process.exit(1);
33964
+ }
33965
+ await retryStream(id, options.replayFailed);
33966
+ return;
33967
+ }
33968
+ if (options.all) {
33969
+ if (!state) {
33970
+ error("State is required: sl streams set --all <active|paused>");
33971
+ process.exit(1);
33972
+ }
33973
+ await setAllStreams(state, options.wait);
33974
+ return;
33975
+ }
33976
+ if (options.option?.length) {
33977
+ if (!id) {
33978
+ error("Stream ID is required with --option");
33979
+ process.exit(1);
33980
+ }
33981
+ const parsedOptions = parseOptions2(options.option);
33982
+ await updateStream(id, { options: parsedOptions });
33983
+ success(`Updated stream options: ${Object.entries(parsedOptions).map(([k, v]) => `${k}=${v}`).join(", ")}`);
33984
+ if (!state)
33985
+ return;
33986
+ }
33987
+ if (!id || !state) {
33988
+ error("Usage: sl streams set <id> <active|disabled>");
33989
+ console.log(dim(`
33990
+ Examples:`));
33991
+ console.log(dim(" sl streams set <id> active"));
33992
+ console.log(dim(" sl streams set <id> disabled"));
33993
+ console.log(dim(" sl streams set --all paused --wait"));
33994
+ console.log(dim(" sl streams set <id> --retry --replay-failed"));
33995
+ console.log(dim(" sl streams set <id> --option maxRetries=5"));
33996
+ process.exit(1);
33997
+ }
33998
+ await setSingleStream(id, state);
33999
+ } catch (err) {
34000
+ handleApiError(err, "set stream state");
34001
+ }
34002
+ });
34003
+ }
34004
+ async function setSingleStream(id, state) {
34005
+ switch (state) {
34006
+ case "active": {
34007
+ const stream = await enableStream(id);
34008
+ success(`Enabled stream: ${stream.name} (status: active)`);
34009
+ break;
34010
+ }
34011
+ case "disabled": {
34012
+ const stream = await disableStream(id);
34013
+ success(`Disabled stream: ${stream.name} (status: inactive)`);
34014
+ break;
34015
+ }
34016
+ default:
34017
+ error(`Unknown state: ${state}. Use active, disabled, or paused (with --all)`);
34018
+ process.exit(1);
34019
+ }
34020
+ }
34021
+ async function setAllStreams(state, wait) {
34022
+ switch (state) {
34023
+ case "paused": {
34024
+ const result = await pauseAllStreams();
34025
+ if (result.paused === 0) {
34026
+ info("No active streams to pause");
34027
+ return;
34028
+ }
34029
+ success(`Paused ${result.paused} stream${result.paused === 1 ? "" : "s"}`);
34030
+ if (wait) {
34031
+ await waitForQueueDrain();
34032
+ }
34033
+ break;
34034
+ }
34035
+ case "active": {
34036
+ const result = await resumeAllStreams();
34037
+ if (result.resumed === 0) {
34038
+ info("No paused streams to resume");
34039
+ return;
34040
+ }
34041
+ success(`Resumed ${result.resumed} stream${result.resumed === 1 ? "" : "s"}`);
34042
+ break;
34043
+ }
34044
+ default:
34045
+ error(`Unknown state for --all: ${state}. Use active or paused`);
34046
+ process.exit(1);
34047
+ }
34048
+ }
34049
+ async function retryStream(id, replayFailed) {
34050
+ const config = await loadConfig();
34051
+ const apiUrl = resolveApiUrl(config);
34052
+ const getRes = await fetch(`${apiUrl}/api/streams/${id}`, {
34053
+ headers: authHeaders(config)
34054
+ });
34055
+ if (!getRes.ok) {
34056
+ const body = await getRes.text();
34057
+ throw new Error(parseError3(getRes.status, body));
34058
+ }
34059
+ const stream = await getRes.json();
34060
+ if (stream.status !== "failed") {
34061
+ warn(`Stream is not in failed status (current status: ${stream.status})`);
34062
+ console.log(dim(`
34063
+ Use 'sl streams set <id> active' to enable an inactive stream.`));
34064
+ process.exit(1);
34065
+ }
34066
+ if (stream.errorMessage) {
34067
+ console.log(red(`Previous error: ${stream.errorMessage}`));
34068
+ console.log("");
34069
+ }
34070
+ const enableRes = await fetch(`${apiUrl}/streams/${id}/enable`, {
34071
+ method: "POST",
34072
+ headers: authHeaders(config)
34073
+ });
34074
+ if (!enableRes.ok) {
34075
+ const body = await enableRes.text();
34076
+ throw new Error(parseError3(enableRes.status, body));
34077
+ }
34078
+ success(`Re-enabled stream: ${stream.name}`);
34079
+ if (replayFailed) {
34080
+ info("Replaying failed deliveries...");
34081
+ const replayRes = await fetch(`${apiUrl}/streams/${id}/replay-failed`, {
34082
+ method: "POST",
34083
+ headers: authHeaders(config)
34084
+ });
34085
+ if (!replayRes.ok) {
34086
+ const body = await replayRes.text();
34087
+ warn(`Failed to replay: ${parseError3(replayRes.status, body)}`);
34088
+ } else {
34089
+ const result = await replayRes.json();
34090
+ success(`Enqueued ${result.jobCount} replay jobs`);
34091
+ }
34092
+ }
34093
+ console.log(dim(`
34094
+ Monitor with: sl streams logs ` + id + " -f"));
34095
+ }
34096
+ async function waitForQueueDrain() {
34097
+ const POLL_INTERVAL_MS = 1000;
34098
+ process.stdout.write(dim("Waiting for jobs to complete..."));
34099
+ while (true) {
34100
+ const stats = await getQueueStats();
34101
+ const active = stats.pending + stats.processing;
34102
+ if (active === 0) {
34103
+ process.stdout.write(`
34104
+ `);
34105
+ success("All jobs completed");
34106
+ return;
34107
+ }
34108
+ process.stdout.write(`\r${dim(`Waiting for jobs to complete... ${active} remaining`)}`);
34109
+ await Bun.sleep(POLL_INTERVAL_MS);
34110
+ }
34111
+ }
34112
+ function parseOptions2(kvPairs) {
34113
+ const result = {};
34114
+ for (const kv of kvPairs) {
34115
+ const eqIndex = kv.indexOf("=");
34116
+ if (eqIndex === -1) {
34117
+ throw new Error(`Invalid option format: "${kv}". Use key=value.`);
34118
+ }
34119
+ const key = kv.slice(0, eqIndex);
34120
+ const raw = kv.slice(eqIndex + 1);
34121
+ if (raw === "true")
34122
+ result[key] = true;
34123
+ else if (raw === "false")
34124
+ result[key] = false;
34125
+ else if (raw !== "" && !isNaN(Number(raw)))
34126
+ result[key] = Number(raw);
34127
+ else
34128
+ result[key] = raw;
34129
+ }
34130
+ return result;
34131
+ }
34132
+ function parseError3(status, body) {
34133
+ let message = `HTTP ${status}`;
34134
+ try {
34135
+ const json = JSON.parse(body);
34136
+ message = json.error || json.message || message;
34137
+ } catch {
34138
+ if (body)
34139
+ message = body;
34140
+ }
34141
+ return message;
34142
+ }
34143
+
34067
34144
  // src/commands/streams.ts
34068
34145
  function registerStreamsCommand(program2) {
34069
34146
  const streams = program2.command("streams").description("Manage event streams");
@@ -34078,8 +34155,8 @@ function registerStreamsCommand(program2) {
34078
34155
  registerRotateSecretCommand(streams);
34079
34156
  }
34080
34157
  // src/commands/status.ts
34081
- init_config();
34082
34158
  init_api_client();
34159
+ init_config();
34083
34160
  init_output();
34084
34161
  function registerStatusCommand(program2) {
34085
34162
  program2.command("status").description("Show system status").option("--json", "Output as JSON").action(async (options) => {
@@ -34176,7 +34253,10 @@ function printStatus(status) {
34176
34253
  [" Total", status.streams.total.toString()],
34177
34254
  [" Active", green(status.streams.active.toString())],
34178
34255
  [" Paused", yellow(status.streams.paused.toString())],
34179
- [" Error", status.streams.error > 0 ? red(status.streams.error.toString()) : "0"]
34256
+ [
34257
+ " Error",
34258
+ status.streams.error > 0 ? red(status.streams.error.toString()) : "0"
34259
+ ]
34180
34260
  ]));
34181
34261
  console.log("");
34182
34262
  if (status.activeSubgraphs !== undefined || status.recentDeliveries !== undefined) {
@@ -34194,19 +34274,22 @@ function printStatus(status) {
34194
34274
  // src/commands/sync.ts
34195
34275
  init_config();
34196
34276
  init_output();
34277
+ import { confirm as confirm4 } from "@inquirer/prompts";
34278
+ import { getDb } from "@secondlayer/shared/db";
34279
+ import {
34280
+ countMissingBlocks,
34281
+ findGaps
34282
+ } from "@secondlayer/shared/db/queries/integrity";
34197
34283
  import { StacksNodeClient } from "@secondlayer/shared/node";
34198
34284
  import { HiroClient } from "@secondlayer/shared/node/hiro-client";
34199
34285
  import { LocalClient } from "@secondlayer/shared/node/local-client";
34200
- import { findGaps, countMissingBlocks } from "@secondlayer/shared/db/queries/integrity";
34201
- import { getDb } from "@secondlayer/shared/db";
34202
- import { confirm as confirm4 } from "@inquirer/prompts";
34203
34286
  var DEV_DATABASE_URL = "postgres://postgres:postgres@localhost:5432/streams_dev";
34204
34287
  function registerSyncCommand(program2) {
34205
34288
  program2.command("sync").description("Fetch missing blocks and index them").option("--from <block>", "Start block height").option("--to <block>", "End block height").option("--gaps", "Auto-detect and fill all gaps").option("--concurrency <n>", "Parallel fetch limit (default: 1 for hiro, 5 for node)").option("--delay <ms>", "Delay between batches in ms (default: 500 for hiro, 0 for node)").option("--source <source>", "Data source: auto, local, hiro, node", "auto").option("-y, --yes", "Skip confirmation prompt").action(async function() {
34206
34289
  const opts = this.opts();
34207
34290
  const config = await loadConfig();
34208
34291
  const indexerUrl = process.env.INDEXER_URL || `http://localhost:${config.ports.indexer}`;
34209
- let concurrency = opts.concurrency ? parseInt(opts.concurrency) : 0;
34292
+ let concurrency = opts.concurrency ? Number.parseInt(opts.concurrency) : 0;
34210
34293
  try {
34211
34294
  const nodeClient = new StacksNodeClient;
34212
34295
  const hiroClient = new HiroClient;
@@ -34274,10 +34357,13 @@ Set HIRO_API_URL or check your internet connection.`));
34274
34357
  process.exit(0);
34275
34358
  }
34276
34359
  info(`Found ${gaps.length} gaps, ${missing} missing blocks`);
34277
- ranges = gaps.map((g) => ({ start: g.gapStart, end: g.gapEnd }));
34360
+ ranges = gaps.map((g) => ({
34361
+ start: g.gapStart,
34362
+ end: g.gapEnd
34363
+ }));
34278
34364
  } else if (opts.from && opts.to) {
34279
- const from = parseInt(opts.from);
34280
- const to = parseInt(opts.to);
34365
+ const from = Number.parseInt(opts.from);
34366
+ const to = Number.parseInt(opts.to);
34281
34367
  if (isNaN(from) || from < 0) {
34282
34368
  error("--from must be a non-negative number");
34283
34369
  process.exit(1);
@@ -34317,7 +34403,7 @@ Examples:`));
34317
34403
  process.exit(0);
34318
34404
  }
34319
34405
  }
34320
- const batchDelay = opts.delay ? parseInt(opts.delay) : useHiro ? 500 : 0;
34406
+ const batchDelay = opts.delay ? Number.parseInt(opts.delay) : useHiro ? 500 : 0;
34321
34407
  if (useHiro && batchDelay > 0) {
34322
34408
  info(`Pacing: ${batchDelay}ms delay between batches (concurrency: ${concurrency})`);
34323
34409
  }
@@ -34402,13 +34488,16 @@ Examples:`));
34402
34488
  });
34403
34489
  }
34404
34490
  // src/commands/db.ts
34405
- init_output();
34406
- init_dev_state();
34407
34491
  init_config();
34492
+ init_dev_state();
34493
+ init_output();
34494
+ import { confirm as confirm5 } from "@inquirer/prompts";
34408
34495
  import { getDb as getDb2, sql } from "@secondlayer/shared/db";
34409
- import { findGaps as findGaps2, countMissingBlocks as countMissingBlocks2 } from "@secondlayer/shared/db/queries/integrity";
34496
+ import {
34497
+ countMissingBlocks as countMissingBlocks2,
34498
+ findGaps as findGaps2
34499
+ } from "@secondlayer/shared/db/queries/integrity";
34410
34500
  import { StacksNodeClient as StacksNodeClient2 } from "@secondlayer/shared/node";
34411
- import { confirm as confirm5 } from "@inquirer/prompts";
34412
34501
  var DEV_DATABASE_URL2 = "postgres://postgres:postgres@localhost:5432/streams_dev";
34413
34502
  function registerDbCommand(program2) {
34414
34503
  const dbCmd = program2.command("db").description("Inspect indexer database tables").hook("preAction", async () => {
@@ -34418,19 +34507,19 @@ function registerDbCommand(program2) {
34418
34507
  });
34419
34508
  dbCmd.command("blocks").description("Show recent blocks").option("--limit <n>", "Number of rows", "10").option("--json", "Output as JSON").action(async function() {
34420
34509
  const opts = this.opts();
34421
- await showBlocks(parseInt(opts.limit), opts.json);
34510
+ await showBlocks(Number.parseInt(opts.limit), opts.json);
34422
34511
  });
34423
34512
  dbCmd.command("txs").description("Show recent transactions").option("--limit <n>", "Number of rows", "10").option("--json", "Output as JSON").action(async function() {
34424
34513
  const opts = this.opts();
34425
- await showTransactions(parseInt(opts.limit), opts.json);
34514
+ await showTransactions(Number.parseInt(opts.limit), opts.json);
34426
34515
  });
34427
34516
  dbCmd.command("events").description("Show recent events").option("--limit <n>", "Number of rows", "10").option("--json", "Output as JSON").action(async function() {
34428
34517
  const opts = this.opts();
34429
- await showEvents(parseInt(opts.limit), opts.json);
34518
+ await showEvents(Number.parseInt(opts.limit), opts.json);
34430
34519
  });
34431
34520
  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() {
34432
34521
  const opts = this.opts();
34433
- await showGaps(parseInt(opts.limit), opts.json);
34522
+ await showGaps(Number.parseInt(opts.limit), opts.json);
34434
34523
  });
34435
34524
  dbCmd.command("reset").description("Truncate all indexed data (blocks, txs, events, jobs, deliveries)").option("-y, --yes", "Skip confirmation prompt").action(async function() {
34436
34525
  const opts = this.opts();
@@ -34510,7 +34599,15 @@ async function showBlocks(limit, json) {
34510
34599
  async function showTransactions(limit, json) {
34511
34600
  try {
34512
34601
  const db = ensureDb();
34513
- const rows = await db.selectFrom("transactions").select(["tx_id", "block_height", "type", "sender", "status", "contract_id", "function_name"]).orderBy("block_height", "desc").limit(limit).execute();
34602
+ const rows = await db.selectFrom("transactions").select([
34603
+ "tx_id",
34604
+ "block_height",
34605
+ "type",
34606
+ "sender",
34607
+ "status",
34608
+ "contract_id",
34609
+ "function_name"
34610
+ ]).orderBy("block_height", "desc").limit(limit).execute();
34514
34611
  if (json) {
34515
34612
  console.log(JSON.stringify(rows, null, 2));
34516
34613
  process.exit(0);
@@ -34710,7 +34807,10 @@ async function resyncDatabase(skipConfirm, backfill) {
34710
34807
  throw new Error(`Block ${height} not found`);
34711
34808
  const res = await fetch(`${indexerUrl}/new_block`, {
34712
34809
  method: "POST",
34713
- headers: { "Content-Type": "application/json", "X-Source": "backfill" },
34810
+ headers: {
34811
+ "Content-Type": "application/json",
34812
+ "X-Source": "backfill"
34813
+ },
34714
34814
  body: JSON.stringify(block)
34715
34815
  });
34716
34816
  if (!res.ok)
@@ -34741,8 +34841,8 @@ async function resyncDatabase(skipConfirm, backfill) {
34741
34841
  }
34742
34842
  }
34743
34843
  // src/commands/receiver.ts
34744
- init_output();
34745
34844
  init_config();
34845
+ init_output();
34746
34846
  import { join as join5 } from "node:path";
34747
34847
  function registerReceiverCommand(program2) {
34748
34848
  const receiver = program2.command("receiver").description("Receiver development tools").hook("preAction", async () => {
@@ -34751,7 +34851,7 @@ function registerReceiverCommand(program2) {
34751
34851
  receiver.command("init <directory>").description("Scaffold a receiver handler with types and signature verification").option("-n, --name <name>", "Stream name", "my-stream").option("--network <network>", "Network (mainnet/testnet)", "mainnet").option("-p, --port <port>", "Server port", "4000").action(async (directory, options) => {
34752
34852
  try {
34753
34853
  const config = await loadConfig();
34754
- const port = parseInt(options.port);
34854
+ const port = Number.parseInt(options.port);
34755
34855
  const network = options.network;
34756
34856
  const dir = join5(process.cwd(), directory);
34757
34857
  const dirExists = await Bun.file(join5(dir, "package.json")).exists();
@@ -35068,65 +35168,8 @@ async function generatePackageJson(dir, name) {
35068
35168
  `);
35069
35169
  }
35070
35170
  // src/commands/subgraphs.ts
35071
- init_output();
35072
- import { resolve } from "node:path";
35073
35171
  import { existsSync, mkdirSync, watch } from "node:fs";
35074
-
35075
- // src/templates/subgraph.ts
35076
- function generateSubgraphTemplate(name) {
35077
- return `import { defineSubgraph } from "@secondlayer/subgraphs";
35078
-
35079
- export default defineSubgraph({
35080
- name: "${name}",
35081
- version: "1.0.0",
35082
- description: "TODO: describe what this subgraph tracks",
35083
-
35084
- // Sources define what blockchain data this subgraph processes.
35085
- // Each source filters transactions/events by contract, type, function, or event.
35086
- // Examples:
35087
- // { contract: "SP000...::my-contract" } — all txs to a contract
35088
- // { contract: "SP000...::my-contract", event: "transfer" } — specific event
35089
- // { type: "stx_transfer", minAmount: 1000000n } — STX transfers >= 1 STX
35090
- // { contract: "*.pox-*" } — wildcard contract match
35091
- sources: [
35092
- { contract: "SP000000000000000000002Q6VF78.pox-4" },
35093
- ],
35094
-
35095
- // Schema defines the tables this subgraph creates.
35096
- // Each table gets auto-columns: _id, _block_height, _tx_id, _created_at.
35097
- // Column types: text, uint, int, principal, boolean, timestamp, jsonb
35098
- schema: {
35099
- data: {
35100
- columns: {
35101
- sender: { type: "principal", indexed: true },
35102
- amount: { type: "uint" },
35103
- memo: { type: "text", nullable: true },
35104
- },
35105
- // Optional composite indexes
35106
- // indexes: [["sender", "amount"]],
35107
- },
35108
- },
35109
-
35110
- // Handlers process matched events and write to your tables via the context.
35111
- // Keys match source patterns (use sourceKey format), or "*" as catch-all.
35112
- // Context methods: ctx.insert(), ctx.update(), ctx.delete()
35113
- handlers: {
35114
- "*": async (event, ctx) => {
35115
- await ctx.insert("data", {
35116
- sender: event.sender ?? event.tx?.sender,
35117
- amount: event.amount ?? 0,
35118
- memo: event.memo ?? null,
35119
- });
35120
- },
35121
- },
35122
- });
35123
- `;
35124
- }
35125
-
35126
- // src/commands/subgraphs.ts
35127
- init_api_client();
35128
- init_config();
35129
- init_fs();
35172
+ import { resolve } from "node:path";
35130
35173
 
35131
35174
  // src/generators/subgraph-scaffold.ts
35132
35175
  init_format();
@@ -35134,9 +35177,9 @@ init_format();
35134
35177
  // src/generators/clarity-to-subgraph.ts
35135
35178
  import {
35136
35179
  isAbiBuffer,
35180
+ isAbiOptional,
35137
35181
  isAbiStringAscii,
35138
- isAbiStringUtf8,
35139
- isAbiOptional
35182
+ isAbiStringUtf8
35140
35183
  } from "@secondlayer/stacks/clarity";
35141
35184
  function clarityTypeToSubgraphColumn(abiType) {
35142
35185
  return mapType(abiType, false);
@@ -35231,9 +35274,6 @@ ${handlersBlock}
35231
35274
  return formatCode(code);
35232
35275
  }
35233
35276
 
35234
- // src/generators/subgraphs.ts
35235
- init_format();
35236
-
35237
35277
  // src/utils/case-conversion.ts
35238
35278
  import { toCamelCase } from "@secondlayer/stacks/clarity";
35239
35279
  function capitalize(str) {
@@ -35244,6 +35284,7 @@ function toPascalCase(str) {
35244
35284
  }
35245
35285
 
35246
35286
  // src/generators/subgraphs.ts
35287
+ init_format();
35247
35288
  async function generateSubgraphConsumer(subgraphName, detail) {
35248
35289
  const tables = Object.entries(detail.tables);
35249
35290
  const rowInterfaces = tables.map(([tableName, tableDef]) => {
@@ -35317,8 +35358,65 @@ function subgraphTypeToTS(type) {
35317
35358
  }
35318
35359
 
35319
35360
  // src/commands/subgraphs.ts
35320
- init_api();
35361
+ init_api_client();
35362
+ init_config();
35363
+ init_fs();
35364
+ init_output();
35321
35365
  init_clarity();
35366
+
35367
+ // src/templates/subgraph.ts
35368
+ function generateSubgraphTemplate(name) {
35369
+ return `import { defineSubgraph } from "@secondlayer/subgraphs";
35370
+
35371
+ export default defineSubgraph({
35372
+ name: "${name}",
35373
+ version: "1.0.0",
35374
+ description: "TODO: describe what this subgraph tracks",
35375
+
35376
+ // Sources define what blockchain data this subgraph processes.
35377
+ // Each source filters transactions/events by contract, type, function, or event.
35378
+ // Examples:
35379
+ // { contract: "SP000...::my-contract" } — all txs to a contract
35380
+ // { contract: "SP000...::my-contract", event: "transfer" } — specific event
35381
+ // { type: "stx_transfer", minAmount: 1000000n } — STX transfers >= 1 STX
35382
+ // { contract: "*.pox-*" } — wildcard contract match
35383
+ sources: [
35384
+ { contract: "SP000000000000000000002Q6VF78.pox-4" },
35385
+ ],
35386
+
35387
+ // Schema defines the tables this subgraph creates.
35388
+ // Each table gets auto-columns: _id, _block_height, _tx_id, _created_at.
35389
+ // Column types: text, uint, int, principal, boolean, timestamp, jsonb
35390
+ schema: {
35391
+ data: {
35392
+ columns: {
35393
+ sender: { type: "principal", indexed: true },
35394
+ amount: { type: "uint" },
35395
+ memo: { type: "text", nullable: true },
35396
+ },
35397
+ // Optional composite indexes
35398
+ // indexes: [["sender", "amount"]],
35399
+ },
35400
+ },
35401
+
35402
+ // Handlers process matched events and write to your tables via the context.
35403
+ // Keys match source patterns (use sourceKey format), or "*" as catch-all.
35404
+ // Context methods: ctx.insert(), ctx.update(), ctx.delete()
35405
+ handlers: {
35406
+ "*": async (event, ctx) => {
35407
+ await ctx.insert("data", {
35408
+ sender: event.sender ?? event.tx?.sender,
35409
+ amount: event.amount ?? 0,
35410
+ memo: event.memo ?? null,
35411
+ });
35412
+ },
35413
+ },
35414
+ });
35415
+ `;
35416
+ }
35417
+
35418
+ // src/commands/subgraphs.ts
35419
+ init_api();
35322
35420
  function registerSubgraphsCommand(program2) {
35323
35421
  const subgraphs = program2.command("subgraphs").description("Manage materialized subgraphs");
35324
35422
  subgraphs.command("new <name>").description("Scaffold a new subgraph definition file").action(async (name) => {
@@ -35356,7 +35454,9 @@ function registerSubgraphsCommand(program2) {
35356
35454
  const { getDb: getDb3 } = await import("@secondlayer/shared/db");
35357
35455
  validateSubgraphDefinition(def);
35358
35456
  const db = getDb3();
35359
- const result = await deploySchema(db, def, absPath, { forceReindex: false });
35457
+ const result = await deploySchema(db, def, absPath, {
35458
+ forceReindex: false
35459
+ });
35360
35460
  if (result.action === "unchanged") {
35361
35461
  info(`[${new Date().toLocaleTimeString()}] No schema changes`);
35362
35462
  } else if (result.action === "created") {
@@ -35435,7 +35535,9 @@ Stopped watching.`);
35435
35535
  const { deploySchema } = await import("@secondlayer/subgraphs");
35436
35536
  const { getDb: getDb3, closeDb } = await import("@secondlayer/shared/db");
35437
35537
  const db = getDb3();
35438
- const result = await deploySchema(db, def, absPath, { forceReindex: options2.reindex });
35538
+ const result = await deploySchema(db, def, absPath, {
35539
+ forceReindex: options2.reindex
35540
+ });
35439
35541
  if (result.action === "unchanged") {
35440
35542
  info(`Subgraph "${def.name}" is up to date (no schema changes)`);
35441
35543
  } else if (result.action === "created") {
@@ -35486,10 +35588,19 @@ ${data.length} subgraph(s) total`));
35486
35588
  const subgraph = await getSubgraphApi(name);
35487
35589
  const rowCounts = Object.entries(subgraph.tables).map(([t, info2]) => `${t}: ${info2.rowCount}`).join(", ") || "N/A";
35488
35590
  const errorRate = subgraph.health.totalProcessed > 0 ? `${(subgraph.health.errorRate * 100).toFixed(2)}%` : "N/A";
35591
+ const sync = subgraph.sync;
35592
+ const syncStatus = sync ? `${sync.status} — ${(sync.progress * 100).toFixed(1)}% (${sync.lastProcessedBlock} / ${sync.chainTip})` : "unknown";
35593
+ const blocksRemaining = sync ? String(sync.blocksRemaining) : "N/A";
35594
+ const gapSummary = sync && sync.gaps.count > 0 ? `${sync.gaps.count} unresolved (${sync.gaps.totalMissingBlocks} missing blocks)` : "none";
35595
+ const integrity = sync?.integrity ?? "unknown";
35489
35596
  console.log(formatKeyValue([
35490
35597
  ["Name", subgraph.name],
35491
35598
  ["Version", subgraph.version],
35492
35599
  ["Status", subgraph.status],
35600
+ ["Sync", syncStatus],
35601
+ ["Blocks Remaining", blocksRemaining],
35602
+ ["Integrity", integrity],
35603
+ ["Gaps", gapSummary],
35493
35604
  ["Last Block", String(subgraph.lastProcessedBlock)],
35494
35605
  ["Row Count", rowCounts],
35495
35606
  ["Total Processed", String(subgraph.health.totalProcessed)],
@@ -35500,6 +35611,20 @@ ${data.length} subgraph(s) total`));
35500
35611
  ["Created", subgraph.createdAt],
35501
35612
  ["Updated", subgraph.updatedAt]
35502
35613
  ]));
35614
+ if (sync && sync.gaps.count > 0 && sync.gaps.ranges.length > 0) {
35615
+ console.log(dim(`
35616
+ Gap ranges (top 10):`));
35617
+ const gapRows = sync.gaps.ranges.map((g) => [
35618
+ String(g.start),
35619
+ String(g.end),
35620
+ String(g.size),
35621
+ g.reason
35622
+ ]);
35623
+ console.log(formatTable(["Start", "End", "Size", "Reason"], gapRows));
35624
+ if (sync.gaps.count > sync.gaps.ranges.length) {
35625
+ console.log(dim(` ... and ${sync.gaps.count - sync.gaps.ranges.length} more. Run: sl subgraphs gaps ${name}`));
35626
+ }
35627
+ }
35503
35628
  const tableEntries = Object.entries(subgraph.tables);
35504
35629
  if (tableEntries.length > 0) {
35505
35630
  console.log(dim(`
@@ -35516,8 +35641,8 @@ Table endpoints:`));
35516
35641
  try {
35517
35642
  info(`Reindexing subgraph "${name}"...`);
35518
35643
  const result = await reindexSubgraphApi(name, {
35519
- fromBlock: options2.from ? parseInt(options2.from, 10) : undefined,
35520
- toBlock: options2.to ? parseInt(options2.to, 10) : undefined
35644
+ fromBlock: options2.from ? Number.parseInt(options2.from, 10) : undefined,
35645
+ toBlock: options2.to ? Number.parseInt(options2.to, 10) : undefined
35521
35646
  });
35522
35647
  success(result.message);
35523
35648
  info(`From block ${result.fromBlock} to ${result.toBlock}`);
@@ -35527,8 +35652,8 @@ Table endpoints:`));
35527
35652
  });
35528
35653
  subgraphs.command("backfill <name>").description("Backfill a block range without dropping existing data").requiredOption("--from <block>", "Start block height").requiredOption("--to <block>", "End block height").action(async (name, options2) => {
35529
35654
  try {
35530
- const fromBlock = parseInt(options2.from, 10);
35531
- const toBlock = parseInt(options2.to, 10);
35655
+ const fromBlock = Number.parseInt(options2.from, 10);
35656
+ const toBlock = Number.parseInt(options2.to, 10);
35532
35657
  if (isNaN(fromBlock) || isNaN(toBlock)) {
35533
35658
  error("--from and --to must be valid block numbers");
35534
35659
  process.exit(1);
@@ -35541,6 +35666,35 @@ Table endpoints:`));
35541
35666
  handleApiError(err, "backfill subgraph");
35542
35667
  }
35543
35668
  });
35669
+ subgraphs.command("gaps <name>").description("Show block gaps for a subgraph").option("--resolved", "Include resolved gaps").option("--limit <n>", "Max gaps to return", "50").option("--json", "Output as JSON").action(async (name, options2) => {
35670
+ try {
35671
+ const result = await getSubgraphGaps(name, {
35672
+ limit: Number.parseInt(options2.limit ?? "50", 10),
35673
+ resolved: options2.resolved
35674
+ });
35675
+ if (options2.json) {
35676
+ console.log(JSON.stringify(result, null, 2));
35677
+ return;
35678
+ }
35679
+ if (result.data.length === 0) {
35680
+ success("No gaps detected");
35681
+ return;
35682
+ }
35683
+ const rows = result.data.map((g) => [
35684
+ String(g.start),
35685
+ String(g.end),
35686
+ String(g.size),
35687
+ g.reason,
35688
+ g.detectedAt.replace("T", " ").slice(0, 19),
35689
+ g.resolvedAt ? g.resolvedAt.replace("T", " ").slice(0, 19) : dim("—")
35690
+ ]);
35691
+ console.log(formatTable(["Start", "End", "Size", "Reason", "Detected", "Resolved"], rows));
35692
+ console.log(dim(`
35693
+ ${result.meta.total} gap(s), ${result.meta.totalMissingBlocks} total missing blocks`));
35694
+ } catch (err) {
35695
+ handleApiError(err, "get subgraph gaps");
35696
+ }
35697
+ });
35544
35698
  subgraphs.command("query <name> <table>").description("Query a subgraph table").option("--sort <column>", "Sort by column").option("--order <dir>", "Sort direction (asc|desc)", "asc").option("--limit <n>", "Max rows to return", "20").option("--offset <n>", "Skip first N rows").option("--fields <cols>", "Comma-separated columns to include").option("--filter <kv...>", "Filter as key=value (supports .gte/.lte/.gt/.lt/.neq suffixes)").option("--count", "Return row count only").option("--json", "Output as JSON").action(async (name, table, options2) => {
35545
35699
  try {
35546
35700
  const filters = {};
@@ -35557,8 +35711,8 @@ Table endpoints:`));
35557
35711
  const params = {
35558
35712
  sort: options2.sort,
35559
35713
  order: options2.sort ? options2.order : undefined,
35560
- limit: parseInt(options2.limit, 10),
35561
- offset: options2.offset ? parseInt(options2.offset, 10) : undefined,
35714
+ limit: Number.parseInt(options2.limit, 10),
35715
+ offset: options2.offset ? Number.parseInt(options2.offset, 10) : undefined,
35562
35716
  fields: options2.fields,
35563
35717
  filters: Object.keys(filters).length > 0 ? filters : undefined
35564
35718
  };
@@ -35666,10 +35820,10 @@ ${rows.length} row(s)`));
35666
35820
  }
35667
35821
  // src/commands/stack.ts
35668
35822
  init_config();
35669
- init_network();
35670
- init_node_manager();
35671
35823
  init_dev_state();
35672
35824
  init_docker();
35825
+ init_network();
35826
+ init_node_manager();
35673
35827
  init_output();
35674
35828
  function registerStackCommand(program2) {
35675
35829
  const stack = program2.command("stack").description("Manage the full stack");
@@ -35763,7 +35917,13 @@ async function stackStart(options2) {
35763
35917
  if (await isDevRunning()) {
35764
35918
  info("Dev services already running");
35765
35919
  } else {
35766
- const args = ["bun", "run", import.meta.dir + "/../../bin/secondlayer.ts", "dev", "start"];
35920
+ const args = [
35921
+ "bun",
35922
+ "run",
35923
+ import.meta.dir + "/../../bin/secondlayer.ts",
35924
+ "dev",
35925
+ "start"
35926
+ ];
35767
35927
  if (config.node?.installPath) {
35768
35928
  args.push("--stacks-node");
35769
35929
  }
@@ -35810,7 +35970,13 @@ async function stackStop(options2) {
35810
35970
  if (options2.dev) {
35811
35971
  if (await isDevRunning()) {
35812
35972
  info("Stopping dev services...");
35813
- const proc = Bun.spawn(["bun", "run", import.meta.dir + "/../../bin/secondlayer.ts", "dev", "stop"], { stdin: "inherit", stdout: "inherit", stderr: "inherit" });
35973
+ const proc = Bun.spawn([
35974
+ "bun",
35975
+ "run",
35976
+ import.meta.dir + "/../../bin/secondlayer.ts",
35977
+ "dev",
35978
+ "stop"
35979
+ ], { stdin: "inherit", stdout: "inherit", stderr: "inherit" });
35814
35980
  await proc.exited;
35815
35981
  } else {
35816
35982
  info("Dev services not running");
@@ -35854,11 +36020,15 @@ async function stackStop(options2) {
35854
36020
  success("Stack stopped");
35855
36021
  console.log("");
35856
36022
  }
36023
+ // src/commands/doctor.ts
36024
+ init_api_client();
36025
+ init_config();
36026
+
35857
36027
  // src/lib/health.ts
35858
- init_dev_state();
35859
- init_node_manager();
35860
36028
  init_config();
36029
+ init_dev_state();
35861
36030
  init_network();
36031
+ init_node_manager();
35862
36032
  async function checkHealth() {
35863
36033
  const config = await loadConfig();
35864
36034
  const issues = [];
@@ -35926,7 +36096,7 @@ async function checkHealth() {
35926
36096
  try {
35927
36097
  const inspect2 = await Bun.$`docker inspect --format={{.RestartCount}} ${name}`.quiet().nothrow();
35928
36098
  if (inspect2.exitCode === 0) {
35929
- restartCount = parseInt(inspect2.stdout.toString().trim()) || 0;
36099
+ restartCount = Number.parseInt(inspect2.stdout.toString().trim()) || 0;
35930
36100
  }
35931
36101
  } catch {}
35932
36102
  containers.push({ name, status, health, restartCount });
@@ -35995,8 +36165,6 @@ async function checkHealth() {
35995
36165
  }
35996
36166
 
35997
36167
  // src/commands/doctor.ts
35998
- init_config();
35999
- init_api_client();
36000
36168
  init_network();
36001
36169
  init_output();
36002
36170
  function registerDoctorCommand(program2) {
@@ -36017,7 +36185,9 @@ async function runHostedDoctor(jsonOutput) {
36017
36185
  let apiHealthy = false;
36018
36186
  let statusData = null;
36019
36187
  try {
36020
- const res = await fetch(`${apiUrl}/status`, { headers: authHeaders(config) });
36188
+ const res = await fetch(`${apiUrl}/status`, {
36189
+ headers: authHeaders(config)
36190
+ });
36021
36191
  apiHealthy = res.ok;
36022
36192
  if (res.ok) {
36023
36193
  statusData = await res.json();
@@ -36033,7 +36203,9 @@ async function runHostedDoctor(jsonOutput) {
36033
36203
  let authOk = false;
36034
36204
  let account = null;
36035
36205
  try {
36036
- const res = await fetch(`${apiUrl}/api/accounts/me`, { headers: authHeaders(config) });
36206
+ const res = await fetch(`${apiUrl}/api/accounts/me`, {
36207
+ headers: authHeaders(config)
36208
+ });
36037
36209
  if (res.ok) {
36038
36210
  authOk = true;
36039
36211
  account = await res.json();
@@ -36117,7 +36289,10 @@ async function runLocalDoctor(jsonOutput) {
36117
36289
  console.log(blue("Stack"));
36118
36290
  console.log(formatKeyValue([
36119
36291
  [" Network", network ?? dim("not configured")],
36120
- [" Chain ID", network ? `${chainIdHex} (${report.node.chainIdValid ? green("valid") : red("MISMATCH")})` : dim("N/A")]
36292
+ [
36293
+ " Chain ID",
36294
+ network ? `${chainIdHex} (${report.node.chainIdValid ? green("valid") : red("MISMATCH")})` : dim("N/A")
36295
+ ]
36121
36296
  ]));
36122
36297
  console.log("");
36123
36298
  console.log(blue("Node"));
@@ -36127,7 +36302,10 @@ async function runLocalDoctor(jsonOutput) {
36127
36302
  console.log(formatKeyValue([
36128
36303
  [" Height", `${heightStr} ${burnStr}`],
36129
36304
  [" Peers", report.node.peers.toString()],
36130
- [" Status", report.node.height ? green("syncing") : yellow("starting")],
36305
+ [
36306
+ " Status",
36307
+ report.node.height ? green("syncing") : yellow("starting")
36308
+ ],
36131
36309
  [" Version", report.node.version ?? dim("unknown")]
36132
36310
  ]));
36133
36311
  } else {
@@ -36209,8 +36387,8 @@ async function runLocalDoctor(jsonOutput) {
36209
36387
  console.log("");
36210
36388
  }
36211
36389
  // src/commands/auth.ts
36212
- init_config();
36213
36390
  init_api_client();
36391
+ init_config();
36214
36392
  init_output();
36215
36393
  import { hostname } from "node:os";
36216
36394
  import { input as input2 } from "@inquirer/prompts";
@@ -36235,25 +36413,33 @@ function registerAuthCommand(program2) {
36235
36413
  });
36236
36414
  await assertOk(mlRes);
36237
36415
  console.log(dim("Check your email for a 6-digit login code."));
36238
- const token = await input2({
36416
+ const code = await input2({
36239
36417
  message: "Code:",
36240
- validate: (v) => v.trim().length > 0 || "Token is required"
36418
+ validate: (v) => /^\d{6}$/.test(v.trim()) || "Enter the 6-digit code from your email"
36241
36419
  });
36242
36420
  const verifyRes = await fetch(`${apiUrl}/api/auth/verify`, {
36243
36421
  method: "POST",
36244
36422
  headers: { "Content-Type": "application/json" },
36245
- body: JSON.stringify({ token: token.trim() })
36423
+ body: JSON.stringify({ code: code.trim(), email })
36246
36424
  });
36247
36425
  await assertOk(verifyRes);
36248
36426
  const result = await verifyRes.json();
36249
- const sessionHeaders = { Authorization: `Bearer ${result.sessionToken}`, "Content-Type": "application/json" };
36427
+ const sessionHeaders = {
36428
+ Authorization: `Bearer ${result.sessionToken}`,
36429
+ "Content-Type": "application/json"
36430
+ };
36250
36431
  const keyName = `cli-${hostname().toLowerCase()}`;
36251
- const listRes = await fetch(`${apiUrl}/api/keys`, { headers: sessionHeaders });
36432
+ const listRes = await fetch(`${apiUrl}/api/keys`, {
36433
+ headers: sessionHeaders
36434
+ });
36252
36435
  if (listRes.ok) {
36253
36436
  const { keys: keys2 } = await listRes.json();
36254
36437
  const existing = keys2.find((k) => k.name === keyName && k.status === "active");
36255
36438
  if (existing) {
36256
- await fetch(`${apiUrl}/api/keys/${existing.id}`, { method: "DELETE", headers: sessionHeaders });
36439
+ await fetch(`${apiUrl}/api/keys/${existing.id}`, {
36440
+ method: "DELETE",
36441
+ headers: sessionHeaders
36442
+ });
36257
36443
  }
36258
36444
  }
36259
36445
  const createRes = await fetch(`${apiUrl}/api/keys`, {
@@ -36266,7 +36452,10 @@ function registerAuthCommand(program2) {
36266
36452
  config.apiKey = key;
36267
36453
  await saveConfig(config);
36268
36454
  try {
36269
- await fetch(`${apiUrl}/api/auth/logout`, { method: "POST", headers: sessionHeaders });
36455
+ await fetch(`${apiUrl}/api/auth/logout`, {
36456
+ method: "POST",
36457
+ headers: sessionHeaders
36458
+ });
36270
36459
  } catch {}
36271
36460
  success(`Authenticated as ${result.account.email}`);
36272
36461
  console.log(dim(`Key: ${prefix}...`));
@@ -36292,7 +36481,10 @@ function registerAuthCommand(program2) {
36292
36481
  const currentPrefix = config.apiKey.slice(0, 14);
36293
36482
  const match = keys2.find((k) => currentPrefix.startsWith(k.prefix));
36294
36483
  if (match) {
36295
- await fetch(`${apiUrl}/api/keys/${match.id}`, { method: "DELETE", headers });
36484
+ await fetch(`${apiUrl}/api/keys/${match.id}`, {
36485
+ method: "DELETE",
36486
+ headers
36487
+ });
36296
36488
  }
36297
36489
  }
36298
36490
  } catch {}
@@ -36306,7 +36498,10 @@ function registerAuthCommand(program2) {
36306
36498
  const pairs = [
36307
36499
  ["Network", config.network],
36308
36500
  ["API", apiUrl || "(not configured)"],
36309
- ["API Key", config.apiKey ? config.apiKey.slice(0, 14) + "..." : "(none)"]
36501
+ [
36502
+ "API Key",
36503
+ config.apiKey ? `${config.apiKey.slice(0, 14)}...` : "(none)"
36504
+ ]
36310
36505
  ];
36311
36506
  if (config.apiKey && apiUrl) {
36312
36507
  try {
@@ -36348,7 +36543,10 @@ function registerAuthCommand(program2) {
36348
36543
  keys.command("create").description("Create a new API key").option("--name <name>", "Name for the API key").action(async (options2) => {
36349
36544
  const config = await loadConfig();
36350
36545
  const apiUrl = resolveApiUrl(config);
36351
- const headers = { ...authHeaders(config), "Content-Type": "application/json" };
36546
+ const headers = {
36547
+ ...authHeaders(config),
36548
+ "Content-Type": "application/json"
36549
+ };
36352
36550
  try {
36353
36551
  const res = await fetch(`${apiUrl}/api/keys`, {
36354
36552
  method: "POST",
@@ -36448,7 +36646,13 @@ init_config();
36448
36646
  init_dev_state();
36449
36647
  init_node_manager();
36450
36648
  init_output();
36451
- var DEV_SERVICES = ["api", "indexer", "worker", "receiver", "subgraphs"];
36649
+ var DEV_SERVICES = [
36650
+ "api",
36651
+ "indexer",
36652
+ "worker",
36653
+ "receiver",
36654
+ "subgraphs"
36655
+ ];
36452
36656
  function registerLocalCommand(program2) {
36453
36657
  const local = program2.command("local").description("Manage local development environment and Stacks node").hook("preAction", async (_thisCommand, actionCommand) => {
36454
36658
  if (actionCommand.name() === "help")
@@ -36511,7 +36715,7 @@ function registerLocalCommand(program2) {
36511
36715
  });
36512
36716
  node.command("config-check").description("Show events observer configuration for Config.toml").option("--indexer-port <port>", "Indexer port to display", "3700").action(async (options2) => {
36513
36717
  const { showConfigCheck: showConfigCheck2 } = await Promise.resolve().then(() => (init_node_impl(), exports_node_impl));
36514
- await showConfigCheck2(parseInt(options2.indexerPort));
36718
+ await showConfigCheck2(Number.parseInt(options2.indexerPort));
36515
36719
  });
36516
36720
  node.command("logs").description("View Stacks node logs").option("-f, --follow", "Follow log output").option("-n, --lines <n>", "Number of lines to show", "50").option("-q, --quiet", "Filter out common noise").action(async (options2) => {
36517
36721
  await showLocalLogs({ ...options2, service: "node" });
@@ -36558,13 +36762,18 @@ var serviceColors2 = {
36558
36762
  node: red
36559
36763
  };
36560
36764
  async function showLocalLogs(options2) {
36561
- const lines = parseInt(options2.lines);
36765
+ const lines = Number.parseInt(options2.lines);
36562
36766
  const service = options2.service?.toLowerCase();
36563
36767
  const showDev = !service || service === "dev" || DEV_SERVICES.includes(service);
36564
36768
  const showNode = !service || service === "node";
36565
36769
  if (service && DEV_SERVICES.includes(service)) {
36566
36770
  const { showLogs: showLogs2 } = await Promise.resolve().then(() => (init_dev_impl(), exports_dev_impl));
36567
- await showLogs2({ follow: options2.follow, service, lines: options2.lines, verbose: options2.verbose });
36771
+ await showLogs2({
36772
+ follow: options2.follow,
36773
+ service,
36774
+ lines: options2.lines,
36775
+ verbose: options2.verbose
36776
+ });
36568
36777
  return;
36569
36778
  }
36570
36779
  if (service === "node") {
@@ -36585,7 +36794,7 @@ async function showNodeLogs(options2) {
36585
36794
  }
36586
36795
  const logs = await getNodeLogs({
36587
36796
  follow: options2.follow,
36588
- lines: parseInt(options2.lines),
36797
+ lines: Number.parseInt(options2.lines),
36589
36798
  quiet: options2.quiet,
36590
36799
  format: true
36591
36800
  });
@@ -36730,8 +36939,8 @@ function formatDeliverySummary2(jsonStr) {
36730
36939
  }
36731
36940
  }
36732
36941
  // src/commands/whoami.ts
36733
- init_config();
36734
36942
  init_api_client();
36943
+ init_config();
36735
36944
  init_output();
36736
36945
  function registerWhoamiCommand(program2) {
36737
36946
  program2.command("whoami").description("Show current authenticated account").action(async () => {
@@ -36803,5 +37012,5 @@ registerWhoamiCommand(program);
36803
37012
  registerReceiverCommand(program);
36804
37013
  program.parse();
36805
37014
 
36806
- //# debugId=25ACF0102B2A97C864756E2164756E21
37015
+ //# debugId=D8ABBF7C05EEC0E764756E2164756E21
36807
37016
  //# sourceMappingURL=cli.js.map