terminalhire 0.10.10 → 0.11.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.
@@ -5840,6 +5840,53 @@ var init_chatCrypto = __esm({
5840
5840
  }
5841
5841
  });
5842
5842
 
5843
+ // ../../packages/core/src/job-status.ts
5844
+ function recordClick(map, id) {
5845
+ const prev = map[id];
5846
+ if (prev?.clicked === true) return map;
5847
+ return { ...map, [id]: { ...prev, clicked: true } };
5848
+ }
5849
+ function setStatus(map, id, s, markedAt = (/* @__PURE__ */ new Date()).toISOString()) {
5850
+ const prev = map[id];
5851
+ return { ...map, [id]: { ...prev, status: s, markedAt } };
5852
+ }
5853
+ function funnelCounts(map) {
5854
+ const counts = { clicked: 0, applied: 0, saved: 0, dismissed: 0 };
5855
+ for (const key of Object.keys(map)) {
5856
+ const rec = map[key];
5857
+ if (rec.clicked === true) counts.clicked++;
5858
+ if (rec.status) counts[rec.status]++;
5859
+ }
5860
+ return counts;
5861
+ }
5862
+ function pageMatches(items, page, limit) {
5863
+ const lim = Math.max(1, Math.floor(limit));
5864
+ const total = items.length;
5865
+ const totalPages = Math.max(1, Math.ceil(total / lim));
5866
+ const clamped = Math.min(Math.max(1, Math.floor(page)), totalPages);
5867
+ const start = (clamped - 1) * lim;
5868
+ return {
5869
+ items: items.slice(start, start + lim),
5870
+ page: clamped,
5871
+ limit: lim,
5872
+ total,
5873
+ totalPages,
5874
+ hasPrev: clamped > 1,
5875
+ hasNext: clamped < totalPages
5876
+ };
5877
+ }
5878
+ function decorate(matches, statusMap) {
5879
+ return matches.map((m) => {
5880
+ const rec = statusMap[m.job.id];
5881
+ return rec ? { ...m, jobStatus: rec } : { ...m };
5882
+ });
5883
+ }
5884
+ var init_job_status = __esm({
5885
+ "../../packages/core/src/job-status.ts"() {
5886
+ "use strict";
5887
+ }
5888
+ });
5889
+
5843
5890
  // ../../packages/core/src/index.ts
5844
5891
  var src_exports = {};
