@secondlayer/cli 23.4.3 → 23.5.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/dist/cli.js +93 -43
- package/dist/cli.js.map +12 -11
- 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 {
|
|
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 (
|
|
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 =
|
|
2201
|
+
async function readSession(apiUrl = resolveArchiveOpsUrl(), path = getSessionPath()) {
|
|
2197
2202
|
const file = await readSessionFile(path);
|
|
2198
|
-
|
|
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 =
|
|
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 =
|
|
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 {
|
|
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
|
-
|
|
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:
|
|
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
|
|
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`."
|
|
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
|
|
@@ -29022,6 +29034,25 @@ var init_subgraphs = __esm(() => {
|
|
|
29022
29034
|
init_format();
|
|
29023
29035
|
});
|
|
29024
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
|
+
|
|
29025
29056
|
// src/lib/derive-abi.ts
|
|
29026
29057
|
import {
|
|
29027
29058
|
extractSubgraphDefinition,
|
|
@@ -30006,14 +30037,26 @@ import {
|
|
|
30006
30037
|
generatePrintSchemaSubgraph as generatePrintSchemaSubgraph2,
|
|
30007
30038
|
generateTraitSubgraph as generateTraitSubgraph2
|
|
30008
30039
|
} from "@secondlayer/scaffold";
|
|
30009
|
-
import {
|
|
30010
|
-
billingPausedCode
|
|
30011
|
-
} from "@secondlayer/shared/schemas";
|
|
30012
30040
|
import { TRAIT_STANDARDS } from "@secondlayer/stacks/clarity";
|
|
30013
30041
|
import {
|
|
30014
30042
|
deriveVerification as deriveVerification2,
|
|
30015
30043
|
isDeterminismViolation
|
|
30016
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
|
+
}
|
|
30017
30060
|
async function loadSubgraphDefinition(source, fileName) {
|
|
30018
30061
|
const { bundleSubgraphCode } = await import("@secondlayer/bundler");
|
|
30019
30062
|
const bundled = await bundleSubgraphCode(source, { fileName });
|
|
@@ -30379,9 +30422,8 @@ function formatOperationRange(op) {
|
|
|
30379
30422
|
return "whole subgraph";
|
|
30380
30423
|
return `${op.fromBlock ?? "start"} → ${op.toBlock ?? "tip"}`;
|
|
30381
30424
|
}
|
|
30382
|
-
function formatSubgraphStatus(status,
|
|
30383
|
-
|
|
30384
|
-
return code ? `${status} (paused: ${code.replace(/_/g, " ")})` : status;
|
|
30425
|
+
function formatSubgraphStatus(status, billingPausedCode) {
|
|
30426
|
+
return billingPausedCode ? `${status} (paused: ${billingPausedCode.replace(/_/g, " ")})` : status;
|
|
30385
30427
|
}
|
|
30386
30428
|
function formatOperationProgress(progress) {
|
|
30387
30429
|
if (progress === null)
|
|
@@ -30627,7 +30669,8 @@ Check the contract id, and that the contract has emitted print events.`);
|
|
|
30627
30669
|
error(`[${new Date().toLocaleTimeString()}] ${err}`);
|
|
30628
30670
|
}
|
|
30629
30671
|
};
|
|
30630
|
-
|
|
30672
|
+
const runner = createCoalescedRunner(deploySubgraph);
|
|
30673
|
+
await runner.trigger();
|
|
30631
30674
|
let timeout = null;
|
|
30632
30675
|
const watcher = watch(absPath, () => {
|
|
30633
30676
|
if (timeout)
|
|
@@ -30635,7 +30678,7 @@ Check the contract id, and that the contract has emitted print events.`);
|
|
|
30635
30678
|
timeout = setTimeout(async () => {
|
|
30636
30679
|
console.log("");
|
|
30637
30680
|
info("File changed, redeploying...");
|
|
30638
|
-
await
|
|
30681
|
+
await runner.trigger();
|
|
30639
30682
|
}, 300);
|
|
30640
30683
|
});
|
|
30641
30684
|
process.on("SIGINT", () => {
|
|
@@ -30892,7 +30935,7 @@ ${data.length} subgraph(s) total`));
|
|
|
30892
30935
|
handleApiError(err, "list subgraphs");
|
|
30893
30936
|
}
|
|
30894
30937
|
});
|
|
30895
|
-
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) => {
|
|
30896
30939
|
const renderOnce = async (opts = {}) => {
|
|
30897
30940
|
const subgraph = await getSubgraphApi(name);
|
|
30898
30941
|
if (opts.clearScreen)
|
|
@@ -30915,7 +30958,7 @@ ${data.length} subgraph(s) total`));
|
|
|
30915
30958
|
["Verification", formatVerificationStatus(subgraph.verification)],
|
|
30916
30959
|
[
|
|
30917
30960
|
"Status",
|
|
30918
|
-
formatSubgraphStatus(subgraph.status, subgraph.health.
|
|
30961
|
+
formatSubgraphStatus(subgraph.status, subgraph.health.billingPausedCode)
|
|
30919
30962
|
],
|
|
30920
30963
|
["Sync", syncDisplay.line],
|
|
30921
30964
|
[syncDisplay.remainingLabel, syncDisplay.remaining],
|
|
@@ -30964,11 +31007,16 @@ Table endpoints:`));
|
|
|
30964
31007
|
await new Promise((res) => setTimeout(res, 2000));
|
|
30965
31008
|
continue;
|
|
30966
31009
|
}
|
|
30967
|
-
|
|
31010
|
+
const verdict = watchVerdict(sg);
|
|
31011
|
+
if (verdict.kind === "synced") {
|
|
30968
31012
|
console.log(dim(`
|
|
30969
31013
|
Synced, exiting watch.`));
|
|
30970
31014
|
return;
|
|
30971
31015
|
}
|
|
31016
|
+
if (verdict.kind === "stopped") {
|
|
31017
|
+
error(verdict.message);
|
|
31018
|
+
process.exit(1);
|
|
31019
|
+
}
|
|
30972
31020
|
await new Promise((res) => setTimeout(res, 2000));
|
|
30973
31021
|
}
|
|
30974
31022
|
} catch (err) {
|
|
@@ -31868,7 +31916,7 @@ var {
|
|
|
31868
31916
|
// package.json
|
|
31869
31917
|
var package_default = {
|
|
31870
31918
|
name: "@secondlayer/cli",
|
|
31871
|
-
version: "23.
|
|
31919
|
+
version: "23.5.1",
|
|
31872
31920
|
description: "Run a Stacks index on your own hardware — set it up, restore verified history, and keep it current.",
|
|
31873
31921
|
repository: {
|
|
31874
31922
|
type: "git",
|
|
@@ -31917,10 +31965,10 @@ var package_default = {
|
|
|
31917
31965
|
"@inquirer/prompts": "^8.2.0",
|
|
31918
31966
|
"@secondlayer/bundler": "^0.7.3",
|
|
31919
31967
|
"@secondlayer/scaffold": "^1.6.3",
|
|
31920
|
-
"@secondlayer/sdk": "^16.2
|
|
31921
|
-
"@secondlayer/shared": "^15.2
|
|
31968
|
+
"@secondlayer/sdk": "^16.3.2",
|
|
31969
|
+
"@secondlayer/shared": "^15.3.2",
|
|
31922
31970
|
"@secondlayer/stacks": "^6.6.0",
|
|
31923
|
-
"@secondlayer/subgraphs": "^8.0
|
|
31971
|
+
"@secondlayer/subgraphs": "^8.1.0",
|
|
31924
31972
|
"@secondlayer/verify": "^0.6.3",
|
|
31925
31973
|
esbuild: "^0.28.2",
|
|
31926
31974
|
"smol-toml": "^1.9.1",
|
|
@@ -34489,7 +34537,7 @@ function registerCreditsCommand(program) {
|
|
|
34489
34537
|
credits.command("buy").description("Open a one-time Stripe checkout for 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));
|
|
34490
34538
|
credits.command("balance").description("Show prepaid archive credit balance").option("--json", "Output as JSON").action((opts) => runBalance(opts));
|
|
34491
34539
|
credits.command("usage").description("Rows delivered per day: free vs paid, by source").option("--days <n>", "Days to show (max 30)", "30").option("--json", "Output as JSON").action((opts) => runUsage(opts));
|
|
34492
|
-
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));
|
|
34540
|
+
credits.command("refill").description("Opt-in auto-refill when the balance drops under a threshold. Needs a signed-in session (`secondlayer login`), not an API key").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));
|
|
34493
34541
|
}
|
|
34494
34542
|
async function runRefill(opts) {
|
|
34495
34543
|
if (!opts.off && opts.below === undefined) {
|
|
@@ -35317,7 +35365,7 @@ function attachWebhookSubcommands(parent) {
|
|
|
35317
35365
|
console.log(formatTable(["Name", "ID", "Status", "Target", "Format", "Last Success"], data.map((sub) => [
|
|
35318
35366
|
sub.name,
|
|
35319
35367
|
sub.id,
|
|
35320
|
-
sub.status === "active" ? green(sub.status) :
|
|
35368
|
+
sub.status === "active" ? green(sub.status) : yellow(sub.status),
|
|
35321
35369
|
formatWebhookTarget(sub),
|
|
35322
35370
|
sub.format,
|
|
35323
35371
|
formatMaybeDate(sub.lastSuccessAt)
|
|
@@ -37083,7 +37131,9 @@ function registerLoginCommand(program) {
|
|
|
37083
37131
|
Examples:
|
|
37084
37132
|
$ secondlayer login
|
|
37085
37133
|
$ secondlayer login --email
|
|
37086
|
-
$ echo "$SECONDLAYER_API_KEY" | secondlayer login --with-token
|
|
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 }));
|
|
37087
37137
|
}
|
|
37088
37138
|
// src/commands/logout.ts
|
|
37089
37139
|
init_http();
|
|
@@ -37718,5 +37768,5 @@ registerCreditsCommand(program);
|
|
|
37718
37768
|
registerArchiveCommand(program);
|
|
37719
37769
|
program.parse();
|
|
37720
37770
|
|
|
37721
|
-
//# debugId=
|
|
37771
|
+
//# debugId=2C10A69C0E77EE3B64756E2164756E21
|
|
37722
37772
|
//# sourceMappingURL=cli.js.map
|