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