@secondlayer/cli 4.0.1 → 5.0.1
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/README.md +2 -2
- package/dist/cli.js +447 -111
- package/dist/cli.js.map +8 -7
- package/package.json +6 -6
package/dist/cli.js
CHANGED
|
@@ -9549,23 +9549,23 @@ var require_client_request = __commonJS((exports, module) => {
|
|
|
9549
9549
|
var kPendingAgentPromise = Symbol("pendingAgentPromise");
|
|
9550
9550
|
|
|
9551
9551
|
class ClientRequest extends Writable {
|
|
9552
|
-
constructor(
|
|
9552
|
+
constructor(input3, options, callback) {
|
|
9553
9553
|
super({
|
|
9554
9554
|
autoDestroy: false,
|
|
9555
9555
|
emitClose: false
|
|
9556
9556
|
});
|
|
9557
|
-
if (typeof
|
|
9558
|
-
|
|
9559
|
-
} else if (
|
|
9560
|
-
|
|
9557
|
+
if (typeof input3 === "string") {
|
|
9558
|
+
input3 = urlToHttpOptions(new URL2(input3));
|
|
9559
|
+
} else if (input3 instanceof URL2) {
|
|
9560
|
+
input3 = urlToHttpOptions(input3);
|
|
9561
9561
|
} else {
|
|
9562
|
-
|
|
9562
|
+
input3 = { ...input3 };
|
|
9563
9563
|
}
|
|
9564
9564
|
if (typeof options === "function" || options === undefined) {
|
|
9565
9565
|
callback = options;
|
|
9566
|
-
options =
|
|
9566
|
+
options = input3;
|
|
9567
9567
|
} else {
|
|
9568
|
-
options = Object.assign(
|
|
9568
|
+
options = Object.assign(input3, options);
|
|
9569
9569
|
}
|
|
9570
9570
|
if (options.h2session) {
|
|
9571
9571
|
this[kSession] = options.h2session;
|
|
@@ -10060,19 +10060,19 @@ var require_auto = __commonJS((exports, module) => {
|
|
|
10060
10060
|
};
|
|
10061
10061
|
};
|
|
10062
10062
|
var defaultResolveProtocol = createResolveProtocol(cache, queue);
|
|
10063
|
-
module.exports = async (
|
|
10064
|
-
if (typeof
|
|
10065
|
-
|
|
10066
|
-
} else if (
|
|
10067
|
-
|
|
10063
|
+
module.exports = async (input3, options, callback) => {
|
|
10064
|
+
if (typeof input3 === "string") {
|
|
10065
|
+
input3 = urlToHttpOptions(new URL2(input3));
|
|
10066
|
+
} else if (input3 instanceof URL2) {
|
|
10067
|
+
input3 = urlToHttpOptions(input3);
|
|
10068
10068
|
} else {
|
|
10069
|
-
|
|
10069
|
+
input3 = { ...input3 };
|
|
10070
10070
|
}
|
|
10071
10071
|
if (typeof options === "function" || options === undefined) {
|
|
10072
10072
|
callback = options;
|
|
10073
|
-
options =
|
|
10073
|
+
options = input3;
|
|
10074
10074
|
} else {
|
|
10075
|
-
options = Object.assign(
|
|
10075
|
+
options = Object.assign(input3, options);
|
|
10076
10076
|
}
|
|
10077
10077
|
options.ALPNProtocols = options.ALPNProtocols || ["h2", "http/1.1"];
|
|
10078
10078
|
if (!Array.isArray(options.ALPNProtocols) || options.ALPNProtocols.length === 0) {
|
|
@@ -10785,7 +10785,7 @@ var init_options = __esm(() => {
|
|
|
10785
10785
|
enableUnixSockets: false
|
|
10786
10786
|
};
|
|
10787
10787
|
Options = class Options {
|
|
10788
|
-
constructor(
|
|
10788
|
+
constructor(input3, options, defaults) {
|
|
10789
10789
|
Object.defineProperty(this, "_unixOptions", {
|
|
10790
10790
|
enumerable: true,
|
|
10791
10791
|
configurable: true,
|
|
@@ -10810,10 +10810,10 @@ var init_options = __esm(() => {
|
|
|
10810
10810
|
writable: true,
|
|
10811
10811
|
value: undefined
|
|
10812
10812
|
});
|
|
10813
|
-
assert.any([dist_default.string, dist_default.urlInstance, dist_default.object, dist_default.undefined],
|
|
10813
|
+
assert.any([dist_default.string, dist_default.urlInstance, dist_default.object, dist_default.undefined], input3);
|
|
10814
10814
|
assert.any([dist_default.object, dist_default.undefined], options);
|
|
10815
10815
|
assert.any([dist_default.object, dist_default.undefined], defaults);
|
|
10816
|
-
if (
|
|
10816
|
+
if (input3 instanceof Options || options instanceof Options) {
|
|
10817
10817
|
throw new TypeError("The defaults must be passed as the third argument");
|
|
10818
10818
|
}
|
|
10819
10819
|
this._internals = cloneInternals(defaults?._internals ?? defaults ?? defaultInternals);
|
|
@@ -10821,25 +10821,25 @@ var init_options = __esm(() => {
|
|
|
10821
10821
|
this._merging = false;
|
|
10822
10822
|
this._unixOptions = undefined;
|
|
10823
10823
|
try {
|
|
10824
|
-
if (dist_default.plainObject(
|
|
10824
|
+
if (dist_default.plainObject(input3)) {
|
|
10825
10825
|
try {
|
|
10826
|
-
this.merge(
|
|
10826
|
+
this.merge(input3);
|
|
10827
10827
|
this.merge(options);
|
|
10828
10828
|
} finally {
|
|
10829
|
-
this.url =
|
|
10829
|
+
this.url = input3.url;
|
|
10830
10830
|
}
|
|
10831
10831
|
} else {
|
|
10832
10832
|
try {
|
|
10833
10833
|
this.merge(options);
|
|
10834
10834
|
} finally {
|
|
10835
10835
|
if (options?.url !== undefined) {
|
|
10836
|
-
if (
|
|
10836
|
+
if (input3 === undefined) {
|
|
10837
10837
|
this.url = options.url;
|
|
10838
10838
|
} else {
|
|
10839
10839
|
throw new TypeError("The `url` option is mutually exclusive with the `input` argument");
|
|
10840
10840
|
}
|
|
10841
|
-
} else if (
|
|
10842
|
-
this.url =
|
|
10841
|
+
} else if (input3 !== undefined) {
|
|
10842
|
+
this.url = input3;
|
|
10843
10843
|
}
|
|
10844
10844
|
}
|
|
10845
10845
|
}
|
|
@@ -14219,7 +14219,7 @@ __export(exports_node_impl, {
|
|
|
14219
14219
|
runSetupWizard: () => runSetupWizard,
|
|
14220
14220
|
restartNode: () => restartNode
|
|
14221
14221
|
});
|
|
14222
|
-
import { confirm as confirm4, input as
|
|
14222
|
+
import { confirm as confirm4, input as input3, select as select3 } from "@inquirer/prompts";
|
|
14223
14223
|
async function runSetupWizard() {
|
|
14224
14224
|
console.log("");
|
|
14225
14225
|
console.log(blue("Stacks Node Setup Wizard"));
|
|
@@ -14237,7 +14237,7 @@ async function runSetupWizard() {
|
|
|
14237
14237
|
}
|
|
14238
14238
|
success("Docker is running");
|
|
14239
14239
|
console.log("");
|
|
14240
|
-
const installPath = await
|
|
14240
|
+
const installPath = await input3({
|
|
14241
14241
|
message: "Where is stacks-blockchain-docker installed?",
|
|
14242
14242
|
validate: async (value) => {
|
|
14243
14243
|
if (!value.trim())
|
|
@@ -14267,7 +14267,7 @@ async function runSetupWizard() {
|
|
|
14267
14267
|
default: true
|
|
14268
14268
|
});
|
|
14269
14269
|
if (!customPort) {
|
|
14270
|
-
const portInput = await
|
|
14270
|
+
const portInput = await input3({
|
|
14271
14271
|
message: "Enter custom indexer port:",
|
|
14272
14272
|
default: "3700",
|
|
14273
14273
|
validate: (value) => {
|
|
@@ -32443,7 +32443,7 @@ var {
|
|
|
32443
32443
|
// package.json
|
|
32444
32444
|
var package_default = {
|
|
32445
32445
|
name: "@secondlayer/cli",
|
|
32446
|
-
version: "
|
|
32446
|
+
version: "5.0.1",
|
|
32447
32447
|
description: "CLI for subgraphs and blockchain indexing on Stacks",
|
|
32448
32448
|
type: "module",
|
|
32449
32449
|
bin: {
|
|
@@ -32485,11 +32485,11 @@ var package_default = {
|
|
|
32485
32485
|
license: "MIT",
|
|
32486
32486
|
dependencies: {
|
|
32487
32487
|
"@inquirer/prompts": "^8.2.0",
|
|
32488
|
-
"@secondlayer/bundler": "^0.3.
|
|
32489
|
-
"@secondlayer/sdk": "^3.3.
|
|
32490
|
-
"@secondlayer/shared": "^
|
|
32491
|
-
"@secondlayer/stacks": "^2.0
|
|
32492
|
-
"@secondlayer/subgraphs": "^
|
|
32488
|
+
"@secondlayer/bundler": "^0.3.5",
|
|
32489
|
+
"@secondlayer/sdk": "^3.3.2",
|
|
32490
|
+
"@secondlayer/shared": "^6.3.1",
|
|
32491
|
+
"@secondlayer/stacks": "^2.2.0",
|
|
32492
|
+
"@secondlayer/subgraphs": "^2.0.0",
|
|
32493
32493
|
"@biomejs/js-api": "^0.7.0",
|
|
32494
32494
|
"@biomejs/wasm-nodejs": "^1.9.0",
|
|
32495
32495
|
esbuild: "^0.19.0",
|
|
@@ -34542,10 +34542,109 @@ function subgraphTypeToTS(type) {
|
|
|
34542
34542
|
init_config();
|
|
34543
34543
|
init_fs();
|
|
34544
34544
|
init_output();
|
|
34545
|
+
|
|
34546
|
+
// src/commands/login.ts
|
|
34547
|
+
init_http();
|
|
34548
|
+
init_output();
|
|
34549
|
+
init_session();
|
|
34550
|
+
import { input as input2 } from "@inquirer/prompts";
|
|
34551
|
+
async function runLoginFlow() {
|
|
34552
|
+
const email = await input2({
|
|
34553
|
+
message: "Email",
|
|
34554
|
+
validate: (v) => /^.+@.+\..+$/.test(v) ? true : "Invalid email"
|
|
34555
|
+
});
|
|
34556
|
+
try {
|
|
34557
|
+
const res = await httpPlatformAnon("/api/auth/magic-link", {
|
|
34558
|
+
method: "POST",
|
|
34559
|
+
body: { email }
|
|
34560
|
+
});
|
|
34561
|
+
info("Check your inbox for a 6-digit code.");
|
|
34562
|
+
if (res.code) {
|
|
34563
|
+
info(dim(`(DEV_MODE code: ${res.code})`));
|
|
34564
|
+
}
|
|
34565
|
+
} catch (err) {
|
|
34566
|
+
if (err instanceof CliHttpError) {
|
|
34567
|
+
error(err.message);
|
|
34568
|
+
} else {
|
|
34569
|
+
error(err instanceof Error ? err.message : String(err));
|
|
34570
|
+
}
|
|
34571
|
+
process.exit(1);
|
|
34572
|
+
}
|
|
34573
|
+
const code = await input2({
|
|
34574
|
+
message: "Enter the 6-digit code",
|
|
34575
|
+
validate: (v) => /^\d{6}$/.test(v) ? true : "Expected 6 digits"
|
|
34576
|
+
});
|
|
34577
|
+
try {
|
|
34578
|
+
const verified = await httpPlatformAnon("/api/auth/verify", {
|
|
34579
|
+
method: "POST",
|
|
34580
|
+
body: { email, code }
|
|
34581
|
+
});
|
|
34582
|
+
const expiresAt = new Date(Date.now() + 90 * 24 * 60 * 60 * 1000).toISOString();
|
|
34583
|
+
await writeSession({
|
|
34584
|
+
token: verified.sessionToken,
|
|
34585
|
+
email: verified.account.email,
|
|
34586
|
+
accountId: verified.account.id,
|
|
34587
|
+
expiresAt
|
|
34588
|
+
});
|
|
34589
|
+
success(`Logged in as ${verified.account.email}`);
|
|
34590
|
+
info(dim("Run 'sl whoami' to see your account status."));
|
|
34591
|
+
} catch (err) {
|
|
34592
|
+
if (err instanceof CliHttpError) {
|
|
34593
|
+
error(err.message);
|
|
34594
|
+
} else {
|
|
34595
|
+
error(err instanceof Error ? err.message : String(err));
|
|
34596
|
+
}
|
|
34597
|
+
process.exit(1);
|
|
34598
|
+
}
|
|
34599
|
+
}
|
|
34600
|
+
function registerLoginCommand(program2) {
|
|
34601
|
+
program2.command("login").description("Log in to Secondlayer (magic-link email)").action(runLoginFlow);
|
|
34602
|
+
}
|
|
34603
|
+
|
|
34604
|
+
// src/lib/require-auth.ts
|
|
34605
|
+
init_output();
|
|
34606
|
+
init_session();
|
|
34607
|
+
async function requireAuth() {
|
|
34608
|
+
const session = await readSession();
|
|
34609
|
+
if (session)
|
|
34610
|
+
return;
|
|
34611
|
+
info("You're not logged in. Starting login flow.");
|
|
34612
|
+
await runLoginFlow();
|
|
34613
|
+
}
|
|
34614
|
+
|
|
34615
|
+
// src/commands/subgraphs.ts
|
|
34545
34616
|
init_clarity();
|
|
34546
34617
|
|
|
34547
34618
|
// src/templates/subgraph.ts
|
|
34548
|
-
|
|
34619
|
+
var SUBGRAPH_TEMPLATE_SLUGS = [
|
|
34620
|
+
"basic",
|
|
34621
|
+
"sip-010-balances",
|
|
34622
|
+
"sbtc-flows",
|
|
34623
|
+
"pox-stacking",
|
|
34624
|
+
"bns-names"
|
|
34625
|
+
];
|
|
34626
|
+
var SUBGRAPH_TEMPLATE_DESCRIPTIONS = {
|
|
34627
|
+
basic: "Empty starter — pick any source filter type",
|
|
34628
|
+
"sip-010-balances": "SIP-010 token balances (transfers + mints + burns)",
|
|
34629
|
+
"sbtc-flows": "sBTC protocol flows (deposits, withdrawals, signer rotations)",
|
|
34630
|
+
"pox-stacking": "PoX-4 stacking lifecycle calls",
|
|
34631
|
+
"bns-names": "BNS-V2 name ownership and lifecycle"
|
|
34632
|
+
};
|
|
34633
|
+
function generateSubgraphTemplate(name, slug = "basic") {
|
|
34634
|
+
switch (slug) {
|
|
34635
|
+
case "sip-010-balances":
|
|
34636
|
+
return sip010Balances(name);
|
|
34637
|
+
case "sbtc-flows":
|
|
34638
|
+
return sbtcFlows(name);
|
|
34639
|
+
case "pox-stacking":
|
|
34640
|
+
return poxStacking(name);
|
|
34641
|
+
case "bns-names":
|
|
34642
|
+
return bnsNames(name);
|
|
34643
|
+
default:
|
|
34644
|
+
return basic(name);
|
|
34645
|
+
}
|
|
34646
|
+
}
|
|
34647
|
+
function basic(name) {
|
|
34549
34648
|
return `import { defineSubgraph } from "@secondlayer/subgraphs";
|
|
34550
34649
|
|
|
34551
34650
|
export default defineSubgraph({
|
|
@@ -34596,6 +34695,269 @@ export default defineSubgraph({
|
|
|
34596
34695
|
});
|
|
34597
34696
|
`;
|
|
34598
34697
|
}
|
|
34698
|
+
function sip010Balances(name) {
|
|
34699
|
+
return `import { defineSubgraph } from "@secondlayer/subgraphs";
|
|
34700
|
+
|
|
34701
|
+
/**
|
|
34702
|
+
* Track SIP-010 token balances per (asset_identifier, holder).
|
|
34703
|
+
* Mirrors the shape of the Foundation Datasets sBTC token-events
|
|
34704
|
+
* surface — but works for ANY SIP-010 token. Constrain to a single
|
|
34705
|
+
* token by adding \`assetIdentifier: "SP...token::token-name"\` to each
|
|
34706
|
+
* source filter.
|
|
34707
|
+
*
|
|
34708
|
+
* Query examples once deployed:
|
|
34709
|
+
* GET /api/subgraphs/${name}/balances?_search=SP1...
|
|
34710
|
+
* GET /api/subgraphs/${name}/balances?holder=SP1...
|
|
34711
|
+
*/
|
|
34712
|
+
export default defineSubgraph({
|
|
34713
|
+
name: "${name}",
|
|
34714
|
+
version: "1.0.0",
|
|
34715
|
+
description: "Per-token balance tracking for any SIP-010 asset",
|
|
34716
|
+
|
|
34717
|
+
sources: {
|
|
34718
|
+
transfer: { type: "ft_transfer" },
|
|
34719
|
+
mint: { type: "ft_mint" },
|
|
34720
|
+
burn: { type: "ft_burn" },
|
|
34721
|
+
},
|
|
34722
|
+
|
|
34723
|
+
schema: {
|
|
34724
|
+
balances: {
|
|
34725
|
+
columns: {
|
|
34726
|
+
asset_identifier: { type: "text", indexed: true, search: true },
|
|
34727
|
+
holder: { type: "principal", indexed: true, search: true },
|
|
34728
|
+
amount: { type: "uint" },
|
|
34729
|
+
},
|
|
34730
|
+
uniqueKeys: [["asset_identifier", "holder"]],
|
|
34731
|
+
},
|
|
34732
|
+
},
|
|
34733
|
+
|
|
34734
|
+
handlers: {
|
|
34735
|
+
transfer: async (event, ctx) => {
|
|
34736
|
+
const amount = BigInt(event.amount ?? 0);
|
|
34737
|
+
if (event.sender) {
|
|
34738
|
+
await adjust(ctx, event.assetIdentifier, event.sender, -amount);
|
|
34739
|
+
}
|
|
34740
|
+
if (event.recipient) {
|
|
34741
|
+
await adjust(ctx, event.assetIdentifier, event.recipient, amount);
|
|
34742
|
+
}
|
|
34743
|
+
},
|
|
34744
|
+
mint: async (event, ctx) => {
|
|
34745
|
+
if (event.recipient) {
|
|
34746
|
+
await adjust(ctx, event.assetIdentifier, event.recipient, BigInt(event.amount ?? 0));
|
|
34747
|
+
}
|
|
34748
|
+
},
|
|
34749
|
+
burn: async (event, ctx) => {
|
|
34750
|
+
if (event.sender) {
|
|
34751
|
+
await adjust(ctx, event.assetIdentifier, event.sender, -BigInt(event.amount ?? 0));
|
|
34752
|
+
}
|
|
34753
|
+
},
|
|
34754
|
+
},
|
|
34755
|
+
});
|
|
34756
|
+
|
|
34757
|
+
async function adjust(
|
|
34758
|
+
// biome-ignore lint/suspicious/noExplicitAny: subgraph runtime ctx shape
|
|
34759
|
+
ctx: any,
|
|
34760
|
+
assetIdentifier: string,
|
|
34761
|
+
holder: string,
|
|
34762
|
+
delta: bigint,
|
|
34763
|
+
): Promise<void> {
|
|
34764
|
+
const existing = await ctx.findOne("balances", { asset_identifier: assetIdentifier, holder });
|
|
34765
|
+
const current = existing ? BigInt(existing.amount) : 0n;
|
|
34766
|
+
const next = current + delta;
|
|
34767
|
+
await ctx.upsert(
|
|
34768
|
+
"balances",
|
|
34769
|
+
{ asset_identifier: assetIdentifier, holder },
|
|
34770
|
+
{ asset_identifier: assetIdentifier, holder, amount: next },
|
|
34771
|
+
);
|
|
34772
|
+
}
|
|
34773
|
+
`;
|
|
34774
|
+
}
|
|
34775
|
+
function sbtcFlows(name) {
|
|
34776
|
+
return `import { defineSubgraph } from "@secondlayer/subgraphs";
|
|
34777
|
+
|
|
34778
|
+
/**
|
|
34779
|
+
* Track sBTC protocol flows: deposits, withdrawals, signer rotations,
|
|
34780
|
+
* governance updates. Mirrors the shape of the Foundation Datasets
|
|
34781
|
+
* \`/v1/datasets/sbtc/events\` surface but in your own subgraph.
|
|
34782
|
+
*
|
|
34783
|
+
* Source contract: sbtc-registry (mainnet).
|
|
34784
|
+
*
|
|
34785
|
+
* Query examples once deployed:
|
|
34786
|
+
* GET /api/subgraphs/${name}/flows?topic=completed-deposit
|
|
34787
|
+
* GET /api/subgraphs/${name}/flows?topic=withdrawal-create
|
|
34788
|
+
*/
|
|
34789
|
+
export default defineSubgraph({
|
|
34790
|
+
name: "${name}",
|
|
34791
|
+
version: "1.0.0",
|
|
34792
|
+
description: "sBTC deposits, withdrawals, signer rotations, governance",
|
|
34793
|
+
|
|
34794
|
+
sources: {
|
|
34795
|
+
registry: {
|
|
34796
|
+
type: "print_event",
|
|
34797
|
+
contractId: "SM3VDXK3WZZSA84XXFKAFAF15NNZX32CTSG82JFQ4.sbtc-registry",
|
|
34798
|
+
},
|
|
34799
|
+
},
|
|
34800
|
+
|
|
34801
|
+
schema: {
|
|
34802
|
+
flows: {
|
|
34803
|
+
columns: {
|
|
34804
|
+
topic: { type: "text", indexed: true, search: true },
|
|
34805
|
+
request_id: { type: "uint", nullable: true, indexed: true },
|
|
34806
|
+
amount: { type: "text", nullable: true },
|
|
34807
|
+
sender: { type: "principal", nullable: true, indexed: true },
|
|
34808
|
+
bitcoin_txid: { type: "text", nullable: true, search: true },
|
|
34809
|
+
burn_height: { type: "uint", nullable: true },
|
|
34810
|
+
},
|
|
34811
|
+
},
|
|
34812
|
+
},
|
|
34813
|
+
|
|
34814
|
+
handlers: {
|
|
34815
|
+
registry: (event, ctx) => {
|
|
34816
|
+
// biome-ignore lint/suspicious/noExplicitAny: print payload shape varies by topic
|
|
34817
|
+
const payload = (event as any).payload ?? {};
|
|
34818
|
+
const topic = payload.topic;
|
|
34819
|
+
if (typeof topic !== "string") return;
|
|
34820
|
+
|
|
34821
|
+
ctx.insert("flows", {
|
|
34822
|
+
topic,
|
|
34823
|
+
request_id: payload["request-id"] ?? null,
|
|
34824
|
+
amount: payload.amount ? String(payload.amount) : null,
|
|
34825
|
+
sender: payload.sender ?? null,
|
|
34826
|
+
bitcoin_txid: payload["bitcoin-txid"] ?? null,
|
|
34827
|
+
burn_height: payload["burn-height"] ?? null,
|
|
34828
|
+
});
|
|
34829
|
+
},
|
|
34830
|
+
},
|
|
34831
|
+
});
|
|
34832
|
+
`;
|
|
34833
|
+
}
|
|
34834
|
+
function poxStacking(name) {
|
|
34835
|
+
return `import { defineSubgraph } from "@secondlayer/subgraphs";
|
|
34836
|
+
|
|
34837
|
+
/**
|
|
34838
|
+
* Track Stacking lifecycle calls on PoX-4 — solo stacking, delegation,
|
|
34839
|
+
* extension, increase, aggregation, signer-key authorizations. Mirrors
|
|
34840
|
+
* the shape of the Foundation Datasets \`/v1/datasets/pox-4/calls\`
|
|
34841
|
+
* surface as your own subgraph.
|
|
34842
|
+
*
|
|
34843
|
+
* Note: PoX-4 emits zero print events; this subgraph captures contract
|
|
34844
|
+
* calls. Decoding function args + raw_result is left to your handler —
|
|
34845
|
+
* the dataset shows one possible shape.
|
|
34846
|
+
*
|
|
34847
|
+
* Query examples once deployed:
|
|
34848
|
+
* GET /api/subgraphs/${name}/calls?function_name=stack-stx
|
|
34849
|
+
* GET /api/subgraphs/${name}/calls?caller=SP1...
|
|
34850
|
+
*/
|
|
34851
|
+
export default defineSubgraph({
|
|
34852
|
+
name: "${name}",
|
|
34853
|
+
version: "1.0.0",
|
|
34854
|
+
description: "PoX-4 stacking lifecycle calls",
|
|
34855
|
+
|
|
34856
|
+
sources: {
|
|
34857
|
+
pox: {
|
|
34858
|
+
type: "contract_call",
|
|
34859
|
+
contractId: "SP000000000000000000002Q6VF78.pox-4",
|
|
34860
|
+
},
|
|
34861
|
+
},
|
|
34862
|
+
|
|
34863
|
+
schema: {
|
|
34864
|
+
calls: {
|
|
34865
|
+
columns: {
|
|
34866
|
+
function_name: { type: "text", indexed: true, search: true },
|
|
34867
|
+
caller: { type: "principal", indexed: true, search: true },
|
|
34868
|
+
result_ok: { type: "boolean" },
|
|
34869
|
+
},
|
|
34870
|
+
},
|
|
34871
|
+
},
|
|
34872
|
+
|
|
34873
|
+
handlers: {
|
|
34874
|
+
pox: (event, ctx) => {
|
|
34875
|
+
// biome-ignore lint/suspicious/noExplicitAny: contract_call event shape
|
|
34876
|
+
const fnName = (event as any).functionName ?? ctx.tx.functionName ?? "";
|
|
34877
|
+
// biome-ignore lint/suspicious/noExplicitAny: raw_result is hex-encoded Clarity
|
|
34878
|
+
const resultHex = (event as any).rawResult ?? "";
|
|
34879
|
+
ctx.insert("calls", {
|
|
34880
|
+
function_name: fnName,
|
|
34881
|
+
caller: ctx.tx.sender,
|
|
34882
|
+
result_ok: resultHex.startsWith("0x07"), // 0x07 = response-ok type tag
|
|
34883
|
+
});
|
|
34884
|
+
},
|
|
34885
|
+
},
|
|
34886
|
+
});
|
|
34887
|
+
`;
|
|
34888
|
+
}
|
|
34889
|
+
function bnsNames(name) {
|
|
34890
|
+
return `import { defineSubgraph } from "@secondlayer/subgraphs";
|
|
34891
|
+
|
|
34892
|
+
/**
|
|
34893
|
+
* Track BNS-V2 name lifecycle events — registrations, transfers,
|
|
34894
|
+
* renewals, burns, airdrops. Mirrors the Foundation Datasets
|
|
34895
|
+
* \`/v1/datasets/bns/name-events\` surface as your own subgraph.
|
|
34896
|
+
*
|
|
34897
|
+
* Source: BNS-V2 print events (topic-discriminated payloads).
|
|
34898
|
+
*
|
|
34899
|
+
* Query examples once deployed:
|
|
34900
|
+
* GET /api/subgraphs/${name}/names?owner=SP1...
|
|
34901
|
+
* GET /api/subgraphs/${name}/names?_search=alice
|
|
34902
|
+
*/
|
|
34903
|
+
export default defineSubgraph({
|
|
34904
|
+
name: "${name}",
|
|
34905
|
+
version: "1.0.0",
|
|
34906
|
+
description: "BNS-V2 name ownership and lifecycle",
|
|
34907
|
+
|
|
34908
|
+
sources: {
|
|
34909
|
+
bns: {
|
|
34910
|
+
type: "print_event",
|
|
34911
|
+
contractId: "SP2QEZ06AGJ3RKJPBV14SY1V5BBFNAW33D96YPGZF.BNS-V2",
|
|
34912
|
+
},
|
|
34913
|
+
},
|
|
34914
|
+
|
|
34915
|
+
schema: {
|
|
34916
|
+
names: {
|
|
34917
|
+
columns: {
|
|
34918
|
+
topic: { type: "text", indexed: true },
|
|
34919
|
+
namespace: { type: "text", indexed: true, search: true },
|
|
34920
|
+
name: { type: "text", indexed: true, search: true },
|
|
34921
|
+
fqn: { type: "text", indexed: true, search: true },
|
|
34922
|
+
owner: { type: "principal", nullable: true, indexed: true, search: true },
|
|
34923
|
+
},
|
|
34924
|
+
},
|
|
34925
|
+
},
|
|
34926
|
+
|
|
34927
|
+
handlers: {
|
|
34928
|
+
bns: (event, ctx) => {
|
|
34929
|
+
// biome-ignore lint/suspicious/noExplicitAny: print payload shape
|
|
34930
|
+
const payload = (event as any).payload ?? {};
|
|
34931
|
+
const topic = payload.topic;
|
|
34932
|
+
if (typeof topic !== "string") return;
|
|
34933
|
+
const namespace = decodeBuffUtf8(payload.namespace);
|
|
34934
|
+
const nameLabel = decodeBuffUtf8(payload.name);
|
|
34935
|
+
if (!namespace || !nameLabel) return;
|
|
34936
|
+
ctx.insert("names", {
|
|
34937
|
+
topic,
|
|
34938
|
+
namespace,
|
|
34939
|
+
name: nameLabel,
|
|
34940
|
+
fqn: \`\${nameLabel}.\${namespace}\`,
|
|
34941
|
+
owner: topic === "burn-name" ? null : (payload.owner ?? null),
|
|
34942
|
+
});
|
|
34943
|
+
},
|
|
34944
|
+
},
|
|
34945
|
+
});
|
|
34946
|
+
|
|
34947
|
+
function decodeBuffUtf8(value: unknown): string | null {
|
|
34948
|
+
if (typeof value !== "string") return null;
|
|
34949
|
+
const hex = value.startsWith("0x") ? value.slice(2) : value;
|
|
34950
|
+
if (hex.length === 0) return null;
|
|
34951
|
+
const bytes = new Uint8Array(hex.length / 2);
|
|
34952
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
34953
|
+
bytes[i] = Number.parseInt(hex.slice(i * 2, i * 2 + 2), 16);
|
|
34954
|
+
}
|
|
34955
|
+
let end = bytes.length;
|
|
34956
|
+
while (end > 0 && bytes[end - 1] === 0) end -= 1;
|
|
34957
|
+
return new TextDecoder("utf-8").decode(bytes.subarray(0, end));
|
|
34958
|
+
}
|
|
34959
|
+
`;
|
|
34960
|
+
}
|
|
34599
34961
|
|
|
34600
34962
|
// src/commands/subgraphs.ts
|
|
34601
34963
|
init_api();
|
|
@@ -34640,9 +35002,9 @@ async function writeOrPrintSubgraphSpec(spec, format, output) {
|
|
|
34640
35002
|
await writeTextFile(outPath, text);
|
|
34641
35003
|
success(`Created ${outPath}`);
|
|
34642
35004
|
}
|
|
34643
|
-
function createLocalSubgraphDetail(
|
|
35005
|
+
function createLocalSubgraphDetail(input3) {
|
|
34644
35006
|
const tables = {};
|
|
34645
|
-
for (const [tableName, rawTable] of Object.entries(
|
|
35007
|
+
for (const [tableName, rawTable] of Object.entries(input3.schema)) {
|
|
34646
35008
|
const table = rawTable;
|
|
34647
35009
|
const columns = {};
|
|
34648
35010
|
for (const [columnName, column] of Object.entries(table.columns ?? {})) {
|
|
@@ -34659,22 +35021,22 @@ function createLocalSubgraphDetail(input2) {
|
|
|
34659
35021
|
columns._tx_id = { type: "text" };
|
|
34660
35022
|
columns._created_at = { type: "timestamp" };
|
|
34661
35023
|
tables[tableName] = {
|
|
34662
|
-
endpoint: `/subgraphs/${
|
|
35024
|
+
endpoint: `/subgraphs/${input3.name}/${tableName}`,
|
|
34663
35025
|
columns,
|
|
34664
35026
|
rowCount: 0,
|
|
34665
|
-
example: `/subgraphs/${
|
|
35027
|
+
example: `/subgraphs/${input3.name}/${tableName}?_sort=_block_height&_order=desc&_limit=10`,
|
|
34666
35028
|
...table.indexes && { indexes: table.indexes },
|
|
34667
35029
|
...table.uniqueKeys && { uniqueKeys: table.uniqueKeys }
|
|
34668
35030
|
};
|
|
34669
35031
|
}
|
|
34670
35032
|
return {
|
|
34671
|
-
name:
|
|
34672
|
-
version:
|
|
34673
|
-
schemaHash:
|
|
35033
|
+
name: input3.name,
|
|
35034
|
+
version: input3.version ?? "0.0.0",
|
|
35035
|
+
schemaHash: input3.schemaHash,
|
|
34674
35036
|
status: "local",
|
|
34675
35037
|
lastProcessedBlock: 0,
|
|
34676
|
-
...
|
|
34677
|
-
sources:
|
|
35038
|
+
...input3.description && { description: input3.description },
|
|
35039
|
+
sources: input3.sources,
|
|
34678
35040
|
health: {
|
|
34679
35041
|
totalProcessed: 0,
|
|
34680
35042
|
totalErrors: 0,
|
|
@@ -34832,7 +35194,14 @@ function formatSubgraphSync(sync) {
|
|
|
34832
35194
|
}
|
|
34833
35195
|
function registerSubgraphsCommand(program2) {
|
|
34834
35196
|
const subgraphs = program2.command("subgraphs").description("Manage materialized subgraphs");
|
|
34835
|
-
subgraphs.command("new <name>").description("Scaffold a new subgraph definition file").action(async (name) => {
|
|
35197
|
+
subgraphs.command("new <name>").description("Scaffold a new subgraph definition file").option("--template <slug>", `Foundation Dataset starter (one of: ${SUBGRAPH_TEMPLATE_SLUGS.join(", ")})`).action(async (name, opts) => {
|
|
35198
|
+
const slug = opts.template ?? "basic";
|
|
35199
|
+
if (!SUBGRAPH_TEMPLATE_SLUGS.includes(slug)) {
|
|
35200
|
+
error(`Unknown template "${opts.template}". Available templates:
|
|
35201
|
+
${SUBGRAPH_TEMPLATE_SLUGS.map((s) => ` ${s.padEnd(20)} ${SUBGRAPH_TEMPLATE_DESCRIPTIONS[s]}`).join(`
|
|
35202
|
+
`)}`);
|
|
35203
|
+
process.exit(1);
|
|
35204
|
+
}
|
|
34836
35205
|
const dir = resolve3("subgraphs");
|
|
34837
35206
|
const filePath = resolve3(dir, `${name}.ts`);
|
|
34838
35207
|
if (existsSync3(filePath)) {
|
|
@@ -34842,9 +35211,12 @@ function registerSubgraphsCommand(program2) {
|
|
|
34842
35211
|
if (!existsSync3(dir)) {
|
|
34843
35212
|
mkdirSync2(dir, { recursive: true });
|
|
34844
35213
|
}
|
|
34845
|
-
const content = generateSubgraphTemplate(name);
|
|
35214
|
+
const content = generateSubgraphTemplate(name, slug);
|
|
34846
35215
|
await writeTextFile(filePath, content);
|
|
34847
35216
|
success(`Created ${filePath}`);
|
|
35217
|
+
if (slug !== "basic") {
|
|
35218
|
+
info(`Template: ${slug} — ${SUBGRAPH_TEMPLATE_DESCRIPTIONS[slug]}`);
|
|
35219
|
+
}
|
|
34848
35220
|
info(`Next: sl subgraphs deploy subgraphs/${name}.ts`);
|
|
34849
35221
|
});
|
|
34850
35222
|
subgraphs.command("dev <file>").description("Watch a subgraph file and auto-redeploy on change").action(async (file) => {
|
|
@@ -34912,6 +35284,9 @@ Stopped watching.`);
|
|
|
34912
35284
|
try {
|
|
34913
35285
|
const absPath = resolve3(file);
|
|
34914
35286
|
const config = await loadConfig();
|
|
35287
|
+
if (config.network !== "local") {
|
|
35288
|
+
await requireAuth();
|
|
35289
|
+
}
|
|
34915
35290
|
const dryRun = options2.dryRun || options2.preview;
|
|
34916
35291
|
const startBlock = parseStartBlockOption(options2.startBlock);
|
|
34917
35292
|
if (startBlock !== undefined) {
|
|
@@ -36214,62 +36589,6 @@ function registerWhoamiCommand(program2) {
|
|
|
36214
36589
|
console.log(formatKeyValue(rows));
|
|
36215
36590
|
});
|
|
36216
36591
|
}
|
|
36217
|
-
// src/commands/login.ts
|
|
36218
|
-
init_http();
|
|
36219
|
-
init_output();
|
|
36220
|
-
init_session();
|
|
36221
|
-
import { input as input3 } from "@inquirer/prompts";
|
|
36222
|
-
function registerLoginCommand(program2) {
|
|
36223
|
-
program2.command("login").description("Log in to Secondlayer (magic-link email)").action(async () => {
|
|
36224
|
-
const email = await input3({
|
|
36225
|
-
message: "Email",
|
|
36226
|
-
validate: (v) => /^.+@.+\..+$/.test(v) ? true : "Invalid email"
|
|
36227
|
-
});
|
|
36228
|
-
try {
|
|
36229
|
-
const res = await httpPlatformAnon("/api/auth/magic-link", {
|
|
36230
|
-
method: "POST",
|
|
36231
|
-
body: { email }
|
|
36232
|
-
});
|
|
36233
|
-
info("Check your inbox for a 6-digit code.");
|
|
36234
|
-
if (res.code) {
|
|
36235
|
-
info(dim(`(DEV_MODE code: ${res.code})`));
|
|
36236
|
-
}
|
|
36237
|
-
} catch (err) {
|
|
36238
|
-
if (err instanceof CliHttpError) {
|
|
36239
|
-
error(err.message);
|
|
36240
|
-
} else {
|
|
36241
|
-
error(err instanceof Error ? err.message : String(err));
|
|
36242
|
-
}
|
|
36243
|
-
process.exit(1);
|
|
36244
|
-
}
|
|
36245
|
-
const code = await input3({
|
|
36246
|
-
message: "Enter the 6-digit code",
|
|
36247
|
-
validate: (v) => /^\d{6}$/.test(v) ? true : "Expected 6 digits"
|
|
36248
|
-
});
|
|
36249
|
-
try {
|
|
36250
|
-
const verified = await httpPlatformAnon("/api/auth/verify", {
|
|
36251
|
-
method: "POST",
|
|
36252
|
-
body: { email, code }
|
|
36253
|
-
});
|
|
36254
|
-
const expiresAt = new Date(Date.now() + 90 * 24 * 60 * 60 * 1000).toISOString();
|
|
36255
|
-
await writeSession({
|
|
36256
|
-
token: verified.sessionToken,
|
|
36257
|
-
email: verified.account.email,
|
|
36258
|
-
accountId: verified.account.id,
|
|
36259
|
-
expiresAt
|
|
36260
|
-
});
|
|
36261
|
-
success(`Logged in as ${verified.account.email}`);
|
|
36262
|
-
info(dim("Run 'sl whoami' to see your account status."));
|
|
36263
|
-
} catch (err) {
|
|
36264
|
-
if (err instanceof CliHttpError) {
|
|
36265
|
-
error(err.message);
|
|
36266
|
-
} else {
|
|
36267
|
-
error(err instanceof Error ? err.message : String(err));
|
|
36268
|
-
}
|
|
36269
|
-
process.exit(1);
|
|
36270
|
-
}
|
|
36271
|
-
});
|
|
36272
|
-
}
|
|
36273
36592
|
// src/commands/logout.ts
|
|
36274
36593
|
init_http();
|
|
36275
36594
|
init_output();
|
|
@@ -36302,12 +36621,12 @@ import { confirm as confirm5, input as input4, select as select4 } from "@inquir
|
|
|
36302
36621
|
var INSTANCE_CREATE_TIMEOUT_MS = 180000;
|
|
36303
36622
|
function registerInstanceCommand(program2) {
|
|
36304
36623
|
const instance = program2.command("instance").description("Manage your dedicated Secondlayer instance");
|
|
36305
|
-
instance.command("create").description("Provision a new dedicated instance for the active project").option("--plan <plan>", "Plan:
|
|
36624
|
+
instance.command("create").description("Provision a new dedicated instance for the active project").option("--plan <plan>", "Plan: launch | scale", "launch").action(async (opts) => {
|
|
36306
36625
|
guardOssMode();
|
|
36307
36626
|
const activeSlug = await requireActiveProject();
|
|
36308
36627
|
const plan = opts.plan;
|
|
36309
|
-
if (!["
|
|
36310
|
-
error(`Invalid plan: ${plan} (expected
|
|
36628
|
+
if (!["launch", "scale"].includes(plan)) {
|
|
36629
|
+
error(`Invalid plan: ${plan} (expected launch or scale)`);
|
|
36311
36630
|
process.exit(1);
|
|
36312
36631
|
}
|
|
36313
36632
|
const spinner = createSpinner("Provisioning your instance (~60s; safe to interrupt — instance will still be created; check `sl instance info`)");
|
|
@@ -36325,6 +36644,11 @@ function registerInstanceCommand(program2) {
|
|
|
36325
36644
|
error("Provisioning may still finish server-side. Run `sl instance info` to check before retrying.");
|
|
36326
36645
|
process.exit(1);
|
|
36327
36646
|
}
|
|
36647
|
+
if (err instanceof CliHttpError && err.code === "SUBSCRIPTION_REQUIRED") {
|
|
36648
|
+
spinner.fail("Trial required before provisioning.");
|
|
36649
|
+
await printTrialCheckoutUrl(plan);
|
|
36650
|
+
process.exit(1);
|
|
36651
|
+
}
|
|
36328
36652
|
spinner.fail("Provision failed.");
|
|
36329
36653
|
handleInstanceError(err, "provision instance");
|
|
36330
36654
|
}
|
|
@@ -36333,17 +36657,13 @@ function registerInstanceCommand(program2) {
|
|
|
36333
36657
|
guardOssMode();
|
|
36334
36658
|
await renderInstanceInfo();
|
|
36335
36659
|
});
|
|
36336
|
-
instance.command("resize").description("Change your instance plan (brief downtime)").option("--plan <plan>", "Target plan:
|
|
36660
|
+
instance.command("resize").description("Change your instance plan (brief downtime)").option("--plan <plan>", "Target plan: launch | scale").option("--yes", "Skip confirm").action(async (opts) => {
|
|
36337
36661
|
guardOssMode();
|
|
36338
36662
|
let target = opts.plan;
|
|
36339
36663
|
if (!target) {
|
|
36340
36664
|
const answer = await select4({
|
|
36341
36665
|
message: "Target plan",
|
|
36342
36666
|
choices: [
|
|
36343
|
-
{
|
|
36344
|
-
value: "hobby",
|
|
36345
|
-
name: "Hobby — free (0.5 vCPU · 1 GB · 10 GB, auto-pause after 7d idle)"
|
|
36346
|
-
},
|
|
36347
36667
|
{
|
|
36348
36668
|
value: "launch",
|
|
36349
36669
|
name: "Launch — $99/mo (2 vCPU · 6 GB · 100 GB)"
|
|
@@ -36356,6 +36676,10 @@ function registerInstanceCommand(program2) {
|
|
|
36356
36676
|
});
|
|
36357
36677
|
target = answer;
|
|
36358
36678
|
}
|
|
36679
|
+
if (!["launch", "scale"].includes(target)) {
|
|
36680
|
+
error(`Invalid plan: ${target} (expected launch or scale)`);
|
|
36681
|
+
process.exit(1);
|
|
36682
|
+
}
|
|
36359
36683
|
if (!opts.yes) {
|
|
36360
36684
|
const ok = await confirm5({
|
|
36361
36685
|
message: `Resize to ${target}? ~30s downtime while containers recreate. Data preserved.`,
|
|
@@ -36532,6 +36856,18 @@ function registerInstanceCommand(program2) {
|
|
|
36532
36856
|
}
|
|
36533
36857
|
});
|
|
36534
36858
|
}
|
|
36859
|
+
async function printTrialCheckoutUrl(plan) {
|
|
36860
|
+
const res = await httpPlatform("/api/billing/upgrade", {
|
|
36861
|
+
method: "POST",
|
|
36862
|
+
body: { tier: plan }
|
|
36863
|
+
});
|
|
36864
|
+
if (!res.url) {
|
|
36865
|
+
error("No checkout URL returned. Open Billing in the dashboard.");
|
|
36866
|
+
return;
|
|
36867
|
+
}
|
|
36868
|
+
info("Start your 30-day trial, then rerun this command:");
|
|
36869
|
+
console.log(green(res.url));
|
|
36870
|
+
}
|
|
36535
36871
|
function guardOssMode() {
|
|
36536
36872
|
if (isOssMode()) {
|
|
36537
36873
|
error("`sl instance` commands are for hosted deployments. For OSS use `sl local` / `sl stack` or your own provisioning.");
|
|
@@ -36803,5 +37139,5 @@ registerLocalCommand(program);
|
|
|
36803
37139
|
registerAccountCommand(program);
|
|
36804
37140
|
program.parse();
|
|
36805
37141
|
|
|
36806
|
-
//# debugId=
|
|
37142
|
+
//# debugId=40B4A90A59C890CE64756E2164756E21
|
|
36807
37143
|
//# sourceMappingURL=cli.js.map
|