@secondlayer/cli 23.4.2 → 23.5.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.
Files changed (3) hide show
  1. package/dist/cli.js +100 -45
  2. package/dist/cli.js.map +12 -11
  3. package/package.json +4 -4
package/dist/cli.js CHANGED
@@ -2129,12 +2129,16 @@ Expecting one of '${allowedValues.join("', '")}'`);
2129
2129
  });
2130
2130
 
2131
2131
  // src/lib/api-url.ts
2132
- import { isHostedApiUrl, resolveBaseUrl } from "@secondlayer/sdk";
2132
+ import {
2133
+ isHostedApiUrl,
2134
+ resolveAccountUrl,
2135
+ resolveBaseUrl
2136
+ } from "@secondlayer/sdk";
2133
2137
  function resolveApiUrl() {
2134
2138
  return resolveBaseUrl();
2135
2139
  }
2136
2140
  function resolveArchiveOpsUrl() {
2137
- return (process.env.SL_CREDITS_API_URL ?? ARCHIVE_OPS_API_URL).replace(/\/+$/, "");
2141
+ return resolveAccountUrl();
2138
2142
  }
2139
2143
  function isMerchantUrl(url = resolveApiUrl()) {
2140
2144
  return isHostedApiUrl(url);
@@ -2160,6 +2164,7 @@ import {
2160
2164
  } from "node:fs/promises";
2161
2165
  import { homedir } from "node:os";
2162
2166
  import { dirname, join } from "node:path";
2167
+ import { isHostedApiUrl as isHostedApiUrl2 } from "@secondlayer/sdk";
2163
2168
  import { z } from "zod";
2164
2169
  function getSessionPath() {
2165
2170
  return join(process.env.HOME || homedir(), ".secondlayer", "session.json");
@@ -2193,9 +2198,22 @@ async function readSessionFile(path) {
2193
2198
  return { sessions: {} };
2194
2199
  }
2195
2200
  }
2196
- async function readSession(apiUrl = resolveApiUrl(), path = getSessionPath()) {
2201
+ async function readSession(apiUrl = resolveArchiveOpsUrl(), path = getSessionPath()) {
2197
2202
  const file = await readSessionFile(path);
2198
- return file.sessions[sessionKey(apiUrl)] ?? null;
2203
+ const key = sessionKey(apiUrl);
2204
+ const found = file.sessions[key];
2205
+ if (found)
2206
+ return found;
2207
+ if (key !== sessionKey(resolveArchiveOpsUrl()) || !isHostedApiUrl2(key)) {
2208
+ return null;
2209
+ }
2210
+ const legacy = [ARCHIVE_OPS_API_URL, resolveApiUrl()].map(sessionKey).filter((candidate) => candidate !== key && isHostedApiUrl2(candidate)).map((candidate) => file.sessions[candidate]).find((session) => session !== undefined);
2211
+ if (!legacy)
2212
+ return null;
2213
+ try {
2214
+ await writeSessionFile({ sessions: { ...file.sessions, [key]: legacy } }, path);
2215
+ } catch {}
2216
+ return legacy;
2199
2217
  }
2200
2218
  async function writeSessionFile(file, path) {
2201
2219
  const dir = dirname(path);
@@ -2209,12 +2227,12 @@ async function writeSessionFile(file, path) {
2209
2227
  await chmod(tmp, 384);
2210
2228
  await rename(tmp, path);
2211
2229
  }
2212
- async function writeSession(session, apiUrl = resolveApiUrl(), path = getSessionPath()) {
2230
+ async function writeSession(session, apiUrl = resolveArchiveOpsUrl(), path = getSessionPath()) {
2213
2231
  const file = await readSessionFile(path);
2214
2232
  file.sessions[sessionKey(apiUrl)] = session;
2215
2233
  await writeSessionFile(file, path);
2216
2234
  }
2217
- async function clearSession(apiUrl = resolveApiUrl(), path = getSessionPath()) {
2235
+ async function clearSession(apiUrl = resolveArchiveOpsUrl(), path = getSessionPath()) {
2218
2236
  const file = await readSessionFile(path);
2219
2237
  delete file.sessions[sessionKey(apiUrl)];
2220
2238
  if (Object.keys(file.sessions).length === 0) {
@@ -2258,7 +2276,7 @@ async function resolveAuth() {
2258
2276
  if (isOssMode()) {
2259
2277
  return { apiUrl, ephemeralKey: "", fromEnv: true };
2260
2278
  }
2261
- const session = await readSession();
2279
+ const session = sessionKey(apiUrl) === sessionKey(resolveArchiveOpsUrl()) ? await readSession() : null;
2262
2280
  if (!session) {
2263
2281
  const err = new Error("Not logged in — run `secondlayer login`");
2264
2282
  err.code = "SESSION_EXPIRED";
@@ -2299,7 +2317,11 @@ var init_resolve_auth = __esm(() => {
2299
2317
  });
2300
2318
 
2301
2319
  // src/lib/http.ts
2302
- import { resolveAccountKey as resolveAccountKey2, resolveApiKey as resolveApiKey2 } from "@secondlayer/sdk";
2320
+ import {
2321
+ resolveAccountKey as resolveAccountKey2,
2322
+ resolveApiKey as resolveApiKey2,
2323
+ resolveIdentity
2324
+ } from "@secondlayer/sdk";
2303
2325
  async function request(url, opts) {
2304
2326
  const headers = {
2305
2327
  "content-type": "application/json",
@@ -2340,20 +2362,11 @@ async function httpArchiveOpsAnon(path, opts = {}) {
2340
2362
  async function resolveArchiveOpsBearer() {
2341
2363
  const envKey = resolveAccountKey2();
2342
2364
  const session = await readSession(resolveArchiveOpsUrl());
2343
- if (session) {
2344
- return {
2345
- bearer: session.token,
2346
- source: "session",
2347
- ignoredEnvKey: false
2348
- };
2349
- }
2350
- if (envKey && ARCHIVE_CREDENTIAL_RE.test(envKey)) {
2351
- return { bearer: envKey, source: "env", ignoredEnvKey: false };
2352
- }
2365
+ const identity = resolveIdentity("account", { session: session?.token });
2353
2366
  return {
2354
- bearer: undefined,
2355
- source: null,
2356
- ignoredEnvKey: resolveApiKey2() !== undefined && envKey === undefined
2367
+ bearer: identity.token,
2368
+ source: identity.source === "none" ? null : identity.source,
2369
+ ignoredEnvKey: identity.source === "none" && resolveApiKey2() !== undefined && envKey === undefined
2357
2370
  };
2358
2371
  }
2359
2372
  function ignoredEnvKeyNote(ignored) {
@@ -2371,13 +2384,13 @@ async function httpArchiveOps(path, opts = {}) {
2371
2384
  });
2372
2385
  } catch (err) {
2373
2386
  if (err instanceof CliHttpError && err.status === 401) {
2374
- const remedy = source === "session" ? `Your saved login was rejected (${err.message}). Run \`secondlayer login\`, or \`secondlayer logout\` to use SECONDLAYER_API_KEY.` : `Your account rejected the credential (${err.message}). ${ARCHIVE_AUTH_HINT}${ignoredEnvKeyNote(ignoredEnvKey)}`;
2387
+ const remedy = source === "session" ? `Your saved login was rejected (${err.message}). Run \`secondlayer login\`, or set SECONDLAYER_API_KEY (it takes precedence over a saved login).` : `Your account rejected the credential (${err.message}). ${ARCHIVE_AUTH_HINT}${ignoredEnvKeyNote(ignoredEnvKey)}`;
2375
2388
  throw new CliHttpError(401, err.code, err.body, remedy);
