@secondlayer/cli 8.12.0 → 8.14.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
@@ -31811,7 +31811,7 @@ var {
31811
31811
  // package.json
31812
31812
  var package_default = {
31813
31813
  name: "@secondlayer/cli",
31814
- version: "8.12.0",
31814
+ version: "8.14.0",
31815
31815
  repository: {
31816
31816
  type: "git",
31817
31817
  url: "git+https://github.com/ryanwaits/secondlayer.git",
@@ -31862,9 +31862,9 @@ var package_default = {
31862
31862
  "@secondlayer/bundler": "^0.3.7",
31863
31863
  "@secondlayer/scaffold": "^1.3.0",
31864
31864
  "@secondlayer/sdk": "^6.23.1",
31865
- "@secondlayer/shared": "^6.35.1",
31865
+ "@secondlayer/shared": "^6.40.0",
31866
31866
  "@secondlayer/stacks": "^2.5.1",
31867
- "@secondlayer/subgraphs": "^3.14.1",
31867
+ "@secondlayer/subgraphs": "^3.19.0",
31868
31868
  "@biomejs/js-api": "^0.7.0",
31869
31869
  "@biomejs/wasm-nodejs": "^1.9.0",
31870
31870
  esbuild: "^0.19.0",
@@ -32137,11 +32137,15 @@ async function showProfile(json) {
32137
32137
  ["Plan", profile.plan],
32138
32138
  ["Display Name", profile.displayName ?? dim("—")],
32139
32139
  ["Bio", profile.bio ?? dim("—")],
32140
- ["Slug", profile.slug ?? dim("—")]
32140
+ ["Slug", profile.slug ?? dim("—")],
32141
+ [
32142
+ "Notify on reindex complete",
32143
+ profile.notifyReindexComplete ? "on" : "off"
32144
+ ]
32141
32145
  ]))
32142
32146
  });
32143
32147
  }
