@secondlayer/cli 3.1.0 → 3.1.2-alpha.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/README.md ADDED
@@ -0,0 +1,204 @@
1
+ # @secondlayer/cli
2
+
3
+ The Secondlayer CLI — one binary for dedicated Stacks indexing, real-time
4
+ subgraphs, and per-tenant hosting lifecycle.
5
+
6
+ ```bash
7
+ bun add -g @secondlayer/cli
8
+ sl --version
9
+ ```
10
+
11
+ ## Quickstart
12
+
13
+ ```bash
14
+ sl login # magic-link auth, session cached at ~/.secondlayer/session.json
15
+ sl project create my-app # scaffold a project
16
+ sl project use my-app # bind cwd to the project (writes ./.secondlayer/project)
17
+ sl instance create --plan launch # provision dedicated Postgres + API + processor
18
+ sl subgraphs deploy ./x.ts # deploy to your instance
19
+ ```
20
+
21
+ ## Command surface
22
+
23
+ ### Auth (top-level)
24
+
25
+ | Command | What it does |
26
+ |---|---|
27
+ | `sl login` | Magic-link email → 6-digit code → writes session to `~/.secondlayer/session.json` |
28
+ | `sl logout` | Revokes the session and clears the local file |
29
+ | `sl whoami` | Prints account, active project, instance URL, plan, status |
30
+
31
+ ### Project
32
+
33
+ Projects are the unit that binds a working directory to a dedicated instance.
34
+ Binding is **per-directory** — `.secondlayer/project` in cwd takes precedence
35
+ over the global default at `~/.secondlayer/config.json:defaultProject`. The
36
+ walk-up stops at `.git` (never crosses repos).
37
+
38
+ | Command | What it does |
39
+ |---|---|
40
+ | `sl project create [name]` | Scaffold a new project on the platform |
41
+ | `sl project list` | List all projects for the account |
42
+ | `sl project use <slug>` | Write `./.secondlayer/project` — binds cwd to that project |
43
+ | `sl project current` | Prints the resolved slug + the file it was read from |
44
+
45
+ ### Instance (dedicated hosting)
46
+
47
+ One instance per project. The platform API spawns a dedicated `sl-pg-{slug}`,
48
+ `sl-api-{slug}`, and `sl-proc-{slug}` container set on the hosting side.
49
+
50
+ | Command | What it does |
51
+ |---|---|
52
+ | `sl instance create --plan <launch\|grow\|scale>` | Provision containers. Boxed reveal of `serviceKey` + `anonKey` (shown once). |
53
+ | `sl instance info` | Plan, status, resource usage |
54
+ | `sl instance resize --plan <...>` | Recreate containers with new CPU/memory (~30s downtime) |
55
+ | `sl instance suspend` / `resume` | Stop/start containers, volume preserved |
56
+ | `sl instance keys rotate [--service\|--anon\|--both]` | Bump JWT gen, recreate API container, mint replacement key(s) |
57
+ | `sl instance delete` | Typed-slug confirm, hard teardown |
58
+ | `sl instance db` | Print `ssh -L` command + `DATABASE_URL` for tunneled Postgres access |
59
+ | `sl instance db add-key <path>` | Upload an SSH pubkey to the bastion |
60
+ | `sl instance db revoke-key` | Revoke your bastion access |
61
+
62
+ ### Subgraphs (tenant-scoped)
63
+
64
+ All tenant-scoped commands auto-mint a 5-minute ephemeral service JWT per
65
+ invocation. No long-lived key on disk.
66
+
67
+ | Command | What it does |
68
+ |---|---|
69
+ | `sl subgraphs new <name>` | Scaffold a subgraph definition file |
70
+ | `sl subgraphs deploy <file>` | Deploy to the active instance |
71
+ | `sl subgraphs dev <file>` | Watch + hot-redeploy |
72
+ | `sl subgraphs list` | List deployed subgraphs |
73
+ | `sl subgraphs status <name>` | Indexing progress, row counts, gaps |
74
+ | `sl subgraphs query <name> <table>` | Query a subgraph table with filters, sort, pagination |
75
+ | `sl subgraphs reindex <name>` | Drop + re-process from the tip backwards |
76
+ | `sl subgraphs backfill <name>` | Fill a specific block range |
77
+ | `sl subgraphs stop <name>` | Pause processing |
78
+ | `sl subgraphs gaps <name>` | List missing block ranges |
79
+ | `sl subgraphs delete <name>` | Drop the subgraph + its schema |
80
+ | `sl subgraphs scaffold <SP...::contract>` | Generate a starter subgraph from a deployed contract |
81
+ | `sl subgraphs generate <name>` | Regenerate TS types for an existing subgraph |
82
+
83
+ ### Local dev + OSS
84
+
85
+ | Command | What it does |
86
+ |---|---|
87
+ | `sl local start/stop/restart/status/logs` | Manage the local Docker stack |
88
+ | `sl local node setup/start/stop/...` | Manage the local Stacks node |
89
+ | `sl stack start/stop/restart` | Alias for `sl local` |
90
+ | `sl db blocks/txs/events/gaps/reset/resync` | Inspect the local source DB |
91
+
92
+ ### Other
93
+
94
+ | Command | What it does |
95
+ |---|---|
96
+ | `sl generate [files...]` (aliases: `gen`, `codegen`) | Generate TS interfaces from Clarity contracts |
97
+ | `sl init` | Scaffold `secondlayer.config.ts` |
98
+ | `sl doctor` | Session + project + instance reachability check |
99
+ | `sl status` | Platform/instance health |
100
+ | `sl account profile` | Update display name / bio / slug |
101
+ | `sl config show/set/reset/clear` | Inspect or reset local config |
102
+
103
+ ## Environment variables
104
+
105
+ | Var | Purpose |
106
+ |---|---|
107
+ | `SL_API_URL` | Bypass platform resolution — point at an OSS or internal API directly |
108
+ | `SL_SERVICE_KEY` | Service key when using env-var bypass |
109
+ | `SL_PLATFORM_API_URL` | Override the platform API base (default `https://api.secondlayer.tools`) |
110
+ | `STACKS_NETWORK` | Override via `--network <local\|testnet\|mainnet>` |
111
+ | `HIRO_API_KEY` | Used by `sl generate` for remote contract fetches |
112
+
113
+ ## Error codes
114
+
115
+ Every tenant-scoped failure surfaces a typed code and an action hint:
116
+
117
+ | Code | CLI hint |
118
+ |---|---|
119
+ | `SESSION_EXPIRED` | `Session expired. Run: sl login` |
120
+ | `TENANT_SUSPENDED` | `Instance is suspended. Run: sl instance resume` |
121
+ | `NO_ACTIVE_PROJECT` | `No project selected. Run: sl project use <slug>` |
122
+ | `NO_TENANT_FOR_PROJECT` | `Project has no instance. Run: sl instance create --plan launch` |
123
+ | `KEY_ROTATED` | Handled transparently — `http.ts` re-mints and retries once |
124
+
125
+ ## Code generation (`sl generate`)
126
+
127
+ Generate type-safe interfaces, functions, and optional React hooks from
128
+ Clarity contracts — works against local `.clar` files, deployed contracts
129
+ (network inferred from address prefix), or glob patterns.
130
+
131
+ ```bash
132
+ # Local .clar files
133
+ sl generate ./contracts/token.clar -o ./src/generated.ts
134
+
135
+ # Deployed contracts (SP/SM → mainnet, ST/SN → testnet)
136
+ sl generate SP3K8BC0PPEVCV7NZ6QSRWPQ2JE9E5B6N3PA0KBR9.alex-vault -o ./src/generated.ts
137
+
138
+ # Glob
139
+ sl generate "./contracts/*.clar" -o ./src/generated.ts
140
+ ```
141
+
142
+ Config-driven mode:
143
+
144
+ ```bash
145
+ sl init # creates secondlayer.config.ts
146
+ sl generate # regenerates from the config
147
+ ```
148
+
149
+ ```typescript
150
+ // secondlayer.config.ts
151
+ import { defineConfig } from "@secondlayer/cli"
152
+ import { clarinet, actions, react } from "@secondlayer/cli/plugins"
153
+
154
+ export default defineConfig({
155
+ out: "src/generated.ts",
156
+ plugins: [clarinet(), actions(), react()],
157
+ })
158
+ ```
159
+
160
+ ### Plugins
161
+
162
+ | Plugin | What it adds |
163
+ |---|---|
164
+ | `clarinet()` | Parse local Clarinet project |
165
+ | `actions()` | `read.*` + `write.*` helpers on each contract |
166
+ | `react()` | Typed React Query hooks (`useTokenTransfer`, `useTokenBalance`, etc.) |
167
+ | `testing()` | Clarinet SDK test helpers |
168
+
169
+ ### Usage examples
170
+
171
+ ```typescript
172
+ import { token } from "./generated/contracts"
173
+ import { makeContractCall, fetchCallReadOnlyFunction } from "@stacks/transactions"
174
+
175
+ // Works with @stacks/transactions directly:
176
+ await makeContractCall({
177
+ ...token.transfer({ amount: 100n, recipient: "SP..." }),
178
+ network: "mainnet",
179
+ })
180
+
181
+ // With actions() plugin — read/write helpers:
182
+ const balance = await token.read.getBalance({ account: "SP..." })
183
+ await token.write.transfer({ amount: 100n, recipient: "SP..." })
184
+
185
+ // Maps / vars / constants:
186
+ const supply = await token.vars.totalSupply.get()
187
+ const bal = await token.maps.balances.get("SP...")
188
+ const max = await token.constants.maxSupply.get()
189
+ ```
190
+
191
+ ```typescript
192
+ // With react() plugin:
193
+ import { useTokenTransfer, useTokenBalance } from "./generated/hooks"
194
+
195
+ function App() {
196
+ const { transfer, isRequestPending } = useTokenTransfer()
197
+ const { data: balance } = useTokenBalance("SP...")
198
+ // ...
199
+ }
200
+ ```
201
+
202
+ ## License
203
+
204
+ MIT
package/dist/cli.js CHANGED
@@ -2583,8 +2583,8 @@ function isOssMode() {
2583
2583
  return !!process.env.SL_API_URL && !process.env.SL_SERVICE_KEY;
2584
2584
  }
