@secondlayer/cli 8.12.0 → 8.13.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.13.0",
31815
31815
  repository: {
31816
31816
  type: "git",
31817
31817
  url: "git+https://github.com/ryanwaits/secondlayer.git",
@@ -31864,7 +31864,7 @@ var package_default = {
31864
31864
  "@secondlayer/sdk": "^6.23.1",
31865
31865
  "@secondlayer/shared": "^6.35.1",
31866
31866
  "@secondlayer/stacks": "^2.5.1",
31867
- "@secondlayer/subgraphs": "^3.14.1",
31867
+ "@secondlayer/subgraphs": "^3.18.0",
31868
31868
  "@biomejs/js-api": "^0.7.0",
31869
31869
  "@biomejs/wasm-nodejs": "^1.9.0",
31870
31870
  esbuild: "^0.19.0",
@@ -32786,6 +32786,9 @@ import { dirname as dirname2, join as join4, relative, resolve } from "node:path
32786
32786
  import { fileURLToPath } from "node:url";
32787
32787
  import { input, select as select2 } from "@inquirer/prompts";
32788
32788
  import { SecondLayer as SecondLayer3 } from "@secondlayer/sdk";
32789
+ import {
32790
+ ChainTriggerSchema
32791
+ } from "@secondlayer/shared/schemas/subscriptions";
32789
32792
  init_output();
32790
32793
  init_resolve_auth();
32791
32794
 
@@ -32838,6 +32841,38 @@ function copyTemplate(src, dest, vars) {
32838
32841
  }
32839
32842
  }
32840
32843
  }
