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