@secondlayer/cli 5.0.0 → 5.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -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(input2, options, callback) {
9552
+ constructor(input3, options, callback) {
9553
9553
  super({
9554
9554
  autoDestroy: false,
9555
9555
  emitClose: false
9556
9556
  });
9557
- if (typeof input2 === "string") {
9558
- input2 = urlToHttpOptions(new URL2(input2));
9559
- } else if (input2 instanceof URL2) {
9560
- input2 = urlToHttpOptions(input2);
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
- input2 = { ...input2 };
9562
+ input3 = { ...input3 };
9563
9563
  }
9564
9564
  if (typeof options === "function" || options === undefined) {
9565
9565
  callback = options;
9566
- options = input2;
9566
+ options = input3;
9567
9567
  } else {
9568
- options = Object.assign(input2, options);
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 (input2, options, callback) => {
10064
- if (typeof input2 === "string") {
10065
- input2 = urlToHttpOptions(new URL2(input2));
10066
- } else if (input2 instanceof URL2) {
10067
- input2 = urlToHttpOptions(input2);
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
- input2 = { ...input2 };
10069
+ input3 = { ...input3 };
10070
10070
  }
10071
10071
  if (typeof options === "function" || options === undefined) {
10072
10072
  callback = options;
10073
- options = input2;
10073
+ options = input3;
10074
10074
  } else {
10075
- options = Object.assign(input2, options);
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(input2, options, defaults) {
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], input2);
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 (input2 instanceof Options || options instanceof Options) {
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(input2)) {
10824
+ if (dist_default.plainObject(input3)) {
10825
10825
  try {
10826
- this.merge(input2);
10826
+ this.merge(input3);
10827
10827
  this.merge(options);
10828
10828
  } finally {
10829
- this.url = input2.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 (input2 === undefined) {
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 (input2 !== undefined) {
10842
- this.url = input2;
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 input2, select as select3 } from "@inquirer/prompts";
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 input2({
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 input2({
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: "5.0.0",
32446
+ version: "5.1.0",
32447
32447
  description: "CLI for subgraphs and blockchain indexing on Stacks",
32448
32448
  type: "module",
32449
32449
  bin: {
@@ -32487,8 +32487,8 @@ var package_default = {
32487
32487
  "@inquirer/prompts": "^8.2.0",
32488
32488
  "@secondlayer/bundler": "^0.3.5",
32489
32489
  "@secondlayer/sdk": "^3.3.2",
32490
- "@secondlayer/shared": "^6.0.0",
32491
- "@secondlayer/stacks": "^2.0.1",
32490
+ "@secondlayer/shared": "^6.3.1",
32491
+ "@secondlayer/stacks": "^2.2.0",
32492
32492
  "@secondlayer/subgraphs": "^2.0.0",
32493
32493
  "@biomejs/js-api": "^0.7.0",
32494
32494
  "@biomejs/wasm-nodejs": "^1.9.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
- function generateSubgraphTemplate(name) {
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(input2) {
35005
+ function createLocalSubgraphDetail(input3) {
34644
35006
  const tables = {};
34645
- for (const [tableName, rawTable] of Object.entries(input2.schema)) {
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/${input2.name}/${tableName}`,
35024
+ endpoint: `/subgraphs/${input3.name}/${tableName}`,
34663
35025
  columns,
34664
35026
  rowCount: 0,
34665
- example: `/subgraphs/${input2.name}/${tableName}?_sort=_block_height&_order=desc&_limit=10`,
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: input2.name,
34672
- version: input2.version ?? "0.0.0",
34673
- schemaHash: input2.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
- ...input2.description && { description: input2.description },
34677
- sources: input2.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) {
@@ -35544,6 +35919,152 @@ async function stackStop(options2) {
35544
35919
  success("Stack stopped");
35545
35920
  console.log("");
35546
35921
  }
35922
+ // src/commands/streams.ts
35923
+ init_output();
35924
+ import { createStreamsClient } from "@secondlayer/sdk";
35925
+ var DEFAULT_BASE_URL = "https://api.secondlayer.tools";
35926
+ var VALID_TYPES = [
35927
+ "stx_transfer",
35928
+ "stx_mint",
35929
+ "stx_burn",
35930
+ "stx_lock",
35931
+ "ft_transfer",
35932
+ "ft_mint",
35933
+ "ft_burn",
35934
+ "nft_transfer",
35935
+ "nft_mint",
35936
+ "nft_burn",
35937
+ "print"
35938
+ ];
35939
+ function readApiKey() {
35940
+ const key = process.env.SL_STREAMS_API_KEY;
35941
+ if (!key) {
35942
+ error("SL_STREAMS_API_KEY env var not set. Issue a key at https://www.secondlayer.tools/platform/api-keys (product: Streams).");
35943
+ process.exit(1);
35944
+ }
35945
+ return key;
35946
+ }
35947
+ function client() {
35948
+ return createStreamsClient({
35949
+ baseUrl: process.env.SL_API_URL ?? DEFAULT_BASE_URL,
35950
+ apiKey: readApiKey()
35951
+ });
35952
+ }
35953
+ function parseTypes(value) {
35954
+ if (!value)
35955
+ return;
35956
+ const parts = value.split(",").map((s) => s.trim());
35957
+ for (const p of parts) {
35958
+ if (!VALID_TYPES.includes(p)) {
35959
+ throw new Error(`invalid --types value "${p}"; expected one of: ${VALID_TYPES.join(", ")}`);
35960
+ }
35961
+ }
35962
+ return parts;
35963
+ }
35964
+ function parseLimit(value) {
35965
+ if (value === undefined)
35966
+ return;
35967
+ const n = Number.parseInt(value, 10);
35968
+ if (!Number.isFinite(n) || n < 1 || n > 1000) {
35969
+ throw new Error("--limit must be an integer between 1 and 1000");
35970
+ }
35971
+ return n;
35972
+ }
35973
+ function parseHeight(value, name) {
35974
+ if (value === undefined)
35975
+ return;
35976
+ const n = Number.parseInt(value, 10);
35977
+ if (!Number.isFinite(n) || n < 0) {
35978
+ throw new Error(`${name} must be a non-negative integer`);
35979
+ }
35980
+ return n;
35981
+ }
35982
+ function registerStreamsCommand(program2) {
35983
+ const streams = program2.command("streams").description("Read raw chain events from Streams (requires SL_STREAMS_API_KEY)");
35984
+ streams.command("tip").description("Print current canonical tip").action(async () => {
35985
+ try {
35986
+ const tip = await client().tip();
35987
+ process.stdout.write(`${JSON.stringify(tip, null, 2)}
35988
+ `);
35989
+ } catch (err) {
35990
+ error(err instanceof Error ? err.message : String(err));
35991
+ process.exit(1);
35992
+ }
35993
+ });
35994
+ streams.command("events").description("List events (cursor-paginated; one page per call)").option("--types <types>", `comma-separated event types (${VALID_TYPES.join(", ")})`).option("--contract-id <id>", "filter to a single contract identifier").option("--cursor <cursor>", "start cursor (block_height:event_index)").option("--from-height <n>", "filter to blocks >= n").option("--to-height <n>", "filter to blocks <= n").option("--limit <n>", "page size (1-1000, default 100)", "100").action(async (options2) => {
35995
+ try {
35996
+ const envelope = await client().events.list({
35997
+ types: parseTypes(options2.types),
35998
+ contractId: options2.contractId,
35999
+ cursor: options2.cursor,
36000
+ fromHeight: parseHeight(options2.fromHeight, "--from-height"),
36001
+ toHeight: parseHeight(options2.toHeight, "--to-height"),
36002
+ limit: parseLimit(options2.limit)
36003
+ });
36004
+ process.stdout.write(`${JSON.stringify(envelope, null, 2)}
36005
+ `);
36006
+ } catch (err) {
36007
+ error(err instanceof Error ? err.message : String(err));
36008
+ process.exit(1);
36009
+ }
36010
+ });
36011
+ streams.command("consume").description("Long-running pull from a cursor; emits one event per line (jsonl) until SIGINT or --max-pages").option("--types <types>", `comma-separated event types (${VALID_TYPES.join(", ")})`).option("--contract-id <id>", "filter to a single contract identifier").option("--cursor <cursor>", "start cursor (block_height:event_index)").option("--batch-size <n>", "events per batch (1-1000, default 100)", "100").option("--max-pages <n>", "stop after N pages (default: run until SIGINT)").action(async (options2) => {
36012
+ try {
36013
+ const types6 = parseTypes(options2.types);
36014
+ const batchSize = parseLimit(options2.batchSize) ?? 100;
36015
+ const maxPages = options2.maxPages ? Number.parseInt(options2.maxPages, 10) : undefined;
36016
+ info(dim("# streaming events to stdout (jsonl); next_cursor printed to stderr"));
36017
+ await client().events.consume({
36018
+ fromCursor: options2.cursor,
36019
+ types: types6,
36020
+ contractId: options2.contractId,
36021
+ batchSize,
36022
+ mode: "tail",
36023
+ maxPages,
36024
+ onBatch: (events, envelope) => {
36025
+ for (const e of events) {
36026
+ process.stdout.write(`${JSON.stringify(e)}
36027
+ `);
36028
+ }
36029
+ if (envelope.next_cursor) {
36030
+ process.stderr.write(`# next_cursor=${envelope.next_cursor}
36031
+ `);
36032
+ }
36033
+ return envelope.next_cursor;
36034
+ }
36035
+ });
36036
+ } catch (err) {
36037
+ error(err instanceof Error ? err.message : String(err));
36038
+ process.exit(1);
36039
+ }
36040
+ });
36041
+ streams.command("reorgs").description("List recent reorgs (cursor-paginated)").requiredOption("--since <cursor>", "start cursor (block_height:event_index)").option("--limit <n>", "page size (default 100)", "100").action(async (options2) => {
36042
+ try {
36043
+ const envelope = await client().reorgs.list({
36044
+ since: options2.since,
36045
+ limit: parseLimit(options2.limit)
36046
+ });
36047
+ process.stdout.write(`${JSON.stringify(envelope, null, 2)}
36048
+ `);
36049
+ } catch (err) {
36050
+ error(err instanceof Error ? err.message : String(err));
36051
+ process.exit(1);
36052
+ }
36053
+ });
36054
+ streams.command("canonical <height>").description("Canonical block metadata at a given height").action(async (heightArg) => {
36055
+ try {
36056
+ const height = parseHeight(heightArg, "<height>");
36057
+ if (height === undefined)
36058
+ throw new Error("<height> is required");
36059
+ const block = await client().canonical(height);
36060
+ process.stdout.write(`${JSON.stringify(block, null, 2)}
36061
+ `);
36062
+ } catch (err) {
36063
+ error(err instanceof Error ? err.message : String(err));
36064
+ process.exit(1);
36065
+ }
36066
+ });
36067
+ }
35547
36068
  // src/commands/doctor.ts
35548
36069
  init_config();
35549
36070
 
@@ -36214,62 +36735,6 @@ function registerWhoamiCommand(program2) {
36214
36735
  console.log(formatKeyValue(rows));
36215
36736
  });
36216
36737
  }
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
36738
  // src/commands/logout.ts
36274
36739
  init_http();
36275
36740
  init_output();
@@ -36811,6 +37276,7 @@ registerInstanceCommand(program);
36811
37276
  registerSubgraphsCommand(program);
36812
37277
  registerCreateCommand(program);
36813
37278
  registerSubscriptionsCommand(program);
37279
+ registerStreamsCommand(program);
36814
37280
  registerStatusCommand(program);
36815
37281
  registerStackCommand(program);
36816
37282
  registerDbCommand(program);
@@ -36820,5 +37286,5 @@ registerLocalCommand(program);
36820
37286
  registerAccountCommand(program);
36821
37287
  program.parse();
36822
37288
 
36823
- //# debugId=62D7A79023B8002464756E2164756E21
37289
+ //# debugId=CEAA7239644824A064756E2164756E21
36824
37290
  //# sourceMappingURL=cli.js.map