@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.
- package/dist/cli.js +100 -45
- 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
|
|
@@ -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
|
-
|
|
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,
|
|
30378
|
-
|
|
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
|
-
|
|
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
|
|
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.
|
|
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
|
-
|
|
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.
|
|
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.
|
|
31916
|
-
"@secondlayer/shared": "^15.0
|
|
31968
|
+
"@secondlayer/sdk": "^16.3.0",
|
|
31969
|
+
"@secondlayer/shared": "^15.3.0",
|
|
31917
31970
|
"@secondlayer/stacks": "^6.6.0",
|
|
31918
|
-
"@secondlayer/subgraphs": "^8.0
|
|
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) :
|
|
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
|
|
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=
|
|
37771
|
+
//# debugId=1383FA11DACB878164756E2164756E21
|
|
37717
37772
|
//# sourceMappingURL=cli.js.map
|