5845
5892
  __export(src_exports, {
@@ -5881,6 +5928,7 @@ __export(src_exports, {
5881
5928
  computeAcceptanceCredential: () => computeAcceptanceCredential,
5882
5929
  computeAcceptanceCredentialPublic: () => computeAcceptanceCredentialPublic,
5883
5930
  coreTagsFromTitle: () => coreTagsFromTitle,
5931
+ decorate: () => decorate,
5884
5932
  decryptMessage: () => decryptMessage,
5885
5933
  deriveResumeTrend: () => deriveResumeTrend,
5886
5934
  deriveSharedKey: () => deriveSharedKey,
@@ -5891,6 +5939,7 @@ __export(src_exports, {
5891
5939
  fetchOwnedRepoTraction: () => fetchOwnedRepoTraction,
5892
5940
  fetchRepoRecency: () => fetchRepoRecency,
5893
5941
  flattenTiers: () => flattenTiers,
5942
+ funnelCounts: () => funnelCounts,
5894
5943
  generateIdentityKeypair: () => generateIdentityKeypair,
5895
5944
  getBuyer: () => getBuyer,
5896
5945
  githubBounties: () => githubBounties,
@@ -5909,12 +5958,15 @@ __export(src_exports, {
5909
5958
  match: () => match,
5910
5959
  normalize: () => normalize,
5911
5960
  opire: () => opire,
5961
+ pageMatches: () => pageMatches,
5912
5962
  passesMaturityGate: () => passesMaturityGate,
5913
5963
  personCardToJob: () => personCardToJob,
5914
5964
  projectCardToJob: () => projectCardToJob,
5965
+ recordClick: () => recordClick,
5915
5966
  rejectExtraIntroFields: () => rejectExtraIntroFields,
5916
5967
  revealIntroContacts: () => revealIntroContacts,
5917
5968
  safetyNumber: () => safetyNumber,
5969
+ setStatus: () => setStatus,
5918
5970
  tokenize: () => tokenize,
5919
5971
  validateGraph: () => validateGraph,
5920
5972
  validateIntroPayload: () => validateIntroPayload,
@@ -5935,6 +5987,7 @@ var init_src = __esm({
5935
5987
  init_intro();
5936
5988
  init_directoryThreshold();
5937
5989
  init_chatCrypto();
5990
+ init_job_status();
5938
5991
  }
5939
5992
  });
5940
5993
 
@@ -3749,6 +3749,13 @@ var init_chatCrypto = __esm({
3749
3749
  }
3750
3750
  });
3751
3751
 
3752
+ // ../../packages/core/src/job-status.ts
3753
+ var init_job_status = __esm({
3754
+ "../../packages/core/src/job-status.ts"() {
3755
+ "use strict";
3756
+ }
3757
+ });
3758
+
3752
3759
  // ../../packages/core/src/index.ts
3753
3760
  var init_src = __esm({
3754
3761
  "../../packages/core/src/index.ts"() {
@@ -3763,6 +3770,7 @@ var init_src = __esm({
3763
3770
  init_intro();
3764
3771
  init_directoryThreshold();
3765
3772
  init_chatCrypto();
3773
+ init_job_status();
3766
3774
  }
3767
3775
  });
3768
3776
 
@@ -4039,7 +4047,21 @@ function createChatClient(overrides) {
4039
4047
  const data = await res.json();
4040
4048
  const presence = data.presence ?? null;
4041
4049
  if (!presence) return null;
4042
- return { login: presence.login, lastSeen: presence.lastSeen };
4050
+ return {
4051
+ login: presence.login,
4052
+ lastSeen: presence.lastSeen ?? null,
4053
+ optin: presence.optin === true,
4054
+ shareActivity: presence.shareActivity === true
4055
+ };
4056
+ }
4057
+ async function setActivitySharing(share) {
4058
+ const res = await authedFetch("/api/chat/activity", {
4059
+ method: "POST",
4060
+ headers: { "Content-Type": "application/json" },
4061
+ body: JSON.stringify({ share })
4062
+ });
4063
+ if (!res.ok) throw new ChatRequestError("/api/chat/activity", res.status);
4064
+ await res.json();
4043
4065
  }
4044
4066
  async function applyBlock(login, action) {
4045
4067
  const target = login.trim();
@@ -4063,6 +4085,7 @@ function createChatClient(overrides) {
4063
4085
  pollMessages,
4064
4086
  heartbeat,
4065
4087
  getPeerPresence,
4088
+ setActivitySharing,
4066
4089
  blockPeer: (login) => applyBlock(login, "block"),
4067
4090
  unblock: (login) => applyBlock(login, "unblock"),
4068
4091
  getSafetyNumber
@@ -4151,6 +4174,7 @@ var init_config = __esm({
4151
4174
  peerConnectPrompted: false,
4152
4175
  resumePublishPrompted: false,
4153
4176
  chatDisclosureAck: false,
4177
+ chatShareActivity: false,
4154
4178
  inboundNudgeMuted: false,
4155
4179
  inboundNudgeDisclosed: false
4156
4180
  };
@@ -4159,6 +4183,9 @@ var init_config = __esm({
4159
4183
 
4160
4184
  // bin/jpi-chat.js
4161
4185
  import { createInterface } from "readline";
4186
+ import { existsSync as existsSync6, readFileSync as readFileSync7 } from "fs";
4187
+ import { homedir as homedir6 } from "os";
4188
+ import { join as join7 } from "path";
4162
4189
  function sanitizeLine(text) {
4163
4190
  return String(text).replace(ANSI_CSI, "").replace(ANSI_OSC, "").replace(ANSI_OTHER, "").replace(C0_C1_DEL, "");
4164
4191
  }
@@ -4251,7 +4278,7 @@ async function defaultListPendingInvites(deps = {}) {
4251
4278
  const invites = listed.intros.filter((it) => it && it.role === "incoming" && it.status === "pending" && it.counterpartyLogin).map((it) => ({ login: it.counterpartyLogin }));
4252
4279
  return { status: "ok", invites };
4253
4280
  }
4254
- 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;
4281
+ 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;
4255
4282
  var init_jpi_chat = __esm({
4256
4283
  "bin/jpi-chat.js"() {
4257
4284
  "use strict";
@@ -4268,20 +4295,21 @@ var init_jpi_chat = __esm({
4268
4295
  CHAT_AT_REST = "Your private key is encrypted against casual access, not full machine compromise.";
4269
4296
  CHAT_CODE_OF_CONDUCT = "Code of conduct: keep it professional \u2014 harassment, spam, or abuse gets you blocked and removed.";
4270
4297
  CHAT_MIN_AGE = "You must be at least 13 years old to use connections chat.";
4298
+ ACTIVE_WINDOW_MS = 2 * 60 * 1e3;
4271
4299
  }
4272
4300
  });
4273
4301
 
4274
4302
  // bin/jpi-chat-read.js
4275
- import { existsSync as existsSync6, mkdirSync as mkdirSync6, readFileSync as readFileSync7, writeFileSync as writeFileSync6 } from "fs";
4276
- import { homedir as homedir6 } from "os";
4277
- import { join as join7 } from "path";
4303
+ import { existsSync as existsSync7, mkdirSync as mkdirSync6, readFileSync as readFileSync8, writeFileSync as writeFileSync6 } from "fs";
4304
+ import { homedir as homedir7 } from "os";
4305
+ import { join as join8 } from "path";
4278
4306
  async function syncUnreadBadge(deps = {}) {
4279
4307
  const readCookie = deps.readCookie ?? readWebSessionCookie;
4280
4308
  const fetchImpl = deps.fetchImpl ?? globalThis.fetch;
4281
4309
  const cacheFile = deps.cacheFile ?? INDEX_CACHE_FILE;
4282
4310
  try {
4283
4311
  const cookie = readCookie();
4284
- if (!cookie || !existsSync6(cacheFile)) return;
4312
+ if (!cookie || !existsSync7(cacheFile)) return;
4285
4313
  const res = await fetchImpl(`${CHAT_BASE3}/api/chat/inbox`, {
4286
4314
  method: "GET",
4287
4315
  headers: { Cookie: `${GH_SESSION_COOKIE3}=${cookie}` },
@@ -4294,7 +4322,7 @@ async function syncUnreadBadge(deps = {}) {
4294
4322
  (sum, it) => sum + (it && typeof it.unreadCount === "number" && it.unreadCount > 0 ? it.unreadCount : 0),
4295
4323
  0
4296
4324
  );
4297
- const entry = JSON.parse(readFileSync7(cacheFile, "utf8"));
4325
+ const entry = JSON.parse(readFileSync8(cacheFile, "utf8"));
4298
4326
  entry.unreadChat = { count: total };
4299
4327
  writeFileSync6(cacheFile, JSON.stringify(entry), "utf8");
4300
4328
  } catch {
@@ -4302,8 +4330,8 @@ async function syncUnreadBadge(deps = {}) {
4302
4330
  }
4303
4331
  function readReadCursors() {
4304
4332
  try {
4305
- if (!existsSync6(READS_FILE)) return {};
4306
- const parsed = JSON.parse(readFileSync7(READS_FILE, "utf8"));
4333
+ if (!existsSync7(READS_FILE)) return {};
4334
+ const parsed = JSON.parse(readFileSync8(READS_FILE, "utf8"));
4307
4335
  if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) return {};
4308
4336
  const out = {};
4309
4337
  for (const [login, iso] of Object.entries(parsed)) {
@@ -4593,7 +4621,10 @@ async function runSend(opts = {}) {
4593
4621
  input = process.stdin,
4594
4622
  client = createChatClient(),
4595
4623
  resolveConnection = defaultResolveConnection,
4596
- ensureDisclosure = ensureChatDisclosure
4624
+ ensureDisclosure = ensureChatDisclosure,
4625
+ writeCursor = writeReadCursor,
4626
+ syncCursor = postReadCursor,
4627
+ syncBadge = syncUnreadBadge
4597
4628
  } = opts;
4598
4629
  const target = String(login ?? "").replace(/^@/, "").trim();
4599
4630
  const body = String(text ?? "").trim();
@@ -4619,6 +4650,19 @@ async function runSend(opts = {}) {
4619
4650
  `);
4620
4651
  return { ok: false, reason: "error" };
4621
4652
  }
4653
+ const nowIso = (/* @__PURE__ */ new Date()).toISOString();
4654
+ try {
4655
+ writeCursor(peerLogin, nowIso);
4656
+ } catch {
4657
+ }
4658
+ await syncCursor(peerLogin, nowIso);
4659
+ await syncBadge();
4660
+ if (typeof client.heartbeat === "function") {
4661
+ try {
4662
+ await client.heartbeat(false);
4663
+ } catch {
4664
+ }
4665
+ }
4622
4666
  output.write(
4623
4667
  `
4624
4668
  Sent to @${sanitizeLine(peerLogin)}: ${sanitizeLine(body)}
@@ -4636,9 +4680,9 @@ var init_jpi_chat_read = __esm({
4636
4680
  init_jpi_chat();
4637
4681
  CHAT_BASE3 = process.env["TERMINALHIRE_API_URL"] || "https://www.terminalhire.com";
4638
4682
  GH_SESSION_COOKIE3 = "__jpi_gh_session";
4639
- TERMINALHIRE_DIR5 = join7(homedir6(), ".terminalhire");
4640
- READS_FILE = join7(TERMINALHIRE_DIR5, "chat-reads.json");
4641
- INDEX_CACHE_FILE = join7(TERMINALHIRE_DIR5, "index-cache.json");
4683
+ TERMINALHIRE_DIR5 = join8(homedir7(), ".terminalhire");
4684
+ READS_FILE = join8(TERMINALHIRE_DIR5, "chat-reads.json");
4685
+ INDEX_CACHE_FILE = join8(TERMINALHIRE_DIR5, "index-cache.json");
4642
4686
  }
4643
4687
  });
4644
4688
  init_jpi_chat_read();