@secondlayer/cli 10.2.0 → 10.3.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 +12 -59
- package/dist/cli.js +196 -122
- package/dist/cli.js.map +10 -9
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -10,51 +10,27 @@ sl --version
|
|
|
10
10
|
|
|
11
11
|
## Auth
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
Default API is `http://127.0.0.1:3800`. Override with `SL_API_URL`.
|
|
14
14
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
- **Machine / CI** — set `SL_API_KEY` to an API key created in the platform
|
|
18
|
-
console at https://secondlayer.tools/platform/api-keys. Keys are prefixed
|
|
19
|
-
`sk-sl_`. For a persisted headless login: `echo "$SL_API_KEY" | sl login --with-token`.
|
|
15
|
+
Writes against a published bind use `INSTANCE_TOKEN` from `sl init` as
|
|
16
|
+
`SL_API_KEY`. Loopback reads need no token.
|
|
20
17
|
|
|
21
18
|
```bash
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
# machine / CI
|
|
26
|
-
export SL_API_KEY=sk-sl_xxxxxxxx
|
|
19
|
+
export SL_API_URL=http://127.0.0.1:3800
|
|
20
|
+
export SL_API_KEY=<INSTANCE_TOKEN>
|
|
27
21
|
```
|
|
28
22
|
|
|
29
|
-
`sl whoami` shows the active account, the effective API URL, and the credential
|
|
30
|
-
source (and exits non-zero when not logged in). The global `--api-key` /
|
|
31
|
-
`--api-url` flags (and `SL_API_KEY` / `SL_API_URL`) apply to every command.
|
|
32
|
-
|
|
33
|
-
During open beta, reads are public (no key needed) — for subgraphs that means
|
|
34
|
-
**public** subgraphs only (managed deploys default public; BYO deploys and all
|
|
35
|
-
pre-existing subgraphs are private, readable with your key). Writes — deploying,
|
|
36
|
-
publishing, or managing subgraphs and subscriptions — require a session or an
|
|
37
|
-
`sk-sl_` key.
|
|
38
|
-
|
|
39
23
|
## Quickstart
|
|
40
24
|
|
|
41
25
|
```bash
|
|
42
26
|
bun add -g @secondlayer/cli
|
|
43
|
-
sl
|
|
44
|
-
sl
|
|
45
|
-
|
|
27
|
+
sl init --network mainnet
|
|
28
|
+
sl start --print
|
|
29
|
+
# docker compose -f docker/oss/docker-compose.yml up -d
|
|
46
30
|
|
|
47
|
-
# scaffold + deploy a subgraph from a deployed contract
|
|
48
31
|
sl subgraphs scaffold SP1234ABCD.my-contract -o subgraphs/my-contract.ts
|
|
49
32
|
sl subgraphs deploy subgraphs/my-contract.ts --start-block <recent-block>
|
|
50
33
|
sl subgraphs query my-contract <table> --sort _block_height --order desc
|
|
51
|
-
|
|
52
|
-
# wire a webhook receiver
|
|
53
|
-
sl subscriptions create my-hook \
|
|
54
|
-
--runtime node \
|
|
55
|
-
--subgraph my-contract \
|
|
56
|
-
--table <table> \
|
|
57
|
-
--url https://<receiver-host>/webhook
|
|
58
34
|
```
|
|
59
35
|
|
|
60
36
|
`sl subgraphs scaffold` writes the definition file, creates/updates
|
|
@@ -75,27 +51,6 @@ No account. Writes `.env.local`, restores history, prints the Stacks observer st
|
|
|
75
51
|
| `sl verify [all\|raw\|decode:<name>\|subgraph:<name>] --against <manifest> [--quick\|--deep\|--anchor]` | Compare local data to a signed archive. Default target `raw`. Exit `0` clean, `1` diverged, `2` unanchored |
|
|
76
52
|
| `sl repair --against <archive> [--apply]` | Plan (default) or apply an archive repair |
|
|
77
53
|
|
|
78
|
-
### Auth & project
|
|
79
|
-
|
|
80
|
-
| Command | What it does |
|
|
81
|
-
|---|---|
|
|
82
|
-
| `sl login` / `sl logout` | Start or revoke a session |
|
|
83
|
-
| `sl whoami` | Print account, credential source, and active project |
|
|
84
|
-
| `sl keys create --product streams [--name <name>]` | Mint a scoped `streams`/`index` read key; prints the `sk-sl_` key **once**. Needs an account key in `SL_API_KEY` |
|
|
85
|
-
| `sl projects create [name]` | Create a project |
|
|
86
|
-
| `sl projects list` | List projects |
|
|
87
|
-
| `sl projects use <slug>` | Bind cwd to a project (writes `./.secondlayer/project`) |
|
|
88
|
-
| `sl projects get` | Show resolved project + source file |
|
|
89
|
-
| `sl projects delete <slug>` (alias `rm`) | Delete a project (`-y` to skip confirm) |
|
|
90
|
-
|
|
91
|
-
Project binding is per-directory: `.secondlayer/project` in cwd takes
|
|
92
|
-
precedence over `~/.secondlayer/config.json:defaultProject`. The walk-up stops
|
|
93
|
-
at `.git`.
|
|
94
|
-
|
|
95
|
-
**Key products:** an `account` key (dashboard default) grants both `streams:read`
|
|
96
|
-
and `index:read` and is the only key that can mint; `streams`/`index` keys are
|
|
97
|
-
scoped reads and cannot mint (403). Minted keys inherit your plan's tier.
|
|
98
|
-
|
|
99
54
|
### Subgraphs
|
|
100
55
|
|
|
101
56
|
| Command | What it does |
|
|
@@ -153,19 +108,17 @@ deliveries/...) operates on both kinds.
|
|
|
153
108
|
| Command | What it does |
|
|
154
109
|
|---|---|
|
|
155
110
|
| `sl contracts generate [files...]` (alias `gen`) | Generate TS interfaces from Clarity contracts |
|
|
156
|
-
| `sl context` |
|
|
111
|
+
| `sl context` | Instance snapshot — Streams + Index tips, subgraphs, subscriptions |
|
|
157
112
|
| `sl doctor` / `sl status` | Reachability + health checks |
|
|
158
|
-
| `sl account get` / `sl account update` | Show or update display name / bio / slug |
|
|
159
|
-
| `sl account billing` | Show plan, subscription, trial, discounts |
|
|
160
113
|
| `sl config get/set/reset/delete` | Inspect or reset local config |
|
|
161
114
|
|
|
162
115
|
## Environment variables
|
|
163
116
|
|
|
164
117
|
| Var | Purpose |
|
|
165
118
|
|---|---|
|
|
166
|
-
| `SL_API_KEY` |
|
|
167
|
-
| `SL_API_URL` |
|
|
168
|
-
| `SL_PLATFORM_API_URL` |
|
|
119
|
+
| `SL_API_KEY` | `INSTANCE_TOKEN` from `sl init` for writes. Loopback reads need no token |
|
|
120
|
+
| `SL_API_URL` | Instance API. Default `http://127.0.0.1:3800` |
|
|
121
|
+
| `SL_PLATFORM_API_URL` | Alias of `SL_API_URL` |
|
|
169
122
|
| `STACKS_NETWORK` | Default network (also via `--network <local\|testnet\|mainnet>`) |
|
|
170
123
|
| `HIRO_API_KEY` | Used by `sl contracts generate` for remote contract fetches |
|
|
171
124
|
|
package/dist/cli.js
CHANGED
|
@@ -2580,7 +2580,7 @@ function isOssMode() {
|
|
|
2580
2580
|
return true;
|
|
2581
2581
|
}
|
|
2582
2582
|
}
|
|
2583
|
-
var LOCAL_API_URL = "http://127.0.0.1:3800";
|
|
2583
|
+
var LOCAL_API_URL = "http://127.0.0.1:3800", ARCHIVE_OPS_API_URL = "https://api.secondlayer.tools";
|
|
2584
2584
|
var init_resolve_auth = __esm(() => {
|
|
2585
2585
|
init_session();
|
|
2586
2586
|
});
|
|
@@ -2629,9 +2629,28 @@ async function httpPlatform(path2, opts = {}) {
|
|
|
2629
2629
|
async function httpPlatformAnon(path2, opts = {}) {
|
|
2630
2630
|
return request(`${resolveApiUrl()}${path2}`, opts);
|
|
2631
2631
|
}
|
|
2632
|
+
function resolveArchiveOpsUrl() {
|
|
2633
|
+
return (process.env.SL_CREDITS_API_URL ?? ARCHIVE_OPS_API_URL).replace(/\/+$/, "");
|
|
2634
|
+
}
|
|
2635
|
+
async function httpArchiveOpsAnon(path2, opts = {}) {
|
|
2636
|
+
return request(`${resolveArchiveOpsUrl()}${path2}`, opts);
|
|
2637
|
+
}
|
|
2638
|
+
async function httpArchiveOps(path2, opts = {}) {
|
|
2639
|
+
const envKey = resolveEnvKey();
|
|
2640
|
+
const session = envKey ? null : await readSession();
|
|
2641
|
+
const bearer = envKey ?? session?.token;
|
|
2642
|
+
if (!bearer) {
|
|
2643
|
+
throw new CliHttpError(401, "SESSION_EXPIRED", { error: "Not logged in" }, "Not logged in — run `SL_API_URL=https://api.secondlayer.tools sl login`");
|
|
2644
|
+
}
|
|
2645
|
+
return request(`${resolveArchiveOpsUrl()}${path2}`, {
|
|
2646
|
+
...opts,
|
|
2647
|
+
bearer
|
|
2648
|
+
});
|
|
2649
|
+
}
|
|
2632
2650
|
var CliHttpError, REQUEST_TIMEOUT_MS = 30000;
|
|
2633
2651
|
var init_http = __esm(() => {
|
|
2634
2652
|
init_resolve_auth();
|
|
2653
|
+
init_session();
|
|
2635
2654
|
CliHttpError = class CliHttpError extends Error {
|
|
2636
2655
|
status;
|
|
2637
2656
|
code;
|
|
@@ -34787,7 +34806,7 @@ init_esm();
|
|
|
34787
34806
|
// package.json
|
|
34788
34807
|
var package_default = {
|
|
34789
34808
|
name: "@secondlayer/cli",
|
|
34790
|
-
version: "10.
|
|
34809
|
+
version: "10.3.0",
|
|
34791
34810
|
repository: {
|
|
34792
34811
|
type: "git",
|
|
34793
34812
|
url: "git+https://github.com/ryanwaits/secondlayer.git",
|
|
@@ -34831,7 +34850,7 @@ var package_default = {
|
|
|
34831
34850
|
"@secondlayer/bundler": "^0.5.0",
|
|
34832
34851
|
"@secondlayer/scaffold": "^1.5.1",
|
|
34833
34852
|
"@secondlayer/sdk": "^6.54.0",
|
|
34834
|
-
"@secondlayer/shared": "^7.11.
|
|
34853
|
+
"@secondlayer/shared": "^7.11.1",
|
|
34835
34854
|
"@secondlayer/stacks": "^3.3.1",
|
|
34836
34855
|
"@secondlayer/subgraphs": "^3.26.4",
|
|
34837
34856
|
esbuild: "^0.27.4",
|
|
@@ -34874,15 +34893,13 @@ async function runBillingStatus(json) {
|
|
|
34874
34893
|
output({ json, data: res, human: () => renderBillingStatus(res) });
|
|
34875
34894
|
}
|
|
34876
34895
|
function addBillingCommand(parent) {
|
|
34877
|
-
parent.command("billing").description("Show
|
|
34896
|
+
parent.command("billing").description("Show archive credit balance (hidden; use sl credits)").option("--json", "Output as JSON").action((options) => runBillingStatus(options.json));
|
|
34878
34897
|
}
|
|
34879
34898
|
function renderBillingStatus(res) {
|
|
34880
34899
|
if (!res.subscription) {
|
|
34881
34900
|
console.log(formatKeyValue([
|
|
34882
|
-
["
|
|
34883
|
-
["
|
|
34884
|
-
["Subgraphs", dim("upgrade to deploy / go private")],
|
|
34885
|
-
["Upgrade", dim("https://secondlayer.tools/platform/billing")]
|
|
34901
|
+
["Credits", dim("sl credits balance")],
|
|
34902
|
+
["Buy", dim("sl credits buy --email you@example.com --pack 25")]
|
|
34886
34903
|
]));
|
|
34887
34904
|
return;
|
|
34888
34905
|
}
|
|
@@ -34980,6 +34997,115 @@ function registerAccountCommand(program2) {
|
|
|
34980
34997
|
action: "update profile"
|
|
34981
34998
|
}));
|
|
34982
34999
|
}
|
|
35000
|
+
// src/commands/credits.ts
|
|
35001
|
+
init_http();
|
|
35002
|
+
init_output();
|
|
35003
|
+
var PACKS = [10, 25, 50, 100];
|
|
35004
|
+
function parsePack(raw) {
|
|
35005
|
+
const n = Number(raw);
|
|
35006
|
+
if (!PACKS.includes(n)) {
|
|
35007
|
+
error(`--pack must be one of ${PACKS.join(", ")}`);
|
|
35008
|
+
process.exit(1);
|
|
35009
|
+
}
|
|
35010
|
+
return n;
|
|
35011
|
+
}
|
|
35012
|
+
async function runBuy(opts) {
|
|
35013
|
+
const email = opts.email.trim().toLowerCase();
|
|
35014
|
+
if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) {
|
|
35015
|
+
error("--email must be a valid address");
|
|
35016
|
+
process.exit(1);
|
|
35017
|
+
}
|
|
35018
|
+
const amount = parsePack(opts.pack);
|
|
35019
|
+
let res;
|
|
35020
|
+
try {
|
|
35021
|
+
res = await httpArchiveOpsAnon("/api/public/credits/checkout", { method: "POST", body: { email, amount } });
|
|
35022
|
+
} catch (err) {
|
|
35023
|
+
if (err instanceof CliHttpError) {
|
|
35024
|
+
error(err.message);
|
|
35025
|
+
process.exit(1);
|
|
35026
|
+
}
|
|
35027
|
+
throw err;
|
|
35028
|
+
}
|
|
35029
|
+
output({
|
|
35030
|
+
json: opts.json,
|
|
35031
|
+
data: res,
|
|
35032
|
+
human: () => {
|
|
35033
|
+
success(`Checkout for $${amount} → ${email}`);
|
|
35034
|
+
console.log(res.url);
|
|
35035
|
+
console.log(dim("After payment: SL_API_URL=https://api.secondlayer.tools sl login && sl credits balance"));
|
|
35036
|
+
}
|
|
35037
|
+
});
|
|
35038
|
+
}
|
|
35039
|
+
async function runBalance(opts) {
|
|
35040
|
+
let res;
|
|
35041
|
+
try {
|
|
35042
|
+
res = await httpArchiveOps("/api/billing/status");
|
|
35043
|
+
} catch (err) {
|
|
35044
|
+
if (err instanceof CliHttpError) {
|
|
35045
|
+
error(err.message);
|
|
35046
|
+
process.exit(1);
|
|
35047
|
+
}
|
|
35048
|
+
throw err;
|
|
35049
|
+
}
|
|
35050
|
+
const micros = BigInt(res.creditsUsdMicros ?? "0");
|
|
35051
|
+
const usd = Number(micros) / 1e6;
|
|
35052
|
+
output({
|
|
35053
|
+
json: opts.json,
|
|
35054
|
+
data: { creditsUsd: usd, creditsUsdMicros: micros.toString() },
|
|
35055
|
+
human: () => {
|
|
35056
|
+
const refill = res.refill;
|
|
35057
|
+
console.log(formatKeyValue([
|
|
35058
|
+
["Balance", `$${usd.toFixed(2)}`],
|
|
35059
|
+
[
|
|
35060
|
+
"Refill",
|
|
35061
|
+
refill?.belowUsd != null ? `on — below $${refill.belowUsd} buy $${refill.packUsd}` : dim("off")
|
|
35062
|
+
],
|
|
35063
|
+
["Buy", dim("sl credits buy --email you@example.com --pack 25")]
|
|
35064
|
+
]));
|
|
35065
|
+
}
|
|
35066
|
+
});
|
|
35067
|
+
}
|
|
35068
|
+
function registerCreditsCommand(program2) {
|
|
35069
|
+
const credits = program2.command("credits").description("Buy and check archive credits");
|
|
35070
|
+
credits.command("buy").description("Open a one-time Stripe checkout for archive credits").requiredOption("--email <email>", "Receipt email; becomes the account").option("--pack <usd>", `One of ${PACKS.join(", ")}`, "25").option("--json", "Output as JSON").action((opts) => runBuy(opts));
|
|
35071
|
+
credits.command("balance").description("Show prepaid archive credit balance").option("--json", "Output as JSON").action((opts) => runBalance(opts));
|
|
35072
|
+
credits.command("refill").description("Opt-in auto-refill when the balance drops under a threshold").option("--below <usd>", "Trigger when balance is under this USD amount").option("--pack <usd>", `Pack to buy (${PACKS.join(", ")})`, "25").option("--off", "Turn auto-refill off").option("--json", "Output as JSON").action((opts) => runRefill(opts));
|
|
35073
|
+
}
|
|
35074
|
+
async function runRefill(opts) {
|
|
35075
|
+
if (!opts.off && opts.below === undefined) {
|
|
35076
|
+
await runBalance(opts);
|
|
35077
|
+
return;
|
|
35078
|
+
}
|
|
35079
|
+
const body = opts.off ? { belowUsd: null } : { belowUsd: Number(opts.below), packUsd: parsePack(opts.pack) };
|
|
35080
|
+
if (!opts.off && (!Number.isFinite(body.belowUsd) || (body.belowUsd ?? 0) < 1)) {
|
|
35081
|
+
error("--below must be at least 1");
|
|
35082
|
+
process.exit(1);
|
|
35083
|
+
}
|
|
35084
|
+
let res;
|
|
35085
|
+
try {
|
|
35086
|
+
res = await httpArchiveOps("/api/billing/refill", {
|
|
35087
|
+
method: "POST",
|
|
35088
|
+
body
|
|
35089
|
+
});
|
|
35090
|
+
} catch (err) {
|
|
35091
|
+
if (err instanceof CliHttpError) {
|
|
35092
|
+
error(err.message);
|
|
35093
|
+
process.exit(1);
|
|
35094
|
+
}
|
|
35095
|
+
throw err;
|
|
35096
|
+
}
|
|
35097
|
+
output({
|
|
35098
|
+
json: opts.json,
|
|
35099
|
+
data: res,
|
|
35100
|
+
human: () => {
|
|
35101
|
+
if (res.belowUsd == null) {
|
|
35102
|
+
success("Auto-refill off");
|
|
35103
|
+
return;
|
|
35104
|
+
}
|
|
35105
|
+
success(`Auto-refill on — below $${res.belowUsd} buy $${res.packUsd}`);
|
|
35106
|
+
}
|
|
35107
|
+
});
|
|
35108
|
+
}
|
|
34983
35109
|
|
|
34984
35110
|
// src/commands/index.ts
|
|
34985
35111
|
init_codegen();
|
|
@@ -35406,94 +35532,71 @@ function registerContextCommand(program2) {
|
|
|
35406
35532
|
});
|
|
35407
35533
|
}
|
|
35408
35534
|
// src/commands/status.ts
|
|
35409
|
-
init_config2();
|
|
35410
35535
|
init_http();
|
|
35411
35536
|
init_output();
|
|
35537
|
+
init_resolve_auth();
|
|
35412
35538
|
function registerStatusCommand(program2) {
|
|
35413
|
-
program2.command("status").description("Show
|
|
35414
|
-
const config = await loadConfig2();
|
|
35539
|
+
program2.command("status").description("Show local instance status").option("--json", "Output as JSON").action(async (options3) => {
|
|
35415
35540
|
try {
|
|
35416
|
-
const
|
|
35417
|
-
const status = local ? await httpPlatform("/public/status") : await httpPlatform("/status");
|
|
35541
|
+
const status = await httpPlatformAnon("/public/status");
|
|
35418
35542
|
output({
|
|
35419
35543
|
json: options3.json,
|
|
35420
35544
|
data: status,
|
|
35421
|
-
human: () =>
|
|
35545
|
+
human: () => printPublicStatus(status)
|
|
35422
35546
|
});
|
|
35423
|
-
} catch
|
|
35424
|
-
if (err instanceof CliHttpError && err.code === "SESSION_EXPIRED") {
|
|
35425
|
-
console.error("Not logged in. Run: sl login");
|
|
35426
|
-
process.exit(1);
|
|
35427
|
-
}
|
|
35547
|
+
} catch {
|
|
35428
35548
|
console.log("");
|
|
35429
35549
|
console.log(blue("System Status"));
|
|
35430
35550
|
console.log(` ${red("NOT RUNNING")}`);
|
|
35431
35551
|
console.log("");
|
|
35432
|
-
|
|
35433
|
-
|
|
35434
|
-
console.log(dim(" Start with: sl local start"));
|
|
35435
|
-
} else {
|
|
35436
|
-
console.log(dim(" Can't reach the platform API."));
|
|
35437
|
-
console.log(dim(" Check your connection or try again."));
|
|
35438
|
-
}
|
|
35552
|
+
console.log(dim(` Can't reach ${resolveApiUrl()}/public/status.`));
|
|
35553
|
+
console.log(dim(" Start with: docker compose up -d"));
|
|
35439
35554
|
console.log("");
|
|
35440
35555
|
process.exit(1);
|
|
35441
35556
|
}
|
|
35442
35557
|
});
|
|
35443
35558
|
}
|
|
35444
|
-
function
|
|
35559
|
+
function printPublicStatus(status) {
|
|
35560
|
+
const overall = String(status.status ?? "unknown");
|
|
35561
|
+
const color = overall === "healthy" ? green : red;
|
|
35445
35562
|
console.log("");
|
|
35446
|
-
const statusColor = status.status === "healthy" ? green : red;
|
|
35447
35563
|
console.log(blue("System Status"));
|
|
35448
|
-
console.log(` ${
|
|
35564
|
+
console.log(` ${color(overall.toUpperCase())}`);
|
|
35449
35565
|
console.log("");
|
|
35450
|
-
|
|
35451
|
-
|
|
35452
|
-
|
|
35453
|
-
|
|
35454
|
-
|
|
35455
|
-
|
|
35456
|
-
|
|
35457
|
-
} else {
|
|
35458
|
-
for (const p2 of status.indexProgress) {
|
|
35459
|
-
const behind = p2.highestSeenBlock - p2.lastIndexedBlock;
|
|
35460
|
-
const behindStr = behind > 0 ? yellow(` (${behind} behind)`) : green(" (synced)");
|
|
35461
|
-
console.log(` ${p2.network}: block ${p2.lastIndexedBlock}${behindStr}`);
|
|
35462
|
-
if (status.chainTip && status.chainTip > 0) {
|
|
35463
|
-
const pct = Math.min(p2.lastIndexedBlock / status.chainTip * 100, 100);
|
|
35464
|
-
const barWidth = 30;
|
|
35465
|
-
const filled = Math.round(pct / 100 * barWidth);
|
|
35466
|
-
const empty = barWidth - filled;
|
|
35467
|
-
const bar = `${"█".repeat(filled)}${"░".repeat(empty)}`;
|
|
35468
|
-
const pctStr = `${pct.toFixed(1)}%`;
|
|
35469
|
-
const color = pct >= 99.9 ? green : pct >= 50 ? yellow : red;
|
|
35470
|
-
console.log(` ${dim("chain:")} ${color(bar)} ${color(pctStr)} ${dim(`(tip: ${status.chainTip.toLocaleString()})`)}`);
|
|
35471
|
-
}
|
|
35472
|
-
const contiguous = p2.lastContiguousBlock ?? 0;
|
|
35473
|
-
if (status.integrity === "complete") {
|
|
35474
|
-
console.log(` ${dim("contiguous:")} ${contiguous} ${green("(complete)")}`);
|
|
35475
|
-
} else {
|
|
35476
|
-
const gapCount = status.gaps?.length ?? 0;
|
|
35477
|
-
const missing = status.totalMissingBlocks ?? 0;
|
|
35478
|
-
console.log(` ${dim("contiguous:")} ${contiguous} ${yellow(`(${gapCount} gaps, ${missing} missing blocks)`)}`);
|
|
35479
|
-
if (status.gaps) {
|
|
35480
|
-
for (const gap of status.gaps.slice(0, 5)) {
|
|
35481
|
-
const range = gap.gapStart === gap.gapEnd ? `${gap.gapStart}` : `${gap.gapStart}-${gap.gapEnd}`;
|
|
35482
|
-
console.log(` ${dim("gap:")} ${range} ${dim(`(${gap.size} block${gap.size > 1 ? "s" : ""})`)}`);
|
|
35483
|
-
}
|
|
35484
|
-
}
|
|
35485
|
-
}
|
|
35566
|
+
const tip = status.chainTip;
|
|
35567
|
+
if (typeof tip === "number") {
|
|
35568
|
+
console.log(blue("Chain"));
|
|
35569
|
+
console.log(` tip: ${tip.toLocaleString()}`);
|
|
35570
|
+
const integrity = status.chainIntegrity;
|
|
35571
|
+
if (integrity) {
|
|
35572
|
+
console.log(` integrity: ${integrity.ok ? green("ok") : yellow("check")}`);
|
|
35486
35573
|
}
|
|
35574
|
+
console.log("");
|
|
35487
35575
|
}
|
|
35488
|
-
|
|
35489
|
-
if (
|
|
35490
|
-
console.log(blue("
|
|
35491
|
-
console.log(
|
|
35492
|
-
|
|
35493
|
-
|
|
35576
|
+
const streams = status.streams;
|
|
35577
|
+
if (streams) {
|
|
35578
|
+
console.log(blue("Streams"));
|
|
35579
|
+
console.log(` ${streams.status ?? "unknown"}`);
|
|
35580
|
+
if (typeof streams.tip?.lag_seconds === "number") {
|
|
35581
|
+
console.log(` lag: ${streams.tip.lag_seconds}s`);
|
|
35582
|
+
}
|
|
35494
35583
|
console.log("");
|
|
35495
35584
|
}
|
|
35496
|
-
|
|
35585
|
+
const index = status.index;
|
|
35586
|
+
if (index) {
|
|
35587
|
+
console.log(blue("Index"));
|
|
35588
|
+
console.log(` ${index.status ?? "unknown"}`);
|
|
35589
|
+
const bad = (index.decoders ?? []).filter((d2) => d2.status !== "ok");
|
|
35590
|
+
if (bad.length > 0) {
|
|
35591
|
+
for (const d2 of bad.slice(0, 8)) {
|
|
35592
|
+
console.log(` ${yellow(d2.decoder)} ${d2.status}`);
|
|
35593
|
+
}
|
|
35594
|
+
}
|
|
35595
|
+
console.log("");
|
|
35596
|
+
}
|
|
35597
|
+
if (typeof status.timestamp === "string") {
|
|
35598
|
+
console.log(dim(`Last updated: ${status.timestamp}`));
|
|
35599
|
+
}
|
|
35497
35600
|
}
|
|
35498
35601
|
// src/commands/subscriptions.ts
|
|
35499
35602
|
init_api_client();
|
|
@@ -37316,7 +37419,6 @@ function registerKeysCommand(program2) {
|
|
|
37316
37419
|
});
|
|
37317
37420
|
}
|
|
37318
37421
|
// src/commands/doctor.ts
|
|
37319
|
-
init_api_client();
|
|
37320
37422
|
init_config2();
|
|
37321
37423
|
|
|
37322
37424
|
// src/lib/health.ts
|
|
@@ -37462,50 +37564,31 @@ async function checkHealth() {
|
|
|
37462
37564
|
init_http();
|
|
37463
37565
|
init_network();
|
|
37464
37566
|
init_output();
|
|
37567
|
+
init_resolve_auth();
|
|
37465
37568
|
function registerDoctorCommand(program2) {
|
|
37466
37569
|
program2.command("doctor").description("Run diagnostics on the full stack").option("--json", "Output as JSON").action(async (options3) => {
|
|
37467
37570
|
const config = await loadConfig2();
|
|
37468
37571
|
if (config.network === "local") {
|
|
37469
37572
|
await runLocalDoctor(options3.json);
|
|
37470
|
-
|
|
37471
|
-
await runHostedDoctor(options3.json);
|
|
37573
|
+
return;
|
|
37472
37574
|
}
|
|
37575
|
+
await runInstanceDoctor(options3.json);
|
|
37473
37576
|
});
|
|
37474
37577
|
}
|
|
37475
|
-
async function
|
|
37578
|
+
async function runInstanceDoctor(jsonOutput) {
|
|
37476
37579
|
const config = await loadConfig2();
|
|
37477
|
-
const apiUrl =
|
|
37580
|
+
const apiUrl = resolveApiUrl();
|
|
37478
37581
|
const issues = [];
|
|
37479
37582
|
const results = { network: config.network, apiUrl };
|
|
37480
37583
|
let apiHealthy = false;
|
|
37481
37584
|
let statusData = null;
|
|
37482
37585
|
try {
|
|
37483
|
-
statusData = await
|
|
37586
|
+
statusData = await httpPlatformAnon("/public/status");
|
|
37484
37587
|
apiHealthy = true;
|
|
37485
|
-
} catch
|
|
37486
|
-
|
|
37487
|
-
if (err.code === "SESSION_EXPIRED") {
|
|
37488
|
-
issues.push("Not authenticated. Run: sl login");
|
|
37489
|
-
} else {
|
|
37490
|
-
issues.push(`API error: ${err.code}`);
|
|
37491
|
-
}
|
|
37492
|
-
} else {
|
|
37493
|
-
issues.push(`Cannot reach API at ${apiUrl}`);
|
|
37494
|
-
}
|
|
37588
|
+
} catch {
|
|
37589
|
+
issues.push(`Cannot reach ${apiUrl}/public/status`);
|
|
37495
37590
|
}
|
|
37496
37591
|
results.apiHealthy = apiHealthy;
|
|
37497
|
-
let authOk = false;
|
|
37498
|
-
let account = null;
|
|
37499
|
-
try {
|
|
37500
|
-
account = await getAccountProfile();
|
|
37501
|
-
authOk = true;
|
|
37502
|
-
} catch (err) {
|
|
37503
|
-
if (err instanceof CliHttpError && err.code === "SESSION_EXPIRED") {
|
|
37504
|
-
issues.push("Not authenticated. Run: sl login");
|
|
37505
|
-
}
|
|
37506
|
-
}
|
|
37507
|
-
results.authOk = authOk;
|
|
37508
|
-
results.account = account;
|
|
37509
37592
|
if (jsonOutput) {
|
|
37510
37593
|
console.log(JSON.stringify({ ...results, status: statusData, issues }, null, 2));
|
|
37511
37594
|
return;
|
|
@@ -37517,28 +37600,18 @@ async function runHostedDoctor(jsonOutput) {
|
|
|
37517
37600
|
console.log(blue("API"));
|
|
37518
37601
|
console.log(` ${apiHealthy ? green("✓") : red("✗")} ${apiHealthy ? green("reachable") : red("unreachable")}`);
|
|
37519
37602
|
console.log("");
|
|
37520
|
-
console.log(blue("Auth"));
|
|
37521
|
-
if (authOk && account) {
|
|
37522
|
-
console.log(` ${green("✓")} Authenticated`);
|
|
37523
|
-
console.log(formatKeyValue([
|
|
37524
|
-
[" Email", account.email ?? dim("unknown")],
|
|
37525
|
-
[" Plan", account.plan ?? dim("unknown")]
|
|
37526
|
-
]));
|
|
37527
|
-
} else {
|
|
37528
|
-
console.log(` ${red("✗")} Not authenticated`);
|
|
37529
|
-
}
|
|
37530
|
-
console.log("");
|
|
37531
37603
|
if (statusData) {
|
|
37532
|
-
const
|
|
37533
|
-
|
|
37534
|
-
|
|
37535
|
-
|
|
37536
|
-
|
|
37537
|
-
|
|
37538
|
-
|
|
37539
|
-
}
|
|
37540
|
-
|
|
37541
|
-
|
|
37604
|
+
const tip = statusData.chainTip;
|
|
37605
|
+
const streams = statusData.streams;
|
|
37606
|
+
const index = statusData.index;
|
|
37607
|
+
console.log(blue("Instance"));
|
|
37608
|
+
if (typeof tip === "number")
|
|
37609
|
+
console.log(` tip: ${tip.toLocaleString()}`);
|
|
37610
|
+
if (streams?.status)
|
|
37611
|
+
console.log(` streams: ${streams.status}`);
|
|
37612
|
+
if (index?.status)
|
|
37613
|
+
console.log(` index: ${index.status}`);
|
|
37614
|
+
console.log("");
|
|
37542
37615
|
}
|
|
37543
37616
|
console.log(blue("Issues"));
|
|
37544
37617
|
if (issues.length === 0) {
|
|
@@ -39886,9 +39959,10 @@ registerContextCommand(program);
|
|
|
39886
39959
|
registerVerifyCommand(program);
|
|
39887
39960
|
registerRepairCommand(program);
|
|
39888
39961
|
program.commandsGroup("Account:");
|
|
39962
|
+
registerCreditsCommand(program);
|
|
39889
39963
|
registerAccountCommand(program);
|
|
39890
39964
|
registerKeysCommand(program);
|
|
39891
39965
|
program.parse();
|
|
39892
39966
|
|
|
39893
|
-
//# debugId=
|
|
39967
|
+
//# debugId=F9D9C4C9412258E564756E2164756E21
|
|
39894
39968
|
//# sourceMappingURL=cli.js.map
|