@secondlayer/cli 10.2.1 → 10.3.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 CHANGED
@@ -10,51 +10,27 @@ sl --version
10
10
 
11
11
  ## Auth
12
12
 
13
- Two ways to authenticate:
13
+ Default API is `http://127.0.0.1:3800`. Override with `SL_API_URL`.
14
14
 
15
- - **Interactive (`sl login`)** — magic-link email → 6-digit code → session
16
- written to `~/.secondlayer/session.json`. Used for day-to-day CLI work.
17
- - **Machine / CI** — set `SL_API_KEY` to an API key created in the platform
18
- console at https://secondlayer.tools/platform/api-keys. Keys are prefixed
19
- `sk-sl_`. For a persisted headless login: `echo "$SL_API_KEY" | sl login --with-token`.
15
+ Writes against a published bind use `INSTANCE_TOKEN` from `sl init` as
16
+ `SL_API_KEY`. Loopback reads need no token.
20
17
 
21
18
  ```bash
22
- # interactive
23
- sl login
24
-
25
- # machine / CI
26
- export SL_API_KEY=sk-sl_xxxxxxxx
19
+ export SL_API_URL=http://127.0.0.1:3800
20
+ export SL_API_KEY=<INSTANCE_TOKEN>
27
21
  ```
28
22
 
29
- `sl whoami` shows the active account, the effective API URL, and the credential
30
- source (and exits non-zero when not logged in). The global `--api-key` /
31
- `--api-url` flags (and `SL_API_KEY` / `SL_API_URL`) apply to every command.
32
-
33
- During open beta, reads are public (no key needed) — for subgraphs that means
34
- **public** subgraphs only (managed deploys default public; BYO deploys and all
35
- pre-existing subgraphs are private, readable with your key). Writes — deploying,
36
- publishing, or managing subgraphs and subscriptions — require a session or an
37
- `sk-sl_` key.
38
-
39
23
  ## Quickstart
40
24
 
41
25
  ```bash
42
26
  bun add -g @secondlayer/cli
43
- sl login
44
- sl projects create my-app
45
- sl projects use my-app
27
+ sl init --network mainnet
28
+ sl start --print
29
+ # docker compose -f docker/oss/docker-compose.yml up -d
46
30
 
47
- # scaffold + deploy a subgraph from a deployed contract
48
31
  sl subgraphs scaffold SP1234ABCD.my-contract -o subgraphs/my-contract.ts
49
32
  sl subgraphs deploy subgraphs/my-contract.ts --start-block <recent-block>
50
33
  sl subgraphs query my-contract <table> --sort _block_height --order desc
51
-
52
- # wire a webhook receiver
53
- sl subscriptions create my-hook \
54
- --runtime node \
55
- --subgraph my-contract \
56
- --table <table> \
57
- --url https://<receiver-host>/webhook
58
34
  ```
59
35
 
60
36
  `sl subgraphs scaffold` writes the definition file, creates/updates
@@ -75,27 +51,6 @@ No account. Writes `.env.local`, restores history, prints the Stacks observer st
75
51
  | `sl verify [all\|raw\|decode:<name>\|subgraph:<name>] --against <manifest> [--quick\|--deep\|--anchor]` | Compare local data to a signed archive. Default target `raw`. Exit `0` clean, `1` diverged, `2` unanchored |
76
52
  | `sl repair --against <archive> [--apply]` | Plan (default) or apply an archive repair |
77
53
 
