terminalhire 0.10.3 → 0.10.5
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/bin/jpi-chat-read.js +22 -1
- package/dist/bin/jpi-chat.js +28 -7
- package/dist/bin/jpi-dispatch.js +62 -20
- package/dist/bin/jpi-intro.js +8 -6
- package/dist/bin/jpi-refresh.js +20 -1
- package/dist/bin/jpi.js +18 -2
- package/dist/src/intro.js +8 -6
- package/package.json +1 -1
|
@@ -4290,6 +4290,22 @@ function writeReadCursor(login, iso, deps = {}) {
|
|
|
4290
4290
|
mkdirSync6(TERMINALHIRE_DIR5, { recursive: true });
|
|
4291
4291
|
writeFileSync6(READS_FILE, JSON.stringify(cursors, null, 2), { mode: 384, encoding: "utf8" });
|
|
4292
4292
|
}
|
|
4293
|
+
async function postReadCursor(peerLogin, lastReadAt, deps = {}) {
|
|
4294
|
+
const readCookie = deps.readCookie ?? readWebSessionCookie;
|
|
4295
|
+
const cookie = readCookie();
|
|
4296
|
+
if (!cookie) return;
|
|
4297
|
+
try {
|
|
4298
|
+
await fetch(`${CHAT_BASE3}/api/chat/read-cursor`, {
|
|
4299
|
+
method: "POST",
|
|
4300
|
+
headers: { "Content-Type": "application/json", Cookie: `${GH_SESSION_COOKIE3}=${cookie}` },
|
|
4301
|
+
body: JSON.stringify({ peerLogin, lastReadAt }),
|
|
4302
|
+
// Best-effort cross-device sync — not latency-sensitive, so a short bound
|
|
4303
|
+
// keeps a cold/unreachable server from stalling the reader's exit.
|
|
4304
|
+
signal: AbortSignal.timeout(2500)
|
|
4305
|
+
});
|
|
4306
|
+
} catch {
|
|
4307
|
+
}
|
|
4308
|
+
}
|
|
4293
4309
|
function formatClock(iso) {
|
|
4294
4310
|
const d = new Date(iso);
|
|
4295
4311
|
if (Number.isNaN(d.getTime())) return "--:--";
|
|
@@ -4469,6 +4485,7 @@ async function runReadThread(opts = {}) {
|
|
|
4469
4485
|
client = createChatClient(),
|
|
4470
4486
|
resolveConnection = defaultResolveConnection,
|
|
4471
4487
|
writeCursor = writeReadCursor,
|
|
4488
|
+
syncCursor = postReadCursor,
|
|
4472
4489
|
ensureDisclosure = ensureChatDisclosure
|
|
4473
4490
|
} = opts;
|
|
4474
4491
|
const target = String(login ?? "").replace(/^@/, "").trim();
|
|
@@ -4514,6 +4531,7 @@ async function runReadThread(opts = {}) {
|
|
|
4514
4531
|
writeCursor(peerLogin, newest.createdAt);
|
|
4515
4532
|
} catch {
|
|
4516
4533
|
}
|
|
4534
|
+
await syncCursor(peerLogin, newest.createdAt);
|
|
4517
4535
|
}
|
|
4518
4536
|
}
|
|
4519
4537
|
return { ok: true, shown: shownMessages.length, total };
|
|
@@ -4561,12 +4579,14 @@ async function runSend(opts = {}) {
|
|
|
4561
4579
|
);
|
|
4562
4580
|
return { ok: true };
|
|
4563
4581
|
}
|
|
4564
|
-
var CHAT_BASE3, TERMINALHIRE_DIR5, READS_FILE;
|
|
4582
|
+
var CHAT_BASE3, GH_SESSION_COOKIE3, TERMINALHIRE_DIR5, READS_FILE;
|
|
4565
4583
|
var init_jpi_chat_read = __esm({
|
|
4566
4584
|
"bin/jpi-chat-read.js"() {
|
|
4567
4585
|
init_chat_client();
|
|
4586
|
+
init_web_session();
|
|
4568
4587
|
init_jpi_chat();
|
|
4569
4588
|
CHAT_BASE3 = process.env["TERMINALHIRE_API_URL"] || "https://www.terminalhire.com";
|
|
4589
|
+
GH_SESSION_COOKIE3 = "__jpi_gh_session";
|
|
4570
4590
|
TERMINALHIRE_DIR5 = join7(homedir6(), ".terminalhire");
|
|
4571
4591
|
READS_FILE = join7(TERMINALHIRE_DIR5, "chat-reads.json");
|
|
4572
4592
|
}
|
|
@@ -4575,6 +4595,7 @@ init_jpi_chat_read();
|
|
|
4575
4595
|
export {
|
|
4576
4596
|
formatClock,
|
|
4577
4597
|
formatStamp,
|
|
4598
|
+
postReadCursor,
|
|
4578
4599
|
readReadCursors,
|
|
4579
4600
|
renderInbox,
|
|
4580
4601
|
renderThread,
|
package/dist/bin/jpi-chat.js
CHANGED
|
@@ -4167,6 +4167,7 @@ var jpi_chat_read_exports = {};
|
|
|
4167
4167
|
__export(jpi_chat_read_exports, {
|
|
4168
4168
|
formatClock: () => formatClock,
|
|
4169
4169
|
formatStamp: () => formatStamp,
|
|
4170
|
+
postReadCursor: () => postReadCursor,
|
|
4170
4171
|
readReadCursors: () => readReadCursors,
|
|
4171
4172
|
renderInbox: () => renderInbox,
|
|
4172
4173
|
renderThread: () => renderThread,
|
|
@@ -4199,6 +4200,22 @@ function writeReadCursor(login, iso, deps = {}) {
|
|
|
4199
4200
|
mkdirSync6(TERMINALHIRE_DIR5, { recursive: true });
|
|
4200
4201
|
writeFileSync6(READS_FILE, JSON.stringify(cursors, null, 2), { mode: 384, encoding: "utf8" });
|
|
4201
4202
|
}
|
|
4203
|
+
async function postReadCursor(peerLogin, lastReadAt, deps = {}) {
|
|
4204
|
+
const readCookie = deps.readCookie ?? readWebSessionCookie;
|
|
4205
|
+
const cookie = readCookie();
|
|
4206
|
+
if (!cookie) return;
|
|
4207
|
+
try {
|
|
4208
|
+
await fetch(`${CHAT_BASE2}/api/chat/read-cursor`, {
|
|
4209
|
+
method: "POST",
|
|
4210
|
+
headers: { "Content-Type": "application/json", Cookie: `${GH_SESSION_COOKIE2}=${cookie}` },
|
|
4211
|
+
body: JSON.stringify({ peerLogin, lastReadAt }),
|
|
4212
|
+
// Best-effort cross-device sync — not latency-sensitive, so a short bound
|
|
4213
|
+
// keeps a cold/unreachable server from stalling the reader's exit.
|
|
4214
|
+
signal: AbortSignal.timeout(2500)
|
|
4215
|
+
});
|
|
4216
|
+
} catch {
|
|
4217
|
+
}
|
|
4218
|
+
}
|
|
4202
4219
|
function formatClock(iso) {
|
|
4203
4220
|
const d = new Date(iso);
|
|
4204
4221
|
if (Number.isNaN(d.getTime())) return "--:--";
|
|
@@ -4378,6 +4395,7 @@ async function runReadThread(opts = {}) {
|
|
|
4378
4395
|
client = createChatClient(),
|
|
4379
4396
|
resolveConnection = defaultResolveConnection,
|
|
4380
4397
|
writeCursor = writeReadCursor,
|
|
4398
|
+
syncCursor = postReadCursor,
|
|
4381
4399
|
ensureDisclosure = ensureChatDisclosure
|
|
4382
4400
|
} = opts;
|
|
4383
4401
|
const target = String(login ?? "").replace(/^@/, "").trim();
|
|
@@ -4423,6 +4441,7 @@ async function runReadThread(opts = {}) {
|
|
|
4423
4441
|
writeCursor(peerLogin, newest.createdAt);
|
|
4424
4442
|
} catch {
|
|
4425
4443
|
}
|
|
4444
|
+
await syncCursor(peerLogin, newest.createdAt);
|
|
4426
4445
|
}
|
|
4427
4446
|
}
|
|
4428
4447
|
return { ok: true, shown: shownMessages.length, total };
|
|
@@ -4470,13 +4489,15 @@ async function runSend(opts = {}) {
|
|
|
4470
4489
|
);
|
|
4471
4490
|
return { ok: true };
|
|
4472
4491
|
}
|
|
4473
|
-
var CHAT_BASE2, TERMINALHIRE_DIR5, READS_FILE;
|
|
4492
|
+
var CHAT_BASE2, GH_SESSION_COOKIE2, TERMINALHIRE_DIR5, READS_FILE;
|
|
4474
4493
|
var init_jpi_chat_read = __esm({
|
|
4475
4494
|
"bin/jpi-chat-read.js"() {
|
|
4476
4495
|
"use strict";
|
|
4477
4496
|
init_chat_client();
|
|
4497
|
+
init_web_session();
|
|
4478
4498
|
init_jpi_chat();
|
|
4479
4499
|
CHAT_BASE2 = process.env["TERMINALHIRE_API_URL"] || "https://www.terminalhire.com";
|
|
4500
|
+
GH_SESSION_COOKIE2 = "__jpi_gh_session";
|
|
4480
4501
|
TERMINALHIRE_DIR5 = join7(homedir6(), ".terminalhire");
|
|
4481
4502
|
READS_FILE = join7(TERMINALHIRE_DIR5, "chat-reads.json");
|
|
4482
4503
|
}
|
|
@@ -4538,7 +4559,7 @@ async function fetchIntroList(deps = {}) {
|
|
|
4538
4559
|
try {
|
|
4539
4560
|
res = await fetchImpl(`${CHAT_BASE3}/api/intro/list`, {
|
|
4540
4561
|
method: "GET",
|
|
4541
|
-
headers: { Cookie: `${
|
|
4562
|
+
headers: { Cookie: `${GH_SESSION_COOKIE3}=${cookie}` },
|
|
4542
4563
|
signal: AbortSignal.timeout(1e4)
|
|
4543
4564
|
});
|
|
4544
4565
|
} catch (err) {
|
|
@@ -4699,9 +4720,9 @@ async function runChatPane(opts = {}) {
|
|
|
4699
4720
|
output.write(
|
|
4700
4721
|
`
|
|
4701
4722
|
@${peerLogin} isn't reachable for chat yet.
|
|
4702
|
-
|
|
4703
|
-
|
|
4704
|
-
terminalhire chat
|
|
4723
|
+
Chat is end-to-end encrypted, so they need to open chat once to
|
|
4724
|
+
publish their key. As soon as they run
|
|
4725
|
+
terminalhire chat
|
|
4705
4726
|
on their side, messages will flow.
|
|
4706
4727
|
|
|
4707
4728
|
`
|
|
@@ -5080,14 +5101,14 @@ async function run() {
|
|
|
5080
5101
|
process.exit(1);
|
|
5081
5102
|
}
|
|
5082
5103
|
}
|
|
5083
|
-
var CHAT_BASE3,
|
|
5104
|
+
var CHAT_BASE3, GH_SESSION_COOKIE3, HIDE_CURSOR, SHOW_CURSOR, CLEAR, KEY_CTRL_C, KEY_CTRL_S, KEY_ENTER_A, KEY_ENTER_B, KEY_BACKSPACE_A, KEY_BACKSPACE_B, MAX_INPUT_LEN, ANSI_CSI, ANSI_OSC, ANSI_OTHER, C0_C1_DEL, CHAT_DISCLOSURE, CHAT_AT_REST, CHAT_CODE_OF_CONDUCT, CHAT_MIN_AGE, DEPOSIT_CTA;
|
|
5084
5105
|
var init_jpi_chat = __esm({
|
|
5085
5106
|
"bin/jpi-chat.js"() {
|
|
5086
5107
|
init_chat_client();
|
|
5087
5108
|
init_config();
|
|
5088
5109
|
init_web_session();
|
|
5089
5110
|
CHAT_BASE3 = process.env["TERMINALHIRE_API_URL"] || "https://www.terminalhire.com";
|
|
5090
|
-
|
|
5111
|
+
GH_SESSION_COOKIE3 = "__jpi_gh_session";
|
|
5091
5112
|
HIDE_CURSOR = "\x1B[?25l";
|
|
5092
5113
|
SHOW_CURSOR = "\x1B[?25h";
|
|
5093
5114
|
CLEAR = "\x1B[2J\x1B[H";
|
package/dist/bin/jpi-dispatch.js
CHANGED
|
@@ -9634,7 +9634,7 @@ function defaultIntroDeps() {
|
|
|
9634
9634
|
try {
|
|
9635
9635
|
const { readProfile: readProfile2 } = await Promise.resolve().then(() => (init_profile(), profile_exports));
|
|
9636
9636
|
const profile = await readProfile2();
|
|
9637
|
-
return { displayName: profile?.displayName
|
|
9637
|
+
return { displayName: profile?.displayName };
|
|
9638
9638
|
} catch {
|
|
9639
9639
|
return {};
|
|
9640
9640
|
}
|
|
@@ -9704,7 +9704,7 @@ async function runIntroRequest(args5, overrides) {
|
|
|
9704
9704
|
}
|
|
9705
9705
|
const profile = await deps.readProfileContact();
|
|
9706
9706
|
const displayName = (args5.name ?? profile.displayName ?? requesterLogin).trim();
|
|
9707
|
-
const explicitContact = (args5.contact ??
|
|
9707
|
+
const explicitContact = (args5.contact ?? "").trim();
|
|
9708
9708
|
const contact = explicitContact || `@${requesterLogin}`;
|
|
9709
9709
|
const payload = buildIntroPayload({
|
|
9710
9710
|
requesterLogin,
|
|
@@ -9835,8 +9835,7 @@ async function runIntroDecision(args5, overrides) {
|
|
|
9835
9835
|
let contact = "";
|
|
9836
9836
|
let shareHandle = false;
|
|
9837
9837
|
if (args5.action === "accept") {
|
|
9838
|
-
|
|
9839
|
-
contact = (args5.contact ?? profile.contactEmail ?? "").trim();
|
|
9838
|
+
contact = (args5.contact ?? "").trim();
|
|
9840
9839
|
shareHandle = contact.length === 0;
|
|
9841
9840
|
let shareLabel = contact;
|
|
9842
9841
|
if (shareHandle) {
|
|
@@ -9906,8 +9905,11 @@ async function runIntroDecision(args5, overrides) {
|
|
|
9906
9905
|
deps.log("\n Declined \u2014 no contact was shared.\n");
|
|
9907
9906
|
return;
|
|
9908
9907
|
}
|
|
9909
|
-
|
|
9910
|
-
|
|
9908
|
+
const peer = data.counterpartyLogin;
|
|
9909
|
+
deps.log(`
|
|
9910
|
+
\u2713 Connected${peer ? ` with @${peer}` : ""}.`);
|
|
9911
|
+
if (peer) deps.log(` Message them any time: terminalhire chat @${peer}`);
|
|
9912
|
+
if (data.contact) deps.log(` They also shared: ${data.contact}`);
|
|
9911
9913
|
deps.log("");
|
|
9912
9914
|
}
|
|
9913
9915
|
async function runIntroList(overrides) {
|
|
@@ -10287,6 +10289,7 @@ var jpi_chat_read_exports = {};
|
|
|
10287
10289
|
__export(jpi_chat_read_exports, {
|
|
10288
10290
|
formatClock: () => formatClock,
|
|
10289
10291
|
formatStamp: () => formatStamp,
|
|
10292
|
+
postReadCursor: () => postReadCursor,
|
|
10290
10293
|
readReadCursors: () => readReadCursors,
|
|
10291
10294
|
renderInbox: () => renderInbox,
|
|
10292
10295
|
renderThread: () => renderThread,
|
|
@@ -10319,6 +10322,22 @@ function writeReadCursor(login, iso, deps = {}) {
|
|
|
10319
10322
|
mkdirSync13(TERMINALHIRE_DIR12, { recursive: true });
|
|
10320
10323
|
writeFileSync13(READS_FILE, JSON.stringify(cursors, null, 2), { mode: 384, encoding: "utf8" });
|
|
10321
10324
|
}
|
|
10325
|
+
async function postReadCursor(peerLogin, lastReadAt, deps = {}) {
|
|
10326
|
+
const readCookie = deps.readCookie ?? readWebSessionCookie;
|
|
10327
|
+
const cookie = readCookie();
|
|
10328
|
+
if (!cookie) return;
|
|
10329
|
+
try {
|
|
10330
|
+
await fetch(`${CHAT_BASE2}/api/chat/read-cursor`, {
|
|
10331
|
+
method: "POST",
|
|
10332
|
+
headers: { "Content-Type": "application/json", Cookie: `${GH_SESSION_COOKIE4}=${cookie}` },
|
|
10333
|
+
body: JSON.stringify({ peerLogin, lastReadAt }),
|
|
10334
|
+
// Best-effort cross-device sync — not latency-sensitive, so a short bound
|
|
10335
|
+
// keeps a cold/unreachable server from stalling the reader's exit.
|
|
10336
|
+
signal: AbortSignal.timeout(2500)
|
|
10337
|
+
});
|
|
10338
|
+
} catch {
|
|
10339
|
+
}
|
|
10340
|
+
}
|
|
10322
10341
|
function formatClock(iso) {
|
|
10323
10342
|
const d = new Date(iso);
|
|
10324
10343
|
if (Number.isNaN(d.getTime())) return "--:--";
|
|
@@ -10498,6 +10517,7 @@ async function runReadThread(opts = {}) {
|
|
|
10498
10517
|
client = createChatClient(),
|
|
10499
10518
|
resolveConnection = defaultResolveConnection,
|
|
10500
10519
|
writeCursor = writeReadCursor,
|
|
10520
|
+
syncCursor = postReadCursor,
|
|
10501
10521
|
ensureDisclosure = ensureChatDisclosure
|
|
10502
10522
|
} = opts;
|
|
10503
10523
|
const target = String(login ?? "").replace(/^@/, "").trim();
|
|
@@ -10543,6 +10563,7 @@ async function runReadThread(opts = {}) {
|
|
|
10543
10563
|
writeCursor(peerLogin, newest.createdAt);
|
|
10544
10564
|
} catch {
|
|
10545
10565
|
}
|
|
10566
|
+
await syncCursor(peerLogin, newest.createdAt);
|
|
10546
10567
|
}
|
|
10547
10568
|
}
|
|
10548
10569
|
return { ok: true, shown: shownMessages.length, total };
|
|
@@ -10590,13 +10611,15 @@ async function runSend(opts = {}) {
|
|
|
10590
10611
|
);
|
|
10591
10612
|
return { ok: true };
|
|
10592
10613
|
}
|
|
10593
|
-
var CHAT_BASE2, TERMINALHIRE_DIR12, READS_FILE;
|
|
10614
|
+
var CHAT_BASE2, GH_SESSION_COOKIE4, TERMINALHIRE_DIR12, READS_FILE;
|
|
10594
10615
|
var init_jpi_chat_read = __esm({
|
|
10595
10616
|
"bin/jpi-chat-read.js"() {
|
|
10596
10617
|
"use strict";
|
|
10597
10618
|
init_chat_client();
|
|
10619
|
+
init_web_session();
|
|
10598
10620
|
init_jpi_chat();
|
|
10599
10621
|
CHAT_BASE2 = process.env["TERMINALHIRE_API_URL"] || "https://www.terminalhire.com";
|
|
10622
|
+
GH_SESSION_COOKIE4 = "__jpi_gh_session";
|
|
10600
10623
|
TERMINALHIRE_DIR12 = join16(homedir15(), ".terminalhire");
|
|
10601
10624
|
READS_FILE = join16(TERMINALHIRE_DIR12, "chat-reads.json");
|
|
10602
10625
|
}
|
|
@@ -10673,7 +10696,7 @@ async function fetchIntroList(deps = {}) {
|
|
|
10673
10696
|
try {
|
|
10674
10697
|
res = await fetchImpl(`${CHAT_BASE3}/api/intro/list`, {
|
|
10675
10698
|
method: "GET",
|
|
10676
|
-
headers: { Cookie: `${
|
|
10699
|
+
headers: { Cookie: `${GH_SESSION_COOKIE5}=${cookie}` },
|
|
10677
10700
|
signal: AbortSignal.timeout(1e4)
|
|
10678
10701
|
});
|
|
10679
10702
|
} catch (err) {
|
|
@@ -10834,9 +10857,9 @@ async function runChatPane(opts = {}) {
|
|
|
10834
10857
|
output.write(
|
|
10835
10858
|
`
|
|
10836
10859
|
@${peerLogin} isn't reachable for chat yet.
|
|
10837
|
-
|
|
10838
|
-
|
|
10839
|
-
terminalhire chat
|
|
10860
|
+
Chat is end-to-end encrypted, so they need to open chat once to
|
|
10861
|
+
publish their key. As soon as they run
|
|
10862
|
+
terminalhire chat
|
|
10840
10863
|
on their side, messages will flow.
|
|
10841
10864
|
|
|
10842
10865
|
`
|
|
@@ -11215,7 +11238,7 @@ async function run9() {
|
|
|
11215
11238
|
process.exit(1);
|
|
11216
11239
|
}
|
|
11217
11240
|
}
|
|
11218
|
-
var CHAT_BASE3,
|
|
11241
|
+
var CHAT_BASE3, GH_SESSION_COOKIE5, HIDE_CURSOR, SHOW_CURSOR, CLEAR, KEY_CTRL_C, KEY_CTRL_S, KEY_ENTER_A, KEY_ENTER_B, KEY_BACKSPACE_A, KEY_BACKSPACE_B, MAX_INPUT_LEN, ANSI_CSI, ANSI_OSC, ANSI_OTHER, C0_C1_DEL, CHAT_DISCLOSURE, CHAT_AT_REST, CHAT_CODE_OF_CONDUCT, CHAT_MIN_AGE, DEPOSIT_CTA;
|
|
11219
11242
|
var init_jpi_chat = __esm({
|
|
11220
11243
|
"bin/jpi-chat.js"() {
|
|
11221
11244
|
"use strict";
|
|
@@ -11223,7 +11246,7 @@ var init_jpi_chat = __esm({
|
|
|
11223
11246
|
init_config();
|
|
11224
11247
|
init_web_session();
|
|
11225
11248
|
CHAT_BASE3 = process.env["TERMINALHIRE_API_URL"] || "https://www.terminalhire.com";
|
|
11226
|
-
|
|
11249
|
+
GH_SESSION_COOKIE5 = "__jpi_gh_session";
|
|
11227
11250
|
HIDE_CURSOR = "\x1B[?25l";
|
|
11228
11251
|
SHOW_CURSOR = "\x1B[?25h";
|
|
11229
11252
|
CLEAR = "\x1B[2J\x1B[H";
|
|
@@ -11429,7 +11452,7 @@ async function runLinkLogout(overrides) {
|
|
|
11429
11452
|
try {
|
|
11430
11453
|
const res = await deps.fetchImpl(`${LINK_BASE3}/api/auth/session`, {
|
|
11431
11454
|
method: "DELETE",
|
|
11432
|
-
headers: { Cookie: `${
|
|
11455
|
+
headers: { Cookie: `${GH_SESSION_COOKIE6}=${token}` },
|
|
11433
11456
|
signal: AbortSignal.timeout(1e4)
|
|
11434
11457
|
});
|
|
11435
11458
|
revoked = res.ok;
|
|
@@ -11444,14 +11467,14 @@ async function runLinkLogout(overrides) {
|
|
|
11444
11467
|
}
|
|
11445
11468
|
deps.exit(0);
|
|
11446
11469
|
}
|
|
11447
|
-
var LINK_BASE3,
|
|
11470
|
+
var LINK_BASE3, GH_SESSION_COOKIE6, LINK_TIMEOUT_MS, LINKED_HTML, FAILED_HTML;
|
|
11448
11471
|
var init_link = __esm({
|
|
11449
11472
|
"src/link.ts"() {
|
|
11450
11473
|
"use strict";
|
|
11451
11474
|
init_web_session();
|
|
11452
11475
|
init_config();
|
|
11453
11476
|
LINK_BASE3 = "https://www.terminalhire.com";
|
|
11454
|
-
|
|
11477
|
+
GH_SESSION_COOKIE6 = "__jpi_gh_session";
|
|
11455
11478
|
LINK_TIMEOUT_MS = 12e4;
|
|
11456
11479
|
LINKED_HTML = `<!doctype html><html><head><meta charset="utf-8"><title>terminalhire</title></head>
|
|
11457
11480
|
<body style="font-family:system-ui;padding:2rem;background:#0b0d10;color:#e6e6e6">
|
|
@@ -13069,7 +13092,7 @@ async function run18() {
|
|
|
13069
13092
|
if (sessionCookie && !isInboundNudgeMuted()) try {
|
|
13070
13093
|
const res = await fetch(`${API_URL6}/api/intro/list`, {
|
|
13071
13094
|
method: "GET",
|
|
13072
|
-
headers: { Cookie: `${
|
|
13095
|
+
headers: { Cookie: `${GH_SESSION_COOKIE7}=${sessionCookie}` },
|
|
13073
13096
|
signal: AbortSignal.timeout(1e4)
|
|
13074
13097
|
});
|
|
13075
13098
|
if (res.ok) {
|
|
@@ -13080,6 +13103,24 @@ async function run18() {
|
|
|
13080
13103
|
}
|
|
13081
13104
|
} catch {
|
|
13082
13105
|
}
|
|
13106
|
+
let unreadChat = { count: 0 };
|
|
13107
|
+
if (sessionCookie && !isInboundNudgeMuted()) try {
|
|
13108
|
+
const res = await fetch(`${API_URL6}/api/chat/inbox`, {
|
|
13109
|
+
method: "GET",
|
|
13110
|
+
headers: { Cookie: `${GH_SESSION_COOKIE7}=${sessionCookie}` },
|
|
13111
|
+
signal: AbortSignal.timeout(1e4)
|
|
13112
|
+
});
|
|
13113
|
+
if (res.ok) {
|
|
13114
|
+
const body = await res.json();
|
|
13115
|
+
const inbox = Array.isArray(body?.inbox) ? body.inbox : [];
|
|
13116
|
+
const total = inbox.reduce(
|
|
13117
|
+
(sum, it) => sum + (it && typeof it.unreadCount === "number" && it.unreadCount > 0 ? it.unreadCount : 0),
|
|
13118
|
+
0
|
|
13119
|
+
);
|
|
13120
|
+
unreadChat = { count: total };
|
|
13121
|
+
}
|
|
13122
|
+
} catch {
|
|
13123
|
+
}
|
|
13083
13124
|
mkdirSync17(TERMINALHIRE_DIR14, { recursive: true });
|
|
13084
13125
|
const cacheEntry = {
|
|
13085
13126
|
ts: Date.now(),
|
|
@@ -13087,7 +13128,8 @@ async function run18() {
|
|
|
13087
13128
|
matchCount,
|
|
13088
13129
|
topMatches,
|
|
13089
13130
|
topPeers,
|
|
13090
|
-
incomingPending
|
|
13131
|
+
incomingPending,
|
|
13132
|
+
unreadChat
|
|
13091
13133
|
};
|
|
13092
13134
|
writeFileSync17(INDEX_CACHE_FILE5, JSON.stringify(cacheEntry), "utf8");
|
|
13093
13135
|
try {
|
|
@@ -13140,14 +13182,14 @@ async function run18() {
|
|
|
13140
13182
|
process.exit(1);
|
|
13141
13183
|
}
|
|
13142
13184
|
}
|
|
13143
|
-
var
|
|
13185
|
+
var GH_SESSION_COOKIE7, __dirname4, TERMINALHIRE_DIR14, INDEX_CACHE_FILE5, API_URL6;
|
|
13144
13186
|
var init_jpi_refresh = __esm({
|
|
13145
13187
|
"bin/jpi-refresh.js"() {
|
|
13146
13188
|
"use strict";
|
|
13147
13189
|
init_directory2();
|
|
13148
13190
|
init_config();
|
|
13149
13191
|
init_web_session();
|
|
13150
|
-
|
|
13192
|
+
GH_SESSION_COOKIE7 = "__jpi_gh_session";
|
|
13151
13193
|
__dirname4 = fileURLToPath5(new URL(".", import.meta.url));
|
|
13152
13194
|
TERMINALHIRE_DIR14 = join23(homedir21(), ".terminalhire");
|
|
13153
13195
|
INDEX_CACHE_FILE5 = join23(TERMINALHIRE_DIR14, "index-cache.json");
|
package/dist/bin/jpi-intro.js
CHANGED
|
@@ -1108,7 +1108,7 @@ function defaultIntroDeps() {
|
|
|
1108
1108
|
try {
|
|
1109
1109
|
const { readProfile: readProfile2 } = await Promise.resolve().then(() => (init_profile(), profile_exports));
|
|
1110
1110
|
const profile = await readProfile2();
|
|
1111
|
-
return { displayName: profile?.displayName
|
|
1111
|
+
return { displayName: profile?.displayName };
|
|
1112
1112
|
} catch {
|
|
1113
1113
|
return {};
|
|
1114
1114
|
}
|
|
@@ -1178,7 +1178,7 @@ async function runIntroRequest(args, overrides) {
|
|
|
1178
1178
|
}
|
|
1179
1179
|
const profile = await deps.readProfileContact();
|
|
1180
1180
|
const displayName = (args.name ?? profile.displayName ?? requesterLogin).trim();
|
|
1181
|
-
const explicitContact = (args.contact ??
|
|
1181
|
+
const explicitContact = (args.contact ?? "").trim();
|
|
1182
1182
|
const contact = explicitContact || `@${requesterLogin}`;
|
|
1183
1183
|
const payload = buildIntroPayload({
|
|
1184
1184
|
requesterLogin,
|
|
@@ -1309,8 +1309,7 @@ async function runIntroDecision(args, overrides) {
|
|
|
1309
1309
|
let contact = "";
|
|
1310
1310
|
let shareHandle = false;
|
|
1311
1311
|
if (args.action === "accept") {
|
|
1312
|
-
|
|
1313
|
-
contact = (args.contact ?? profile.contactEmail ?? "").trim();
|
|
1312
|
+
contact = (args.contact ?? "").trim();
|
|
1314
1313
|
shareHandle = contact.length === 0;
|
|
1315
1314
|
let shareLabel = contact;
|
|
1316
1315
|
if (shareHandle) {
|
|
@@ -1380,8 +1379,11 @@ async function runIntroDecision(args, overrides) {
|
|
|
1380
1379
|
deps.log("\n Declined \u2014 no contact was shared.\n");
|
|
1381
1380
|
return;
|
|
1382
1381
|
}
|
|
1383
|
-
|
|
1384
|
-
|
|
1382
|
+
const peer = data.counterpartyLogin;
|
|
1383
|
+
deps.log(`
|
|
1384
|
+
\u2713 Connected${peer ? ` with @${peer}` : ""}.`);
|
|
1385
|
+
if (peer) deps.log(` Message them any time: terminalhire chat @${peer}`);
|
|
1386
|
+
if (data.contact) deps.log(` They also shared: ${data.contact}`);
|
|
1385
1387
|
deps.log("");
|
|
1386
1388
|
}
|
|
1387
1389
|
async function runIntroList(overrides) {
|
package/dist/bin/jpi-refresh.js
CHANGED
|
@@ -7094,6 +7094,24 @@ async function run() {
|
|
|
7094
7094
|
}
|
|
7095
7095
|
} catch {
|
|
7096
7096
|
}
|
|
7097
|
+
let unreadChat = { count: 0 };
|
|
7098
|
+
if (sessionCookie && !isInboundNudgeMuted()) try {
|
|
7099
|
+
const res = await fetch(`${API_URL2}/api/chat/inbox`, {
|
|
7100
|
+
method: "GET",
|
|
7101
|
+
headers: { Cookie: `${GH_SESSION_COOKIE}=${sessionCookie}` },
|
|
7102
|
+
signal: AbortSignal.timeout(1e4)
|
|
7103
|
+
});
|
|
7104
|
+
if (res.ok) {
|
|
7105
|
+
const body = await res.json();
|
|
7106
|
+
const inbox = Array.isArray(body?.inbox) ? body.inbox : [];
|
|
7107
|
+
const total = inbox.reduce(
|
|
7108
|
+
(sum, it) => sum + (it && typeof it.unreadCount === "number" && it.unreadCount > 0 ? it.unreadCount : 0),
|
|
7109
|
+
0
|
|
7110
|
+
);
|
|
7111
|
+
unreadChat = { count: total };
|
|
7112
|
+
}
|
|
7113
|
+
} catch {
|
|
7114
|
+
}
|
|
7097
7115
|
mkdirSync6(TERMINALHIRE_DIR4, { recursive: true });
|
|
7098
7116
|
const cacheEntry = {
|
|
7099
7117
|
ts: Date.now(),
|
|
@@ -7101,7 +7119,8 @@ async function run() {
|
|
|
7101
7119
|
matchCount,
|
|
7102
7120
|
topMatches,
|
|
7103
7121
|
topPeers,
|
|
7104
|
-
incomingPending
|
|
7122
|
+
incomingPending,
|
|
7123
|
+
unreadChat
|
|
7105
7124
|
};
|
|
7106
7125
|
writeFileSync6(INDEX_CACHE_FILE2, JSON.stringify(cacheEntry), "utf8");
|
|
7107
7126
|
try {
|
package/dist/bin/jpi.js
CHANGED
|
@@ -94,6 +94,17 @@ function getCachedIncomingCount() {
|
|
|
94
94
|
return 0;
|
|
95
95
|
}
|
|
96
96
|
}
|
|
97
|
+
function getCachedUnreadChatCount() {
|
|
98
|
+
try {
|
|
99
|
+
const raw = readFileSync(INDEX_CACHE_FILE, "utf8");
|
|
100
|
+
const entry = JSON.parse(raw);
|
|
101
|
+
if (Date.now() - entry.ts > INDEX_CACHE_TTL_MS) return 0;
|
|
102
|
+
const n = entry.unreadChat && entry.unreadChat.count;
|
|
103
|
+
return typeof n === "number" && n > 0 ? n : 0;
|
|
104
|
+
} catch {
|
|
105
|
+
return 0;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
97
108
|
function getNudgeMode() {
|
|
98
109
|
const envVal = process.env["TERMINALHIRE_NUDGE"];
|
|
99
110
|
if (envVal) {
|
|
@@ -166,8 +177,9 @@ try {
|
|
|
166
177
|
}
|
|
167
178
|
const matchCount = getCachedMatchCount();
|
|
168
179
|
const incomingCount = getCachedIncomingCount();
|
|
180
|
+
const unreadChatCount = getCachedUnreadChatCount();
|
|
169
181
|
const haveRoles = matchCount !== null && matchCount > 0;
|
|
170
|
-
if (!haveRoles && incomingCount === 0) process.exit(0);
|
|
182
|
+
if (!haveRoles && incomingCount === 0 && unreadChatCount === 0) process.exit(0);
|
|
171
183
|
const nudgeMode = getNudgeMode();
|
|
172
184
|
if (!shouldNudge(nudgeMode, sessionId)) process.exit(0);
|
|
173
185
|
let line;
|
|
@@ -175,8 +187,12 @@ try {
|
|
|
175
187
|
const plural = matchCount === 1 ? "role" : "roles";
|
|
176
188
|
line = `\u2726 ${matchCount} ${plural} match your current work \u2014 run: terminalhire jobs`;
|
|
177
189
|
if (incomingCount > 0) line += ` \xB7 \u2709 ${incomingCount} waiting to connect`;
|
|
178
|
-
}
|
|
190
|
+
if (unreadChatCount > 0) line += ` \xB7 \u{1F4AC} ${unreadChatCount} unread`;
|
|
191
|
+
} else if (incomingCount > 0) {
|
|
179
192
|
line = `\u2709 ${incomingCount} waiting to connect \u2014 run: terminalhire intro --list`;
|
|
193
|
+
if (unreadChatCount > 0) line += ` \xB7 \u{1F4AC} ${unreadChatCount} unread`;
|
|
194
|
+
} else {
|
|
195
|
+
line = `\u{1F4AC} ${unreadChatCount} unread \u2014 run: terminalhire chat`;
|
|
180
196
|
}
|
|
181
197
|
process.stdout.write(line + "\n");
|
|
182
198
|
if (nudgeMode === "session") {
|
package/dist/src/intro.js
CHANGED
|
@@ -1101,7 +1101,7 @@ function defaultIntroDeps() {
|
|
|
1101
1101
|
try {
|
|
1102
1102
|
const { readProfile: readProfile2 } = await Promise.resolve().then(() => (init_profile(), profile_exports));
|
|
1103
1103
|
const profile = await readProfile2();
|
|
1104
|
-
return { displayName: profile?.displayName
|
|
1104
|
+
return { displayName: profile?.displayName };
|
|
1105
1105
|
} catch {
|
|
1106
1106
|
return {};
|
|
1107
1107
|
}
|
|
@@ -1171,7 +1171,7 @@ async function runIntroRequest(args, overrides) {
|
|
|
1171
1171
|
}
|
|
1172
1172
|
const profile = await deps.readProfileContact();
|
|
1173
1173
|
const displayName = (args.name ?? profile.displayName ?? requesterLogin).trim();
|
|
1174
|
-
const explicitContact = (args.contact ??
|
|
1174
|
+
const explicitContact = (args.contact ?? "").trim();
|
|
1175
1175
|
const contact = explicitContact || `@${requesterLogin}`;
|
|
1176
1176
|
const payload = buildIntroPayload({
|
|
1177
1177
|
requesterLogin,
|
|
@@ -1303,8 +1303,7 @@ async function runIntroDecision(args, overrides) {
|
|
|
1303
1303
|
let contact = "";
|
|
1304
1304
|
let shareHandle = false;
|
|
1305
1305
|
if (args.action === "accept") {
|
|
1306
|
-
|
|
1307
|
-
contact = (args.contact ?? profile.contactEmail ?? "").trim();
|
|
1306
|
+
contact = (args.contact ?? "").trim();
|
|
1308
1307
|
shareHandle = contact.length === 0;
|
|
1309
1308
|
let shareLabel = contact;
|
|
1310
1309
|
if (shareHandle) {
|
|
@@ -1374,8 +1373,11 @@ async function runIntroDecision(args, overrides) {
|
|
|
1374
1373
|
deps.log("\n Declined \u2014 no contact was shared.\n");
|
|
1375
1374
|
return;
|
|
1376
1375
|
}
|
|
1377
|
-
|
|
1378
|
-
|
|
1376
|
+
const peer = data.counterpartyLogin;
|
|
1377
|
+
deps.log(`
|
|
1378
|
+
\u2713 Connected${peer ? ` with @${peer}` : ""}.`);
|
|
1379
|
+
if (peer) deps.log(` Message them any time: terminalhire chat @${peer}`);
|
|
1380
|
+
if (data.contact) deps.log(` They also shared: ${data.contact}`);
|
|
1379
1381
|
deps.log("");
|
|
1380
1382
|
}
|
|
1381
1383
|
async function runIntroList(overrides) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "terminalhire",
|
|
3
|
-
"version": "0.10.
|
|
3
|
+
"version": "0.10.5",
|
|
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",
|