@getcirrus/pds 0.11.0 → 0.12.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/dist/cli.js +118 -96
- package/dist/index.js +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -4204,8 +4204,6 @@ const MAX_EVENTS = 100;
|
|
|
4204
4204
|
function createInitialState() {
|
|
4205
4205
|
return {
|
|
4206
4206
|
collections: [],
|
|
4207
|
-
syncStatus: "checking",
|
|
4208
|
-
relayRev: null,
|
|
4209
4207
|
pdsRev: null,
|
|
4210
4208
|
subscribers: 0,
|
|
4211
4209
|
latestSeq: null,
|
|
@@ -4215,7 +4213,10 @@ function createInitialState() {
|
|
|
4215
4213
|
accountActive: false,
|
|
4216
4214
|
wsConnected: false,
|
|
4217
4215
|
statusMessage: null,
|
|
4218
|
-
statusMessageTimeout: null
|
|
4216
|
+
statusMessageTimeout: null,
|
|
4217
|
+
handleCheck: null,
|
|
4218
|
+
didCheck: null,
|
|
4219
|
+
relayHostStatus: []
|
|
4219
4220
|
};
|
|
4220
4221
|
}
|
|
4221
4222
|
async function fetchRepo(client, did, state, render) {
|
|
@@ -4242,22 +4243,6 @@ async function fetchRepo(client, did, state, render) {
|
|
|
4242
4243
|
render();
|
|
4243
4244
|
} catch {}
|
|
4244
4245
|
}
|
|
4245
|
-
async function fetchRelaySync(did, state, render) {
|
|
4246
|
-
if (!state.pdsRev) return;
|
|
4247
|
-
try {
|
|
4248
|
-
const res = await fetch(`https://bsky.network/xrpc/com.atproto.sync.getLatestCommit?did=${encodeURIComponent(did)}`);
|
|
4249
|
-
if (res.status === 404) state.syncStatus = "unknown";
|
|
4250
|
-
else if (res.ok) {
|
|
4251
|
-
const data = await res.json();
|
|
4252
|
-
state.syncStatus = data.rev === state.pdsRev ? "synced" : "behind";
|
|
4253
|
-
state.relayRev = data.rev;
|
|
4254
|
-
} else state.syncStatus = "error";
|
|
4255
|
-
render();
|
|
4256
|
-
} catch {
|
|
4257
|
-
state.syncStatus = "error";
|
|
4258
|
-
render();
|
|
4259
|
-
}
|
|
4260
|
-
}
|
|
4261
4246
|
async function fetchFirehoseStatus(client, state, render) {
|
|
4262
4247
|
try {
|
|
4263
4248
|
const data = await client.getFirehoseStatus();
|
|
@@ -4289,7 +4274,27 @@ async function fetchAccountStatus(client, state, render) {
|
|
|
4289
4274
|
render();
|
|
4290
4275
|
} catch {}
|
|
4291
4276
|
}
|
|
4292
|
-
function
|
|
4277
|
+
async function fetchIdentityChecks(client, handle, did, pdsHostname, state, render) {
|
|
4278
|
+
try {
|
|
4279
|
+
const [handleResult, didResult] = await Promise.all([checkHandleResolutionDetailed(client, handle, did), checkDidResolution(client, did, pdsHostname)]);
|
|
4280
|
+
state.handleCheck = {
|
|
4281
|
+
ok: handleResult.ok,
|
|
4282
|
+
methods: handleResult.methods
|
|
4283
|
+
};
|
|
4284
|
+
state.didCheck = {
|
|
4285
|
+
ok: didResult.ok,
|
|
4286
|
+
pdsEndpoint: didResult.pdsEndpoint
|
|
4287
|
+
};
|
|
4288
|
+
render();
|
|
4289
|
+
} catch {}
|
|
4290
|
+
}
|
|
4291
|
+
async function fetchRelayHostStatus(client, pdsHostname, state, render) {
|
|
4292
|
+
try {
|
|
4293
|
+
state.relayHostStatus = await client.getAllRelayHostStatus(pdsHostname);
|
|
4294
|
+
render();
|
|
4295
|
+
} catch {}
|
|
4296
|
+
}
|
|
4297
|
+
function connectFirehose(targetUrl, state, render, onCommit) {
|
|
4293
4298
|
let ws = null;
|
|
4294
4299
|
let reconnectTimer = null;
|
|
4295
4300
|
let closed = false;
|
|
@@ -4313,12 +4318,15 @@ function connectFirehose(targetUrl, state, render) {
|
|
|
4313
4318
|
action: "identity",
|
|
4314
4319
|
path: event.handle ?? ""
|
|
4315
4320
|
});
|
|
4316
|
-
else
|
|
4317
|
-
|
|
4318
|
-
|
|
4319
|
-
|
|
4320
|
-
|
|
4321
|
-
|
|
4321
|
+
else {
|
|
4322
|
+
for (const op of event.ops) state.events.unshift({
|
|
4323
|
+
time,
|
|
4324
|
+
seq: event.seq,
|
|
4325
|
+
action: op.action,
|
|
4326
|
+
path: op.path
|
|
4327
|
+
});
|
|
4328
|
+
onCommit?.();
|
|
4329
|
+
}
|
|
4322
4330
|
if (state.events.length > MAX_EVENTS) state.events.length = MAX_EVENTS;
|
|
4323
4331
|
render();
|
|
4324
4332
|
};
|
|
@@ -4348,78 +4356,65 @@ function renderDashboard(state, config) {
|
|
|
4348
4356
|
const rows = process.stdout.rows || 24;
|
|
4349
4357
|
const lines = [];
|
|
4350
4358
|
const indent = " ";
|
|
4359
|
+
const accountDot = state.accountActive ? pc.green("●") + " " + pc.green("ACTIVE") : pc.yellow("○") + " " + pc.yellow("INACTIVE");
|
|
4351
4360
|
lines.push("");
|
|
4352
4361
|
lines.push(`${indent}${pc.bold("☁ CIRRUS")} ${pc.dim("·")} ${pc.cyan(config.hostname)} ${pc.dim("·")} ${pc.dim("v" + config.version)}`);
|
|
4353
|
-
lines.push(`${indent} ${pc.white("@" + config.handle)} ${pc.dim("·")} ${pc.dim(config.did)}`);
|
|
4362
|
+
lines.push(`${indent} ${pc.white("@" + config.handle)} ${pc.dim("·")} ${pc.dim(config.did)} ${pc.dim("·")} ${accountDot}`);
|
|
4354
4363
|
lines.push("");
|
|
4355
|
-
const colWidth = Math.floor((cols - 6) /
|
|
4364
|
+
const colWidth = Math.floor((cols - 6) / 2);
|
|
4356
4365
|
const col1 = [pc.dim("REPOSITORY"), ""];
|
|
4357
|
-
if (state.collections.length === 0) col1.push(pc.dim("
|
|
4366
|
+
if (state.collections.length === 0) col1.push(pc.dim("No records"));
|
|
4358
4367
|
else for (const c of state.collections) {
|
|
4359
4368
|
const name = c.friendlyName.padEnd(16);
|
|
4360
4369
|
const count = String(c.count).padStart(5);
|
|
4361
4370
|
const more = c.hasMore ? "+" : " ";
|
|
4362
4371
|
col1.push(`${name} ${pc.bold(count)}${more}`);
|
|
4363
4372
|
}
|
|
4364
|
-
const col2 = [pc.dim("
|
|
4365
|
-
|
|
4366
|
-
|
|
4367
|
-
|
|
4368
|
-
|
|
4369
|
-
|
|
4370
|
-
|
|
4371
|
-
|
|
4373
|
+
const col2 = [pc.dim("NETWORK"), ""];
|
|
4374
|
+
if (state.handleCheck) {
|
|
4375
|
+
const icon = state.handleCheck.ok ? pc.green("✓") : pc.red("✗");
|
|
4376
|
+
const methods = state.handleCheck.methods.length > 0 ? pc.dim(` ${state.handleCheck.methods.join(" ")}`) : "";
|
|
4377
|
+
col2.push(`${icon} handle${methods}`);
|
|
4378
|
+
} else col2.push(pc.dim("○ handle checking…"));
|
|
4379
|
+
if (state.didCheck) {
|
|
4380
|
+
const icon = state.didCheck.ok ? pc.green("✓") : pc.red("✗");
|
|
4381
|
+
col2.push(`${icon} did document`);
|
|
4382
|
+
} else col2.push(pc.dim("○ did doc checking…"));
|
|
4383
|
+
col2.push("");
|
|
4384
|
+
const relayStatusColors = {
|
|
4385
|
+
active: pc.green,
|
|
4386
|
+
idle: pc.yellow,
|
|
4387
|
+
offline: pc.red,
|
|
4388
|
+
throttled: pc.red,
|
|
4389
|
+
banned: pc.red
|
|
4372
4390
|
};
|
|
4373
|
-
const
|
|
4374
|
-
|
|
4375
|
-
|
|
4376
|
-
|
|
4377
|
-
|
|
4378
|
-
|
|
4391
|
+
const relayDotColors = {
|
|
4392
|
+
active: pc.green("●"),
|
|
4393
|
+
idle: pc.yellow("●"),
|
|
4394
|
+
offline: pc.red("●"),
|
|
4395
|
+
throttled: pc.red("●"),
|
|
4396
|
+
banned: pc.red("●")
|
|
4379
4397
|
};
|
|
4380
|
-
const
|
|
4381
|
-
|
|
4382
|
-
|
|
4383
|
-
|
|
4398
|
+
if (state.relayHostStatus.length > 0) for (const relay of state.relayHostStatus) {
|
|
4399
|
+
const name = relay.relay.replace("https://relay1.", "").replace(".bsky.network", "");
|
|
4400
|
+
const colorFn = relayStatusColors[relay.status] ?? pc.dim;
|
|
4401
|
+
const dot = relayDotColors[relay.status] ?? pc.dim("○");
|
|
4402
|
+
col2.push(`${dot} ${name} ${colorFn(relay.status)}`);
|
|
4403
|
+
}
|
|
4404
|
+
else col2.push(pc.dim("○ relay unknown"));
|
|
4405
|
+
col2.push("");
|
|
4384
4406
|
const subDot = state.subscribers > 0 ? pc.green("●") : pc.dim("○");
|
|
4385
|
-
|
|
4386
|
-
|
|
4387
|
-
|
|
4388
|
-
|
|
4389
|
-
for (const sub of state.subscriberDetails.slice(0, 5)) {
|
|
4390
|
-
const ip = sub.ip ? ` ${shortenIP(sub.ip)}` : "";
|
|
4391
|
-
col3.push(pc.dim(`${relativeTime(sub.connectedAt)} cursor: ${sub.cursor}${ip}`));
|
|
4392
|
-
}
|
|
4407
|
+
col2.push(`${subDot} ${pc.bold(String(state.subscribers))} subscriber${state.subscribers !== 1 ? "s" : ""} ${pc.dim("seq:")} ${state.latestSeq != null ? state.latestSeq : pc.dim("—")}`);
|
|
4408
|
+
if (state.subscriberDetails.length > 0) for (const sub of state.subscriberDetails.slice(0, 3)) {
|
|
4409
|
+
const ip = sub.ip ? shortenIP(sub.ip) : "";
|
|
4410
|
+
col2.push(pc.dim(` ${relativeTime(sub.connectedAt)} cursor: ${sub.cursor} ${ip}`));
|
|
4393
4411
|
}
|
|
4394
|
-
const columnLines = renderColumns([
|
|
4395
|
-
col1,
|
|
4396
|
-
col2,
|
|
4397
|
-
col3
|
|
4398
|
-
], [
|
|
4399
|
-
colWidth,
|
|
4400
|
-
colWidth,
|
|
4401
|
-
colWidth
|
|
4402
|
-
]);
|
|
4412
|
+
const columnLines = renderColumns([col1, col2], [colWidth, colWidth]);
|
|
4403
4413
|
for (const line of columnLines) lines.push(indent + line);
|
|
4404
4414
|
lines.push("");
|
|
4405
4415
|
const remaining = rows - lines.length - 3;
|
|
4406
|
-
const notifHeight = Math.max(3, Math.floor(remaining * .
|
|
4416
|
+
const notifHeight = Math.max(3, Math.floor(remaining * .35));
|
|
4407
4417
|
const eventsHeight = Math.max(3, remaining - notifHeight);
|
|
4408
|
-
const notifSeparator = "─".repeat(Math.max(0, cols - visibleLength(indent + "NOTIFICATIONS ") - 2));
|
|
4409
|
-
lines.push(`${indent}${pc.dim("NOTIFICATIONS " + notifSeparator)}`);
|
|
4410
|
-
if (state.notifications.length === 0) {
|
|
4411
|
-
lines.push(`${indent}${pc.dim("No notifications yet")}`);
|
|
4412
|
-
for (let i = 1; i < notifHeight - 1; i++) lines.push("");
|
|
4413
|
-
} else {
|
|
4414
|
-
const visibleNotifs = state.notifications.slice(0, notifHeight - 1);
|
|
4415
|
-
for (const n of visibleNotifs) {
|
|
4416
|
-
const readDim = n.isRead ? pc.dim : (s) => s;
|
|
4417
|
-
const line = `${indent}${pc.dim(n.time)} ${n.icon} ${readDim(n.author)} ${readDim(pc.dim(n.text))}`;
|
|
4418
|
-
lines.push(truncate(line, cols));
|
|
4419
|
-
}
|
|
4420
|
-
for (let i = visibleNotifs.length; i < notifHeight - 1; i++) lines.push("");
|
|
4421
|
-
}
|
|
4422
|
-
lines.push("");
|
|
4423
4418
|
const wsStatusText = state.wsConnected ? "● connected" : "○ disconnected";
|
|
4424
4419
|
indent + "";
|
|
4425
4420
|
const eventsSuffix = " " + wsStatusText + " ";
|
|
@@ -4438,16 +4433,35 @@ function renderDashboard(state, config) {
|
|
|
4438
4433
|
delete: pc.red,
|
|
4439
4434
|
identity: pc.cyan
|
|
4440
4435
|
}[ev.action] ?? pc.dim;
|
|
4441
|
-
const line = `${indent}${pc.dim(ev.time)} ${pc.dim("#" + String(ev.seq).padStart(4))} ${actionColor(ev.action.toUpperCase().padEnd(
|
|
4436
|
+
const line = `${indent}${pc.dim(ev.time)} ${pc.dim("#" + String(ev.seq).padStart(4))} ${actionColor(ev.action.toUpperCase().padEnd(8))} ${ev.path}`;
|
|
4442
4437
|
lines.push(truncate(line, cols));
|
|
4443
4438
|
}
|
|
4444
4439
|
for (let i = visibleEvents.length; i < eventsHeight - 1; i++) lines.push("");
|
|
4445
4440
|
}
|
|
4441
|
+
const notifSeparator = "─".repeat(Math.max(0, cols - visibleLength(indent + "NOTIFICATIONS ") - 2));
|
|
4442
|
+
lines.push(`${indent}${pc.dim("NOTIFICATIONS " + notifSeparator)}`);
|
|
4443
|
+
if (state.notifications.length === 0) {
|
|
4444
|
+
lines.push(`${indent}${pc.dim("No notifications yet")}`);
|
|
4445
|
+
for (let i = 1; i < notifHeight - 1; i++) lines.push("");
|
|
4446
|
+
} else {
|
|
4447
|
+
const visibleNotifs = state.notifications.slice(0, notifHeight - 1);
|
|
4448
|
+
for (const n of visibleNotifs) {
|
|
4449
|
+
const readDim = n.isRead ? pc.dim : (s) => s;
|
|
4450
|
+
const line = `${indent}${pc.dim(n.time)} ${n.icon} ${readDim(n.author)} ${readDim(pc.dim(n.text))}`;
|
|
4451
|
+
lines.push(truncate(line, cols));
|
|
4452
|
+
}
|
|
4453
|
+
for (let i = visibleNotifs.length; i < notifHeight - 1; i++) lines.push("");
|
|
4454
|
+
}
|
|
4446
4455
|
lines.push("");
|
|
4447
|
-
const
|
|
4448
|
-
|
|
4456
|
+
const keys = [];
|
|
4457
|
+
if (!state.accountActive) keys.push(`${pc.dim("[a]")} activate`);
|
|
4458
|
+
else {
|
|
4459
|
+
keys.push(`${pc.dim("[r]")} crawl`);
|
|
4460
|
+
keys.push(`${pc.dim("[e]")} emit identity`);
|
|
4461
|
+
}
|
|
4462
|
+
keys.push(`${pc.dim("[q]")} quit`);
|
|
4463
|
+
let footer = `${indent}${keys.join(` ${pc.dim("·")} `)}`;
|
|
4449
4464
|
if (state.statusMessage) footer += ` ${pc.yellow(state.statusMessage)}`;
|
|
4450
|
-
else footer += ` ${accountStatus}`;
|
|
4451
4465
|
lines.push(footer);
|
|
4452
4466
|
while (lines.length < rows) lines.push("");
|
|
4453
4467
|
const output = lines.slice(0, rows).map((l) => padRight(l, cols)).join("\n");
|
|
@@ -4490,6 +4504,7 @@ const dashboardCommand = defineCommand({
|
|
|
4490
4504
|
const authToken = config.AUTH_TOKEN;
|
|
4491
4505
|
const handle = config.HANDLE ?? "";
|
|
4492
4506
|
const did = config.DID ?? "";
|
|
4507
|
+
const pdsHostname = config.PDS_HOSTNAME ?? "";
|
|
4493
4508
|
if (!authToken) {
|
|
4494
4509
|
console.error(pc.red("Error:"), "No AUTH_TOKEN found. Run 'pds init' first.");
|
|
4495
4510
|
process.exit(1);
|
|
@@ -4501,7 +4516,7 @@ const dashboardCommand = defineCommand({
|
|
|
4501
4516
|
}
|
|
4502
4517
|
const state = createInitialState();
|
|
4503
4518
|
const dashConfig = {
|
|
4504
|
-
hostname:
|
|
4519
|
+
hostname: pdsHostname || targetUrl,
|
|
4505
4520
|
handle,
|
|
4506
4521
|
did,
|
|
4507
4522
|
version: "0.10.6"
|
|
@@ -4512,9 +4527,11 @@ const dashboardCommand = defineCommand({
|
|
|
4512
4527
|
clearScreen();
|
|
4513
4528
|
const intervals = [];
|
|
4514
4529
|
let firehose = null;
|
|
4530
|
+
let repoRefetchTimer = null;
|
|
4515
4531
|
function cleanup() {
|
|
4516
4532
|
for (const interval of intervals) clearInterval(interval);
|
|
4517
4533
|
if (firehose) firehose.close();
|
|
4534
|
+
if (repoRefetchTimer) clearTimeout(repoRefetchTimer);
|
|
4518
4535
|
if (state.statusMessageTimeout) clearTimeout(state.statusMessageTimeout);
|
|
4519
4536
|
if (process.stdin.isTTY) process.stdin.setRawMode(false);
|
|
4520
4537
|
showCursor();
|
|
@@ -4528,20 +4545,28 @@ const dashboardCommand = defineCommand({
|
|
|
4528
4545
|
cleanup();
|
|
4529
4546
|
process.exit(0);
|
|
4530
4547
|
});
|
|
4548
|
+
const scheduleRepoRefetch = () => {
|
|
4549
|
+
if (repoRefetchTimer) clearTimeout(repoRefetchTimer);
|
|
4550
|
+
repoRefetchTimer = setTimeout(() => {
|
|
4551
|
+
fetchRepo(client, did, state, render);
|
|
4552
|
+
}, 1e3);
|
|
4553
|
+
};
|
|
4531
4554
|
render();
|
|
4532
4555
|
await Promise.all([
|
|
4533
4556
|
fetchRepo(client, did, state, render),
|
|
4534
4557
|
fetchFirehoseStatus(client, state, render),
|
|
4535
4558
|
fetchAccountStatus(client, state, render),
|
|
4536
|
-
fetchNotifications(client, state, render)
|
|
4559
|
+
fetchNotifications(client, state, render),
|
|
4560
|
+
fetchIdentityChecks(client, handle, did, pdsHostname, state, render)
|
|
4537
4561
|
]);
|
|
4538
|
-
await
|
|
4562
|
+
await fetchRelayHostStatus(client, pdsHostname, state, render);
|
|
4539
4563
|
intervals.push(setInterval(() => fetchRepo(client, did, state, render), 3e4));
|
|
4540
|
-
intervals.push(setInterval(() =>
|
|
4564
|
+
intervals.push(setInterval(() => fetchRelayHostStatus(client, pdsHostname, state, render), 5e3));
|
|
4541
4565
|
intervals.push(setInterval(() => fetchFirehoseStatus(client, state, render), 1e4));
|
|
4542
4566
|
intervals.push(setInterval(() => fetchNotifications(client, state, render), 15e3));
|
|
4543
4567
|
intervals.push(setInterval(() => fetchAccountStatus(client, state, render), 3e4));
|
|
4544
|
-
|
|
4568
|
+
intervals.push(setInterval(() => fetchIdentityChecks(client, handle, did, pdsHostname, state, render), 15e3));
|
|
4569
|
+
firehose = connectFirehose(targetUrl, state, render, scheduleRepoRefetch);
|
|
4545
4570
|
process.stdout.on("resize", render);
|
|
4546
4571
|
if (process.stdin.isTTY) {
|
|
4547
4572
|
process.stdin.setRawMode(true);
|
|
@@ -4559,6 +4584,7 @@ const dashboardCommand = defineCommand({
|
|
|
4559
4584
|
process.exit(0);
|
|
4560
4585
|
}
|
|
4561
4586
|
if (key === "a" || key === "A") {
|
|
4587
|
+
if (state.accountActive) return;
|
|
4562
4588
|
if (awaitingActivateConfirm) {
|
|
4563
4589
|
awaitingActivateConfirm = false;
|
|
4564
4590
|
if (activateConfirmTimeout) clearTimeout(activateConfirmTimeout);
|
|
@@ -4566,12 +4592,7 @@ const dashboardCommand = defineCommand({
|
|
|
4566
4592
|
try {
|
|
4567
4593
|
await client.activateAccount();
|
|
4568
4594
|
state.accountActive = true;
|
|
4569
|
-
|
|
4570
|
-
if (pdsHostname && !isDev) await client.requestCrawl(pdsHostname);
|
|
4571
|
-
try {
|
|
4572
|
-
await client.emitIdentity();
|
|
4573
|
-
} catch {}
|
|
4574
|
-
setStatusMessage(state, pc.green("✓ Activated! Crawl requested."), render, 5e3);
|
|
4595
|
+
setStatusMessage(state, pc.green("✓ Account activated"), render, 5e3);
|
|
4575
4596
|
} catch (err) {
|
|
4576
4597
|
setStatusMessage(state, pc.red(`\u2717 ${err instanceof Error ? err.message : "Activation failed"}`), render, 5e3);
|
|
4577
4598
|
}
|
|
@@ -4587,7 +4608,7 @@ const dashboardCommand = defineCommand({
|
|
|
4587
4608
|
return;
|
|
4588
4609
|
}
|
|
4589
4610
|
if (key === "r" || key === "R") {
|
|
4590
|
-
|
|
4611
|
+
if (!state.accountActive) return;
|
|
4591
4612
|
if (!pdsHostname || isDev) {
|
|
4592
4613
|
setStatusMessage(state, pc.yellow("No PDS hostname configured"), render);
|
|
4593
4614
|
return;
|
|
@@ -4597,6 +4618,7 @@ const dashboardCommand = defineCommand({
|
|
|
4597
4618
|
return;
|
|
4598
4619
|
}
|
|
4599
4620
|
if (key === "e" || key === "E") {
|
|
4621
|
+
if (!state.accountActive) return;
|
|
4600
4622
|
setStatusMessage(state, "Emitting identity…", render, 1e4);
|
|
4601
4623
|
try {
|
|
4602
4624
|
const result = await client.emitIdentity();
|
package/dist/index.js
CHANGED