78
- ### Auth & project
79
-
80
- | Command | What it does |
81
- |---|---|
82
- | `sl login` / `sl logout` | Start or revoke a session |
83
- | `sl whoami` | Print account, credential source, and active project |
84
- | `sl keys create --product streams [--name <name>]` | Mint a scoped `streams`/`index` read key; prints the `sk-sl_` key **once**. Needs an account key in `SL_API_KEY` |
85
- | `sl projects create [name]` | Create a project |
86
- | `sl projects list` | List projects |
87
- | `sl projects use <slug>` | Bind cwd to a project (writes `./.secondlayer/project`) |
88
- | `sl projects get` | Show resolved project + source file |
89
- | `sl projects delete <slug>` (alias `rm`) | Delete a project (`-y` to skip confirm) |
90
-
91
- Project binding is per-directory: `.secondlayer/project` in cwd takes
92
- precedence over `~/.secondlayer/config.json:defaultProject`. The walk-up stops
93
- at `.git`.
94
-
95
- **Key products:** an `account` key (dashboard default) grants both `streams:read`
96
- and `index:read` and is the only key that can mint; `streams`/`index` keys are
97
- scoped reads and cannot mint (403). Minted keys inherit your plan's tier.
98
-
99
54
  ### Subgraphs
100
55
 
101
56
  | Command | What it does |
@@ -126,7 +81,8 @@ Reads emit JSON to stdout (`--json` accepted across all read commands); `-o/--ou
126
81
 
127
82
  | Command | What it does |
128
83
  |---|---|
129
- | `sl subscriptions create <name> --runtime <inngest\|trigger\|cloudflare\|node> [--auth-token <token>] [--filter key=value]` | Scaffold a receiver wired to a new subscription |
84
+ | `sl subscriptions create <name> --subgraph <name> --table <name> [--runtime <inngest\|trigger\|cloudflare\|node>] [--url <url>]` | Subgraph subscription (optional local receiver scaffold) |
85
+ | `sl subscriptions create <name> --url <url> --trigger '<json>'` | Chain subscription (repeat `--trigger` or pass `--triggers-file`) |
130
86
  | `sl subscriptions list` / `get <id\|name>` | List or show config + delivery state |
131
87
  | `sl subscriptions update <id\|name> --url <url> [--filter key.gte=value]` | Patch URL, filter, format, retry, etc. |
132
88
  | `sl subscriptions pause/resume <id\|name>` | Stop or restart delivery |
@@ -140,32 +96,28 @@ Read/action commands support `--json`. Destructive commands prompt unless
140
96
  `-y` / `--yes`. Filters are schema-aware: unknown tables/columns, bad operators,
141
97
  and non-scalar columns are rejected before the API call.
142
98
 
143
- Subscriptions are polymorphic — **subgraph** (fires on a subgraph table's rows)
144
- or **chain** (fires on raw chain events with no subgraph). `sl subscriptions
145
- create` makes subgraph subscriptions only (via the `--subgraph`/`--table`
146
- flags). Create chain subscriptions — a webhook on a contract / event / function /
147
- trait — via the SDK, REST, or MCP with a `triggers` array. Every other
148
- `sl subscriptions` command (list/get/update/pause/resume/delete/doctor/test/
149
- deliveries/...) operates on both kinds.
99
+ Subscriptions are **subgraph** (a table's rows) or **chain** (raw events, no
100
+ subgraph). `sl subscriptions create` with `--subgraph`/`--table` makes the
101
+ first. Pass `--trigger` or `--triggers-file` for the second. SDK, REST, and
102
+ MCP take the same `triggers` array. Every other `sl subscriptions` command
103
+ operates on both kinds.
150
104
 
151
105
  ### Other
152
106
 
153
107
  | Command | What it does |
154
108
  |---|---|
155
109
  | `sl contracts generate [files...]` (alias `gen`) | Generate TS interfaces from Clarity contracts |
156
- | `sl context` | Print a headless orientation snapshot — account, Streams + Index tips, your subgraphs, subscriptions, and in-flight reindex operations. CLI counterpart to the MCP `secondlayer://context` resource |
110
+ | `sl context` | Instance snapshot — Streams + Index tips, subgraphs, subscriptions |
157
111
  | `sl doctor` / `sl status` | Reachability + health checks |
158
- | `sl account get` / `sl account update` | Show or update display name / bio / slug |
159
- | `sl account billing` | Show plan, subscription, trial, discounts |
160
112
  | `sl config get/set/reset/delete` | Inspect or reset local config |
161
113
 
162
114
  ## Environment variables
163
115
 
164
116
  | Var | Purpose |
165
117
  |---|---|
