@martintrojer/murmur 0.2.1 → 0.2.2

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/index.d.ts CHANGED
@@ -412,6 +412,21 @@ declare function pidAlive(pid: number): boolean;
412
412
  * owns the pane, so this reads the same for a local and a remote pane -- a
413
413
  * reader cannot resolve a remote window id against its own tmux.
414
414
  *
415
+ * The session name is shortened to its last segment, and that is load-bearing
416
+ * rather than cosmetic. It is reached far more often than it looks: `agent_name`
417
+ * is set only by mu, `window_name` is null whenever tmux is auto-renaming (its
418
+ * default), and `pi_session` is null for the whole life of an unnamed session --
419
+ * pi's own auto-namer runs at CLOSE, so a live agent, which is exactly the one
420
+ * you are looking at, has no session name yet. So the common row for a
421
+ * hand-started pi fell through to here and printed `hacking/murmur`: a path,
422
+ * where a name belongs.
423
+ *
424
+ * The full session name is not lost. The picker's stream column shows it, and
425
+ * shows it precisely BECAUSE of this shortening: that column is blanked when it
426
+ * would repeat the name, so `hacking/murmur` in both cells collapsed to one
427
+ * path and a blank. Shortened, the row reads `murmur` + `hacking/murmur` -- the
428
+ * same width, carrying strictly more.
429
+ *
415
430
  * Falls back to the window id only when a node recorded no names at all, which
416
431
  * means a non-tmux harness.
417
432
  */
@@ -457,6 +472,36 @@ declare const ssh: Channel;
457
472
  declare function hasWarmSocket(target: string): boolean;
458
473
 
459
474
  declare const MAX_CONCURRENT_PEERS = 8;