32144
- async function applyProfileUpdate(options) {
32148
+ async function applyProfileUpdate(options, getOptionValueSource) {
32145
32149
  const data = {};
32146
32150
  if (options.name)
32147
32151
  data.display_name = options.name;
@@ -32149,6 +32153,9 @@ async function applyProfileUpdate(options) {
32149
32153
  data.bio = options.bio;
32150
32154
  if (options.slug)
32151
32155
  data.slug = options.slug;
32156
+ if (getOptionValueSource("notifyReindexComplete") === "cli") {
32157
+ data.notify_reindex_complete = options.notifyReindexComplete;
32158
+ }
32152
32159
  const updated = await updateAccountProfile(data);
32153
32160
  output({
32154
32161
  json: options.json,
@@ -32158,7 +32165,11 @@ async function applyProfileUpdate(options) {
32158
32165
  console.log(formatKeyValue([
32159
32166
  ["Display Name", updated.displayName ?? dim("—")],
32160
32167
  ["Bio", updated.bio ?? dim("—")],
32161
- ["Slug", updated.slug ?? dim("—")]
32168
+ ["Slug", updated.slug ?? dim("—")],
32169
+ [
32170
+ "Notify on reindex complete",
32171
+ updated.notifyReindexComplete ? "on" : "off"
32172
+ ]
32162
32173
  ]));
32163
32174
  }
32164
32175
  });
@@ -32167,7 +32178,7 @@ function registerAccountCommand(program2) {
32167
32178
  const account = program2.command("account").description("Manage your account profile");
32168
32179
  addBillingCommand(account);
32169
32180
  account.command("get").description("Show your account profile").option("--json", "Output as JSON").action(withErrorHandling(async (options) => showProfile(options.json), { action: "show profile" }));
32170
- account.command("update").description("Update your public profile").option("--name <name>", "Set display name").option("--bio <bio>", "Set bio").option("--slug <slug>", "Set public URL slug").option("--json", "Output as JSON").action(withErrorHandling(async (options) => applyProfileUpdate(options), {
32181
+ account.command("update").description("Update your public profile").option("--name <name>", "Set display name").option("--bio <bio>", "Set bio").option("--slug <slug>", "Set public URL slug").option("--notify-reindex-complete", "Email me when a subgraph reindex finishes").option("--no-notify-reindex-complete", "Don't email me when a subgraph reindex finishes").option("--json", "Output as JSON").action(withErrorHandling(async (options, command) => applyProfileUpdate(options, (key) => command.getOptionValueSource(key)), {
32171
32182
  action: "update profile"
32172
32183
  }));
32173
32184
  }
@@ -32786,6 +32797,9 @@ import { dirname as dirname2, join as join4, relative, resolve } from "node:path
32786
32797
  import { fileURLToPath } from "node:url";
32787
32798
  import { input, select as select2 } from "@inquirer/prompts";
32788
32799
  import { SecondLayer as SecondLayer3 } from "@secondlayer/sdk";
32800
+ import {
32801
+ ChainTriggerSchema
32802
+ } from "@secondlayer/shared/schemas/subscriptions";
32789
32803
  init_output();
32790
32804
  init_resolve_auth();
32791
32805
 
@@ -32838,6 +32852,38 @@ function copyTemplate(src, dest, vars) {
32838
32852
  }
32839
32853
  }
32840
32854
  }
32855
+ function parseTriggersInput(opts) {
32856
+ const raw = [];
32857
+ if (opts.triggersFile) {
32858
+ let parsed;
32859
+ try {
32860
+ parsed = JSON.parse(readFileSync(opts.triggersFile, "utf8"));
32861
+ } catch (err) {
32862
+ throw new Error(`Could not read/parse --triggers-file ${opts.triggersFile}: ${err instanceof Error ? err.message : String(err)}`);
32863
+ }
32864
+ if (!Array.isArray(parsed)) {
32865
+ throw new Error("--triggers-file must contain a JSON array of triggers");
32866
+ }
32867
+ raw.push(...parsed);
32868
+ }
32869
+ for (const value of opts.trigger ?? []) {
32870
+ try {
32871
+ raw.push(JSON.parse(value));
32872
+ } catch {
32873
+ throw new Error(`--trigger is not valid JSON: ${value}`);
32874
+ }
32875
+ }
32876
+ if (raw.length === 0) {
32877
+ throw new Error("Provide at least one --trigger or --triggers-file");
32878
+ }
32879
+ return raw.map((entry, i) => {
32880
+ const result = ChainTriggerSchema.safeParse(entry);
32881
+ if (!result.success) {
32882
+ throw new Error(`Invalid trigger at index ${i}: ${result.error.issues[0]?.message ?? "validation failed"}`);
32883
+ }
32884
+ return result.data;
32885
+ });
32886
+ }
32841
32887
  async function promptFor(_name, opts) {
32842
32888
  const runtime = opts.runtime ?? await select2({
32843
32889
  message: "Runtime?",
@@ -32988,15 +33034,75 @@ SIGNING_SECRET=${signingSecret}
32988
33034
  success(`Done. Next:
32989
33035
  ${dashboardLine}${pausedLine}${runHint}`);
32990
33036
  }
33037
+ async function createChainSubscription(name, opts) {
33038
+ for (const [flag, value] of [
33039
+ ["--subgraph", opts.subgraph],
33040
+ ["--table", opts.table],
33041
+ ["--runtime", opts.runtime]
33042
+ ]) {
33043
+ if (value !== undefined) {
33044
+ error(`${flag} is not valid with --trigger/--triggers-file (chain mode)`);
33045
+ process.exit(1);
33046
+ }
33047
+ }
33048
+ if (!opts.url) {
33049
+ error("--url is required when creating a chain subscription");
33050
+ process.exit(1);
33051
+ }
33052
+ let triggers;
33053
+ let authConfig;
33054
+ try {
33055
+ triggers = parseTriggersInput(opts);
33056
+ authConfig = buildSubscriptionAuthConfig(opts.authToken);
33057
+ } catch (err) {
33058
+ error(err instanceof Error ? err.message : String(err));
33059
+ process.exit(1);
33060
+ }
33061
+ const sl = await getSubscriptionClient();
33062
+ let subscriptionId;
33063
+ let signingSecret;
33064
+ try {
33065
+ const res = await sl.subscriptions.create({
33066
+ name,
33067
+ url: opts.url,
33068
+ triggers,
33069
+ format: opts.format ?? "standard-webhooks",
33070
+ ...authConfig ? { authConfig } : {}
33071
+ });
33072
+ subscriptionId = res.subscription.id;
33073
+ signingSecret = res.signingSecret;
33074
+ success(`Chain subscription provisioned: ${blue(subscriptionId)}`);
33075
+ } catch (err) {
33076
+ error(`Subscription provisioning failed: ${err instanceof Error ? err.message : String(err)}`);
33077
+ process.exit(1);
33078
+ }
33079
+ info(`Signing secret (store securely): ${signingSecret}`);
33080
+ let dashboardLine = "";
33081
+ try {
33082
+ const { apiUrl } = await resolveAuth();
33083
+ const base = deriveBaseUrl(apiUrl);
33084
+ dashboardLine = `Dashboard: ${base}/platform/subscriptions/${subscriptionId}
33085
+ `;
33086
+ } catch {}
33087
+ console.log();
33088
+ success(`Done. Next:
33089
+ ${dashboardLine}View deliveries:
33090
+ sl subscriptions get ${name}`);
33091
+ }
32991
33092
  async function getSubscriptionClient() {
32992
33093
  const { apiUrl, ephemeralKey } = await resolveAuth();
32993
33094
  return new SecondLayer3({ baseUrl: apiUrl, apiKey: ephemeralKey });
32994
33095
  }
32995
33096
  function addSubscriptionScaffold(parent, commandSpec, opts) {
32996
- parent.command(commandSpec).description(opts.description).option("-r, --runtime <runtime>", "inngest | trigger | cloudflare | node").option("-s, --subgraph <name>", "Subgraph to subscribe to").option("-t, --table <name>", "Table to subscribe to").option("-u, --url <url>", "Webhook URL").option("--auth-token <token>", "Bearer token for receiver API auth").option("--filter <kv...>", "Filter as key=value (supports .eq/.neq/.gt/.gte/.lt/.lte suffixes)").option("--skip-api", "Copy template only, don't call the API").option("--no-scaffold", "Skip the local runtime template directory (webhook-only setups)").addHelpText("after", `
33097
+ parent.command(commandSpec).description(opts.description).option("-r, --runtime <runtime>", "inngest | trigger | cloudflare | node").option("-s, --subgraph <name>", "Subgraph to subscribe to").option("-t, --table <name>", "Table to subscribe to").option("-u, --url <url>", "Webhook URL").option("--auth-token <token>", "Bearer token for receiver API auth").option("--filter <kv...>", "Filter as key=value (supports .eq/.neq/.gt/.gte/.lt/.lte suffixes)").option("--trigger <json...>", `Chain-subscription trigger as JSON (repeatable), e.g. '{"type":"sbtc_deposit"}'`).option("--triggers-file <path>", "Chain-subscription triggers as a JSON array file").option("--format <format>", "Chain mode delivery format (default standard-webhooks)").option("--skip-api", "Copy template only, don't call the API").option("--no-scaffold", "Skip the local runtime template directory (webhook-only setups)").addHelpText("after", `
32997
33098
  Examples:
32998
33099
  $ ${opts.examplePrefix} my-sub -s my-graph -t transfers -u https://example.com/webhook
32999
- $ ${opts.examplePrefix} my-sub -s my-graph -t balances -r inngest --filter amount.gte=1000`).action(async (name, options) => {
33100
+ $ ${opts.examplePrefix} my-sub -s my-graph -t balances -r inngest --filter amount.gte=1000
33101
+ $ ${opts.examplePrefix} my-hook -u https://example.com/webhook --trigger '{"type":"sbtc_deposit"}'`).action(async (name, options) => {
33102
+ if (options.trigger?.length || options.triggersFile) {
33103
+ await createChainSubscription(name, options);
33104
+ return;
33105
+ }
33000
33106
  await createSubscription(name, options);
33001
33107
  });
33002
33108
  }
@@ -33561,6 +33667,9 @@ Examples:
33561
33667
  }
33562
33668
  const client = await getSubscriptionClient();
33563
33669
  const { id, detail } = await resolveSubscriptionRef(client, idOrName);
33670
+ if (detail.triggers?.some((t) => t.type === "sbtc_withdrawal_swept_confirmed")) {
33671
+ warn("sbtc_withdrawal_swept_confirmed triggers are forward-only and not replayed; only on-Stacks sBTC and chain triggers emit over this range.");
33672
+ }
33564
33673
  const ok = await confirmOrExit(`Replay ${detail.name} from block ${fromBlock} to ${toBlock}?`, options.yes);
33565
33674
  if (!ok)
33566
33675
  return;
@@ -35290,8 +35399,9 @@ function formatSubgraphSync(sync) {
35290
35399
  };
35291
35400
  }
35292
35401
  const range = processedBlocks !== undefined && totalBlocks !== undefined ? `${processedBlocks} / ${totalBlocks} blocks` : `${sync.lastProcessedBlock} / ${targetBlock}`;
35402
+ const eta = sync.etaSeconds != null ? ` · est ${formatDuration(sync.etaSeconds)}` : "";
35293
35403
  return {
35294
- line: `reindexing — ${progress} (${range}, target #${targetBlock})`,
35404
+ line: `reindexing — ${progress} (${range}, target #${targetBlock})${eta}`,
35295
35405
  remainingLabel: "Reindex Remaining",
35296
35406
  remaining: String(sync.blocksRemaining)
35297
35407
  };
@@ -35517,6 +35627,10 @@ Stopped watching.`);
35517
35627
  info(` REST: ${apiUrl}/api/subgraphs/${effectiveDef.name}/${firstTable}`);
35518
35628
  info(` Publish: sl subgraphs publish ${effectiveDef.name} (open anon /v1 reads)`);
35519
35629
  }
35630
+ if (result.reindexStarted) {
35631
+ const denom = result.estimatedEvents != null ? ` ~${result.estimatedEvents.toLocaleString()} events —` : "";
35632
+ info(` Reindex: ${denom} started in the background — check status in ~30s for an ETA`);
35633
+ }
35520
35634
  info(` Watch: sl subgraphs status ${effectiveDef.name}`);
35521
35635
  if (firstTable) {
35522
35636
  info(` Webhook: sl subscriptions create ${effectiveDef.name}-hook --subgraph ${effectiveDef.name} --table ${firstTable} --url <your-endpoint>`);
@@ -38022,5 +38136,5 @@ registerAccountCommand(program);
38022
38136
  registerKeysCommand(program);
38023
38137
  program.parse();
38024
38138
 
38025
- //# debugId=00D518F35B468C4664756E2164756E21
38139
+ //# debugId=7477A77F4D7194B764756E2164756E21
38026
38140
  //# sourceMappingURL=cli.js.map