terminalhire 0.11.0 → 0.11.1

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.
@@ -4278,6 +4278,35 @@ async function defaultListPendingInvites(deps = {}) {
4278
4278
  const invites = listed.intros.filter((it) => it && it.role === "incoming" && it.status === "pending" && it.counterpartyLogin).map((it) => ({ login: it.counterpartyLogin }));
4279
4279
  return { status: "ok", invites };
4280
4280
  }
4281
+ function relativeTime(then, now = /* @__PURE__ */ new Date()) {
4282
+ const t = new Date(then).getTime();
4283
+ if (Number.isNaN(t)) return "";
4284
+ const deltaMs = Math.max(0, now.getTime() - t);
4285
+ const sec = Math.floor(deltaMs / 1e3);
4286
+ if (sec < 60) return "just now";
4287
+ const min = Math.floor(sec / 60);
4288
+ if (min < 60) return `${Math.max(1, min)}m ago`;
4289
+ const hr = Math.floor(min / 60);
4290
+ if (hr < 24) return `${hr}h ago`;
4291
+ const day = Math.floor(hr / 24);
4292
+ if (day < 7) return `${day}d ago`;
4293
+ const wk = Math.floor(day / 7);
4294
+ if (wk < 5) return `${wk}w ago`;
4295
+ return "a while ago";
4296
+ }
4297
+ function formatPresence(presence, now = /* @__PURE__ */ new Date()) {
4298
+ if (!presence) return "\u25CB not on chat yet";
4299
+ const share = presence.shareActivity === true;
4300
+ const seenMs = share && presence.lastSeen ? new Date(presence.lastSeen).getTime() : NaN;
4301
+ const hasSeen = !Number.isNaN(seenMs);
4302
+ const fresh = hasSeen && now.getTime() - seenMs <= ACTIVE_WINDOW_MS;
4303
+ if (share && presence.optin === true && fresh) return "\u25CF active now";
4304
+ if (share && hasSeen) {
4305
+ const rel = relativeTime(presence.lastSeen, now);
4306
+ return rel ? `\u25D0 reachable \xB7 seen ${rel}` : "\u25D0 reachable";
4307
+ }
4308
+ return "\u25D0 reachable";
4309
+ }
4281
4310
  var CHAT_BASE2, GH_SESSION_COOKIE2, ANSI_CSI, ANSI_OSC, ANSI_OTHER, C0_C1_DEL, CHAT_DISCLOSURE, CHAT_AT_REST, CHAT_CODE_OF_CONDUCT, CHAT_MIN_AGE, ACTIVE_WINDOW_MS;