475
+ /**
476
+ * The optional half of a collect, as a bag rather than four positional
477
+ * arguments.
478
+ *
479
+ * `collect(store, ssh, now, undefined, mux)` was already the call site before
480
+ * the floor was added, and a fifth positional -- a bare number, next to another
481
+ * bare number -- is how `now` and `floorMs` get silently swapped.
482
+ */
483
+ type CollectOptions = {
484
+ /** Bounds the whole run. Injectable so tests need not wait out real time. */
485
+ deadline?: Promise<void>;
486
+ /** The mux reconciliation asks which panes are alive. */
487
+ mux?: Mux;
488
+ /**
489
+ * Skip peers attempted within this many ms. Zero -- the default -- fetches
490
+ * every peer, which is what a deliberate `murmur collect` and the picker both
491
+ * want. Only the status bar, which repaints on a timer, passes a floor.
492
+ *
493
+ * Opt IN rather than opt out: a surface that forgets this argument keeps the
494
+ * old always-fetch behaviour, which is merely wasteful. The opposite default
495
+ * would mean a new surface silently serves stale data.
496
+ */
497
+ floorMs?: number;
498
+ /**
499
+ * Source of the floor's jitter, in [0, 1). Injected for the same reason `now`
500
+ * and `isAlive` are: a test pins both edges of the window by returning 0 and
501
+ * a value approaching 1, rather than sampling and hoping.
502
+ */
503
+ random?: () => number;
504
+ };
460
505
  type CollectResult = {
461
506
  peer: string;
462
507
  ok: boolean;
@@ -486,7 +531,7 @@ type CollectResult = {
486
531
  * One round trip per peer, and never a second: the document is complete, so what
487
532
  * arrives either replaces the cache entirely or does not touch it.
488
533
  */
489
- declare function collect(store: Store, channel: Channel, now?: number, deadline?: Promise<void>, mux?: Mux): Promise<CollectResult[]>;
534
+ declare function collect(store: Store, channel: Channel, now?: number, options?: CollectOptions): Promise<CollectResult[]>;
490
535
 
491
536
  declare function glance(store: Store, agent: PaneView, lines?: number): string | null;
492
537
 
@@ -554,8 +599,15 @@ declare function status(store: Store, identity: NodeIdentity, now?: number): Sta
554
599
  * display-popup, so one sleeping laptop would otherwise write ssh diagnostics
555
600
  * to stderr several times a minute, forever. `murmur collect`, which a human
556
601
  * runs deliberately, is the only place that prints.
602
+ *
603
+ * `floorMs` is how a caller says whether it is a TIMER or a PERSON. The status
604
+ * bar repaints on `status-interval` and passes COLLECT_FLOOR_MS, so fetch rate
605
+ * stops being tied to redraw rate. The picker passes nothing: pressing the key
606
+ * is a person asking now, and its `^r` reload comes back through here too --
607
+ * a refresh key that skipped the fetch would be a key that silently does
608
+ * nothing.
557
609
  */
558
- declare function statusWithCollect(store: Store, identity: NodeIdentity, now?: number, channel?: Channel, mux?: Mux): Promise<Status>;
610
+ declare function statusWithCollect(store: Store, identity: NodeIdentity, now?: number, channel?: Channel, options?: CollectOptions): Promise<Status>;
559
611
 
560
612
  declare const MURMUR_VERSION: string;
561
613
 
package/dist/index.js CHANGED
@@ -185,8 +185,12 @@ function pidAlive(pid) {
185
185
  }
186
186
 
187
187
  // src/agents.ts
188
+ function sessionLeaf(session) {
189
+ const leaf = session.split("/").filter(Boolean).at(-1);
190
+ return leaf ?? session;
191
+ }
188
192
  function agentLabel(agent) {
189
- const name = agent.agent_name ?? agent.pi_session ?? agent.window_name ?? agent.session_name;
193
+ const name = agent.agent_name ?? agent.pi_session ?? agent.window_name ?? (agent.session_name === null ? null : sessionLeaf(agent.session_name));
190
194
  return terminalText(name ?? agent.window);
191
195
  }
192
196
  function agentLocation(agent) {
@@ -580,6 +584,7 @@ function viewSort(views) {
580
584
  // src/collector.ts
581
585
  var MAX_CONCURRENT_PEERS = 8;
582
586
  var COLLECT_DEADLINE_MS = 4e3;
587
+ var COLLECT_JITTER_MS = 2e4;
583
588
  async function mapSettled(items, limit, task, deadline) {
584
589
  const results = new Array(items.length);
585
590
  let cursor = 0;
@@ -601,6 +606,14 @@ async function mapSettled(items, limit, task, deadline) {
601
606
  await (stop ? Promise.race([pool, stop]) : pool);
602
607
  return results;
603
608
  }
609
+ function duePeers(peers, now, floorMs, random) {
610
+ if (floorMs <= 0) return [...peers];
611
+ return peers.filter((peer) => {
612
+ if (peer.last_attempt_at === null) return true;
613
+ const jitter = (random() - 0.5) * COLLECT_JITTER_MS;
614
+ return now - peer.last_attempt_at >= floorMs + jitter;
615
+ });
616
+ }
604
617
  function isUnreachable(message) {
605
618
  return /Host is down|No route to host|Connection refused|Connection timed out|Connection closed|Operation timed out|Network is unreachable|Name or service not known|Could not resolve hostname|timed out after/i.test(
606
619
  message
@@ -615,11 +628,12 @@ function stripInvocation(message) {
615
628
  function normalizeFailure(message) {
616
629
  return stripInvocation(message).replace(/\s+/g, " ").trim();
617
630
  }
618
- async function collect(store, channel, now = Date.now(), deadline, mux = tmux) {
631
+ async function collect(store, channel, now = Date.now(), options = {}) {
632
+ const { deadline, mux = tmux, floorMs = 0, random = Math.random } = options;
619
633
  const results = [];
620
634
  let timer;
621
635
  try {
622
- const peers = store.peers();
636
+ const peers = duePeers(store.peers(), now, floorMs, random);
623
637
  const bounded = deadline ?? new Promise((resolve) => {
624
638
  timer = setTimeout(resolve, COLLECT_DEADLINE_MS);
625
639
  timer.unref?.();
@@ -787,9 +801,9 @@ function status(store, identity, now = Date.now()) {
787
801
  }))
788
802
  };
789
803
  }
790
- async function statusWithCollect(store, identity, now = Date.now(), channel = ssh, mux = tmux) {
804
+ async function statusWithCollect(store, identity, now = Date.now(), channel = ssh, options = {}) {
791
805
  try {
792
- await collect(store, channel, now, void 0, mux);
806
+ await collect(store, channel, now, options);
793
807
  } catch {
794
808
  }
795
809
  return status(store, identity, now);