32844
+ function parseTriggersInput(opts) {
32845
+ const raw = [];
32846
+ if (opts.triggersFile) {
32847
+ let parsed;
32848
+ try {
32849
+ parsed = JSON.parse(readFileSync(opts.triggersFile, "utf8"));
32850
+ } catch (err) {
32851
+ throw new Error(`Could not read/parse --triggers-file ${opts.triggersFile}: ${err instanceof Error ? err.message : String(err)}`);
32852
+ }
32853
+ if (!Array.isArray(parsed)) {
32854
+ throw new Error("--triggers-file must contain a JSON array of triggers");
32855
+ }
32856
+ raw.push(...parsed);
32857
+ }
32858
+ for (const value of opts.trigger ?? []) {
32859
+ try {
32860
+ raw.push(JSON.parse(value));
32861
+ } catch {
32862
+ throw new Error(`--trigger is not valid JSON: ${value}`);
32863
+ }
32864
+ }
32865
+ if (raw.length === 0) {
32866
+ throw new Error("Provide at least one --trigger or --triggers-file");
32867
+ }
32868
+ return raw.map((entry, i) => {
32869
+ const result = ChainTriggerSchema.safeParse(entry);
32870
+ if (!result.success) {
32871
+ throw new Error(`Invalid trigger at index ${i}: ${result.error.issues[0]?.message ?? "validation failed"}`);
32872
+ }
32873
+ return result.data;
32874
+ });
32875
+ }
32841
32876
  async function promptFor(_name, opts) {
32842
32877
  const runtime = opts.runtime ?? await select2({
32843
32878
  message: "Runtime?",
@@ -32988,15 +33023,75 @@ SIGNING_SECRET=${signingSecret}
32988
33023
  success(`Done. Next:
32989
33024
  ${dashboardLine}${pausedLine}${runHint}`);
32990
33025
  }
33026
+ async function createChainSubscription(name, opts) {
33027
+ for (const [flag, value] of [
33028
+ ["--subgraph", opts.subgraph],
33029
+ ["--table", opts.table],
33030
+ ["--runtime", opts.runtime]
33031
+ ]) {
33032
+ if (value !== undefined) {
33033
+ error(`${flag} is not valid with --trigger/--triggers-file (chain mode)`);
33034
+ process.exit(1);
33035
+ }
33036
+ }
33037
+ if (!opts.url) {
33038
+ error("--url is required when creating a chain subscription");
33039
+ process.exit(1);
33040
+ }
33041
+ let triggers;
33042
+ let authConfig;
33043
+ try {
33044
+ triggers = parseTriggersInput(opts);
33045
+ authConfig = buildSubscriptionAuthConfig(opts.authToken);
33046
+ } catch (err) {
33047
+ error(err instanceof Error ? err.message : String(err));
33048
+ process.exit(1);
33049
+ }
33050
+ const sl = await getSubscriptionClient();
33051
+ let subscriptionId;
33052
+ let signingSecret;
33053
+ try {
33054
+ const res = await sl.subscriptions.create({
33055
+ name,
33056
+ url: opts.url,
33057
+ triggers,
33058
+ format: opts.format ?? "standard-webhooks",
33059
+ ...authConfig ? { authConfig } : {}
33060
+ });
33061
+ subscriptionId = res.subscription.id;
33062
+ signingSecret = res.signingSecret;
33063
+ success(`Chain subscription provisioned: ${blue(subscriptionId)}`);
33064
+ } catch (err) {
33065
+ error(`Subscription provisioning failed: ${err instanceof Error ? err.message : String(err)}`);
33066
+ process.exit(1);
33067
+ }
33068
+ info(`Signing secret (store securely): ${signingSecret}`);
33069
+ let dashboardLine = "";
33070
+ try {
33071
+ const { apiUrl } = await resolveAuth();
33072
+ const base = deriveBaseUrl(apiUrl);
33073
+ dashboardLine = `Dashboard: ${base}/platform/subscriptions/${subscriptionId}
33074
+ `;
33075
+ } catch {}
33076
+ console.log();
33077
+ success(`Done. Next:
33078
+ ${dashboardLine}View deliveries:
33079
+ sl subscriptions get ${name}`);
33080
+ }
32991
33081
  async function getSubscriptionClient() {
32992
33082
  const { apiUrl, ephemeralKey } = await resolveAuth();
32993
33083
  return new SecondLayer3({ baseUrl: apiUrl, apiKey: ephemeralKey });
32994
33084
  }
32995
33085
  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", `
33086
+ 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
33087
  Examples:
32998
33088
  $ ${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) => {
33089
+ $ ${opts.examplePrefix} my-sub -s my-graph -t balances -r inngest --filter amount.gte=1000
33090
+ $ ${opts.examplePrefix} my-hook -u https://example.com/webhook --trigger '{"type":"sbtc_deposit"}'`).action(async (name, options) => {
33091
+ if (options.trigger?.length || options.triggersFile) {
33092
+ await createChainSubscription(name, options);
33093
+ return;
33094
+ }
33000
33095
  await createSubscription(name, options);
33001
33096
  });
33002
33097
  }
@@ -33561,6 +33656,9 @@ Examples:
33561
33656
  }
33562
33657
  const client = await getSubscriptionClient();
33563
33658
  const { id, detail } = await resolveSubscriptionRef(client, idOrName);
33659
+ if (detail.triggers?.some((t) => t.type === "sbtc_withdrawal_swept_confirmed")) {
33660
+ warn("sbtc_withdrawal_swept_confirmed triggers are forward-only and not replayed; only on-Stacks sBTC and chain triggers emit over this range.");
33661
+ }
33564
33662
  const ok = await confirmOrExit(`Replay ${detail.name} from block ${fromBlock} to ${toBlock}?`, options.yes);
33565
33663
  if (!ok)
33566
33664
  return;
@@ -38022,5 +38120,5 @@ registerAccountCommand(program);
38022
38120
  registerKeysCommand(program);
38023
38121
  program.parse();
38024
38122
 
38025
- //# debugId=00D518F35B468C4664756E2164756E21
38123
+ //# debugId=DE13EDE0FC51695A64756E2164756E21
38026
38124
  //# sourceMappingURL=cli.js.map