1// Tail the source of truth's indexing progress from anywhere:
2//
3// node run tail-progress # production
4// node run tail-progress http://zenith:43201/progress # staging
5//
6// Opens a fetch to the /progress route and renders the live progress tree
7// in this terminal. Reconnects automatically; each (re)connect resumes from
8// the server's current state, since the stream encoder snapshots on attach.
9const defaultUrl = "https://db.paperclover.net/progress";
10
11export async function main() {
12 const url = process.argv[2] ?? defaultUrl;
13 const token = process.env.CLOVER_SOT_KEY;
14 if (!token) {
15 console.warn("CLOVER_SOT_KEY is not set; the server will likely 401");
16 }
17
18 let delay = 1000;
19 while (true) {
20 const connectedAt = Date.now();
21 try {
22 const res = await fetch(url, {
23 headers: token ? { Authorization: token } : {},
24 });
25 if (res.status === 401) {
26 console.error("unauthorized; set CLOVER_SOT_KEY");
27 process.exit(1);
28 }
29 if (!res.ok || !res.body) {
30 throw new Error(`server responded ${res.status} ${res.statusText}`);
31 }
32 console.info(`connected to ${url}`);
33 // resolves only when the stream ends; the indexer root never ends, so
34 // this await effectively lasts until disconnect.
35 await progress.decodeByteStream(
36 res.body as ReadableStream<Uint8Array>,
37 progress.globalRoot,
38 );
39 } catch (err: any) {
40 console.warn(`disconnected: ${err?.message ?? err}`);
41 }
42 if (Date.now() - connectedAt > 15_000) delay = 1000;
43 else delay = Math.min(delay * 2, 30_000);
44 console.info(`reconnecting in ${Math.round(delay / 1000)}s...`);
45 await async.delay(delay);
46 }
47}
48
49import * as async from "@clo/lib/async";
50import * as progress from "@clo/lib/progress";