166
- | `SL_API_KEY` | An `sk-sl_` API key (or session token) for machine/CI use; bypasses platform resolution |
167
- | `SL_API_URL` | Point at an OSS or internal API directly, bypassing the platform |
168
- | `SL_PLATFORM_API_URL` | Override the platform API base (default `https://api.secondlayer.tools`) |
118
+ | `SL_API_KEY` | `INSTANCE_TOKEN` from `sl init` for writes. Loopback reads need no token |
119
+ | `SL_API_URL` | Instance API. Default `http://127.0.0.1:3800` |
120
+ | `SL_PLATFORM_API_URL` | Alias of `SL_API_URL` |
169
121
  | `STACKS_NETWORK` | Default network (also via `--network <local\|testnet\|mainnet>`) |
170
122
  | `HIRO_API_KEY` | Used by `sl contracts generate` for remote contract fetches |
171
123
 
package/dist/cli.js CHANGED
@@ -2580,7 +2580,7 @@ function isOssMode() {
2580
2580
  return true;
2581
2581
  }
2582
2582
  }
2583
- var LOCAL_API_URL = "http://127.0.0.1:3800";
2583
+ var LOCAL_API_URL = "http://127.0.0.1:3800", ARCHIVE_OPS_API_URL = "https://api.secondlayer.tools";
2584
2584
  var init_resolve_auth = __esm(() => {
2585
2585
  init_session();
2586
2586
  });
