@secondlayer/cli 12.0.0 → 12.1.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 CHANGED
@@ -181,8 +181,8 @@ const supply = await token.vars.totalSupply.get()
181
181
  ## Frozen commands
182
182
 
183
183
  Local-dev periphery: still functional, hidden from `secondlayer --help`, no further
184
- investment — may be removed in a future major. The hosted dev loop
185
- (`secondlayer subgraphs create/deploy`) is the supported path.
184
+ investment — may be removed in a future major. `secondlayer subgraphs
185
+ create/deploy` against your own instance is the supported path.
186
186
 
187
187
  - `secondlayer local up/down/start/stop/restart/status/logs` — local stack + dev services
188
188
  - `secondlayer local node ...` — local Stacks node management
package/dist/cli.js CHANGED
@@ -34320,14 +34320,14 @@ init_esm();
34320
34320
  // package.json
34321
34321
  var package_default = {
34322
34322
  name: "@secondlayer/cli",
34323
- version: "12.0.0",
34323
+ version: "12.1.0",
34324
+ description: "Run a Stacks index on your own hardware — set it up, restore verified history, and keep it current.",
34324
34325
  repository: {
34325
34326
  type: "git",
34326
34327
  url: "git+https://github.com/ryanwaits/secondlayer.git",
34327
34328
  directory: "packages/cli"
34328
34329
  },
34329
34330
  homepage: "https://github.com/ryanwaits/secondlayer/tree/main/packages/cli#readme",
34330
- description: "CLI for subgraphs and blockchain indexing on Stacks",
34331
34331
  type: "module",
34332
34332
  bin: {
34333
34333
  secondlayer: "./dist/cli.js",
@@ -34363,10 +34363,10 @@ var package_default = {
34363
34363
  "@inquirer/prompts": "^8.2.0",
34364
34364
  "@secondlayer/bundler": "^0.5.1",
34365
34365
  "@secondlayer/scaffold": "^1.5.4",
34366
- "@secondlayer/sdk": "^8.0.0",
34367
- "@secondlayer/shared": "^9.0.0",
34366
+ "@secondlayer/sdk": "^8.0.1",
34367
+ "@secondlayer/shared": "^10.0.0",
34368
34368
  "@secondlayer/stacks": "^4.0.0",
34369
- "@secondlayer/subgraphs": "^4.0.1",
34369
+ "@secondlayer/subgraphs": "^4.0.2",
34370
34370
  esbuild: "^0.27.4",
34371
34371
  "smol-toml": "^1.6.1",
34372
34372
  zod: "^4.3.6"
@@ -39006,19 +39006,91 @@ Exit codes:
39006
39006
  function registerBootstrapCommand(program2) {
39007
39007
  attachBootstrapCommand(program2.command("bootstrap").description("Restore chain history from a verified archive instead of syncing from genesis"));
39008
39008
  }
39009
+ // src/commands/uninstall.ts
39010
+ init_output();
39011
+ import { spawnSync as spawnSync3 } from "node:child_process";
39012
+ import { existsSync as existsSync8, readFileSync as readFileSync7 } from "node:fs";
39013
+ import { join as join17, resolve as resolve13 } from "node:path";
39014
+ import { planUninstall, uninstallCommand } from "@secondlayer/shared/runtime";
39015
+ var UNINSTALL_EXIT = { OK: 0, FAILED: 1, REFUSED: 2 };
39016
+ var DEFAULT_COMPOSE = "docker/oss/docker-compose.yml";
39017
+ function bundleHasKeys(bundleDir) {
39018
+ const manifest = join17(bundleDir, "manifest.json");
39019
+ if (!existsSync8(manifest))
39020
+ return false;
39021
+ try {
39022
+ const parsed = JSON.parse(readFileSync7(manifest, "utf8"));
39023
+ return parsed.secrets != null;
39024
+ } catch {
39025
+ return false;
39026
+ }
39027
+ }
39028
+ function registerUninstallCommand(program2) {
39029
+ program2.command("uninstall").description("Stop the stack; the index, chainstate, and keys survive").option("--compose <file>", "compose file to tear down", DEFAULT_COMPOSE).option("--purge", "also destroy the volumes (wipes the index)").option("--yes", "confirm a purge").option("--backup <dir>", "a bundle proving the keys exist elsewhere (required to purge)").option("--apply", "actually run it (default is a dry run)").option("--json", "Output as JSON").action(async (opts) => {
39030
+ const dataDir = process.env.DATA_DIR ?? "./data";
39031
+ const secretsPresent = !!process.env.SECONDLAYER_SECRETS_KEY || existsSync8(resolve13(process.cwd(), ".env.local"));
39032
+ const keysBackedUp = opts.backup ? bundleHasKeys(resolve13(opts.backup)) : false;
39033
+ const decision = planUninstall({
39034
+ purge: opts.purge === true,
39035
+ confirmed: opts.yes === true,
39036
+ keysBackedUp,
39037
+ secretsPresent,
39038
+ dataDir
39039
+ });
39040
+ if (!decision.ok) {
39041
+ printError(decision.reason);
39042
+ process.exit(UNINSTALL_EXIT.REFUSED);
39043
+ }
39044
+ const cmd = uninstallCommand(decision.plan, opts.compose);
39045
+ if (!opts.apply) {
39046
+ output({
39047
+ json: opts.json,
39048
+ data: { dryRun: true, plan: decision.plan, command: cmd },
39049
+ human: () => {
39050
+ note("dry run — nothing removed. Pass --apply to proceed.");
39051
+ note(`would run: docker ${cmd.join(" ")}`);
39052
+ for (const p2 of decision.plan.preserves) {
39053
+ note(`preserved · ${p2.what}: ${p2.detail}`);
39054
+ }
39055
+ for (const d2 of decision.plan.destroys) {
39056
+ warn(`DESTROYED · volume ${d2}`);
39057
+ }
39058
+ }
39059
+ });
39060
+ return;
39061
+ }
39062
+ const res = spawnSync3("docker", cmd, { stdio: "inherit" });
39063
+ if ((res.status ?? 1) !== 0) {
39064
+ printError(`docker ${cmd.join(" ")} exited ${res.status}`);
39065
+ process.exit(UNINSTALL_EXIT.FAILED);
39066
+ }
39067
+ output({
39068
+ json: opts.json,
39069
+ data: { removed: true, plan: decision.plan },
39070
+ human: () => {
39071
+ success("stack removed");
39072
+ for (const w2 of decision.warnings)
39073
+ warn(w2);
39074
+ for (const p2 of decision.plan.preserves) {
39075
+ note(`preserved · ${p2.what}: ${p2.detail}`);
39076
+ }
39077
+ }
39078
+ });
39079
+ });
39080
+ }
39009
39081
  // src/commands/backup.ts
39010
39082
  init_output();
39011
39083
  import { createHash as createHash4 } from "node:crypto";
39012
39084
  import {
39013
39085
  chmodSync,
39014
- existsSync as existsSync8,
39086
+ existsSync as existsSync9,
39015
39087
  mkdirSync as mkdirSync6,
39016
- readFileSync as readFileSync7,
39088
+ readFileSync as readFileSync8,
39017
39089
  statSync as statSync3,
39018
39090
  writeFileSync as writeFileSync7
39019
39091
  } from "node:fs";
39020
- import { join as join17, resolve as resolve13 } from "node:path";
39021
- import { getDb as getDb5, sql as sql4 } from "@secondlayer/shared/db";
39092
+ import { join as join18, resolve as resolve14 } from "node:path";
39093
+ import { closeDb, getDb as getDb5, sql as sql4 } from "@secondlayer/shared/db";
39022
39094
  import {
39023
39095
  decryptBundle,
39024
39096
  encryptBundle,
@@ -39044,7 +39116,7 @@ function readSecretsKey() {
39044
39116
  return key ? key : undefined;
39045
39117
  }
39046
39118
  function sha256File(path11) {
39047
- return createHash4("sha256").update(readFileSync7(path11)).digest("hex");
39119
+ return createHash4("sha256").update(readFileSync8(path11)).digest("hex");
39048
39120
  }
39049
39121
  async function readScope() {
39050
39122
  const db = getDb5();
@@ -39076,6 +39148,7 @@ function attachBackupCommand(cmd) {
39076
39148
  return cmd.requiredOption("--out <dir>", "directory to write the bundle into").option("--passphrase <passphrase>", "encrypt bundled secrets (or set SECONDLAYER_BACKUP_PASSPHRASE)").option("--no-secrets", "omit keys; the bundle restores data only").option("--json", "Output as JSON").action(async (opts) => {
39077
39149
  try {
39078
39150
  await runBackup(opts);
39151
+ await closeDb();
39079
39152
  } catch (error2) {
39080
39153
  printError(error2 instanceof Error ? error2.message : String(error2));
39081
39154
  process.exit(BACKUP_EXIT.FAILED);
@@ -39104,10 +39177,10 @@ async function runBackup(opts) {
39104
39177
  printError(plan.reason);
39105
39178
  process.exit(BACKUP_EXIT.REFUSED);
39106
39179
  }
39107
- const outDir = resolve13(opts.out);
39180
+ const outDir = resolve14(opts.out);
39108
39181
  mkdirSync6(outDir, { recursive: true });
39109
39182
  console.error("dumping database…");
39110
- const dumpPath = join17(outDir, DB_FILE);
39183
+ const dumpPath = join18(outDir, DB_FILE);
39111
39184
  const dump = await runProcess(["pg_dump", "-Fc", "--no-owner", "--no-acl", "-f", dumpPath, databaseUrl], "pg_dump");
39112
39185
  if (!dump.ok) {
39113
39186
  printError(dump.stderr);
@@ -39126,7 +39199,7 @@ async function runBackup(opts) {
39126
39199
  const lines2 = SECRET_ENV_KEYS.filter((k2) => process.env[k2]).map((k2) => `${k2}=${process.env[k2]}`);
39127
39200
  const sealed = encryptBundle(lines2.join(`
39128
39201
  `), passphrase);
39129
- const secretsPath = join17(outDir, SECRETS_FILE);
39202
+ const secretsPath = join18(outDir, SECRETS_FILE);
39130
39203
  writeFileSync7(secretsPath, sealed);
39131
39204
  chmodSync(secretsPath, 384);
39132
39205
  manifest.secrets = {
@@ -39135,7 +39208,7 @@ async function runBackup(opts) {
39135
39208
  canary: sealKeyCanary(secretsKey)
39136
39209
  };
39137
39210
  }
39138
- writeFileSync7(join17(outDir, MANIFEST_FILE), `${JSON.stringify(manifest, null, 2)}
39211
+ writeFileSync7(join18(outDir, MANIFEST_FILE), `${JSON.stringify(manifest, null, 2)}
39139
39212
  `);
39140
39213
  output({
39141
39214
  json: opts.json,
@@ -39155,6 +39228,7 @@ function attachRestoreCommand(cmd) {
39155
39228
  return cmd.requiredOption("--from <dir>", "bundle directory to restore from").option("--passphrase <passphrase>", "decrypt bundled secrets (or set SECONDLAYER_BACKUP_PASSPHRASE)").option("--apply", "actually restore (default is a dry run)").option("--force", "restore over a database that already holds chain data").option("--json", "Output as JSON").action(async (opts) => {
39156
39229
  try {
39157
39230
  await runRestore(opts);
39231
+ await closeDb();
39158
39232
  } catch (error2) {
39159
39233
  printError(error2 instanceof Error ? error2.message : String(error2));
39160
39234
  process.exit(BACKUP_EXIT.FAILED);
@@ -39163,14 +39237,14 @@ function attachRestoreCommand(cmd) {
39163
39237
  }
39164
39238
  async function runRestore(opts) {
39165
39239
  const databaseUrl = requireDatabaseUrl();
39166
- const dir = resolve13(opts.from);
39167
- const manifestPath = join17(dir, MANIFEST_FILE);
39168
- if (!existsSync8(manifestPath)) {
39240
+ const dir = resolve14(opts.from);
39241
+ const manifestPath = join18(dir, MANIFEST_FILE);
39242
+ if (!existsSync9(manifestPath)) {
39169
39243
  printError(`no ${MANIFEST_FILE} in ${dir}`);
39170
39244
  process.exit(BACKUP_EXIT.REFUSED);
39171
39245
  }
39172
- const manifest = JSON.parse(readFileSync7(manifestPath, "utf8"));
39173
- const dumpPath = join17(dir, manifest.db?.file ?? DB_FILE);
39246
+ const manifest = JSON.parse(readFileSync8(manifestPath, "utf8"));
39247
+ const dumpPath = join18(dir, manifest.db?.file ?? DB_FILE);
39174
39248
  if (manifest.db && sha256File(dumpPath) !== manifest.db.sha256) {
39175
39249
  printError("the database dump does not match the digest in the manifest; this bundle is corrupt or truncated");
39176
39250
  process.exit(BACKUP_EXIT.REFUSED);
@@ -39184,7 +39258,7 @@ async function runRestore(opts) {
39184
39258
  process.exit(BACKUP_EXIT.REFUSED);
39185
39259
  }
39186
39260
  try {
39187
- const env2 = decryptBundle(readFileSync7(join17(dir, manifest.secrets.file)), passphrase);
39261
+ const env2 = decryptBundle(readFileSync8(join18(dir, manifest.secrets.file)), passphrase);
39188
39262
  bundleKey = env2.split(`
39189
39263
  `).find((l2) => l2.startsWith("SECONDLAYER_SECRETS_KEY="))?.slice("SECONDLAYER_SECRETS_KEY=".length).trim();
39190
39264
  } catch {
@@ -39263,8 +39337,8 @@ function registerRestoreCommand(program2) {
39263
39337
  }
39264
39338
  // src/lib/instance-init.ts
39265
39339
  import { randomBytes as randomBytes3 } from "node:crypto";
39266
- import { existsSync as existsSync9, readFileSync as readFileSync8, writeFileSync as writeFileSync8 } from "node:fs";
39267
- import { resolve as resolve14 } from "node:path";
39340
+ import { existsSync as existsSync10, readFileSync as readFileSync9, writeFileSync as writeFileSync8 } from "node:fs";
39341
+ import { resolve as resolve15 } from "node:path";
39268
39342
  import { generateEd25519KeyPair } from "@secondlayer/shared/crypto/ed25519";
39269
39343
  import { INSTANCE_NETWORKS } from "@secondlayer/shared/db/queries/instance";
39270
39344
  var INSTANCE_ENV_FILE = ".env.local";
@@ -39298,9 +39372,9 @@ function unescapeEnvValue(raw) {
39298
39372
  `).replace(/\\"/g, '"').replace(/\\\\/g, "\\");
39299
39373
  }
39300
39374
  function readEnvValue(envPath, key) {
39301
- if (!existsSync9(envPath))
39375
+ if (!existsSync10(envPath))
39302
39376
  return null;
39303
- const match = readFileSync8(envPath, "utf8").match(new RegExp(`^${key}=(.*)$`, "m"));
39377
+ const match = readFileSync9(envPath, "utf8").match(new RegExp(`^${key}=(.*)$`, "m"));
39304
39378
  return match ? unescapeEnvValue(match[1]) : null;
39305
39379
  }
39306
39380
  function buildInstanceEnv(input4) {
@@ -39343,12 +39417,12 @@ function renderInstanceEnv(env2) {
39343
39417
  `);
39344
39418
  }
39345
39419
  function writeInstanceEnv(cwd, env2, file = INSTANCE_ENV_FILE) {
39346
- const path11 = resolve14(cwd, file);
39420
+ const path11 = resolve15(cwd, file);
39347
39421
  writeFileSync8(path11, renderInstanceEnv(env2), { mode: 384 });
39348
39422
  return path11;
39349
39423
  }
39350
39424
  function loadExistingInstanceEnv(cwd, file = INSTANCE_ENV_FILE) {
39351
- const path11 = resolve14(cwd, file);
39425
+ const path11 = resolve15(cwd, file);
39352
39426
  return {
39353
39427
  INSTANCE_TOKEN: readEnvValue(path11, "INSTANCE_TOKEN"),
39354
39428
  SECONDLAYER_SECRETS_KEY: readEnvValue(path11, "SECONDLAYER_SECRETS_KEY"),
@@ -39502,7 +39576,7 @@ function registerStartCommand(program2) {
39502
39576
  }
39503
39577
  // src/cli.ts
39504
39578
  var { version } = package_default;
39505
- program.name("secondlayer").alias("sl").description("Secondlayer CLI — self-hosted Stacks data runtime").version(version).option("--network <network>", "Override network (local, testnet, mainnet)").option("--api-key <key>", "API credential (overrides SL_API_KEY)").option("--api-url <url>", "API endpoint (overrides SL_API_URL)").showSuggestionAfterError(true).showHelpAfterError("(run `secondlayer --help` to see available commands)");
39579
+ program.name("secondlayer").alias("sl").description("Secondlayer CLI — run a Stacks index on your own hardware").version(version).option("--network <network>", "Override network (local, testnet, mainnet)").option("--api-key <key>", "API credential (overrides SL_API_KEY)").option("--api-url <url>", "API endpoint (overrides SL_API_URL)").showSuggestionAfterError(true).showHelpAfterError("(run `secondlayer --help` to see available commands)");
39506
39580
  program.hook("preAction", (thisCommand) => {
39507
39581
  const { network, apiKey, apiUrl } = thisCommand.opts();
39508
39582
  if (network)
@@ -39516,7 +39590,7 @@ program.addHelpText("after", `
39516
39590
  Quickstart:
39517
39591
  $ secondlayer init --network mainnet
39518
39592
  $ secondlayer bootstrap --against <manifest>
39519
- $ secondlayer subgraphs create my-watcher --template sip-010-balances
39593
+ $ secondlayer subgraphs create my-watcher --from-contract SP....my-contract
39520
39594
  $ secondlayer subgraphs deploy subgraphs/my-watcher.ts
39521
39595
  `);
39522
39596
  program.commandsGroup("Getting started:");
@@ -39528,7 +39602,7 @@ registerConsoleCommand(program);
39528
39602
  registerLoginCommand(program);
39529
39603
  registerLogoutCommand(program);
39530
39604
  registerWhoamiCommand(program);
39531
- program.commandsGroup("Data products:");
39605
+ program.commandsGroup("Your data:");
39532
39606
  registerSubgraphsCommand(program);
39533
39607
  registerSubscriptionsCommand(program);
39534
39608
  registerStreamsCommand(program);
@@ -39560,9 +39634,10 @@ registerVerifyCommand(program);
39560
39634
  registerRepairCommand(program);
39561
39635
  registerBackupCommand(program);
39562
39636
  registerRestoreCommand(program);
39563
- program.commandsGroup("Account:");
39637
+ registerUninstallCommand(program);
39638
+ program.commandsGroup("Archive:");
39564
39639
  registerCreditsCommand(program);
39565
39640
  program.parse();
39566
39641
 
39567
- //# debugId=D0F621CCA5D6F2E664756E2164756E21
39642
+ //# debugId=135579D3DFB23FDC64756E2164756E21
39568
39643
  //# sourceMappingURL=cli.js.map