2585
2585
  var init_resolve_tenant = __esm(() => {
2586
- init_http();
2587
2586
  init_config();
2587
+ init_http();
2588
2588
  init_project_file();
2589
2589
  });
2590
2590
 
@@ -32410,7 +32410,7 @@ var {
32410
32410
  // package.json
32411
32411
  var package_default = {
32412
32412
  name: "@secondlayer/cli",
32413
- version: "3.1.0",
32413
+ version: "3.1.2-alpha.0",
32414
32414
  description: "CLI for subgraphs and blockchain indexing on Stacks",
32415
32415
  type: "module",
32416
32416
  bin: {
@@ -32431,7 +32431,8 @@ var package_default = {
32431
32431
  },
32432
32432
  files: [
32433
32433
  "dist",
32434
- "templates"
32434
+ "templates",
32435
+ "README.md"
32435
32436
  ],
32436
32437
  scripts: {
32437
32438
  build: "bunup",
@@ -32451,12 +32452,11 @@ var package_default = {
32451
32452
  license: "MIT",
32452
32453
  dependencies: {
32453
32454
  "@inquirer/prompts": "^8.2.0",
32454
- "@secondlayer/bundler": "^0.3.0",
32455
- "@secondlayer/sdk": "^2.0.0",
32456
- "@secondlayer/shared": "^2.1.0",
32457
- "@secondlayer/stacks": "^0.3.0",
32458
- "@secondlayer/subgraphs": "^0.11.8",
32459
- "@secondlayer/workflows": "^1.1.0",
32455
+ "@secondlayer/bundler": "^0.3.1-alpha.0",
32456
+ "@secondlayer/sdk": "^3.0.0-alpha.0",
32457
+ "@secondlayer/shared": "^3.0.0-alpha.0",
32458
+ "@secondlayer/stacks": "^1.0.0-alpha.0",
32459
+ "@secondlayer/subgraphs": "^1.0.0-alpha.0",
32460
32460
  "@biomejs/js-api": "^0.7.0",
32461
32461
  "@biomejs/wasm-nodejs": "^1.9.0",
32462
32462
  esbuild: "^0.19.0",
@@ -32487,11 +32487,6 @@ function handleApiError(err, action) {
32487
32487
  console.error("Session expired. Run: sl login");
32488
32488
  process.exit(1);
32489
32489
  }
32490
- if (err.code === "TRIAL_EXPIRED") {
32491
- console.error(err.message);
32492
- console.error("Run: sl instance resize --plan <...> and add payment");
32493
- process.exit(1);
32494
- }
32495
32490
  if (err.code === "TENANT_SUSPENDED") {
32496
32491
  console.error("Tenant is suspended. Run: sl instance resume");
32497
32492
  process.exit(1);
@@ -34933,13 +34928,9 @@ function registerWhoamiCommand(program2) {
34933
34928
  try {
34934
34929
  const tenant = await httpPlatform("/api/tenants/me");
34935
34930
  if (tenant.tenant) {
34936
- const trialDays = Math.max(0, Math.ceil((new Date(tenant.tenant.trialEndsAt).getTime() - Date.now()) / (24 * 60 * 60 * 1000)));
34937
34931
  rows.push(["Instance", tenant.tenant.apiUrl]);
34932
+ rows.push(["Plan", tenant.tenant.plan]);
34938
34933
  rows.push(["Status", tenant.tenant.status]);
34939
- rows.push([
34940
- "Trial",
34941
- `${trialDays} day${trialDays === 1 ? "" : "s"} left`
34942
- ]);
34943
34934
  } else {
34944
34935
  rows.push([
34945
34936
  "Instance",
@@ -35037,12 +35028,12 @@ init_resolve_tenant();
35037
35028
  import { confirm as confirm4, input as input3, select as select3 } from "@inquirer/prompts";
35038
35029
  function registerInstanceCommand(program2) {
35039
35030
  const instance = program2.command("instance").description("Manage your dedicated Secondlayer instance");
35040
- instance.command("create").description("Provision a new dedicated instance for the active project").option("--plan <plan>", "Plan: launch | grow | scale", "launch").action(async (opts) => {
35031
+ instance.command("create").description("Provision a new dedicated instance for the active project").option("--plan <plan>", "Plan: hobby (free) | launch | grow | scale", "hobby").action(async (opts) => {
35041
35032
  guardOssMode();
35042
35033
  const activeSlug = await requireActiveProject();
35043
35034
  const plan = opts.plan;
35044
- if (!["launch", "grow", "scale"].includes(plan)) {
35045
- error(`Invalid plan: ${plan} (expected launch, grow, or scale)`);
35035
+ if (!["hobby", "launch", "grow", "scale"].includes(plan)) {
35036
+ error(`Invalid plan: ${plan} (expected hobby, launch, grow, or scale)`);
35046
35037
  process.exit(1);
35047
35038
  }
35048
35039
  try {
@@ -35060,13 +35051,17 @@ function registerInstanceCommand(program2) {
35060
35051
  guardOssMode();
35061
35052
  await renderInstanceInfo();
35062
35053
  });
35063
- instance.command("resize").description("Change your instance plan (brief downtime)").option("--plan <plan>", "Target plan: launch | grow | scale").option("--yes", "Skip confirm").action(async (opts) => {
35054
+ instance.command("resize").description("Change your instance plan (brief downtime)").option("--plan <plan>", "Target plan: hobby | launch | grow | scale").option("--yes", "Skip confirm").action(async (opts) => {
35064
35055
  guardOssMode();
35065
35056
  let target = opts.plan;
35066
35057
  if (!target) {
35067
35058
  const answer = await select3({
35068
35059
  message: "Target plan",
35069
35060
  choices: [
35061
+ {
35062
+ value: "hobby",
35063
+ name: "Hobby — free (0.5 vCPU · 512 MB · 5 GB, auto-pause after 7d idle)"
35064
+ },
35070
35065
  {
35071
35066
  value: "launch",
35072
35067
  name: "Launch — $99/mo (1 vCPU · 2 GB · 10 GB)"
@@ -35268,12 +35263,10 @@ async function renderInstanceInfo() {
35268
35263
  return;
35269
35264
  }
35270
35265
  const t = res.tenant;
35271
- const trialDays = Math.max(0, Math.ceil((new Date(t.trialEndsAt).getTime() - Date.now()) / (24 * 60 * 60 * 1000)));
35272
35266
  console.log(formatKeyValue([
35273
35267
  ["URL", t.apiUrl],
35274
35268
  ["Plan", t.plan],
35275
35269
  ["Status", t.status],
35276
- ["Trial", `${trialDays} day${trialDays === 1 ? "" : "s"} left`],
35277
35270
  ["Created", new Date(t.createdAt).toLocaleString()]
35278
35271
  ]));
35279
35272
  } catch (err) {
@@ -35307,11 +35300,6 @@ function handleInstanceError(err, action) {
35307
35300
  error("Session expired. Run: sl login");
35308
35301
  process.exit(1);
35309
35302
  }
35310
- if (err.code === "TRIAL_EXPIRED") {
35311
- error(err.message);
35312
- error("Add a payment method at the dashboard to keep your instance running.");
35313
- process.exit(1);
35314
- }
35315
35303
  if (err.code === "TENANT_SUSPENDED") {
35316
35304
  error("Instance is suspended. Run: sl instance resume");
35317
35305
  process.exit(1);
@@ -35344,7 +35332,7 @@ function registerProjectCommand(program2) {
35344
35332
  success(`Created project ${res.project.name} (${res.project.slug})`);
35345
35333
  const path = await writeActiveProject(res.project.slug, process.cwd());
35346
35334
  info(dim(`Bound to this directory → ${path}`));
35347
- info(dim(`Next: sl instance create --plan launch`));
35335
+ info(dim("Next: sl instance create --plan launch"));
35348
35336
  } catch (err) {
35349
35337
  handleProjectError(err);
35350
35338
  }
@@ -35448,5 +35436,5 @@ registerLocalCommand(program);
35448
35436
  registerAccountCommand(program);
35449
35437
  program.parse();
35450
35438
 
35451
- //# debugId=395566BC0854202D64756E2164756E21
35439
+ //# debugId=2929270B62E4733C64756E2164756E21
35452
35440
  //# sourceMappingURL=cli.js.map