@secondlayer/cli 1.6.8 → 1.8.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,
@@ -4567,6 +4571,7 @@ __export(exports_api_client, {
4567
4571
  deleteSubgraphApi: () => deleteSubgraphApi,
4568
4572
  deleteStream: () => deleteStream,
4569
4573
  createStream: () => createStream,
4574
+ backfillSubgraphApi: () => backfillSubgraphApi,
4570
4575
  authHeaders: () => authHeaders,
4571
4576
  assertOk: () => assertOk,
4572
4577
  ApiError: () => ApiError
@@ -4651,6 +4656,9 @@ async function getSubgraphApi(name) {
4651
4656
  async function reindexSubgraphApi(name, options) {
4652
4657
  return (await getClient()).subgraphs.reindex(name, options);
4653
4658
  }
4659
+ async function backfillSubgraphApi(name, options) {
4660
+ return (await getClient()).subgraphs.backfill(name, options);
4661
+ }
4654
4662
  async function deleteSubgraphApi(name) {
4655
4663
  return (await getClient()).subgraphs.delete(name);
4656
4664
  }
@@ -4663,6 +4671,9 @@ async function querySubgraphTable(name, table, params = {}) {
4663
4671
  async function querySubgraphTableCount(name, table, params = {}) {
4664
4672
  return (await getClient()).subgraphs.queryTableCount(name, table, params);
4665
4673
  }
4674
+ async function getSubgraphGaps(name, opts) {
4675
+ return (await getClient()).subgraphs.gaps(name, opts);
4676
+ }
4666
4677
  var init_api_client = __esm(() => {
4667
4678
  init_config();
4668
4679
  });
@@ -4800,6 +4811,261 @@ async function formatCode(code) {
4800
4811
  var biome = null;
4801
4812
  var init_format = () => {};
4802
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
+
4803
5069
  // ../../node_modules/@sindresorhus/is/dist/index.js
4804
5070
  function isTypedArrayName(name) {
4805
5071
  return typedArrayTypeNames.includes(name);
@@ -12491,8 +12757,8 @@ class StacksApiClient {
12491
12757
  var gotWithRetry;
12492
12758
  var init_api = __esm(() => {
12493
12759
  init_source3();
12494
- init_config();
12495
12760
  init_api_client();
12761
+ init_config();
12496
12762
  gotWithRetry = source_default2.extend({
12497
12763
  timeout: { request: 30000 },
12498
12764
  retry: {
@@ -12513,261 +12779,6 @@ function inferNetwork(address) {
12513
12779
  return;
12514
12780
  }
12515
12781
 
12516
- // src/utils/abi-compat.ts
12517
- function normalizeAccess(access) {
12518
- if (access === "read_only")
12519
- return "read-only";
12520
- return access;
12521
- }
12522
- function normalizeType(type) {
12523
- if (typeof type === "string") {
12524
- switch (type) {
12525
- case "uint128":
12526
- case "int128":
12527
- case "bool":
12528
- case "principal":
12529
- case "trait_reference":
12530
- return type;
12531
- default:
12532
- return type;
12533
- }
12534
- }
12535
- if (typeof type !== "object" || type === null) {
12536
- throw new Error(`Invalid ABI type: expected object, got ${typeof type}`);
12537
- }
12538
- const typeObj = type;
12539
- if ("buffer" in typeObj) {
12540
- const buffer2 = typeObj.buffer;
12541
- return {
12542
- buff: {
12543
- length: buffer2?.length ?? 32
12544
- }
12545
- };
12546
- }
12547
- if ("buff" in typeObj) {
12548
- const buff = typeObj.buff;
12549
- return {
12550
- buff: {
12551
- length: buff?.length ?? 32
12552
- }
12553
- };
12554
- }
12555
- if ("string-ascii" in typeObj) {
12556
- const strAscii = typeObj["string-ascii"];
12557
- return {
12558
- "string-ascii": {
12559
- length: strAscii?.length ?? 256
12560
- }
12561
- };
12562
- }
12563
- if ("string-utf8" in typeObj) {
12564
- const strUtf8 = typeObj["string-utf8"];
12565
- return {
12566
- "string-utf8": {
12567
- length: strUtf8?.length ?? 256
12568
- }
12569
- };
12570
- }
12571
- if ("response" in typeObj) {
12572
- const response2 = typeObj.response;
12573
- return {
12574
- response: {
12575
- ok: normalizeType(response2?.ok ?? "bool"),
12576
- error: normalizeType(response2?.error ?? "uint128")
12577
- }
12578
- };
12579
- }
12580
- if ("optional" in typeObj) {
12581
- return {
12582
- optional: normalizeType(typeObj.optional)
12583
- };
12584
- }
12585
- if ("list" in typeObj) {
12586
- const list = typeObj.list;
12587
- return {
12588
- list: {
12589
- type: normalizeType(list?.type ?? "uint128"),
12590
- length: list?.length ?? 100
12591
- }
12592
- };
12593
- }
12594
- if ("tuple" in typeObj) {
12595
- const tuple = typeObj.tuple;
12596
- return {
12597
- tuple: tuple.map((field) => ({
12598
- name: field.name,
12599
- type: normalizeType(field.type)
12600
- }))
12601
- };
12602
- }
12603
- throw new Error(`Unknown ABI type structure: ${JSON.stringify(type)}`);
12604
- }
12605
- function normalizeFunction(func) {
12606
- const access = normalizeAccess(func.access);
12607
- const args = func.args ?? [];
12608
- const outputs = func.outputs;
12609
- return {
12610
- name: func.name,
12611
- access,
12612
- args: args.map((arg) => ({
12613
- name: arg.name,
12614
- type: normalizeType(arg.type)
12615
- })),
12616
- outputs: normalizeType(typeof outputs === "object" && outputs !== null && "type" in outputs ? outputs.type : outputs)
12617
- };
12618
- }
12619
- function normalizeMap(map) {
12620
- return {
12621
- name: map.name,
12622
- key: normalizeType(map.key),
12623
- value: normalizeType(map.value)
12624
- };
12625
- }
12626
- function normalizeVariable(variable) {
12627
- return {
12628
- name: variable.name,
12629
- type: normalizeType(variable.type),
12630
- access: variable.access
12631
- };
12632
- }
12633
- function normalizeAbi(abi) {
12634
- if (typeof abi !== "object" || abi === null) {
12635
- return { functions: [] };
12636
- }
12637
- const abiObj = abi;
12638
- const functions = [];
12639
- const maps = [];
12640
- const variables = [];
12641
- if (Array.isArray(abiObj.functions)) {
12642
- for (const func of abiObj.functions) {
12643
- if (typeof func === "object" && func !== null) {
12644
- functions.push(normalizeFunction(func));
12645
- }
12646
- }
12647
- }
12648
- if (Array.isArray(abiObj.maps)) {
12649
- for (const map of abiObj.maps) {
12650
- if (typeof map === "object" && map !== null) {
12651
- maps.push(normalizeMap(map));
12652
- }
12653
- }
12654
- }
12655
- if (Array.isArray(abiObj.variables)) {
12656
- for (const variable of abiObj.variables) {
12657
- if (typeof variable === "object" && variable !== null) {
12658
- variables.push(normalizeVariable(variable));
12659
- }
12660
- }
12661
- }
12662
- return {
12663
- functions,
12664
- maps: maps.length > 0 ? maps : undefined,
12665
- variables: variables.length > 0 ? variables : undefined
12666
- };
12667
- }
12668
-
12669
- // src/parsers/clarity.ts
12670
- import { promises as fs2 } from "fs";
12671
- async function parseClarityFile(filePath) {
12672
- try {
12673
- const content = await fs2.readFile(filePath, "utf-8");
12674
- const result = parseClarityContent(content);
12675
- if (result.functions.length === 0) {
12676
- console.warn(`⚠️ No functions found in ${filePath}. ` + `For complex contracts, deploy first and use the contract address instead.`);
12677
- }
12678
- return result;
12679
- } catch (error2) {
12680
- throw new Error(`Unable to parse ${filePath}. ` + `For complex contracts, deploy first and use the contract address instead.
12681
- ` + `Original error: ${error2}`);
12682
- }
12683
- }
12684
- function parseClarityContent(content) {
12685
- const functions = [];
12686
- const functionRegex = /\(define-(public|read-only|private)\s+\(([^)]+)\)([\s\S]*?)\)\s*$/gm;
12687
- let match;
12688
- while ((match = functionRegex.exec(content)) !== null) {
12689
- const [, access, signature, body] = match;
12690
- const func = parseFunctionSignature(signature, access, body);
12691
- if (func) {
12692
- functions.push(func);
12693
- }
12694
- }
12695
- return { functions };
12696
- }
12697
- function parseFunctionSignature(signature, access, body) {
12698
- const parts = signature.trim().split(/\s+/);
12699
- const name = parts[0];
12700
- const args = [];
12701
- for (let i = 1;i < parts.length; i += 2) {
12702
- if (parts[i] && parts[i + 1]) {
12703
- const argName = parts[i].replace(/[()]/g, "");
12704
- const argType = parseType(parts[i + 1]);
12705
- if (argType) {
12706
- args.push({ name: argName, type: argType });
12707
- }
12708
- }
12709
- }
12710
- const outputs = inferReturnType(body);
12711
- return {
12712
- name,
12713
- access,
12714
- args,
12715
- outputs
12716
- };
12717
- }
12718
- function parseType(typeStr) {
12719
- typeStr = typeStr.replace(/[()]/g, "").trim();
12720
- switch (typeStr) {
12721
- case "uint":
12722
- case "uint128":
12723
- return "uint128";
12724
- case "int":
12725
- case "int128":
12726
- return "int128";
12727
- case "bool":
12728
- return "bool";
12729
- case "principal":
12730
- return "principal";
12731
- case "trait_reference":
12732
- return "principal";
12733
- default:
12734
- if (typeStr.startsWith("string-ascii")) {
12735
- return { "string-ascii": { length: 256 } };
12736
- }
12737
- if (typeStr.startsWith("string-utf8")) {
12738
- return { "string-utf8": { length: 256 } };
12739
- }
12740
- if (typeStr.startsWith("buff")) {
12741
- return { buff: { length: 32 } };
12742
- }
12743
- return "uint128";
12744
- }
12745
- }
12746
- function inferReturnType(body) {
12747
- if (body.includes("(ok")) {
12748
- if (body.includes("(err")) {
12749
- return {
12750
- response: {
12751
- ok: "bool",
12752
- error: "uint128"
12753
- }
12754
- };
12755
- }
12756
- }
12757
- if (body.includes("true") || body.includes("false")) {
12758
- return "bool";
12759
- }
12760
- return "bool";
12761
- }
12762
- function parseApiResponse(apiResponse) {
12763
- try {
12764
- return normalizeAbi(apiResponse);
12765
- } catch (error2) {
12766
- throw new Error(`Failed to parse API response: ${error2}`);
12767
- }
12768
- }
12769
- var init_clarity = () => {};
12770
-
12771
12782
  // src/lib/network.ts
12772
12783
  function getChainId(network) {
12773
12784
  return CHAIN_IDS[network].decimal.toString();
@@ -12807,7 +12818,7 @@ async function validateNetworkConsistency(config) {
12807
12818
  const envStr = result.stdout.toString();
12808
12819
  const match = envStr.match(/STACKS_CHAIN_ID=(0x[0-9a-fA-F]+|\d+)/);
12809
12820
  if (match) {
12810
- const containerId = parseInt(match[1], match[1].startsWith("0x") ? 16 : 10);
12821
+ const containerId = Number.parseInt(match[1], match[1].startsWith("0x") ? 16 : 10);
12811
12822
  if (containerId !== expected) {
12812
12823
  issues.push(`Container STACKS_CHAIN_ID=${containerId} but config expects ${expected} (${network})`);
12813
12824
  }
@@ -12821,7 +12832,7 @@ async function validateNetworkConsistency(config) {
12821
12832
  const content = await envFile.text();
12822
12833
  const match = content.match(/STACKS_CHAIN_ID=(0x[0-9a-fA-F]+|\d+)/);
12823
12834
  if (match) {
12824
- const fileId = parseInt(match[1], match[1].startsWith("0x") ? 16 : 10);
12835
+ const fileId = Number.parseInt(match[1], match[1].startsWith("0x") ? 16 : 10);
12825
12836
  if (fileId !== expected) {
12826
12837
  issues.push(`Node .env STACKS_CHAIN_ID=${fileId} but config expects ${expected} (${network})`);
12827
12838
  }
@@ -12860,7 +12871,15 @@ async function validateNodePath(path) {
12860
12871
  return { valid: true };
12861
12872
  }
12862
12873
  async function runManageScript(installPath, network, action, extraArgs = []) {
12863
- 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
+ ];
12864
12883
  const proc = Bun.spawn(args, {
12865
12884
  cwd: installPath,
12866
12885
  stdin: "inherit",
@@ -12871,9 +12890,7 @@ async function runManageScript(installPath, network, action, extraArgs = []) {
12871
12890
  return { stdout: "", stderr: "", exitCode };
12872
12891
  }
12873
12892
  async function startNodeContainers(installPath, network) {
12874
- const dirs = [
12875
- `${installPath}/persistent-data/${network}/stacks-blockchain`
12876
- ];
12893
+ const dirs = [`${installPath}/persistent-data/${network}/stacks-blockchain`];
12877
12894
  for (const dir of dirs) {
12878
12895
  await Bun.$`mkdir -p ${dir}`.quiet().nothrow();
12879
12896
  }
@@ -12884,7 +12901,11 @@ async function startNodeContainers(installPath, network) {
12884
12901
  const envStr = inspect2.stdout.toString();
12885
12902
  const match = envStr.match(/STACKS_CHAIN_ID=(\d+)/);
12886
12903
  if (match && match[1] !== chainId) {
12887
- 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();
12888
12909
  }
12889
12910
  }
12890
12911
  } catch {}
@@ -12999,7 +13020,7 @@ function formatLogLine(line) {
12999
13020
  return line;
13000
13021
  }
13001
13022
  const [, level, timestamp, _source, _thread, message] = match;
13002
- const date = new Date(parseFloat(timestamp) * 1000);
13023
+ const date = new Date(Number.parseFloat(timestamp) * 1000);
13003
13024
  const isoTime = date.toISOString();
13004
13025
  const shortMessage = extractKeyInfo(message);
13005
13026
  return `[stacks-node] [${isoTime}] ${level}: ${shortMessage}`;
@@ -13146,7 +13167,7 @@ var init_manager = __esm(() => {
13146
13167
  });
13147
13168
 
13148
13169
  // src/services/indexer.ts
13149
- import { resolve as resolve2, dirname } from "node:path";
13170
+ import { dirname, resolve as resolve2 } from "node:path";
13150
13171
  async function startIndexer(options2) {
13151
13172
  const port = options2.port ?? 3700;
13152
13173
  const rootDir = dirname(dirname(dirname(dirname(import.meta.dir))));
@@ -13166,7 +13187,7 @@ var init_indexer = __esm(() => {
13166
13187
  });
13167
13188
 
13168
13189
  // src/services/worker.ts
13169
- import { resolve as resolve3, dirname as dirname2 } from "node:path";
13190
+ import { dirname as dirname2, resolve as resolve3 } from "node:path";
13170
13191
  async function startWorker(options2) {
13171
13192
  const rootDir = dirname2(dirname2(dirname2(dirname2(import.meta.dir))));
13172
13193
  const workerPath = resolve3(rootDir, "packages/worker/src/index.ts");
@@ -13184,7 +13205,7 @@ var init_worker = __esm(() => {
13184
13205
  });
13185
13206
 
13186
13207
  // src/services/api.ts
13187
- import { resolve as resolve4, dirname as dirname3 } from "node:path";
13208
+ import { dirname as dirname3, resolve as resolve4 } from "node:path";
13188
13209
  async function startApi(options2) {
13189
13210
  const port = options2.port ?? 3800;
13190
13211
  const rootDir = dirname3(dirname3(dirname3(dirname3(import.meta.dir))));
@@ -13291,7 +13312,7 @@ var init_receiver_server = __esm(() => {
13291
13312
  });
13292
13313
 
13293
13314
  // src/services/subgraph-processor.ts
13294
- import { resolve as resolve5, dirname as dirname4 } from "node:path";
13315
+ import { dirname as dirname4, resolve as resolve5 } from "node:path";
13295
13316
  async function startSubgraphProcessor(options2) {
13296
13317
  const rootDir = dirname4(dirname4(dirname4(dirname4(import.meta.dir))));
13297
13318
  const servicePath = resolve5(rootDir, "packages/subgraphs/src/service.ts");
@@ -13329,8 +13350,8 @@ __export(exports_dev_impl, {
13329
13350
  restartDev: () => restartDev,
13330
13351
  isDevAlreadyRunning: () => isDevAlreadyRunning
13331
13352
  });
13332
- import { resolve as resolve6, dirname as dirname5, join as join6 } from "node:path";
13333
13353
  import { mkdirSync as mkdirSync2 } from "node:fs";
13354
+ import { dirname as dirname5, join as join6, resolve as resolve6 } from "node:path";
13334
13355
  async function isDevAlreadyRunning() {
13335
13356
  if (await isDevRunning()) {
13336
13357
  const running = await getRunningServices();
@@ -13356,9 +13377,9 @@ async function runBackground(options2) {
13356
13377
  }
13357
13378
  const config = await loadConfig();
13358
13379
  const dataDir = getDataDir(config);
13359
- const indexerPort = options2.stacksNode ? 3701 : parseInt(options2.indexerPort) || config.ports.indexer;
13360
- const apiPort = parseInt(options2.apiPort) || config.ports.api;
13361
- 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;
13362
13383
  if (options2.stacksNode && config.node) {
13363
13384
  const validation = await validateNetworkConsistency(config);
13364
13385
  if (!validation.valid) {
@@ -13455,7 +13476,11 @@ async function runBackground(options2) {
13455
13476
  }
13456
13477
  {
13457
13478
  const subgraphsLogFile = getLogFile("subgraphs");
13458
- 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
+ ], {
13459
13484
  env: { ...process.env, ...env },
13460
13485
  stdout: Bun.file(subgraphsLogFile),
13461
13486
  stderr: Bun.file(subgraphsLogFile)
@@ -13470,8 +13495,15 @@ async function runBackground(options2) {
13470
13495
  }
13471
13496
  if (options2.receiver) {
13472
13497
  const receiverLogFile = getLogFile("receiver");
13473
- const receiverArgs = ["bun", "run", resolve6(packagesDir, "packages/cli/src/services/receiver-standalone.ts")];
13474
- 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
+ };
13475
13507
  if (options2.secret)
13476
13508
  receiverEnv.SIGNING_SECRET = options2.secret;
13477
13509
  const receiverProc = Bun.spawn(receiverArgs, {
@@ -13531,9 +13563,9 @@ async function runForeground(options2) {
13531
13563
  }
13532
13564
  const config = await loadConfig();
13533
13565
  const dataDir = getDataDir(config);
13534
- const indexerPort = options2.stacksNode ? 3701 : parseInt(options2.indexerPort) || config.ports.indexer;
13535
- const apiPort = parseInt(options2.apiPort) || config.ports.api;
13536
- 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;
13537
13569
  let devPostgresStarted = false;
13538
13570
  const shutdown = async () => {
13539
13571
  console.log(`
@@ -13593,13 +13625,18 @@ async function runForeground(options2) {
13593
13625
  }
13594
13626
  await startApi({ port: apiPort, onLog: (line) => logService("api", line) });
13595
13627
  console.log(green(" ✓ API"), dim(`http://localhost:${apiPort}`));
13596
- await startIndexer({ port: indexerPort, onLog: (line) => logService("indexer", line) });
13628
+ await startIndexer({
13629
+ port: indexerPort,
13630
+ onLog: (line) => logService("indexer", line)
13631
+ });
13597
13632
  console.log(green(" ✓ Indexer"), dim(`http://localhost:${indexerPort}`));
13598
13633
  if (options2.worker) {
13599
13634
  await startWorker({ onLog: (line) => logService("worker", line) });
13600
13635
  console.log(green(" ✓ Worker"), dim("processing jobs"));
13601
13636
  }
13602
- await startSubgraphProcessor({ onLog: (line) => logService("subgraphs", line) });
13637
+ await startSubgraphProcessor({
13638
+ onLog: (line) => logService("subgraphs", line)
13639
+ });
13603
13640
  console.log(green(" ✓ Subgraph processor"), dim("processing subgraphs"));
13604
13641
  console.log("");
13605
13642
  printUrls(indexerPort, apiPort, receiverPort, options2.receiver);
@@ -13634,7 +13671,7 @@ async function showLogs(options2) {
13634
13671
  info("No log files found");
13635
13672
  return;
13636
13673
  }
13637
- const lines = parseInt(options2.lines);
13674
+ const lines = Number.parseInt(options2.lines);
13638
13675
  const verbose = options2.verbose ?? false;
13639
13676
  if (options2.follow) {
13640
13677
  await followLogs2(serviceEntries, lines, verbose);
@@ -13855,8 +13892,15 @@ async function restartDev() {
13855
13892
  if (state.services.receiver) {
13856
13893
  const receiverPort = config.ports.receiver;
13857
13894
  const receiverLogFile = getLogFile("receiver");
13858
- const receiverProc = Bun.spawn(["bun", "run", resolve6(packagesDir, "packages/cli/src/services/receiver-standalone.ts")], {
13859
- 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
+ },
13860
13904
  stdout: Bun.file(receiverLogFile),
13861
13905
  stderr: Bun.file(receiverLogFile)
13862
13906
  });
@@ -13979,13 +14023,21 @@ async function isDatabaseReachable(url) {
13979
14023
  try {
13980
14024
  const parsed = new URL(url);
13981
14025
  const host = parsed.hostname;
13982
- const port = parseInt(parsed.port || "5432");
14026
+ const port = Number.parseInt(parsed.port || "5432");
13983
14027
  const socket = await Bun.$`pg_isready -h ${host} -p ${port}`.quiet().nothrow();
13984
14028
  if (socket.exitCode === 0)
13985
14029
  return true;
13986
- const conn = await Bun.connect({ hostname: host, port, socket: { data() {}, open(s) {
13987
- s.end();
13988
- }, 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);
13989
14041
  return conn !== null;
13990
14042
  } catch {
13991
14043
  return false;
@@ -14021,12 +14073,12 @@ async function runMigrations(databaseUrl) {
14021
14073
  }
14022
14074
  var DEV_DATABASE_URL3 = "postgres://postgres:postgres@localhost:5432/streams_dev", serviceColors;
14023
14075
  var init_dev_impl = __esm(() => {
14024
- init_services();
14025
- init_output();
14026
- init_dev_state();
14027
14076
  init_config();
14077
+ init_dev_state();
14028
14078
  init_docker();
14029
14079
  init_network();
14080
+ init_output();
14081
+ init_services();
14030
14082
  serviceColors = {
14031
14083
  api: blue,
14032
14084
  indexer: cyan,
@@ -14047,7 +14099,7 @@ __export(exports_node_impl, {
14047
14099
  runSetupWizard: () => runSetupWizard,
14048
14100
  restartNode: () => restartNode
14049
14101
  });
14050
- 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";
14051
14103
  async function runSetupWizard() {
14052
14104
  console.log("");
14053
14105
  console.log(blue("Stacks Node Setup Wizard"));
@@ -14099,14 +14151,14 @@ async function runSetupWizard() {
14099
14151
  message: "Enter custom indexer port:",
14100
14152
  default: "3700",
14101
14153
  validate: (value) => {
14102
- const port = parseInt(value);
14154
+ const port = Number.parseInt(value);
14103
14155
  if (isNaN(port) || port < 1 || port > 65535) {
14104
14156
  return "Invalid port number";
14105
14157
  }
14106
14158
  return true;
14107
14159
  }
14108
14160
  });
14109
- indexerPort = parseInt(portInput);
14161
+ indexerPort = Number.parseInt(portInput);
14110
14162
  }
14111
14163
  }
14112
14164
  const config = await loadConfig();
@@ -14126,7 +14178,10 @@ async function runSetupWizard() {
14126
14178
  console.log(formatKeyValue([
14127
14179
  [" Install path", installPath],
14128
14180
  [" Network", network],
14129
- [" Auto-start indexer", autoStartIndexer ? `Yes (port ${indexerPort})` : "No"]
14181
+ [
14182
+ " Auto-start indexer",
14183
+ autoStartIndexer ? `Yes (port ${indexerPort})` : "No"
14184
+ ]
14130
14185
  ]));
14131
14186
  console.log("");
14132
14187
  console.log(dim("Next steps:"));
@@ -14357,8 +14412,14 @@ async function showStatus(pathOverride, jsonOutput) {
14357
14412
  if (nodeInfo) {
14358
14413
  console.log(blue("Chain Info"));
14359
14414
  console.log(formatKeyValue([
14360
- [" Stacks Height", nodeInfo.stacks_tip_height?.toString() || "syncing..."],
14361
- [" 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
+ ],
14362
14423
  [" Peers", peerCount.toString()],
14363
14424
  [" Version", nodeInfo.server_version || "unknown"]
14364
14425
  ]));
@@ -14447,11 +14508,11 @@ async function showConfigCheck(indexerPort) {
14447
14508
  }
14448
14509
  var DEFAULT_RPC_PORT = 20443;
14449
14510
  var init_node_impl = __esm(() => {
14511
+ init_api_client();
14450
14512
  init_config();
14513
+ init_network();
14451
14514
  init_node_manager();
14452
14515
  init_output();
14453
- init_api_client();
14454
- init_network();
14455
14516
  });
14456
14517
 
14457
14518
  // ../../node_modules/chalk/source/vendor/ansi-styles/index.js
@@ -20467,197 +20528,16 @@ var init_plugin_manager = __esm(() => {
20467
20528
  validateStacksAddress = _validateStacksAddress;
20468
20529
  });
20469
20530
 
20470
- // src/utils/config.ts
20471
- import { promises as fs4 } from "fs";
20472
- import path2 from "path";
20473
- import { tmpdir } from "os";
20474
- import { randomBytes } from "crypto";
20475
- import { pathToFileURL } from "url";
20476
- import { createRequire as createRequire2 } from "module";
20477
- async function findConfigFile(cwd) {
20478
- for (const fileName of CONFIG_FILE_NAMES) {
20479
- const filePath = path2.join(cwd, fileName);
20480
- try {
20481
- await fs4.access(filePath);
20482
- return filePath;
20483
- } catch {}
20484
- }
20485
- return null;
20486
- }
20487
- async function loadConfig2(configPath) {
20488
- const cwd = process.cwd();
20489
- const resolvedPath = configPath ? path2.resolve(cwd, configPath) : await findConfigFile(cwd);
20490
- if (!resolvedPath) {
20491
- throw new Error("No config file found. Create a secondlayer.config.ts file or specify a path with --config");
20492
- }
20493
- let config;
20494
- if (resolvedPath.endsWith(".ts")) {
20495
- const code = await fs4.readFile(resolvedPath, "utf-8");
20496
- let replacementPath;
20497
- try {
20498
- const require2 = createRequire2(import.meta.url);
20499
- const packagePath = require2.resolve("@secondlayer/cli");
20500
- replacementPath = pathToFileURL(packagePath).href;
20501
- } catch {
20502
- const currentModuleDir = path2.dirname(new URL(import.meta.url).pathname);
20503
- const indexPath = path2.resolve(currentModuleDir, "../index");
20504
- replacementPath = pathToFileURL(indexPath).href;
20505
- }
20506
- const transformedCode = code.replace(/from\s+["']@secondlayer\/cli["']/g, `from '${replacementPath}'`);
20507
- const { transformSync } = await import("esbuild");
20508
- const result = transformSync(transformedCode, {
20509
- format: "esm",
20510
- target: "node18",
20511
- loader: "ts"
20512
- });
20513
- const tempPath = path2.join(tmpdir(), `secondlayer-config-${randomBytes(4).toString("hex")}.mjs`);
20514
- await fs4.writeFile(tempPath, result.code);
20515
- try {
20516
- const fileUrl = pathToFileURL(tempPath).href;
20517
- const module = await import(fileUrl);
20518
- config = module.default;
20519
- } finally {
20520
- await fs4.unlink(tempPath).catch(() => {});
20521
- }
20522
- } else {
20523
- const fileUrl = pathToFileURL(resolvedPath).href;
20524
- const module = await import(fileUrl);
20525
- config = module.default;
20526
- }
20527
- if (!config) {
20528
- throw new Error("Config file must export a default configuration");
20529
- }
20530
- if (typeof config === "function") {
20531
- config = config({});
20532
- }
20533
- validateConfig(config);
20534
- const pluginManager = new PluginManager;
20535
- if (config.plugins && Array.isArray(config.plugins)) {
20536
- for (const plugin of config.plugins) {
20537
- pluginManager.register(plugin);
20538
- }
20539
- }
20540
- const resolvedConfig = await pluginManager.transformConfig(config);
20541
- return resolvedConfig;
20542
- }
20543
- function validateConfig(config) {
20544
- if (!config || typeof config !== "object") {
20545
- throw new Error("Config must be an object");
20546
- }
20547
- const c = config;
20548
- if (c.contracts && !Array.isArray(c.contracts)) {
20549
- throw new Error("Config contracts must be an array");
20550
- }
20551
- if (!c.out || typeof c.out !== "string") {
20552
- throw new Error("Config out must be a string path");
20553
- }
20554
- if (c.contracts) {
20555
- for (const contract of c.contracts) {
20556
- if (!contract.address && !contract.source) {
20557
- throw new Error("Each contract must have either an address or source");
20558
- }
20559
- }
20560
- }
20561
- if (c.plugins && !Array.isArray(c.plugins)) {
20562
- throw new Error("Config plugins must be an array");
20563
- }
20564
- }
20565
- var CONFIG_FILE_NAMES;
20566
- var init_config2 = __esm(() => {
20567
- init_plugin_manager();
20568
- CONFIG_FILE_NAMES = [
20569
- "secondlayer.config.ts",
20570
- "secondlayer.config",
20571
- "secondlayer.config.mjs"
20572
- ];
20573
- });
20574
-
20575
- // src/utils/type-mapping.ts
20531
+ // src/utils/clarity-conversion.ts
20576
20532
  import {
20577
- toCamelCase as toCamelCase3,
20533
+ isAbiBuffer as isAbiBuffer2,
20578
20534
  isAbiList,
20579
- isAbiTuple,
20580
20535
  isAbiOptional as isAbiOptional2,
20581
20536
  isAbiResponse,
20582
- isAbiBuffer as isAbiBuffer2,
20583
20537
  isAbiStringAscii as isAbiStringAscii2,
20584
- isAbiStringUtf8 as isAbiStringUtf82
20585
- } from "@secondlayer/stacks/clarity";
20586
- function clarityTypeToTS(type) {
20587
- if (typeof type === "string") {
20588
- switch (type) {
20589
- case "uint128":
20590
- case "int128":
20591
- return "bigint";
20592
- case "bool":
20593
- return "boolean";
20594
- case "principal":
20595
- case "trait_reference":
20596
- return "string";
20597
- default: {
20598
- const typeStr = type;
20599
- if (typeStr === "none") {
20600
- return "null";
20601
- }
20602
- if (typeStr.includes("string") || typeStr.includes("ascii") || typeStr.includes("utf8")) {
20603
- return "string";
20604
- }
20605
- if (typeStr.includes("buff")) {
20606
- return "Uint8Array | string | { type: 'ascii' | 'utf8' | 'hex'; value: string }";
20607
- }
20608
- if (typeStr.includes("uint") || typeStr.includes("int")) {
20609
- return "bigint";
20610
- }
20611
- return "any";
20612
- }
20613
- }
20614
- }
20615
- if (isAbiBuffer2(type)) {
20616
- return "Uint8Array | string | { type: 'ascii' | 'utf8' | 'hex'; value: string }";
20617
- }
20618
- if (isAbiStringAscii2(type) || isAbiStringUtf82(type)) {
20619
- return "string";
20620
- }
20621
- if (isAbiOptional2(type)) {
20622
- const innerType = clarityTypeToTS(type.optional);
20623
- if (innerType.includes(" | ") && !innerType.startsWith("(")) {
20624
- return `(${innerType}) | null`;
20625
- }
20626
- return `${innerType} | null`;
20627
- }
20628
- if (isAbiList(type)) {
20629
- const innerType = clarityTypeToTS(type.list.type);
20630
- if (innerType.includes(" | ") && !innerType.startsWith("(")) {
20631
- return `(${innerType})[]`;
20632
- }
20633
- return `${innerType}[]`;
20634
- }
20635
- if (isAbiTuple(type)) {
20636
- const fields = type.tuple.map((field) => `${toCamelCase3(field.name)}: ${clarityTypeToTS(field.type)}`).join("; ");
20637
- return `{ ${fields} }`;
20638
- }
20639
- if (isAbiResponse(type)) {
20640
- const okType = clarityTypeToTS(type.response.ok);
20641
- const errType = clarityTypeToTS(type.response.error);
20642
- return `{ ok: ${okType} } | { err: ${errType} }`;
20643
- }
20644
- return "any";
20645
- }
20646
- function getTypeForArg(arg) {
20647
- return clarityTypeToTS(arg.type);
20648
- }
20649
- var init_type_mapping = () => {};
20650
-
20651
- // src/utils/clarity-conversion.ts
20652
- import {
20653
- toCamelCase as toCamelCase4,
20654
- isAbiStringAscii as isAbiStringAscii3,
20655
- isAbiStringUtf8 as isAbiStringUtf83,
20656
- isAbiBuffer as isAbiBuffer3,
20657
- isAbiOptional as isAbiOptional3,
20658
- isAbiList as isAbiList2,
20659
- isAbiTuple as isAbiTuple2,
20660
- isAbiResponse as isAbiResponse2
20538
+ isAbiStringUtf8 as isAbiStringUtf82,
20539
+ isAbiTuple,
20540
+ toCamelCase as toCamelCase3
20661
20541
  } from "@secondlayer/stacks/clarity";
20662
20542
  function generateClarityConversion(argName, argType) {
20663
20543
  const type = argType.type;
@@ -20688,13 +20568,13 @@ function generateClarityConversion(argName, argType) {
20688
20568
  return `${argName}`;
20689
20569
  }
20690
20570
  }
20691
- if (isAbiStringAscii3(type)) {
20571
+ if (isAbiStringAscii2(type)) {
20692
20572
  return `Cl.stringAscii(${argName})`;
20693
20573
  }
20694
- if (isAbiStringUtf83(type)) {
20574
+ if (isAbiStringUtf82(type)) {
20695
20575
  return `Cl.stringUtf8(${argName})`;
20696
20576
  }
20697
- if (isAbiBuffer3(type)) {
20577
+ if (isAbiBuffer2(type)) {
20698
20578
  return `(() => {
20699
20579
  const value = ${argName};
20700
20580
  if (value instanceof Uint8Array) {
@@ -20725,13 +20605,13 @@ function generateClarityConversion(argName, argType) {
20725
20605
  throw new Error(\`Invalid buffer value: \${value}\`);
20726
20606
  })()`;
20727
20607
  }
20728
- if (isAbiOptional3(type)) {
20608
+ if (isAbiOptional2(type)) {
20729
20609
  const innerConversion = generateClarityConversion(argName, {
20730
20610
  type: type.optional
20731
20611
  });
20732
20612
  return `${argName} !== null ? Cl.some(${innerConversion.replace(argName, `${argName}`)}) : Cl.none()`;
20733
20613
  }
20734
- if (isAbiList2(type)) {
20614
+ if (isAbiList(type)) {
20735
20615
  const innerConversion = generateClarityConversion("item", {
20736
20616
  type: type.list.type
20737
20617
  });
@@ -20744,11 +20624,11 @@ function generateClarityConversion(argName, argType) {
20744
20624
  return Cl.list(listValue.map(item => ${innerConversion}));
20745
20625
  })()`;
20746
20626
  }
20747
- if (isAbiTuple2(type)) {
20627
+ if (isAbiTuple(type)) {
20748
20628
  const requiredFields = type.tuple.map((f) => f.name);
20749
20629
  const fieldNames = JSON.stringify(requiredFields);
20750
20630
  const fields = type.tuple.map((field) => {
20751
- const camelFieldName = toCamelCase4(field.name);
20631
+ const camelFieldName = toCamelCase3(field.name);
20752
20632
  const fieldConversion = generateClarityConversion(`tupleValue.${camelFieldName}`, { type: field.type });
20753
20633
  return `"${field.name}": ${fieldConversion}`;
20754
20634
  }).join(", ");
@@ -20764,7 +20644,7 @@ function generateClarityConversion(argName, argType) {
20764
20644
  return Cl.tuple({ ${fields} });
20765
20645
  })()`;
20766
20646
  }
20767
- if (isAbiResponse2(type)) {
20647
+ if (isAbiResponse(type)) {
20768
20648
  const okConversion = generateClarityConversion(`responseValue.ok`, {
20769
20649
  type: type.response.ok
20770
20650
  });
@@ -20788,8 +20668,87 @@ function generateClarityConversion(argName, argType) {
20788
20668
  }
20789
20669
  var init_clarity_conversion = () => {};
20790
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
+
20791
20747
  // src/utils/generator-helpers.ts
20792
- 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";
20793
20752
  function generateMapKeyConversion(keyType) {
20794
20753
  if (isAbiTuple3(keyType)) {
20795
20754
  const fields = keyType.tuple.map((field) => {
@@ -20802,8 +20761,8 @@ function generateMapKeyConversion(keyType) {
20802
20761
  return generateClarityConversion("key", { type: keyType });
20803
20762
  }
20804
20763
  var init_generator_helpers = __esm(() => {
20805
- init_type_mapping();
20806
20764
  init_clarity_conversion();
20765
+ init_type_mapping();
20807
20766
  });
20808
20767
 
20809
20768
  // src/generators/contract.ts
@@ -21104,10 +21063,115 @@ function generateConstantsObject(variables, address, contractName) {
21104
21063
  }`;
21105
21064
  }
21106
21065
  var init_contract = __esm(() => {
21107
- init_format();
21108
- init_type_mapping();
21109
21066
  init_clarity_conversion();
21067
+ init_format();
21110
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
+ ];
21111
21175
  });
21112
21176
 
21113
21177
  // ../../node_modules/ansis/index.cjs
@@ -32229,8 +32293,8 @@ async function checkBaseDependencies(targetDir) {
32229
32293
  var BASE_DEPENDENCIES;
32230
32294
  var init_dependencies = __esm(() => {
32231
32295
  init_dist4();
32232
- init_execa();
32233
32296
  init_source4();
32297
+ init_execa();
32234
32298
  BASE_DEPENDENCIES = {
32235
32299
  dependencies: ["@secondlayer/stacks"],
32236
32300
  devDependencies: []
@@ -32245,8 +32309,8 @@ __export(exports_generate, {
32245
32309
  generate: () => generate
32246
32310
  });
32247
32311
  import path10 from "path";
32248
- import { toCamelCase as toCamelCase7 } from "@secondlayer/stacks/clarity";
32249
32312
  import { getErrorMessage as getErrorMessage2 } from "@secondlayer/shared";
32313
+ import { toCamelCase as toCamelCase7 } from "@secondlayer/stacks/clarity";
32250
32314
  function isContractAddress(input4) {
32251
32315
  const contractIdPattern = /^(SP|ST|SM|SN)[A-Z0-9]{38,}\.[a-zA-Z][a-zA-Z0-9-]*$/;
32252
32316
  return contractIdPattern.test(input4);
@@ -32475,11 +32539,11 @@ async function resolveContracts(source, defaultNetwork, apiKey, apiUrl) {
32475
32539
  var import_fast_glob, DEFAULT_DEVNET_ADDRESS = "ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM";
32476
32540
  var init_generate = __esm(() => {
32477
32541
  init_source4();
32478
- init_config2();
32479
- init_api();
32480
- init_clarity();
32481
- init_contract();
32482
32542
  init_plugin_manager();
32543
+ init_contract();
32544
+ init_clarity();
32545
+ init_api();
32546
+ init_config2();
32483
32547
  init_dependencies();
32484
32548
  import_fast_glob = __toESM(require_out4(), 1);
32485
32549
  });
@@ -32552,7 +32616,7 @@ var {
32552
32616
  // package.json
32553
32617
  var package_default = {
32554
32618
  name: "@secondlayer/cli",
32555
- version: "1.6.8",
32619
+ version: "1.8.0",
32556
32620
  description: "CLI for streams, subgraphs, and real-time blockchain indexing on Stacks",
32557
32621
  type: "module",
32558
32622
  bin: {
@@ -32593,10 +32657,10 @@ var package_default = {
32593
32657
  license: "MIT",
32594
32658
  dependencies: {
32595
32659
  "@inquirer/prompts": "^8.2.0",
32596
- "@secondlayer/sdk": "^0.6.4",
32597
- "@secondlayer/shared": "^0.7.0",
32660
+ "@secondlayer/sdk": "^0.8.0",
32661
+ "@secondlayer/shared": "^0.8.0",
32598
32662
  "@secondlayer/stacks": "^0.2.2",
32599
- "@secondlayer/subgraphs": "^0.5.7",
32663
+ "@secondlayer/subgraphs": "^0.7.0",
32600
32664
  "@biomejs/js-api": "^0.7.0",
32601
32665
  "@biomejs/wasm-nodejs": "^1.9.0",
32602
32666
  esbuild: "^0.19.0",
@@ -32984,10 +33048,10 @@ async function validateDatabaseConnection(url) {
32984
33048
  }
32985
33049
  }
32986
33050
  // src/commands/setup.ts
32987
- init_output();
32988
33051
  init_api_client();
32989
- import { select as select2, input, confirm } from "@inquirer/prompts";
32990
33052
  init_config();
33053
+ import { confirm, input, select as select2 } from "@inquirer/prompts";
33054
+ init_output();
32991
33055
  var STREAMS_DIR = "streams";
32992
33056
  function registerSetupCommand(program2) {
32993
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) => {
@@ -33050,9 +33114,15 @@ async function runWizard() {
33050
33114
  const network = await select2({
33051
33115
  message: "How do you want to use Stacks Streams?",
33052
33116
  choices: [
33053
- { name: "Hosted mainnet (recommended — zero setup)", value: "mainnet" },
33117
+ {
33118
+ name: "Hosted mainnet (recommended — zero setup)",
33119
+ value: "mainnet"
33120
+ },
33054
33121
  { name: "Hosted testnet", value: "testnet" },
33055
- { name: "Local development (run your own node + services)", value: "local" }
33122
+ {
33123
+ name: "Local development (run your own node + services)",
33124
+ value: "local"
33125
+ }
33056
33126
  ]
33057
33127
  });
33058
33128
  config.network = network;
@@ -33262,14 +33332,22 @@ async function hostedLogin(config) {
33262
33332
  }
33263
33333
  const result = await verifyRes.json();
33264
33334
  const { hostname } = await import("node:os");
33265
- const sessionHeaders = { Authorization: `Bearer ${result.sessionToken}`, "Content-Type": "application/json" };
33335
+ const sessionHeaders = {
33336
+ Authorization: `Bearer ${result.sessionToken}`,
33337
+ "Content-Type": "application/json"
33338
+ };
33266
33339
  const keyName = `cli-${hostname().toLowerCase()}`;
33267
- const listRes = await fetch(`${apiUrl}/api/keys`, { headers: sessionHeaders });
33340
+ const listRes = await fetch(`${apiUrl}/api/keys`, {
33341
+ headers: sessionHeaders
33342
+ });
33268
33343
  if (listRes.ok) {
33269
33344
  const { keys } = await listRes.json();
33270
33345
  const existing = keys.find((k) => k.name === keyName && k.status === "active");
33271
33346
  if (existing) {
33272
- 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
+ });
33273
33351
  }
33274
33352
  }
33275
33353
  const createRes = await fetch(`${apiUrl}/api/keys`, {
@@ -33287,7 +33365,10 @@ async function hostedLogin(config) {
33287
33365
  config.apiKey = key;
33288
33366
  await saveConfig(config);
33289
33367
  try {
33290
- await fetch(`${apiUrl}/api/auth/logout`, { method: "POST", headers: sessionHeaders });
33368
+ await fetch(`${apiUrl}/api/auth/logout`, {
33369
+ method: "POST",
33370
+ headers: sessionHeaders
33371
+ });
33291
33372
  } catch {}
33292
33373
  const account = result.account;
33293
33374
  console.log();
@@ -33355,97 +33436,28 @@ function printNodeInfo(node) {
33355
33436
  console.log(` network: ${node.network}, source: ${source}
33356
33437
  `);
33357
33438
  }
33358
- // src/commands/new.ts
33359
- import { join as join3 } from "node:path";
33360
-
33361
- // src/templates/stream.ts
33362
- function generateStreamTemplate(name, endpointUrl) {
33363
- return {
33364
- name,
33365
- endpointUrl: endpointUrl || "https://example.com/endpoint",
33366
- filters: [
33367
- {
33368
- type: "contract_call",
33369
- contractId: "SP000000000000000000002Q6VF78.pox-4"
33370
- }
33371
- ],
33372
- options: {
33373
- decodeClarityValues: true,
33374
- includeRawTx: false,
33375
- includeBlockMetadata: true,
33376
- rateLimit: 10,
33377
- timeoutMs: 1e4,
33378
- maxRetries: 3
33379
- }
33380
- };
33381
- }
33382
-
33383
- // src/commands/new.ts
33384
- init_output();
33385
- init_config();
33386
- init_fs();
33387
- var STREAMS_DIR2 = "streams";
33388
- function registerNewCommand(program2) {
33389
- 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) => {
33390
- try {
33391
- const config = await loadConfig();
33392
- const outputPath = options.output || join3(STREAMS_DIR2, `${name}.json`);
33393
- if (await fileExists(outputPath)) {
33394
- warn(`File already exists: ${outputPath}`);
33395
- process.exit(1);
33396
- }
33397
- const dir = outputPath.substring(0, outputPath.lastIndexOf("/"));
33398
- if (dir) {
33399
- await ensureDir(dir);
33400
- }
33401
- const template = generateStreamTemplate(name, config.defaultEndpointUrl);
33402
- await writeTextFile(outputPath, JSON.stringify(template, null, 2) + `
33403
- `);
33404
- success(`Created ${outputPath}`);
33405
- if (!config.defaultEndpointUrl) {
33406
- warn("Edit the endpointUrl before registering — it must be a reachable HTTPS endpoint");
33407
- }
33408
- console.log(`
33409
- Edit the file to configure your stream, then run:`);
33410
- console.log(` sl streams register ${outputPath}`);
33411
- } catch (err) {
33412
- error(`Failed to create stream: ${err}`);
33413
- process.exit(1);
33414
- }
33415
- });
33416
- }
33417
-
33418
- // src/commands/list.ts
33439
+ // src/commands/delete.ts
33419
33440
  init_api_client();
33420
33441
  init_output();
33421
- function registerListCommand(program2) {
33422
- 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) => {
33423
33445
  try {
33424
- const { streams, total } = await listStreams({
33425
- status: options.status
33426
- });
33427
- if (options.json) {
33428
- console.log(JSON.stringify(streams, null, 2));
33429
- return;
33430
- }
33431
- if (streams.length === 0) {
33432
- console.log("No streams found");
33433
- 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
+ }
33434
33456
  }
33435
- const rows = streams.map((s) => {
33436
- const statusColor = s.status === "active" ? green : s.status === "failed" ? red : s.status === "paused" ? yellow : dim;
33437
- return [
33438
- s.id.slice(0, 8),
33439
- s.name,
33440
- statusColor(s.status),
33441
- s.totalDeliveries.toString()
33442
- ];
33443
- });
33444
- console.log(formatTable(["ID", "Name", "Status", "Deliveries"], rows));
33445
- console.log(dim(`
33446
- ${total} stream(s) total`));
33457
+ await deleteStream(id);
33458
+ success(`Deleted stream: ${stream.name}`);
33447
33459
  } catch (err) {
33448
- handleApiError(err, "list streams");
33460
+ handleApiError(err, "delete stream");
33449
33461
  }
33450
33462
  });
33451
33463
  }
@@ -33490,280 +33502,44 @@ ${dim("Options:")}`);
33490
33502
  });
33491
33503
  }
33492
33504
 
33493
- // src/commands/register.ts
33494
- init_api_client();
33495
- init_output();
33496
- init_fs();
33497
- import { CreateStreamSchema } from "@secondlayer/shared/schemas";
33498
- function registerRegisterCommand(program2) {
33499
- 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) => {
33500
- try {
33501
- if (!await fileExists(filePath)) {
33502
- error(`File not found: ${filePath}`);
33503
- process.exit(1);
33504
- }
33505
- const content = await readJsonFile(filePath);
33506
- const parsed = CreateStreamSchema.safeParse(content);
33507
- if (!parsed.success) {
33508
- error("Invalid stream configuration:");
33509
- for (const issue of parsed.error.issues) {
33510
- console.log(` - ${issue.path.join(".")}: ${issue.message}`);
33511
- }
33512
- process.exit(1);
33513
- }
33514
- const streamData = parsed.data;
33515
- if (options.update) {
33516
- try {
33517
- const updated = await updateStreamByName(streamData.name, streamData);
33518
- success(`Updated stream: ${updated.name}`);
33519
- console.log(formatKeyValue([
33520
- ["ID", updated.id],
33521
- ["Name", updated.name]
33522
- ]));
33523
- return;
33524
- } catch (err) {
33525
- if (err instanceof ApiError && err.status === 404) {
33526
- warn("Stream not found, creating new...");
33527
- } else {
33528
- throw err;
33529
- }
33530
- }
33531
- }
33532
- const result = await createStream(streamData);
33533
- success(`Registered stream: ${result.stream.name}`);
33534
- console.log(formatKeyValue([
33535
- ["ID", result.stream.id],
33536
- ["Name", result.stream.name],
33537
- ["Signing Secret", result.signingSecret]
33538
- ]));
33539
- console.log(dim(`
33540
- Save the signing secret - it won't be shown again!`));
33541
- } catch (err) {
33542
- handleApiError(err, "register stream");
33543
- }
33544
- });
33545
- }
33546
-
33547
- // src/commands/delete.ts
33548
- init_api_client();
33549
- init_output();
33550
- import { confirm as confirm2 } from "@inquirer/prompts";
33551
- function registerDeleteCommand(program2) {
33552
- program2.command("delete <id>").alias("rm").description("Delete a stream").option("-f, --force", "Skip confirmation prompt").action(async (id, options) => {
33553
- try {
33554
- const stream = await getStream(id);
33555
- if (!options.force) {
33556
- const confirmed = await confirm2({
33557
- message: `Delete stream "${stream.name}" (${stream.id})?`,
33558
- default: false
33559
- });
33560
- if (!confirmed) {
33561
- warn("Aborted");
33562
- return;
33563
- }
33564
- }
33565
- await deleteStream(id);
33566
- success(`Deleted stream: ${stream.name}`);
33567
- } catch (err) {
33568
- handleApiError(err, "delete stream");
33569
- }
33570
- });
33571
- }
33572
-
33573
- // src/commands/set.ts
33574
- init_config();
33505
+ // src/commands/list.ts
33575
33506
  init_api_client();
33576
33507
  init_output();
33577
- function registerSetCommand(program2) {
33578
- 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) => {
33579
33510
  try {
33580
- if (options.retry) {
33581
- if (!id) {
33582
- error("Stream ID is required with --retry");
33583
- process.exit(1);
33584
- }
33585
- 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));
33586
33516
  return;
33587
33517
  }
33588
- if (options.all) {
33589
- if (!state) {
33590
- error("State is required: sl streams set --all <active|paused>");
33591
- process.exit(1);
33592
- }
33593
- await setAllStreams(state, options.wait);
33518
+ if (streams.length === 0) {
33519
+ console.log("No streams found");
33594
33520
  return;
33595
33521
  }
33596
- if (options.option?.length) {
33597
- if (!id) {
33598
- error("Stream ID is required with --option");
33599
- process.exit(1);
33600
- }
33601
- const parsedOptions = parseOptions2(options.option);
33602
- await updateStream(id, { options: parsedOptions });
33603
- success(`Updated stream options: ${Object.entries(parsedOptions).map(([k, v]) => `${k}=${v}`).join(", ")}`);
33604
- if (!state)
33605
- return;
33606
- }
33607
- if (!id || !state) {
33608
- error("Usage: sl streams set <id> <active|disabled>");
33609
- console.log(dim(`
33610
- Examples:`));
33611
- console.log(dim(" sl streams set <id> active"));
33612
- console.log(dim(" sl streams set <id> disabled"));
33613
- console.log(dim(" sl streams set --all paused --wait"));
33614
- console.log(dim(" sl streams set <id> --retry --replay-failed"));
33615
- console.log(dim(" sl streams set <id> --option maxRetries=5"));
33616
- process.exit(1);
33617
- }
33618
- 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`));
33619
33534
  } catch (err) {
33620
- handleApiError(err, "set stream state");
33621
- }
33622
- });
33623
- }
33624
- async function setSingleStream(id, state) {
33625
- switch (state) {
33626
- case "active": {
33627
- const stream = await enableStream(id);
33628
- success(`Enabled stream: ${stream.name} (status: active)`);
33629
- break;
33630
- }
33631
- case "disabled": {
33632
- const stream = await disableStream(id);
33633
- success(`Disabled stream: ${stream.name} (status: inactive)`);
33634
- break;
33635
- }
33636
- default:
33637
- error(`Unknown state: ${state}. Use active, disabled, or paused (with --all)`);
33638
- process.exit(1);
33639
- }
33640
- }
33641
- async function setAllStreams(state, wait) {
33642
- switch (state) {
33643
- case "paused": {
33644
- const result = await pauseAllStreams();
33645
- if (result.paused === 0) {
33646
- info("No active streams to pause");
33647
- return;
33648
- }
33649
- success(`Paused ${result.paused} stream${result.paused === 1 ? "" : "s"}`);
33650
- if (wait) {
33651
- await waitForQueueDrain();
33652
- }
33653
- break;
33654
- }
33655
- case "active": {
33656
- const result = await resumeAllStreams();
33657
- if (result.resumed === 0) {
33658
- info("No paused streams to resume");
33659
- return;
33660
- }
33661
- success(`Resumed ${result.resumed} stream${result.resumed === 1 ? "" : "s"}`);
33662
- break;
33535
+ handleApiError(err, "list streams");
33663
33536
  }
33664
- default:
33665
- error(`Unknown state for --all: ${state}. Use active or paused`);
33666
- process.exit(1);
33667
- }
33668
- }
33669
- async function retryStream(id, replayFailed) {
33670
- const config = await loadConfig();
33671
- const apiUrl = resolveApiUrl(config);
33672
- const getRes = await fetch(`${apiUrl}/api/streams/${id}`, {
33673
- headers: authHeaders(config)
33674
33537
  });
33675
- if (!getRes.ok) {
33676
- const body = await getRes.text();
33677
- throw new Error(parseError2(getRes.status, body));
33678
- }
33679
- const stream = await getRes.json();
33680
- if (stream.status !== "failed") {
33681
- warn(`Stream is not in failed status (current status: ${stream.status})`);
33682
- console.log(dim(`
33683
- Use 'sl streams set <id> active' to enable an inactive stream.`));
33684
- process.exit(1);
33685
- }
33686
- if (stream.errorMessage) {
33687
- console.log(red(`Previous error: ${stream.errorMessage}`));
33688
- console.log("");
33689
- }
33690
- const enableRes = await fetch(`${apiUrl}/streams/${id}/enable`, {
33691
- method: "POST",
33692
- headers: authHeaders(config)
33693
- });
33694
- if (!enableRes.ok) {
33695
- const body = await enableRes.text();
33696
- throw new Error(parseError2(enableRes.status, body));
33697
- }
33698
- success(`Re-enabled stream: ${stream.name}`);
33699
- if (replayFailed) {
33700
- info("Replaying failed deliveries...");
33701
- const replayRes = await fetch(`${apiUrl}/streams/${id}/replay-failed`, {
33702
- method: "POST",
33703
- headers: authHeaders(config)
33704
- });
33705
- if (!replayRes.ok) {
33706
- const body = await replayRes.text();
33707
- warn(`Failed to replay: ${parseError2(replayRes.status, body)}`);
33708
- } else {
33709
- const result = await replayRes.json();
33710
- success(`Enqueued ${result.jobCount} replay jobs`);
33711
- }
33712
- }
33713
- console.log(dim(`
33714
- Monitor with: sl streams logs ` + id + " -f"));
33715
- }
33716
- async function waitForQueueDrain() {
33717
- const POLL_INTERVAL_MS = 1000;
33718
- process.stdout.write(dim("Waiting for jobs to complete..."));
33719
- while (true) {
33720
- const stats = await getQueueStats();
33721
- const active = stats.pending + stats.processing;
33722
- if (active === 0) {
33723
- process.stdout.write(`
33724
- `);
33725
- success("All jobs completed");
33726
- return;
33727
- }
33728
- process.stdout.write(`\r${dim(`Waiting for jobs to complete... ${active} remaining`)}`);
33729
- await Bun.sleep(POLL_INTERVAL_MS);
33730
- }
33731
- }
33732
- function parseOptions2(kvPairs) {
33733
- const result = {};
33734
- for (const kv of kvPairs) {
33735
- const eqIndex = kv.indexOf("=");
33736
- if (eqIndex === -1) {
33737
- throw new Error(`Invalid option format: "${kv}". Use key=value.`);
33738
- }
33739
- const key = kv.slice(0, eqIndex);
33740
- const raw = kv.slice(eqIndex + 1);
33741
- if (raw === "true")
33742
- result[key] = true;
33743
- else if (raw === "false")
33744
- result[key] = false;
33745
- else if (raw !== "" && !isNaN(Number(raw)))
33746
- result[key] = Number(raw);
33747
- else
33748
- result[key] = raw;
33749
- }
33750
- return result;
33751
- }
33752
- function parseError2(status, body) {
33753
- let message = `HTTP ${status}`;
33754
- try {
33755
- const json = JSON.parse(body);
33756
- message = json.error || json.message || message;
33757
- } catch {
33758
- if (body)
33759
- message = body;
33760
- }
33761
- return message;
33762
33538
  }
33763
33539
 
33764
33540
  // src/commands/logs.ts
33765
- init_config();
33766
33541
  init_api_client();
33542
+ init_config();
33767
33543
  init_output();
33768
33544
  function registerLogsCommand(program2) {
33769
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) => {
@@ -33773,7 +33549,7 @@ function registerLogsCommand(program2) {
33773
33549
  if (options.follow) {
33774
33550
  await followLogs(resolveApiUrl(config), fullId, config, options.status);
33775
33551
  } else {
33776
- await showRecentLogs(resolveApiUrl(config), fullId, parseInt(options.limit), config, options.status);
33552
+ await showRecentLogs(resolveApiUrl(config), fullId, Number.parseInt(options.limit), config, options.status);
33777
33553
  }
33778
33554
  } catch (err) {
33779
33555
  handleApiError(err, "get logs");
@@ -33884,9 +33660,123 @@ function printDelivery(d) {
33884
33660
  }
33885
33661
  }
33886
33662
 
33887
- // src/commands/replay.ts
33663
+ // src/commands/new.ts
33888
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
33889
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();
33890
33780
  init_output();
33891
33781
  function registerReplayCommand(program2) {
33892
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) => {
@@ -33904,7 +33794,7 @@ function registerReplayCommand(program2) {
33904
33794
  let fromBlock;
33905
33795
  let toBlock;
33906
33796
  if (options.last) {
33907
- const lastCount = parseInt(options.last);
33797
+ const lastCount = Number.parseInt(options.last);
33908
33798
  if (isNaN(lastCount) || lastCount <= 0) {
33909
33799
  error("--last must be a positive number");
33910
33800
  process.exit(1);
@@ -33925,8 +33815,8 @@ function registerReplayCommand(program2) {
33925
33815
  fromBlock = Math.max(0, toBlock - lastCount + 1);
33926
33816
  info(`Replaying last ${lastCount} blocks (${fromBlock} to ${toBlock})`);
33927
33817
  } else if (options.from && options.to) {
33928
- fromBlock = parseInt(options.from);
33929
- toBlock = parseInt(options.to);
33818
+ fromBlock = Number.parseInt(options.from);
33819
+ toBlock = Number.parseInt(options.to);
33930
33820
  if (isNaN(fromBlock) || fromBlock < 0) {
33931
33821
  error("--from must be a non-negative number");
33932
33822
  process.exit(1);
@@ -33956,7 +33846,7 @@ Examples:`));
33956
33846
  });
33957
33847
  if (!response.ok) {
33958
33848
  const body = await response.text();
33959
- throw new Error(parseError3(response.status, body));
33849
+ throw new Error(parseError2(response.status, body));
33960
33850
  }
33961
33851
  const result = await response.json();
33962
33852
  success("Replay started");
@@ -33975,7 +33865,7 @@ Jobs will be processed by the worker in block order.`));
33975
33865
  });
33976
33866
  }
33977
33867
  async function triggerBlock(config, streamId, blockStr, fixturePath) {
33978
- const blockHeight = parseInt(blockStr);
33868
+ const blockHeight = Number.parseInt(blockStr);
33979
33869
  if (isNaN(blockHeight) || blockHeight < 0) {
33980
33870
  error("Block height must be a non-negative number");
33981
33871
  process.exit(1);
@@ -34006,7 +33896,7 @@ async function triggerBlock(config, streamId, blockStr, fixturePath) {
34006
33896
  });
34007
33897
  if (!response.ok) {
34008
33898
  const body = await response.text();
34009
- throw new Error(parseError3(response.status, body));
33899
+ throw new Error(parseError2(response.status, body));
34010
33900
  }
34011
33901
  const result = await response.json();
34012
33902
  success(`Triggered evaluation for block ${blockHeight}`);
@@ -34016,7 +33906,7 @@ async function triggerBlock(config, streamId, blockStr, fixturePath) {
34016
33906
  ["Block", result.blockHeight.toString()]
34017
33907
  ]));
34018
33908
  }
34019
- function parseError3(status, body) {
33909
+ function parseError2(status, body) {
34020
33910
  let message = `HTTP ${status}`;
34021
33911
  try {
34022
33912
  const json = JSON.parse(body);
@@ -34060,6 +33950,197 @@ Save the signing secret - it won't be shown again!`));
34060
33950
  });
34061
33951
  }
34062
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
+
34063
34144
  // src/commands/streams.ts
34064
34145
  function registerStreamsCommand(program2) {
34065
34146
  const streams = program2.command("streams").description("Manage event streams");
@@ -34074,8 +34155,8 @@ function registerStreamsCommand(program2) {
34074
34155
  registerRotateSecretCommand(streams);
34075
34156
  }
34076
34157
  // src/commands/status.ts
34077
- init_config();
34078
34158
  init_api_client();
34159
+ init_config();
34079
34160
  init_output();
34080
34161
  function registerStatusCommand(program2) {
34081
34162
  program2.command("status").description("Show system status").option("--json", "Output as JSON").action(async (options) => {
@@ -34172,7 +34253,10 @@ function printStatus(status) {
34172
34253
  [" Total", status.streams.total.toString()],
34173
34254
  [" Active", green(status.streams.active.toString())],
34174
34255
  [" Paused", yellow(status.streams.paused.toString())],
34175
- [" 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
+ ]
34176
34260
  ]));
34177
34261
  console.log("");
34178
34262
  if (status.activeSubgraphs !== undefined || status.recentDeliveries !== undefined) {
@@ -34190,19 +34274,22 @@ function printStatus(status) {
34190
34274
  // src/commands/sync.ts
34191
34275
  init_config();
34192
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";
34193
34283
  import { StacksNodeClient } from "@secondlayer/shared/node";
34194
34284
  import { HiroClient } from "@secondlayer/shared/node/hiro-client";
34195
34285
  import { LocalClient } from "@secondlayer/shared/node/local-client";
34196
- import { findGaps, countMissingBlocks } from "@secondlayer/shared/db/queries/integrity";
34197
- import { getDb } from "@secondlayer/shared/db";
34198
- import { confirm as confirm4 } from "@inquirer/prompts";
34199
34286
  var DEV_DATABASE_URL = "postgres://postgres:postgres@localhost:5432/streams_dev";
34200
34287
  function registerSyncCommand(program2) {
34201
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() {
34202
34289
  const opts = this.opts();
34203
34290
  const config = await loadConfig();
34204
34291
  const indexerUrl = process.env.INDEXER_URL || `http://localhost:${config.ports.indexer}`;
34205
- let concurrency = opts.concurrency ? parseInt(opts.concurrency) : 0;
34292
+ let concurrency = opts.concurrency ? Number.parseInt(opts.concurrency) : 0;
34206
34293
  try {
34207
34294
  const nodeClient = new StacksNodeClient;
34208
34295
  const hiroClient = new HiroClient;
@@ -34270,10 +34357,13 @@ Set HIRO_API_URL or check your internet connection.`));
34270
34357
  process.exit(0);
34271
34358
  }
34272
34359
  info(`Found ${gaps.length} gaps, ${missing} missing blocks`);
34273
- ranges = gaps.map((g) => ({ start: g.gapStart, end: g.gapEnd }));
34360
+ ranges = gaps.map((g) => ({
34361
+ start: g.gapStart,
34362
+ end: g.gapEnd
34363
+ }));
34274
34364
  } else if (opts.from && opts.to) {
34275
- const from = parseInt(opts.from);
34276
- const to = parseInt(opts.to);
34365
+ const from = Number.parseInt(opts.from);
34366
+ const to = Number.parseInt(opts.to);
34277
34367
  if (isNaN(from) || from < 0) {
34278
34368
  error("--from must be a non-negative number");
34279
34369
  process.exit(1);
@@ -34313,7 +34403,7 @@ Examples:`));
34313
34403
  process.exit(0);
34314
34404
  }
34315
34405
  }
34316
- const batchDelay = opts.delay ? parseInt(opts.delay) : useHiro ? 500 : 0;
34406
+ const batchDelay = opts.delay ? Number.parseInt(opts.delay) : useHiro ? 500 : 0;
34317
34407
  if (useHiro && batchDelay > 0) {
34318
34408
  info(`Pacing: ${batchDelay}ms delay between batches (concurrency: ${concurrency})`);
34319
34409
  }
@@ -34398,13 +34488,16 @@ Examples:`));
34398
34488
  });
34399
34489
  }
34400
34490
  // src/commands/db.ts
34401
- init_output();
34402
- init_dev_state();
34403
34491
  init_config();
34492
+ init_dev_state();
34493
+ init_output();
34494
+ import { confirm as confirm5 } from "@inquirer/prompts";
34404
34495
  import { getDb as getDb2, sql } from "@secondlayer/shared/db";
34405
- 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";
34406
34500
  import { StacksNodeClient as StacksNodeClient2 } from "@secondlayer/shared/node";
34407
- import { confirm as confirm5 } from "@inquirer/prompts";
34408
34501
  var DEV_DATABASE_URL2 = "postgres://postgres:postgres@localhost:5432/streams_dev";
34409
34502
  function registerDbCommand(program2) {
34410
34503
  const dbCmd = program2.command("db").description("Inspect indexer database tables").hook("preAction", async () => {
@@ -34414,19 +34507,19 @@ function registerDbCommand(program2) {
34414
34507
  });
34415
34508
  dbCmd.command("blocks").description("Show recent blocks").option("--limit <n>", "Number of rows", "10").option("--json", "Output as JSON").action(async function() {
34416
34509
  const opts = this.opts();
34417
- await showBlocks(parseInt(opts.limit), opts.json);
34510
+ await showBlocks(Number.parseInt(opts.limit), opts.json);
34418
34511
  });
34419
34512
  dbCmd.command("txs").description("Show recent transactions").option("--limit <n>", "Number of rows", "10").option("--json", "Output as JSON").action(async function() {
34420
34513
  const opts = this.opts();
34421
- await showTransactions(parseInt(opts.limit), opts.json);
34514
+ await showTransactions(Number.parseInt(opts.limit), opts.json);
34422
34515
  });
34423
34516
  dbCmd.command("events").description("Show recent events").option("--limit <n>", "Number of rows", "10").option("--json", "Output as JSON").action(async function() {
34424
34517
  const opts = this.opts();
34425
- await showEvents(parseInt(opts.limit), opts.json);
34518
+ await showEvents(Number.parseInt(opts.limit), opts.json);
34426
34519
  });
34427
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() {
34428
34521
  const opts = this.opts();
34429
- await showGaps(parseInt(opts.limit), opts.json);
34522
+ await showGaps(Number.parseInt(opts.limit), opts.json);
34430
34523
  });
34431
34524
  dbCmd.command("reset").description("Truncate all indexed data (blocks, txs, events, jobs, deliveries)").option("-y, --yes", "Skip confirmation prompt").action(async function() {
34432
34525
  const opts = this.opts();
@@ -34506,7 +34599,15 @@ async function showBlocks(limit, json) {
34506
34599
  async function showTransactions(limit, json) {
34507
34600
  try {
34508
34601
  const db = ensureDb();
34509
- 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();
34510
34611
  if (json) {
34511
34612
  console.log(JSON.stringify(rows, null, 2));
34512
34613
  process.exit(0);
@@ -34706,7 +34807,10 @@ async function resyncDatabase(skipConfirm, backfill) {
34706
34807
  throw new Error(`Block ${height} not found`);
34707
34808
  const res = await fetch(`${indexerUrl}/new_block`, {
34708
34809
  method: "POST",
34709
- headers: { "Content-Type": "application/json", "X-Source": "backfill" },
34810
+ headers: {
34811
+ "Content-Type": "application/json",
34812
+ "X-Source": "backfill"
34813
+ },
34710
34814
  body: JSON.stringify(block)
34711
34815
  });
34712
34816
  if (!res.ok)
@@ -34737,8 +34841,8 @@ async function resyncDatabase(skipConfirm, backfill) {
34737
34841
  }
34738
34842
  }
34739
34843
  // src/commands/receiver.ts
34740
- init_output();
34741
34844
  init_config();
34845
+ init_output();
34742
34846
  import { join as join5 } from "node:path";
34743
34847
  function registerReceiverCommand(program2) {
34744
34848
  const receiver = program2.command("receiver").description("Receiver development tools").hook("preAction", async () => {
@@ -34747,7 +34851,7 @@ function registerReceiverCommand(program2) {
34747
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) => {
34748
34852
  try {
34749
34853
  const config = await loadConfig();
34750
- const port = parseInt(options.port);
34854
+ const port = Number.parseInt(options.port);
34751
34855
  const network = options.network;
34752
34856
  const dir = join5(process.cwd(), directory);
34753
34857
  const dirExists = await Bun.file(join5(dir, "package.json")).exists();
@@ -35064,65 +35168,8 @@ async function generatePackageJson(dir, name) {
35064
35168
  `);
35065
35169
  }
35066
35170
  // src/commands/subgraphs.ts
35067
- init_output();
35068
- import { resolve } from "node:path";
35069
35171
  import { existsSync, mkdirSync, watch } from "node:fs";
35070
-
35071
- // src/templates/subgraph.ts
35072
- function generateSubgraphTemplate(name) {
35073
- return `import { defineSubgraph } from "@secondlayer/subgraphs";
35074
-
35075
- export default defineSubgraph({
35076
- name: "${name}",
35077
- version: "1.0.0",
35078
- description: "TODO: describe what this subgraph tracks",
35079
-
35080
- // Sources define what blockchain data this subgraph processes.
35081
- // Each source filters transactions/events by contract, type, function, or event.
35082
- // Examples:
35083
- // { contract: "SP000...::my-contract" } — all txs to a contract
35084
- // { contract: "SP000...::my-contract", event: "transfer" } — specific event
35085
- // { type: "stx_transfer", minAmount: 1000000n } — STX transfers >= 1 STX
35086
- // { contract: "*.pox-*" } — wildcard contract match
35087
- sources: [
35088
- { contract: "SP000000000000000000002Q6VF78.pox-4" },
35089
- ],
35090
-
35091
- // Schema defines the tables this subgraph creates.
35092
- // Each table gets auto-columns: _id, _block_height, _tx_id, _created_at.
35093
- // Column types: text, uint, int, principal, boolean, timestamp, jsonb
35094
- schema: {
35095
- data: {
35096
- columns: {
35097
- sender: { type: "principal", indexed: true },
35098
- amount: { type: "uint" },
35099
- memo: { type: "text", nullable: true },
35100
- },
35101
- // Optional composite indexes
35102
- // indexes: [["sender", "amount"]],
35103
- },
35104
- },
35105
-
35106
- // Handlers process matched events and write to your tables via the context.
35107
- // Keys match source patterns (use sourceKey format), or "*" as catch-all.
35108
- // Context methods: ctx.insert(), ctx.update(), ctx.delete()
35109
- handlers: {
35110
- "*": async (event, ctx) => {
35111
- await ctx.insert("data", {
35112
- sender: event.sender ?? event.tx?.sender,
35113
- amount: event.amount ?? 0,
35114
- memo: event.memo ?? null,
35115
- });
35116
- },
35117
- },
35118
- });
35119
- `;
35120
- }
35121
-
35122
- // src/commands/subgraphs.ts
35123
- init_api_client();
35124
- init_config();
35125
- init_fs();
35172
+ import { resolve } from "node:path";
35126
35173
 
35127
35174
  // src/generators/subgraph-scaffold.ts
35128
35175
  init_format();
@@ -35130,9 +35177,9 @@ init_format();
35130
35177
  // src/generators/clarity-to-subgraph.ts
35131
35178
  import {
35132
35179
  isAbiBuffer,
35180
+ isAbiOptional,
35133
35181
  isAbiStringAscii,
35134
- isAbiStringUtf8,
35135
- isAbiOptional
35182
+ isAbiStringUtf8
35136
35183
  } from "@secondlayer/stacks/clarity";
35137
35184
  function clarityTypeToSubgraphColumn(abiType) {
35138
35185
  return mapType(abiType, false);
@@ -35227,9 +35274,6 @@ ${handlersBlock}
35227
35274
  return formatCode(code);
35228
35275
  }
35229
35276
 
35230
- // src/generators/subgraphs.ts
35231
- init_format();
35232
-
35233
35277
  // src/utils/case-conversion.ts
35234
35278
  import { toCamelCase } from "@secondlayer/stacks/clarity";
35235
35279
  function capitalize(str) {
@@ -35240,6 +35284,7 @@ function toPascalCase(str) {
35240
35284
  }
35241
35285
 
35242
35286
  // src/generators/subgraphs.ts
35287
+ init_format();
35243
35288
  async function generateSubgraphConsumer(subgraphName, detail) {
35244
35289
  const tables = Object.entries(detail.tables);
35245
35290
  const rowInterfaces = tables.map(([tableName, tableDef]) => {
@@ -35313,8 +35358,65 @@ function subgraphTypeToTS(type) {
35313
35358
  }
35314
35359
 
35315
35360
  // src/commands/subgraphs.ts
35316
- init_api();
35361
+ init_api_client();
35362
+ init_config();
35363
+ init_fs();
35364
+ init_output();
35317
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();
35318
35420
  function registerSubgraphsCommand(program2) {
35319
35421
  const subgraphs = program2.command("subgraphs").description("Manage materialized subgraphs");
35320
35422
  subgraphs.command("new <name>").description("Scaffold a new subgraph definition file").action(async (name) => {
@@ -35352,7 +35454,9 @@ function registerSubgraphsCommand(program2) {
35352
35454
  const { getDb: getDb3 } = await import("@secondlayer/shared/db");
35353
35455
  validateSubgraphDefinition(def);
35354
35456
  const db = getDb3();
35355
- const result = await deploySchema(db, def, absPath, { forceReindex: false });
35457
+ const result = await deploySchema(db, def, absPath, {
35458
+ forceReindex: false
35459
+ });
35356
35460
  if (result.action === "unchanged") {
35357
35461
  info(`[${new Date().toLocaleTimeString()}] No schema changes`);
35358
35462
  } else if (result.action === "created") {
@@ -35431,7 +35535,9 @@ Stopped watching.`);
35431
35535
  const { deploySchema } = await import("@secondlayer/subgraphs");
35432
35536
  const { getDb: getDb3, closeDb } = await import("@secondlayer/shared/db");
35433
35537
  const db = getDb3();
35434
- const result = await deploySchema(db, def, absPath, { forceReindex: options2.reindex });
35538
+ const result = await deploySchema(db, def, absPath, {
35539
+ forceReindex: options2.reindex
35540
+ });
35435
35541
  if (result.action === "unchanged") {
35436
35542
  info(`Subgraph "${def.name}" is up to date (no schema changes)`);
35437
35543
  } else if (result.action === "created") {
@@ -35482,10 +35588,19 @@ ${data.length} subgraph(s) total`));
35482
35588
  const subgraph = await getSubgraphApi(name);
35483
35589
  const rowCounts = Object.entries(subgraph.tables).map(([t, info2]) => `${t}: ${info2.rowCount}`).join(", ") || "N/A";
35484
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";
35485
35596
  console.log(formatKeyValue([
35486
35597
  ["Name", subgraph.name],
35487
35598
  ["Version", subgraph.version],
35488
35599
  ["Status", subgraph.status],
35600
+ ["Sync", syncStatus],
35601
+ ["Blocks Remaining", blocksRemaining],
35602
+ ["Integrity", integrity],
35603
+ ["Gaps", gapSummary],
35489
35604
  ["Last Block", String(subgraph.lastProcessedBlock)],
35490
35605
  ["Row Count", rowCounts],
35491
35606
  ["Total Processed", String(subgraph.health.totalProcessed)],
@@ -35496,6 +35611,20 @@ ${data.length} subgraph(s) total`));
35496
35611
  ["Created", subgraph.createdAt],
35497
35612
  ["Updated", subgraph.updatedAt]
35498
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
+ }
35499
35628
  const tableEntries = Object.entries(subgraph.tables);
35500
35629
  if (tableEntries.length > 0) {
35501
35630
  console.log(dim(`
@@ -35512,8 +35641,8 @@ Table endpoints:`));
35512
35641
  try {
35513
35642
  info(`Reindexing subgraph "${name}"...`);
35514
35643
  const result = await reindexSubgraphApi(name, {
35515
- fromBlock: options2.from ? parseInt(options2.from, 10) : undefined,
35516
- 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
35517
35646
  });
35518
35647
  success(result.message);
35519
35648
  info(`From block ${result.fromBlock} to ${result.toBlock}`);
@@ -35521,6 +35650,51 @@ Table endpoints:`));
35521
35650
  handleApiError(err, "reindex subgraph");
35522
35651
  }
35523
35652
  });
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) => {
35654
+ try {
35655
+ const fromBlock = Number.parseInt(options2.from, 10);
35656
+ const toBlock = Number.parseInt(options2.to, 10);
35657
+ if (isNaN(fromBlock) || isNaN(toBlock)) {
35658
+ error("--from and --to must be valid block numbers");
35659
+ process.exit(1);
35660
+ }
35661
+ info(`Backfilling subgraph "${name}" from block ${fromBlock} to ${toBlock}...`);
35662
+ const result = await backfillSubgraphApi(name, { fromBlock, toBlock });
35663
+ success(result.message);
35664
+ info(`From block ${result.fromBlock} to ${result.toBlock}`);
35665
+ } catch (err) {
35666
+ handleApiError(err, "backfill subgraph");
35667
+ }
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
+ });
35524
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) => {
35525
35699
  try {
35526
35700
  const filters = {};
@@ -35537,8 +35711,8 @@ Table endpoints:`));
35537
35711
  const params = {
35538
35712
  sort: options2.sort,
35539
35713
  order: options2.sort ? options2.order : undefined,
35540
- limit: parseInt(options2.limit, 10),
35541
- 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,
35542
35716
  fields: options2.fields,
35543
35717
  filters: Object.keys(filters).length > 0 ? filters : undefined
35544
35718
  };
@@ -35646,10 +35820,10 @@ ${rows.length} row(s)`));
35646
35820
  }
35647
35821
  // src/commands/stack.ts
35648
35822
  init_config();
35649
- init_network();
35650
- init_node_manager();
35651
35823
  init_dev_state();
35652
35824
  init_docker();
35825
+ init_network();
35826
+ init_node_manager();
35653
35827
  init_output();
35654
35828
  function registerStackCommand(program2) {
35655
35829
  const stack = program2.command("stack").description("Manage the full stack");
@@ -35743,7 +35917,13 @@ async function stackStart(options2) {
35743
35917
  if (await isDevRunning()) {
35744
35918
  info("Dev services already running");
35745
35919
  } else {
35746
- 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
+ ];
35747
35927
  if (config.node?.installPath) {
35748
35928
  args.push("--stacks-node");
35749
35929
  }
@@ -35790,7 +35970,13 @@ async function stackStop(options2) {
35790
35970
  if (options2.dev) {
35791
35971
  if (await isDevRunning()) {
35792
35972
  info("Stopping dev services...");
35793
- 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" });
35794
35980
  await proc.exited;
35795
35981
  } else {
35796
35982
  info("Dev services not running");
@@ -35834,11 +36020,15 @@ async function stackStop(options2) {
35834
36020
  success("Stack stopped");
35835
36021
  console.log("");
35836
36022
  }
36023
+ // src/commands/doctor.ts
36024
+ init_api_client();
36025
+ init_config();
36026
+
35837
36027
  // src/lib/health.ts
35838
- init_dev_state();
35839
- init_node_manager();
35840
36028
  init_config();
36029
+ init_dev_state();
35841
36030
  init_network();
36031
+ init_node_manager();
35842
36032
  async function checkHealth() {
35843
36033
  const config = await loadConfig();
35844
36034
  const issues = [];
@@ -35906,7 +36096,7 @@ async function checkHealth() {
35906
36096
  try {
35907
36097
  const inspect2 = await Bun.$`docker inspect --format={{.RestartCount}} ${name}`.quiet().nothrow();
35908
36098
  if (inspect2.exitCode === 0) {
35909
- restartCount = parseInt(inspect2.stdout.toString().trim()) || 0;
36099
+ restartCount = Number.parseInt(inspect2.stdout.toString().trim()) || 0;
35910
36100
  }
35911
36101
  } catch {}
35912
36102
  containers.push({ name, status, health, restartCount });
@@ -35975,8 +36165,6 @@ async function checkHealth() {
35975
36165
  }
35976
36166
 
35977
36167
  // src/commands/doctor.ts
35978
- init_config();
35979
- init_api_client();
35980
36168
  init_network();
35981
36169
  init_output();
35982
36170
  function registerDoctorCommand(program2) {
@@ -35997,7 +36185,9 @@ async function runHostedDoctor(jsonOutput) {
35997
36185
  let apiHealthy = false;
35998
36186
  let statusData = null;
35999
36187
  try {
36000
- const res = await fetch(`${apiUrl}/status`, { headers: authHeaders(config) });
36188
+ const res = await fetch(`${apiUrl}/status`, {
36189
+ headers: authHeaders(config)
36190
+ });
36001
36191
  apiHealthy = res.ok;
36002
36192
  if (res.ok) {
36003
36193
  statusData = await res.json();
@@ -36013,7 +36203,9 @@ async function runHostedDoctor(jsonOutput) {
36013
36203
  let authOk = false;
36014
36204
  let account = null;
36015
36205
  try {
36016
- 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
+ });
36017
36209
  if (res.ok) {
36018
36210
  authOk = true;
36019
36211
  account = await res.json();
@@ -36097,7 +36289,10 @@ async function runLocalDoctor(jsonOutput) {
36097
36289
  console.log(blue("Stack"));
36098
36290
  console.log(formatKeyValue([
36099
36291
  [" Network", network ?? dim("not configured")],
36100
- [" 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
+ ]
36101
36296
  ]));
36102
36297
  console.log("");
36103
36298
  console.log(blue("Node"));
@@ -36107,7 +36302,10 @@ async function runLocalDoctor(jsonOutput) {
36107
36302
  console.log(formatKeyValue([
36108
36303
  [" Height", `${heightStr} ${burnStr}`],
36109
36304
  [" Peers", report.node.peers.toString()],
36110
- [" Status", report.node.height ? green("syncing") : yellow("starting")],
36305
+ [
36306
+ " Status",
36307
+ report.node.height ? green("syncing") : yellow("starting")
36308
+ ],
36111
36309
  [" Version", report.node.version ?? dim("unknown")]
36112
36310
  ]));
36113
36311
  } else {
@@ -36189,8 +36387,8 @@ async function runLocalDoctor(jsonOutput) {
36189
36387
  console.log("");
36190
36388
  }
36191
36389
  // src/commands/auth.ts
36192
- init_config();
36193
36390
  init_api_client();
36391
+ init_config();
36194
36392
  init_output();
36195
36393
  import { hostname } from "node:os";
36196
36394
  import { input as input2 } from "@inquirer/prompts";
@@ -36226,14 +36424,22 @@ function registerAuthCommand(program2) {
36226
36424
  });
36227
36425
  await assertOk(verifyRes);
36228
36426
  const result = await verifyRes.json();
36229
- const sessionHeaders = { Authorization: `Bearer ${result.sessionToken}`, "Content-Type": "application/json" };
36427
+ const sessionHeaders = {
36428
+ Authorization: `Bearer ${result.sessionToken}`,
36429
+ "Content-Type": "application/json"
36430
+ };
36230
36431
  const keyName = `cli-${hostname().toLowerCase()}`;
36231
- const listRes = await fetch(`${apiUrl}/api/keys`, { headers: sessionHeaders });
36432
+ const listRes = await fetch(`${apiUrl}/api/keys`, {
36433
+ headers: sessionHeaders
36434
+ });
36232
36435
  if (listRes.ok) {
36233
36436
  const { keys: keys2 } = await listRes.json();
36234
36437
  const existing = keys2.find((k) => k.name === keyName && k.status === "active");
36235
36438
  if (existing) {
36236
- 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
+ });
36237
36443
  }
36238
36444
  }
36239
36445
  const createRes = await fetch(`${apiUrl}/api/keys`, {
@@ -36246,7 +36452,10 @@ function registerAuthCommand(program2) {
36246
36452
  config.apiKey = key;
36247
36453
  await saveConfig(config);
36248
36454
  try {
36249
- await fetch(`${apiUrl}/api/auth/logout`, { method: "POST", headers: sessionHeaders });
36455
+ await fetch(`${apiUrl}/api/auth/logout`, {
36456
+ method: "POST",
36457
+ headers: sessionHeaders
36458
+ });
36250
36459
  } catch {}
36251
36460
  success(`Authenticated as ${result.account.email}`);
36252
36461
  console.log(dim(`Key: ${prefix}...`));
@@ -36272,7 +36481,10 @@ function registerAuthCommand(program2) {
36272
36481
  const currentPrefix = config.apiKey.slice(0, 14);
36273
36482
  const match = keys2.find((k) => currentPrefix.startsWith(k.prefix));
36274
36483
  if (match) {
36275
- await fetch(`${apiUrl}/api/keys/${match.id}`, { method: "DELETE", headers });
36484
+ await fetch(`${apiUrl}/api/keys/${match.id}`, {
36485
+ method: "DELETE",
36486
+ headers
36487
+ });
36276
36488
  }
36277
36489
  }
36278
36490
  } catch {}
@@ -36286,7 +36498,10 @@ function registerAuthCommand(program2) {
36286
36498
  const pairs = [
36287
36499
  ["Network", config.network],
36288
36500
  ["API", apiUrl || "(not configured)"],
36289
- ["API Key", config.apiKey ? config.apiKey.slice(0, 14) + "..." : "(none)"]
36501
+ [
36502
+ "API Key",
36503
+ config.apiKey ? config.apiKey.slice(0, 14) + "..." : "(none)"
36504
+ ]
36290
36505
  ];
36291
36506
  if (config.apiKey && apiUrl) {
36292
36507
  try {
@@ -36328,7 +36543,10 @@ function registerAuthCommand(program2) {
36328
36543
  keys.command("create").description("Create a new API key").option("--name <name>", "Name for the API key").action(async (options2) => {
36329
36544
  const config = await loadConfig();
36330
36545
  const apiUrl = resolveApiUrl(config);
36331
- const headers = { ...authHeaders(config), "Content-Type": "application/json" };
36546
+ const headers = {
36547
+ ...authHeaders(config),
36548
+ "Content-Type": "application/json"
36549
+ };
36332
36550
  try {
36333
36551
  const res = await fetch(`${apiUrl}/api/keys`, {
36334
36552
  method: "POST",
@@ -36428,7 +36646,13 @@ init_config();
36428
36646
  init_dev_state();
36429
36647
  init_node_manager();
36430
36648
  init_output();
36431
- var DEV_SERVICES = ["api", "indexer", "worker", "receiver", "subgraphs"];
36649
+ var DEV_SERVICES = [
36650
+ "api",
36651
+ "indexer",
36652
+ "worker",
36653
+ "receiver",
36654
+ "subgraphs"
36655
+ ];
36432
36656
  function registerLocalCommand(program2) {
36433
36657
  const local = program2.command("local").description("Manage local development environment and Stacks node").hook("preAction", async (_thisCommand, actionCommand) => {
36434
36658
  if (actionCommand.name() === "help")
@@ -36491,7 +36715,7 @@ function registerLocalCommand(program2) {
36491
36715
  });
36492
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) => {
36493
36717
  const { showConfigCheck: showConfigCheck2 } = await Promise.resolve().then(() => (init_node_impl(), exports_node_impl));
36494
- await showConfigCheck2(parseInt(options2.indexerPort));
36718
+ await showConfigCheck2(Number.parseInt(options2.indexerPort));
36495
36719
  });
36496
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) => {
36497
36721
  await showLocalLogs({ ...options2, service: "node" });
@@ -36538,13 +36762,18 @@ var serviceColors2 = {
36538
36762
  node: red
36539
36763
  };
36540
36764
  async function showLocalLogs(options2) {
36541
- const lines = parseInt(options2.lines);
36765
+ const lines = Number.parseInt(options2.lines);
36542
36766
  const service = options2.service?.toLowerCase();
36543
36767
  const showDev = !service || service === "dev" || DEV_SERVICES.includes(service);
36544
36768
  const showNode = !service || service === "node";
36545
36769
  if (service && DEV_SERVICES.includes(service)) {
36546
36770
  const { showLogs: showLogs2 } = await Promise.resolve().then(() => (init_dev_impl(), exports_dev_impl));
36547
- 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
+ });
36548
36777
  return;
36549
36778
  }
36550
36779
  if (service === "node") {
@@ -36565,7 +36794,7 @@ async function showNodeLogs(options2) {
36565
36794
  }
36566
36795
  const logs = await getNodeLogs({
36567
36796
  follow: options2.follow,
36568
- lines: parseInt(options2.lines),
36797
+ lines: Number.parseInt(options2.lines),
36569
36798
  quiet: options2.quiet,
36570
36799
  format: true
36571
36800
  });
@@ -36710,8 +36939,8 @@ function formatDeliverySummary2(jsonStr) {
36710
36939
  }
36711
36940
  }
36712
36941
  // src/commands/whoami.ts
36713
- init_config();
36714
36942
  init_api_client();
36943
+ init_config();
36715
36944
  init_output();
36716
36945
  function registerWhoamiCommand(program2) {
36717
36946
  program2.command("whoami").description("Show current authenticated account").action(async () => {
@@ -36783,5 +37012,5 @@ registerWhoamiCommand(program);
36783
37012
  registerReceiverCommand(program);
36784
37013
  program.parse();
36785
37014
 
36786
- //# debugId=60C3F60FD3E0B1F064756E2164756E21
37015
+ //# debugId=CCE78279B6461B3364756E2164756E21
36787
37016
  //# sourceMappingURL=cli.js.map