terminalhire 0.35.1 → 0.35.4

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.
Files changed (42) hide show
  1. package/dist/bin/claim-push-bg.js +11 -59
  2. package/dist/bin/jpi-bounties.js +97 -67
  3. package/dist/bin/jpi-chat-read.js +25 -66
  4. package/dist/bin/jpi-chat.js +29 -70
  5. package/dist/bin/jpi-claim.js +99 -69
  6. package/dist/bin/jpi-config.js +59 -4
  7. package/dist/bin/jpi-contribute.js +23 -95
  8. package/dist/bin/jpi-devs.js +98 -67
  9. package/dist/bin/jpi-dispatch.js +243 -165
  10. package/dist/bin/jpi-hub.js +107 -76
  11. package/dist/bin/jpi-inbox.js +29 -70
  12. package/dist/bin/jpi-init.js +84 -6
  13. package/dist/bin/jpi-intro.js +15 -62
  14. package/dist/bin/jpi-jobs.js +108 -72
  15. package/dist/bin/jpi-learn.js +14 -61
  16. package/dist/bin/jpi-link.js +11 -5
  17. package/dist/bin/jpi-login.js +109 -73
  18. package/dist/bin/jpi-mcp.js +29 -11
  19. package/dist/bin/jpi-profile.js +14 -61
  20. package/dist/bin/jpi-project.js +84 -6
  21. package/dist/bin/jpi-refresh.js +167 -112
  22. package/dist/bin/jpi-repo.js +13 -60
  23. package/dist/bin/jpi-save.js +14 -61
  24. package/dist/bin/jpi-spinner.js +0 -12
  25. package/dist/bin/jpi-sync.js +14 -61
  26. package/dist/bin/jpi-trajectory.js +16 -63
  27. package/dist/bin/peer-connect-prompt.js +10 -4
  28. package/dist/bin/pulse-prompt.js +11 -5
  29. package/dist/bin/spinner.js +4 -15
  30. package/dist/src/chat-client.js +14 -65
  31. package/dist/src/chat-keystore.js +12 -63
  32. package/dist/src/config.js +27 -9
  33. package/dist/src/crypto-store.js +12 -63
  34. package/dist/src/github-auth.js +11 -62
  35. package/dist/src/intro.js +15 -62
  36. package/dist/src/link.js +11 -5
  37. package/dist/src/profile.js +13 -64
  38. package/dist/src/repo-experience.js +12 -63
  39. package/dist/src/trajectory.js +16 -63
  40. package/dist/src/web-session.js +1 -1
  41. package/package.json +2 -2
  42. package/dist/keytar-KOAAH267.node +0 -0
@@ -4,16 +4,10 @@ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
4
  var __getOwnPropNames = Object.getOwnPropertyNames;
5
5
  var __getProtoOf = Object.getPrototypeOf;
6
6
  var __hasOwnProp = Object.prototype.hasOwnProperty;
7
- var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
8
- get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
9
- }) : x)(function(x) {
10
- if (typeof require !== "undefined") return require.apply(this, arguments);
11
- throw Error('Dynamic require of "' + x + '" is not supported');
12
- });
13
7
  var __esm = (fn, res) => function __init() {
14
8
  return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
15
9
  };
16
- var __commonJS = (cb, mod) => function __require2() {
10
+ var __commonJS = (cb, mod) => function __require() {
17
11
  return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
18
12
  };
19
13
  var __export = (target, all) => {
@@ -37,61 +31,19 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
37
31
  mod
38
32
  ));
39
33
 
40
- // ../../node_modules/keytar/build/Release/keytar.node
41
- var keytar_default;
42
- var init_keytar = __esm({
43
- "../../node_modules/keytar/build/Release/keytar.node"() {
44
- keytar_default = "../keytar-KOAAH267.node";
45
- }
46
- });
47
-
48
- // node-file:/private/tmp/claude-501/-Users-ericgang-job-placement-inline/9716ff9c-0531-4844-adf4-286763cf8ab8/scratchpad/deeplink-wt/node_modules/keytar/build/Release/keytar.node
49
- var require_keytar = __commonJS({
50
- "node-file:/private/tmp/claude-501/-Users-ericgang-job-placement-inline/9716ff9c-0531-4844-adf4-286763cf8ab8/scratchpad/deeplink-wt/node_modules/keytar/build/Release/keytar.node"(exports, module) {
51
- "use strict";
52
- init_keytar();
53
- try {
54
- module.exports = __require(keytar_default);
55
- } catch {
56
- }
57
- }
58
- });
59
-
60
34
  // ../../node_modules/keytar/lib/keytar.js
