@secondlayer/cli 19.4.2 → 20.0.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/README.md +9 -11
- package/dist/cli.js +139 -513
- package/dist/cli.js.map +9 -10
- package/package.json +9 -9
- package/templates/webhooks/cloudflare/.env.example +0 -1
- package/templates/webhooks/cloudflare/README.md +0 -44
- package/templates/webhooks/cloudflare/package.json +0 -15
- package/templates/webhooks/cloudflare/src/index.ts +0 -34
- package/templates/webhooks/cloudflare/tsconfig.json +0 -10
- package/templates/webhooks/cloudflare/wrangler.toml +0 -8
- package/templates/webhooks/inngest/.env.example +0 -2
- package/templates/webhooks/inngest/README.md +0 -29
- package/templates/webhooks/inngest/package.json +0 -18
- package/templates/webhooks/inngest/src/inngest.ts +0 -19
- package/templates/webhooks/inngest/src/server.ts +0 -14
- package/templates/webhooks/inngest/tsconfig.json +0 -11
- package/templates/webhooks/node/.env.example +0 -2
- package/templates/webhooks/node/README.md +0 -36
- package/templates/webhooks/node/package.json +0 -18
- package/templates/webhooks/node/src/index.ts +0 -53
- package/templates/webhooks/node/tsconfig.json +0 -11
- package/templates/webhooks/trigger/.env.example +0 -2
- package/templates/webhooks/trigger/README.md +0 -38
- package/templates/webhooks/trigger/package.json +0 -17
- package/templates/webhooks/trigger/src/tasks/subgraph-event.ts +0 -15
- package/templates/webhooks/trigger/trigger.config.ts +0 -6
- package/templates/webhooks/trigger/tsconfig.json +0 -10
package/dist/cli.js
CHANGED
|
@@ -29360,11 +29360,10 @@ function parseFilterArg(input) {
|
|
|
29360
29360
|
return {
|
|
29361
29361
|
field,
|
|
29362
29362
|
operator,
|
|
29363
|
-
value
|
|
29364
|
-
explicitOperator: true
|
|
29363
|
+
value
|
|
29365
29364
|
};
|
|
29366
29365
|
}
|
|
29367
|
-
return { field: key, operator: "eq", value
|
|
29366
|
+
return { field: key, operator: "eq", value };
|
|
29368
29367
|
}
|
|
29369
29368
|
function parseFilterArgs(args) {
|
|
29370
29369
|
return (args ?? []).map(parseFilterArg);
|
|
@@ -29377,25 +29376,6 @@ function parseQueryFilters(args) {
|
|
|
29377
29376
|
}
|
|
29378
29377
|
return Object.keys(filters).length > 0 ? filters : undefined;
|
|
29379
29378
|
}
|
|
29380
|
-
function parseWebhookFilter(args) {
|
|
29381
|
-
const filters = {};
|
|
29382
|
-
const seenFields = new Set;
|
|
29383
|
-
for (const filter of parseFilterArgs(args)) {
|
|
29384
|
-
if (filter.operator === "like") {
|
|
29385
|
-
throw new Error(`Webhook filters do not support ".like". Supported operators: ${FILTER_OPERATORS.join(", ")}.`);
|
|
29386
|
-
}
|
|
29387
|
-
if (seenFields.has(filter.field)) {
|
|
29388
|
-
throw new Error(`Webhook filters support one condition per field; got multiple filters for "${filter.field}".`);
|
|
29389
|
-
}
|
|
29390
|
-
seenFields.add(filter.field);
|
|
29391
|
-
if (!filter.explicitOperator || filter.operator === "eq") {
|
|
29392
|
-
filters[filter.field] = filter.value;
|
|
29393
|
-
} else {
|
|
29394
|
-
filters[filter.field] = { [filter.operator]: filter.value };
|
|
29395
|
-
}
|
|
29396
|
-
}
|
|
29397
|
-
return Object.keys(filters).length > 0 ? filters : undefined;
|
|
29398
|
-
}
|
|
29399
29379
|
var FILTER_OPERATORS, QUERY_ONLY_OPERATORS, ALL_OPERATORS, ALL_OPERATOR_LABELS;
|
|
29400
29380
|
var init_filter_params = __esm(() => {
|
|
29401
29381
|
FILTER_OPERATORS = ["eq", "neq", "gt", "gte", "lt", "lte"];
|
|
@@ -198809,11 +198789,6 @@ import {
|
|
|
198809
198789
|
deriveVerification as deriveVerification2,
|
|
198810
198790
|
isDeterminismViolation
|
|
198811
198791
|
} from "@secondlayer/subgraphs/verification";
|
|
198812
|
-
function webhookHint(apiUrl, name, firstTable) {
|
|
198813
|
-
if (!firstTable || isMerchantUrl(apiUrl))
|
|
198814
|
-
return null;
|
|
198815
|
-
return `secondlayer webhooks create ${name}-hook --subgraph ${name} --table ${firstTable} --url <your-endpoint>`;
|
|
198816
|
-
}
|
|
198817
198792
|
async function loadSubgraphWithDepCheck(absPath) {
|
|
198818
198793
|
try {
|
|
198819
198794
|
return await import(absPath);
|
|
@@ -199604,9 +199579,6 @@ deploy does not backfill from genesis.`).action(async (file, options) => {
|
|
|
199604
199579
|
info(` Reindex: ${denom} started in the background — check status in ~30s for an ETA`);
|
|
199605
199580
|
}
|
|
199606
199581
|
info(` Watch: secondlayer subgraphs status ${effectiveDef.name}`);
|
|
199607
|
-
const hint = webhookHint(apiUrl, effectiveDef.name, firstTable);
|
|
199608
|
-
if (hint)
|
|
199609
|
-
info(` Webhook: ${hint}`);
|
|
199610
199582
|
} catch {}
|
|
199611
199583
|
};
|
|
199612
199584
|
if (result.action === "unchanged") {
|
|
@@ -200273,7 +200245,6 @@ var init_subgraphs2 = __esm(() => {
|
|
|
200273
200245
|
init_subgraphs();
|
|
200274
200246
|
init_api_client();
|
|
200275
200247
|
init_derive_abi();
|
|
200276
|
-
init_api_url();
|
|
200277
200248
|
init_config2();
|
|
200278
200249
|
init_filter_params();
|
|
200279
200250
|
init_fs();
|
|
@@ -202507,7 +202478,7 @@ var init_src = __esm(() => {
|
|
|
202507
202478
|
|
|
202508
202479
|
// src/lib/dev-state.ts
|
|
202509
202480
|
import { homedir as homedir4 } from "node:os";
|
|
202510
|
-
import { join as
|
|
202481
|
+
import { join as join12 } from "node:path";
|
|
202511
202482
|
function getLogsDir() {
|
|
202512
202483
|
return LOGS_DIR;
|
|
202513
202484
|
}
|
|
@@ -202532,9 +202503,9 @@ function isProcessRunning(pid) {
|
|
|
202532
202503
|
}
|
|
202533
202504
|
var STATE_DIR, DEV_STATE_PATH, LOGS_DIR;
|
|
202534
202505
|
var init_dev_state = __esm(() => {
|
|
202535
|
-
STATE_DIR =
|
|
202536
|
-
DEV_STATE_PATH =
|
|
202537
|
-
LOGS_DIR =
|
|
202506
|
+
STATE_DIR = join12(homedir4(), ".secondlayer");
|
|
202507
|
+
DEV_STATE_PATH = join12(STATE_DIR, "dev.json");
|
|
202508
|
+
LOGS_DIR = join12(STATE_DIR, "logs");
|
|
202538
202509
|
});
|
|
202539
202510
|
|
|
202540
202511
|
// ../../node_modules/commander/index.js
|
|
@@ -202598,7 +202569,7 @@ var {
|
|
|
202598
202569
|
// package.json
|
|
202599
202570
|
var package_default = {
|
|
202600
202571
|
name: "@secondlayer/cli",
|
|
202601
|
-
version: "
|
|
202572
|
+
version: "20.0.0",
|
|
202602
202573
|
description: "Run a Stacks index on your own hardware — set it up, restore verified history, and keep it current.",
|
|
202603
202574
|
repository: {
|
|
202604
202575
|
type: "git",
|
|
@@ -202619,7 +202590,7 @@ var package_default = {
|
|
|
202619
202590
|
import: "./dist/index.js"
|
|
202620
202591
|
}
|
|
202621
202592
|
},
|
|
202622
|
-
files: ["dist", "
|
|
202593
|
+
files: ["dist", "README.md"],
|
|
202623
202594
|
scripts: {
|
|
202624
202595
|
build: "bunup",
|
|
202625
202596
|
dev: "bunup --watch",
|
|
@@ -202635,15 +202606,15 @@ var package_default = {
|
|
|
202635
202606
|
"@biomejs/wasm-nodejs": "^1.9.0",
|
|
202636
202607
|
"@dsnp/parquetjs": "~1.8.8",
|
|
202637
202608
|
"@inquirer/prompts": "^8.2.0",
|
|
202638
|
-
"@secondlayer/bundler": "^0.7.
|
|
202639
|
-
"@secondlayer/scaffold": "^1.6.
|
|
202640
|
-
"@secondlayer/sdk": "^
|
|
202641
|
-
"@secondlayer/shared": "^
|
|
202609
|
+
"@secondlayer/bundler": "^0.7.1",
|
|
202610
|
+
"@secondlayer/scaffold": "^1.6.2",
|
|
202611
|
+
"@secondlayer/sdk": "^15.0.0",
|
|
202612
|
+
"@secondlayer/shared": "^12.0.0",
|
|
202642
202613
|
"@secondlayer/stacks": "^6.2.0",
|
|
202643
|
-
"@secondlayer/subgraphs": "^
|
|
202644
|
-
"@secondlayer/verify": "^0.
|
|
202614
|
+
"@secondlayer/subgraphs": "^7.0.0",
|
|
202615
|
+
"@secondlayer/verify": "^0.4.0",
|
|
202645
202616
|
esbuild: "^0.28.2",
|
|
202646
|
-
"smol-toml": "^1.
|
|
202617
|
+
"smol-toml": "^1.9.1",
|
|
202647
202618
|
zod: "^4.3.6"
|
|
202648
202619
|
},
|
|
202649
202620
|
peerDependencies: {
|
|
@@ -204121,7 +204092,7 @@ async function readCheckpoint(path) {
|
|
|
204121
204092
|
function defaultSource(node) {
|
|
204122
204093
|
const api = new SecondlayerProofSource({
|
|
204123
204094
|
baseUrl: resolveApiUrl(),
|
|
204124
|
-
apiKey: resolveDataPlaneKey()
|
|
204095
|
+
apiKey: resolveDataPlaneKey()
|
|
204125
204096
|
});
|
|
204126
204097
|
return node ? { ...api, ...new NodeRpcProofSource({ nodeUrl: node }) } : api;
|
|
204127
204098
|
}
|
|
@@ -204335,13 +204306,13 @@ function parseHeight2(raw, flag) {
|
|
|
204335
204306
|
throw new InputError(`${flag} must be a block height, got "${raw}"`);
|
|
204336
204307
|
return Number(raw);
|
|
204337
204308
|
}
|
|
204338
|
-
async function readTable(fetchRows, name, table) {
|
|
204309
|
+
async function readTable(fetchRows, name, table, pageSize = PAGE) {
|
|
204339
204310
|
const out = [];
|
|
204340
204311
|
let cursor;
|
|
204341
204312
|
for (;; ) {
|
|
204342
204313
|
const page = await fetchRows(name, table, cursor);
|
|
204343
204314
|
out.push(...page.rows);
|
|
204344
|
-
if (!page.next_cursor || page.rows.length
|
|
204315
|
+
if (!page.next_cursor || page.rows.length < pageSize)
|
|
204345
204316
|
return out;
|
|
204346
204317
|
cursor = page.next_cursor;
|
|
204347
204318
|
}
|
|
@@ -205574,84 +205545,19 @@ function printPublicStatus(status, diagnosis) {
|
|
|
205574
205545
|
}
|
|
205575
205546
|
// src/commands/webhooks.ts
|
|
205576
205547
|
init_api_client();
|
|
205577
|
-
init_filter_params();
|
|
205578
205548
|
init_output();
|
|
205579
205549
|
init_resolve_auth();
|
|
205580
205550
|
import { buildDoctorReport, isSuccessDelivery } from "@secondlayer/sdk";
|
|
205581
205551
|
import { sign } from "@secondlayer/shared/crypto/standard-webhooks";
|
|
205582
205552
|
|
|
205583
|
-
// src/lib/webhook-validation.ts
|
|
205584
|
-
import {
|
|
205585
|
-
validateWebhookFilterForTable
|
|
205586
|
-
} from "@secondlayer/shared/schemas/webhooks";
|
|
205587
|
-
async function validateWebhookTargetFromApi(client, input) {
|
|
205588
|
-
const subgraph = await client.subgraphs.status(input.subgraphName);
|
|
205589
|
-
const errors = validateWebhookFilterForTable({
|
|
205590
|
-
subgraphName: input.subgraphName,
|
|
205591
|
-
tableName: input.tableName,
|
|
205592
|
-
filter: input.filter,
|
|
205593
|
-
tables: subgraph.tables
|
|
205594
|
-
});
|
|
205595
|
-
if (errors.length > 0) {
|
|
205596
|
-
throw new Error(errors.join(`
|
|
205597
|
-
`));
|
|
205598
|
-
}
|
|
205599
|
-
}
|
|
205600
|
-
|
|
205601
205553
|
// src/commands/create.ts
|
|
205602
|
-
init_filter_params();
|
|
205603
205554
|
init_output();
|
|
205604
205555
|
init_resolve_auth();
|
|
205605
|
-
import {
|
|
205606
|
-
chmodSync,
|
|
205607
|
-
copyFileSync,
|
|
205608
|
-
existsSync as existsSync5,
|
|
205609
|
-
mkdirSync as mkdirSync3,
|
|
205610
|
-
readFileSync as readFileSync5,
|
|
205611
|
-
readdirSync,
|
|
205612
|
-
statSync as statSync2,
|
|
205613
|
-
writeFileSync as writeFileSync4
|
|
205614
|
-
} from "node:fs";
|
|
205615
|
-
import { dirname as dirname6, join as join9, relative as relative2, resolve as resolve7 } from "node:path";
|
|
205616
|
-
import { fileURLToPath as fileURLToPath4 } from "node:url";
|
|
205617
|
-
import { input, select as select3 } from "@inquirer/prompts";
|
|
205556
|
+
import { readFileSync as readFileSync5 } from "node:fs";
|
|
205618
205557
|
import { SecondLayer as SecondLayer4 } from "@secondlayer/sdk";
|
|
205619
205558
|
import {
|
|
205620
205559
|
ChainTriggerSchema
|
|
205621
205560
|
} from "@secondlayer/shared/schemas/webhooks";
|
|
205622
|
-
var RUNTIMES = ["inngest", "trigger", "cloudflare", "node"];
|
|
205623
|
-
var FORMAT_BY_RUNTIME = {
|
|
205624
|
-
inngest: "inngest",
|
|
205625
|
-
trigger: "trigger",
|
|
205626
|
-
cloudflare: "cloudflare",
|
|
205627
|
-
node: "standard-webhooks"
|
|
205628
|
-
};
|
|
205629
|
-
function templatesRoot() {
|
|
205630
|
-
const here = dirname6(fileURLToPath4(import.meta.url));
|
|
205631
|
-
const candidateDist = resolve7(here, "..", "templates", "webhooks");
|
|
205632
|
-
if (existsSync5(candidateDist))
|
|
205633
|
-
return candidateDist;
|
|
205634
|
-
return resolve7(here, "..", "..", "templates", "webhooks");
|
|
205635
|
-
}
|
|
205636
|
-
function copyTemplate(src, dest, vars) {
|
|
205637
|
-
if (!existsSync5(src)) {
|
|
205638
|
-
throw new Error(`template dir missing: ${src}`);
|
|
205639
|
-
}
|
|
205640
|
-
mkdirSync3(dest, { recursive: true });
|
|
205641
|
-
const entries = readdirSync(src);
|
|
205642
|
-
for (const entry of entries) {
|
|
205643
|
-
const from = join9(src, entry);
|
|
205644
|
-
const to = join9(dest, entry);
|
|
205645
|
-
const st = statSync2(from);
|
|
205646
|
-
if (st.isDirectory()) {
|
|
205647
|
-
copyTemplate(from, to, vars);
|
|
205648
|
-
} else {
|
|
205649
|
-
const raw = readFileSync5(from, "utf8");
|
|
205650
|
-
const rendered = raw.replace(/\{\{([A-Z_]+)\}\}/g, (match, key) => vars[key] ?? match);
|
|
205651
|
-
writeFileSync4(to, rendered);
|
|
205652
|
-
}
|
|
205653
|
-
}
|
|
205654
|
-
}
|
|
205655
205561
|
function parseTriggersInput(opts) {
|
|
205656
205562
|
const raw = [];
|
|
205657
205563
|
if (opts.triggersFile) {
|
|
@@ -205684,53 +205590,6 @@ function parseTriggersInput(opts) {
|
|
|
205684
205590
|
return result.data;
|
|
205685
205591
|
});
|
|
205686
205592
|
}
|
|
205687
|
-
function resolveRuntime(opts) {
|
|
205688
|
-
if (opts.runtime)
|
|
205689
|
-
return opts.runtime;
|
|
205690
|
-
const scripted = opts.subgraph !== undefined || opts.table !== undefined || opts.url !== undefined || opts.scaffold === false;
|
|
205691
|
-
return scripted ? "node" : undefined;
|
|
205692
|
-
}
|
|
205693
|
-
function requireTtyFor(flag) {
|
|
205694
|
-
if (process.stdin.isTTY)
|
|
205695
|
-
return;
|
|
205696
|
-
error(`${flag} is required when stdin is not a TTY (no prompt can run). Pass it, or run from a terminal.`);
|
|
205697
|
-
process.exit(1);
|
|
205698
|
-
}
|
|
205699
|
-
async function promptFor(_name, opts) {
|
|
205700
|
-
let runtime = resolveRuntime(opts);
|
|
205701
|
-
if (runtime === undefined) {
|
|
205702
|
-
requireTtyFor("--runtime");
|
|
205703
|
-
runtime = await select3({
|
|
205704
|
-
message: "Runtime?",
|
|
205705
|
-
choices: RUNTIMES.map((r) => ({ name: r, value: r }))
|
|
205706
|
-
});
|
|
205707
|
-
}
|
|
205708
|
-
let subgraph = opts.subgraph;
|
|
205709
|
-
if (subgraph === undefined) {
|
|
205710
|
-
requireTtyFor("--subgraph");
|
|
205711
|
-
subgraph = await input({
|
|
205712
|
-
message: "Subgraph name (must already be deployed):",
|
|
205713
|
-
validate: (v) => v.trim().length > 0 ? true : "required"
|
|
205714
|
-
});
|
|
205715
|
-
}
|
|
205716
|
-
let table = opts.table;
|
|
205717
|
-
if (table === undefined) {
|
|
205718
|
-
requireTtyFor("--table");
|
|
205719
|
-
table = await input({
|
|
205720
|
-
message: "Table to subscribe to:",
|
|
205721
|
-
validate: (v) => v.trim().length > 0 ? true : "required"
|
|
205722
|
-
});
|
|
205723
|
-
}
|
|
205724
|
-
let url = opts.url;
|
|
205725
|
-
if (url === undefined) {
|
|
205726
|
-
requireTtyFor("--url");
|
|
205727
|
-
url = await input({
|
|
205728
|
-
message: runtime === "inngest" ? "Inngest event endpoint URL (e.g. https://inn.gs/e/<KEY> or http://localhost:8288/e/<KEY>):" : runtime === "trigger" ? "Trigger.dev task URL (e.g. https://api.trigger.dev/api/v1/tasks/<TASK_ID>/trigger):" : runtime === "cloudflare" ? "Cloudflare Workflows instances URL:" : "Your HTTP receiver URL (e.g. https://yourapp.example/webhook):",
|
|
205729
|
-
validate: (v) => v.startsWith("http://") || v.startsWith("https://") ? true : "must be http(s) URL"
|
|
205730
|
-
});
|
|
205731
|
-
}
|
|
205732
|
-
return { runtime, subgraph, table, url };
|
|
205733
|
-
}
|
|
205734
205593
|
function buildWebhookAuthConfig(authToken) {
|
|
205735
205594
|
if (authToken === undefined)
|
|
205736
205595
|
return;
|
|
@@ -205740,139 +205599,9 @@ function buildWebhookAuthConfig(authToken) {
|
|
|
205740
205599
|
}
|
|
205741
205600
|
return { authType: "bearer", token };
|
|
205742
205601
|
}
|
|
205743
|
-
function writeSigningSecretEnv(targetDir, signingSecret) {
|
|
205744
|
-
const envTarget = join9(targetDir, ".env");
|
|
205745
|
-
const envExample = join9(targetDir, ".env.example");
|
|
205746
|
-
if (existsSync5(envExample) && !existsSync5(envTarget)) {
|
|
205747
|
-
copyFileSync(envExample, envTarget);
|
|
205748
|
-
chmodSync(envTarget, 384);
|
|
205749
|
-
}
|
|
205750
|
-
if (!existsSync5(envTarget)) {
|
|
205751
|
-
writeFileSync4(envTarget, `SIGNING_SECRET=${signingSecret}
|
|
205752
|
-
`, {
|
|
205753
|
-
mode: 384
|
|
205754
|
-
});
|
|
205755
|
-
} else {
|
|
205756
|
-
const cur = readFileSync5(envTarget, "utf8");
|
|
205757
|
-
writeFileSync4(envTarget, cur.match(/^SIGNING_SECRET=/m) ? cur.replace(/^SIGNING_SECRET=.*/m, `SIGNING_SECRET=${signingSecret}`) : `${cur}
|
|
205758
|
-
SIGNING_SECRET=${signingSecret}
|
|
205759
|
-
`, { mode: 384 });
|
|
205760
|
-
chmodSync(envTarget, 384);
|
|
205761
|
-
}
|
|
205762
|
-
return envTarget;
|
|
205763
|
-
}
|
|
205764
|
-
async function createWebhook(name, opts) {
|
|
205765
|
-
if (opts.runtime && !RUNTIMES.includes(opts.runtime)) {
|
|
205766
|
-
error(`Unknown --runtime "${opts.runtime}". Valid: ${RUNTIMES.join(", ")}`);
|
|
205767
|
-
process.exit(1);
|
|
205768
|
-
}
|
|
205769
|
-
const { runtime, subgraph, table, url } = await promptFor(name, opts);
|
|
205770
|
-
let filter;
|
|
205771
|
-
let authConfig;
|
|
205772
|
-
try {
|
|
205773
|
-
filter = parseWebhookFilter(opts.filter);
|
|
205774
|
-
authConfig = buildWebhookAuthConfig(opts.authToken);
|
|
205775
|
-
} catch (err) {
|
|
205776
|
-
error(err instanceof Error ? err.message : String(err));
|
|
205777
|
-
process.exit(1);
|
|
205778
|
-
}
|
|
205779
|
-
let sl = null;
|
|
205780
|
-
if (!opts.skipApi) {
|
|
205781
|
-
try {
|
|
205782
|
-
sl = await getWebhookClient();
|
|
205783
|
-
await validateWebhookTargetFromApi(sl, {
|
|
205784
|
-
subgraphName: subgraph,
|
|
205785
|
-
tableName: table,
|
|
205786
|
-
filter
|
|
205787
|
-
});
|
|
205788
|
-
} catch (err) {
|
|
205789
|
-
error(err instanceof Error ? err.message : String(err));
|
|
205790
|
-
process.exit(1);
|
|
205791
|
-
}
|
|
205792
|
-
}
|
|
205793
|
-
const eventName = `${subgraph}.${table}.created`;
|
|
205794
|
-
const targetDir = resolve7(process.cwd(), name);
|
|
205795
|
-
if (opts.scaffold !== false) {
|
|
205796
|
-
if (existsSync5(targetDir)) {
|
|
205797
|
-
error(`Directory already exists: ${relative2(process.cwd(), targetDir)}`);
|
|
205798
|
-
process.exit(1);
|
|
205799
|
-
}
|
|
205800
|
-
const tplRoot = templatesRoot();
|
|
205801
|
-
const tplDir = join9(tplRoot, runtime);
|
|
205802
|
-
info(`Scaffolding ${blue(runtime)} template at ${blue(targetDir)}`);
|
|
205803
|
-
copyTemplate(tplDir, targetDir, {
|
|
205804
|
-
NAME: name,
|
|
205805
|
-
EVENT_NAME: eventName,
|
|
205806
|
-
TASK_ID: `${subgraph}-${table}`
|
|
205807
|
-
});
|
|
205808
|
-
}
|
|
205809
|
-
let signingSecret = null;
|
|
205810
|
-
let provisioningFailed = false;
|
|
205811
|
-
if (!opts.skipApi) {
|
|
205812
|
-
try {
|
|
205813
|
-
if (!sl)
|
|
205814
|
-
sl = await getWebhookClient();
|
|
205815
|
-
const res = await sl.webhooks.create({
|
|
205816
|
-
name,
|
|
205817
|
-
subgraphName: subgraph,
|
|
205818
|
-
tableName: table,
|
|
205819
|
-
url,
|
|
205820
|
-
format: FORMAT_BY_RUNTIME[runtime],
|
|
205821
|
-
runtime,
|
|
205822
|
-
...filter ? { filter } : {},
|
|
205823
|
-
...authConfig ? { authConfig } : {}
|
|
205824
|
-
});
|
|
205825
|
-
signingSecret = res.signingSecret;
|
|
205826
|
-
success(`Webhook provisioned: ${blue(res.webhook.id)}`);
|
|
205827
|
-
} catch (err) {
|
|
205828
|
-
provisioningFailed = true;
|
|
205829
|
-
warn(`Webhook provisioning failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
205830
|
-
info("Template copied, but the webhook was not created. Fix the API error, remove the template directory, and rerun.");
|
|
205831
|
-
}
|
|
205832
|
-
}
|
|
205833
|
-
let webhookStatus;
|
|
205834
|
-
if (sl) {
|
|
205835
|
-
try {
|
|
205836
|
-
const list = await sl.webhooks.list();
|
|
205837
|
-
const rows = list.data ?? [];
|
|
205838
|
-
const created = rows.find((s) => s.name === name);
|
|
205839
|
-
webhookStatus = created?.status;
|
|
205840
|
-
} catch {}
|
|
205841
|
-
}
|
|
205842
|
-
if (signingSecret && opts.scaffold !== false) {
|
|
205843
|
-
const envTarget = writeSigningSecretEnv(targetDir, signingSecret);
|
|
205844
|
-
success(`Signing secret written to ${relative2(process.cwd(), envTarget)}`);
|
|
205845
|
-
} else if (signingSecret && opts.scaffold === false) {
|
|
205846
|
-
info(`Signing secret (store securely): ${signingSecret}`);
|
|
205847
|
-
}
|
|
205848
|
-
console.log();
|
|
205849
|
-
if (provisioningFailed) {
|
|
205850
|
-
error("Webhook was not created.");
|
|
205851
|
-
process.exit(1);
|
|
205852
|
-
}
|
|
205853
|
-
const pausedLine = webhookStatus === "paused" ? `Webhook is paused. Resume:
|
|
205854
|
-
secondlayer webhooks resume ${name}
|
|
205855
|
-
` : "";
|
|
205856
|
-
const runHint = opts.scaffold === false ? `View deliveries:
|
|
205857
|
-
secondlayer webhooks get ${name}` : `cd ${name}
|
|
205858
|
-
bun install
|
|
205859
|
-
bun run dev`;
|
|
205860
|
-
success(`Done. Next:
|
|
205861
|
-
${pausedLine}${runHint}`);
|
|
205862
|
-
}
|
|
205863
205602
|
async function createChainWebhook(name, opts) {
|
|
205864
|
-
for (const [flag, value] of [
|
|
205865
|
-
["--subgraph", opts.subgraph],
|
|
205866
|
-
["--table", opts.table],
|
|
205867
|
-
["--runtime", opts.runtime]
|
|
205868
|
-
]) {
|
|
205869
|
-
if (value !== undefined) {
|
|
205870
|
-
error(`${flag} is not valid with --trigger/--triggers-file (chain mode)`);
|
|
205871
|
-
process.exit(1);
|
|
205872
|
-
}
|
|
205873
|
-
}
|
|
205874
205603
|
if (!opts.url) {
|
|
205875
|
-
error("--url is required
|
|
205604
|
+
error("--url is required");
|
|
205876
205605
|
process.exit(1);
|
|
205877
205606
|
}
|
|
205878
205607
|
let triggers;
|
|
@@ -205897,7 +205626,7 @@ async function createChainWebhook(name, opts) {
|
|
|
205897
205626
|
});
|
|
205898
205627
|
webhookId = res.webhook.id;
|
|
205899
205628
|
signingSecret = res.signingSecret;
|
|
205900
|
-
success(`
|
|
205629
|
+
success(`Webhook provisioned: ${blue(webhookId)}`);
|
|
205901
205630
|
} catch (err) {
|
|
205902
205631
|
error(`Webhook provisioning failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
205903
205632
|
process.exit(1);
|
|
@@ -205912,24 +205641,11 @@ async function getWebhookClient() {
|
|
|
205912
205641
|
const { apiUrl, ephemeralKey } = await resolveHostedAuth();
|
|
205913
205642
|
return new SecondLayer4({ baseUrl: apiUrl, apiKey: ephemeralKey });
|
|
205914
205643
|
}
|
|
205915
|
-
function addWebhookScaffold(parent, commandSpec, opts) {
|
|
205916
|
-
parent.command(commandSpec).description(opts.description).option("-r, --runtime <runtime>", "inngest | trigger | cloudflare | node (default: node once -s/-t/-u or --no-scaffold is given; prompts only in a terminal with no flags)").option("-s, --subgraph <name>", "Subgraph to subscribe to").option("-t, --table <name>", "Table to subscribe to").option("-u, --url <url>", "Webhook URL").option("--auth-token <token>", "Bearer token for receiver API auth; a flag lands in shell history and ps, so set it in the receiver's env and pass it here only for a throwaway token").option("--filter <kv...>", "Filter as key=value (supports .eq/.neq/.gt/.gte/.lt/.lte suffixes)").option("--trigger <json...>", `Chain-webhook trigger as JSON (repeatable), e.g. '{"type":"sbtc_deposit"}'`).option("--triggers-file <path>", "Chain-webhook triggers as a JSON array file").option("--format <format>", "Chain mode delivery format (default standard-webhooks)").option("--skip-api", "Copy template only, don't call the API").option("--no-scaffold", "Skip the local runtime template directory (webhook-only setups)").addHelpText("after", `
|
|
205917
|
-
Examples:
|
|
205918
|
-
$ ${opts.examplePrefix} my-sub -s my-graph -t transfers -u https://example.com/webhook
|
|
205919
|
-
$ ${opts.examplePrefix} my-sub -s my-graph -t balances -r inngest --filter amount.gte=1000
|
|
205920
|
-
$ ${opts.examplePrefix} my-hook -u https://example.com/webhook --trigger '{"type":"sbtc_deposit"}'`).action(async (name, options) => {
|
|
205921
|
-
if (options.trigger?.length || options.triggersFile) {
|
|
205922
|
-
await createChainWebhook(name, options);
|
|
205923
|
-
return;
|
|
205924
|
-
}
|
|
205925
|
-
await createWebhook(name, options);
|
|
205926
|
-
});
|
|
205927
|
-
}
|
|
205928
205644
|
function addWebhooksCreateCommand(parent) {
|
|
205929
|
-
|
|
205930
|
-
|
|
205931
|
-
|
|
205932
|
-
|
|
205645
|
+
parent.command("create <name>").description("Create a webhook on raw chain events").option("-u, --url <url>", "Webhook URL").option("--auth-token <token>", "Bearer token for receiver API auth; a flag lands in shell history and ps, so set it in the receiver's env and pass it here only for a throwaway token").option("--trigger <json...>", `Chain trigger as JSON (repeatable), e.g. '{"type":"sbtc_deposit"}'`).option("--triggers-file <path>", "Chain triggers as a JSON array file").option("--format <format>", "Delivery format (default standard-webhooks)").addHelpText("after", `
|
|
205646
|
+
Examples:
|
|
205647
|
+
$ secondlayer webhooks create my-hook -u https://example.com/webhook --trigger '{"type":"sbtc_deposit"}'
|
|
205648
|
+
$ secondlayer webhooks create my-hook -u https://example.com/webhook --triggers-file triggers.json`).action(createChainWebhook);
|
|
205933
205649
|
}
|
|
205934
205650
|
|
|
205935
205651
|
// src/commands/webhooks.ts
|
|
@@ -205999,17 +205715,6 @@ function buildWebhookUpdatePatch(options) {
|
|
|
205999
205715
|
if (options.format) {
|
|
206000
205716
|
patch.format = options.format;
|
|
206001
205717
|
}
|
|
206002
|
-
if (options.runtime !== undefined) {
|
|
206003
|
-
patch.runtime = options.runtime === "none" || options.runtime === "null" ? null : options.runtime;
|
|
206004
|
-
}
|
|
206005
|
-
if (options.clearFilter)
|
|
206006
|
-
patch.filter = {};
|
|
206007
|
-
if (options.filter) {
|
|
206008
|
-
if (options.clearFilter) {
|
|
206009
|
-
throw new Error("Use either --filter or --clear-filter, not both");
|
|
206010
|
-
}
|
|
206011
|
-
patch.filter = parseWebhookFilter(options.filter) ?? {};
|
|
206012
|
-
}
|
|
206013
205718
|
const maxRetries = parseIntegerOption(options.maxRetries, "--max-retries", 0);
|
|
206014
205719
|
if (maxRetries !== undefined)
|
|
206015
205720
|
patch.maxRetries = maxRetries;
|
|
@@ -206025,11 +205730,8 @@ function buildWebhookUpdatePatch(options) {
|
|
|
206025
205730
|
return patch;
|
|
206026
205731
|
}
|
|
206027
205732
|
function formatWebhookTarget(sub) {
|
|
206028
|
-
|
|
206029
|
-
|
|
206030
|
-
return types && types.length > 0 ? `chain.${types.join(",")}` : "chain";
|
|
206031
|
-
}
|
|
206032
|
-
return `${sub.subgraphName}.${sub.tableName}`;
|
|
205733
|
+
const types = "triggers" in sub ? sub.triggers.map((t) => t.type) : [];
|
|
205734
|
+
return types.length > 0 ? `chain.${types.join(",")}` : "chain";
|
|
206033
205735
|
}
|
|
206034
205736
|
function formatAuthLine(auth) {
|
|
206035
205737
|
const scheme = auth.type === "bearer" ? auth.hasSecret ? "bearer token (set, hidden)" : "bearer (no token set)" : auth.type === "basic" ? auth.hasSecret ? "basic auth (set, hidden)" : "basic (no credentials set)" : "none";
|
|
@@ -206042,7 +205744,6 @@ function printWebhookDetail(sub) {
|
|
|
206042
205744
|
["Status", sub.status],
|
|
206043
205745
|
["Target", formatWebhookTarget(sub)],
|
|
206044
205746
|
["Format", sub.format],
|
|
206045
|
-
["Runtime", sub.runtime ?? "none"],
|
|
206046
205747
|
["URL", sub.url],
|
|
206047
205748
|
["Auth", formatAuthLine(sub.auth)],
|
|
206048
205749
|
["Last Delivery", sub.lastDeliveryAt ?? "none"],
|
|
@@ -206058,8 +205759,8 @@ function printWebhookDetail(sub) {
|
|
|
206058
205759
|
["Updated", sub.updatedAt]
|
|
206059
205760
|
]));
|
|
206060
205761
|
console.log(dim(`
|
|
206061
|
-
|
|
206062
|
-
console.log(JSON.stringify(sub.
|
|
205762
|
+
Triggers:`));
|
|
205763
|
+
console.log(JSON.stringify(sub.triggers, null, 2));
|
|
206063
205764
|
}
|
|
206064
205765
|
var DOCTOR_ISSUE_TITLES = {
|
|
206065
205766
|
warning: "This webhook can't fire yet",
|
|
@@ -206067,8 +205768,6 @@ var DOCTOR_ISSUE_TITLES = {
|
|
|
206067
205768
|
last_error: "The last delivery failed",
|
|
206068
205769
|
circuit: "Your receiver is failing",
|
|
206069
205770
|
dead_letters: "Some events exhausted every retry",
|
|
206070
|
-
subgraph_gaps: "The linked subgraph has gaps",
|
|
206071
|
-
subgraph_catching_up: "The linked subgraph is still catching up",
|
|
206072
205771
|
no_deliveries: "No deliveries yet",
|
|
206073
205772
|
receiver_rate_limited: "Your receiver is rate-limiting us",
|
|
206074
205773
|
receiver_down: "Your receiver looks down",
|
|
@@ -206100,7 +205799,6 @@ function formatDoctorReport(report) {
|
|
|
206100
205799
|
["Status", sub.status],
|
|
206101
205800
|
["Target", formatWebhookTarget(sub)],
|
|
206102
205801
|
["Format", sub.format],
|
|
206103
|
-
["Runtime", sub.runtime ?? "none"],
|
|
206104
205802
|
["URL", sub.url],
|
|
206105
205803
|
[
|
|
206106
205804
|
"Circuit",
|
|
@@ -206126,24 +205824,6 @@ Delivery summary:`),
|
|
|
206126
205824
|
].join(`
|
|
206127
205825
|
`)
|
|
206128
205826
|
];
|
|
206129
|
-
if (report.subgraph) {
|
|
206130
|
-
sections.push([
|
|
206131
|
-
dim(`
|
|
206132
|
-
Linked subgraph:`),
|
|
206133
|
-
formatKeyValue([
|
|
206134
|
-
["Name", report.subgraph.name],
|
|
206135
|
-
["Status", report.subgraph.status],
|
|
206136
|
-
["Sync", report.subgraph.syncStatus],
|
|
206137
|
-
[
|
|
206138
|
-
"Blocks",
|
|
206139
|
-
`${report.subgraph.lastProcessedBlock} / ${report.subgraph.chainTip}`
|
|
206140
|
-
],
|
|
206141
|
-
["Integrity", report.subgraph.integrity],
|
|
206142
|
-
["Gaps", String(report.subgraph.gapCount)]
|
|
206143
|
-
])
|
|
206144
|
-
].join(`
|
|
206145
|
-
`));
|
|
206146
|
-
}
|
|
206147
205827
|
if (report.primary)
|
|
206148
205828
|
sections.push(formatPrimaryInsight(report.primary));
|
|
206149
205829
|
const primaryIndex = report.primary ? report.issues.indexOf(report.primary) : -1;
|
|
@@ -206159,35 +205839,6 @@ Next steps:`), ...rest.map((hint) => ` - ${hint}`)].join(`
|
|
|
206159
205839
|
function printDoctorReport(report) {
|
|
206160
205840
|
console.log(formatDoctorReport(report));
|
|
206161
205841
|
}
|
|
206162
|
-
function syntheticValue(type) {
|
|
206163
|
-
switch (type) {
|
|
206164
|
-
case "uint":
|
|
206165
|
-
case "int":
|
|
206166
|
-
return "1000";
|
|
206167
|
-
case "boolean":
|
|
206168
|
-
return true;
|
|
206169
|
-
case "timestamp":
|
|
206170
|
-
return new Date(0).toISOString();
|
|
206171
|
-
case "jsonb":
|
|
206172
|
-
return {};
|
|
206173
|
-
case "principal":
|
|
206174
|
-
return "SP000000000000000000002Q6VF78";
|
|
206175
|
-
default:
|
|
206176
|
-
return "example";
|
|
206177
|
-
}
|
|
206178
|
-
}
|
|
206179
|
-
function buildSyntheticRow(subgraph, tableName) {
|
|
206180
|
-
const table = subgraph?.tables[tableName];
|
|
206181
|
-
if (!table)
|
|
206182
|
-
return { id: "example", value: "example" };
|
|
206183
|
-
const row = {};
|
|
206184
|
-
for (const [column, def] of Object.entries(table.columns)) {
|
|
206185
|
-
if (column.startsWith("_"))
|
|
206186
|
-
continue;
|
|
206187
|
-
row[column] = syntheticValue(def.type);
|
|
206188
|
-
}
|
|
206189
|
-
return Object.keys(row).length > 0 ? row : { id: "example", value: "example" };
|
|
206190
|
-
}
|
|
206191
205842
|
function resolveSigningSecret(options, env = process.env) {
|
|
206192
205843
|
const secret = options.signingSecret ?? env.SIGNING_SECRET;
|
|
206193
205844
|
if (!secret) {
|
|
@@ -206201,17 +205852,16 @@ function shellQuote(value) {
|
|
|
206201
205852
|
function buildWebhookTestFixture(input) {
|
|
206202
205853
|
const nowSeconds = input.nowSeconds ?? Math.floor(Date.now() / 1000);
|
|
206203
205854
|
const sub = input.webhook;
|
|
206204
|
-
const
|
|
206205
|
-
const
|
|
206206
|
-
const data = isChain ? {
|
|
205855
|
+
const type = `chain.${sub.triggers[0]?.type ?? "event"}.apply`;
|
|
205856
|
+
const data = {
|
|
206207
205857
|
action: "apply",
|
|
206208
205858
|
canonical: true,
|
|
206209
205859
|
block_hash: "0xexampleblock",
|
|
206210
205860
|
block_height: 0,
|
|
206211
205861
|
tx_id: input.row.tx_id ?? "0xexampletransaction",
|
|
206212
|
-
trigger: sub.triggers
|
|
205862
|
+
trigger: sub.triggers[0]?.type ?? "event",
|
|
206213
205863
|
event: input.row
|
|
206214
|
-
}
|
|
205864
|
+
};
|
|
206215
205865
|
const body = JSON.stringify({
|
|
206216
205866
|
type,
|
|
206217
205867
|
timestamp: new Date(nowSeconds * 1000).toISOString(),
|
|
@@ -206234,25 +205884,12 @@ function buildWebhookTestFixture(input) {
|
|
|
206234
205884
|
`);
|
|
206235
205885
|
return { body, headers, curl };
|
|
206236
205886
|
}
|
|
206237
|
-
|
|
206238
|
-
|
|
206239
|
-
|
|
206240
|
-
|
|
206241
|
-
|
|
206242
|
-
|
|
206243
|
-
};
|
|
206244
|
-
}
|
|
206245
|
-
try {
|
|
206246
|
-
const { rows } = await client.subgraphs.rows(sub.subgraphName, sub.tableName, {
|
|
206247
|
-
sort: "_block_height",
|
|
206248
|
-
order: "desc",
|
|
206249
|
-
limit: 1
|
|
206250
|
-
});
|
|
206251
|
-
const row = rows[0];
|
|
206252
|
-
if (row && typeof row === "object")
|
|
206253
|
-
return row;
|
|
206254
|
-
} catch {}
|
|
206255
|
-
return buildSyntheticRow(subgraph, sub.tableName);
|
|
205887
|
+
function representativeRow(sub) {
|
|
205888
|
+
return {
|
|
205889
|
+
tx_id: "0xexampletransaction",
|
|
205890
|
+
contract_id: "SP000000000000000000002Q6VF78.example",
|
|
205891
|
+
...sub.triggers[0]?.type === "contract_call" ? { function_name: "example-fn" } : {}
|
|
205892
|
+
};
|
|
206256
205893
|
}
|
|
206257
205894
|
function printDeliveries(rows) {
|
|
206258
205895
|
if (rows.length === 0) {
|
|
@@ -206329,22 +205966,14 @@ ${data.length} webhook(s) total`));
|
|
|
206329
205966
|
handleApiError(err, "get webhook");
|
|
206330
205967
|
}
|
|
206331
205968
|
});
|
|
206332
|
-
parent.command("update <idOrName>").description("Update webhook config").option("--name <name>", "Rename webhook").option("--url <url>", "Webhook URL").option("--auth-token <token>", "Set bearer token auth config; a flag lands in shell history and ps, so pass it here only for a throwaway token").option("--format <format>", "standard-webhooks | inngest | trigger | cloudflare | cloudevents | raw").option("--
|
|
205969
|
+
parent.command("update <idOrName>").description("Update webhook config").option("--name <name>", "Rename webhook").option("--url <url>", "Webhook URL").option("--auth-token <token>", "Set bearer token auth config; a flag lands in shell history and ps, so pass it here only for a throwaway token").option("--format <format>", "standard-webhooks | inngest | trigger | cloudflare | cloudevents | raw").option("--max-retries <n>", "Maximum delivery retries").option("--timeout-ms <n>", "Delivery timeout in milliseconds").option("--concurrency <n>", "Per-webhook delivery concurrency").option("--json", "Output as JSON").addHelpText("after", `
|
|
206333
205970
|
Examples:
|
|
206334
205971
|
$ secondlayer webhooks update my-sub --url https://example.com/hook
|
|
206335
|
-
$ secondlayer webhooks update my-sub --
|
|
206336
|
-
$ secondlayer webhooks update my-sub --clear-filter`).action(async (idOrName, options) => {
|
|
205972
|
+
$ secondlayer webhooks update my-sub --max-retries 5 --timeout-ms 20000`).action(async (idOrName, options) => {
|
|
206337
205973
|
try {
|
|
206338
205974
|
const client = await getWebhookClient();
|
|
206339
205975
|
const patch = buildWebhookUpdatePatch(options);
|
|
206340
|
-
const { id
|
|
206341
|
-
if (patch.filter !== undefined && detail.subgraphName && detail.tableName) {
|
|
206342
|
-
await validateWebhookTargetFromApi(client, {
|
|
206343
|
-
subgraphName: detail.subgraphName,
|
|
206344
|
-
tableName: detail.tableName,
|
|
206345
|
-
filter: patch.filter
|
|
206346
|
-
});
|
|
206347
|
-
}
|
|
205976
|
+
const { id } = await resolveWebhookRef(client, idOrName);
|
|
206348
205977
|
const updated = await client.webhooks.update(id, patch);
|
|
206349
205978
|
if (options.json)
|
|
206350
205979
|
printJson(updated);
|
|
@@ -206495,16 +206124,14 @@ Examples:
|
|
|
206495
206124
|
try {
|
|
206496
206125
|
const client = await getWebhookClient();
|
|
206497
206126
|
const { id, detail } = await resolveWebhookRef(client, idOrName);
|
|
206498
|
-
const [deliveries, dead
|
|
206127
|
+
const [deliveries, dead] = await Promise.allSettled([
|
|
206499
206128
|
client.webhooks.deliveries(id),
|
|
206500
|
-
client.webhooks.dead(id)
|
|
206501
|
-
detail.subgraphName ? client.subgraphs.status(detail.subgraphName) : Promise.resolve(null)
|
|
206129
|
+
client.webhooks.dead(id)
|
|
206502
206130
|
]);
|
|
206503
206131
|
const report = buildDoctorReport({
|
|
206504
206132
|
webhook: detail,
|
|
206505
206133
|
deliveries: deliveries.status === "fulfilled" ? deliveries.value.data : [],
|
|
206506
|
-
dead: dead.status === "fulfilled" ? dead.value.data : []
|
|
206507
|
-
subgraph: subgraph.status === "fulfilled" ? subgraph.value : null
|
|
206134
|
+
dead: dead.status === "fulfilled" ? dead.value.data : []
|
|
206508
206135
|
});
|
|
206509
206136
|
if (options.json)
|
|
206510
206137
|
printJson(report);
|
|
@@ -206519,8 +206146,7 @@ Examples:
|
|
|
206519
206146
|
const signingSecret = resolveSigningSecret(options);
|
|
206520
206147
|
const client = await getWebhookClient();
|
|
206521
206148
|
const { detail } = await resolveWebhookRef(client, idOrName);
|
|
206522
|
-
const
|
|
206523
|
-
const row = await representativeRow(client, detail, subgraph);
|
|
206149
|
+
const row = representativeRow(detail);
|
|
206524
206150
|
const fixture = buildWebhookTestFixture({
|
|
206525
206151
|
webhook: detail,
|
|
206526
206152
|
row,
|
|
@@ -206580,8 +206206,8 @@ function registerWebhooksCommand(program) {
|
|
|
206580
206206
|
}
|
|
206581
206207
|
// src/commands/devnet.ts
|
|
206582
206208
|
import { spawnSync as spawnSync3 } from "node:child_process";
|
|
206583
|
-
import { existsSync as
|
|
206584
|
-
import { dirname as
|
|
206209
|
+
import { existsSync as existsSync6, mkdirSync as mkdirSync3, writeFileSync as writeFileSync5 } from "node:fs";
|
|
206210
|
+
import { dirname as dirname7, join as join11 } from "node:path";
|
|
206585
206211
|
|
|
206586
206212
|
// src/lib/devnet-compose.ts
|
|
206587
206213
|
var DEFAULT_IMAGE_OWNER = "ryanwaits";
|
|
@@ -206735,23 +206361,23 @@ volumes:
|
|
|
206735
206361
|
}
|
|
206736
206362
|
|
|
206737
206363
|
// src/lib/devnet-config.ts
|
|
206738
|
-
import { existsSync as
|
|
206739
|
-
import { dirname as
|
|
206364
|
+
import { existsSync as existsSync5, readFileSync as readFileSync6, writeFileSync as writeFileSync4 } from "node:fs";
|
|
206365
|
+
import { dirname as dirname6, join as join9, parse as parsePath } from "node:path";
|
|
206740
206366
|
import { parse as parseToml } from "smol-toml";
|
|
206741
206367
|
var DEFAULT_INDEXER_OBSERVER = "host.docker.internal:3700";
|
|
206742
206368
|
function findClarinetProject(startDir) {
|
|
206743
206369
|
let dir = startDir;
|
|
206744
206370
|
const { root } = parsePath(dir);
|
|
206745
206371
|
while (true) {
|
|
206746
|
-
if (
|
|
206372
|
+
if (existsSync5(join9(dir, "Clarinet.toml")))
|
|
206747
206373
|
return dir;
|
|
206748
206374
|
if (dir === root)
|
|
206749
206375
|
return null;
|
|
206750
|
-
dir =
|
|
206376
|
+
dir = dirname6(dir);
|
|
206751
206377
|
}
|
|
206752
206378
|
}
|
|
206753
206379
|
function ensureEventObserver(devnetTomlPath, endpoint = DEFAULT_INDEXER_OBSERVER) {
|
|
206754
|
-
const exists =
|
|
206380
|
+
const exists = existsSync5(devnetTomlPath);
|
|
206755
206381
|
const original = exists ? readFileSync6(devnetTomlPath, "utf8") : "";
|
|
206756
206382
|
if (original.trim()) {
|
|
206757
206383
|
const parsed = parseToml(original);
|
|
@@ -206766,7 +206392,7 @@ function ensureEventObserver(devnetTomlPath, endpoint = DEFAULT_INDEXER_OBSERVER
|
|
|
206766
206392
|
const at = (arrayStart.index ?? 0) + arrayStart[0].length;
|
|
206767
206393
|
const next = original.slice(at).trimStart().startsWith("]") ? entry : `${entry}, `;
|
|
206768
206394
|
const patched = original.slice(0, at) + next + original.slice(at);
|
|
206769
|
-
|
|
206395
|
+
writeFileSync4(devnetTomlPath, patched);
|
|
206770
206396
|
return { status: "added" };
|
|
206771
206397
|
}
|
|
206772
206398
|
const header = original.match(/^\[devnet\]\s*$/m);
|
|
@@ -206774,7 +206400,7 @@ function ensureEventObserver(devnetTomlPath, endpoint = DEFAULT_INDEXER_OBSERVER
|
|
|
206774
206400
|
const at = (header.index ?? 0) + header[0].length;
|
|
206775
206401
|
const patched = `${original.slice(0, at)}
|
|
206776
206402
|
stacks_node_events_observers = [${entry}]${original.slice(at)}`;
|
|
206777
|
-
|
|
206403
|
+
writeFileSync4(devnetTomlPath, patched);
|
|
206778
206404
|
return { status: "added" };
|
|
206779
206405
|
}
|
|
206780
206406
|
const prefix = original && !original.endsWith(`
|
|
@@ -206784,7 +206410,7 @@ stacks_node_events_observers = [${entry}]${original.slice(at)}`;
|
|
|
206784
206410
|
` : ""}[devnet]
|
|
206785
206411
|
stacks_node_events_observers = [${entry}]
|
|
206786
206412
|
`;
|
|
206787
|
-
|
|
206413
|
+
writeFileSync4(devnetTomlPath, block);
|
|
206788
206414
|
return { status: exists ? "added" : "created" };
|
|
206789
206415
|
}
|
|
206790
206416
|
|
|
@@ -206795,7 +206421,7 @@ init_resolve_auth();
|
|
|
206795
206421
|
// src/commands/faucet.ts
|
|
206796
206422
|
import { randomBytes as randomBytes2 } from "node:crypto";
|
|
206797
206423
|
import { readFile as readFile5 } from "node:fs/promises";
|
|
206798
|
-
import { join as
|
|
206424
|
+
import { join as join10 } from "node:path";
|
|
206799
206425
|
import { http as http3, createWalletClient } from "@secondlayer/stacks";
|
|
206800
206426
|
import { mnemonicToAccount } from "@secondlayer/stacks/accounts";
|
|
206801
206427
|
import { devnet } from "@secondlayer/stacks/chains";
|
|
@@ -206805,7 +206431,7 @@ init_output();
|
|
|
206805
206431
|
var DUST_LIMIT_SATS = 546n;
|
|
206806
206432
|
var DEFAULT_AMOUNT_SATS = 1000000n;
|
|
206807
206433
|
async function readDeployerMnemonic(projectDir) {
|
|
206808
|
-
const path =
|
|
206434
|
+
const path = join10(projectDir, "settings", "Devnet.toml");
|
|
206809
206435
|
let raw;
|
|
206810
206436
|
try {
|
|
206811
206437
|
raw = await readFile5(path, "utf8");
|
|
@@ -206922,7 +206548,7 @@ is what makes minting possible without Bitcoin.`).action(async (opts) => {
|
|
|
206922
206548
|
}
|
|
206923
206549
|
|
|
206924
206550
|
// src/commands/devnet.ts
|
|
206925
|
-
var COMPOSE_REL =
|
|
206551
|
+
var COMPOSE_REL = join11(".secondlayer", "docker-compose.yml");
|
|
206926
206552
|
function ensureDocker() {
|
|
206927
206553
|
const probe = spawnSync3("docker", ["info"], { stdio: "ignore" });
|
|
206928
206554
|
if (probe.error || probe.status !== 0) {
|
|
@@ -206967,7 +206593,7 @@ function registerDevnetCommand(program) {
|
|
|
206967
206593
|
}
|
|
206968
206594
|
function resolveProject(explicit) {
|
|
206969
206595
|
if (explicit) {
|
|
206970
|
-
if (!
|
|
206596
|
+
if (!existsSync6(join11(explicit, "Clarinet.toml"))) {
|
|
206971
206597
|
error(`No Clarinet.toml in ${explicit}`);
|
|
206972
206598
|
process.exit(1);
|
|
206973
206599
|
}
|
|
@@ -206983,8 +206609,8 @@ function resolveProject(explicit) {
|
|
|
206983
206609
|
}
|
|
206984
206610
|
async function connect(options) {
|
|
206985
206611
|
const project = resolveProject(options.project);
|
|
206986
|
-
const devnetToml =
|
|
206987
|
-
|
|
206612
|
+
const devnetToml = join11(project, "settings", "Devnet.toml");
|
|
206613
|
+
mkdirSync3(dirname7(devnetToml), { recursive: true });
|
|
206988
206614
|
const result = ensureEventObserver(devnetToml, DEFAULT_INDEXER_OBSERVER);
|
|
206989
206615
|
console.log(`${green("✓")} found Clarinet.toml`);
|
|
206990
206616
|
if (result.status === "present") {
|
|
@@ -206993,9 +206619,9 @@ async function connect(options) {
|
|
|
206993
206619
|
console.log(`${green("✓")} ${result.status === "created" ? "created" : "patched"} settings/Devnet.toml`);
|
|
206994
206620
|
console.log(dim(` + stacks_node_events_observers = ["${DEFAULT_INDEXER_OBSERVER}"]`));
|
|
206995
206621
|
}
|
|
206996
|
-
const composePath =
|
|
206997
|
-
|
|
206998
|
-
|
|
206622
|
+
const composePath = join11(project, COMPOSE_REL);
|
|
206623
|
+
mkdirSync3(dirname7(composePath), { recursive: true });
|
|
206624
|
+
writeFileSync5(composePath, buildDevnetCompose({ owner: options.owner, imageTag: options.imageTag }));
|
|
206999
206625
|
console.log(`${green("✓")} wrote ${COMPOSE_REL}`);
|
|
207000
206626
|
if (!options.up) {
|
|
207001
206627
|
console.log(dim(`
|
|
@@ -207026,8 +206652,8 @@ Stop with: secondlayer devnet down`));
|
|
|
207026
206652
|
}
|
|
207027
206653
|
async function down(options) {
|
|
207028
206654
|
const project = resolveProject(options.project);
|
|
207029
|
-
const composePath =
|
|
207030
|
-
if (!
|
|
206655
|
+
const composePath = join11(project, COMPOSE_REL);
|
|
206656
|
+
if (!existsSync6(composePath)) {
|
|
207031
206657
|
error(`No ${COMPOSE_REL} in ${project} — nothing to stop. Run \`secondlayer devnet connect\` first.`);
|
|
207032
206658
|
process.exit(1);
|
|
207033
206659
|
}
|
|
@@ -207041,8 +206667,8 @@ async function down(options) {
|
|
|
207041
206667
|
}
|
|
207042
206668
|
async function logs(service, options) {
|
|
207043
206669
|
const project = resolveProject(options.project);
|
|
207044
|
-
const composePath =
|
|
207045
|
-
if (!
|
|
206670
|
+
const composePath = join11(project, COMPOSE_REL);
|
|
206671
|
+
if (!existsSync6(composePath)) {
|
|
207046
206672
|
error(`No ${COMPOSE_REL} in ${project} — run \`secondlayer devnet connect\` first.`);
|
|
207047
206673
|
process.exit(1);
|
|
207048
206674
|
}
|
|
@@ -207174,7 +206800,7 @@ init_output();
|
|
|
207174
206800
|
init_resolve_auth();
|
|
207175
206801
|
import { createHash as createHash5 } from "node:crypto";
|
|
207176
206802
|
import { mkdir as mkdir3, open as open2, rename as rename2 } from "node:fs/promises";
|
|
207177
|
-
import { dirname as
|
|
206803
|
+
import { dirname as dirname8, isAbsolute, resolve as resolve7, sep as sep2 } from "node:path";
|
|
207178
206804
|
import { createStreamsClient } from "@secondlayer/sdk";
|
|
207179
206805
|
import { STREAMS_EVENT_TYPES } from "@secondlayer/shared";
|
|
207180
206806
|
var VALID_TYPES = STREAMS_EVENT_TYPES;
|
|
@@ -207197,12 +206823,12 @@ function dumpsClient(dumpsUrl) {
|
|
|
207197
206823
|
});
|
|
207198
206824
|
}
|
|
207199
206825
|
function resolveDumpDest(to, filePath) {
|
|
207200
|
-
const root =
|
|
206826
|
+
const root = resolve7(to);
|
|
207201
206827
|
const segments = filePath.split(/[\\/]/);
|
|
207202
206828
|
if (filePath.length === 0 || isAbsolute(filePath) || filePath.includes("\\") || segments.some((s) => s === "..")) {
|
|
207203
206829
|
throw new Error(`refusing dump path "${filePath}": paths must be relative and stay under ${root}`);
|
|
207204
206830
|
}
|
|
207205
|
-
const dest =
|
|
206831
|
+
const dest = resolve7(root, filePath);
|
|
207206
206832
|
if (!dest.startsWith(root + sep2)) {
|
|
207207
206833
|
throw new Error(`refusing dump path "${filePath}": resolves outside ${root}`);
|
|
207208
206834
|
}
|
|
@@ -207213,7 +206839,7 @@ async function downloadDumpTo(file, dest, fetchImpl, url) {
|
|
|
207213
206839
|
if (!res.ok || !res.body) {
|
|
207214
206840
|
throw new Error(`could not download dump ${file.path} (${res.status})`);
|
|
207215
206841
|
}
|
|
207216
|
-
await mkdir3(
|
|
206842
|
+
await mkdir3(dirname8(dest), { recursive: true });
|
|
207217
206843
|
const part = `${dest}.part`;
|
|
207218
206844
|
const hash = createHash5("sha256");
|
|
207219
206845
|
let bytes = 0;
|
|
@@ -207897,10 +207523,10 @@ init_output();
|
|
|
207897
207523
|
import { resolveApiKey as resolveApiKey4 } from "@secondlayer/sdk";
|
|
207898
207524
|
|
|
207899
207525
|
// src/lib/project-file.ts
|
|
207900
|
-
import { existsSync as
|
|
207526
|
+
import { existsSync as existsSync7 } from "node:fs";
|
|
207901
207527
|
import { mkdir as mkdir4, readFile as readFile6, writeFile as writeFile5 } from "node:fs/promises";
|
|
207902
207528
|
import { homedir as homedir5 } from "node:os";
|
|
207903
|
-
import { dirname as
|
|
207529
|
+
import { dirname as dirname9, join as join13, parse as parsePath2, resolve as resolve8 } from "node:path";
|
|
207904
207530
|
import { z as z4 } from "zod";
|
|
207905
207531
|
var ProjectFileSchema = z4.object({
|
|
207906
207532
|
slug: z4.string().min(1)
|
|
@@ -207908,23 +207534,23 @@ var ProjectFileSchema = z4.object({
|
|
|
207908
207534
|
var FILENAME = "project";
|
|
207909
207535
|
var DIRNAME = ".secondlayer";
|
|
207910
207536
|
async function readActiveProject(cwd, globalDefault) {
|
|
207911
|
-
let dir =
|
|
207912
|
-
const home =
|
|
207537
|
+
let dir = resolve8(cwd);
|
|
207538
|
+
const home = resolve8(homedir5());
|
|
207913
207539
|
const fsRoot = parsePath2(dir).root;
|
|
207914
207540
|
while (true) {
|
|
207915
|
-
const candidate =
|
|
207916
|
-
if (
|
|
207541
|
+
const candidate = join13(dir, DIRNAME, FILENAME);
|
|
207542
|
+
if (existsSync7(candidate)) {
|
|
207917
207543
|
try {
|
|
207918
207544
|
const raw = await readFile6(candidate, "utf8");
|
|
207919
207545
|
const parsed = ProjectFileSchema.parse(JSON.parse(raw));
|
|
207920
207546
|
return { slug: parsed.slug, resolvedFrom: candidate };
|
|
207921
207547
|
} catch {}
|
|
207922
207548
|
}
|
|
207923
|
-
if (
|
|
207549
|
+
if (existsSync7(join13(dir, ".git")))
|
|
207924
207550
|
break;
|
|
207925
207551
|
if (dir === home || dir === fsRoot)
|
|
207926
207552
|
break;
|
|
207927
|
-
const parent =
|
|
207553
|
+
const parent = dirname9(dir);
|
|
207928
207554
|
if (parent === dir)
|
|
207929
207555
|
break;
|
|
207930
207556
|
dir = parent;
|
|
@@ -207932,7 +207558,7 @@ async function readActiveProject(cwd, globalDefault) {
|
|
|
207932
207558
|
if (globalDefault) {
|
|
207933
207559
|
return {
|
|
207934
207560
|
slug: globalDefault,
|
|
207935
|
-
resolvedFrom:
|
|
207561
|
+
resolvedFrom: join13(homedir5(), ".secondlayer", "config.json")
|
|
207936
207562
|
};
|
|
207937
207563
|
}
|
|
207938
207564
|
return null;
|
|
@@ -208005,7 +207631,7 @@ init_api_url();
|
|
|
208005
207631
|
init_http();
|
|
208006
207632
|
init_output();
|
|
208007
207633
|
init_session();
|
|
208008
|
-
import { confirm as confirm3, input
|
|
207634
|
+
import { confirm as confirm3, input } from "@inquirer/prompts";
|
|
208009
207635
|
function sessionExpiry() {
|
|
208010
207636
|
return new Date(Date.now() + 90 * 24 * 60 * 60 * 1000).toISOString();
|
|
208011
207637
|
}
|
|
@@ -208065,7 +207691,7 @@ async function runLoginFlow(options = {}) {
|
|
|
208065
207691
|
}
|
|
208066
207692
|
}
|
|
208067
207693
|
}
|
|
208068
|
-
const email = await
|
|
207694
|
+
const email = await input({
|
|
208069
207695
|
message: "Email",
|
|
208070
207696
|
validate: (v) => /^.+@.+\..+$/.test(v) ? true : "Invalid email"
|
|
208071
207697
|
});
|
|
@@ -208086,7 +207712,7 @@ async function runLoginFlow(options = {}) {
|
|
|
208086
207712
|
}
|
|
208087
207713
|
process.exit(1);
|
|
208088
207714
|
}
|
|
208089
|
-
const code = await
|
|
207715
|
+
const code = await input({
|
|
208090
207716
|
message: "Enter the 6-digit code",
|
|
208091
207717
|
validate: (v) => /^\d{6}$/.test(v) ? true : "Expected 6 digits"
|
|
208092
207718
|
});
|
|
@@ -208147,15 +207773,15 @@ function registerLogoutCommand(program) {
|
|
|
208147
207773
|
}
|
|
208148
207774
|
// src/commands/uninstall.ts
|
|
208149
207775
|
import { spawnSync as spawnSync4 } from "node:child_process";
|
|
208150
|
-
import { existsSync as
|
|
208151
|
-
import { join as
|
|
207776
|
+
import { existsSync as existsSync9, readFileSync as readFileSync8 } from "node:fs";
|
|
207777
|
+
import { join as join14, resolve as resolve10 } from "node:path";
|
|
208152
207778
|
import { planUninstall, uninstallCommand } from "@secondlayer/shared/runtime";
|
|
208153
207779
|
|
|
208154
207780
|
// src/lib/instance-init.ts
|
|
208155
207781
|
init_output();
|
|
208156
207782
|
import { randomBytes as randomBytes3 } from "node:crypto";
|
|
208157
|
-
import { existsSync as
|
|
208158
|
-
import { resolve as
|
|
207783
|
+
import { existsSync as existsSync8, readFileSync as readFileSync7, writeFileSync as writeFileSync6 } from "node:fs";
|
|
207784
|
+
import { resolve as resolve9 } from "node:path";
|
|
208159
207785
|
import { ARCHIVE_ROOT_PUBLIC_KEY_PEM } from "@secondlayer/shared/archive/root-key";
|
|
208160
207786
|
import { generateEd25519KeyPair } from "@secondlayer/shared/crypto/ed25519";
|
|
208161
207787
|
import { INSTANCE_NETWORKS } from "@secondlayer/shared/db/queries/instance";
|
|
@@ -208204,7 +207830,7 @@ function unescapeEnvValue(raw) {
|
|
|
208204
207830
|
`).replace(/\\"/g, '"').replace(/\\\\/g, "\\");
|
|
208205
207831
|
}
|
|
208206
207832
|
function readEnvValue(envPath, key) {
|
|
208207
|
-
if (!
|
|
207833
|
+
if (!existsSync8(envPath))
|
|
208208
207834
|
return null;
|
|
208209
207835
|
const match = readFileSync7(envPath, "utf8").match(new RegExp(`^${key}=(.*)$`, "m"));
|
|
208210
207836
|
return match ? unescapeEnvValue(match[1]) : null;
|
|
@@ -208252,12 +207878,12 @@ function renderInstanceEnv(env) {
|
|
|
208252
207878
|
`);
|
|
208253
207879
|
}
|
|
208254
207880
|
function writeInstanceEnv(cwd, env, file = INSTANCE_ENV_FILE) {
|
|
208255
|
-
const path =
|
|
208256
|
-
|
|
207881
|
+
const path = resolve9(cwd, file);
|
|
207882
|
+
writeFileSync6(path, renderInstanceEnv(env), { mode: 384 });
|
|
208257
207883
|
return path;
|
|
208258
207884
|
}
|
|
208259
207885
|
function loadExistingInstanceEnv(cwd, file = INSTANCE_ENV_FILE) {
|
|
208260
|
-
const path =
|
|
207886
|
+
const path = resolve9(cwd, file);
|
|
208261
207887
|
return {
|
|
208262
207888
|
INSTANCE_TOKEN: readEnvValue(path, "INSTANCE_TOKEN"),
|
|
208263
207889
|
SECONDLAYER_SECRETS_KEY: readEnvValue(path, "SECONDLAYER_SECRETS_KEY"),
|
|
@@ -208275,8 +207901,8 @@ init_resolve_auth();
|
|
|
208275
207901
|
var UNINSTALL_EXIT = { OK: 0, FAILED: 1, REFUSED: 2 };
|
|
208276
207902
|
var REPO_COMPOSE = "docker/oss/docker-compose.yml";
|
|
208277
207903
|
function bundleHasKeys(bundleDir) {
|
|
208278
|
-
const manifest =
|
|
208279
|
-
if (!
|
|
207904
|
+
const manifest = join14(bundleDir, "manifest.json");
|
|
207905
|
+
if (!existsSync9(manifest))
|
|
208280
207906
|
return false;
|
|
208281
207907
|
try {
|
|
208282
207908
|
const parsed = JSON.parse(readFileSync8(manifest, "utf8"));
|
|
@@ -208286,27 +207912,27 @@ function bundleHasKeys(bundleDir) {
|
|
|
208286
207912
|
}
|
|
208287
207913
|
}
|
|
208288
207914
|
function resolveUninstallLayout(dir, composeOverride) {
|
|
208289
|
-
const setupCompose =
|
|
208290
|
-
const setupEnv =
|
|
208291
|
-
const initEnv =
|
|
208292
|
-
const secretsFile = [setupEnv, initEnv].find((file) => readEnvValue(file, "SECONDLAYER_SECRETS_KEY") !== null) ?? [setupEnv, initEnv].find((file) =>
|
|
207915
|
+
const setupCompose = resolve10(dir, "docker-compose.yml");
|
|
207916
|
+
const setupEnv = resolve10(dir, ".env");
|
|
207917
|
+
const initEnv = resolve10(dir, ".env.local");
|
|
207918
|
+
const secretsFile = [setupEnv, initEnv].find((file) => readEnvValue(file, "SECONDLAYER_SECRETS_KEY") !== null) ?? [setupEnv, initEnv].find((file) => existsSync9(file));
|
|
208293
207919
|
if (composeOverride) {
|
|
208294
|
-
const composeFile =
|
|
208295
|
-
const sibling =
|
|
207920
|
+
const composeFile = resolve10(dir, composeOverride);
|
|
207921
|
+
const sibling = resolve10(composeFile, "..", ".env");
|
|
208296
207922
|
return {
|
|
208297
207923
|
composeFile,
|
|
208298
|
-
envFile:
|
|
207924
|
+
envFile: existsSync9(sibling) ? sibling : undefined,
|
|
208299
207925
|
secretsFile
|
|
208300
207926
|
};
|
|
208301
207927
|
}
|
|
208302
|
-
if (
|
|
207928
|
+
if (existsSync9(setupCompose)) {
|
|
208303
207929
|
return {
|
|
208304
207930
|
composeFile: setupCompose,
|
|
208305
|
-
envFile:
|
|
207931
|
+
envFile: existsSync9(setupEnv) ? setupEnv : undefined,
|
|
208306
207932
|
secretsFile
|
|
208307
207933
|
};
|
|
208308
207934
|
}
|
|
208309
|
-
return { composeFile:
|
|
207935
|
+
return { composeFile: resolve10(dir, REPO_COMPOSE), secretsFile };
|
|
208310
207936
|
}
|
|
208311
207937
|
function composeDownArgs(plan, layout) {
|
|
208312
207938
|
const base = uninstallCommand(plan, layout.composeFile);
|
|
@@ -208326,7 +207952,7 @@ function registerUninstallCommand(program) {
|
|
|
208326
207952
|
const dataDir = process.env.DATA_DIR ?? "./data";
|
|
208327
207953
|
const layout = resolveUninstallLayout(process.cwd(), opts.compose);
|
|
208328
207954
|
const secretsPresent = !!process.env.SECONDLAYER_SECRETS_KEY || !!layout.secretsFile;
|
|
208329
|
-
const keysBackedUp = opts.backup ? bundleHasKeys(
|
|
207955
|
+
const keysBackedUp = opts.backup ? bundleHasKeys(resolve10(opts.backup)) : false;
|
|
208330
207956
|
const decision = planUninstall({
|
|
208331
207957
|
purge: opts.purge === true,
|
|
208332
207958
|
confirmed: opts.yes === true,
|
|
@@ -208399,14 +208025,14 @@ init_fs();
|
|
|
208399
208025
|
init_output();
|
|
208400
208026
|
init_resolve_auth();
|
|
208401
208027
|
import {
|
|
208402
|
-
chmodSync
|
|
208403
|
-
existsSync as
|
|
208404
|
-
mkdirSync as
|
|
208028
|
+
chmodSync,
|
|
208029
|
+
existsSync as existsSync10,
|
|
208030
|
+
mkdirSync as mkdirSync4,
|
|
208405
208031
|
readFileSync as readFileSync9,
|
|
208406
|
-
statSync as
|
|
208407
|
-
writeFileSync as
|
|
208032
|
+
statSync as statSync2,
|
|
208033
|
+
writeFileSync as writeFileSync7
|
|
208408
208034
|
} from "node:fs";
|
|
208409
|
-
import { join as
|
|
208035
|
+
import { join as join15, resolve as resolve11 } from "node:path";
|
|
208410
208036
|
import { closeDb, getDb as getDb4, sql as sql2 } from "@secondlayer/shared/db";
|
|
208411
208037
|
import {
|
|
208412
208038
|
decryptBundle,
|
|
@@ -208555,10 +208181,10 @@ async function runBackup(opts) {
|
|
|
208555
208181
|
printError(plan.reason);
|
|
208556
208182
|
process.exit(BACKUP_EXIT.REFUSED);
|
|
208557
208183
|
}
|
|
208558
|
-
const outDir =
|
|
208559
|
-
|
|
208184
|
+
const outDir = resolve11(opts.out);
|
|
208185
|
+
mkdirSync4(outDir, { recursive: true });
|
|
208560
208186
|
console.error("dumping database…");
|
|
208561
|
-
const dumpPath =
|
|
208187
|
+
const dumpPath = join15(outDir, DB_FILE);
|
|
208562
208188
|
const dumpCmd = pgDumpInvocation(databaseUrl, dumpPath);
|
|
208563
208189
|
const dump = await runProcess(dumpCmd.cmd, "pg_dump", dumpCmd.env);
|
|
208564
208190
|
if (!dump.ok) {
|
|
@@ -208570,7 +208196,7 @@ async function runBackup(opts) {
|
|
|
208570
208196
|
db: {
|
|
208571
208197
|
file: DB_FILE,
|
|
208572
208198
|
sha256: await sha256File(dumpPath),
|
|
208573
|
-
bytes:
|
|
208199
|
+
bytes: statSync2(dumpPath).size,
|
|
208574
208200
|
format: "pg_dump-custom"
|
|
208575
208201
|
}
|
|
208576
208202
|
};
|
|
@@ -208578,16 +208204,16 @@ async function runBackup(opts) {
|
|
|
208578
208204
|
const lines = SECRET_ENV_KEYS.filter((k) => process.env[k]).map((k) => `${k}=${process.env[k]}`);
|
|
208579
208205
|
const sealed = encryptBundle(lines.join(`
|
|
208580
208206
|
`), passphrase);
|
|
208581
|
-
const secretsPath =
|
|
208582
|
-
|
|
208583
|
-
|
|
208207
|
+
const secretsPath = join15(outDir, SECRETS_FILE);
|
|
208208
|
+
writeFileSync7(secretsPath, sealed);
|
|
208209
|
+
chmodSync(secretsPath, 384);
|
|
208584
208210
|
manifest.secrets = {
|
|
208585
208211
|
file: SECRETS_FILE,
|
|
208586
208212
|
encrypted: true,
|
|
208587
208213
|
canary: sealKeyCanary(secretsKey)
|
|
208588
208214
|
};
|
|
208589
208215
|
}
|
|
208590
|
-
|
|
208216
|
+
writeFileSync7(join15(outDir, MANIFEST_FILE), `${JSON.stringify(manifest, null, 2)}
|
|
208591
208217
|
`);
|
|
208592
208218
|
output({
|
|
208593
208219
|
json: opts.json,
|
|
@@ -208616,14 +208242,14 @@ function attachRestoreCommand(cmd) {
|
|
|
208616
208242
|
}
|
|
208617
208243
|
async function runRestore(opts) {
|
|
208618
208244
|
const databaseUrl = requireDatabaseUrl();
|
|
208619
|
-
const dir =
|
|
208620
|
-
const manifestPath =
|
|
208621
|
-
if (!
|
|
208245
|
+
const dir = resolve11(opts.from);
|
|
208246
|
+
const manifestPath = join15(dir, MANIFEST_FILE);
|
|
208247
|
+
if (!existsSync10(manifestPath)) {
|
|
208622
208248
|
printError(`no ${MANIFEST_FILE} in ${dir}`);
|
|
208623
208249
|
process.exit(BACKUP_EXIT.REFUSED);
|
|
208624
208250
|
}
|
|
208625
208251
|
const manifest = JSON.parse(readFileSync9(manifestPath, "utf8"));
|
|
208626
|
-
const dumpPath =
|
|
208252
|
+
const dumpPath = join15(dir, manifest.db?.file ?? DB_FILE);
|
|
208627
208253
|
if (manifest.db && await sha256File(dumpPath) !== manifest.db.sha256) {
|
|
208628
208254
|
printError("the database dump does not match the digest in the manifest; this bundle is corrupt or truncated");
|
|
208629
208255
|
process.exit(BACKUP_EXIT.REFUSED);
|
|
@@ -208637,7 +208263,7 @@ async function runRestore(opts) {
|
|
|
208637
208263
|
process.exit(BACKUP_EXIT.REFUSED);
|
|
208638
208264
|
}
|
|
208639
208265
|
try {
|
|
208640
|
-
const env = decryptBundle(readFileSync9(
|
|
208266
|
+
const env = decryptBundle(readFileSync9(join15(dir, manifest.secrets.file)), passphrase);
|
|
208641
208267
|
bundleKey = env.split(`
|
|
208642
208268
|
`).find((l) => l.startsWith("SECONDLAYER_SECRETS_KEY="))?.slice("SECONDLAYER_SECRETS_KEY=".length).trim();
|
|
208643
208269
|
} catch {
|
|
@@ -208827,7 +208453,7 @@ init_resolve_auth();
|
|
|
208827
208453
|
|
|
208828
208454
|
// src/lib/setup-prompts.ts
|
|
208829
208455
|
import { resolve as resolvePath3 } from "node:path";
|
|
208830
|
-
import { confirm as confirm4, input as
|
|
208456
|
+
import { confirm as confirm4, input as input2, select as select3 } from "@inquirer/prompts";
|
|
208831
208457
|
|
|
208832
208458
|
// src/lib/oss-compose.ts
|
|
208833
208459
|
function buildOssCompose(opts = {}) {
|
|
@@ -209005,10 +208631,10 @@ dbcache=4096
|
|
|
209005
208631
|
// src/lib/setup-wizard.ts
|
|
209006
208632
|
import { spawn as spawn3, spawnSync as spawnSync5 } from "node:child_process";
|
|
209007
208633
|
import { randomBytes as randomBytes4 } from "node:crypto";
|
|
209008
|
-
import { existsSync as
|
|
208634
|
+
import { existsSync as existsSync11 } from "node:fs";
|
|
209009
208635
|
import { writeFile as writeFile6 } from "node:fs/promises";
|
|
209010
208636
|
import { createServer } from "node:net";
|
|
209011
|
-
import { join as
|
|
208637
|
+
import { join as join16, resolve as resolvePath2 } from "node:path";
|
|
209012
208638
|
import { closeDb as closeDb2, getRawClientFor } from "@secondlayer/shared/db";
|
|
209013
208639
|
import { FLOORS, diskFloorGb } from "@secondlayer/shared/runtime";
|
|
209014
208640
|
init_resolve_auth();
|
|
@@ -209108,8 +208734,8 @@ function randomHex(bytes) {
|
|
|
209108
208734
|
return randomBytes4(bytes).toString("hex");
|
|
209109
208735
|
}
|
|
209110
208736
|
function resolveSecrets(config) {
|
|
209111
|
-
const envPath =
|
|
209112
|
-
const existing = config.force || !
|
|
208737
|
+
const envPath = join16(config.dir, ".env");
|
|
208738
|
+
const existing = config.force || !existsSync11(envPath) ? {} : readEnvFile(config.dir, envPath);
|
|
209113
208739
|
const instance = buildInstanceEnv({
|
|
209114
208740
|
network: config.network,
|
|
209115
208741
|
existing: {
|
|
@@ -209201,13 +208827,13 @@ function renderSetupEnv(config, secrets) {
|
|
|
209201
208827
|
`);
|
|
209202
208828
|
}
|
|
209203
208829
|
async function writeSetupFiles(config, secrets) {
|
|
209204
|
-
const composePath =
|
|
209205
|
-
const envPath =
|
|
208830
|
+
const composePath = join16(config.dir, "docker-compose.yml");
|
|
208831
|
+
const envPath = join16(config.dir, ".env");
|
|
209206
208832
|
await writeFile6(composePath, buildOssCompose({ owner: config.owner, imageTag: config.imageTag }));
|
|
209207
208833
|
await writeFile6(envPath, renderSetupEnv(config, secrets), { mode: 384 });
|
|
209208
208834
|
const result = { composePath, envPath };
|
|
209209
208835
|
if (config.nodeMode === "full") {
|
|
209210
|
-
const configTomlPath =
|
|
208836
|
+
const configTomlPath = join16(config.dir, "Config.toml");
|
|
209211
208837
|
await writeFile6(configTomlPath, buildOssStacksConfigToml({
|
|
209212
208838
|
network: config.network,
|
|
209213
208839
|
bitcoinRpcPassword: secrets.bitcoinRpcPassword ?? ""
|
|
@@ -209215,7 +208841,7 @@ async function writeSetupFiles(config, secrets) {
|
|
|
209215
208841
|
result.configTomlPath = configTomlPath;
|
|
209216
208842
|
}
|
|
209217
208843
|
if (config.nodeMode === "full") {
|
|
209218
|
-
const bitcoinConfPath =
|
|
208844
|
+
const bitcoinConfPath = join16(config.dir, "bitcoin.conf");
|
|
209219
208845
|
await writeFile6(bitcoinConfPath, buildOssBitcoinConf({ rpcPassword: secrets.bitcoinRpcPassword ?? "" }));
|
|
209220
208846
|
result.bitcoinConfPath = bitcoinConfPath;
|
|
209221
208847
|
}
|
|
@@ -209593,7 +209219,7 @@ var NODE_MODE_DESCRIPTIONS = {
|
|
|
209593
209219
|
full: "Bundled Stacks node + bitcoind."
|
|
209594
209220
|
};
|
|
209595
209221
|
async function promptSetupConfig(flags) {
|
|
209596
|
-
const network = safeParse(flags.network, parseInstanceNetwork) ?? await
|
|
209222
|
+
const network = safeParse(flags.network, parseInstanceNetwork) ?? await select3({
|
|
209597
209223
|
message: "Network",
|
|
209598
209224
|
choices: [
|
|
209599
209225
|
{
|
|
@@ -209613,7 +209239,7 @@ async function promptSetupConfig(flags) {
|
|
|
209613
209239
|
}
|
|
209614
209240
|
]
|
|
209615
209241
|
});
|
|
209616
|
-
const nodeMode = safeParse(flags.nodeMode, parseSetupNodeMode) ?? await
|
|
209242
|
+
const nodeMode = safeParse(flags.nodeMode, parseSetupNodeMode) ?? await select3({
|
|
209617
209243
|
message: "Node mode",
|
|
209618
209244
|
choices: SETUP_NODE_MODES.map((mode) => {
|
|
209619
209245
|
const floor = guardrailPreview(mode, network);
|
|
@@ -209627,7 +209253,7 @@ async function promptSetupConfig(flags) {
|
|
|
209627
209253
|
let against = flags.against;
|
|
209628
209254
|
let skipBootstrap = !!flags.skipBootstrap;
|
|
209629
209255
|
if (!skipBootstrap && !against) {
|
|
209630
|
-
const choice = await
|
|
209256
|
+
const choice = await select3({
|
|
209631
209257
|
message: "Bootstrap from",
|
|
209632
209258
|
choices: [
|
|
209633
209259
|
{
|
|
@@ -209652,7 +209278,7 @@ async function promptSetupConfig(flags) {
|
|
|
209652
209278
|
} else if (choice === "skip") {
|
|
209653
209279
|
skipBootstrap = true;
|
|
209654
209280
|
} else {
|
|
209655
|
-
against = (await
|
|
209281
|
+
against = (await input2({
|
|
209656
209282
|
message: "Manifest URL or local path",
|
|
209657
209283
|
default: DEFAULT_ARCHIVE_MANIFEST
|
|
209658
209284
|
})).trim();
|
|
@@ -209840,5 +209466,5 @@ registerCreditsCommand(program);
|
|
|
209840
209466
|
registerArchiveCommand(program);
|
|
209841
209467
|
program.parse();
|
|
209842
209468
|
|
|
209843
|
-
//# debugId=
|
|
209469
|
+
//# debugId=DA489CE32ABB5ED564756E2164756E21
|
|
209844
209470
|
//# sourceMappingURL=cli.js.map
|