2376
2389
  }
2377
2390
  throw err;
2378
2391
  }
2379
2392
  }
2380
- var CliHttpError, REQUEST_TIMEOUT_MS = 30000, ARCHIVE_AUTH_HINT = "Set SECONDLAYER_API_KEY to your account key (sk-sl_…, from /account/keys), or run `secondlayer login`.", ARCHIVE_CREDENTIAL_RE;
2393
+ var CliHttpError, REQUEST_TIMEOUT_MS = 30000, ARCHIVE_AUTH_HINT = "Set SECONDLAYER_API_KEY to your account key (sk-sl_…, from /account/keys), or run `secondlayer login`.";
2381
2394
  var init_http = __esm(() => {
2382
2395
  init_api_url();
2383
2396
  init_resolve_auth();
@@ -2395,7 +2408,6 @@ var init_http = __esm(() => {
2395
2408
  this.body = body;
2396
2409
  }
2397
2410
  };
2398
- ARCHIVE_CREDENTIAL_RE = /^s[ks]-sl_/;
2399
2411
  });
2400
2412
 
2401
2413
  // src/lib/output.ts
@@ -2546,8 +2558,13 @@ function nextStepHint(status, detail, code) {
2546
2558
  return;
2547
2559
  if (code === "STACK_ASLEEP")
2548
2560
  return "Any write starts it, such as creating or updating a webhook.";
2549
- if (status === 402)
2550
- return detail === "spend_cap_reached" ? `You reached your monthly spend cap. Raise it: ${CREDITS_URL}` : `Add credits to create webhooks and deploy subgraphs: ${CREDITS_URL}`;
2561
+ if (status === 402) {
2562
+ if (detail === "spend_cap_reached")
2563
+ return `You reached your monthly spend cap. Raise it: ${CREDITS_URL}`;
2564
+ if (detail === "rows_limit_reached")
2565
+ return `This docs key read all its rows. Make a lasting key: ${SIGNIN_URL}`;
2566
+ return `Add credits to create webhooks and deploy subgraphs: ${CREDITS_URL}`;
2567
+ }
2551
2568
  if (status === 403)
2552
2569
  return "You may not have access — check your active project with `secondlayer whoami`.";
2553
2570
  if (status === 404)
@@ -2666,7 +2683,7 @@ async function getContractPrintSchema(contractId) {
2666
2683
  }
2667
2684
  return client.index.printSchema(contractId);
2668
2685
  }
