terminalhire 0.30.1 → 0.30.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.
@@ -305,6 +305,315 @@ var init_version_nudge = __esm({
305
305
  }
306
306
  });
307
307
 
308
+ // src/open-url.js
309
+ var open_url_exports = {};
310
+ __export(open_url_exports, {
311
+ openInBrowser: () => openInBrowser
312
+ });
313
+ import { spawn } from "child_process";
314
+ function openInBrowser(url) {
315
+ let cmd;
316
+ let args5;
317
+ if (process.platform === "darwin") {
318
+ cmd = "open";
319
+ args5 = [url];
320
+ } else if (process.platform === "win32") {
321
+ cmd = "cmd";
322
+ args5 = ["/c", "start", "", url];
323
+ } else {
324
+ cmd = "xdg-open";
325
+ args5 = [url];
326
+ }
327
+ try {
328
+ const child = spawn(cmd, args5, { stdio: "ignore", detached: true });
329
+ child.on("error", () => {
330
+ });
331
+ child.unref();
332
+ } catch {
333
+ }
334
+ }
335
+ var init_open_url = __esm({
336
+ "src/open-url.js"() {
337
+ "use strict";
338
+ }
339
+ });
340
+
341
+ // ../../node_modules/keytar/build/Release/keytar.node
342
+ var keytar_default;
343
+ var init_keytar = __esm({
344
+ "../../node_modules/keytar/build/Release/keytar.node"() {
345
+ keytar_default = "../keytar-KOAAH267.node";
346
+ }
347
+ });
348
+
349
+ // node-file:/Users/ericgang/job-placement-inline/node_modules/keytar/build/Release/keytar.node
350
+ var require_keytar = __commonJS({
351
+ "node-file:/Users/ericgang/job-placement-inline/node_modules/keytar/build/Release/keytar.node"(exports, module) {
352
+ "use strict";
353
+ init_keytar();
354
+ try {
355
+ module.exports = __require(keytar_default);
356
+ } catch {
357
+ }
358
+ }
359
+ });
360
+
361
+ // ../../node_modules/keytar/lib/keytar.js
362
+ var require_keytar2 = __commonJS({
363
+ "../../node_modules/keytar/lib/keytar.js"(exports, module) {
364
+ "use strict";
365
+ var keytar = require_keytar();
366
+ function checkRequired(val, name) {
367
+ if (!val || val.length <= 0) {
368
+ throw new Error(name + " is required.");
369
+ }
370
+ }
371
+ module.exports = {
372
+ getPassword: function(service, account) {
373
+ checkRequired(service, "Service");
374
+ checkRequired(account, "Account");
375
+ return keytar.getPassword(service, account);
376
+ },
377
+ setPassword: function(service, account, password) {
378
+ checkRequired(service, "Service");
379
+ checkRequired(account, "Account");
380
+ checkRequired(password, "Password");
381
+ return keytar.setPassword(service, account, password);
382
+ },
383
+ deletePassword: function(service, account) {
384
+ checkRequired(service, "Service");
385
+ checkRequired(account, "Account");
386
+ return keytar.deletePassword(service, account);
387
+ },
388
+ findPassword: function(service) {
389
+ checkRequired(service, "Service");
390
+ return keytar.findPassword(service);
391
+ },
392
+ findCredentials: function(service) {
393
+ checkRequired(service, "Service");
394
+ return keytar.findCredentials(service);
395
+ }
396
+ };
397
+ }
398
+ });
399
+
400
+ // src/github-auth.ts
401
+ var github_auth_exports = {};
402
+ __export(github_auth_exports, {
403
+ GITHUB_SCOPE: () => GITHUB_SCOPE,
404
+ decrypt: () => decrypt,
405
+ deleteGitHubToken: () => deleteGitHubToken,
406
+ encrypt: () => encrypt,
407
+ hasGitHubToken: () => hasGitHubToken,
408
+ loadKey: () => loadKey,
409
+ readGitHubToken: () => readGitHubToken,
410
+ resolveStoredLogin: () => resolveStoredLogin,
411
+ runDeviceFlow: () => runDeviceFlow,
412
+ writeGitHubToken: () => writeGitHubToken
413
+ });
414
+ import {
415
+ createCipheriv,
416
+ createDecipheriv,
417
+ randomBytes
418
+ } from "crypto";
419
+ import {
420
+ readFileSync as readFileSync4,
421
+ writeFileSync as writeFileSync4,
422
+ mkdirSync as mkdirSync4,
423
+ existsSync as existsSync4,
424
+ rmSync as rmSync2
425
+ } from "fs";
426
+ import { join as join4 } from "path";
427
+ import { homedir as homedir4 } from "os";
428
+ async function loadKey() {
429
+ try {
430
+ const kt = await Promise.resolve().then(() => __toESM(require_keytar2(), 1));
431
+ const stored = await kt.getPassword("terminalhire", "profile-key");
432
+ if (stored) return Buffer.from(stored, "hex");
433
+ const key2 = randomBytes(KEY_BYTES);
434
+ await kt.setPassword("terminalhire", "profile-key", key2.toString("hex"));
435
+ return key2;
436
+ } catch {
437
+ }
438
+ mkdirSync4(TERMINALHIRE_DIR2, { recursive: true });
439
+ if (existsSync4(KEY_FILE)) {
440
+ return Buffer.from(readFileSync4(KEY_FILE, "utf8").trim(), "hex");
441
+ }
442
+ const key = randomBytes(KEY_BYTES);
443
+ writeFileSync4(KEY_FILE, key.toString("hex"), { mode: 384, encoding: "utf8" });
444
+ return key;
445
+ }
446
+ function encrypt(plaintext, key) {
447
+ const iv = randomBytes(IV_BYTES);
448
+ const cipher = createCipheriv(ALGO, key, iv);
449
+ const ct = Buffer.concat([cipher.update(plaintext, "utf8"), cipher.final()]);
450
+ const tag = cipher.getAuthTag();
451
+ return { iv: iv.toString("hex"), tag: tag.toString("hex"), ciphertext: ct.toString("hex") };
452
+ }
453
+ function decrypt(blob, key) {
454
+ const decipher = createDecipheriv(ALGO, key, Buffer.from(blob.iv, "hex"));
455
+ decipher.setAuthTag(Buffer.from(blob.tag, "hex"));
456
+ const plain = Buffer.concat([
457
+ decipher.update(Buffer.from(blob.ciphertext, "hex")),
458
+ decipher.final()
459
+ ]);
460
+ return plain.toString("utf8");
461
+ }
462
+ async function readGitHubToken() {
463
+ if (!existsSync4(TOKEN_FILE)) return void 0;
464
+ try {
465
+ const key = await loadKey();
466
+ const raw = readFileSync4(TOKEN_FILE, "utf8");
467
+ const blob = JSON.parse(raw);
468
+ return decrypt(blob, key);
469
+ } catch {
470
+ return void 0;
471
+ }
472
+ }
473
+ async function writeGitHubToken(token) {
474
+ mkdirSync4(TERMINALHIRE_DIR2, { recursive: true });
475
+ const key = await loadKey();
476
+ const blob = encrypt(token, key);
477
+ writeFileSync4(TOKEN_FILE, JSON.stringify(blob, null, 2), { encoding: "utf8" });
478
+ }
479
+ async function deleteGitHubToken() {
480
+ try {
481
+ rmSync2(TOKEN_FILE);
482
+ } catch {
483
+ }
484
+ }
485
+ async function hasGitHubToken() {
486
+ return existsSync4(TOKEN_FILE);
487
+ }
488
+ async function runDeviceFlow() {
489
+ if (process.env["TERMINALHIRE_GITHUB_MOCK"] === "1" || process.env["TERMINALHIRE_GITHUB_MOCK"] === "1" || process.env["JPI_GITHUB_MOCK"] === "1") {
490
+ console.log("\n[mock] GitHub OAuth skipped (JPI_GITHUB_MOCK=1)");
491
+ console.log(`[mock] Using fixture profile: ${MOCK_LOGIN}`);
492
+ await writeGitHubToken(MOCK_TOKEN);
493
+ return MOCK_LOGIN;
494
+ }
495
+ const clientId = process.env["GITHUB_DEVICE_CLIENT_ID"] ?? process.env["GITHUB_CLIENT_ID"] ?? BAKED_IN_CLIENT_ID;
496
+ if (clientId === "Iv1.PLACEHOLDER_REGISTER_YOUR_APP") {
497
+ console.warn("\nWarning: GITHUB_CLIENT_ID env var looks like a placeholder.");
498
+ console.warn("Remove it to use the baked-in client ID, or set it to your own OAuth App Client ID.\n");
499
+ }
500
+ const deviceRes = await fetch(DEVICE_CODE_URL, {
501
+ method: "POST",
502
+ headers: {
503
+ Accept: "application/json",
504
+ "Content-Type": "application/x-www-form-urlencoded"
505
+ },
506
+ body: new URLSearchParams({ client_id: clientId, scope: GITHUB_SCOPE }).toString(),
507
+ signal: AbortSignal.timeout(15e3)
508
+ });
509
+ if (!deviceRes.ok) {
510
+ throw new Error(`GitHub device code request failed: HTTP ${deviceRes.status}`);
511
+ }
512
+ const deviceData = await deviceRes.json();
513
+ console.log("");
514
+ console.log(" GitHub sign-in (device flow)");
515
+ console.log(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500");
516
+ console.log(` 1. Open: ${deviceData.verification_uri}`);
517
+ console.log(` 2. Enter code: ${deviceData.user_code}`);
518
+ console.log(' 3. Authorize "Terminalhire" (scope: read:user \u2014 public data only)');
519
+ console.log(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500");
520
+ console.log(" Waiting for authorization...");
521
+ console.log("");
522
+ let intervalSecs = deviceData.interval ?? 5;
523
+ const expiresAt = Date.now() + (deviceData.expires_in ?? 900) * 1e3;
524
+ const clientSecret = process.env["GITHUB_CLIENT_SECRET"];
525
+ while (Date.now() < expiresAt) {
526
+ await sleep(intervalSecs * 1e3);
527
+ const body = new URLSearchParams({
528
+ client_id: clientId,
529
+ device_code: deviceData.device_code,
530
+ grant_type: "urn:ietf:params:oauth:grant-type:device_code"
531
+ });
532
+ if (clientSecret) body.set("client_secret", clientSecret);
533
+ const tokenRes = await fetch(ACCESS_TOKEN_URL, {
534
+ method: "POST",
535
+ headers: {
536
+ Accept: "application/json",
537
+ "Content-Type": "application/x-www-form-urlencoded"
538
+ },
539
+ body: body.toString(),
540
+ signal: AbortSignal.timeout(15e3)
541
+ });
542
+ if (!tokenRes.ok) {
543
+ throw new Error(`GitHub token poll failed: HTTP ${tokenRes.status}`);
544
+ }
545
+ const tokenData = await tokenRes.json();
546
+ if (tokenData.access_token) {
547
+ await writeGitHubToken(tokenData.access_token);
548
+ const login = await fetchAuthedLogin(tokenData.access_token);
549
+ console.log(` Authorized as: ${login}`);
550
+ return login;
551
+ }
552
+ if (tokenData.error === "authorization_pending") {
553
+ continue;
554
+ }
555
+ if (tokenData.error === "slow_down") {
556
+ intervalSecs = (tokenData.interval ?? intervalSecs) + 5;
557
+ continue;
558
+ }
559
+ if (tokenData.error === "expired_token") {
560
+ throw new Error("GitHub device code expired. Please run `terminalhire login` again.");
561
+ }
562
+ if (tokenData.error === "access_denied") {
563
+ throw new Error("GitHub authorization was denied by the user.");
564
+ }
565
+ throw new Error(
566
+ `GitHub device flow error: ${tokenData.error ?? "unknown"} \u2014 ${tokenData.error_description ?? ""}`
567
+ );
568
+ }
569
+ throw new Error("GitHub device code expired before authorization. Please run `terminalhire login` again.");
570
+ }
571
+ async function fetchAuthedLogin(token) {
572
+ if (token === MOCK_TOKEN) return MOCK_LOGIN;
573
+ const res = await fetch("https://api.github.com/user", {
574
+ headers: {
575
+ Authorization: `Bearer ${token}`,
576
+ Accept: "application/vnd.github+json",
577
+ "X-GitHub-Api-Version": "2022-11-28"
578
+ },
579
+ signal: AbortSignal.timeout(1e4)
580
+ });
581
+ if (!res.ok) throw new Error(`GitHub /user: HTTP ${res.status}`);
582
+ const data = await res.json();
583
+ return data.login;
584
+ }
585
+ async function resolveStoredLogin() {
586
+ if (process.env["TERMINALHIRE_GITHUB_MOCK"] === "1" || process.env["TERMINALHIRE_GITHUB_MOCK"] === "1" || process.env["JPI_GITHUB_MOCK"] === "1") return MOCK_LOGIN;
587
+ const token = await readGitHubToken();
588
+ if (!token) return void 0;
589
+ try {
590
+ return await fetchAuthedLogin(token);
591
+ } catch {
592
+ return void 0;
593
+ }
594
+ }
595
+ function sleep(ms) {
596
+ return new Promise((resolve2) => setTimeout(resolve2, ms));
597
+ }
598
+ var TERMINALHIRE_DIR2, TOKEN_FILE, KEY_FILE, ALGO, KEY_BYTES, IV_BYTES, GITHUB_SCOPE, DEVICE_CODE_URL, ACCESS_TOKEN_URL, BAKED_IN_CLIENT_ID, MOCK_TOKEN, MOCK_LOGIN;
599
+ var init_github_auth = __esm({
600
+ "src/github-auth.ts"() {
601
+ "use strict";
602
+ TERMINALHIRE_DIR2 = join4(homedir4(), ".terminalhire");
603
+ TOKEN_FILE = join4(TERMINALHIRE_DIR2, "github-token.enc");
604
+ KEY_FILE = join4(TERMINALHIRE_DIR2, "key");
605
+ ALGO = "aes-256-gcm";
606
+ KEY_BYTES = 32;
607
+ IV_BYTES = 12;
608
+ GITHUB_SCOPE = "read:user";
609
+ DEVICE_CODE_URL = "https://github.com/login/device/code";
610
+ ACCESS_TOKEN_URL = "https://github.com/login/oauth/access_token";
611
+ BAKED_IN_CLIENT_ID = "Ov23lignE2ZSBe0J3a6B";
612
+ MOCK_TOKEN = "mock-github-token-jpi-dev";
613
+ MOCK_LOGIN = "janedev";
614
+ }
615
+ });
616
+
308
617
  // ../../packages/core/src/types.ts
309
618
  function isBounty(job) {
310
619
  return job.source === "bounty" && job.bounty != null;
@@ -4312,21 +4621,21 @@ var init_contributions = __esm({
4312
4621
  });
4313
4622
 
4314
4623
  // ../../packages/core/src/partners.ts
4315
- import { readFileSync as readFileSync4 } from "fs";
4316
- import { join as join4 } from "path";
4624
+ import { readFileSync as readFileSync5 } from "fs";
4625
+ import { join as join5 } from "path";
4317
4626
  import { fileURLToPath as fileURLToPath2 } from "url";
4318
4627
  function resolveDataPath() {
4319
4628
  try {
4320
4629
  const dir = fileURLToPath2(new URL("../../../data", import.meta.url));
4321
- return join4(dir, "partner-roles.json");
4630
+ return join5(dir, "partner-roles.json");
4322
4631
  } catch {
4323
- return join4(process.cwd(), "data", "partner-roles.json");
4632
+ return join5(process.cwd(), "data", "partner-roles.json");
4324
4633
  }
4325
4634
  }
4326
4635
  function loadPartnerRoles() {
4327
4636
  const filePath = resolveDataPath();
4328
4637
  try {
4329
- const raw = readFileSync4(filePath, "utf-8");
4638
+ const raw = readFileSync5(filePath, "utf-8");
4330
4639
  const parsed = JSON.parse(raw);
4331
4640
  if (!Array.isArray(parsed)) {
4332
4641
  console.warn("[partners] partner-roles.json is not an array \u2014 skipping");
@@ -4928,7 +5237,7 @@ function createHasher(hashCons) {
4928
5237
  hashC.create = () => hashCons();
4929
5238
  return hashC;
4930
5239
  }
4931
- function randomBytes(bytesLength = 32) {
5240
+ function randomBytes2(bytesLength = 32) {
4932
5241
  if (crypto && typeof crypto.getRandomValues === "function") {
4933
5242
  return crypto.getRandomValues(new Uint8Array(bytesLength));
4934
5243
  }
@@ -6353,7 +6662,7 @@ function eddsa(Point, cHash, eddsaOpts = {}) {
6353
6662
  });
6354
6663
  const { prehash } = eddsaOpts;
6355
6664
  const { BASE, Fp: Fp2, Fn: Fn2 } = Point;
6356
- const randomBytes6 = eddsaOpts.randomBytes || randomBytes;
6665
+ const randomBytes6 = eddsaOpts.randomBytes || randomBytes2;
6357
6666
  const adjustScalarBytes2 = eddsaOpts.adjustScalarBytes || ((bytes) => bytes);
6358
6667
  const domain = eddsaOpts.domain || ((data, ctx, phflag) => {
6359
6668
  _abool2(phflag, "phflag");
@@ -6631,7 +6940,7 @@ function montgomery(curveDef) {
6631
6940
  const is25519 = type === "x25519";
6632
6941
  if (!is25519 && type !== "x448")
6633
6942
  throw new Error("invalid type");
6634
- const randomBytes_ = rand || randomBytes;
6943
+ const randomBytes_ = rand || randomBytes2;
6635
6944
  const montgomeryBits = is25519 ? 255 : 448;
6636
6945
  const fieldLen = is25519 ? 32 : 56;
6637
6946
  const Gu = is25519 ? BigInt(9) : BigInt(5);
@@ -7770,7 +8079,7 @@ var init_chacha = __esm({
7770
8079
  });
7771
8080
 
7772
8081
  // ../../packages/core/src/chatCrypto.ts
7773
- import { hkdfSync, createHash, randomBytes as randomBytes2 } from "crypto";
8082
+ import { hkdfSync, createHash, randomBytes as randomBytes3 } from "crypto";
7774
8083
  function bytesToHex2(bytes) {
7775
8084
  return Buffer.from(bytes).toString("hex");
7776
8085
  }
@@ -7799,7 +8108,7 @@ function deriveSharedKey(myPrivateKey, peerPublicKey) {
7799
8108
  }
7800
8109
  function encryptMessage(plaintext, myPrivateKey, peerPublicKey) {
7801
8110
  const key = deriveSharedKey(myPrivateKey, peerPublicKey);
7802
- const nonce = new Uint8Array(randomBytes2(NONCE_BYTES));
8111
+ const nonce = new Uint8Array(randomBytes3(NONCE_BYTES));
7803
8112
  const cipher = xchacha20poly1305(key, nonce);
7804
8113
  const ct = cipher.encrypt(new Uint8Array(Buffer.from(plaintext, "utf8")));
7805
8114
  return { ciphertext: bytesToB64(ct), nonce: bytesToB64(nonce) };
@@ -8613,437 +8922,6 @@ var init_src = __esm({
8613
8922
  }
8614
8923
  });
8615
8924
 
8616
- // bin/job-status-store.js
8617
- var job_status_store_exports = {};
8618
- __export(job_status_store_exports, {
8619
- markClicked: () => markClicked,
8620
- markStatus: () => markStatus,
8621
- readStatusMap: () => readStatusMap,
8622
- statusFilePath: () => statusFilePath
8623
- });
8624
- import {
8625
- readFileSync as readFileSync5,
8626
- writeFileSync as writeFileSync4,
8627
- renameSync,
8628
- mkdirSync as mkdirSync4,
8629
- existsSync as existsSync4,
8630
- copyFileSync,
8631
- openSync,
8632
- closeSync,
8633
- unlinkSync
8634
- } from "fs";
8635
- import { join as join5, dirname } from "path";
8636
- import { homedir as homedir4 } from "os";
8637
- function statusFilePath() {
8638
- return STATUS_FILE;
8639
- }
8640
- function atomicWriteJson(path, obj) {
8641
- mkdirSync4(dirname(path), { recursive: true });
8642
- const tmp = `${path}.tmp-${process.pid}`;
8643
- writeFileSync4(tmp, JSON.stringify(obj, null, 2) + "\n", "utf8");
8644
- renameSync(tmp, path);
8645
- }
8646
- function sleepMs(ms) {
8647
- try {
8648
- Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
8649
- } catch {
8650
- const end = Date.now() + ms;
8651
- while (Date.now() < end) {
8652
- }
8653
- }
8654
- }
8655
- function withLock(fn) {
8656
- const deadline = Date.now() + 2e3;
8657
- for (; ; ) {
8658
- let fd;
8659
- try {
8660
- mkdirSync4(dirname(LOCK_FILE), { recursive: true });
8661
- fd = openSync(LOCK_FILE, "wx");
8662
- } catch (err) {
8663
- if (err && err.code === "EEXIST") {
8664
- if (Date.now() > deadline) {
8665
- try {
8666
- unlinkSync(LOCK_FILE);
8667
- } catch {
8668
- }
8669
- continue;
8670
- }
8671
- sleepMs(5);
8672
- continue;
8673
- }
8674
- throw err;
8675
- }
8676
- try {
8677
- return fn();
8678
- } finally {
8679
- try {
8680
- closeSync(fd);
8681
- } catch {
8682
- }
8683
- try {
8684
- unlinkSync(LOCK_FILE);
8685
- } catch {
8686
- }
8687
- }
8688
- }
8689
- }
8690
- function readStatusMap() {
8691
- if (!existsSync4(STATUS_FILE)) return {};
8692
- let raw;
8693
- try {
8694
- raw = readFileSync5(STATUS_FILE, "utf8");
8695
- } catch {
8696
- return {};
8697
- }
8698
- try {
8699
- const parsed = JSON.parse(raw);
8700
- if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) return parsed;
8701
- throw new Error("status store is not an object");
8702
- } catch {
8703
- try {
8704
- copyFileSync(STATUS_FILE, BAK_FILE);
8705
- } catch {
8706
- }
8707
- return {};
8708
- }
8709
- }
8710
- function markStatus(id, status) {
8711
- return withLock(() => {
8712
- const current = readStatusMap();
8713
- const next = status === "claimed" ? { ...current, [id]: { ...current[id], status: "claimed", markedAt: (/* @__PURE__ */ new Date()).toISOString() } } : setStatus(current, id, status);
8714
- atomicWriteJson(STATUS_FILE, next);
8715
- return next[id];
8716
- });
8717
- }
8718
- function markClicked(id) {
8719
- return withLock(() => {
8720
- const current = readStatusMap();
8721
- const next = recordClick(current, id);
8722
- atomicWriteJson(STATUS_FILE, next);
8723
- return next[id];
8724
- });
8725
- }
8726
- var TERMINALHIRE_DIR2, STATUS_FILE, LOCK_FILE, BAK_FILE;
8727
- var init_job_status_store = __esm({
8728
- "bin/job-status-store.js"() {
8729
- "use strict";
8730
- init_src();
8731
- TERMINALHIRE_DIR2 = process.env.TERMINALHIRE_DIR || join5(homedir4(), ".terminalhire");
8732
- STATUS_FILE = join5(TERMINALHIRE_DIR2, "job-status.json");
8733
- LOCK_FILE = `${STATUS_FILE}.lock`;
8734
- BAK_FILE = `${STATUS_FILE}.bak`;
8735
- }
8736
- });
8737
-
8738
- // src/open-url.js
8739
- var open_url_exports = {};
8740
- __export(open_url_exports, {
8741
- openInBrowser: () => openInBrowser
8742
- });
8743
- import { spawn } from "child_process";
8744
- function openInBrowser(url) {
8745
- let cmd;
8746
- let args5;
8747
- if (process.platform === "darwin") {
8748
- cmd = "open";
8749
- args5 = [url];
8750
- } else if (process.platform === "win32") {
8751
- cmd = "cmd";
8752
- args5 = ["/c", "start", "", url];
8753
- } else {
8754
- cmd = "xdg-open";
8755
- args5 = [url];
8756
- }
8757
- try {
8758
- const child = spawn(cmd, args5, { stdio: "ignore", detached: true });
8759
- child.on("error", () => {
8760
- });
8761
- child.unref();
8762
- } catch {
8763
- }
8764
- }
8765
- var init_open_url = __esm({
8766
- "src/open-url.js"() {
8767
- "use strict";
8768
- }
8769
- });
8770
-
8771
- // ../../node_modules/keytar/build/Release/keytar.node
8772
- var keytar_default;
8773
- var init_keytar = __esm({
8774
- "../../node_modules/keytar/build/Release/keytar.node"() {
8775
- keytar_default = "../keytar-KOAAH267.node";
8776
- }
8777
- });
8778
-
8779
- // node-file:/Users/ericgang/job-placement-inline/node_modules/keytar/build/Release/keytar.node
8780
- var require_keytar = __commonJS({
8781
- "node-file:/Users/ericgang/job-placement-inline/node_modules/keytar/build/Release/keytar.node"(exports, module) {
8782
- "use strict";
8783
- init_keytar();
8784
- try {
8785
- module.exports = __require(keytar_default);
8786
- } catch {
8787
- }
8788
- }
8789
- });
8790
-
8791
- // ../../node_modules/keytar/lib/keytar.js
8792
- var require_keytar2 = __commonJS({
8793
- "../../node_modules/keytar/lib/keytar.js"(exports, module) {
8794
- "use strict";
8795
- var keytar = require_keytar();
8796
- function checkRequired(val, name) {
8797
- if (!val || val.length <= 0) {
8798
- throw new Error(name + " is required.");
8799
- }
8800
- }
8801
- module.exports = {
8802
- getPassword: function(service, account) {
8803
- checkRequired(service, "Service");
8804
- checkRequired(account, "Account");
8805
- return keytar.getPassword(service, account);
8806
- },
8807
- setPassword: function(service, account, password) {
8808
- checkRequired(service, "Service");
8809
- checkRequired(account, "Account");
8810
- checkRequired(password, "Password");
8811
- return keytar.setPassword(service, account, password);
8812
- },
8813
- deletePassword: function(service, account) {
8814
- checkRequired(service, "Service");
8815
- checkRequired(account, "Account");
8816
- return keytar.deletePassword(service, account);
8817
- },
8818
- findPassword: function(service) {
8819
- checkRequired(service, "Service");
8820
- return keytar.findPassword(service);
8821
- },
8822
- findCredentials: function(service) {
8823
- checkRequired(service, "Service");
8824
- return keytar.findCredentials(service);
8825
- }
8826
- };
8827
- }
8828
- });
8829
-
8830
- // src/github-auth.ts
8831
- var github_auth_exports = {};
8832
- __export(github_auth_exports, {
8833
- GITHUB_SCOPE: () => GITHUB_SCOPE,
8834
- decrypt: () => decrypt,
8835
- deleteGitHubToken: () => deleteGitHubToken,
8836
- encrypt: () => encrypt,
8837
- hasGitHubToken: () => hasGitHubToken,
8838
- loadKey: () => loadKey,
8839
- readGitHubToken: () => readGitHubToken,
8840
- resolveStoredLogin: () => resolveStoredLogin,
8841
- runDeviceFlow: () => runDeviceFlow,
8842
- writeGitHubToken: () => writeGitHubToken
8843
- });
8844
- import {
8845
- createCipheriv,
8846
- createDecipheriv,
8847
- randomBytes as randomBytes3
8848
- } from "crypto";
8849
- import {
8850
- readFileSync as readFileSync6,
8851
- writeFileSync as writeFileSync5,
8852
- mkdirSync as mkdirSync5,
8853
- existsSync as existsSync5,
8854
- rmSync as rmSync2
8855
- } from "fs";
8856
- import { join as join6 } from "path";
8857
- import { homedir as homedir5 } from "os";
8858
- async function loadKey() {
8859
- try {
8860
- const kt = await Promise.resolve().then(() => __toESM(require_keytar2(), 1));
8861
- const stored = await kt.getPassword("terminalhire", "profile-key");
8862
- if (stored) return Buffer.from(stored, "hex");
8863
- const key2 = randomBytes3(KEY_BYTES);
8864
- await kt.setPassword("terminalhire", "profile-key", key2.toString("hex"));
8865
- return key2;
8866
- } catch {
8867
- }
8868
- mkdirSync5(TERMINALHIRE_DIR3, { recursive: true });
8869
- if (existsSync5(KEY_FILE)) {
8870
- return Buffer.from(readFileSync6(KEY_FILE, "utf8").trim(), "hex");
8871
- }
8872
- const key = randomBytes3(KEY_BYTES);
8873
- writeFileSync5(KEY_FILE, key.toString("hex"), { mode: 384, encoding: "utf8" });
8874
- return key;
8875
- }
8876
- function encrypt(plaintext, key) {
8877
- const iv = randomBytes3(IV_BYTES);
8878
- const cipher = createCipheriv(ALGO, key, iv);
8879
- const ct = Buffer.concat([cipher.update(plaintext, "utf8"), cipher.final()]);
8880
- const tag = cipher.getAuthTag();
8881
- return { iv: iv.toString("hex"), tag: tag.toString("hex"), ciphertext: ct.toString("hex") };
8882
- }
8883
- function decrypt(blob, key) {
8884
- const decipher = createDecipheriv(ALGO, key, Buffer.from(blob.iv, "hex"));
8885
- decipher.setAuthTag(Buffer.from(blob.tag, "hex"));
8886
- const plain = Buffer.concat([
8887
- decipher.update(Buffer.from(blob.ciphertext, "hex")),
8888
- decipher.final()
8889
- ]);
8890
- return plain.toString("utf8");
8891
- }
8892
- async function readGitHubToken() {
8893
- if (!existsSync5(TOKEN_FILE)) return void 0;
8894
- try {
8895
- const key = await loadKey();
8896
- const raw = readFileSync6(TOKEN_FILE, "utf8");
8897
- const blob = JSON.parse(raw);
8898
- return decrypt(blob, key);
8899
- } catch {
8900
- return void 0;
8901
- }
8902
- }
8903
- async function writeGitHubToken(token) {
8904
- mkdirSync5(TERMINALHIRE_DIR3, { recursive: true });
8905
- const key = await loadKey();
8906
- const blob = encrypt(token, key);
8907
- writeFileSync5(TOKEN_FILE, JSON.stringify(blob, null, 2), { encoding: "utf8" });
8908
- }
8909
- async function deleteGitHubToken() {
8910
- try {
8911
- rmSync2(TOKEN_FILE);
8912
- } catch {
8913
- }
8914
- }
8915
- async function hasGitHubToken() {
8916
- return existsSync5(TOKEN_FILE);
8917
- }
8918
- async function runDeviceFlow() {
8919
- if (process.env["TERMINALHIRE_GITHUB_MOCK"] === "1" || process.env["TERMINALHIRE_GITHUB_MOCK"] === "1" || process.env["JPI_GITHUB_MOCK"] === "1") {
8920
- console.log("\n[mock] GitHub OAuth skipped (JPI_GITHUB_MOCK=1)");
8921
- console.log(`[mock] Using fixture profile: ${MOCK_LOGIN}`);
8922
- await writeGitHubToken(MOCK_TOKEN);
8923
- return MOCK_LOGIN;
8924
- }
8925
- const clientId = process.env["GITHUB_DEVICE_CLIENT_ID"] ?? process.env["GITHUB_CLIENT_ID"] ?? BAKED_IN_CLIENT_ID;
8926
- if (clientId === "Iv1.PLACEHOLDER_REGISTER_YOUR_APP") {
8927
- console.warn("\nWarning: GITHUB_CLIENT_ID env var looks like a placeholder.");
8928
- console.warn("Remove it to use the baked-in client ID, or set it to your own OAuth App Client ID.\n");
8929
- }
8930
- const deviceRes = await fetch(DEVICE_CODE_URL, {
8931
- method: "POST",
8932
- headers: {
8933
- Accept: "application/json",
8934
- "Content-Type": "application/x-www-form-urlencoded"
8935
- },
8936
- body: new URLSearchParams({ client_id: clientId, scope: GITHUB_SCOPE }).toString(),
8937
- signal: AbortSignal.timeout(15e3)
8938
- });
8939
- if (!deviceRes.ok) {
8940
- throw new Error(`GitHub device code request failed: HTTP ${deviceRes.status}`);
8941
- }
8942
- const deviceData = await deviceRes.json();
8943
- console.log("");
8944
- console.log(" GitHub sign-in (device flow)");
8945
- console.log(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500");
8946
- console.log(` 1. Open: ${deviceData.verification_uri}`);
8947
- console.log(` 2. Enter code: ${deviceData.user_code}`);
8948
- console.log(' 3. Authorize "Terminalhire" (scope: read:user \u2014 public data only)');
8949
- console.log(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500");
8950
- console.log(" Waiting for authorization...");
8951
- console.log("");
8952
- let intervalSecs = deviceData.interval ?? 5;
8953
- const expiresAt = Date.now() + (deviceData.expires_in ?? 900) * 1e3;
8954
- const clientSecret = process.env["GITHUB_CLIENT_SECRET"];
8955
- while (Date.now() < expiresAt) {
8956
- await sleep(intervalSecs * 1e3);
8957
- const body = new URLSearchParams({
8958
- client_id: clientId,
8959
- device_code: deviceData.device_code,
8960
- grant_type: "urn:ietf:params:oauth:grant-type:device_code"
8961
- });
8962
- if (clientSecret) body.set("client_secret", clientSecret);
8963
- const tokenRes = await fetch(ACCESS_TOKEN_URL, {
8964
- method: "POST",
8965
- headers: {
8966
- Accept: "application/json",
8967
- "Content-Type": "application/x-www-form-urlencoded"
8968
- },
8969
- body: body.toString(),
8970
- signal: AbortSignal.timeout(15e3)
8971
- });
8972
- if (!tokenRes.ok) {
8973
- throw new Error(`GitHub token poll failed: HTTP ${tokenRes.status}`);
8974
- }
8975
- const tokenData = await tokenRes.json();
8976
- if (tokenData.access_token) {
8977
- await writeGitHubToken(tokenData.access_token);
8978
- const login = await fetchAuthedLogin(tokenData.access_token);
8979
- console.log(` Authorized as: ${login}`);
8980
- return login;
8981
- }
8982
- if (tokenData.error === "authorization_pending") {
8983
- continue;
8984
- }
8985
- if (tokenData.error === "slow_down") {
8986
- intervalSecs = (tokenData.interval ?? intervalSecs) + 5;
8987
- continue;
8988
- }
8989
- if (tokenData.error === "expired_token") {
8990
- throw new Error("GitHub device code expired. Please run `terminalhire login` again.");
8991
- }
8992
- if (tokenData.error === "access_denied") {
8993
- throw new Error("GitHub authorization was denied by the user.");
8994
- }
8995
- throw new Error(
8996
- `GitHub device flow error: ${tokenData.error ?? "unknown"} \u2014 ${tokenData.error_description ?? ""}`
8997
- );
8998
- }
8999
- throw new Error("GitHub device code expired before authorization. Please run `terminalhire login` again.");
9000
- }
9001
- async function fetchAuthedLogin(token) {
9002
- if (token === MOCK_TOKEN) return MOCK_LOGIN;
9003
- const res = await fetch("https://api.github.com/user", {
9004
- headers: {
9005
- Authorization: `Bearer ${token}`,
9006
- Accept: "application/vnd.github+json",
9007
- "X-GitHub-Api-Version": "2022-11-28"
9008
- },
9009
- signal: AbortSignal.timeout(1e4)
9010
- });
9011
- if (!res.ok) throw new Error(`GitHub /user: HTTP ${res.status}`);
9012
- const data = await res.json();
9013
- return data.login;
9014
- }
9015
- async function resolveStoredLogin() {
9016
- if (process.env["TERMINALHIRE_GITHUB_MOCK"] === "1" || process.env["TERMINALHIRE_GITHUB_MOCK"] === "1" || process.env["JPI_GITHUB_MOCK"] === "1") return MOCK_LOGIN;
9017
- const token = await readGitHubToken();
9018
- if (!token) return void 0;
9019
- try {
9020
- return await fetchAuthedLogin(token);
9021
- } catch {
9022
- return void 0;
9023
- }
9024
- }
9025
- function sleep(ms) {
9026
- return new Promise((resolve2) => setTimeout(resolve2, ms));
9027
- }
9028
- var TERMINALHIRE_DIR3, TOKEN_FILE, KEY_FILE, ALGO, KEY_BYTES, IV_BYTES, GITHUB_SCOPE, DEVICE_CODE_URL, ACCESS_TOKEN_URL, BAKED_IN_CLIENT_ID, MOCK_TOKEN, MOCK_LOGIN;
9029
- var init_github_auth = __esm({
9030
- "src/github-auth.ts"() {
9031
- "use strict";
9032
- TERMINALHIRE_DIR3 = join6(homedir5(), ".terminalhire");
9033
- TOKEN_FILE = join6(TERMINALHIRE_DIR3, "github-token.enc");
9034
- KEY_FILE = join6(TERMINALHIRE_DIR3, "key");
9035
- ALGO = "aes-256-gcm";
9036
- KEY_BYTES = 32;
9037
- IV_BYTES = 12;
9038
- GITHUB_SCOPE = "read:user";
9039
- DEVICE_CODE_URL = "https://github.com/login/device/code";
9040
- ACCESS_TOKEN_URL = "https://github.com/login/oauth/access_token";
9041
- BAKED_IN_CLIENT_ID = "Ov23lignE2ZSBe0J3a6B";
9042
- MOCK_TOKEN = "mock-github-token-jpi-dev";
9043
- MOCK_LOGIN = "janedev";
9044
- }
9045
- });
9046
-
9047
8925
  // src/profile.ts
9048
8926
  var profile_exports = {};
9049
8927
  __export(profile_exports, {
@@ -9064,13 +8942,13 @@ import {
9064
8942
  randomBytes as randomBytes4
9065
8943
  } from "crypto";
9066
8944
  import {
9067
- readFileSync as readFileSync7,
9068
- writeFileSync as writeFileSync6,
9069
- mkdirSync as mkdirSync6,
9070
- existsSync as existsSync6
8945
+ readFileSync as readFileSync6,
8946
+ writeFileSync as writeFileSync5,
8947
+ mkdirSync as mkdirSync5,
8948
+ existsSync as existsSync5
9071
8949
  } from "fs";
9072
- import { join as join7 } from "path";
9073
- import { homedir as homedir6 } from "os";
8950
+ import { join as join6 } from "path";
8951
+ import { homedir as homedir5 } from "os";
9074
8952
  async function loadKey2() {
9075
8953
  try {
9076
8954
  const kt = await Promise.resolve().then(() => __toESM(require_keytar2(), 1));
@@ -9083,12 +8961,12 @@ async function loadKey2() {
9083
8961
  return key2;
9084
8962
  } catch {
9085
8963
  }
9086
- mkdirSync6(TERMINALHIRE_DIR4, { recursive: true });
9087
- if (existsSync6(KEY_FILE2)) {
9088
- return Buffer.from(readFileSync7(KEY_FILE2, "utf8").trim(), "hex");
8964
+ mkdirSync5(TERMINALHIRE_DIR3, { recursive: true });
8965
+ if (existsSync5(KEY_FILE2)) {
8966
+ return Buffer.from(readFileSync6(KEY_FILE2, "utf8").trim(), "hex");
9089
8967
  }
9090
8968
  const key = randomBytes4(KEY_BYTES2);
9091
- writeFileSync6(KEY_FILE2, key.toString("hex"), { mode: 384, encoding: "utf8" });
8969
+ writeFileSync5(KEY_FILE2, key.toString("hex"), { mode: 384, encoding: "utf8" });
9092
8970
  return key;
9093
8971
  }
9094
8972
  function encrypt2(plaintext, key) {
@@ -9146,10 +9024,10 @@ function migrateTagWeights(profile) {
9146
9024
  }
9147
9025
  }
9148
9026
  async function readProfile() {
9149
- if (!existsSync6(PROFILE_FILE)) return blankProfile();
9027
+ if (!existsSync5(PROFILE_FILE)) return blankProfile();
9150
9028
  try {
9151
9029
  const key = await loadKey2();
9152
- const raw = readFileSync7(PROFILE_FILE, "utf8");
9030
+ const raw = readFileSync6(PROFILE_FILE, "utf8");
9153
9031
  const blob = JSON.parse(raw);
9154
9032
  const plaintext = decrypt2(blob, key);
9155
9033
  const parsed = JSON.parse(plaintext);
@@ -9160,12 +9038,12 @@ async function readProfile() {
9160
9038
  }
9161
9039
  }
9162
9040
  async function writeProfile(profile) {
9163
- mkdirSync6(TERMINALHIRE_DIR4, { recursive: true });
9041
+ mkdirSync5(TERMINALHIRE_DIR3, { recursive: true });
9164
9042
  const key = await loadKey2();
9165
9043
  profile.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
9166
9044
  profile.skillTags = deriveSkillTags(profile.tagWeights);
9167
9045
  const blob = encrypt2(JSON.stringify(profile), key);
9168
- writeFileSync6(PROFILE_FILE, JSON.stringify(blob, null, 2), { encoding: "utf8" });
9046
+ writeFileSync5(PROFILE_FILE, JSON.stringify(blob, null, 2), { encoding: "utf8" });
9169
9047
  }
9170
9048
  function accumulateSession(profile, tags, isEmployerContext2, inferredSeniority, seniorityIsAuthoritative = false) {
9171
9049
  const now = (/* @__PURE__ */ new Date()).toISOString();
@@ -9249,14 +9127,14 @@ function profileToFingerprint(profile) {
9249
9127
  }
9250
9128
  };
9251
9129
  }
9252
- var TERMINALHIRE_DIR4, PROFILE_FILE, KEY_FILE2, ALGO2, KEY_BYTES2, IV_BYTES2, DECAY_HALF_LIFE_MS, LANGUAGE_TAGS, MIN_FINGERPRINT_SCORE;
9130
+ var TERMINALHIRE_DIR3, PROFILE_FILE, KEY_FILE2, ALGO2, KEY_BYTES2, IV_BYTES2, DECAY_HALF_LIFE_MS, LANGUAGE_TAGS, MIN_FINGERPRINT_SCORE;
9253
9131
  var init_profile = __esm({
9254
9132
  "src/profile.ts"() {
9255
9133
  "use strict";
9256
9134
  init_src();
9257
- TERMINALHIRE_DIR4 = join7(homedir6(), ".terminalhire");
9258
- PROFILE_FILE = join7(TERMINALHIRE_DIR4, "profile.enc");
9259
- KEY_FILE2 = join7(TERMINALHIRE_DIR4, "key");
9135
+ TERMINALHIRE_DIR3 = join6(homedir5(), ".terminalhire");
9136
+ PROFILE_FILE = join6(TERMINALHIRE_DIR3, "profile.enc");
9137
+ KEY_FILE2 = join6(TERMINALHIRE_DIR3, "key");
9260
9138
  ALGO2 = "aes-256-gcm";
9261
9139
  KEY_BYTES2 = 32;
9262
9140
  IV_BYTES2 = 12;
@@ -9549,6 +9427,128 @@ var init_jpi_login = __esm({
9549
9427
  }
9550
9428
  });
9551
9429
 
9430
+ // bin/job-status-store.js
9431
+ var job_status_store_exports = {};
9432
+ __export(job_status_store_exports, {
9433
+ markClicked: () => markClicked,
9434
+ markStatus: () => markStatus,
9435
+ readStatusMap: () => readStatusMap,
9436
+ statusFilePath: () => statusFilePath
9437
+ });
9438
+ import {
9439
+ readFileSync as readFileSync7,
9440
+ writeFileSync as writeFileSync6,
9441
+ renameSync,
9442
+ mkdirSync as mkdirSync6,
9443
+ existsSync as existsSync6,
9444
+ copyFileSync,
9445
+ openSync,
9446
+ closeSync,
9447
+ unlinkSync
9448
+ } from "fs";
9449
+ import { join as join7, dirname } from "path";
9450
+ import { homedir as homedir6 } from "os";
9451
+ function statusFilePath() {
9452
+ return STATUS_FILE;
9453
+ }
9454
+ function atomicWriteJson(path, obj) {
9455
+ mkdirSync6(dirname(path), { recursive: true });
9456
+ const tmp = `${path}.tmp-${process.pid}`;
9457
+ writeFileSync6(tmp, JSON.stringify(obj, null, 2) + "\n", "utf8");
9458
+ renameSync(tmp, path);
9459
+ }
9460
+ function sleepMs(ms) {
9461
+ try {
9462
+ Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
9463
+ } catch {
9464
+ const end = Date.now() + ms;
9465
+ while (Date.now() < end) {
9466
+ }
9467
+ }
9468
+ }
9469
+ function withLock(fn) {
9470
+ const deadline = Date.now() + 2e3;
9471
+ for (; ; ) {
9472
+ let fd;
9473
+ try {
9474
+ mkdirSync6(dirname(LOCK_FILE), { recursive: true });
9475
+ fd = openSync(LOCK_FILE, "wx");
9476
+ } catch (err) {
9477
+ if (err && err.code === "EEXIST") {
9478
+ if (Date.now() > deadline) {
9479
+ try {
9480
+ unlinkSync(LOCK_FILE);
9481
+ } catch {
9482
+ }
9483
+ continue;
9484
+ }
9485
+ sleepMs(5);
9486
+ continue;
9487
+ }
9488
+ throw err;
9489
+ }
9490
+ try {
9491
+ return fn();
9492
+ } finally {
9493
+ try {
9494
+ closeSync(fd);
9495
+ } catch {
9496
+ }
9497
+ try {
9498
+ unlinkSync(LOCK_FILE);
9499
+ } catch {
9500
+ }
9501
+ }
9502
+ }
9503
+ }
9504
+ function readStatusMap() {
9505
+ if (!existsSync6(STATUS_FILE)) return {};
9506
+ let raw;
9507
+ try {
9508
+ raw = readFileSync7(STATUS_FILE, "utf8");
9509
+ } catch {
9510
+ return {};
9511
+ }
9512
+ try {
9513
+ const parsed = JSON.parse(raw);
9514
+ if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) return parsed;
9515
+ throw new Error("status store is not an object");
9516
+ } catch {
9517
+ try {
9518
+ copyFileSync(STATUS_FILE, BAK_FILE);
9519
+ } catch {
9520
+ }
9521
+ return {};
9522
+ }
9523
+ }
9524
+ function markStatus(id, status) {
9525
+ return withLock(() => {
9526
+ const current = readStatusMap();
9527
+ const next = status === "claimed" ? { ...current, [id]: { ...current[id], status: "claimed", markedAt: (/* @__PURE__ */ new Date()).toISOString() } } : setStatus(current, id, status);
9528
+ atomicWriteJson(STATUS_FILE, next);
9529
+ return next[id];
9530
+ });
9531
+ }
9532
+ function markClicked(id) {
9533
+ return withLock(() => {
9534
+ const current = readStatusMap();
9535
+ const next = recordClick(current, id);
9536
+ atomicWriteJson(STATUS_FILE, next);
9537
+ return next[id];
9538
+ });
9539
+ }
9540
+ var TERMINALHIRE_DIR4, STATUS_FILE, LOCK_FILE, BAK_FILE;
9541
+ var init_job_status_store = __esm({
9542
+ "bin/job-status-store.js"() {
9543
+ "use strict";
9544
+ init_src();
9545
+ TERMINALHIRE_DIR4 = process.env.TERMINALHIRE_DIR || join7(homedir6(), ".terminalhire");
9546
+ STATUS_FILE = join7(TERMINALHIRE_DIR4, "job-status.json");
9547
+ LOCK_FILE = `${STATUS_FILE}.lock`;
9548
+ BAK_FILE = `${STATUS_FILE}.bak`;
9549
+ }
9550
+ });
9551
+
9552
9552
  // bin/cache-store.js
9553
9553
  var cache_store_exports = {};
9554
9554
  __export(cache_store_exports, {
@@ -43097,12 +43097,7 @@ async function run22() {
43097
43097
  unpushedClaims,
43098
43098
  baseUrl: API_URL8,
43099
43099
  seenHistory,
43100
- widen,
43101
- // When the refresh monitor set up its loopback click-catcher, it threads
43102
- // the base in via this env var so tip /j/ links record clicks locally.
43103
- // Absent (a manual `terminalhire refresh` outside the monitor) ⇒ undefined
43104
- // ⇒ public /j/ URLs, unchanged. baseUrl/API_URL are untouched.
43105
- clickCatcher: process.env.TH_CLICK_CATCHER || void 0
43100
+ widen
43106
43101
  });
43107
43102
  } catch {
43108
43103
  }
@@ -43686,17 +43681,6 @@ if (firstArg === "chat" || firstArg === "link" || firstArg === "claim") {
43686
43681
  } catch {
43687
43682
  }
43688
43683
  }
43689
- if (firstArg === "__record-click") {
43690
- const id = process.argv[3];
43691
- if (id && /^[a-z0-9._-]+:.+$/.test(id)) {
43692
- try {
43693
- const { markClicked: markClicked2 } = await Promise.resolve().then(() => (init_job_status_store(), job_status_store_exports));
43694
- markClicked2(id);
43695
- } catch {
43696
- }
43697
- }
43698
- process.exit(0);
43699
- }
43700
43684
  if (firstArg === "login" || firstArg === "logout") {
43701
43685
  const mod2 = await Promise.resolve().then(() => (init_jpi_login(), jpi_login_exports));
43702
43686
  await mod2.run();
@@ -10484,12 +10484,7 @@ async function run() {
10484
10484
  unpushedClaims,
10485
10485
  baseUrl: API_URL2,
10486
10486
  seenHistory,
10487
- widen,
10488
- // When the refresh monitor set up its loopback click-catcher, it threads
10489
- // the base in via this env var so tip /j/ links record clicks locally.
10490
- // Absent (a manual `terminalhire refresh` outside the monitor) ⇒ undefined
10491
- // ⇒ public /j/ URLs, unchanged. baseUrl/API_URL are untouched.
10492
- clickCatcher: process.env.TH_CLICK_CATCHER || void 0
10487
+ widen
10493
10488
  });
10494
10489
  } catch {
10495
10490
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "terminalhire",
3
- "version": "0.30.1",
3
+ "version": "0.30.2",
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",