@secondlayer/cli 3.5.1 → 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 -26
- 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,8 +34898,8 @@ ${data.length} subgraph(s) total`));
|
|
|
34895
34898
|
["Integrity", integrity],
|
|
34896
34899
|
["Gaps", gapSummary],
|
|
34897
34900
|
["Last Block", String(subgraph.lastProcessedBlock)],
|
|
34898
|
-
["
|
|
34899
|
-
["
|
|
34901
|
+
["Rows Indexed", totalRows.toLocaleString()],
|
|
34902
|
+
["Table Rows", rowCounts],
|
|
34900
34903
|
["Total Errors", String(subgraph.health.totalErrors)],
|
|
34901
34904
|
["Error Rate", errorRate],
|
|
34902
34905
|
["Last Error", subgraph.health.lastError ?? "none"],
|
|
@@ -35041,9 +35044,9 @@ ${rows.length} row(s)`));
|
|
|
35041
35044
|
handleApiError(err, "query subgraph");
|
|
35042
35045
|
}
|
|
35043
35046
|
});
|
|
35044
|
-
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) => {
|
|
35045
35048
|
try {
|
|
35046
|
-
if (!options2.yes) {
|
|
35049
|
+
if (!options2.yes && !options2.force) {
|
|
35047
35050
|
const { confirm: confirm4 } = await import("@inquirer/prompts");
|
|
35048
35051
|
const ok = await confirm4({
|
|
35049
35052
|
message: `Delete subgraph "${name}" and all its data? This cannot be undone.`
|
|
@@ -35053,7 +35056,9 @@ ${rows.length} row(s)`));
|
|
|
35053
35056
|
return;
|
|
35054
35057
|
}
|
|
35055
35058
|
}
|
|
35056
|
-
const result = await deleteSubgraphApi(name
|
|
35059
|
+
const result = await deleteSubgraphApi(name, {
|
|
35060
|
+
force: options2.force
|
|
35061
|
+
});
|
|
35057
35062
|
success(result.message);
|
|
35058
35063
|
} catch (err) {
|
|
35059
35064
|
handleApiError(err, "delete subgraph");
|
|
@@ -36040,6 +36045,7 @@ init_output();
|
|
|
36040
36045
|
init_project_file();
|
|
36041
36046
|
init_resolve_tenant();
|
|
36042
36047
|
import { confirm as confirm5, input as input4, select as select4 } from "@inquirer/prompts";
|
|
36048
|
+
var INSTANCE_CREATE_TIMEOUT_MS = 180000;
|
|
36043
36049
|
function registerInstanceCommand(program2) {
|
|
36044
36050
|
const instance = program2.command("instance").description("Manage your dedicated Secondlayer instance");
|
|
36045
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) => {
|
|
@@ -36050,14 +36056,22 @@ function registerInstanceCommand(program2) {
|
|
|
36050
36056
|
error(`Invalid plan: ${plan} (expected hobby, launch, grow, or scale)`);
|
|
36051
36057
|
process.exit(1);
|
|
36052
36058
|
}
|
|
36059
|
+
const spinner = createSpinner("Provisioning your instance (~60s; safe to interrupt — instance will still be created; check `sl instance info`)");
|
|
36053
36060
|
try {
|
|
36054
36061
|
const res = await httpPlatform(`/api/projects/${encodeURIComponent(activeSlug)}/instance`, {
|
|
36055
36062
|
method: "POST",
|
|
36056
|
-
body: { plan }
|
|
36063
|
+
body: { plan },
|
|
36064
|
+
timeoutMs: INSTANCE_CREATE_TIMEOUT_MS
|
|
36057
36065
|
});
|
|
36058
|
-
|
|
36066
|
+
spinner.succeed(`Instance provisioned: ${res.tenant.slug}`);
|
|
36059
36067
|
printKeyReveal(res.tenant, res.credentials);
|
|
36060
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.");
|
|
36061
36075
|
handleInstanceError(err, "provision instance");
|
|
36062
36076
|
}
|
|
36063
36077
|
});
|
|
@@ -36136,6 +36150,10 @@ function registerInstanceCommand(program2) {
|
|
|
36136
36150
|
}
|
|
36137
36151
|
const slug = info_.tenant.slug;
|
|
36138
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
|
+
}
|
|
36139
36157
|
const typed = await input4({
|
|
36140
36158
|
message: `Type the slug "${slug}" to confirm permanent deletion`,
|
|
36141
36159
|
validate: (v) => v === slug ? true : "Slug must match exactly"
|
|
@@ -36303,6 +36321,43 @@ function printKeyReveal(tenant, creds) {
|
|
|
36303
36321
|
console.log(dim("Run `sl subgraphs deploy <file>` to deploy your first subgraph."));
|
|
36304
36322
|
console.log("");
|
|
36305
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
|
+
}
|
|
36306
36361
|
function warn_box(message) {
|
|
36307
36362
|
return `${"━".repeat(message.length + 4)}
|
|
36308
36363
|
${message}
|
|
@@ -36322,10 +36377,10 @@ function handleInstanceError(err, action) {
|
|
|
36322
36377
|
error("This project already has an instance. Run `sl instance info` to see it.");
|
|
36323
36378
|
process.exit(1);
|
|
36324
36379
|
}
|
|
36325
|
-
error(err.message);
|
|
36380
|
+
error(err.message || `Failed to ${action}.`);
|
|
36326
36381
|
process.exit(1);
|
|
36327
36382
|
}
|
|
36328
|
-
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)}`);
|
|
36329
36384
|
process.exit(1);
|
|
36330
36385
|
}
|
|
36331
36386
|
// src/commands/project.ts
|
|
@@ -36336,15 +36391,24 @@ init_project_file();
|
|
|
36336
36391
|
import { input as input5 } from "@inquirer/prompts";
|
|
36337
36392
|
function registerProjectCommand(program2) {
|
|
36338
36393
|
const project = program2.command("project").description("Manage Secondlayer projects");
|
|
36339
|
-
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 = {}) => {
|
|
36340
36395
|
const name = nameArg ?? await input5({
|
|
36341
36396
|
message: "Project name",
|
|
36342
36397
|
validate: (v) => v.length >= 2 ? true : "Name must be at least 2 characters"
|
|
36343
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
|
+
}
|
|
36344
36405
|
try {
|
|
36345
|
-
const res = await httpPlatform("/api/projects", {
|
|
36346
|
-
|
|
36347
|
-
|
|
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());
|
|
36348
36412
|
info(dim(`Bound to this directory → ${path2}`));
|
|
36349
36413
|
info(dim("Next: sl instance create --plan launch"));
|
|
36350
36414
|
} catch (err) {
|
|
@@ -36411,6 +36475,18 @@ function handleProjectError(err) {
|
|
|
36411
36475
|
error(err instanceof Error ? err.message : String(err));
|
|
36412
36476
|
process.exit(1);
|
|
36413
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
|
+
}
|
|
36414
36490
|
// src/cli.ts
|
|
36415
36491
|
var { version } = package_default;
|
|
36416
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)");
|
|
@@ -36452,5 +36528,5 @@ registerLocalCommand(program);
|
|
|
36452
36528
|
registerAccountCommand(program);
|
|
36453
36529
|
program.parse();
|
|
36454
36530
|
|
|
36455
|
-
//# debugId=
|
|
36531
|
+
//# debugId=BE2AD99915C01F0964756E2164756E21
|
|
36456
36532
|
//# sourceMappingURL=cli.js.map
|