@@ -2629,9 +2629,28 @@ async function httpPlatform(path2, opts = {}) {
2629
2629
  async function httpPlatformAnon(path2, opts = {}) {
2630
2630
  return request(`${resolveApiUrl()}${path2}`, opts);
2631
2631
  }
2632
+ function resolveArchiveOpsUrl() {
2633
+ return (process.env.SL_CREDITS_API_URL ?? ARCHIVE_OPS_API_URL).replace(/\/+$/, "");
2634
+ }
2635
+ async function httpArchiveOpsAnon(path2, opts = {}) {
2636
+ return request(`${resolveArchiveOpsUrl()}${path2}`, opts);
2637
+ }
2638
+ async function httpArchiveOps(path2, opts = {}) {
2639
+ const envKey = resolveEnvKey();
2640
+ const session = envKey ? null : await readSession();
2641
+ const bearer = envKey ?? session?.token;
2642
+ if (!bearer) {
2643
+ throw new CliHttpError(401, "SESSION_EXPIRED", { error: "Not logged in" }, "Not logged in — run `SL_API_URL=https://api.secondlayer.tools sl login`");
2644
+ }
2645
+ return request(`${resolveArchiveOpsUrl()}${path2}`, {
2646
+ ...opts,
2647
+ bearer
2648
+ });
2649
+ }
2632
2650
  var CliHttpError, REQUEST_TIMEOUT_MS = 30000;
2633
2651
  var init_http = __esm(() => {
2634
2652
  init_resolve_auth();
2653
+ init_session();
2635
2654
  CliHttpError = class CliHttpError extends Error {
2636
2655
  status;
2637
2656
  code;
@@ -29315,21 +29334,6 @@ var init_subgraph = __esm(() => {
29315
29334
  };
29316
29335
  });
29317
29336
 
29318
- // src/utils/urls.ts
29319
- function deriveBaseUrl(apiUrl) {
29320
- const override = process.env.SL_DASHBOARD_URL?.trim();
29321
- if (override)
29322
- return override.replace(/\/$/, "");
29323
- try {
29324
- const url = new URL(apiUrl);
29325
- url.hostname = url.hostname.replace(/^api\./, "");
29326
- url.pathname = "/";
29327
- return url.toString().replace(/\/$/, "");
29328
- } catch {
29329
- return apiUrl.replace(/\/$/, "");
29330
- }
29331
- }
29332
-
29333
29337
  // src/commands/subgraph-test.ts
29334
29338
  var exports_subgraph_test = {};
29335
29339
  __export(exports_subgraph_test, {
@@ -30265,10 +30269,8 @@ Stopped watching.`);
30265
30269
  const printDeployFooter = async () => {
30266
30270
  try {
30267
30271
  const { apiUrl } = await resolveAuth();
30268
- const baseUrl = deriveBaseUrl(apiUrl);
30269
30272
  const firstTable = Object.keys(effectiveDef.schema ?? {})[0];
30270
30273
  const isPublic = result.visibility === "public";
30271
- info(` Dashboard: ${baseUrl}/platform/subgraphs/${effectiveDef.name}`);
30272
30274
  if (isPublic && firstTable) {
30273
30275
  info(` Read: ${apiUrl}/v1/subgraphs/${effectiveDef.name}/${firstTable}`);
30274
30276
  info(` Share: ${apiUrl}/v1/subgraphs/${effectiveDef.name} (public — no key needed)`);
@@ -34787,7 +34789,7 @@ init_esm();
34787
34789
  // package.json
34788
34790
  var package_default = {
34789
34791
  name: "@secondlayer/cli",
34790
- version: "10.2.1",
34792
+ version: "10.3.1",
34791
34793
  repository: {
34792
34794
  type: "git",
34793
34795
  url: "git+https://github.com/ryanwaits/secondlayer.git",
@@ -34830,8 +34832,8 @@ var package_default = {
34830
34832
  "@inquirer/prompts": "^8.2.0",
34831
34833
  "@secondlayer/bundler": "^0.5.0",
34832
34834
  "@secondlayer/scaffold": "^1.5.1",
34833
- "@secondlayer/sdk": "^6.54.0",
34834
- "@secondlayer/shared": "^7.11.0",
34835
+ "@secondlayer/sdk": "^6.54.1",
34836
+ "@secondlayer/shared": "^7.11.1",
34835
34837
  "@secondlayer/stacks": "^3.3.1",
34836
34838
  "@secondlayer/subgraphs": "^3.26.4",
34837
34839
  esbuild: "^0.27.4",
@@ -34874,15 +34876,13 @@ async function runBillingStatus(json) {
34874
34876
  output({ json, data: res, human: () => renderBillingStatus(res) });
34875
34877
  }
34876
34878
  function addBillingCommand(parent) {
34877
- parent.command("billing").description("Show your plan, Stripe subscription, trial, and discounts").option("--json", "Output as JSON").action((options) => runBillingStatus(options.json));
34879
+ parent.command("billing").description("Show archive credit balance (hidden; use sl credits)").option("--json", "Output as JSON").action((options) => runBillingStatus(options.json));
34878
34880
  }
34879
34881
  function renderBillingStatus(res) {
34880
34882
  if (!res.subscription) {
34881
34883
  console.log(formatKeyValue([
34882
- ["Plan", "Free"],
34883
- ["Reads", dim("10 req/s · last ~24h window")],
34884
- ["Subgraphs", dim("upgrade to deploy / go private")],
34885
- ["Upgrade", dim("https://secondlayer.tools/platform/billing")]
34884
+ ["Credits", dim("sl credits balance")],
34885
+ ["Buy", dim("sl credits buy --email you@example.com --pack 25")]
34886
34886
  ]));
34887
34887
  return;
34888
34888
  }
@@ -34980,6 +34980,115 @@ function registerAccountCommand(program2) {
34980
34980
  action: "update profile"
34981
34981
  }));
34982
34982
  }
34983
+ // src/commands/credits.ts
34984
+ init_http();
34985
+ init_output();
34986
+ var PACKS = [10, 25, 50, 100];
34987
+ function parsePack(raw) {
34988
+ const n = Number(raw);
34989
+ if (!PACKS.includes(n)) {
34990
+ error(`--pack must be one of ${PACKS.join(", ")}`);
34991
+ process.exit(1);
34992
+ }
34993
+ return n;
34994
+ }
34995
+ async function runBuy(opts) {
34996
+ const email = opts.email.trim().toLowerCase();
34997
+ if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) {
34998
+ error("--email must be a valid address");
34999
+ process.exit(1);
35000
+ }
35001
+ const amount = parsePack(opts.pack);
35002
+ let res;
35003
+ try {
35004
+ res = await httpArchiveOpsAnon("/api/public/credits/checkout", { method: "POST", body: { email, amount } });
35005
+ } catch (err) {
35006
+ if (err instanceof CliHttpError) {
35007
+ error(err.message);
35008
+ process.exit(1);
35009
+ }
35010
+ throw err;
35011
+ }
35012
+ output({
35013
+ json: opts.json,
35014
+ data: res,
35015
+ human: () => {
35016
+ success(`Checkout for $${amount} → ${email}`);
35017
+ console.log(res.url);
35018
+ console.log(dim("After payment: SL_API_URL=https://api.secondlayer.tools sl login && sl credits balance"));
35019
+ }
35020
+ });
35021
+ }
35022
+ async function runBalance(opts) {
35023
+ let res;
35024
+ try {
35025
+ res = await httpArchiveOps("/api/billing/status");
35026
+ } catch (err) {
35027
+ if (err instanceof CliHttpError) {
35028
+ error(err.message);
35029
+ process.exit(1);
35030
+ }
35031
+ throw err;
35032
+ }
35033
+ const micros = BigInt(res.creditsUsdMicros ?? "0");
35034
+ const usd = Number(micros) / 1e6;
35035
+ output({
35036
+ json: opts.json,
35037
+ data: { creditsUsd: usd, creditsUsdMicros: micros.toString() },
35038
+ human: () => {
35039
+ const refill = res.refill;
35040
+ console.log(formatKeyValue([
35041
+ ["Balance", `$${usd.toFixed(2)}`],
35042
+ [
35043
+ "Refill",
35044
+ refill?.belowUsd != null ? `on — below $${refill.belowUsd} buy $${refill.packUsd}` : dim("off")
35045
+ ],
35046
+ ["Buy", dim("sl credits buy --email you@example.com --pack 25")]
35047
+ ]));
35048
+ }
35049
+ });
35050
+ }
35051
+ function registerCreditsCommand(program2) {
35052
+ const credits = program2.command("credits").description("Buy and check archive credits");
35053
+ credits.command("buy").description("Open a one-time Stripe checkout for archive credits").requiredOption("--email <email>", "Receipt email; becomes the account").option("--pack <usd>", `One of ${PACKS.join(", ")}`, "25").option("--json", "Output as JSON").action((opts) => runBuy(opts));
35054
+ credits.command("balance").description("Show prepaid archive credit balance").option("--json", "Output as JSON").action((opts) => runBalance(opts));
35055
+ credits.command("refill").description("Opt-in auto-refill when the balance drops under a threshold").option("--below <usd>", "Trigger when balance is under this USD amount").option("--pack <usd>", `Pack to buy (${PACKS.join(", ")})`, "25").option("--off", "Turn auto-refill off").option("--json", "Output as JSON").action((opts) => runRefill(opts));
35056
+ }
35057
+ async function runRefill(opts) {
35058
+ if (!opts.off && opts.below === undefined) {
35059
+ await runBalance(opts);
35060
+ return;
35061
+ }
35062
+ const body = opts.off ? { belowUsd: null } : { belowUsd: Number(opts.below), packUsd: parsePack(opts.pack) };
35063
+ if (!opts.off && (!Number.isFinite(body.belowUsd) || (body.belowUsd ?? 0) < 1)) {
35064
+ error("--below must be at least 1");
35065
+ process.exit(1);
35066
+ }
35067
+ let res;
35068
+ try {
35069
+ res = await httpArchiveOps("/api/billing/refill", {
35070
+ method: "POST",
35071
+ body
35072
+ });
35073
+ } catch (err) {
35074
+ if (err instanceof CliHttpError) {
35075
+ error(err.message);
35076
+ process.exit(1);
35077
+ }
35078
+ throw err;
35079
+ }
35080
+ output({
35081
+ json: opts.json,
35082
+ data: res,
35083
+ human: () => {
35084
+ if (res.belowUsd == null) {
35085
+ success("Auto-refill off");
35086
+ return;
35087
+ }
35088
+ success(`Auto-refill on — below $${res.belowUsd} buy $${res.packUsd}`);
35089
+ }
35090
+ });
35091
+ }
34983
35092
 
34984
35093
  // src/commands/index.ts
34985
35094
  init_codegen();
@@ -35676,17 +35785,15 @@ async function createSubscription(name, opts) {
35676
35785
  } catch (err) {
35677
35786
  provisioningFailed = true;
35678
35787
  warn(`Subscription provisioning failed: ${err instanceof Error ? err.message : String(err)}`);
35679
- info("Template copied, but the subscription was not created. Provision it in the dashboard, or remove the template directory and rerun this command after fixing the API error.");
35788
+ info("Template copied, but the subscription was not created. Fix the API error, remove the template directory, and rerun.");
35680
35789
  }
35681
35790
  }
35682
- let subscriptionId;
35683
35791
  let subscriptionStatus;
35684
35792
  if (sl) {
35685
35793
  try {
35686
35794
  const list = await sl.subscriptions.list();
35687
35795
  const rows = list.data ?? [];
35688
35796
  const created = rows.find((s2) => s2.name === name);
35689
- subscriptionId = created?.id;
35690
35797
  subscriptionStatus = created?.status;
35691
35798
  } catch {}
35692
35799
  }
@@ -35714,14 +35821,6 @@ SIGNING_SECRET=${signingSecret}
35714
35821
  error("Subscription was not created.");
35715
35822
  process.exit(1);
35716
35823
  }
35717
- let dashboardLine = "";
35718
- try {
35719
- const { apiUrl } = await resolveAuth();
35720
- const base2 = deriveBaseUrl(apiUrl);
35721
- dashboardLine = subscriptionId ? `Dashboard: ${base2}/platform/subgraphs/${subgraph}/subscriptions/${subscriptionId}
35722
- ` : `Dashboard: ${base2}/platform/subgraphs/${subgraph}/subscriptions
35723
- `;
35724
- } catch {}
35725
35824
  const pausedLine = subscriptionStatus === "paused" ? `Subscription is paused. Resume:
35726
35825
  sl subscriptions resume ${name}
35727
35826
  ` : "";
@@ -35730,7 +35829,7 @@ SIGNING_SECRET=${signingSecret}
35730
35829
  bun install
35731
35830
  bun run dev`;
35732
35831
  success(`Done. Next:
35733
- ${dashboardLine}${pausedLine}${runHint}`);
35832
+ ${pausedLine}${runHint}`);
35734
35833
  }
35735
35834
  async function createChainSubscription(name, opts) {
35736
35835
  for (const [flag, value] of [
@@ -35775,16 +35874,9 @@ async function createChainSubscription(name, opts) {
35775
35874
  process.exit(1);
35776
35875
  }
35777
35876
  info(`Signing secret (store securely): ${signingSecret}`);
35778
- let dashboardLine = "";
35779
- try {
35780
- const { apiUrl } = await resolveAuth();
35781
- const base2 = deriveBaseUrl(apiUrl);
35782
- dashboardLine = `Dashboard: ${base2}/platform/subscriptions/${subscriptionId}
35783
- `;
35784
- } catch {}
35785
35877
  console.log();
35786
35878
  success(`Done. Next:
35787
- ${dashboardLine}View deliveries:
35879
+ View deliveries:
35788
35880
  sl subscriptions get ${name}`);
35789
35881
  }
35790
35882
  async function getSubscriptionClient() {
@@ -39833,9 +39925,10 @@ registerContextCommand(program);
39833
39925
  registerVerifyCommand(program);
39834
39926
  registerRepairCommand(program);
39835
39927
  program.commandsGroup("Account:");
39928
+ registerCreditsCommand(program);
39836
39929
  registerAccountCommand(program);
39837
39930
  registerKeysCommand(program);
39838
39931
  program.parse();
39839
39932
 
39840
- //# debugId=60FAFC7C2FFCA95764756E2164756E21
39933
+ //# debugId=71DA8EB190B9ACCF64756E2164756E21
39841
39934
  //# sourceMappingURL=cli.js.map