4282
4311
  var init_jpi_chat = __esm({
4283
4312
  "bin/jpi-chat.js"() {
@@ -4404,7 +4433,7 @@ function renderInbox(items, invites = []) {
4404
4433
  lines.push(" (no accepted connections yet \u2014 request one: terminalhire intro <login>)");
4405
4434
  } else {
4406
4435
  for (const it of items) {
4407
- const dot = it.online ? "\u25CF" : "\u25CB";
4436
+ const dot = formatPresence(it.presence).charAt(0);
4408
4437
  const unread = it.unread > 0 ? `\u2709 ${it.unread}` : "\u2014";
4409
4438
  const login = `@${sanitizeLine(it.login)}`;
4410
4439
  const stamp = sanitizeLine(it.lastStamp || "");
@@ -4419,13 +4448,12 @@ function renderInbox(items, invites = []) {
4419
4448
  return lines.join("\n") + "\n";
4420
4449
  }
4421
4450
  function renderThread(state) {
4422
- const { peerLogin, online, safety, messages, total } = state;
4451
+ const { peerLogin, presence, safety, messages, total } = state;
4423
4452
  const safePeer = sanitizeLine(peerLogin);
4424
- const dot = online ? "\u25CF" : "\u25CB";
4425
- const presence = online ? "online" : "offline";
4453
+ const status = formatPresence(presence);
4426
4454
  const lines = [];
4427
4455
  lines.push(
4428
- ` @${safePeer} ${dot} ${presence}` + (safety ? ` \xB7 safety# ${sanitizeLine(safety)}` : "")
4456
+ ` @${safePeer} ${status}` + (safety ? ` \xB7 safety# ${sanitizeLine(safety)}` : "")
4429
4457
  );
4430
4458
  lines.push(" " + "\u2500".repeat(64));
4431
4459
  if (!messages || messages.length === 0) {
@@ -4475,21 +4503,6 @@ function writeProblem(output, result, target) {
4475
4503
  return "error";
4476
4504
  }
4477
4505
  }
4478
- async function readPresence(client, peerLogin) {
4479
- if (typeof client.getPeerPresence !== "function") return false;
4480
- try {
4481
- return await client.getPeerPresence(peerLogin) != null;
4482
- } catch {
4483
- return false;
4484
- }
4485
- }
4486
- async function clearPresence(client) {
4487
- if (typeof client.heartbeat !== "function") return;
4488
- try {
4489
- await client.heartbeat(false);
4490
- } catch {
4491
- }
4492
- }
4493
4506
  async function gateDisclosure(ensureDisclosure, input, output) {
4494
4507
  const ack = await ensureDisclosure({ input, output });
4495
4508
  if (!ack.acknowledged) {
@@ -4537,16 +4550,14 @@ async function runInbox(opts = {}) {
4537
4550
  (m) => m.senderLogin === conn.peerLogin && (!cursor || m.createdAt > cursor)
4538
4551
  ).length;
4539
4552
  const last = messages.length > 0 ? messages[messages.length - 1] : null;
4540
- const online = await readPresence(client, conn.peerLogin);
4541
4553
  items.push({
4542
4554
  login: conn.peerLogin,
4543
- online,
4555
+ presence: REACHABLE_DISPLAY,
4544
4556
  unread,
4545
4557
  lastStamp: last ? formatStamp(last.createdAt) : "",
4546
4558
  preview: last ? last.plaintext : ""
4547
4559
  });
4548
4560
  }
4549
- await clearPresence(client);
4550
4561
  output.write(renderInbox(items, invites));
4551
4562
  return { ok: true, count: items.length, invites: invites.length };
4552
4563
  }
@@ -4589,7 +4600,6 @@ async function runReadThread(opts = {}) {
4589
4600
  }
4590
4601
  const total = messages.length;
4591
4602
  const shownMessages = all ? messages : messages.slice(Math.max(0, total - limit));
4592
- const online = await readPresence(client, peerLogin);
4593
4603
  let safety = "";
4594
4604
  if (typeof client.getSafetyNumber === "function") {
4595
4605
  try {
@@ -4598,8 +4608,7 @@ async function runReadThread(opts = {}) {
4598
4608
  safety = "";
4599
4609
  }
4600
4610
  }
4601
- await clearPresence(client);
4602
- output.write(renderThread({ peerLogin, online, safety, messages: shownMessages, total }));
4611
+ output.write(renderThread({ peerLogin, presence: REACHABLE_DISPLAY, safety, messages: shownMessages, total }));
4603
4612
  if (total > 0) {
4604
4613
  const newest = messages[total - 1];
4605
4614
  if (newest && newest.createdAt) {
@@ -4672,7 +4681,7 @@ async function runSend(opts = {}) {
4672
4681
  );
4673
4682
  return { ok: true };
4674
4683
  }
4675
- var CHAT_BASE3, GH_SESSION_COOKIE3, TERMINALHIRE_DIR5, READS_FILE, INDEX_CACHE_FILE;
4684
+ var CHAT_BASE3, GH_SESSION_COOKIE3, TERMINALHIRE_DIR5, READS_FILE, INDEX_CACHE_FILE, REACHABLE_DISPLAY;
4676
4685
  var init_jpi_chat_read = __esm({
4677
4686
  "bin/jpi-chat-read.js"() {
4678
4687
  init_chat_client();
@@ -4683,6 +4692,7 @@ var init_jpi_chat_read = __esm({
4683
4692
  TERMINALHIRE_DIR5 = join8(homedir7(), ".terminalhire");
4684
4693
  READS_FILE = join8(TERMINALHIRE_DIR5, "chat-reads.json");
4685
4694
  INDEX_CACHE_FILE = join8(TERMINALHIRE_DIR5, "index-cache.json");
4695
+ REACHABLE_DISPLAY = { shareActivity: false, optin: false, lastSeen: null };
4686
4696
  }
4687
4697
  });
4688
4698
  init_jpi_chat_read();
@@ -4558,7 +4558,7 @@ function renderInbox(items, invites = []) {
4558
4558
  lines.push(" (no accepted connections yet \u2014 request one: terminalhire intro <login>)");
4559
4559
  } else {
4560
4560
  for (const it of items) {
4561
- const dot = it.online ? "\u25CF" : "\u25CB";
4561
+ const dot = formatPresence(it.presence).charAt(0);
4562
4562
  const unread = it.unread > 0 ? `\u2709 ${it.unread}` : "\u2014";
4563
4563
  const login = `@${sanitizeLine(it.login)}`;
4564
4564
  const stamp = sanitizeLine(it.lastStamp || "");
@@ -4573,13 +4573,12 @@ function renderInbox(items, invites = []) {
4573
4573
  return lines.join("\n") + "\n";
4574
4574
  }
4575
4575
  function renderThread(state) {
4576
- const { peerLogin, online, safety, messages, total } = state;
4576
+ const { peerLogin, presence, safety, messages, total } = state;
4577
4577
  const safePeer = sanitizeLine(peerLogin);
4578
- const dot = online ? "\u25CF" : "\u25CB";
4579
- const presence = online ? "online" : "offline";
4578
+ const status = formatPresence(presence);
4580
4579
  const lines = [];
4581
4580
  lines.push(
4582
- ` @${safePeer} ${dot} ${presence}` + (safety ? ` \xB7 safety# ${sanitizeLine(safety)}` : "")
4581
+ ` @${safePeer} ${status}` + (safety ? ` \xB7 safety# ${sanitizeLine(safety)}` : "")
4583
4582
  );
4584
4583
  lines.push(" " + "\u2500".repeat(64));
4585
4584
  if (!messages || messages.length === 0) {
@@ -4629,21 +4628,6 @@ function writeProblem(output, result, target) {
4629
4628
  return "error";
4630
4629
  }
4631
4630
  }
4632
- async function readPresence(client, peerLogin) {
4633
- if (typeof client.getPeerPresence !== "function") return false;
4634
- try {
4635
- return await client.getPeerPresence(peerLogin) != null;
4636
- } catch {
4637
- return false;
4638
- }
4639
- }
4640
- async function clearPresence(client) {
4641
- if (typeof client.heartbeat !== "function") return;
4642
- try {
4643
- await client.heartbeat(false);
4644
- } catch {
4645
- }
4646
- }
4647
4631
  async function gateDisclosure(ensureDisclosure, input, output) {
4648
4632
  const ack = await ensureDisclosure({ input, output });
4649
4633
  if (!ack.acknowledged) {
@@ -4691,16 +4675,14 @@ async function runInbox(opts = {}) {
4691
4675
  (m) => m.senderLogin === conn.peerLogin && (!cursor || m.createdAt > cursor)
4692
4676
  ).length;
4693
4677
  const last = messages.length > 0 ? messages[messages.length - 1] : null;
4694
- const online = await readPresence(client, conn.peerLogin);
4695
4678
  items.push({
4696
4679
  login: conn.peerLogin,
4697
- online,
4680
+ presence: REACHABLE_DISPLAY,
4698
4681
  unread,
4699
4682
  lastStamp: last ? formatStamp(last.createdAt) : "",
4700
4683
  preview: last ? last.plaintext : ""
4701
4684
  });
4702
4685
  }
4703
- await clearPresence(client);
4704
4686
  output.write(renderInbox(items, invites));
4705
4687
  return { ok: true, count: items.length, invites: invites.length };
4706
4688
  }
@@ -4743,7 +4725,6 @@ async function runReadThread(opts = {}) {
4743
4725
  }
4744
4726
  const total = messages.length;
4745
4727
  const shownMessages = all ? messages : messages.slice(Math.max(0, total - limit));
4746
- const online = await readPresence(client, peerLogin);
4747
4728
  let safety = "";
4748
4729
  if (typeof client.getSafetyNumber === "function") {
4749
4730
  try {
@@ -4752,8 +4733,7 @@ async function runReadThread(opts = {}) {
4752
4733
  safety = "";
4753
4734
  }
4754
4735
  }
4755
- await clearPresence(client);
4756
- output.write(renderThread({ peerLogin, online, safety, messages: shownMessages, total }));
4736
+ output.write(renderThread({ peerLogin, presence: REACHABLE_DISPLAY, safety, messages: shownMessages, total }));
4757
4737
  if (total > 0) {
4758
4738
  const newest = messages[total - 1];
4759
4739
  if (newest && newest.createdAt) {
@@ -4826,7 +4806,7 @@ async function runSend(opts = {}) {
4826
4806
  );
4827
4807
  return { ok: true };
4828
4808
  }
4829
- var CHAT_BASE2, GH_SESSION_COOKIE2, TERMINALHIRE_DIR6, READS_FILE, INDEX_CACHE_FILE;
4809
+ var CHAT_BASE2, GH_SESSION_COOKIE2, TERMINALHIRE_DIR6, READS_FILE, INDEX_CACHE_FILE, REACHABLE_DISPLAY;
4830
4810
  var init_jpi_chat_read = __esm({
4831
4811
  "bin/jpi-chat-read.js"() {
4832
4812
  "use strict";
@@ -4838,6 +4818,7 @@ var init_jpi_chat_read = __esm({
4838
4818
  TERMINALHIRE_DIR6 = join8(homedir7(), ".terminalhire");
4839
4819
  READS_FILE = join8(TERMINALHIRE_DIR6, "chat-reads.json");
4840
4820
  INDEX_CACHE_FILE = join8(TERMINALHIRE_DIR6, "index-cache.json");
4821
+ REACHABLE_DISPLAY = { shareActivity: false, optin: false, lastSeen: null };
4841
4822
  }
4842
4823
  });
4843
4824
 
@@ -10689,7 +10689,7 @@ function renderInbox(items, invites = []) {
10689
10689
  lines.push(" (no accepted connections yet \u2014 request one: terminalhire intro <login>)");
10690
10690
  } else {
10691
10691
  for (const it of items) {
10692
- const dot = it.online ? "\u25CF" : "\u25CB";
10692
+ const dot = formatPresence(it.presence).charAt(0);
10693
10693
  const unread = it.unread > 0 ? `\u2709 ${it.unread}` : "\u2014";
10694
10694
  const login = `@${sanitizeLine(it.login)}`;
10695
10695
  const stamp = sanitizeLine(it.lastStamp || "");
@@ -10704,13 +10704,12 @@ function renderInbox(items, invites = []) {
10704
10704
  return lines.join("\n") + "\n";
10705
10705
  }
10706
10706
  function renderThread(state) {
10707
- const { peerLogin, online, safety, messages, total } = state;
10707
+ const { peerLogin, presence, safety, messages, total } = state;
10708
10708
  const safePeer = sanitizeLine(peerLogin);
10709
- const dot = online ? "\u25CF" : "\u25CB";
10710
- const presence = online ? "online" : "offline";
10709
+ const status = formatPresence(presence);
10711
10710
  const lines = [];
10712
10711
  lines.push(
10713
- ` @${safePeer} ${dot} ${presence}` + (safety ? ` \xB7 safety# ${sanitizeLine(safety)}` : "")
10712
+ ` @${safePeer} ${status}` + (safety ? ` \xB7 safety# ${sanitizeLine(safety)}` : "")
10714
10713
  );
10715
10714
  lines.push(" " + "\u2500".repeat(64));
10716
10715
  if (!messages || messages.length === 0) {
@@ -10760,21 +10759,6 @@ function writeProblem(output, result, target) {
10760
10759
  return "error";
10761
10760
  }
10762
10761
  }
10763
- async function readPresence(client, peerLogin) {
10764
- if (typeof client.getPeerPresence !== "function") return false;
10765
- try {
10766
- return await client.getPeerPresence(peerLogin) != null;
10767
- } catch {
10768
- return false;
10769
- }
10770
- }
10771
- async function clearPresence(client) {
10772
- if (typeof client.heartbeat !== "function") return;
10773
- try {
10774
- await client.heartbeat(false);
10775
- } catch {
10776
- }
10777
- }
10778
10762
  async function gateDisclosure(ensureDisclosure, input, output) {
10779
10763
  const ack = await ensureDisclosure({ input, output });
10780
10764
  if (!ack.acknowledged) {
@@ -10822,16 +10806,14 @@ async function runInbox(opts = {}) {
10822
10806
  (m) => m.senderLogin === conn.peerLogin && (!cursor || m.createdAt > cursor)
10823
10807
  ).length;
10824
10808
  const last = messages.length > 0 ? messages[messages.length - 1] : null;
10825
- const online = await readPresence(client, conn.peerLogin);
10826
10809
  items.push({
10827
10810
  login: conn.peerLogin,
10828
- online,
10811
+ presence: REACHABLE_DISPLAY,
10829
10812
  unread,
10830
10813
  lastStamp: last ? formatStamp(last.createdAt) : "",
10831
10814
  preview: last ? last.plaintext : ""
10832
10815
  });
10833
10816
  }
10834
- await clearPresence(client);
10835
10817
  output.write(renderInbox(items, invites));
10836
10818
  return { ok: true, count: items.length, invites: invites.length };
10837
10819
  }
@@ -10874,7 +10856,6 @@ async function runReadThread(opts = {}) {
10874
10856
  }
10875
10857
  const total = messages.length;
10876
10858
  const shownMessages = all ? messages : messages.slice(Math.max(0, total - limit));
10877
- const online = await readPresence(client, peerLogin);
10878
10859
  let safety = "";
10879
10860
  if (typeof client.getSafetyNumber === "function") {
10880
10861
  try {
@@ -10883,8 +10864,7 @@ async function runReadThread(opts = {}) {
10883
10864
  safety = "";
10884
10865
  }
10885
10866
  }
10886
- await clearPresence(client);
10887
- output.write(renderThread({ peerLogin, online, safety, messages: shownMessages, total }));
10867
+ output.write(renderThread({ peerLogin, presence: REACHABLE_DISPLAY, safety, messages: shownMessages, total }));
10888
10868
  if (total > 0) {
10889
10869
  const newest = messages[total - 1];
10890
10870
  if (newest && newest.createdAt) {
@@ -10957,7 +10937,7 @@ async function runSend(opts = {}) {
10957
10937
  );
10958
10938
  return { ok: true };
10959
10939
  }
10960
- var CHAT_BASE2, GH_SESSION_COOKIE4, TERMINALHIRE_DIR13, READS_FILE, INDEX_CACHE_FILE5;
10940
+ var CHAT_BASE2, GH_SESSION_COOKIE4, TERMINALHIRE_DIR13, READS_FILE, INDEX_CACHE_FILE5, REACHABLE_DISPLAY;
10961
10941
  var init_jpi_chat_read = __esm({
10962
10942
  "bin/jpi-chat-read.js"() {
10963
10943
  "use strict";
@@ -10969,6 +10949,7 @@ var init_jpi_chat_read = __esm({
10969
10949
  TERMINALHIRE_DIR13 = join17(homedir16(), ".terminalhire");
10970
10950
  READS_FILE = join17(TERMINALHIRE_DIR13, "chat-reads.json");
10971
10951
  INDEX_CACHE_FILE5 = join17(TERMINALHIRE_DIR13, "index-cache.json");
10952
+ REACHABLE_DISPLAY = { shareActivity: false, optin: false, lastSeen: null };
10972
10953
  }
10973
10954
  });
10974
10955
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "terminalhire",
3
- "version": "0.11.0",
3
+ "version": "0.11.1",
4
4
  "description": "Local-first job matching for developers — ambient job matches in the Claude Code spinner. Matching runs on your machine; your profile never leaves it.",
5
5
  "repository": {
6
6
  "type": "git",