61
- var require_keytar2 = __commonJS({
35
+ var require_keytar = __commonJS({
62
36
  "../../node_modules/keytar/lib/keytar.js"(exports, module) {
63
37
  "use strict";
64
- var keytar = require_keytar();
65
- function checkRequired(val, name) {
66
- if (!val || val.length <= 0) {
67
- throw new Error(name + " is required.");
68
- }
38
+ function disabled() {
39
+ throw new Error("keytar disabled in this dev checkout (keychain popup guard) \u2014 key-file fallback expected");
69
40
  }
70
41
  module.exports = {
71
- getPassword: function(service, account) {
72
- checkRequired(service, "Service");
73
- checkRequired(account, "Account");
74
- return keytar.getPassword(service, account);
75
- },
76
- setPassword: function(service, account, password) {
77
- checkRequired(service, "Service");
78
- checkRequired(account, "Account");
79
- checkRequired(password, "Password");
80
- return keytar.setPassword(service, account, password);
81
- },
82
- deletePassword: function(service, account) {
83
- checkRequired(service, "Service");
84
- checkRequired(account, "Account");
85
- return keytar.deletePassword(service, account);
86
- },
87
- findPassword: function(service) {
88
- checkRequired(service, "Service");
89
- return keytar.findPassword(service);
90
- },
91
- findCredentials: function(service) {
92
- checkRequired(service, "Service");
93
- return keytar.findCredentials(service);
94
- }
42
+ getPassword: disabled,
43
+ setPassword: disabled,
44
+ deletePassword: disabled,
45
+ findPassword: disabled,
46
+ findCredentials: disabled
95
47
  };
96
48
  }
97
49
  });
@@ -301,7 +253,7 @@ import {
301
253
  } from "fs";
302
254
  import { join } from "path";
303
255
  import { homedir } from "os";
304
- var TERMINALHIRE_DIR = join(homedir(), ".terminalhire");
256
+ var TERMINALHIRE_DIR = process.env.TERMINALHIRE_DIR || join(homedir(), ".terminalhire");
305
257
  var TOKEN_FILE = join(TERMINALHIRE_DIR, "github-token.enc");
306
258
  var KEY_FILE = join(TERMINALHIRE_DIR, "key");
307
259
  var ALGO = "aes-256-gcm";
@@ -313,7 +265,7 @@ function skipKeychain() {
313
265
  async function loadKey() {
314
266
  if (!skipKeychain()) {
315
267
  try {
316
- const kt = await Promise.resolve().then(() => __toESM(require_keytar2(), 1));
268
+ const kt = await Promise.resolve().then(() => __toESM(require_keytar(), 1));
317
269
  const stored = await kt.getPassword("terminalhire", "profile-key");
318
270
  if (stored) return Buffer.from(stored, "hex");
319
271
  const key2 = randomBytes(KEY_BYTES);
@@ -5,16 +5,10 @@ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
5
  var __getOwnPropNames = Object.getOwnPropertyNames;
6
6
  var __getProtoOf = Object.getPrototypeOf;
7
7
  var __hasOwnProp = Object.prototype.hasOwnProperty;
8
- var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
9
- get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
10
- }) : x)(function(x) {
11
- if (typeof require !== "undefined") return require.apply(this, arguments);
12
- throw Error('Dynamic require of "' + x + '" is not supported');
13
- });
14
8
  var __esm = (fn, res) => function __init() {
15
9
  return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
16
10
  };
17
- var __commonJS = (cb, mod2) => function __require2() {
11
+ var __commonJS = (cb, mod2) => function __require() {
18
12
  return mod2 || (0, cb[__getOwnPropNames(cb)[0]])((mod2 = { exports: {} }).exports, mod2), mod2.exports;
19
13
  };
20
14
  var __export = (target, all) => {
@@ -1735,6 +1729,25 @@ async function resolveClosingIssues(owner, name, number, body, token, signal, go
1735
1729
  while ((m = re.exec(body)) !== null) nums.add(parseInt(m[1], 10));
1736
1730
  return { closesIssues: [...nums], linkageSource: nums.size ? "body-keyword" : "none" };
1737
1731
  }
1732
+ async function openPRClosingRefs(owner, name, token, signal, governor) {
1733
+ const q = `query($o:String!,$n:String!){repository(owner:$o,name:$n){pullRequests(states:OPEN,first:100){totalCount nodes{number closingIssuesReferences(first:20){nodes{number}}}}}rateLimit{cost remaining}}`;
1734
+ try {
1735
+ const r = await ghGraphQL(q, { o: owner, n: name }, token, signal, governor);
1736
+ if (r === null) return null;
1737
+ if (!r.data?.repository) return null;
1738
+ const prs = r.data.repository.pullRequests;
1739
+ if (prs && prs.nodes !== void 0 && !Array.isArray(prs.nodes)) return null;
1740
+ const nodes = prs?.nodes ?? [];
1741
+ const refs = /* @__PURE__ */ new Set();
1742
+ for (const node of nodes) {
1743
+ for (const ref of node.closingIssuesReferences?.nodes ?? []) refs.add(ref.number);
1744
+ }
1745
+ const totalCount = prs?.totalCount ?? nodes.length;
1746
+ return { refs, capHit: totalCount > nodes.length, totalCount };
1747
+ } catch {
1748
+ return null;
1749
+ }
1750
+ }
1738
1751
  function makeScoringGovernor(governor) {
1739
1752
  return governor ?? makeGitHubGovernor(
1740
1753
  ((url, init) => fetch(url, init)),
@@ -3951,7 +3964,7 @@ function makeClient(fetchImpl, cfg) {
3951
3964
  async function probe(path) {
3952
3965
  return gov.probe(`${GITHUB_API2}${path}`, { headers: authHeaders2() });
3953
3966
  }
3954
- return { raw, json, probe, setSecondaryHint: gov.setSecondaryHint, getStats: gov.getStats };
3967
+ return { raw, json, probe, governor: gov, setSecondaryHint: gov.setSecondaryHint, getStats: gov.getStats };
3955
3968
  }
3956
3969
  async function contributorCount(client, fullName) {
3957
3970
  const res = await client.raw(`/repos/${fullName}/contributors?per_page=1&anon=false`);
@@ -3967,14 +3980,27 @@ async function contributorCount(client, fullName) {
3967
3980
  }
3968
3981
  }
3969
3982
  async function openPRIssueRefs(client, fullName) {
3983
+ const token = process.env["GITHUB_TOKEN"] ?? process.env["GH_TOKEN"];
3984
+ const [owner, name] = fullName.split("/");
3985
+ if (token && owner && name) {
3986
+ const res = await openPRClosingRefs(owner, name, token, void 0, client.governor);
3987
+ if (res === null) return null;
3988
+ if (res.capHit) {
3989
+ console.warn(
3990
+ `[contribute] open-PR closing-ref scan capped at 100/${res.totalCount} open PRs for ${fullName} (closing refs beyond the first 100 open PRs not scanned)`
3991
+ );
3992
+ }
3993
+ return res.refs;
3994
+ }
3970
3995
  const prs = await client.json(
3971
3996
  `/repos/${fullName}/pulls?state=open&per_page=100`
3972
3997
  );
3973
3998
  if (!Array.isArray(prs)) return null;
3974
3999
  const refs = /* @__PURE__ */ new Set();
3975
4000
  for (const pr of prs) {
3976
- for (const m of `${pr.title ?? ""}
3977
- ${pr.body ?? ""}`.matchAll(/#(\d+)\b/g)) {
4001
+ const text = `${pr.title ?? ""}
4002
+ ${pr.body ?? ""}`;
4003
+ for (const m of text.matchAll(/\b(?:close[sd]?|fix(?:e[sd])?|resolve[sd]?)\s+#(\d+)/gi)) {
3978
4004
  refs.add(Number(m[1]));
3979
4005
  }
3980
4006
  }
@@ -4028,7 +4054,12 @@ function buildContributionJob(a) {
4028
4054
  // TERM-27: persist the repo's primary language so project curation can
4029
4055
  // exclude the repo's OWN language id (folded into every issue's tags) from
4030
4056
  // the distinct-skill signal. Same `repo` used by the tokenize() tag build.
4031
- language: a.repo.language ?? null
4057
+ language: a.repo.language ?? null,
4058
+ // TERM-35: stamp the search item's comment count (already in the response —
4059
+ // zero extra egress). A NEUTRAL volume signal only: set solely when the
4060
+ // search item carries a finite count >= 0; a failed/absent value leaves it
4061
+ // undefined (never a fabricated 0), so a render's chip falls through cleanly.
4062
+ commentsAtDiscovery: typeof a.issue.comments === "number" && Number.isFinite(a.issue.comments) && a.issue.comments >= 0 ? a.issue.comments : void 0
4032
4063
  },
4033
4064
  // Provenance: repo-first discovered items only (label-first omits the field).
4034
4065
  ...a.discovered ? { discovered: true } : {},
@@ -4352,6 +4383,7 @@ var init_contributions = __esm({
4352
4383
  init_contribution_gate();
4353
4384
  init_contribution_classify();
4354
4385
  init_github_bounties();
4386
+ init_github();
4355
4387
  init_http();
4356
4388
  init_gh_governor();
4357
4389
  GITHUB_API2 = "https://api.github.com";
@@ -4543,6 +4575,15 @@ async function enrichWinnability(jobs, contribute, w) {
4543
4575
  );
4544
4576
  }
4545
4577
  }
4578
+ function hasClickableUrl(url) {
4579
+ if (!url) return false;
4580
+ try {
4581
+ const parsed = new URL(url);
4582
+ return parsed.protocol === "http:" || parsed.protocol === "https:";
4583
+ } catch {
4584
+ return false;
4585
+ }
4586
+ }
4546
4587
  async function buildIndex(opts) {
4547
4588
  const includePartners = opts?.includePartners ?? true;
4548
4589
  const publicJobs = await aggregate(opts);
@@ -4553,6 +4594,7 @@ async function buildIndex(opts) {
4553
4594
  ...opts?.partnerRoles ?? []
4554
4595
  ];
4555
4596
  for (const job of partnerJobs) {
4597
+ if (!hasClickableUrl(job.url)) continue;
4556
4598
  if (!seen.has(job.id)) {
4557
4599
  seen.add(job.id);
4558
4600
  allJobs.push(job);
@@ -4634,7 +4676,8 @@ async function fetchIssueStatus(fullName, issueNumber, opts = {}) {
4634
4676
  for (const a of body.assignees ?? []) {
4635
4677
  if (a && typeof a.login === "string") assignees.add(a.login);
4636
4678
  }
4637
- return { state, assignees: [...assignees] };
4679
+ const comments = typeof body.comments === "number" && Number.isFinite(body.comments) && body.comments >= 0 ? body.comments : null;
4680
+ return { state, assignees: [...assignees], comments };
4638
4681
  }
4639
4682
  var GITHUB_API3, DEFAULT_ISSUE_STATUS_TIMEOUT_MS;
4640
4683
  var init_github_issue_status = __esm({
@@ -8200,11 +8243,35 @@ import { createHash as createHash2 } from "crypto";
8200
8243
  function opportunityShortToken(id) {
8201
8244
  return createHash2("sha256").update(id, "utf8").digest("base64url").slice(0, 8);
8202
8245
  }
8203
- var contributeShortToken;
8246
+ function jobShortToken(id) {
8247
+ return createHash2("sha256").update(`job:${id}`, "utf8").digest("base64url").slice(0, 8);
8248
+ }
8249
+ function jobTokenMap(index) {
8250
+ const cached = jobTokenMaps.get(index);
8251
+ if (cached) return cached;
8252
+ const map = /* @__PURE__ */ new Map();
8253
+ for (const job of index.jobs) {
8254
+ const token = jobShortToken(job.id);
8255
+ map.set(token, map.has(token) ? AMBIGUOUS_JOB_TOKEN : job);
8256
+ }
8257
+ jobTokenMaps.set(index, map);
8258
+ return map;
8259
+ }
8260
+ function resolveJobToken(index, token) {
8261
+ const hit = jobTokenMap(index).get(token);
8262
+ return hit && hit !== AMBIGUOUS_JOB_TOKEN ? hit : null;
8263
+ }
8264
+ function _jobTokenMapForTests(index) {
8265
+ return jobTokenMap(index);
8266
+ }
8267
+ var contributeShortToken, AMBIGUOUS_JOB_TOKEN, jobTokenMaps, _AMBIGUOUS_JOB_TOKEN_FOR_TESTS;
8204
8268
  var init_short_token = __esm({
8205
8269
  "../../packages/core/src/short-token.ts"() {
8206
8270
  "use strict";
8207
8271
  contributeShortToken = opportunityShortToken;
8272
+ AMBIGUOUS_JOB_TOKEN = /* @__PURE__ */ Symbol("ambiguous-job-token");
8273
+ jobTokenMaps = /* @__PURE__ */ new WeakMap();
8274
+ _AMBIGUOUS_JOB_TOKEN_FOR_TESTS = AMBIGUOUS_JOB_TOKEN;
8208
8275
  }
8209
8276
  });
8210
8277
 
@@ -8252,6 +8319,8 @@ __export(src_exports, {
8252
8319
  VOCABULARY: () => VOCABULARY,
8253
8320
  VOCAB_NODES: () => VOCAB_NODES,
8254
8321
  WINNABILITY_NORM: () => WINNABILITY_NORM,
8322
+ _AMBIGUOUS_JOB_TOKEN_FOR_TESTS: () => _AMBIGUOUS_JOB_TOKEN_FOR_TESTS,
8323
+ _jobTokenMapForTests: () => _jobTokenMapForTests,
8255
8324
  acceptanceCountForDomains: () => acceptanceCountForDomains,
8256
8325
  aggregate: () => aggregate,
8257
8326
  aggregateBounties: () => aggregateBounties,
@@ -8318,6 +8387,7 @@ __export(src_exports, {
8318
8387
  isOverIntroLimit: () => isOverIntroLimit,
8319
8388
  isTrivialPRTitle: () => isTrivialPRTitle,
8320
8389
  isWinnableIssue: () => isWinnableIssue,
8390
+ jobShortToken: () => jobShortToken,
8321
8391
  joinLabels: () => joinLabels,
8322
8392
  labelFor: () => labelFor,
8323
8393
  lever: () => lever,
@@ -8331,6 +8401,7 @@ __export(src_exports, {
8331
8401
  mergeProbability: () => mergeProbability,
8332
8402
  mmrRerank: () => mmrRerank,
8333
8403
  normalize: () => normalize,
8404
+ openPRClosingRefs: () => openPRClosingRefs,
8334
8405
  opire: () => opire,
8335
8406
  opportunityShortToken: () => opportunityShortToken,
8336
8407
  pageMatches: () => pageMatches,
@@ -8345,6 +8416,7 @@ __export(src_exports, {
8345
8416
  recordClick: () => recordClick,
8346
8417
  rejectExtraIntroFields: () => rejectExtraIntroFields,
8347
8418
  relevanceScore: () => relevanceScore,
8419
+ resolveJobToken: () => resolveJobToken,
8348
8420
  revealIntroContacts: () => revealIntroContacts,
8349
8421
  rosterActiveFromContribution: () => rosterActiveFromContribution,
8350
8422
  safetyNumber: () => safetyNumber,
@@ -8386,61 +8458,19 @@ var init_src = __esm({
8386
8458
  }
8387
8459
  });
8388
8460
 
8389
- // ../../node_modules/keytar/build/Release/keytar.node
8390
- var keytar_default;
8391
- var init_keytar = __esm({
8392
- "../../node_modules/keytar/build/Release/keytar.node"() {
8393
- keytar_default = "../keytar-KOAAH267.node";
8394
- }
8395
- });
8396
-
8397
- // node-file:/private/tmp/claude-501/-Users-ericgang-job-placement-inline/9716ff9c-0531-4844-adf4-286763cf8ab8/scratchpad/deeplink-wt/node_modules/keytar/build/Release/keytar.node
8398
- var require_keytar = __commonJS({
8399
- "node-file:/private/tmp/claude-501/-Users-ericgang-job-placement-inline/9716ff9c-0531-4844-adf4-286763cf8ab8/scratchpad/deeplink-wt/node_modules/keytar/build/Release/keytar.node"(exports, module) {
8400
- "use strict";
8401
- init_keytar();
8402
- try {
8403
- module.exports = __require(keytar_default);
8404
- } catch {
8405
- }
8406
- }
8407
- });
8408
-
8409
8461
  // ../../node_modules/keytar/lib/keytar.js
8410
- var require_keytar2 = __commonJS({
8462
+ var require_keytar = __commonJS({
8411
8463
  "../../node_modules/keytar/lib/keytar.js"(exports, module) {
8412
8464
  "use strict";
8413
- var keytar = require_keytar();
8414
- function checkRequired(val, name) {
8415
- if (!val || val.length <= 0) {
8416
- throw new Error(name + " is required.");
8417
- }
8465
+ function disabled() {
8466
+ throw new Error("keytar disabled in this dev checkout (keychain popup guard) \u2014 key-file fallback expected");
8418
8467
  }
8419
8468
  module.exports = {
8420
- getPassword: function(service, account) {
8421
- checkRequired(service, "Service");
8422
- checkRequired(account, "Account");
8423
- return keytar.getPassword(service, account);
8424
- },
8425
- setPassword: function(service, account, password) {
8426
- checkRequired(service, "Service");
8427
- checkRequired(account, "Account");
8428
- checkRequired(password, "Password");
8429
- return keytar.setPassword(service, account, password);
8430
- },
8431
- deletePassword: function(service, account) {
8432
- checkRequired(service, "Service");
8433
- checkRequired(account, "Account");
8434
- return keytar.deletePassword(service, account);
8435
- },
8436
- findPassword: function(service) {
8437
- checkRequired(service, "Service");
8438
- return keytar.findPassword(service);
8439
- },
8440
- findCredentials: function(service) {
8441
- checkRequired(service, "Service");
8442
- return keytar.findCredentials(service);
8443
- }
8469
+ getPassword: disabled,
8470
+ setPassword: disabled,
8471
+ deletePassword: disabled,
8472
+ findPassword: disabled,
8473
+ findCredentials: disabled
8444
8474
  };
8445
8475
  }
8446
8476
  });
@@ -8492,7 +8522,7 @@ function skipKeychain() {
8492
8522
  async function tryLoadFromKeytar(policy) {
8493
8523
  if (forceKeytarUnavailableForTests || skipKeychain()) return null;
8494
8524
  try {
8495
- const kt = policy === "keychain-required" ? createRequire(import.meta.url)("keytar") : await Promise.resolve().then(() => __toESM(require_keytar2(), 1));
8525
+ const kt = policy === "keychain-required" ? createRequire(import.meta.url)("keytar") : await Promise.resolve().then(() => __toESM(require_keytar(), 1));
8496
8526
  const stored = await kt.getPassword(KEYTAR_SERVICE, KEYTAR_ACCOUNT);
8497
8527
  if (stored) {
8498
8528
  return Buffer.from(stored, "hex");
@@ -8533,7 +8563,7 @@ async function deleteKey() {
8533
8563
  }
8534
8564
  if (!forceKeytarUnavailableForTests && !skipKeychain()) {
8535
8565
  try {
8536
- const kt = await Promise.resolve().then(() => __toESM(require_keytar2(), 1));
8566
+ const kt = await Promise.resolve().then(() => __toESM(require_keytar(), 1));
8537
8567
  await kt.deletePassword(KEYTAR_SERVICE, KEYTAR_ACCOUNT);
8538
8568
  } catch {
8539
8569
  }
@@ -8584,7 +8614,7 @@ var TERMINALHIRE_DIR2, KEY_FILE, KEYTAR_SERVICE, KEYTAR_ACCOUNT, ALGO, KEY_BYTES
8584
8614
  var init_crypto_store = __esm({
8585
8615
  "src/crypto-store.ts"() {
8586
8616
  "use strict";
8587
- TERMINALHIRE_DIR2 = join3(homedir2(), ".terminalhire");
8617
+ TERMINALHIRE_DIR2 = process.env.TERMINALHIRE_DIR || join3(homedir2(), ".terminalhire");
8588
8618
  KEY_FILE = join3(TERMINALHIRE_DIR2, "key");
8589
8619
  KEYTAR_SERVICE = "terminalhire";
8590
8620
  KEYTAR_ACCOUNT = "profile-key";
@@ -8734,7 +8764,7 @@ var init_profile = __esm({
8734
8764
  "use strict";
8735
8765
  init_src();
8736
8766
  init_crypto_store();
8737
- TERMINALHIRE_DIR3 = join4(homedir3(), ".terminalhire");
8767
+ TERMINALHIRE_DIR3 = process.env.TERMINALHIRE_DIR || join4(homedir3(), ".terminalhire");
8738
8768
  PROFILE_FILE = join4(TERMINALHIRE_DIR3, "profile.enc");
8739
8769
  profileStore = createEncryptedStore(PROFILE_FILE, {
8740
8770
  blank: blankProfile,
@@ -5,16 +5,10 @@ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
5
  var __getOwnPropNames = Object.getOwnPropertyNames;
6
6
  var __getProtoOf = Object.getPrototypeOf;
7
7
  var __hasOwnProp = Object.prototype.hasOwnProperty;
8
- var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
9
- get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
10
- }) : x)(function(x) {
11
- if (typeof require !== "undefined") return require.apply(this, arguments);
12
- throw Error('Dynamic require of "' + x + '" is not supported');
13
- });
14
8
  var __esm = (fn, res) => function __init() {
15
9
  return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
16
10
  };
17
- var __commonJS = (cb, mod2) => function __require2() {
11
+ var __commonJS = (cb, mod2) => function __require() {
18
12
  return mod2 || (0, cb[__getOwnPropNames(cb)[0]])((mod2 = { exports: {} }).exports, mod2), mod2.exports;
19
13
  };
20
14
  var __copyProps = (to, from, except, desc) => {
@@ -890,6 +884,7 @@ var init_contributions = __esm({
890
884
  init_contribution_gate();
891
885
  init_contribution_classify();
892
886
  init_github_bounties();
887
+ init_github();
893
888
  init_http();
894
889
  init_gh_governor();
895
890
  CONTRIB_LABEL_QUERIES = [
@@ -4112,61 +4107,19 @@ var init_src = __esm({
4112
4107
  }
4113
4108
  });
4114
4109
 
4115
- // ../../node_modules/keytar/build/Release/keytar.node
4116
- var keytar_default;
4117
- var init_keytar = __esm({
4118
- "../../node_modules/keytar/build/Release/keytar.node"() {
4119
- keytar_default = "../keytar-KOAAH267.node";
4120
- }
4121
- });
4122
-
4123
- // node-file:/private/tmp/claude-501/-Users-ericgang-job-placement-inline/9716ff9c-0531-4844-adf4-286763cf8ab8/scratchpad/deeplink-wt/node_modules/keytar/build/Release/keytar.node
4124
- var require_keytar = __commonJS({
4125
- "node-file:/private/tmp/claude-501/-Users-ericgang-job-placement-inline/9716ff9c-0531-4844-adf4-286763cf8ab8/scratchpad/deeplink-wt/node_modules/keytar/build/Release/keytar.node"(exports, module) {
4126
- "use strict";
4127
- init_keytar();
4128
- try {
4129
- module.exports = __require(keytar_default);
4130
- } catch {
4131
- }
4132
- }
4133
- });
4134
-
4135
4110
  // ../../node_modules/keytar/lib/keytar.js
4136
- var require_keytar2 = __commonJS({
4111
+ var require_keytar = __commonJS({
4137
4112
  "../../node_modules/keytar/lib/keytar.js"(exports, module) {
4138
4113
  "use strict";
4139
- var keytar = require_keytar();
4140
- function checkRequired(val, name) {
4141
- if (!val || val.length <= 0) {
4142
- throw new Error(name + " is required.");
4143
- }
4114
+ function disabled() {
4115
+ throw new Error("keytar disabled in this dev checkout (keychain popup guard) \u2014 key-file fallback expected");
4144
4116
  }
4145
4117
  module.exports = {
4146
- getPassword: function(service, account) {
4147
- checkRequired(service, "Service");
4148
- checkRequired(account, "Account");
4149
- return keytar.getPassword(service, account);
4150
- },
4151
- setPassword: function(service, account, password) {
4152
- checkRequired(service, "Service");
4153
- checkRequired(account, "Account");
4154
- checkRequired(password, "Password");
4155
- return keytar.setPassword(service, account, password);
4156
- },
4157
- deletePassword: function(service, account) {
4158
- checkRequired(service, "Service");
4159
- checkRequired(account, "Account");
4160
- return keytar.deletePassword(service, account);
4161
- },
4162
- findPassword: function(service) {
4163
- checkRequired(service, "Service");
4164
- return keytar.findPassword(service);
4165
- },
4166
- findCredentials: function(service) {
4167
- checkRequired(service, "Service");
4168
- return keytar.findCredentials(service);
4169
- }
4118
+ getPassword: disabled,
4119
+ setPassword: disabled,
4120
+ deletePassword: disabled,
4121
+ findPassword: disabled,
4122
+ findCredentials: disabled
4170
4123
  };
4171
4124
  }
4172
4125
  });
@@ -4193,7 +4146,7 @@ function skipKeychain() {
4193
4146
  async function loadKey() {
4194
4147
  if (!skipKeychain()) {
4195
4148
  try {
4196
- const kt = await Promise.resolve().then(() => __toESM(require_keytar2(), 1));
4149
+ const kt = await Promise.resolve().then(() => __toESM(require_keytar(), 1));
4197
4150
  const stored = await kt.getPassword("terminalhire", "profile-key");
4198
4151
  if (stored) return Buffer.from(stored, "hex");
4199
4152
  const key2 = randomBytes3(KEY_BYTES);
@@ -4230,7 +4183,7 @@ var TERMINALHIRE_DIR, TOKEN_FILE, KEY_FILE, ALGO, KEY_BYTES, IV_BYTES;
4230
4183
  var init_github_auth = __esm({
4231
4184
  "src/github-auth.ts"() {
4232
4185
  "use strict";
4233
- TERMINALHIRE_DIR = join2(homedir(), ".terminalhire");
4186
+ TERMINALHIRE_DIR = process.env.TERMINALHIRE_DIR || join2(homedir(), ".terminalhire");
4234
4187
  TOKEN_FILE = join2(TERMINALHIRE_DIR, "github-token.enc");
4235
4188
  KEY_FILE = join2(TERMINALHIRE_DIR, "key");
4236
4189
  ALGO = "aes-256-gcm";
@@ -4261,7 +4214,7 @@ var init_chat_keystore = __esm({
4261
4214
  "use strict";
4262
4215
  init_src();
4263
4216
  init_github_auth();
4264
- TERMINALHIRE_DIR2 = join3(homedir2(), ".terminalhire");
4217
+ TERMINALHIRE_DIR2 = process.env.TERMINALHIRE_DIR || join3(homedir2(), ".terminalhire");
4265
4218
  IDENTITY_FILE = join3(TERMINALHIRE_DIR2, "chat-identity.enc");
4266
4219
  }
4267
4220
  });
@@ -4278,7 +4231,7 @@ import {
4278
4231
  import { homedir as homedir3 } from "os";
4279
4232
  import { join as join4 } from "path";
4280
4233
  function terminalhireDir() {
4281
- return join4(homedir3(), ".terminalhire");
4234
+ return process.env.TERMINALHIRE_DIR || join4(homedir3(), ".terminalhire");
4282
4235
  }
4283
4236
  function webSessionFilePath() {
4284
4237
  return join4(terminalhireDir(), "web-session");
@@ -4503,7 +4456,7 @@ var init_chat_client = __esm({
4503
4456
  init_web_session();
4504
4457
  CHAT_BASE = process.env["TERMINALHIRE_API_URL"] || "https://terminalhire.com";
4505
4458
  GH_SESSION_COOKIE = "__jpi_gh_session";
4506
- TERMINALHIRE_DIR3 = join5(homedir4(), ".terminalhire");
4459
+ TERMINALHIRE_DIR3 = process.env.TERMINALHIRE_DIR || join5(homedir4(), ".terminalhire");
4507
4460
  PEERS_FILE = join5(TERMINALHIRE_DIR3, "chat-peers.json");
4508
4461
  REQUEST_TIMEOUT_MS = 1e4;
4509
4462
  ChatNotLinkedError = class extends Error {
@@ -4563,13 +4516,19 @@ function writeConfig(config) {
4563
4516
  mkdirSync5(TERMINALHIRE_DIR4, { recursive: true });
4564
4517
  const current = readConfig();
4565
4518
  const merged = { ...current, ...config };
4519
+ if ("contributePrompted" in merged) {
4520
+ if (merged.contributeEnabled === false && !("contributeEnabled" in config)) {
4521
+ delete merged.contributeEnabled;
4522
+ }
4523
+ delete merged.contributePrompted;
4524
+ }
4566
4525
  writeFileSync5(CONFIG_FILE, JSON.stringify(merged, null, 2) + "\n", "utf8");
4567
4526
  }
4568
4527
  var TERMINALHIRE_DIR4, CONFIG_FILE, DEFAULT_CONFIG;
4569
4528
  var init_config = __esm({
4570
4529
  "src/config.ts"() {
4571
4530
  "use strict";
4572
- TERMINALHIRE_DIR4 = join6(homedir5(), ".terminalhire");
4531
+ TERMINALHIRE_DIR4 = process.env.TERMINALHIRE_DIR || join6(homedir5(), ".terminalhire");
4573
4532
  CONFIG_FILE = join6(TERMINALHIRE_DIR4, "config.json");
4574
4533
  DEFAULT_CONFIG = {
4575
4534
  nudge: "session",
@@ -4580,12 +4539,12 @@ var init_config = __esm({
4580
4539
  chatShareActivity: false,
4581
4540
  inboundNudgeMuted: false,
4582
4541
  inboundNudgeDisclosed: false,
4583
- contributeEnabled: false,
4584
- contributePrompted: false,
4542
+ contributeEnabled: true,
4585
4543
  betaOptIn: false,
4586
4544
  lastFullFeedbackAt: null,
4587
4545
  lastPulseAskAt: null,
4588
- pulseDisclosed: false
4546
+ pulseDisclosed: false,
4547
+ mix: "balanced"
4589
4548
  };
4590
4549
  }
4591
4550
  });