@secondlayer/cli 3.5.2 → 3.5.3
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 +102 -25
- package/dist/cli.js.map +8 -8
- package/package.json +4 -4
package/dist/cli.js
CHANGED
|
@@ -2530,7 +2530,7 @@ async function request(url, opts) {
|
|
|
2530
2530
|
method: opts.method ?? "GET",
|
|
2531
2531
|
headers,
|
|
2532
2532
|
body: opts.body !== undefined ? JSON.stringify(opts.body) : undefined,
|
|
2533
|
-
signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS)
|
|
2533
|
+
signal: AbortSignal.timeout(opts.timeoutMs ?? REQUEST_TIMEOUT_MS)
|
|
2534
2534
|
});
|
|
2535
2535
|
if (!res.ok) {
|
|
2536
2536
|
let body = {};
|
|
@@ -2957,7 +2957,8 @@ function success(message) {
|
|
|
2957
2957
|
console.log(green(`✓ ${message}`));
|
|
2958
2958
|
}
|
|
2959
2959
|
function error(message) {
|
|
2960
|
-
|
|
2960
|
+
const text = message.trim() || "Command failed.";
|
|
2961
|
+
console.error(red(`✗ ${text}`));
|
|
2961
2962
|
}
|
|
2962
2963
|
function warn(message) {
|
|
2963
2964
|
console.log(yellow(`⚠ ${message}`));
|
|
@@ -2966,17 +2967,17 @@ function info(message) {
|
|
|
2966
2967
|
console.log(blue(`ℹ ${message}`));
|
|
2967
2968
|
}
|
|
2968
2969
|
function stripAnsi(text) {
|
|
2969
|
-
return text.replace(
|
|
2970
|
+
return text.replace(ANSI_ESCAPE_PATTERN, "");
|
|
2970
2971
|
}
|
|
2971
2972
|
function formatTable(headers, rows) {
|
|
2972
2973
|
const widths = headers.map((h, i) => {
|
|
2973
2974
|
const colValues = [h, ...rows.map((r) => r[i] || "")];
|
|
2974
2975
|
return Math.max(...colValues.map((v) => stripAnsi(v).length));
|
|
2975
2976
|
});
|
|
2976
|
-
const headerRow = headers.map((h, i) => h.padEnd(widths[i])).join(" ");
|
|
2977
|
+
const headerRow = headers.map((h, i) => h.padEnd(widths[i] ?? 0)).join(" ");
|
|
2977
2978
|
const separator = widths.map((w) => "-".repeat(w)).join(" ");
|
|
2978
2979
|
const dataRows = rows.map((row) => row.map((cell, i) => {
|
|
2979
|
-
const width = widths[i];
|
|
2980
|
+
const width = widths[i] ?? 0;
|
|
2980
2981
|
const padding = width - stripAnsi(cell).length;
|
|
2981
2982
|
return cell + " ".repeat(Math.max(0, padding));
|
|
2982
2983
|
}).join(" "));
|
|
@@ -2988,7 +2989,7 @@ function formatKeyValue(pairs) {
|
|
|
2988
2989
|
return pairs.map(([key, value]) => `${dim(key.padEnd(maxKeyLen))} ${value}`).join(`
|
|
2989
2990
|
`);
|
|
2990
2991
|
}
|
|
2991
|
-
var colors;
|
|
2992
|
+
var colors, ANSI_ESCAPE_PATTERN;
|
|
2992
2993
|
var init_output = __esm(() => {
|
|
2993
2994
|
colors = {
|
|
2994
2995
|
reset: "\x1B[0m",
|
|
@@ -3001,6 +3002,7 @@ var init_output = __esm(() => {
|
|
|
3001
3002
|
dim: "\x1B[2m",
|
|
3002
3003
|
bold: "\x1B[1m"
|
|
3003
3004
|
};
|
|
3005
|
+
ANSI_ESCAPE_PATTERN = new RegExp(`${String.fromCharCode(27)}\\[[0-9;]*m`, "g");
|
|
3004
3006
|
});
|
|
3005
3007
|
|
|
3006
3008
|
// src/lib/docker.ts
|
|
@@ -32437,7 +32439,7 @@ var {
|
|
|
32437
32439
|
// package.json
|
|
32438
32440
|
var package_default = {
|
|
32439
32441
|
name: "@secondlayer/cli",
|
|
32440
|
-
version: "3.5.
|
|
32442
|
+
version: "3.5.3",
|
|
32441
32443
|
description: "CLI for subgraphs and blockchain indexing on Stacks",
|
|
32442
32444
|
type: "module",
|
|
32443
32445
|
bin: {
|
|
@@ -32480,10 +32482,10 @@ var package_default = {
|
|
|
32480
32482
|
dependencies: {
|
|
32481
32483
|
"@inquirer/prompts": "^8.2.0",
|
|
32482
32484
|
"@secondlayer/bundler": "^0.3.2",
|
|
32483
|
-
"@secondlayer/sdk": "^3.2.
|
|
32484
|
-
"@secondlayer/shared": "^4.3.
|
|
32485
|
+
"@secondlayer/sdk": "^3.2.2",
|
|
32486
|
+
"@secondlayer/shared": "^4.3.3",
|
|
32485
32487
|
"@secondlayer/stacks": "^2.0.0",
|
|
32486
|
-
"@secondlayer/subgraphs": "^1.2
|
|
32488
|
+
"@secondlayer/subgraphs": "^1.3.2",
|
|
32487
32489
|
"@biomejs/js-api": "^0.7.0",
|
|
32488
32490
|
"@biomejs/wasm-nodejs": "^1.9.0",
|
|
32489
32491
|
esbuild: "^0.19.0",
|
|
@@ -32562,8 +32564,8 @@ async function backfillSubgraphApi(name, options) {
|
|
|
32562
32564
|
async function stopSubgraphApi(name) {
|
|
32563
32565
|
return (await getTenantClient()).subgraphs.stop(name);
|
|
32564
32566
|
}
|
|
32565
|
-
async function deleteSubgraphApi(name) {
|
|
32566
|
-
return (await getTenantClient()).subgraphs.delete(name);
|
|
32567
|
+
async function deleteSubgraphApi(name, options) {
|
|
32568
|
+
return (await getTenantClient()).subgraphs.delete(name, options);
|
|
32567
32569
|
}
|
|
32568
32570
|
async function deploySubgraphApi(data) {
|
|
32569
32571
|
return (await getTenantClient()).subgraphs.deploy(data);
|
|
@@ -34877,6 +34879,7 @@ ${data.length} subgraph(s) total`));
|
|
|
34877
34879
|
try {
|
|
34878
34880
|
const subgraph = await getSubgraphApi(name);
|
|
34879
34881
|
const rowCounts = Object.entries(subgraph.tables).map(([t, info2]) => `${t}: ${info2.rowCount}`).join(", ") || "N/A";
|
|
34882
|
+
const totalRows = Object.values(subgraph.tables).reduce((sum, info2) => sum + info2.rowCount, 0);
|
|
34880
34883
|
const errorRate = subgraph.health.totalProcessed > 0 ? `${(subgraph.health.errorRate * 100).toFixed(2)}%` : "N/A";
|
|
34881
34884
|
const sync = subgraph.sync;
|
|
34882
34885
|
const syncDisplay = sync ? formatSubgraphSync(sync) : {
|
|
@@ -34895,7 +34898,8 @@ ${data.length} subgraph(s) total`));
|
|
|
34895
34898
|
["Integrity", integrity],
|
|
34896
34899
|
["Gaps", gapSummary],
|
|
34897
34900
|
["Last Block", String(subgraph.lastProcessedBlock)],
|
|
34898
|
-
["
|
|
34901
|
+
["Rows Indexed", totalRows.toLocaleString()],
|
|
34902
|
+
["Table Rows", rowCounts],
|
|
34899
34903
|
["Total Errors", String(subgraph.health.totalErrors)],
|
|
34900
34904
|
["Error Rate", errorRate],
|
|
34901
34905
|
["Last Error", subgraph.health.lastError ?? "none"],
|
|
@@ -35040,9 +35044,9 @@ ${rows.length} row(s)`));
|
|
|
35040
35044
|
handleApiError(err, "query subgraph");
|
|
35041
35045
|
}
|
|
35042
35046
|
});
|
|
35043
|
-
subgraphs.command("delete <name>").description("Delete a subgraph and its data").option("-y, --yes", "Skip confirmation").action(async (name, options2) => {
|
|
35047
|
+
subgraphs.command("delete <name>").description("Delete a subgraph and its data").option("-y, --yes", "Skip confirmation").option("--force", "Cancel active operations and force delete").action(async (name, options2) => {
|
|
35044
35048
|
try {
|
|
35045
|
-
if (!options2.yes) {
|
|
35049
|
+
if (!options2.yes && !options2.force) {
|
|
35046
35050
|
const { confirm: confirm4 } = await import("@inquirer/prompts");
|
|
35047
35051
|
const ok = await confirm4({
|
|
35048
35052
|
message: `Delete subgraph "${name}" and all its data? This cannot be undone.`
|
|
@@ -35052,7 +35056,9 @@ ${rows.length} row(s)`));
|
|
|
35052
35056
|
return;
|
|
35053
35057
|
}
|
|
35054
35058
|
}
|
|
35055
|
-
const result = await deleteSubgraphApi(name
|
|
35059
|
+
const result = await deleteSubgraphApi(name, {
|
|
35060
|
+
force: options2.force
|
|
35061
|
+
});
|
|
35056
35062
|
success(result.message);
|
|
35057
35063
|
} catch (err) {
|
|
35058
35064
|
handleApiError(err, "delete subgraph");
|
|
@@ -36039,6 +36045,7 @@ init_output();
|
|
|
36039
36045
|
init_project_file();
|
|
36040
36046
|
init_resolve_tenant();
|
|
36041
36047
|
import { confirm as confirm5, input as input4, select as select4 } from "@inquirer/prompts";
|
|
36048
|
+
var INSTANCE_CREATE_TIMEOUT_MS = 180000;
|
|
36042
36049
|
function registerInstanceCommand(program2) {
|
|
36043
36050
|
const instance = program2.command("instance").description("Manage your dedicated Secondlayer instance");
|
|
36044
36051
|
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) => {
|
|
@@ -36049,14 +36056,22 @@ function registerInstanceCommand(program2) {
|
|
|
36049
36056
|
error(`Invalid plan: ${plan} (expected hobby, launch, grow, or scale)`);
|
|
36050
36057
|
process.exit(1);
|
|
36051
36058
|
}
|
|
36059
|
+
const spinner = createSpinner("Provisioning your instance (~60s; safe to interrupt — instance will still be created; check `sl instance info`)");
|
|
36052
36060
|
try {
|
|
36053
36061
|
const res = await httpPlatform(`/api/projects/${encodeURIComponent(activeSlug)}/instance`, {
|
|
36054
36062
|
method: "POST",
|
|
36055
|
-
body: { plan }
|
|
36063
|
+
body: { plan },
|
|
36064
|
+
timeoutMs: INSTANCE_CREATE_TIMEOUT_MS
|
|
36056
36065
|
});
|
|
36057
|
-
|
|
36066
|
+
spinner.succeed(`Instance provisioned: ${res.tenant.slug}`);
|
|
36058
36067
|
printKeyReveal(res.tenant, res.credentials);
|
|
36059
36068
|
} catch (err) {
|
|
36069
|
+
if (isTimeoutError(err)) {
|
|
36070
|
+
spinner.fail("Provision request timed out after 3 minutes.");
|
|
36071
|
+
error("Provisioning may still finish server-side. Run `sl instance info` to check before retrying.");
|
|
36072
|
+
process.exit(1);
|
|
36073
|
+
}
|
|
36074
|
+
spinner.fail("Provision failed.");
|
|
36060
36075
|
handleInstanceError(err, "provision instance");
|
|
36061
36076
|
}
|
|
36062
36077
|
});
|
|
@@ -36135,6 +36150,10 @@ function registerInstanceCommand(program2) {
|
|
|
36135
36150
|
}
|
|
36136
36151
|
const slug = info_.tenant.slug;
|
|
36137
36152
|
if (!opts.yes) {
|
|
36153
|
+
if (!process.stdin.isTTY) {
|
|
36154
|
+
error(`Refusing to prompt in a non-interactive terminal. Re-run with --yes to delete instance "${slug}".`);
|
|
36155
|
+
process.exit(1);
|
|
36156
|
+
}
|
|
36138
36157
|
const typed = await input4({
|
|
36139
36158
|
message: `Type the slug "${slug}" to confirm permanent deletion`,
|
|
36140
36159
|
validate: (v) => v === slug ? true : "Slug must match exactly"
|
|
@@ -36302,6 +36321,43 @@ function printKeyReveal(tenant, creds) {
|
|
|
36302
36321
|
console.log(dim("Run `sl subgraphs deploy <file>` to deploy your first subgraph."));
|
|
36303
36322
|
console.log("");
|
|
36304
36323
|
}
|
|
36324
|
+
function createSpinner(message) {
|
|
36325
|
+
if (!process.stderr.isTTY) {
|
|
36326
|
+
info(message);
|
|
36327
|
+
return {
|
|
36328
|
+
succeed: success,
|
|
36329
|
+
fail: error
|
|
36330
|
+
};
|
|
36331
|
+
}
|
|
36332
|
+
const frames = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
|
|
36333
|
+
let index = 0;
|
|
36334
|
+
const render = () => {
|
|
36335
|
+
const frame = frames[index % frames.length] ?? frames[0];
|
|
36336
|
+
index += 1;
|
|
36337
|
+
process.stderr.write(`\r${blue(frame)} ${message}`);
|
|
36338
|
+
};
|
|
36339
|
+
const clear = () => {
|
|
36340
|
+
clearInterval(timer3);
|
|
36341
|
+
process.stderr.write("\r\x1B[2K");
|
|
36342
|
+
};
|
|
36343
|
+
const timer3 = setInterval(render, 80);
|
|
36344
|
+
render();
|
|
36345
|
+
return {
|
|
36346
|
+
succeed(message2) {
|
|
36347
|
+
clear();
|
|
36348
|
+
success(message2);
|
|
36349
|
+
},
|
|
36350
|
+
fail(message2) {
|
|
36351
|
+
clear();
|
|
36352
|
+
error(message2);
|
|
36353
|
+
}
|
|
36354
|
+
};
|
|
36355
|
+
}
|
|
36356
|
+
function isTimeoutError(err) {
|
|
36357
|
+
if (!(err instanceof Error))
|
|
36358
|
+
return false;
|
|
36359
|
+
return err.name === "TimeoutError" || err.name === "AbortError" || err.message.toLowerCase().includes("timeout");
|
|
36360
|
+
}
|
|
36305
36361
|
function warn_box(message) {
|
|
36306
36362
|
return `${"━".repeat(message.length + 4)}
|
|
36307
36363
|
${message}
|
|
@@ -36321,10 +36377,10 @@ function handleInstanceError(err, action) {
|
|
|
36321
36377
|
error("This project already has an instance. Run `sl instance info` to see it.");
|
|
36322
36378
|
process.exit(1);
|
|
36323
36379
|
}
|
|
36324
|
-
error(err.message);
|
|
36380
|
+
error(err.message || `Failed to ${action}.`);
|
|
36325
36381
|
process.exit(1);
|
|
36326
36382
|
}
|
|
36327
|
-
error(`Failed to ${action}: ${err instanceof Error ? err.message : String(err)}`);
|
|
36383
|
+
error(`Failed to ${action}: ${err instanceof Error ? err.message || "Unknown error" : String(err)}`);
|
|
36328
36384
|
process.exit(1);
|
|
36329
36385
|
}
|
|
36330
36386
|
// src/commands/project.ts
|
|
@@ -36335,15 +36391,24 @@ init_project_file();
|
|
|
36335
36391
|
import { input as input5 } from "@inquirer/prompts";
|
|
36336
36392
|
function registerProjectCommand(program2) {
|
|
36337
36393
|
const project = program2.command("project").description("Manage Secondlayer projects");
|
|
36338
|
-
project.command("create [name]").description("Create a new project").action(async (nameArg) => {
|
|
36394
|
+
project.command("create [name]").description("Create a new project").option("--slug <slug>", "Project URL identifier").action(async (nameArg, options2 = {}) => {
|
|
36339
36395
|
const name = nameArg ?? await input5({
|
|
36340
36396
|
message: "Project name",
|
|
36341
36397
|
validate: (v) => v.length >= 2 ? true : "Name must be at least 2 characters"
|
|
36342
36398
|
});
|
|
36399
|
+
const slug = options2.slug ?? slugifyProjectName(name);
|
|
36400
|
+
const validation = validateProjectSlug(slug);
|
|
36401
|
+
if (validation !== true) {
|
|
36402
|
+
error(`${validation}. Pass --slug <slug> to choose one explicitly.`);
|
|
36403
|
+
process.exit(1);
|
|
36404
|
+
}
|
|
36343
36405
|
try {
|
|
36344
|
-
const res = await httpPlatform("/api/projects", {
|
|
36345
|
-
|
|
36346
|
-
|
|
36406
|
+
const res = await httpPlatform("/api/projects", {
|
|
36407
|
+
method: "POST",
|
|
36408
|
+
body: { name, slug }
|
|
36409
|
+
});
|
|
36410
|
+
success(`Created project ${res.name} (${res.slug})`);
|
|
36411
|
+
const path2 = await writeActiveProject(res.slug, process.cwd());
|
|
36347
36412
|
info(dim(`Bound to this directory → ${path2}`));
|
|
36348
36413
|
info(dim("Next: sl instance create --plan launch"));
|
|
36349
36414
|
} catch (err) {
|
|
@@ -36410,6 +36475,18 @@ function handleProjectError(err) {
|
|
|
36410
36475
|
error(err instanceof Error ? err.message : String(err));
|
|
36411
36476
|
process.exit(1);
|
|
36412
36477
|
}
|
|
36478
|
+
function slugifyProjectName(name) {
|
|
36479
|
+
return name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 63).replace(/-+$/g, "");
|
|
36480
|
+
}
|
|
36481
|
+
function validateProjectSlug(slug) {
|
|
36482
|
+
if (slug.length < 2 || slug.length > 63) {
|
|
36483
|
+
return "Project slug must be 2-63 characters";
|
|
36484
|
+
}
|
|
36485
|
+
if (!/^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/.test(slug)) {
|
|
36486
|
+
return "Project slug must use lowercase letters, numbers, and hyphens, and start/end with a letter or number";
|
|
36487
|
+
}
|
|
36488
|
+
return true;
|
|
36489
|
+
}
|
|
36413
36490
|
// src/cli.ts
|
|
36414
36491
|
var { version } = package_default;
|
|
36415
36492
|
program.name("secondlayer").alias("sl").description("SecondLayer CLI — dedicated Stacks indexing + real-time subgraphs").version(version).option("--network <network>", "Override network (local, testnet, mainnet)");
|
|
@@ -36451,5 +36528,5 @@ registerLocalCommand(program);
|
|
|
36451
36528
|
registerAccountCommand(program);
|
|
36452
36529
|
program.parse();
|
|
36453
36530
|
|
|
36454
|
-
//# debugId=
|
|
36531
|
+
//# debugId=BE2AD99915C01F0964756E2164756E21
|
|
36455
36532
|
//# sourceMappingURL=cli.js.map
|