@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 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 connectFirehose(targetUrl, state, render) {
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 for (const op of event.ops) state.events.unshift({
4317
- time,
4318
- seq: event.seq,
4319
- action: op.action,
4320
- path: op.path
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) / 3);
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("Loading…"));
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("FEDERATION"), ""];
4365
- col2.push(pc.dim("bsky.network"));
4366
- const statusColors = {
4367
- synced: pc.green,
4368
- behind: pc.yellow,
4369
- error: pc.red,
4370
- checking: pc.dim,
4371
- unknown: pc.dim
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 dotColors = {
4374
- synced: pc.green("●"),
4375
- behind: pc.yellow("●"),
4376
- error: pc.red("●"),
4377
- checking: pc.dim(""),
4378
- unknown: pc.dim("")
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 colorFn = statusColors[state.syncStatus] ?? pc.dim;
4381
- col2.push(`${dotColors[state.syncStatus] ?? pc.dim("")} ${colorFn(state.syncStatus.toUpperCase())}`);
4382
- if (state.relayRev) col2.push(pc.dim(`rev: ${state.relayRev.slice(0, 12)}`));
4383
- const col3 = [pc.dim("FIREHOSE"), ""];
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
- col3.push(`${subDot} ${pc.bold(String(state.subscribers))} subscriber${state.subscribers !== 1 ? "s" : ""}`);
4386
- col3.push(pc.dim(`seq: ${state.latestSeq != null ? state.latestSeq : "—"}`));
4387
- if (state.subscriberDetails.length > 0) {
4388
- col3.push("");
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 * .4));
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(7))} ${ev.path}`;
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 accountStatus = state.accountActive ? pc.green("● active") : pc.yellow("○ deactivated");
4448
- let footer = `${indent}${pc.dim("[a]")} activate ${pc.dim("·")} ${pc.dim("[r]")} crawl ${pc.dim("·")} ${pc.dim("[e]")} emit identity ${pc.dim("·")} ${pc.dim("[q]")} quit`;
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: config.PDS_HOSTNAME ?? targetUrl,
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 fetchRelaySync(did, state, render);
4562
+ await fetchRelayHostStatus(client, pdsHostname, state, render);
4539
4563
  intervals.push(setInterval(() => fetchRepo(client, did, state, render), 3e4));
4540
- intervals.push(setInterval(() => fetchRelaySync(did, state, render), 5e3));
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
- firehose = connectFirehose(targetUrl, state, render);
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
- const pdsHostname = config.PDS_HOSTNAME;
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
- const pdsHostname = config.PDS_HOSTNAME;
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
@@ -4230,7 +4230,7 @@ function renderDashboard(config) {
4230
4230
 
4231
4231
  //#endregion
4232
4232
  //#region package.json
4233
- var version = "0.11.0";
4233
+ var version = "0.12.0";
4234
4234
 
4235
4235
  //#endregion
4236
4236
  //#region src/index.ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@getcirrus/pds",
3
- "version": "0.11.0",
3
+ "version": "0.12.0",
4
4
  "description": "Cirrus – A single-user AT Protocol PDS on Cloudflare Workers",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",