2669
- var CREDITS_URL = "https://www.secondlayer.tools/account/credits";
2686
+ var CREDITS_URL = "https://www.secondlayer.tools/account/credits", SIGNIN_URL = "https://www.secondlayer.tools/signin";
2670
2687
  var init_api_client = __esm(() => {
2671
2688
  init_http();
2672
2689
  init_output();
@@ -29017,6 +29034,25 @@ var init_subgraphs = __esm(() => {
29017
29034
  init_format();
29018
29035
  });
29019
29036
 
29037
+ // src/lib/coalesced-runner.ts
29038
+ function createCoalescedRunner(task) {
29039
+ let state = "idle";
29040
+ const trigger = async () => {
29041
+ if (state !== "idle") {
29042
+ state = "deployingDirty";
29043
+ return;
29044
+ }
29045
+ do {
29046
+ state = "deploying";
29047
+ try {
29048
+ await task();
29049
+ } catch {}
29050
+ } while (state === "deployingDirty");
29051
+ state = "idle";
29052
+ };
29053
+ return { trigger, state: () => state };
29054
+ }
29055
+
29020
29056
  // src/lib/derive-abi.ts
29021
29057
  import {
29022
29058
  extractSubgraphDefinition,
@@ -30001,14 +30037,26 @@ import {
30001
30037
  generatePrintSchemaSubgraph as generatePrintSchemaSubgraph2,
30002
30038
  generateTraitSubgraph as generateTraitSubgraph2
30003
30039
  } from "@secondlayer/scaffold";
30004
- import {
30005
- billingPausedCode
30006
- } from "@secondlayer/shared/schemas";
30007
30040
  import { TRAIT_STANDARDS } from "@secondlayer/stacks/clarity";
30008
30041
  import {
30009
30042
  deriveVerification as deriveVerification2,
30010
30043
  isDeterminismViolation
30011
30044
  } from "@secondlayer/subgraphs/verification";
30045
+ function watchVerdict(subgraph) {
30046
+ if (subgraph.health.billingPausedCode) {
30047
+ return {
30048
+ kind: "stopped",
30049
+ message: `Subgraph is paused: ${subgraph.health.billingPausedCode.replace(/_/g, " ")}. Fix billing, then re-run.`
30050
+ };
30051
+ }
30052
+ if (subgraph.status === "error" || subgraph.sync?.status === "error") {
30053
+ return {
30054
+ kind: "stopped",
30055
+ message: `Subgraph is in error: ${subgraph.health.lastError ?? "no error recorded"}`
30056
+ };
30057
+ }
30058
+ return subgraph.sync?.status === "synced" ? { kind: "synced" } : { kind: "running" };
30059
+ }
30012
30060
  async function loadSubgraphDefinition(source, fileName) {
30013
30061
  const { bundleSubgraphCode } = await import("@secondlayer/bundler");
30014
30062
  const bundled = await bundleSubgraphCode(source, { fileName });
@@ -30374,9 +30422,8 @@ function formatOperationRange(op) {
30374
30422
  return "whole subgraph";
30375
30423
  return `${op.fromBlock ?? "start"} → ${op.toBlock ?? "tip"}`;
30376
30424
  }
30377
- function formatSubgraphStatus(status, lastError) {
30378
- const code = billingPausedCode(lastError);
30379
- return code ? `${status} (paused: ${code.replace(/_/g, " ")})` : status;
30425
+ function formatSubgraphStatus(status, billingPausedCode) {
30426
+ return billingPausedCode ? `${status} (paused: ${billingPausedCode.replace(/_/g, " ")})` : status;
30380
30427
  }
30381
30428
  function formatOperationProgress(progress) {
30382
30429
  if (progress === null)
@@ -30622,7 +30669,8 @@ Check the contract id, and that the contract has emitted print events.`);
30622
30669
  error(`[${new Date().toLocaleTimeString()}] ${err}`);
30623
30670
  }
30624
30671
  };
30625
- await deploySubgraph();
30672
+ const runner = createCoalescedRunner(deploySubgraph);
30673
+ await runner.trigger();
30626
30674
  let timeout = null;
30627
30675
  const watcher = watch(absPath, () => {
30628
30676
  if (timeout)
@@ -30630,7 +30678,7 @@ Check the contract id, and that the contract has emitted print events.`);
30630
30678
  timeout = setTimeout(async () => {
30631
30679
  console.log("");
30632
30680
  info("File changed, redeploying...");
30633
- await deploySubgraph();
30681
+ await runner.trigger();
30634
30682
  }, 300);
30635
30683
  });
30636
30684
  process.on("SIGINT", () => {
@@ -30887,7 +30935,7 @@ ${data.length} subgraph(s) total`));
30887
30935
  handleApiError(err, "list subgraphs");
30888
30936
  }
30889
30937
  });
30890
- subgraphs.command("status <name>").alias("get").description("Show detailed subgraph status").option("-w, --watch", "Refresh every 2s until synced or Ctrl-C").action(async (name, options) => {
30938
+ subgraphs.command("status <name>").alias("get").description("Show detailed subgraph status").option("-w, --watch", "Refresh every 2s until synced; exits non-zero on error or a billing pause, or Ctrl-C").action(async (name, options) => {
30891
30939
  const renderOnce = async (opts = {}) => {
30892
30940
  const subgraph = await getSubgraphApi(name);
30893
30941
  if (opts.clearScreen)
@@ -30910,7 +30958,7 @@ ${data.length} subgraph(s) total`));
30910
30958
  ["Verification", formatVerificationStatus(subgraph.verification)],
30911
30959
  [
30912
30960
  "Status",
30913
- formatSubgraphStatus(subgraph.status, subgraph.health.lastError)
30961
+ formatSubgraphStatus(subgraph.status, subgraph.health.billingPausedCode)
30914
30962
  ],
30915
30963
  ["Sync", syncDisplay.line],
30916
30964
  [syncDisplay.remainingLabel, syncDisplay.remaining],
@@ -30959,11 +31007,16 @@ Table endpoints:`));
30959
31007
  await new Promise((res) => setTimeout(res, 2000));
30960
31008
  continue;
30961
31009
  }
30962
- if (sg?.sync?.status === "synced") {
31010
+ const verdict = watchVerdict(sg);
31011
+ if (verdict.kind === "synced") {
30963
31012
  console.log(dim(`
30964
31013
  Synced, exiting watch.`));
30965
31014
  return;
30966
31015
  }
31016
+ if (verdict.kind === "stopped") {
31017
+ error(verdict.message);
31018
+ process.exit(1);
31019
+ }
30967
31020
  await new Promise((res) => setTimeout(res, 2000));
30968
31021
  }
30969
31022
  } catch (err) {
@@ -31863,7 +31916,7 @@ var {
31863
31916
  // package.json
31864
31917
  var package_default = {
31865
31918
  name: "@secondlayer/cli",
31866
- version: "23.4.2",
31919
+ version: "23.5.0",
31867
31920
  description: "Run a Stacks index on your own hardware — set it up, restore verified history, and keep it current.",
31868
31921
  repository: {
31869
31922
  type: "git",
@@ -31912,10 +31965,10 @@ var package_default = {
31912
31965
  "@inquirer/prompts": "^8.2.0",
31913
31966
  "@secondlayer/bundler": "^0.7.3",
31914
31967
  "@secondlayer/scaffold": "^1.6.3",
31915
- "@secondlayer/sdk": "^16.2.0",
31916
- "@secondlayer/shared": "^15.0.1",
31968
+ "@secondlayer/sdk": "^16.3.0",
31969
+ "@secondlayer/shared": "^15.3.0",
31917
31970
  "@secondlayer/stacks": "^6.6.0",
31918
- "@secondlayer/subgraphs": "^8.0.4",
31971
+ "@secondlayer/subgraphs": "^8.1.0",
31919
31972
  "@secondlayer/verify": "^0.6.3",
31920
31973
  esbuild: "^0.28.2",
31921
31974
  "smol-toml": "^1.9.1",
@@ -35312,7 +35365,7 @@ function attachWebhookSubcommands(parent) {
35312
35365
  console.log(formatTable(["Name", "ID", "Status", "Target", "Format", "Last Success"], data.map((sub) => [
35313
35366
  sub.name,
35314
35367
  sub.id,
35315
- sub.status === "active" ? green(sub.status) : sub.status === "paused" ? yellow(sub.status) : redStatus(sub.status),
35368
+ sub.status === "active" ? green(sub.status) : yellow(sub.status),
35316
35369
  formatWebhookTarget(sub),
35317
35370
  sub.format,
35318
35371
  formatMaybeDate(sub.lastSuccessAt)
@@ -37078,7 +37131,9 @@ function registerLoginCommand(program) {
37078
37131
  Examples:
37079
37132
  $ secondlayer login
37080
37133
  $ secondlayer login --email
37081
- $ echo "$SECONDLAYER_API_KEY" | secondlayer login --with-token`).action((opts) => opts.withToken ? runTokenLogin() : opts.email ? runLoginFlow({ force: opts.force }) : runDeviceLogin({ force: opts.force }));
37134
+ $ echo "$SECONDLAYER_API_KEY" | secondlayer login --with-token
37135
+
37136
+ Precedence: SECONDLAYER_API_KEY (when set) beats the saved login. Unset it to act as the logged-in account.`).action((opts) => opts.withToken ? runTokenLogin() : opts.email ? runLoginFlow({ force: opts.force }) : runDeviceLogin({ force: opts.force }));
37082
37137
  }
37083
37138
  // src/commands/logout.ts
37084
37139
  init_http();
@@ -37713,5 +37768,5 @@ registerCreditsCommand(program);
37713
37768
  registerArchiveCommand(program);
37714
37769
  program.parse();
37715
37770
 
37716
- //# debugId=37772975BA7CA86A64756E2164756E21
37771
+ //# debugId=1383FA11DACB878164756E2164756E21
37717
37772
  //# sourceMappingURL=cli.js.map