@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/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: "4.0.1",
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.4",
32489
- "@secondlayer/sdk": "^3.3.1",
32490
- "@secondlayer/shared": "^5.2.0",
32491
- "@secondlayer/stacks": "^2.0.1",
32492
- "@secondlayer/subgraphs": "^1.3.3",
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
- 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) {
@@ -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: hobby (free) | launch | scale", "hobby").action(async (opts) => {
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 (!["hobby", "launch", "scale"].includes(plan)) {
36310
- error(`Invalid plan: ${plan} (expected hobby, launch, or scale)`);
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: hobby | launch | scale").option("--yes", "Skip confirm").action(async (opts) => {
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=89A6DFBE4B8EE7D064756E2164756E21
37142
+ //# debugId=40B4A90A59C890CE64756E2164756E21
36807
37143
  //# sourceMappingURL=cli.js.map